diff --git a/.editorconfig b/.editorconfig index 536f29a3893..2ac65f2f5c7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,8 +7,11 @@ indent_style = space indent_size = 4 charset = utf-8 -[*.yml] +[*.yaml] indent_size = 2 [tests/_files/*_result_cache.txt] insert_final_newline = false + +[*.phpt] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index b1f86fffb77..89d2d3aafb4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,5 @@ /.github export-ignore /.phive export-ignore -/.psalm export-ignore /build export-ignore /tools export-ignore /tools/* binary @@ -10,6 +9,7 @@ /.gitignore export-ignore /.php-cs-fixer.dist.php export-ignore /build.xml export-ignore +/phpstan.neon export-ignore /phpunit.xml export-ignore *.php diff=php diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 697216fda18..24f5ca5019e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -18,6 +18,21 @@ Please note that this project is released with a [Contributor Code of Conduct](C When you submit code changes, your submissions are understood to be under the same [BSD-3-Clause License](https://github.com/sebastianbergmann/phpunit/blob/main/LICENSE) that covers the project. By contributing to this project, you agree that your contributions will be licensed under its BSD-3-Clause License. +### Do Not Violate Copyright + +Only submit a pull request with your own original code. Do NOT submit a pull request containing code which you have largely copied from +another project, unless you wrote the respective code yourself. + +Open Source does not mean that copyright does not apply. Copyright infringements will not be tolerated and can lead to you being banned from this project and repository. + +### Do Not Submit AI-Generated Pull Requests + +The same goes for (largely) AI-generated pull requests. These are not welcome as they will be based on copyrighted code from others +without accreditation and without taking the license of the original code into account, let alone getting permission +for the use of the code or for re-licensing. + +Aside from that, the experience is that AI-generated pull requests will be incorrect 100% of the time and cost reviewers too much time. +Submitting a (largely) AI-generated pull request will lead to you being banned from this project and repository. ## Write bug reports with detail, background, and sample code @@ -73,12 +88,6 @@ Due to time constraints, we are not always able to respond as quickly as we woul ## Coding Guidelines -This project comes with a configuration file (located at `/.psalm/config.xml` in the repository) and an executable for [Psalm](https://psalm.dev/) (located at `/tools/psalm` in the repository) that you can use to perform static analysis (with a focus on type checking): - -```bash -$ ./tools/psalm --config=.psalm/config.xml -``` - This project comes with a configuration file (located at `/.php-cs-fixer.dist.php` in the repository) and an executable for [php-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) (located at `/tools/php-cs-fixer` in the repository) that you can use to (re)format your source code for compliance with this project's coding guidelines: ```bash @@ -87,6 +96,13 @@ $ ./tools/php-cs-fixer fix Please understand that we will not accept a pull request when its changes violate this project's coding guidelines. +## Static Analysis + +This project comes with a configuration file (located at `/phpstan.neon` in the repository) and an executable for [PHPStan](https://phpstan.org/) (located at `/tools/phpstan` in the repository) that you can use to perform static analysis: + +```bash +$ ./tools/phpstan +``` ## Using PHPUnit from a Git checkout diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a5882a980b8..292fadb829f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,5 @@ github: sebastianbergmann +liberapay: sebastianbergmann +thanks_dev: u/gh/sebastianbergmann tidelift: "packagist/phpunit/phpunit" custom: https://phpunit.de/sponsors.html diff --git a/.github/ISSUE_TEMPLATE/1_BUG.md b/.github/ISSUE_TEMPLATE/1_BUG.md index 5903229d6d6..0876303567f 100644 --- a/.github/ISSUE_TEMPLATE/1_BUG.md +++ b/.github/ISSUE_TEMPLATE/1_BUG.md @@ -1,11 +1,11 @@ --- -name: 🐞 Bug Report for PHPUnit 9 and PHPUnit 10 -about: Something is broken in PHPUnit 9 and in PHPUnit 10? -labels: type/bug, version/9, version/10 +name: 🐞 Bug Report +about: Something is broken? +labels: type/bug --- diff --git a/.github/ISSUE_TEMPLATE/2_BUG_PHPUNIT_10.md b/.github/ISSUE_TEMPLATE/2_BUG_PHPUNIT_10.md deleted file mode 100644 index 08913ce647b..00000000000 --- a/.github/ISSUE_TEMPLATE/2_BUG_PHPUNIT_10.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: 🐞 Bug Report for PHPUnit 10 -about: Something is broken in PHPUnit 10, but it works in PHPUnit 9? -labels: type/bug, version/10 ---- - - - -| Q | A -| --------------------| --------------- -| PHPUnit version | x.y.z -| PHP version | x.y.z -| Installation Method | Composer / PHAR - -#### Summary - - - -#### Current behavior - - - -#### How to reproduce - - - -#### Expected behavior - - diff --git a/.github/ISSUE_TEMPLATE/2_COMPATIBILITY.md b/.github/ISSUE_TEMPLATE/2_COMPATIBILITY.md new file mode 100644 index 00000000000..f3573a1bc7a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2_COMPATIBILITY.md @@ -0,0 +1,39 @@ +--- +name: ⚠️ PHP Compatibility Issue +about: A change in PHP requires adaption in PHPUnit? +labels: type/change-in-php-requires-adaptation +--- + + + +| Q | A +| --------------------| --------------- +| PHPUnit version | x.y.z +| PHP version | x.y.z +| Installation Method | Composer / PHAR + +#### Summary + + + +#### Current behavior + + + +#### How to reproduce + + + +#### Expected behavior + + diff --git a/.github/ISSUE_TEMPLATE/3_BUG_PHPUNIT_9.md b/.github/ISSUE_TEMPLATE/3_BUG_PHPUNIT_9.md deleted file mode 100644 index 67584092e49..00000000000 --- a/.github/ISSUE_TEMPLATE/3_BUG_PHPUNIT_9.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: 🐞 Bug Report for PHPUnit 9 -about: Something is broken in PHPUnit 9, but it works in PHPUnit 10? -labels: type/bug, version/9 ---- - - - -| Q | A -| --------------------| --------------- -| PHPUnit version | x.y.z -| PHP version | x.y.z -| Installation Method | Composer / PHAR - -#### Summary - - - -#### Current behavior - - - -#### How to reproduce - - - -#### Expected behavior - - diff --git a/.github/ISSUE_TEMPLATE/7_FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/3_FEATURE_REQUEST.md similarity index 100% rename from .github/ISSUE_TEMPLATE/7_FEATURE_REQUEST.md rename to .github/ISSUE_TEMPLATE/3_FEATURE_REQUEST.md diff --git a/.github/ISSUE_TEMPLATE/4_COMPATIBILITY_PHPUNIT_10.md b/.github/ISSUE_TEMPLATE/4_COMPATIBILITY_PHPUNIT_10.md deleted file mode 100644 index 32830945e70..00000000000 --- a/.github/ISSUE_TEMPLATE/4_COMPATIBILITY_PHPUNIT_10.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: ⚠️ PHP Compatibility Issue in PHPUnit 10 -about: A change in a new version of PHP requires adaption in PHPUnit 10? -labels: type/change-in-php-requires-adaptation, version/10 ---- - - - -| Q | A -| --------------------| --------------- -| PHPUnit version | x.y.z -| PHP version | x.y.z -| Installation Method | Composer / PHAR - -#### Summary - - - -#### Current behavior - - - -#### How to reproduce - - - -#### Expected behavior - - diff --git a/.github/ISSUE_TEMPLATE/5_COMPATIBILITY_PHPUNIT_9.md b/.github/ISSUE_TEMPLATE/5_COMPATIBILITY_PHPUNIT_9.md deleted file mode 100644 index 1def500976e..00000000000 --- a/.github/ISSUE_TEMPLATE/5_COMPATIBILITY_PHPUNIT_9.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: ⚠️ PHP Compatibility Issue in PHPUnit 9 -about: A change in a new version of PHP requires adaption in PHPUnit 9? -labels: type/change-in-php-requires-adaptation, version/9 ---- - - - -| Q | A -| --------------------| --------------- -| PHPUnit version | x.y.z -| PHP version | x.y.z -| Installation Method | Composer / PHAR - -#### Summary - - - -#### Current behavior - - - -#### How to reproduce - - - -#### Expected behavior - - diff --git a/.github/ISSUE_TEMPLATE/6_COMPATIBILITY_PHPUNIT_8.md b/.github/ISSUE_TEMPLATE/6_COMPATIBILITY_PHPUNIT_8.md deleted file mode 100644 index 45127526085..00000000000 --- a/.github/ISSUE_TEMPLATE/6_COMPATIBILITY_PHPUNIT_8.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: ⚠️ PHP Compatibility Issue in PHPUnit 8 -about: A change in a new version of PHP requires adaption in PHPUnit 8? -labels: type/change-in-php-requires-adaptation, version/8 ---- - - - -| Q | A -| --------------------| --------------- -| PHPUnit version | x.y.z -| PHP version | x.y.z -| Installation Method | Composer / PHAR - -#### Summary - - - -#### Current behavior - - - -#### How to reproduce - - - -#### Expected behavior - - diff --git a/.github/img/bubble-shooter.png b/.github/img/bubble-shooter.png new file mode 100755 index 00000000000..f358c1c1ed0 Binary files /dev/null and b/.github/img/bubble-shooter.png differ diff --git a/.github/img/in2it.svg b/.github/img/in2it.svg new file mode 100644 index 00000000000..a58fb6f6d58 --- /dev/null +++ b/.github/img/in2it.svg @@ -0,0 +1,39 @@ + + + + + Produced by OmniGraffle 7.18.2\n2021-01-25 13:37:49 +0000 + + In2it Logo White SVG + + Layer 1 + + + + + + 2 + + + + in + + + + + + + + + it + + + + + + + + + + + diff --git a/.github/img/phpunit.svg b/.github/img/phpunit.svg new file mode 100755 index 00000000000..8fe04bd143c --- /dev/null +++ b/.github/img/phpunit.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/img/roave.svg b/.github/img/roave.svg new file mode 100644 index 00000000000..9911ef12eeb --- /dev/null +++ b/.github/img/roave.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/img/route4me.svg b/.github/img/route4me.svg new file mode 100644 index 00000000000..d4334e8d880 --- /dev/null +++ b/.github/img/route4me.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/.github/img/testmo.svg b/.github/img/testmo.svg new file mode 100644 index 00000000000..791908c2977 --- /dev/null +++ b/.github/img/testmo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/img/tideways.svg b/.github/img/tideways.svg new file mode 100644 index 00000000000..74b6b993cd4 --- /dev/null +++ b/.github/img/tideways.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/img/typo3.svg b/.github/img/typo3.svg new file mode 100644 index 00000000000..3ceb2f2ecaf --- /dev/null +++ b/.github/img/typo3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.github/img/vema.svg b/.github/img/vema.svg new file mode 100644 index 00000000000..4555ab42774 --- /dev/null +++ b/.github/img/vema.svg @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000000..22e082e39f4 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,332 @@ +# https://docs.github.com/en/actions + +on: + pull_request: null + push: null + schedule: + - cron: "15 0 * * *" + +name: CI + +env: + COMPOSER_ROOT_VERSION: "12.2.x-dev" + +permissions: + contents: read + +jobs: + dependency-validation: + name: Dependency Validation + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + extensions: none, ctype, curl, dom, json, libxml, mbstring, openssl, phar, tokenizer, xml, xmlwriter + coverage: none + tools: none + + - name: Ensure that composer.json is valid + run: ./tools/composer validate --no-ansi --strict composer.json + + - name: Ensure that dependencies can be installed + run: ./tools/composer install --no-ansi --dry-run + + coding-guidelines: + name: Coding Guidelines + + if: github.event_name != 'schedule' + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: none, iconv, json, phar, tokenizer + coverage: none + tools: none + + - name: Run PHP-CS-Fixer + run: ./tools/php-cs-fixer fix --dry-run --show-progress=dots --using-cache=no --verbose + + static-analysis: + name: Static Analysis + + if: github.event_name != 'schedule' + + needs: + - dependency-validation + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + coverage: none + tools: none + + - name: Install dependencies with Composer + run: ./tools/composer update --no-interaction --no-ansi --no-progress + + - name: Run PHPStan + run: ./tools/phpstan analyse --no-progress --error-format=github + + unit-tests: + name: Unit Tests + + needs: + - dependency-validation + + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + + env: + PHP_EXTENSIONS: none, ctype, curl, dom, json, libxml, mbstring, openssl, phar, tokenizer, xml, xmlwriter + PHP_INI_VALUES: memory_limit=-1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + + php-version: + - "8.3" + - "8.4" + - "8.5" + + steps: + - name: Configure Git to avoid issues with line endings + if: matrix.os == 'windows-latest' + run: git config --global core.autocrlf false + + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: ${{ env.PHP_EXTENSIONS }} + ini-values: ${{ env.PHP_INI_VALUES }} + tools: none + + - name: Install dependencies with Composer + run: php ./tools/composer install --no-ansi --no-interaction --no-progress + + - name: Run tests with PHPUnit + run: php ./phpunit --testsuite unit --order-by depends,random + + end-to-end-tests: + name: End-to-End Tests + + needs: + - unit-tests + + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + + env: + PHP_EXTENSIONS: none, ctype, curl, dom, json, libxml, mbstring, openssl, pdo, phar, tokenizer, xml, xmlwriter + PHP_INI_VALUES: zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + + php-version: + - "8.3" + - "8.4" + - "8.5" + + steps: + - name: Configure Git to avoid issues with line endings + if: matrix.os == 'windows-latest' + run: git config --global core.autocrlf false + + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: ${{ env.PHP_EXTENSIONS }} + ini-values: ${{ env.PHP_INI_VALUES }} + tools: none + + - name: Install dependencies with Composer + run: php ./tools/composer install --no-ansi --no-interaction --no-progress + + - name: Run tests with PHPUnit + run: php ./phpunit --testsuite end-to-end --order-by depends,random + + code-coverage: + name: Code Coverage + + if: github.event_name != 'schedule' + + needs: + - end-to-end-tests + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + coverage: xdebug + extensions: none, ctype, curl, dom, json, libxml, mbstring, pdo, phar, tokenizer, xml, xmlwriter + ini-values: zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: none + + - name: Install dependencies with Composer + run: ./tools/composer install --no-ansi --no-interaction --no-progress + + - name: Collect code coverage with PHPUnit + run: ./phpunit --log-junit junit.xml --coverage-clover=coverage.xml + + - name: Upload test results to Codecov.io + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload code coverage data to Codecov.io + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + build-phar: + name: Build PHAR + + if: github.event_name != 'schedule' + + needs: + - end-to-end-tests + + runs-on: ubuntu-latest + timeout-minutes: 5 + + env: + PHP_EXTENSIONS: none, ctype, dom, json, fileinfo, iconv, libxml, mbstring, phar, tokenizer, xml, xmlwriter + PHP_INI_VALUES: phar.readonly=0, zend.assertions=1 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + coverage: none + extensions: ${{ env.PHP_EXTENSIONS }} + ini-values: ${{ env.PHP_INI_VALUES }} + tools: none + + - name: Install java + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 11 + + - name: Build PHAR + run: ant phar-snapshot + + - name: Check whether PHAR is scoped + run: grep -q PHPUnitPHAR\\\\DeepCopy\\\\Exception\\\\CloneException build/artifacts/phpunit-snapshot.phar || (echo "phpunit-snapshot.phar is not scoped." && false) + + - name: Upload PHAR + uses: actions/upload-artifact@v4 + with: + name: phpunit-snapshot-phar + overwrite: true + path: ./build/artifacts/phpunit-snapshot.phar + retention-days: 7 + + test-phar: + name: Test PHAR + + if: github.event_name != 'schedule' + + needs: + - build-phar + + runs-on: ubuntu-latest + timeout-minutes: 5 + + env: + PHP_EXTENSIONS: none, ctype, curl, dom, json, fileinfo, iconv, libxml, mbstring, phar, tokenizer, xml, xmlwriter + PHP_INI_VALUES: phar.readonly=0, zend.assertions=1 + + strategy: + fail-fast: false + matrix: + php-version: + - "8.3" + - "8.4" + - "8.5" + + coverage: + - pcov + - xdebug + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: ${{ matrix.coverage }} + extensions: ${{ env.PHP_EXTENSIONS }} + ini-values: ${{ env.PHP_INI_VALUES }} + tools: none + + - name: Install java + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 11 + + - name: Download PHAR + uses: actions/download-artifact@v4 + with: + name: phpunit-snapshot-phar + path: ./build/artifacts/ + + - name: Make PHAR executable + run: chmod +x ./build/artifacts/phpunit-snapshot.phar + + - name: Run PHAR-specific tests + run: ant run-phar-specific-tests diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index fe6785bed11..00000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,281 +0,0 @@ -# https://help.github.com/en/categories/automating-your-workflow-with-github-actions - -on: - - pull_request - - push - -name: CI - -env: - COMPOSER_ROOT_VERSION: "11.0-dev" - -permissions: - contents: read - -jobs: - coding-guidelines: - name: Coding Guidelines - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - extensions: none, iconv, json, phar, tokenizer - coverage: none - tools: none - - - name: Run PHP-CS-Fixer - run: ./tools/php-cs-fixer fix --dry-run --show-progress=dots --using-cache=no --verbose - - type-checker: - name: Type Checker - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - extensions: none, ctype, curl, date, dom, json, libxml, mbstring, phar, simplexml, soap, tokenizer, xml, xmlwriter, zlib - coverage: none - tools: none - - - name: Install dependencies with Composer - run: ./tools/composer update --no-interaction --no-ansi --no-progress - - - name: Run Psalm on public API - run: ./tools/psalm --config=.psalm/static-analysis.xml --no-progress --show-info=false - - - name: Run Psalm on internal code - run: ./tools/psalm --config=.psalm/config.xml --no-progress --shepherd --show-info=false --stats - - unit-tests: - name: Unit Tests - - runs-on: ${{ matrix.os }} - - env: - PHP_EXTENSIONS: none, curl, dom, json, libxml, mbstring, openssl, phar, soap, tokenizer, xml, xmlwriter - PHP_INI_VALUES: memory_limit=-1, assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On - - strategy: - fail-fast: false - matrix: - os: - - ubuntu-latest - - windows-latest - - php-version: - - "8.2" - - "8.3" - - "8.4" - - steps: - - name: Configure Git to avoid issues with line endings - if: matrix.os == 'windows-latest' - run: git config --global core.autocrlf false - - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.PHP_EXTENSIONS }} - ini-values: ${{ env.PHP_INI_VALUES }} - tools: none - - - name: Install dependencies with Composer - run: php ./tools/composer update --no-ansi --no-interaction --no-progress - - - name: Run tests with PHPUnit - run: php ./phpunit --testsuite unit - - end-to-end-tests: - name: End-to-End Tests - - needs: - - unit-tests - - runs-on: ${{ matrix.os }} - - env: - PHP_EXTENSIONS: none, curl, dom, json, libxml, mbstring, openssl, phar, soap, tokenizer, xml, xmlwriter - PHP_INI_VALUES: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On - - strategy: - fail-fast: false - matrix: - os: - - ubuntu-latest - - windows-latest - - php-version: - - "8.2" - - "8.3" - - "8.4" - - steps: - - name: Configure Git to avoid issues with line endings - if: matrix.os == 'windows-latest' - run: git config --global core.autocrlf false - - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.PHP_EXTENSIONS }} - ini-values: ${{ env.PHP_INI_VALUES }} - coverage: pcov - tools: none - - - name: Install dependencies with Composer - run: php ./tools/composer update --no-ansi --no-interaction --no-progress - - - name: Run tests with PHPUnit - run: php ./phpunit --testsuite end-to-end - - code-coverage: - name: Code Coverage - - needs: - - end-to-end-tests - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - coverage: pcov - extensions: none, curl, dom, json, libxml, mbstring, phar, soap, tokenizer, xml, xmlwriter - ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On - tools: none - - - name: Install dependencies with Composer - run: ./tools/composer update --no-ansi --no-interaction --no-progress - - - name: Collect code coverage with PHPUnit - run: ./phpunit --coverage-clover=coverage.xml - - - name: Send code coverage report to Codecov.io - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - - build-phar: - name: Build PHAR - - needs: - - end-to-end-tests - - runs-on: ubuntu-latest - - env: - PHP_EXTENSIONS: none, dom, json, fileinfo, iconv, libxml, mbstring, phar, soap, tokenizer, xml, xmlwriter - PHP_INI_VALUES: assert.exception=1, phar.readonly=0, zend.assertions=1 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - coverage: none - extensions: ${{ env.PHP_EXTENSIONS }} - ini-values: ${{ env.PHP_INI_VALUES }} - tools: none - - - name: Install java - uses: actions/setup-java@v3 - with: - distribution: zulu - java-version: 11 - - - name: Build PHAR - run: ant phar-snapshot - - - name: Check whether PHAR is scoped - run: cat build/artifacts/phpunit-snapshot.phar | grep -q PHPUnit\\\\DeepCopy\\\\Exception\\\\CloneException || (echo "phpunit-snapshot.phar is not scoped." && false) - - - name: Upload PHAR - uses: actions/upload-artifact@v3 - with: - name: phpunit-snapshot-phar - path: ./build/artifacts/phpunit-snapshot.phar - retention-days: 7 - - test-phar: - name: Test PHAR - - needs: - - build-phar - - runs-on: ubuntu-latest - - env: - PHP_EXTENSIONS: none, curl, dom, json, fileinfo, iconv, libxml, mbstring, phar, soap, tokenizer, xml, xmlwriter - PHP_INI_VALUES: assert.exception=1, phar.readonly=0, zend.assertions=1 - - strategy: - fail-fast: false - matrix: - php-version: - - "8.2" - - "8.3" - - "8.4" - - coverage: - - pcov - - xdebug - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - coverage: ${{ matrix.coverage }} - extensions: ${{ env.PHP_EXTENSIONS }} - ini-values: ${{ env.PHP_INI_VALUES }} - tools: none - - - name: Install java - uses: actions/setup-java@v3 - with: - distribution: zulu - java-version: 11 - - - name: Download PHAR - uses: actions/download-artifact@v3 - with: - name: phpunit-snapshot-phar - path: ./build/artifacts/ - - - name: Make PHAR executable - run: chmod +x ./build/artifacts/phpunit-snapshot.phar - - - name: Run PHAR-specific tests - run: ant run-phar-specific-tests diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000000..b0c7c499112 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,54 @@ +# https://docs.github.com/en/actions + +on: + push: + tags: + - "**" + +name: Release + +jobs: + release: + name: Release + + runs-on: ubuntu-latest + timeout-minutes: 10 + + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + extensions: none + tools: none + + - name: Determine tag + run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + + - name: Parse ChangeLog + run: build/scripts/extract-release-notes.php ${{ env.RELEASE_TAG }} > release-notes.md + + - name: Create release + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ env.RELEASE_TAG }} + name: PHPUnit ${{ env.RELEASE_TAG }} + bodyFile: release-notes.md + commit: "12.2" + + - name: Announce release + id: mastodon + uses: cbrgm/mastodon-github-action@v2 + with: + access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} + url: ${{ secrets.MASTODON_URL }} + language: "en" + message: "#PHPUnit ${{ env.RELEASE_TAG }} has been released: https://github.com/sebastianbergmann/phpunit/releases/tag/${{ env.RELEASE_TAG }}" diff --git a/.gitignore b/.gitignore index 8ec3936ba20..02631f2b73d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ # Composer /vendor -/composer.lock # Apache Ant /.ant_targets @@ -17,9 +16,6 @@ /.php-cs-fixer.php /.php-cs-fixer.cache -# Psalm -/.psalm/cache - # PHPUnit /.phpunit.cache .phpunit.result.cache diff --git a/.phive/phars.xml b/.phive/phars.xml index de3c07c81ac..208232010ef 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,8 +1,7 @@ - - - - - + + + + diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index c5b0ed90d0b..e6c9f823eff 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -14,8 +14,17 @@ ->in(__DIR__ . '/tests/_files') ->in(__DIR__ . '/tests/end-to-end') ->in(__DIR__ . '/tests/unit') + // *WithPropertyWith*Hook.php use PHP 8.4 syntax that currently leads to PHP-CS-Fixer errors + ->notName('ExtendableClassWithPropertyWithGetHook.php') + ->notName('ExtendableClassWithPropertyWithSetHook.php') + ->notName('InterfaceWithPropertyWithGetHook.php') + ->notName('InterfaceWithPropertyWithSetHook.php') + // DeprecatedPhpFeatureTest.php must not use declare(strict_types=1); ->notName('DeprecatedPhpFeatureTest.php') - ->notName('ReadonlyClass.php') + // UseBaselineTest.php must not use declare(strict_types=1); + ->notName('UseBaselineTest.php') + // Issue5795Test.php contains required whitespace that would be cleaned up + ->notName('Issue5795Test.php') ->notName('*.phpt'); $config = new PhpCsFixer\Config; @@ -26,6 +35,9 @@ 'array_indentation' => true, 'array_push' => true, 'array_syntax' => ['syntax' => 'short'], + 'attribute_empty_parentheses' => [ + 'use_parentheses' => false, + ], 'backtick_to_shell_exec' => true, 'binary_operator_spaces' => [ 'operators' => [ @@ -103,7 +115,7 @@ 'explicit_string_variable' => true, 'fopen_flag_order' => true, 'full_opening_tag' => true, - 'fully_qualified_strict_types' => true, + 'fully_qualified_strict_types' => ['import_symbols' => true], 'function_declaration' => true, 'function_to_constant' => true, 'get_class_to_class_keyword' => true, @@ -139,7 +151,7 @@ 'modernize_types_casting' => true, 'multiline_comment_opening_closing' => true, 'multiline_whitespace_before_semicolons' => true, - 'native_constant_invocation' => false, + 'native_constant_invocation' => true, 'native_function_casing' => false, 'native_function_invocation' => [ 'include' => [ @@ -162,7 +174,23 @@ 'no_empty_comment' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, - 'no_extra_blank_lines' => true, + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'attribute', + 'break', + 'case', + 'continue', + 'curly_brace_block', + 'default', + 'extra', + 'parenthesis_brace_block', + 'return', + 'square_brace_block', + 'switch', + 'throw', + 'use', + ], + ], 'no_homoglyph_names' => true, 'no_leading_import_slash' => true, 'no_leading_namespace_whitespace' => true, @@ -201,6 +229,7 @@ 'no_whitespace_in_blank_line' => true, 'non_printable_character' => true, 'normalize_index_brace' => true, + 'nullable_type_declaration_for_default_null_value' => true, 'object_operator_without_whitespace' => true, 'octal_notation' => true, 'operator_linebreak' => [ @@ -344,4 +373,8 @@ 'whitespace_after_comma_in_array' => true, ]); +$config->setCacheFile(__DIR__ . '/.php-cs-fixer.cache/' . json_decode((string) @file_get_contents('composer.json'), true)["extra"]["branch-alias"]["dev-main"] ?? 'unknown'); + +$config->setParallelConfig(\PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()); + return $config; diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php deleted file mode 100644 index 496a943e039..00000000000 --- a/.phpstorm.meta.php +++ /dev/null @@ -1,38 +0,0 @@ -"$0"]) - ); - - override( - \PHPUnit\Framework\TestCase::createConfiguredStub(0), - map([""=>"$0"]) - ); - - override( - \PHPUnit\Framework\TestCase::createMock(0), - map([""=>"$0"]) - ); - - override( - \PHPUnit\Framework\TestCase::createConfiguredMock(0), - map([""=>"$0"]) - ); - - override( - \PHPUnit\Framework\TestCase::createPartialMock(0), - map([""=>"$0"]) - ); - - override( - \PHPUnit\Framework\TestCase::createTestProxy(0), - map([""=>"$0"]) - ); - - override( - \PHPUnit\Framework\TestCase::getMockForAbstractClass(0), - map([""=>"$0"]) - ); -} diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml deleted file mode 100644 index a82dbe73bde..00000000000 --- a/.psalm/baseline.xml +++ /dev/null @@ -1,995 +0,0 @@ - - - - - notify - - - - - $file - $file - $file - $file - $file - $file - $file - $line - $line - $line - $line - $line - $line - $line - $message - $message - $message - $message - $message - $message - $message - $message - - - assert($test instanceof TestMethod) - - - toString - toString - toString - toString - - - - - $calledMethods - - - - - $calledMethods - - - - - $calledMethods - - - - - $calledMethods - - - - - $calledMethods - - - - - $calledMethods - - - - - $calledMethods - - - - - $methods - - - - - $methodNames - - - - - - - - - - - - - get_loaded_extensions - - - - - fromSecondsAndNanoseconds - - - - - fromBytes - - - - - - - - - - - - - - - - - $tests - - - - - $data - - - - - dataFromDataProvider - dataFromDataProvider - hasDataFromDataProvider - hasDataFromDataProvider - - - - - $className - $className - $methodName - name()]]> - name()]]> - - - - - Assert::assertStringNotMatchesFormat(...func_get_args()) - Assert::assertStringNotMatchesFormatFile(...func_get_args()) - - - Assert::anything(...func_get_args()) - Assert::directoryExists(...func_get_args()) - Assert::fileExists(...func_get_args()) - Assert::isEmpty(...func_get_args()) - Assert::isFalse(...func_get_args()) - Assert::isFinite(...func_get_args()) - Assert::isInfinite(...func_get_args()) - Assert::isJson(...func_get_args()) - Assert::isList(...func_get_args()) - Assert::isNan(...func_get_args()) - Assert::isNull(...func_get_args()) - Assert::isReadable(...func_get_args()) - Assert::isTrue(...func_get_args()) - Assert::isWritable(...func_get_args()) - - - - - getCode()]]> - - - - - toString - - - - - toString - toString - - - - - regularExpression]]> - - - - - getMethod - - - toString - - - - - hasProperty - - - toString - toString - - - - - $this->checkConstraint($constraint), - $constraints, - )]]> - - - - - bool - - - - - pattern]]> - - - - - toString - - - - - $line - regularExpressionForFormatDescription( - $this->convertNewlines($this->formatDescription), - )]]> - - - - - toString - - - - - toString - - - - - $type - - - - - toString - - - - - $className - $methodName - - - providedTests]]> - - - ]]> - - - - - $className - $methods - $type - - - render(), - $className['className'], - )]]> - render(), $className['className'])]]> - new UnknownTraitException($traitName) - new UnknownTraitException($traitName) - - - mockObjectForAbstractClass - - - getMethod - - - __getFunctions()]]> - - - - - $this->argumentsForDeclaration, - 'arguments_call' => $this->argumentsForCall, - 'return_declaration' => !empty($this->returnType->asString()) ? (': ' . $this->returnType->asString()) : '', - 'return_type' => $this->returnType->asString(), - 'arguments_count' => !empty($this->argumentsForCall) ? substr_count($this->argumentsForCall, ',') + 1 : 0, - 'class_name' => $this->className, - 'method_name' => $this->methodName, - 'modifier' => $this->modifier, - 'reference' => $this->reference, - 'clone_arguments' => $this->cloneArguments ? 'true' : 'false', - 'deprecation' => $deprecation, - 'return_result' => $returnResult, - ]]]> - - - ')]]> - - - - - mockObjectForAbstractClass - mockObjectForTrait - - - $object - $object - $object - - - - - - - - - - $configurableMethods - - - - - MethodName - - - methodNameRule]]> - - - - - $returnType - $types - - - newInstanceWithoutConstructor()]]> - - - MockObject - - - - - $parameterVerificationResult - - - - - $mockClassName - $originalClassName - expectedException]]> - - - disableArgumentCloning - disallowMockingUnknownTypes - enableProxyingToOriginalMethods - generateClassFromWsdl - mockObjectForAbstractClass - mockObjectForTrait - objectForTrait - - - getMethod - - - $outputBufferingLevel - - - $backupGlobalsExcludeList - $groups - - - - - output()]]> - - - $var - - - bootstrap - - - - - name]]> - - - providedTests]]> - requiredTests]]> - - - ]]> - ]]> - - - - - testSuiteTimes]]> - - - assert($test instanceof TestMethod) - - - - - comparisonFailure - comparisonFailure - - - $flowId - - - assert($test instanceof TestMethod) - assert($testSuite instanceof TestSuiteForTestClass) - assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider) - assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider) - - - - - $value - - - - - $testResults - - - - - status]]> - time]]> - - - testDoubles]]> - - - assert($test instanceof TestMethod) - - - - - $metadata instanceof CoversFunction - $metadata instanceof UsesFunction - assert($metadata instanceof Covers) - assert($metadata instanceof Uses) - - - - - getCode()]]> - - - (int) $matches[0][1] - - - - - assert($metadata instanceof DependsOnClass) - - - - - array_unique($groups) - array_unique($groups) - - - ]]> - - - $metadata instanceof CoversFunction - $metadata instanceof UsesFunction - assert($metadata instanceof Covers) - assert($metadata instanceof Uses) - - - - - assert($metadata instanceof RequiresFunction) - assert($metadata instanceof RequiresMethod) - assert($metadata instanceof RequiresOperatingSystem) - assert($metadata instanceof RequiresOperatingSystemFamily) - assert($metadata instanceof RequiresPhp) - assert($metadata instanceof RequiresPhpExtension) - assert($metadata instanceof RequiresPhpunit) - assert($metadata instanceof RequiresSetting) - - - - - $metadata - - - - - $recordedOffsets], - array_filter( - [ - 'setting' => $recordedSettings, - 'extension_versions' => $extensionVersions, - ], - ), - )]]> - - - parsedRequirements = array_merge( - $requires, - ['__OFFSET' => $recordedOffsets], - array_filter( - [ - 'setting' => $recordedSettings, - 'extension_versions' => $extensionVersions, - ], - ), - )]]> - - - &array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * >]]> - - - - - $_className - $_className - $function - $function - $pieces[0] - $pieces[0] - $pieces[1] - $pieces[1] - $value - $value - $value - $value - $value - $value - - - trim($tmp[0]) - trim($tmp[1]) - - - - - =']]> - - - - - - - - ]]> - - - - - Driver - \SebastianBergmann\CodeCoverage\CodeCoverage - - - codeCoverageGenerationFailed - codeCoverageGenerationFailed - codeCoverageGenerationFailed - codeCoverageGenerationFailed - codeCoverageGenerationFailed - codeCoverageGenerationFailed - codeCoverageGenerationSucceeded - codeCoverageGenerationSucceeded - codeCoverageGenerationSucceeded - codeCoverageGenerationSucceeded - codeCoverageGenerationSucceeded - codeCoverageGenerationSucceeded - coverageCacheDirectory - coverageClover - coverageCobertura - coverageCrap4j - coverageHtml - coverageHtmlCustomCssFile - coveragePhp - coverageText - coverageText - coverageXml - - - codeCoverage]]> - driver]]> - - - start - stop - - - - - $errorFile - $errorLine - $errorString - - - Issue::from($file, $line, null, $description) - Issue::from($file, $line, null, $description) - - - - - GroupFilterIterator - - - groupTests]]> - - - - - $filter - filter]]> - - - NameFilterIterator - - - filter]]> - - - - - TestIdFilterIterator - - - - - RawCodeCoverageData::fromXdebugWithoutPathCoverage([]) - RawCodeCoverageData::fromXdebugWithoutPathCoverage([]) - - - RawCodeCoverageData::fromXdebugWithoutPathCoverage([]) - RawCodeCoverageData::fromXdebugWithoutPathCoverage([]) - - - bootstrap - - - - - - $setting[1] - - - - - assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider) - - - - - require_once $suiteClassFile - - - - - $tests - randomize($suite->tests())]]> - resolveDependencies($tests)]]> - reverse($suite->tests())]]> - sortByDuration($suite->tests())]]> - sortBySize($suite->tests())]]> - sortDefectsFirst($suite->tests())]]> - - - - - generateBaseline()]]> - - - nameAndVersion - - - logfileTeamcity(), - )]]> - logfileTeamcity(), - )]]> - logfileJunit())]]> - logfileJunit())]]> - atLeastVersion - build - configurationFile - listTestsXml - logEventsText - logEventsText - logEventsText - logEventsVerboseText - logEventsVerboseText - logEventsVerboseText - logfileJunit - logfileTeamcity - - - include_once $filename - - - - - Registry::init( - $cliConfiguration, - $xmlConfiguration, - ) - - - - - $options[1] - $parameters - $testSuffixes - - - - - bool - string - - - excludeTestSuite]]> - teamCityPrinter]]> - - - - - configurationFile - configurationFile - configurationFile - - - - - testsCovering]]> - testsUsing]]> - - - ]]> - ]]> - - - $columns - - - - - baseline - detect - - - - - - assert($xmlConfiguration instanceof LoadedFromFileConfiguration) - - - - - $files - $source - $source - $source - $source - - - - - \PHPUnit\Event\TestSuite\TestSuiteBuilder::from($testSuite) - - - - - $constants - - - - - $directories - - - - - $extensionBootstraps - - - - - $files - - - - - $directories - - - - - $groups - - - - - $iniSettings - - - - - $directories - - - - - $files - - - - - $testSuites - - - - - $variables - - - - - getAttribute('class')]]> - getAttribute('phpVersionOperator')]]> - getAttribute('phpVersionOperator')]]> - - - assert($directoryNode instanceof DOMElement) - assert($fileNode instanceof DOMElement) - - - - - createElement - - - - - createElement - - - - - createElement - - - - - createElement - - - - - createElement - - - - - createElement - - - - - removeChild - - - - - removeChild - removeChild - - - - - - - - - - - assert($test instanceof TestMethod) - assert($test instanceof TestMethod) - - - - - Printer - - - DefaultPrinter::standardError() - DefaultPrinter::standardError() - DefaultPrinter::standardOutput() - DefaultPrinter::standardOutput() - DefaultPrinter::standardOutput() - DefaultPrinter::standardOutput() - colors(), - $configuration->columns(), - $configuration->source(), - )]]> - colors(), - $configuration->columns(), - $configuration->source(), - )]]> - - - self::$printer - - - - - stream]]> - stream]]> - - - - - excludeGroups()]]> - filter()]]> - groups()]]> - - - - - self::$directories - self::$directories - self::$directories - - - - - is_array($envVar) - - - $_SERVER - - - [] - - - - - $filter - - - - - ', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], true)]]> - - - $operator - - - diff --git a/.psalm/config.xml b/.psalm/config.xml deleted file mode 100644 index 1afb8a9d293..00000000000 --- a/.psalm/config.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/.psalm/static-analysis-baseline.xml b/.psalm/static-analysis-baseline.xml deleted file mode 100644 index f3a36ea3495..00000000000 --- a/.psalm/static-analysis-baseline.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - getMockBuilder(HelloWorldClass::class)]]> - - - createPartialMock - createTestProxy - getMockBuilder - - - diff --git a/.psalm/static-analysis.xml b/.psalm/static-analysis.xml deleted file mode 100644 index 5867c00e49b..00000000000 --- a/.psalm/static-analysis.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - diff --git a/ChangeLog-11.0.md b/ChangeLog-11.0.md deleted file mode 100644 index 88bd2e7f1e5..00000000000 --- a/ChangeLog-11.0.md +++ /dev/null @@ -1,48 +0,0 @@ -# Changes in PHPUnit 11.0 - -All notable changes of the PHPUnit 11.0 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. - -## [11.0.0] - 2024-02-02 - -### Changed - -* [#5213](https://github.com/sebastianbergmann/phpunit/issues/5213): Make `TestCase` methods `protected` that should have been `protected` all along -* [#5254](https://github.com/sebastianbergmann/phpunit/issues/5254): Make `TestCase` methods `final` that should have been `final` all along - -### Deprecated - -* [#4505](https://github.com/sebastianbergmann/phpunit/issues/4505): Support for metadata in doc-comments -* [#5214](https://github.com/sebastianbergmann/phpunit/issues/5214): `TestCase::iniSet()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5216](https://github.com/sebastianbergmann/phpunit/issues/5216): `TestCase::setLocale()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5240](https://github.com/sebastianbergmann/phpunit/issues/5240): `TestCase::createTestProxy()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5241](https://github.com/sebastianbergmann/phpunit/issues/5241): `TestCase::getMockForAbstractClass()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5242](https://github.com/sebastianbergmann/phpunit/issues/5242): `TestCase::getMockFromWsdl()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5243](https://github.com/sebastianbergmann/phpunit/issues/5243): `TestCase::getMockForTrait()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5244](https://github.com/sebastianbergmann/phpunit/issues/5244): `TestCase::getObjectForTrait()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5305](https://github.com/sebastianbergmann/phpunit/issues/5305): `MockBuilder::getMockForAbstractClass()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5306](https://github.com/sebastianbergmann/phpunit/issues/5306): `MockBuilder::getMockForTrait()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307): `MockBuilder::enableProxyingToOriginalMethods()`, `MockBuilder::disableProxyingToOriginalMethods()`, and `MockBuilder::setProxyTarget()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5308](https://github.com/sebastianbergmann/phpunit/issues/5308): `MockBuilder::allowMockingUnknownTypes()` and `MockBuilder::disallowMockingUnknownTypes()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5309](https://github.com/sebastianbergmann/phpunit/issues/5309): `MockBuilder::enableAutoload()` and `MockBuilder::disableAutoload()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5315](https://github.com/sebastianbergmann/phpunit/issues/5315): `MockBuilder::enableArgumentCloning()` and `MockBuilder::disableArgumentCloning()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5320](https://github.com/sebastianbergmann/phpunit/issues/5320): `MockBuilder::addMethods()` (this method was already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5415](https://github.com/sebastianbergmann/phpunit/issues/5415): Support for doubling interfaces (or classes) that have a method named `method` -* [#5421](https://github.com/sebastianbergmann/phpunit/issues/5421): `MockBuilder::enableAutoReturnValueGeneration()` and `MockBuilder::disableAutoReturnValueGeneration()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423): `TestCase::returnValue()`, `TestCase::onConsecutiveCalls()`, `TestCase::returnValueMap()`, `TestCase::returnArgument()`, `TestCase::returnSelf()`, and `TestCase::returnCallback()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5472](https://github.com/sebastianbergmann/phpunit/issues/5472): `assertStringNotMatchesFormat()` and `assertStringNotMatchesFormatFile()` (these methods were already [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation) in PHPUnit 10) -* [#5535](https://github.com/sebastianbergmann/phpunit/issues/5535): Configuring expectations using `expects()` on test stubs - -### Removed - -* [#4600](https://github.com/sebastianbergmann/phpunit/issues/4600): Support for old cache configuration -* [#4604](https://github.com/sebastianbergmann/phpunit/issues/4604): Support for `backupStaticAttributes` attribute in XML configuration file -* [#4779](https://github.com/sebastianbergmann/phpunit/issues/4779): Support for `forceCoversAnnotation` and `beStrictAboutCoversAnnotation` attributes in XML configuration file -* [#5100](https://github.com/sebastianbergmann/phpunit/issues/5100): Support for non-static data provider methods, non-public data provider methods, and data provider methods that declare parameters -* [#5101](https://github.com/sebastianbergmann/phpunit/issues/5101): Support for PHP 8.1 -* [#5272](https://github.com/sebastianbergmann/phpunit/issues/5272): Optional parameters of `PHPUnit\Framework\Constraint\IsEqual::__construct()` -* [#5329](https://github.com/sebastianbergmann/phpunit/issues/5329): Support for configuring include/exclude list for code coverage using the `` element -* [#5482](https://github.com/sebastianbergmann/phpunit/issues/5482): `dataSet` attribute for `testCaseMethod` elements in the XML document generated by `--list-tests-xml` -* [#5514](https://github.com/sebastianbergmann/phpunit/issues/5514:) `IgnoreClassForCodeCoverage`, `IgnoreMethodForCodeCoverage`, and `IgnoreFunctionForCodeCoverage` attributes -* `CodeCoverageIgnore` attribute - -[11.0.0]: https://github.com/sebastianbergmann/phpunit/compare/10.5...main diff --git a/ChangeLog-12.2.md b/ChangeLog-12.2.md new file mode 100644 index 00000000000..55fdc40f566 --- /dev/null +++ b/ChangeLog-12.2.md @@ -0,0 +1,14 @@ +# Changes in PHPUnit 12.2 + +All notable changes of the PHPUnit 12.2 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. + +## [12.2.0] - 2025-06-06 + +### Added + +* `--with-telemetry` CLI option that can be used together with `--debug` to print debugging information that includes telemetry information +* The new `Test\AfterLastTestMethodFailed`, `Test\AfterTestMethodFailed`, `Test\BeforeFirstTestMethodFailed`, `Test\BeforeTestMethodFailed`, `Test\PostConditionFailed`, `Test\PreConditionFailed` events are now emitted instead of `Test\AfterLastTestMethodErrored`, `Test\AfterTestMethodErrored`, `Test\BeforeFirstTestMethodErrored`, `Test\BeforeTestMethodErrored`, `Test\PostConditionErrored`, `Test\PreConditionErrored` when the `Throwable` extends `AssertionFailedError` to distinguish between errors and failures triggered in hook methods +* The new `Test\PreparationErrored` event is now emitted instead of `Test\PreparationFailed` when the `Throwable` does not extend `AssertionFailedError` to distinguish between errors and failures triggered during test preparation +* `Test\PreparationFailed::throwable()` + +[12.2.0]: https://github.com/sebastianbergmann/phpunit/compare/12.1...main diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index 4c2323ca3a1..bb376b67a9c 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -6,20 +6,20 @@ This functionality is currently [soft-deprecated](https://phpunit.de/backward-co ### Extending PHPUnit -| Issue | Description | Since | Replacement | -|-------|--------------------------------------------------------------------------------------------------------|--------|--------------------------------------------------------------------------------| -| | `PHPUnit\TextUI\Configuration\Configuration::excludeDirectories()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->excludeDirectories()` | -| | `PHPUnit\TextUI\Configuration\Configuration::excludeFiles()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->excludeFiles()` | -| | `PHPUnit\TextUI\Configuration\Configuration::includeDirectories()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->includeDirectories()` | -| | `PHPUnit\TextUI\Configuration\Configuration::includeFiles()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->includeFiles()` | -| | `PHPUnit\TextUI\Configuration\Configuration::loadPharExtensions()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::noExtensions()` | -| | `PHPUnit\TextUI\Configuration\Configuration::hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->notEmpty()` | -| | `PHPUnit\TextUI\Configuration\Configuration::restrictDeprecations()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->restrictDeprecations()` | -| | `PHPUnit\TextUI\Configuration\Configuration::restrictNotices()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->restrictNotices()` | -| | `PHPUnit\TextUI\Configuration\Configuration::restrictWarnings()` | 10.2.0 | `PHPUnit\TextUI\Configuration\Configuration::source()->restrictWarnings()` | -| | `PHPUnit\TextUI\Configuration\Configuration::cliArgument()` | 10.4.0 | `PHPUnit\TextUI\Configuration\Configuration::cliArguments()[0]` | -| | `PHPUnit\TextUI\Configuration\Configuration::hasCliArgument()` | 10.4.0 | `PHPUnit\TextUI\Configuration\Configuration::hasCliArguments()` | -| | `PHPUnit\Framework\Constraint\Constraint::exporter()` | 10.4.0 | | +| Issue | Description | Since | Replacement | +|-------------------------------------------------------------------|---------------------------------------------|--------|-----------------------------------------------| +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `AfterTestMethodCalled::testCaseClass()` | 12.1.0 | `AfterTestMethodCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `AfterTestMethodErrored::testCaseClass()` | 12.1.0 | `AfterTestMethodCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `AfterTestMethodFinished::testCaseClass()` | 12.1.0 | `AfterTestMethodCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `BeforeTestMethodCalled::testCaseClass()` | 12.1.0 | `BeforeTestMethodCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `BeforeTestMethodErrored::testCaseClass()` | 12.1.0 | `BeforeTestMethodCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `BeforeTestMethodFinished::testCaseClass()` | 12.1.0 | `BeforeTestMethodCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `PreConditionCalled::testCaseClass()` | 12.1.0 | `PreConditionCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `PreConditionErrored::testCaseClass()` | 12.1.0 | `PreConditionCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `PreConditionFinished::testCaseClass()` | 12.1.0 | `PreConditionCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `PostConditionCalled::testCaseClass()` | 12.1.0 | `PostConditionCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `PostConditionErrored::testCaseClass()` | 12.1.0 | `PostConditionCalled::test()->className()` | +| [#6140](https://github.com/sebastianbergmann/phpunit/issues/6140) | `PostConditionFinished::testCaseClass()` | 12.1.0 | `PostConditionCalled::test()->className()` | ## Hard Deprecations @@ -29,47 +29,9 @@ This functionality is currently [hard-deprecated](https://phpunit.de/backward-co #### Assertions, Constraints, and Expectations -| Issue | Description | Since | Replacement | -|-------------------------------------------------------------------|------------------------------------------------|--------|-------------| -| [#5472](https://github.com/sebastianbergmann/phpunit/issues/5472) | `TestCase::assertStringNotMatchesFormat()` | 10.4.0 | | -| [#5472](https://github.com/sebastianbergmann/phpunit/issues/5472) | `TestCase::assertStringNotMatchesFormatFile()` | 10.4.0 | | - -#### Test Double API - -| Issue | Description | Since | Replacement | -|-------------------------------------------------------------------|--------------------------------------------------------------------------------|--------|-----------------------------------------------------------------------------------------| -| [#5240](https://github.com/sebastianbergmann/phpunit/issues/5240) | `TestCase::createTestProxy()` | 10.1.0 | | -| [#5241](https://github.com/sebastianbergmann/phpunit/issues/5241) | `TestCase::getMockForAbstractClass()` | 10.1.0 | | -| [#5242](https://github.com/sebastianbergmann/phpunit/issues/5242) | `TestCase::getMockFromWsdl()` | 10.1.0 | | -| [#5243](https://github.com/sebastianbergmann/phpunit/issues/5243) | `TestCase::getMockForTrait()` | 10.1.0 | | -| [#5244](https://github.com/sebastianbergmann/phpunit/issues/5244) | `TestCase::getObjectForTrait()` | 10.1.0 | | -| [#5305](https://github.com/sebastianbergmann/phpunit/issues/5305) | `MockBuilder::getMockForAbstractClass()` | 10.1.0 | | -| [#5306](https://github.com/sebastianbergmann/phpunit/issues/5306) | `MockBuilder::getMockForTrait()` | 10.1.0 | | -| [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307) | `MockBuilder::disableProxyingToOriginalMethods()` | 10.1.0 | | -| [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307) | `MockBuilder::enableProxyingToOriginalMethods()` | 10.1.0 | | -| [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307) | `MockBuilder::setProxyTarget()` | 10.1.0 | | -| [#5308](https://github.com/sebastianbergmann/phpunit/issues/5308) | `MockBuilder::allowMockingUnknownTypes()` | 10.1.0 | | -| [#5308](https://github.com/sebastianbergmann/phpunit/issues/5308) | `MockBuilder::disallowMockingUnknownTypes()` | 10.1.0 | | -| [#5309](https://github.com/sebastianbergmann/phpunit/issues/5309) | `MockBuilder::disableAutoload()` | 10.1.0 | | -| [#5309](https://github.com/sebastianbergmann/phpunit/issues/5309) | `MockBuilder::enableAutoload()` | 10.1.0 | | -| [#5315](https://github.com/sebastianbergmann/phpunit/issues/5315) | `MockBuilder::disableArgumentCloning()` | 10.1.0 | | -| [#5315](https://github.com/sebastianbergmann/phpunit/issues/5315) | `MockBuilder::enableArgumentCloning()` | 10.1.0 | | -| [#5320](https://github.com/sebastianbergmann/phpunit/issues/5320) | `MockBuilder::addMethods()` | 10.1.0 | | -| [#5415](https://github.com/sebastianbergmann/phpunit/issues/5415) | Support for doubling interfaces (or classes) that have a method named `method` | 11.0.0 | | -| [#5421](https://github.com/sebastianbergmann/phpunit/issues/5421) | `MockBuilder::disableAutoReturnValueGeneration()` | 10.3.0 | | -| [#5421](https://github.com/sebastianbergmann/phpunit/issues/5421) | `MockBuilder::enableAutoReturnValueGeneration()` | 10.3.0 | | -| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::onConsecutiveCalls()` | 10.3.0 | Use `$double->willReturn()` instead of `$double->will($this->onConsecutiveCalls())` | -| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnArgument()` | 10.3.0 | Use `$double->willReturnArgument()` instead of `$double->will($this->returnArgument())` | -| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnCallback()` | 10.3.0 | Use `$double->willReturnCallback()` instead of `$double->will($this->returnCallback())` | -| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnSelf()` | 10.3.0 | Use `$double->willReturnSelf()` instead of `$double->will($this->returnSelf())` | -| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnValue()` | 10.3.0 | Use `$double->willReturn()` instead of `$double->will($this->returnValue())` | -| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnValueMap()` | 10.3.0 | Use `$double->willReturnMap()` instead of `$double->will($this->returnValueMap())` | -| [#5535](https://github.com/sebastianbergmann/phpunit/issues/5525) | Configuring expectations using `expects()` on test stubs | 11.0.0 | Create a mock object when you need to configure expectations on a test double | - -#### Miscellaneous - -| Issue | Description | Since | Replacement | -|-------------------------------------------------------------------|--------------------------|--------|------------------------| -| [#4505](https://github.com/sebastianbergmann/phpunit/issues/4505) | Metadata in doc-comments | 10.3.0 | Metadata in attributes | -| [#5214](https://github.com/sebastianbergmann/phpunit/issues/5214) | `TestCase::iniSet()` | 10.3.0 | | -| [#5216](https://github.com/sebastianbergmann/phpunit/issues/5216) | `TestCase::setLocale()` | 10.3.0 | | +| Issue | Description | Since | Replacement | +|-------------------------------------------------------------------|-----------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [#6052](https://github.com/sebastianbergmann/phpunit/issues/6052) | `Assert::isType()` | 11.5.0 | Use `isArray()`, `isBool()`, `isCallable()`, `isFloat()`, `isInt()`, `isIterable()`, `isNull()`, `isNumeric()`, `isObject()`, `isResource()`, `isClosedResource()`, `isScalar()`, or `isString()` instead | +| [#6055](https://github.com/sebastianbergmann/phpunit/issues/6055) | `Assert::assertContainsOnly()` | 11.5.0 | Use `assertContainsOnlyArray()`, `assertContainsOnlyBool()`, `assertContainsOnlyCallable()`, `assertContainsOnlyFloat()`, `assertContainsOnlyInt()`, `assertContainsOnlyIterable()`, `assertContainsOnlyNumeric()`, `assertContainsOnlyObject()`, `assertContainsOnlyResource()`, `assertContainsOnlyClosedResource()`, `assertContainsOnlyScalar()`, or `assertContainsOnlyString()` instead | +| [#6055](https://github.com/sebastianbergmann/phpunit/issues/6055) | `Assert::assertNotContainsOnly()` | 11.5.0 | Use `assertContainsNotOnlyArray()`, `assertContainsNotOnlyBool()`, `assertContainsNotOnlyCallable()`, `assertContainsNotOnlyFloat()`, `assertContainsNotOnlyInt()`, `assertContainsNotOnlyIterable()`, `assertContainsNotOnlyNumeric()`, `assertContainsNotOnlyObject()`, `assertContainsNotOnlyResource()`, `assertContainsNotOnlyClosedResource()`, `assertContainsNotOnlyScalar()`, or `assertContainsNotOnlyString()` instead | +| [#6059](https://github.com/sebastianbergmann/phpunit/issues/6059) | `Assert::containsOnly()` | 11.5.0 | Use `containsOnlyArray()`, `containsOnlyBool()`, `containsOnlyCallable()`, `containsOnlyFloat()`, `containsOnlyInt()`, `containsOnlyIterable()`, `containsOnlyNumeric()`, `containsOnlyObject()`, `containsOnlyResource()`, `containsOnlyClosedResource()`, `containsOnlyScalar()`, or `containsOnlyString()` instead | diff --git a/LICENSE b/LICENSE index 73e955128f6..b687f39ac47 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2001-2023, Sebastian Bergmann +Copyright (c) 2001-2025, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 0e4789a0d68..4b5abe23308 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ -

🇺🇦 UKRAINE NEEDS YOUR HELP NOW!

+[![PHPUnit](.github/img/phpunit.svg)](https://phpunit.de/?ref=github) -# PHPUnit - -[![Latest Stable Version](https://poser.pugx.org/phpunit/phpunit/v/stable.png)](https://packagist.org/packages/phpunit/phpunit) [![CI Status](https://github.com/sebastianbergmann/phpunit/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/phpunit/actions) -[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/phpunit/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/phpunit) -[![codecov](https://codecov.io/gh/sebastianbergmann/phpunit/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/phpunit) +[![codecov](https://codecov.io/gh/sebastianbergmann/phpunit/branch/main/graph/badge.svg?token=0yzBUK8Wri)](https://codecov.io/gh/sebastianbergmann/phpunit) +[![Latest Stable Version](https://poser.pugx.org/phpunit/phpunit/v)](https://packagist.org/packages/phpunit/phpunit) +[![Total Downloads](https://poser.pugx.org/phpunit/phpunit/downloads)](https://packagist.org/packages/phpunit/phpunit/stats) +[![Monthly Downloads](https://poser.pugx.org/phpunit/phpunit/d/monthly)](https://packagist.org/packages/phpunit/phpunit/stats) +[![Daily Downloads](https://poser.pugx.org/phpunit/phpunit/d/daily)](https://packagist.org/packages/phpunit/phpunit/stats) + +# PHPUnit -PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks. +PHPUnit is a programmer-oriented testing framework for PHP. +It is an instance of the xUnit architecture for unit testing frameworks. ## Installation -We distribute a [PHP Archive (PHAR)](https://php.net/phar) that has all required (as well as some optional) dependencies of PHPUnit bundled in a single file: +We distribute a [PHP Archive (PHAR)](https://php.net/phar) that has all required dependencies of PHPUnit bundled in a single file: ```bash $ wget https://phar.phpunit.de/phpunit-X.Y.phar @@ -21,17 +24,86 @@ $ php phpunit-X.Y.phar --version Please replace `X.Y` with the version of PHPUnit you are interested in. -Alternatively, you may use [Composer](https://getcomposer.org/) to download and install PHPUnit as well as its dependencies. Please refer to the "[Getting Started](https://phpunit.de/getting-started-with-phpunit.html)" guide for details on how to install PHPUnit. +Alternatively, you may use [Composer](https://getcomposer.org/) to download and install PHPUnit as well as its dependencies. +Please refer to the [documentation](https://phpunit.de/documentation.html?ref=github) for details on how to install PHPUnit. ## Contribute Please refer to [CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/main/.github/CONTRIBUTING.md) for information on how to contribute to PHPUnit and its related projects. -## List of Contributors +A big "Thank you!" to everyone who has contributed to PHPUnit! +You can find a detailed list of contributors on every PHPUnit related package on GitHub. + +Here is a list of all components that are primarily developed and maintained by [Sebastian Bergmann](https://sebastian-bergmann.de/open-source.html?ref=github): + +* [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) +* [phpunit/php-code-coverage](https://github.com/sebastianbergmann/php-code-coverage) +* [phpunit/php-file-iterator](https://github.com/sebastianbergmann/php-file-iterator) +* [phpunit/php-invoker](https://github.com/sebastianbergmann/php-invoker) +* [phpunit/php-text-template](https://github.com/sebastianbergmann/php-text-template) +* [phpunit/php-timer](https://github.com/sebastianbergmann/php-timer) +* [sebastian/cli-parser](https://github.com/sebastianbergmann/cli-parser) +* [sebastian/comparator](https://github.com/sebastianbergmann/comparator) +* [sebastian/complexity](https://github.com/sebastianbergmann/complexity) +* [sebastian/diff](https://github.com/sebastianbergmann/diff) +* [sebastian/environment](https://github.com/sebastianbergmann/environment) +* [sebastian/exporter](https://github.com/sebastianbergmann/exporter) +* [sebastian/global-state](https://github.com/sebastianbergmann/global-state) +* [sebastian/lines-of-code](https://github.com/sebastianbergmann/lines-of-code) +* [sebastian/object-enumerator](https://github.com/sebastianbergmann/object-enumerator) +* [sebastian/object-reflector](https://github.com/sebastianbergmann/object-reflector) +* [sebastian/recursion-context](https://github.com/sebastianbergmann/recursion-context) +* [sebastian/type](https://github.com/sebastianbergmann/type) +* [sebastian/version](https://github.com/sebastianbergmann/version) + +A very special thanks to everyone who has contributed to the [PHPUnit Manual](https://github.com/sebastianbergmann/phpunit-documentation-english). + +In addition to the components listed above, PHPUnit depends on the components listed below: + +* [myclabs/deep-copy](https://github.com/myclabs/DeepCopy) +* [nikic/php-parser](https://github.com/nikic/php-parser) +* [phar-io/manifest](https://github.com/phar-io/manifest) +* [phar-io/version](https://github.com/phar-io/version) +* [staabm/side-effects-detector](https://github.com/staabm/side-effects-detector) +* [theseer/tokenizer](https://github.com/theseer/tokenizer) + +These tools are used to develop PHPUnit: + +* [Composer](https://getcomposer.org/) +* [Phive](https://phar.io/) +* [PHP Autoload Builder](https://github.com/theseer/Autoload/) +* [PHP-CS-Fixer](https://cs.symfony.com/) +* [PHP-Scoper](https://github.com/humbug/php-scoper) +* [PHPStan](https://phpstan.org/) + +## Sponsors + +It has taken [Sebastian Bergmann](https://sebastian-bergmann.de/open-source.html?ref=github) thousands of hours to develop, test, and support PHPUnit. +[**You can sponsor his Open Source work through GitHub Sponsors**](https://github.com/sponsors/sebastianbergmann), for example. + +These businesses support Sebastian Bergmann's work on PHPUnit: -Thanks to everyone who has contributed to PHPUnit! You can find a detailed list of contributors on every PHPUnit related package on GitHub. This list shows only the major components: + + + + + + + + + + + + + + + + + +
Bubble Shooterin2it vofRoave
Route4MeTestmo GmbHTideways GmbH
TYPO3 GmbHVEMA Versicherungsmakler Genossenschaft eG
-* [PHPUnit](https://github.com/sebastianbergmann/phpunit/graphs/contributors) -* [php-code-coverage](https://github.com/sebastianbergmann/php-code-coverage/graphs/contributors) +Would you like to see your logo here as well as on the [PHPUnit website](https://phpunit.de/sponsors.html?ref=github)? +Contact Sebastian Bergmann at [sponsoring@phpunit.de](mailto:sponsoring@phpunit.de) to learn more about how you can support his work on PHPUnit. -A very special thanks to everyone who has contributed to the [documentation](https://github.com/sebastianbergmann/phpunit-documentation-english/graphs/contributors). +Whether you are a CEO, CFO, CTO, or a developer: your company surely depends on Open Source software. +[It is time to pay your share](https://opensourcepledge.com/) and support maintainers like [Sebastian Bergmann](https://sebastian-bergmann.de/open-source.html?ref=github). diff --git a/SECURITY.md b/SECURITY.md index 965e5ed2d58..5f55c41947b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -24,7 +24,7 @@ PHPUnit is a framework for writing as well as a command-line tool for running te **If you upload PHPUnit to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** -Please note that if you upload PHPUnit to a webserver "bad things" may happen. [You have been warned.](https://thephp.cc/articles/phpunit-a-security-risk) +Please note that if you upload PHPUnit to a webserver "bad things" may happen. [You have been warned.](https://thephp.cc/articles/phpunit-a-security-risk?ref=phpunit) PHPUnit is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using PHPUnit in an HTTP or web context or with untrusted input data is performed. PHPUnit might also contain functionality that intentionally exposes internal application data for debugging purposes. diff --git a/build.xml b/build.xml index 0c4fc1dd408..db8da284df1 100644 --- a/build.xml +++ b/build.xml @@ -1,78 +1,100 @@ - - - + + - + + + + - - - - - - - + + + - + + + + + + + - - - - - + + + + + + - + + + + + - - - + + + + + - - + + + + + + - - - - - - - - - + + + + - - - + + + + + + + + - - - - + + + - - - + + + + - + - - + + + + + + + + + - - + @@ -117,6 +139,8 @@ + + @@ -163,20 +187,6 @@ - - - - - - - - - - - - - - @@ -261,6 +271,13 @@ + + + + + + + @@ -275,6 +292,18 @@ + + + + + + + + + + + + @@ -317,26 +346,22 @@ - - - - - - - - - - - - + + + + + + + + @@ -347,11 +372,16 @@ + + + + + @@ -363,6 +393,10 @@ + + + + @@ -374,7 +408,6 @@ - @@ -386,50 +419,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/config/github-ci-fail.xml b/build/config/github-ci-fail.xml deleted file mode 100644 index 94d1cda5331..00000000000 --- a/build/config/github-ci-fail.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - ../../tests/fail - - - - - - - diff --git a/build/config/php-scoper.php b/build/config/php-scoper.php index b9fbb781343..979bdaccd72 100644 --- a/build/config/php-scoper.php +++ b/build/config/php-scoper.php @@ -8,9 +8,12 @@ * file that was distributed with this source code. */ return [ + 'prefix' => 'PHPUnitPHAR', + 'exclude-namespaces' => [ 'PHPUnit', ], + 'expose-constants' => [ '/^__PHPUNIT_.+$/' ], diff --git a/build/scripts/extract-release-notes.php b/build/scripts/extract-release-notes.php new file mode 100755 index 00000000000..9383ff3abba --- /dev/null +++ b/build/scripts/extract-release-notes.php @@ -0,0 +1,54 @@ +#!/usr/bin/env php +' . PHP_EOL; + + exit(1); +} + +$version = $argv[1]; +$versionSeries = explode('.', $version)[0] . '.' . explode('.', $version)[1]; + +$file = __DIR__ . '/../../ChangeLog-' . $versionSeries . '.md'; + +if (!is_file($file) || !is_readable($file)) { + print $file . ' cannot be read' . PHP_EOL; + + exit(1); +} + +$buffer = ''; +$append = false; + +foreach (file($file) as $line) { + if (str_starts_with($line, '## [' . $version . ']')) { + $append = true; + + continue; + } + + if ($append && (str_starts_with($line, '## ') || str_starts_with($line, '['))) { + break; + } + + if ($append) { + $buffer .= $line; + } +} + +$buffer = trim($buffer); + +if ($buffer === '') { + print 'Unable to extract release notes' . PHP_EOL; + + exit(1); +} + +printf( + '%s%s---%s[How to install or update PHPUnit](https://docs.phpunit.de/en/%s/installation.html)%s', + $buffer, + PHP_EOL, + PHP_EOL, + $versionSeries, + PHP_EOL, +); diff --git a/build/scripts/generate-global-assert-wrappers.php b/build/scripts/generate-global-assert-wrappers.php index b7c654fafe6..d53bac1c421 100755 --- a/build/scripts/generate-global-assert-wrappers.php +++ b/build/scripts/generate-global-assert-wrappers.php @@ -64,6 +64,11 @@ $usedClasses[] = $returnType->getName(); + // skip, so we can later on append a signature including precise analysis types + if ($method->getName() === 'callback') { + continue; + } + $constraintMethods .= \sprintf( "if (!function_exists('PHPUnit\Framework\\" . $method->getName() . "')) {\n%s\n{\n return Assert::%s(...\\func_get_args());\n}\n}\n\n", \str_replace('final public static ', '', \trim($lines[$method->getStartLine() - 1])), @@ -105,6 +110,19 @@ $buffer .= $constraintMethods; $buffer .= <<<'EOT' +if (!function_exists('PHPUnit\Framework\callback')) { + /** + * @template CallbackInput of mixed + * + * @param callable(CallbackInput $callback): bool $callback + * + * @return Callback + */ + function callback(callable $callback): Callback + { + return Assert::callback($callback); + } +} if (!function_exists('PHPUnit\Framework\any')) { /** @@ -182,46 +200,6 @@ function atMost(int $allowedInvocations): InvokedAtMostCountMatcher } } -if (!function_exists('PHPUnit\Framework\returnValue')) { - function returnValue(mixed $value): ReturnStub - { - return new ReturnStub($value); - } -} - -if (!function_exists('PHPUnit\Framework\returnValueMap')) { - function returnValueMap(array $valueMap): ReturnValueMapStub - { - return new ReturnValueMapStub($valueMap); - } -} - -if (!function_exists('PHPUnit\Framework\returnArgument')) { - function returnArgument(int $argumentIndex): ReturnArgumentStub - { - return new ReturnArgumentStub($argumentIndex); - } -} - -if (!function_exists('PHPUnit\Framework\returnCallback')) { - function returnCallback(callable $callback): ReturnCallbackStub - { - return new ReturnCallbackStub($callback); - } -} - -if (!function_exists('PHPUnit\Framework\returnSelf')) { - /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ - function returnSelf(): ReturnSelfStub - { - return new ReturnSelfStub; - } -} - if (!function_exists('PHPUnit\Framework\throwException')) { function throwException(\Throwable $exception): ExceptionStub { @@ -229,18 +207,6 @@ function throwException(\Throwable $exception): ExceptionStub } } -if (!function_exists('PHPUnit\Framework\onConsecutiveCalls')) { - /** - * @param mixed $value , ... - */ - function onConsecutiveCalls(): ConsecutiveCallsStub - { - $arguments = \func_get_args(); - - return new ConsecutiveCallsStub($arguments); - } -} - EOT; \file_put_contents(__DIR__ . '/../../src/Framework/Assert/Functions.php', $buffer); diff --git a/build/scripts/phar-set-timestamps/composer.json b/build/scripts/phar-set-timestamps/composer.json new file mode 100644 index 00000000000..4369496d0e6 --- /dev/null +++ b/build/scripts/phar-set-timestamps/composer.json @@ -0,0 +1,8 @@ +{ + "require": { + "seld/phar-utils": "^1.2" + }, + "config": { + "optimize-autoloader": true + } +} diff --git a/build/scripts/phar-set-timestamps/composer.lock b/build/scripts/phar-set-timestamps/composer.lock new file mode 100644 index 00000000000..e3c10072c6e --- /dev/null +++ b/build/scripts/phar-set-timestamps/composer.lock @@ -0,0 +1,67 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "a2828bc624be51258ae32a2f2dbd5696", + "packages": [ + { + "name": "seld/phar-utils", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "time": "2022-08-31T10:31:18+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/build/scripts/phar-set-timestamps/run.php b/build/scripts/phar-set-timestamps/run.php new file mode 100755 index 00000000000..d845e2b9f3b --- /dev/null +++ b/build/scripts/phar-set-timestamps/run.php @@ -0,0 +1,54 @@ +#!/usr/bin/env php +&1'); + + if (is_string($tag) && strpos($tag, 'fatal') === false) { + $tmp = @shell_exec('git log -1 --format=%at ' . trim($tag) . ' 2>&1'); + + if (is_string($tag) && is_numeric(trim($tmp))) { + $epoch = (int) trim($tmp); + + printf( + 'Setting timestamp of files in PHAR to %d (based on when tag %s was created)' . PHP_EOL, + $epoch, + trim($tag) + ); + } + + unset($tmp); + } + + unset($tag); +} + +if (!isset($epoch)) { + $epoch = time(); + + printf( + 'Setting timestamp of files in PHAR to %d (based on current time)' . PHP_EOL, + $epoch + ); +} + +$timestamp = new DateTime; +$timestamp->setTimestamp($epoch); + +$util = new Timestamps($argv[1]); +$util->updateTimestamps($timestamp); +$util->save($argv[1], Phar::SHA512); diff --git a/build/scripts/phar-set-timestamps/vendor/autoload.php b/build/scripts/phar-set-timestamps/vendor/autoload.php new file mode 100644 index 00000000000..bb1037c7c3b --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/autoload.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/build/scripts/phar-set-timestamps/vendor/composer/InstalledVersions.php b/build/scripts/phar-set-timestamps/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000000..51e734a774b --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/InstalledVersions.php @@ -0,0 +1,359 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/build/scripts/phar-set-timestamps/vendor/composer/LICENSE b/build/scripts/phar-set-timestamps/vendor/composer/LICENSE new file mode 100644 index 00000000000..f27399a042d --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/build/scripts/phar-set-timestamps/vendor/composer/autoload_classmap.php b/build/scripts/phar-set-timestamps/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000000..e6a22096ae5 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/autoload_classmap.php @@ -0,0 +1,12 @@ + $vendorDir . '/composer/InstalledVersions.php', + 'Seld\\PharUtils\\Linter' => $vendorDir . '/seld/phar-utils/src/Linter.php', + 'Seld\\PharUtils\\Timestamps' => $vendorDir . '/seld/phar-utils/src/Timestamps.php', +); diff --git a/build/scripts/phar-set-timestamps/vendor/composer/autoload_namespaces.php b/build/scripts/phar-set-timestamps/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000000..15a2ff3ad6d --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/seld/phar-utils/src'), +); diff --git a/build/scripts/phar-set-timestamps/vendor/composer/autoload_real.php b/build/scripts/phar-set-timestamps/vendor/composer/autoload_real.php new file mode 100644 index 00000000000..69cfbf7e33c --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/autoload_real.php @@ -0,0 +1,38 @@ +register(true); + + return $loader; + } +} diff --git a/build/scripts/phar-set-timestamps/vendor/composer/autoload_static.php b/build/scripts/phar-set-timestamps/vendor/composer/autoload_static.php new file mode 100644 index 00000000000..201af17c152 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/autoload_static.php @@ -0,0 +1,38 @@ + + array ( + 'Seld\\PharUtils\\' => 15, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Seld\\PharUtils\\' => + array ( + 0 => __DIR__ . '/..' . '/seld/phar-utils/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Seld\\PharUtils\\Linter' => __DIR__ . '/..' . '/seld/phar-utils/src/Linter.php', + 'Seld\\PharUtils\\Timestamps' => __DIR__ . '/..' . '/seld/phar-utils/src/Timestamps.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit386a05f6676643b8b2eb49288e20d079::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit386a05f6676643b8b2eb49288e20d079::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit386a05f6676643b8b2eb49288e20d079::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/build/scripts/phar-set-timestamps/vendor/composer/installed.json b/build/scripts/phar-set-timestamps/vendor/composer/installed.json new file mode 100644 index 00000000000..44d066d18b1 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/installed.json @@ -0,0 +1,57 @@ +{ + "packages": [ + { + "name": "seld/phar-utils", + "version": "1.2.1", + "version_normalized": "1.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "time": "2022-08-31T10:31:18+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "install-path": "../seld/phar-utils" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/build/scripts/phar-set-timestamps/vendor/composer/installed.php b/build/scripts/phar-set-timestamps/vendor/composer/installed.php new file mode 100644 index 00000000000..6f559f8743b --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/installed.php @@ -0,0 +1,32 @@ + array( + 'name' => '__root__', + 'pretty_version' => '8.5.x-dev', + 'version' => '8.5.9999999.9999999-dev', + 'reference' => 'aca96fcd8b6799ead1066524b2b91c5184ece78f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => '8.5.x-dev', + 'version' => '8.5.9999999.9999999-dev', + 'reference' => 'aca96fcd8b6799ead1066524b2b91c5184ece78f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'seld/phar-utils' => array( + 'pretty_version' => '1.2.1', + 'version' => '1.2.1.0', + 'reference' => 'ea2f4014f163c1be4c601b9b7bd6af81ba8d701c', + 'type' => 'library', + 'install_path' => __DIR__ . '/../seld/phar-utils', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/build/scripts/phar-set-timestamps/vendor/composer/platform_check.php b/build/scripts/phar-set-timestamps/vendor/composer/platform_check.php new file mode 100644 index 00000000000..7621d4ff97f --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 50300)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 5.3.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/.gitignore b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/.gitignore new file mode 100644 index 00000000000..42cd73d9573 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/.gitignore @@ -0,0 +1 @@ +/vendor/ \ No newline at end of file diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/LICENSE b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/LICENSE new file mode 100644 index 00000000000..c1b62a35fa3 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/README.md b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/README.md new file mode 100644 index 00000000000..27edf743276 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/README.md @@ -0,0 +1,45 @@ +PHAR Utils +========== + +PHAR file format utilities, for when PHP phars you up. + +Installation +------------ + +`composer require seld/phar-utils` + +API +--- + +### `Seld\PharUtils\Timestamps` + +- `__construct($pharFile)` + + > Load a phar file in memory. + +- `updateTimestamps($timestamp = null)` + + > Updates each file's unix timestamps in the PHAR so the PHAR signature + > can be produced in a reproducible manner. + +- `save($path, $signatureAlgo = '')` + + > Saves the updated phar file with an updated signature. + > Algo must be one of `Phar::MD5`, `Phar::SHA1`, `Phar::SHA256` + > or `Phar::SHA512` + +### `Seld\PharUtils\Linter` + +- `Linter::lint($pharFile)` + + > Lints all php files inside a given phar with the current PHP version. + +Requirements +------------ + +PHP 5.3 and above + +License +------- + +PHAR Utils is licensed under the MIT License - see the LICENSE file for details diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/composer.json b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/composer.json new file mode 100644 index 00000000000..8b1f7f2b68a --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/composer.json @@ -0,0 +1,26 @@ +{ + "name": "seld/phar-utils", + "description": "PHAR file format utilities, for when PHP phars you up", + "type": "library", + "keywords": ["phar"], + "license": "MIT", + "require": { + "php": ">=5.3" + }, + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + } +} diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/composer.lock b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/composer.lock new file mode 100644 index 00000000000..21e33c7a217 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/composer.lock @@ -0,0 +1,19 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "e5afe72073d9266712c8e1ddc1648513", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3" + }, + "platform-dev": [] +} diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/src/Linter.php b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/src/Linter.php new file mode 100644 index 00000000000..935d04e8411 --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/src/Linter.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Seld\PharUtils; + +class Linter +{ + /** + * Lints all php files inside a given phar with the current PHP version + * + * @param string $path Phar file path + * @param list $excludedPaths Paths which should be skipped by the linter + */ + public static function lint($path, array $excludedPaths = array()) + { + $php = defined('PHP_BINARY') ? PHP_BINARY : 'php'; + + if ($isWindows = defined('PHP_WINDOWS_VERSION_BUILD')) { + $tmpFile = @tempnam(sys_get_temp_dir(), ''); + + if (!$tmpFile || !is_writable($tmpFile)) { + throw new \RuntimeException('Unable to create temp file'); + } + + $php = self::escapeWindowsPath($php); + $tmpFile = self::escapeWindowsPath($tmpFile); + + // PHP 8 encloses the command in double-quotes + if (PHP_VERSION_ID >= 80000) { + $format = '%s -l %s'; + } else { + $format = '"%s -l %s"'; + } + + $command = sprintf($format, $php, $tmpFile); + } else { + $command = "'".$php."' -l"; + } + + $descriptorspec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ); + + // path to phar + phar:// + trailing slash + $baseLen = strlen(realpath($path)) + 7 + 1; + foreach (new \RecursiveIteratorIterator(new \Phar($path)) as $file) { + if ($file->isDir()) { + continue; + } + if (substr($file, -4) === '.php') { + $filename = (string) $file; + if (in_array(substr($filename, $baseLen), $excludedPaths, true)) { + continue; + } + if ($isWindows) { + file_put_contents($tmpFile, file_get_contents($filename)); + } + + $process = proc_open($command, $descriptorspec, $pipes); + if (is_resource($process)) { + if (!$isWindows) { + fwrite($pipes[0], file_get_contents($filename)); + } + fclose($pipes[0]); + + $stdout = stream_get_contents($pipes[1]); + fclose($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[2]); + + $exitCode = proc_close($process); + + if ($exitCode !== 0) { + if ($isWindows) { + $stderr = str_replace($tmpFile, $filename, $stderr); + } + throw new \UnexpectedValueException('Failed linting '.$file.': '.$stderr); + } + } else { + throw new \RuntimeException('Could not start linter process'); + } + } + } + + if ($isWindows) { + @unlink($tmpFile); + } + } + + /** + * Escapes a Windows file path + * + * @param string $path + * @return string The escaped path + */ + private static function escapeWindowsPath($path) + { + // Quote if path contains spaces or brackets + if (strpbrk($path, " ()") !== false) { + $path = '"'.$path.'"'; + } + + return $path; + } +} diff --git a/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/src/Timestamps.php b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/src/Timestamps.php new file mode 100644 index 00000000000..8077d5b851d --- /dev/null +++ b/build/scripts/phar-set-timestamps/vendor/seld/phar-utils/src/Timestamps.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Seld\PharUtils; + +class Timestamps +{ + private $contents; + + /** + * @param string $file path to the phar file to use + */ + public function __construct($file) + { + $this->contents = file_get_contents($file); + } + + /** + * Updates each file's unix timestamps in the PHAR + * + * The PHAR signature can then be produced in a reproducible manner. + * + * @param int|\DateTimeInterface|string $timestamp Date string or DateTime or unix timestamp to use + */ + public function updateTimestamps($timestamp = null) + { + if ($timestamp instanceof \DateTime || $timestamp instanceof \DateTimeInterface) { + $timestamp = $timestamp->getTimestamp(); + } elseif (is_string($timestamp)) { + $timestamp = strtotime($timestamp); + } elseif (!is_int($timestamp)) { + $timestamp = strtotime('1984-12-24T00:00:00Z'); + } + + // detect manifest offset / end of stub + if (!preg_match('{__HALT_COMPILER\(\);(?: +\?>)?\r?\n}', $this->contents, $match, PREG_OFFSET_CAPTURE)) { + throw new \RuntimeException('Could not detect the stub\'s end in the phar'); + } + + // set starting position and skip past manifest length + $pos = $match[0][1] + strlen($match[0][0]); + $stubEnd = $pos + $this->readUint($pos, 4); + $pos += 4; + + $numFiles = $this->readUint($pos, 4); + $pos += 4; + + // skip API version (YOLO) + $pos += 2; + + // skip PHAR flags + $pos += 4; + + $aliasLength = $this->readUint($pos, 4); + $pos += 4 + $aliasLength; + + $metadataLength = $this->readUint($pos, 4); + $pos += 4 + $metadataLength; + + while ($pos < $stubEnd) { + $filenameLength = $this->readUint($pos, 4); + $pos += 4 + $filenameLength; + + // skip filesize + $pos += 4; + + // update timestamp to a fixed value + $timeStampBytes = pack('L', $timestamp); + $this->contents[$pos + 0] = $timeStampBytes[0]; + $this->contents[$pos + 1] = $timeStampBytes[1]; + $this->contents[$pos + 2] = $timeStampBytes[2]; + $this->contents[$pos + 3] = $timeStampBytes[3]; + + // skip timestamp, compressed file size, crc32 checksum and file flags + $pos += 4*4; + + $metadataLength = $this->readUint($pos, 4); + $pos += 4 + $metadataLength; + + $numFiles--; + } + + if ($numFiles !== 0) { + throw new \LogicException('All files were not processed, something must have gone wrong'); + } + } + + /** + * Saves the updated phar file, optionally with an updated signature. + * + * @param string $path + * @param int $signatureAlgo One of Phar::MD5, Phar::SHA1, Phar::SHA256 or Phar::SHA512 + * @return bool + */ + public function save($path, $signatureAlgo) + { + $pos = $this->determineSignatureBegin(); + + $algos = array( + \Phar::MD5 => 'md5', + \Phar::SHA1 => 'sha1', + \Phar::SHA256 => 'sha256', + \Phar::SHA512 => 'sha512', + ); + + if (!isset($algos[$signatureAlgo])) { + throw new \UnexpectedValueException('Invalid hash algorithm given: '.$signatureAlgo.' expected one of Phar::MD5, Phar::SHA1, Phar::SHA256 or Phar::SHA512'); + } + $algo = $algos[$signatureAlgo]; + + // re-sign phar + // signature + $signature = hash($algo, substr($this->contents, 0, $pos), true) + // sig type + . pack('L', $signatureAlgo) + // ohai Greg & Marcus + . 'GBMB'; + + $this->contents = substr($this->contents, 0, $pos) . $signature; + + return file_put_contents($path, $this->contents); + } + + private function readUint($pos, $bytes) + { + $res = unpack('V', substr($this->contents, $pos, $bytes)); + + return $res[1]; + } + + /** + * Determine the beginning of the signature. + * + * @return int + */ + private function determineSignatureBegin() + { + // detect signature position + if (!preg_match('{__HALT_COMPILER\(\);(?: +\?>)?\r?\n}', $this->contents, $match, PREG_OFFSET_CAPTURE)) { + throw new \RuntimeException('Could not detect the stub\'s end in the phar'); + } + + // set starting position and skip past manifest length + $pos = $match[0][1] + strlen($match[0][0]); + $manifestEnd = $pos + 4 + $this->readUint($pos, 4); + + $pos += 4; + $numFiles = $this->readUint($pos, 4); + + $pos += 4; + + // skip API version (YOLO) + $pos += 2; + + // skip PHAR flags + $pos += 4; + + $aliasLength = $this->readUint($pos, 4); + $pos += 4 + $aliasLength; + + $metadataLength = $this->readUint($pos, 4); + $pos += 4 + $metadataLength; + + $compressedSizes = 0; + while (($numFiles > 0) && ($pos < $manifestEnd - 24)) { + $filenameLength = $this->readUint($pos, 4); + $pos += 4 + $filenameLength; + + // skip filesize and timestamp + $pos += 2*4; + + $compressedSizes += $this->readUint($pos, 4); + // skip compressed file size, crc32 checksum and file flags + $pos += 3*4; + + $metadataLength = $this->readUint($pos, 4); + $pos += 4 + $metadataLength; + + $numFiles--; + } + + if ($numFiles !== 0) { + throw new \LogicException('All files were not processed, something must have gone wrong'); + } + + return $manifestEnd + $compressedSizes; + } +} diff --git a/build/scripts/phar-version.php b/build/scripts/phar-version.php index 1a91016553e..948c3b21b09 100755 --- a/build/scripts/phar-version.php +++ b/build/scripts/phar-version.php @@ -8,8 +8,8 @@ \file_put_contents( __DIR__ . '/../tmp/phar/phpunit/Runner/Version.php', \str_replace( - 'private static $pharVersion = \'\';', - 'private static $pharVersion = \'' . $argv[1] . '\';', + 'private static string $pharVersion = \'\';', + 'private static string $pharVersion = \'' . $argv[1] . '\';', \file_get_contents(__DIR__ . '/../tmp/phar/phpunit/Runner/Version.php') ), \LOCK_EX diff --git a/build/templates/binary-phar-autoload.php.in b/build/templates/binary-phar-autoload.php.in index 4dfcbb20b5d..ce8b60ed8d9 100644 --- a/build/templates/binary-phar-autoload.php.in +++ b/build/templates/binary-phar-autoload.php.in @@ -15,12 +15,12 @@ if (!version_compare(PHP_VERSION, PHP_VERSION, '=')) { die(1); } -if (version_compare('8.2.0', PHP_VERSION, '>')) { +if (version_compare('8.3.0', PHP_VERSION, '>')) { fwrite( STDERR, sprintf( 'PHPUnit X.Y.Z by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . - 'This version of PHPUnit requires PHP >= 8.2.' . PHP_EOL . + 'This version of PHPUnit requires PHP >= 8.3.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, PHP_VERSION, PHP_BINARY @@ -30,7 +30,7 @@ if (version_compare('8.2.0', PHP_VERSION, '>')) { die(1); } -$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; +$requiredExtensions = ['ctype', 'dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; $unavailableExtensions = array_filter( $requiredExtensions, @@ -61,9 +61,11 @@ if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { $execute = false; } -$options = getopt('', array('manifest', 'sbom')); +$options = getopt('', array('composer-lock', 'manifest', 'sbom')); -if (isset($options['manifest'])) { +if (isset($options['composer-lock'])) { + $printComposerLock = true; +} elseif (isset($options['manifest'])) { $printManifest = true; } elseif (isset($options['sbom'])) { $printSbom = true; @@ -99,6 +101,12 @@ foreach ([___CLASSLIST___] as $file) { require __PHPUNIT_PHAR_ROOT__ . '/phpunit/Framework/Assert/Functions.php'; if ($execute) { + if (isset($printComposerLock)) { + print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/composer.lock'); + + exit; + } + if (isset($printManifest)) { print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); diff --git a/build/test-extension/manifest.xml b/build/test-extension/manifest.xml index c9f4caf1c03..d3c05ba2d2d 100644 --- a/build/test-extension/manifest.xml +++ b/build/test-extension/manifest.xml @@ -1,7 +1,7 @@ - + diff --git a/composer.json b/composer.json index 5cb0b9ba550..a4eba5d77ea 100644 --- a/composer.json +++ b/composer.json @@ -22,43 +22,40 @@ }, "prefer-stable": true, "require": { - "php": ">=8.2", + "php": ">=8.3", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "phpunit/php-code-coverage": "^12.2.1", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.0.1", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.0", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.2", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "config": { "platform": { - "php": "8.2.0" + "php": "8.3.0" }, + "classmap-authoritative": true, "optimize-autoloader": true, "sort-packages": true }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, "bin": [ "phpunit" ], @@ -72,10 +69,41 @@ }, "autoload-dev": { "classmap": [ - "tests/" + "tests/_files" ], "files": [ - "tests/_files/CoverageNamespacedFunctionTest.php", + "tests/_files/deprecation-trigger/trigger_deprecation.php", + "tests/unit/Event/AbstractEventTestCase.php", + "tests/unit/TextUI/AbstractSouceFilterTestCase.php", + "tests/unit/Framework/MockObject/TestDoubleTestCase.php", + "tests/unit/Metadata/Parser/AttributeParserTestCase.php", + "tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyBoolTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyCallableTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyFloatTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyInstancesOfTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyIntTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyIterableTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyNullTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyNumericTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyObjectTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyResourceTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyClosedResourceTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyScalarTest.php", + "tests/unit/Framework/Assert/assertContainsOnlyStringTest.php", + "tests/unit/Framework/Assert/assertDirectoryExistsTest.php", + "tests/unit/Framework/Assert/assertFileExistsTest.php", + "tests/unit/Framework/Assert/assertIsNumericTest.php", + "tests/unit/Framework/Assert/assertIsObjectTest.php", + "tests/unit/Framework/Assert/assertIsReadableTest.php", + "tests/unit/Framework/Assert/assertIsResourceTest.php", + "tests/unit/Framework/Assert/assertIsScalarTest.php", + "tests/unit/Framework/Assert/assertIsStringTest.php", + "tests/unit/Framework/Assert/assertIsWritableTest.php", + "tests/unit/Framework/Assert/assertMatchesRegularExpressionTest.php", + "tests/unit/Framework/Assert/assertNullTest.php", + "tests/unit/Framework/Assert/assertSameSizeTest.php", + "tests/unit/Framework/Assert/assertSameTest.php", "tests/_files/CoveredFunction.php", "tests/_files/Generator.php", "tests/_files/NamespaceCoveredFunction.php" @@ -83,7 +111,7 @@ }, "extra": { "branch-alias": { - "dev-main": "11.0-dev" + "dev-main": "12.2-dev" } } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000000..ee481207f35 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1515 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "08406d092d8297e7c8fa686a024113e4", + "packages": [ + { + "name": "myclabs/deep-copy", + "version": "1.13.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-04-29T12:36:36+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + }, + "time": "2024-12-30T11:07:19+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "448f2c504d86dbff3949dcd02c95aa85db2c7617" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/448f2c504d86dbff3949dcd02c95aa85db2c7617", + "reference": "448f2c504d86dbff3949dcd02c95aa85db2c7617", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-05-04T05:25:05+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:37+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:53:50+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b478f34614f934e0291598d0c08cbaba9644bee5", + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-07T07:00:32+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8afe311eca49171bf95405cc0078be9a3821f9f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8afe311eca49171bf95405cc0078be9a3821f9f2", + "reference": "8afe311eca49171bf95405cc0078be9a3821f9f2", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:08+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:42+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:59+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:28+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:01+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069", + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-18T13:37:31+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.3", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*" + }, + "platform-dev": {}, + "platform-overrides": { + "php": "8.3.0" + }, + "plugin-api-version": "2.6.0" +} diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000000..bb657e19fa4 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,65 @@ +parameters: + level: 6 + + paths: + - src + + excludePaths: + # exclude partial traits, which are only used in runtime generated code + - src/Framework/MockObject/Runtime/Api + + checkTooWideReturnTypesInProtectedAndPublicMethods: true + reportAlwaysTrueInLastCondition: true + reportPossiblyNonexistentConstantArrayOffset: true + reportPossiblyNonexistentGeneralArrayOffset: true + treatPhpDocTypesAsCertain: false + + strictRules: + allRules: false + booleansInConditions: true + closureUsesThis: true + disallowedBacktick: true + disallowedEmpty: true + disallowedImplicitArrayCreation: true + disallowedLooseComparison: true + disallowedShortTernary: true + illegalConstructorMethodCall: true + matchingInheritedMethodNames: true + noVariableVariables: true + numericOperandsInArithmeticOperators: true + overwriteVariablesWithLoop: true + requireParentConstructorCall: true + strictArrayFilter: true + strictFunctionCalls: true + switchConditionsMatchingType: true + uselessCast: true + + ergebnis: + allRules: false + final: + enabled: true + classesNotRequiredToBeAbstractOrFinal: + - PHPUnit\Framework\Constraint\Count + - PHPUnit\Framework\AssertionFailedError + - PHPUnit\Framework\Exception + - PHPUnit\Framework\TestSuite + privateInFinalClass: + enabled: true + + type_coverage: + declare: 100 + return: 100 + param: 100 + property: 100 + constant: 100 + + ignoreErrors: + # ignore errors caused by defensive programming + - '#Call to function assert\(\) with true will always evaluate to true.#' + - '#Instanceof between .* and .* will always evaluate to true.#' + - '#Strict comparison using !== between .*non-empty-string.* and .* will always evaluate to true.#' + - '#Strict comparison using !== between .*non-falsy-string.* and .* will always evaluate to true.#' + - identifier: argument.named + +includes: + - phar://phpstan.phar/conf/bleedingEdge.neon diff --git a/phpunit b/phpunit index 860a1910329..84afaf14c1a 100755 --- a/phpunit +++ b/phpunit @@ -24,11 +24,11 @@ if (!version_compare(PHP_VERSION, PHP_VERSION, '=')) { die(1); } -if (version_compare('8.2.0', PHP_VERSION, '>')) { +if (version_compare('8.3.0', PHP_VERSION, '>')) { fwrite( STDERR, sprintf( - 'This version of PHPUnit requires PHP >= 8.2.' . PHP_EOL . + 'This version of PHPUnit requires PHP >= 8.3.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, PHP_VERSION, PHP_BINARY @@ -38,31 +38,6 @@ if (version_compare('8.2.0', PHP_VERSION, '>')) { die(1); } -$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; - -$unavailableExtensions = array_filter( - $requiredExtensions, - static function ($extension) { - return !extension_loaded($extension); - } -); - -if ([] !== $unavailableExtensions) { - fwrite( - STDERR, - sprintf( - 'PHPUnit requires the "%s" extensions, but the "%s" %s not available.' . PHP_EOL, - implode('", "', $requiredExtensions), - implode('", "', $unavailableExtensions), - count($unavailableExtensions) === 1 ? 'extension is' : 'extensions are' - ) - ); - - die(1); -} - -unset($requiredExtensions, $unavailableExtensions); - if (!ini_get('date.timezone')) { ini_set('date.timezone', 'UTC'); } @@ -96,4 +71,34 @@ if (!defined('PHPUNIT_COMPOSER_INSTALL')) { require PHPUNIT_COMPOSER_INSTALL; +$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; + +$unavailableExtensions = array_filter( + $requiredExtensions, + static function ($extension) { + return !extension_loaded($extension); + } +); + +// Workaround for https://github.com/sebastianbergmann/phpunit/issues/5662 +if (!function_exists('ctype_alnum')) { + $unavailableExtensions[] = 'ctype'; +} + +if ([] !== $unavailableExtensions) { + fwrite( + STDERR, + sprintf( + 'PHPUnit requires the "%s" extensions, but the "%s" %s not available.' . PHP_EOL, + implode('", "', $requiredExtensions), + implode('", "', $unavailableExtensions), + count($unavailableExtensions) === 1 ? 'extension is' : 'extensions are' + ) + ); + + die(1); +} + +unset($requiredExtensions, $unavailableExtensions); + exit((new PHPUnit\TextUI\Application)->run($_SERVER['argv'])); diff --git a/phpunit.xml b/phpunit.xml index cabc86fa32b..17257d4d34a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,21 +15,31 @@ tests/end-to-end/baseline tests/end-to-end/cli + tests/end-to-end/data-provider + tests/end-to-end/deprecation-trigger tests/end-to-end/event tests/end-to-end/execution-order - tests/end-to-end/extension + tests/end-to-end/extension-cli + tests/end-to-end/extension-xml tests/end-to-end/generic - tests/end-to-end/logging + tests/end-to-end/groups-from-configuration + tests/end-to-end/logging/junit + tests/end-to-end/logging/teamcity + tests/end-to-end/logging/testdox + tests/end-to-end/metadata tests/end-to-end/migration tests/end-to-end/mock-objects tests/end-to-end/phpt tests/end-to-end/regression + tests/end-to-end/self-direct-indirect tests/end-to-end/testdox tests/end-to-end/event/_files tests/end-to-end/execution-order/_files + tests/end-to-end/groups-from-configuration/_files tests/end-to-end/logging/_files tests/end-to-end/migration/_files + tests/end-to-end/self-direct-indirect/_files tests/end-to-end/testdox/_files diff --git a/phpunit.xsd b/phpunit.xsd index aa8721eb5d5..1f66c40b654 100644 --- a/phpunit.xsd +++ b/phpunit.xsd @@ -2,7 +2,7 @@ - This Schema file defines the rules by which the XML configuration file of PHPUnit 11.0 may be structured. + This Schema file defines the rules by which the XML configuration file of PHPUnit 12.2 may be structured. @@ -23,9 +23,9 @@ + - @@ -35,6 +35,9 @@ + + + @@ -60,7 +63,6 @@ - @@ -170,14 +172,16 @@ - + + + @@ -196,7 +200,6 @@ - @@ -205,18 +208,22 @@ + - + + + + @@ -260,6 +267,7 @@ + @@ -267,6 +275,7 @@ + @@ -319,4 +328,12 @@ + + + + + + + + diff --git a/schema/10.1.xsd b/schema/10.1.xsd new file mode 100644 index 00000000000..1b190c21b90 --- /dev/null +++ b/schema/10.1.xsd @@ -0,0 +1,312 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.1 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/10.2.xsd b/schema/10.2.xsd new file mode 100644 index 00000000000..269b7a3aeb4 --- /dev/null +++ b/schema/10.2.xsd @@ -0,0 +1,319 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.2 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/10.3.xsd b/schema/10.3.xsd new file mode 100644 index 00000000000..03a54ee0b08 --- /dev/null +++ b/schema/10.3.xsd @@ -0,0 +1,321 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.3 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/10.4.xsd b/schema/10.4.xsd new file mode 100644 index 00000000000..bd22b2ca2a7 --- /dev/null +++ b/schema/10.4.xsd @@ -0,0 +1,322 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.4 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/10.5.xsd b/schema/10.5.xsd new file mode 100644 index 00000000000..eab82db2b91 --- /dev/null +++ b/schema/10.5.xsd @@ -0,0 +1,326 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 10.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/11.0.xsd b/schema/11.0.xsd new file mode 100644 index 00000000000..a6e7cb8cd16 --- /dev/null +++ b/schema/11.0.xsd @@ -0,0 +1,323 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 11.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/11.1.xsd b/schema/11.1.xsd new file mode 100644 index 00000000000..6172e834933 --- /dev/null +++ b/schema/11.1.xsd @@ -0,0 +1,333 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 11.1 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/11.2.xsd b/schema/11.2.xsd new file mode 100644 index 00000000000..d7c7dcac065 --- /dev/null +++ b/schema/11.2.xsd @@ -0,0 +1,331 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 11.2 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/11.3.xsd b/schema/11.3.xsd new file mode 100644 index 00000000000..3b30de4d6f7 --- /dev/null +++ b/schema/11.3.xsd @@ -0,0 +1,335 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 11.3 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/11.4.xsd b/schema/11.4.xsd new file mode 100644 index 00000000000..52db363026a --- /dev/null +++ b/schema/11.4.xsd @@ -0,0 +1,334 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 11.4 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/11.5.xsd b/schema/11.5.xsd new file mode 100644 index 00000000000..f19049d8519 --- /dev/null +++ b/schema/11.5.xsd @@ -0,0 +1,336 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 11.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/12.0.xsd b/schema/12.0.xsd new file mode 100644 index 00000000000..c993640f9fb --- /dev/null +++ b/schema/12.0.xsd @@ -0,0 +1,335 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 12.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/12.1.xsd b/schema/12.1.xsd new file mode 100644 index 00000000000..51f225d45ab --- /dev/null +++ b/schema/12.1.xsd @@ -0,0 +1,339 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 12.1 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/8.5.xsd b/schema/8.5.xsd index 5881bafec62..75e22289471 100644 --- a/schema/8.5.xsd +++ b/schema/8.5.xsd @@ -244,7 +244,7 @@ - + @@ -309,8 +309,10 @@ - - + + + + diff --git a/schema/9.0.xsd b/schema/9.0.xsd new file mode 100644 index 00000000000..6db04c09789 --- /dev/null +++ b/schema/9.0.xsd @@ -0,0 +1,315 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/9.1.xsd b/schema/9.1.xsd new file mode 100644 index 00000000000..b10d30b4619 --- /dev/null +++ b/schema/9.1.xsd @@ -0,0 +1,317 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.0 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/9.2.xsd b/schema/9.2.xsd index 883f21bc0d1..d770e8b03bd 100644 --- a/schema/9.2.xsd +++ b/schema/9.2.xsd @@ -163,7 +163,7 @@ - + @@ -248,7 +248,7 @@ - + @@ -282,7 +282,7 @@ - + @@ -310,7 +310,7 @@ - + diff --git a/schema/9.3.xsd b/schema/9.3.xsd new file mode 100644 index 00000000000..638f663ac7b --- /dev/null +++ b/schema/9.3.xsd @@ -0,0 +1,327 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.3 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/9.4.xsd b/schema/9.4.xsd new file mode 100644 index 00000000000..75a91e832f8 --- /dev/null +++ b/schema/9.4.xsd @@ -0,0 +1,328 @@ + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 9.4 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/9.5.xsd b/schema/9.5.xsd index a25d683a434..eabefac30b0 100644 --- a/schema/9.5.xsd +++ b/schema/9.5.xsd @@ -213,7 +213,7 @@ - + @@ -279,8 +279,10 @@ - - + + + + diff --git a/src/Event/Dispatcher/CollectingDispatcher.php b/src/Event/Dispatcher/CollectingDispatcher.php index 39433fe3fc6..c431b93e6a5 100644 --- a/src/Event/Dispatcher/CollectingDispatcher.php +++ b/src/Event/Dispatcher/CollectingDispatcher.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CollectingDispatcher implements Dispatcher diff --git a/src/Event/Dispatcher/DeferringDispatcher.php b/src/Event/Dispatcher/DeferringDispatcher.php index 8c67d2df8e9..6895facb386 100644 --- a/src/Event/Dispatcher/DeferringDispatcher.php +++ b/src/Event/Dispatcher/DeferringDispatcher.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DeferringDispatcher implements SubscribableDispatcher diff --git a/src/Event/Dispatcher/DirectDispatcher.php b/src/Event/Dispatcher/DirectDispatcher.php index 042b24180c2..b5cbc8e31c1 100644 --- a/src/Event/Dispatcher/DirectDispatcher.php +++ b/src/Event/Dispatcher/DirectDispatcher.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Event; +use const PHP_EOL; use function array_key_exists; use function dirname; use function sprintf; @@ -16,6 +17,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DirectDispatcher implements SubscribableDispatcher @@ -23,12 +26,12 @@ final class DirectDispatcher implements SubscribableDispatcher private readonly TypeMap $typeMap; /** - * @psalm-var array> + * @var array> */ private array $subscribers = []; /** - * @psalm-var list + * @var list */ private array $tracers = []; @@ -86,9 +89,11 @@ public function dispatch(Event $event): void foreach ($this->tracers as $tracer) { try { $tracer->trace($event); + // @codeCoverageIgnoreStart } catch (Throwable $t) { $this->handleThrowable($t); } + // @codeCoverageIgnoreEnd } if (!array_key_exists($eventClassName, $this->subscribers)) { @@ -97,6 +102,7 @@ public function dispatch(Event $event): void foreach ($this->subscribers[$eventClassName] as $subscriber) { try { + /** @phpstan-ignore method.notFound */ $subscriber->notify($event); } catch (Throwable $t) { $this->handleThrowable($t); @@ -110,7 +116,7 @@ public function dispatch(Event $event): void public function handleThrowable(Throwable $t): void { if ($this->isThrowableFromThirdPartySubscriber($t)) { - Facade::emitter()->testRunnerTriggeredWarning( + Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Exception in third-party event subscriber: %s%s%s', $t->getMessage(), @@ -122,7 +128,9 @@ public function handleThrowable(Throwable $t): void return; } + // @codeCoverageIgnoreStart throw $t; + // @codeCoverageIgnoreEnd } private function isThrowableFromThirdPartySubscriber(Throwable $t): bool diff --git a/src/Event/Dispatcher/Dispatcher.php b/src/Event/Dispatcher/Dispatcher.php index 35691be71dc..e7086539539 100644 --- a/src/Event/Dispatcher/Dispatcher.php +++ b/src/Event/Dispatcher/Dispatcher.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Dispatcher diff --git a/src/Event/Dispatcher/SubscribableDispatcher.php b/src/Event/Dispatcher/SubscribableDispatcher.php index 067c0cc6e60..c4393da1249 100644 --- a/src/Event/Dispatcher/SubscribableDispatcher.php +++ b/src/Event/Dispatcher/SubscribableDispatcher.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface SubscribableDispatcher extends Dispatcher diff --git a/src/Event/Emitter/DispatchingEmitter.php b/src/Event/Emitter/DispatchingEmitter.php index d47538b5b5e..ec881ccbbb9 100644 --- a/src/Event/Emitter/DispatchingEmitter.php +++ b/src/Event/Emitter/DispatchingEmitter.php @@ -10,8 +10,10 @@ namespace PHPUnit\Event; use function assert; +use function memory_reset_peak_usage; use PHPUnit\Event\Code\ClassMethod; use PHPUnit\Event\Code\ComparisonFailure; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException; use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\TestMethodBuilder; @@ -25,11 +27,11 @@ use PHPUnit\Event\TestSuite\Sorted as TestSuiteSorted; use PHPUnit\Event\TestSuite\Started as TestSuiteStarted; use PHPUnit\Event\TestSuite\TestSuite; -use PHPUnit\Framework\Constraint; use PHPUnit\TextUI\Configuration\Configuration; -use PHPUnit\Util\Exporter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DispatchingEmitter implements Emitter @@ -38,7 +40,6 @@ final class DispatchingEmitter implements Emitter private readonly Telemetry\System $system; private readonly Telemetry\Snapshot $startSnapshot; private Telemetry\Snapshot $previousSnapshot; - private bool $exportObjects = false; public function __construct(Dispatcher $dispatcher, Telemetry\System $system) { @@ -46,17 +47,7 @@ public function __construct(Dispatcher $dispatcher, Telemetry\System $system) $this->system = $system; $this->startSnapshot = $system->snapshot(); - $this->previousSnapshot = $system->snapshot(); - } - - public function exportObjects(): void - { - $this->exportObjects = true; - } - - public function exportsObjects(): bool - { - return $this->exportObjects; + $this->previousSnapshot = $this->startSnapshot; } /** @@ -101,6 +92,8 @@ public function testRunnerConfigured(Configuration $configuration): void } /** + * @param non-empty-string $filename + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -115,6 +108,10 @@ public function testRunnerBootstrapFinished(string $filename): void } /** + * @param non-empty-string $filename + * @param non-empty-string $name + * @param non-empty-string $version + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -131,8 +128,8 @@ public function testRunnerLoadedExtensionFromPhar(string $filename, string $name } /** - * @psalm-param class-string $className - * @psalm-param array $parameters + * @param class-string $className + * @param array $parameters * * @throws InvalidArgumentException * @throws UnknownEventTypeException @@ -271,6 +268,24 @@ public function testRunnerTriggeredGarbageCollection(): void ); } + public function testRunnerStartedChildProcess(): void + { + $this->dispatcher->dispatch( + new TestRunner\ChildProcessStarted($this->telemetryInfo()), + ); + } + + public function testRunnerFinishedChildProcess(string $stdout, string $stderr): void + { + $this->dispatcher->dispatch( + new TestRunner\ChildProcessFinished( + $this->telemetryInfo(), + $stdout, + $stderr, + ), + ); + } + /** * @throws InvalidArgumentException * @throws UnknownEventTypeException @@ -318,95 +333,95 @@ public function testPreparationStarted(Code\Test $test): void * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testPreparationFailed(Code\Test $test): void + public function testPreparationErrored(Code\Test $test, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\PreparationFailed( + new Test\PreparationErrored( $this->telemetryInfo(), $test, + $throwable, ), ); } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testBeforeFirstTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void + public function testPreparationFailed(Code\Test $test, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\BeforeFirstTestMethodCalled( + new Test\PreparationFailed( $this->telemetryInfo(), - $testClassName, - $calledMethod, + $test, + $throwable, ), ); } /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testBeforeFirstTestMethodErrored(string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable): void + public function beforeFirstTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void { $this->dispatcher->dispatch( - new Test\BeforeFirstTestMethodErrored( + new Test\BeforeFirstTestMethodCalled( $this->telemetryInfo(), $testClassName, $calledMethod, - $throwable, ), ); } /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testBeforeFirstTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void + public function beforeFirstTestMethodErrored(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\BeforeFirstTestMethodFinished( + new Test\BeforeFirstTestMethodErrored( $this->telemetryInfo(), $testClassName, - ...$calledMethods, + $calledMethod, + $throwable, ), ); } /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testBeforeTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void + public function beforeFirstTestMethodFailed(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\BeforeTestMethodCalled( + new Test\BeforeFirstTestMethodFailed( $this->telemetryInfo(), $testClassName, $calledMethod, + $throwable, ), ); } /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testBeforeTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void + public function beforeFirstTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void { $this->dispatcher->dispatch( - new Test\BeforeTestMethodFinished( + new Test\BeforeFirstTestMethodFinished( $this->telemetryInfo(), $testClassName, ...$calledMethods, @@ -415,35 +430,32 @@ public function testBeforeTestMethodFinished(string $testClassName, Code\ClassMe } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testPreConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void + public function beforeTestMethodCalled(TestMethod $test, ClassMethod $calledMethod): void { $this->dispatcher->dispatch( - new Test\PreConditionCalled( + new Test\BeforeTestMethodCalled( $this->telemetryInfo(), - $testClassName, + $test, $calledMethod, ), ); } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testPreConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void + public function beforeTestMethodErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\PreConditionFinished( + new Test\BeforeTestMethodErrored( $this->telemetryInfo(), - $testClassName, - ...$calledMethods, + $test, + $calledMethod, + $throwable, ), ); } @@ -452,28 +464,29 @@ public function testPreConditionFinished(string $testClassName, Code\ClassMethod * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testPrepared(Code\Test $test): void + public function beforeTestMethodFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\Prepared( + new Test\BeforeTestMethodFailed( $this->telemetryInfo(), $test, + $calledMethod, + $throwable, ), ); } /** - * @psalm-param class-string $className - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testRegisteredComparator(string $className): void + public function beforeTestMethodFinished(TestMethod $test, ClassMethod ...$calledMethods): void { $this->dispatcher->dispatch( - new Test\ComparatorRegistered( + new Test\BeforeTestMethodFinished( $this->telemetryInfo(), - $className, + $test, + ...$calledMethods, ), ); } @@ -482,15 +495,13 @@ public function testRegisteredComparator(string $className): void * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testAssertionSucceeded(mixed $value, Constraint\Constraint $constraint, string $message): void + public function preConditionCalled(TestMethod $test, ClassMethod $calledMethod): void { $this->dispatcher->dispatch( - new Test\AssertionSucceeded( + new Test\PreConditionCalled( $this->telemetryInfo(), - Exporter::export($value, $this->exportObjects), - $constraint->toString($this->exportObjects), - $constraint->count(), - $message, + $test, + $calledMethod, ), ); } @@ -499,77 +510,75 @@ public function testAssertionSucceeded(mixed $value, Constraint\Constraint $cons * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testAssertionFailed(mixed $value, Constraint\Constraint $constraint, string $message): void + public function preConditionErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\AssertionFailed( + new Test\PreConditionErrored( $this->telemetryInfo(), - Exporter::export($value, $this->exportObjects), - $constraint->toString($this->exportObjects), - $constraint->count(), - $message, + $test, + $calledMethod, + $throwable, ), ); } /** - * @psalm-param class-string $className - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedMockObject(string $className): void + public function preConditionFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void { $this->dispatcher->dispatch( - new Test\MockObjectCreated( + new Test\PreConditionFailed( $this->telemetryInfo(), - $className, + $test, + $calledMethod, + $throwable, ), ); } /** - * @psalm-param list $interfaces - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void + public function preConditionFinished(TestMethod $test, ClassMethod ...$calledMethods): void { $this->dispatcher->dispatch( - new Test\MockObjectForIntersectionOfInterfacesCreated( + new Test\PreConditionFinished( $this->telemetryInfo(), - $interfaces, + $test, + ...$calledMethods, ), ); } /** - * @psalm-param trait-string $traitName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedMockObjectForTrait(string $traitName): void + public function testPrepared(Code\Test $test): void { + memory_reset_peak_usage(); + $this->dispatcher->dispatch( - new Test\MockObjectForTraitCreated( + new Test\Prepared( $this->telemetryInfo(), - $traitName, + $test, ), ); } /** - * @psalm-param class-string $className + * @param class-string $className * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedMockObjectForAbstractClass(string $className): void + public function testRegisteredComparator(string $className): void { $this->dispatcher->dispatch( - new Test\MockObjectForAbstractClassCreated( + new Test\ComparatorRegistered( $this->telemetryInfo(), $className, ), @@ -577,63 +586,56 @@ public function testCreatedMockObjectForAbstractClass(string $className): void } /** - * @psalm-param class-string $originalClassName - * @psalm-param class-string $mockClassName + * @param class-string $className * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedMockObjectFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void + public function testCreatedMockObject(string $className): void { $this->dispatcher->dispatch( - new Test\MockObjectFromWsdlCreated( + new Test\MockObjectCreated( $this->telemetryInfo(), - $wsdlFile, - $originalClassName, - $mockClassName, - $methods, - $callOriginalConstructor, - $options, + $className, ), ); } /** - * @psalm-param class-string $className + * @param list $interfaces * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedPartialMockObject(string $className, string ...$methodNames): void + public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void { $this->dispatcher->dispatch( - new Test\PartialMockObjectCreated( + new Test\MockObjectForIntersectionOfInterfacesCreated( $this->telemetryInfo(), - $className, - ...$methodNames, + $interfaces, ), ); } /** - * @psalm-param class-string $className + * @param class-string $className * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testCreatedTestProxy(string $className, array $constructorArguments): void + public function testCreatedPartialMockObject(string $className, string ...$methodNames): void { $this->dispatcher->dispatch( - new Test\TestProxyCreated( + new Test\PartialMockObjectCreated( $this->telemetryInfo(), $className, - Exporter::export($constructorArguments, $this->exportObjects), + ...$methodNames, ), ); } /** - * @psalm-param class-string $className + * @param class-string $className * * @throws InvalidArgumentException * @throws UnknownEventTypeException @@ -649,7 +651,7 @@ public function testCreatedStub(string $className): void } /** - * @psalm-param list $interfaces + * @param list $interfaces * * @throws InvalidArgumentException * @throws UnknownEventTypeException @@ -755,6 +757,8 @@ public function testSkipped(Code\Test $test, string $message): void } /** + * @param non-empty-string $message + * * @throws InvalidArgumentException * @throws NoTestCaseObjectOnCallStackException * @throws UnknownEventTypeException @@ -783,10 +787,36 @@ public function testTriggeredPhpunitDeprecation(?Code\Test $test, string $messag } /** + * @param non-empty-string $message + * + * @throws InvalidArgumentException + * @throws NoTestCaseObjectOnCallStackException + * @throws UnknownEventTypeException + */ + public function testTriggeredPhpunitNotice(?Code\Test $test, string $message): void + { + if ($test === null) { + $test = TestMethodBuilder::fromCallStack(); + } + + $this->dispatcher->dispatch( + new Test\PhpunitNoticeTriggered( + $this->telemetryInfo(), + $test, + $message, + ), + ); + } + + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testTriggeredPhpDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void + public function testTriggeredPhpDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest, IssueTrigger $trigger): void { $this->dispatcher->dispatch( new Test\PhpDeprecationTriggered( @@ -798,15 +828,21 @@ public function testTriggeredPhpDeprecation(Code\Test $test, string $message, st $suppressed, $ignoredByBaseline, $ignoredByTest, + $trigger, ), ); } /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $stackTrace + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testTriggeredDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void + public function testTriggeredDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest, IssueTrigger $trigger, string $stackTrace): void { $this->dispatcher->dispatch( new Test\DeprecationTriggered( @@ -818,11 +854,17 @@ public function testTriggeredDeprecation(Code\Test $test, string $message, strin $suppressed, $ignoredByBaseline, $ignoredByTest, + $trigger, + $stackTrace, ), ); } /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -841,6 +883,10 @@ public function testTriggeredError(Code\Test $test, string $message, string $fil } /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -860,6 +906,10 @@ public function testTriggeredNotice(Code\Test $test, string $message, string $fi } /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -879,6 +929,10 @@ public function testTriggeredPhpNotice(Code\Test $test, string $message, string } /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -898,6 +952,10 @@ public function testTriggeredWarning(Code\Test $test, string $message, string $f } /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -917,6 +975,8 @@ public function testTriggeredPhpWarning(Code\Test $test, string $message, string } /** + * @param non-empty-string $message + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -932,6 +992,8 @@ public function testTriggeredPhpunitError(Code\Test $test, string $message): voi } /** + * @param non-empty-string $message + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -947,7 +1009,7 @@ public function testTriggeredPhpunitWarning(Code\Test $test, string $message): v } /** - * @psalm-param non-empty-string $output + * @param non-empty-string $output * * @throws InvalidArgumentException * @throws UnknownEventTypeException @@ -963,6 +1025,8 @@ public function testPrintedUnexpectedOutput(string $output): void } /** + * @param non-negative-int $numberOfAssertionsPerformed + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ @@ -978,80 +1042,136 @@ public function testFinished(Code\Test $test, int $numberOfAssertionsPerformed): } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testPostConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void + public function postConditionCalled(TestMethod $test, ClassMethod $calledMethod): void { $this->dispatcher->dispatch( new Test\PostConditionCalled( $this->telemetryInfo(), - $testClassName, + $test, $calledMethod, ), ); } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testPostConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void + public function postConditionErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void + { + $this->dispatcher->dispatch( + new Test\PostConditionErrored( + $this->telemetryInfo(), + $test, + $calledMethod, + $throwable, + ), + ); + } + + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function postConditionFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void + { + $this->dispatcher->dispatch( + new Test\PostConditionFailed( + $this->telemetryInfo(), + $test, + $calledMethod, + $throwable, + ), + ); + } + + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function postConditionFinished(TestMethod $test, ClassMethod ...$calledMethods): void { $this->dispatcher->dispatch( new Test\PostConditionFinished( $this->telemetryInfo(), - $testClassName, + $test, ...$calledMethods, ), ); } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testAfterTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void + public function afterTestMethodCalled(TestMethod $test, ClassMethod $calledMethod): void { $this->dispatcher->dispatch( new Test\AfterTestMethodCalled( $this->telemetryInfo(), - $testClassName, + $test, $calledMethod, ), ); } /** - * @psalm-param class-string $testClassName - * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testAfterTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void + public function afterTestMethodErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void + { + $this->dispatcher->dispatch( + new Test\AfterTestMethodErrored( + $this->telemetryInfo(), + $test, + $calledMethod, + $throwable, + ), + ); + } + + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function afterTestMethodFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void + { + $this->dispatcher->dispatch( + new Test\AfterTestMethodFailed( + $this->telemetryInfo(), + $test, + $calledMethod, + $throwable, + ), + ); + } + + /** + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function afterTestMethodFinished(TestMethod $test, ClassMethod ...$calledMethods): void { $this->dispatcher->dispatch( new Test\AfterTestMethodFinished( $this->telemetryInfo(), - $testClassName, + $test, ...$calledMethods, ), ); } /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testAfterLastTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void + public function afterLastTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void { $this->dispatcher->dispatch( new Test\AfterLastTestMethodCalled( @@ -1063,12 +1183,48 @@ public function testAfterLastTestMethodCalled(string $testClassName, Code\ClassM } /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testAfterLastTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void + public function afterLastTestMethodErrored(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void + { + $this->dispatcher->dispatch( + new Test\AfterLastTestMethodErrored( + $this->telemetryInfo(), + $testClassName, + $calledMethod, + $throwable, + ), + ); + } + + /** + * @param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function afterLastTestMethodFailed(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void + { + $this->dispatcher->dispatch( + new Test\AfterLastTestMethodFailed( + $this->telemetryInfo(), + $testClassName, + $calledMethod, + $throwable, + ), + ); + } + + /** + * @param class-string $testClassName + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function afterLastTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void { $this->dispatcher->dispatch( new Test\AfterLastTestMethodFinished( @@ -1097,7 +1253,40 @@ public function testSuiteFinished(TestSuite $testSuite): void * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testRunnerTriggeredDeprecation(string $message): void + public function testRunnerStartedStaticAnalysisForCodeCoverage(): void + { + $this->dispatcher->dispatch( + new TestRunner\StaticAnalysisForCodeCoverageStarted( + $this->telemetryInfo(), + ), + ); + } + + /** + * @param non-negative-int $cacheHits + * @param non-negative-int $cacheMisses + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerFinishedStaticAnalysisForCodeCoverage(int $cacheHits, int $cacheMisses): void + { + $this->dispatcher->dispatch( + new TestRunner\StaticAnalysisForCodeCoverageFinished( + $this->telemetryInfo(), + $cacheHits, + $cacheMisses, + ), + ); + } + + /** + * @param non-empty-string $message + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerTriggeredPhpunitDeprecation(string $message): void { $this->dispatcher->dispatch( new TestRunner\DeprecationTriggered( @@ -1108,10 +1297,28 @@ public function testRunnerTriggeredDeprecation(string $message): void } /** + * @param non-empty-string $message + * + * @throws InvalidArgumentException + * @throws UnknownEventTypeException + */ + public function testRunnerTriggeredPhpunitNotice(string $message): void + { + $this->dispatcher->dispatch( + new TestRunner\NoticeTriggered( + $this->telemetryInfo(), + $message, + ), + ); + } + + /** + * @param non-empty-string $message + * * @throws InvalidArgumentException * @throws UnknownEventTypeException */ - public function testRunnerTriggeredWarning(string $message): void + public function testRunnerTriggeredPhpunitWarning(string $message): void { $this->dispatcher->dispatch( new TestRunner\WarningTriggered( diff --git a/src/Event/Emitter/Emitter.php b/src/Event/Emitter/Emitter.php index 65752daec7a..340e81767b1 100644 --- a/src/Event/Emitter/Emitter.php +++ b/src/Event/Emitter/Emitter.php @@ -11,33 +11,40 @@ use PHPUnit\Event\Code\ClassMethod; use PHPUnit\Event\Code\ComparisonFailure; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; +use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\Throwable; use PHPUnit\Event\TestSuite\TestSuite; -use PHPUnit\Framework\Constraint; use PHPUnit\TextUI\Configuration\Configuration; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Emitter { - public function exportObjects(): void; - - public function exportsObjects(): bool; - public function applicationStarted(): void; public function testRunnerStarted(): void; public function testRunnerConfigured(Configuration $configuration): void; + /** + * @param non-empty-string $filename + */ public function testRunnerBootstrapFinished(string $filename): void; + /** + * @param non-empty-string $filename + * @param non-empty-string $name + * @param non-empty-string $version + */ public function testRunnerLoadedExtensionFromPhar(string $filename, string $name, string $version): void; /** - * @psalm-param class-string $className - * @psalm-param array $parameters + * @param class-string $className + * @param array $parameters */ public function testRunnerBootstrappedExtension(string $className, array $parameters): void; @@ -59,103 +66,84 @@ public function testRunnerDisabledGarbageCollection(): void; public function testRunnerTriggeredGarbageCollection(): void; + /** + * @param non-empty-string $message + */ public function testSuiteSkipped(TestSuite $testSuite, string $message): void; public function testSuiteStarted(TestSuite $testSuite): void; public function testPreparationStarted(Code\Test $test): void; - public function testPreparationFailed(Code\Test $test): void; + public function testPreparationErrored(Code\Test $test, Throwable $throwable): void; - /** - * @psalm-param class-string $testClassName - */ - public function testBeforeFirstTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void; + public function testPreparationFailed(Code\Test $test, Throwable $throwable): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testBeforeFirstTestMethodErrored(string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable): void; + public function beforeFirstTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testBeforeFirstTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void; + public function beforeFirstTestMethodErrored(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testBeforeTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void; + public function beforeFirstTestMethodFailed(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testBeforeTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void; + public function beforeFirstTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void; - /** - * @psalm-param class-string $testClassName - */ - public function testPreConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void; + public function beforeTestMethodCalled(TestMethod $test, ClassMethod $calledMethod): void; - /** - * @psalm-param class-string $testClassName - */ - public function testPreConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void; + public function beforeTestMethodErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; - public function testPrepared(Code\Test $test): void; + public function beforeTestMethodFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; - /** - * @psalm-param class-string $className - */ - public function testRegisteredComparator(string $className): void; + public function beforeTestMethodFinished(TestMethod $test, ClassMethod ...$calledMethods): void; - public function testAssertionSucceeded(mixed $value, Constraint\Constraint $constraint, string $message): void; + public function preConditionCalled(TestMethod $test, ClassMethod $calledMethod): void; - public function testAssertionFailed(mixed $value, Constraint\Constraint $constraint, string $message): void; + public function preConditionErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; - /** - * @psalm-param class-string $className - */ - public function testCreatedMockObject(string $className): void; + public function preConditionFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; - /** - * @psalm-param list $interfaces - */ - public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void; + public function preConditionFinished(TestMethod $test, ClassMethod ...$calledMethods): void; - /** - * @psalm-param trait-string $traitName - */ - public function testCreatedMockObjectForTrait(string $traitName): void; + public function testPrepared(Code\Test $test): void; /** - * @psalm-param class-string $className + * @param class-string $className */ - public function testCreatedMockObjectForAbstractClass(string $className): void; + public function testRegisteredComparator(string $className): void; /** - * @psalm-param class-string $originalClassName - * @psalm-param class-string $mockClassName + * @param class-string $className */ - public function testCreatedMockObjectFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void; + public function testCreatedMockObject(string $className): void; /** - * @psalm-param class-string $className + * @param list $interfaces */ - public function testCreatedPartialMockObject(string $className, string ...$methodNames): void; + public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void; /** - * @psalm-param class-string $className + * @param class-string $className */ - public function testCreatedTestProxy(string $className, array $constructorArguments): void; + public function testCreatedPartialMockObject(string $className, string ...$methodNames): void; /** - * @psalm-param class-string $className + * @param class-string $className */ public function testCreatedStub(string $className): void; /** - * @psalm-param list $interfaces + * @param list $interfaces */ public function testCreatedStubForIntersectionOfInterfaces(array $interfaces): void; @@ -165,74 +153,162 @@ public function testFailed(Code\Test $test, Throwable $throwable, ?ComparisonFai public function testPassed(Code\Test $test): void; + /** + * @param non-empty-string $message + */ public function testConsideredRisky(Code\Test $test, string $message): void; public function testMarkedAsIncomplete(Code\Test $test, Throwable $throwable): void; + /** + * @param non-empty-string $message + */ public function testSkipped(Code\Test $test, string $message): void; + /** + * @param non-empty-string $message + */ public function testTriggeredPhpunitDeprecation(?Code\Test $test, string $message): void; - public function testTriggeredPhpDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void; + /** + * @param non-empty-string $message + */ + public function testTriggeredPhpunitNotice(?Code\Test $test, string $message): void; - public function testTriggeredDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest): void; + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + */ + public function testTriggeredPhpDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest, IssueTrigger $trigger): void; + + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $stackTrace + */ + public function testTriggeredDeprecation(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest, IssueTrigger $trigger, string $stackTrace): void; + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + */ public function testTriggeredError(Code\Test $test, string $message, string $file, int $line, bool $suppressed): void; + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + */ public function testTriggeredNotice(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + */ public function testTriggeredPhpNotice(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + */ public function testTriggeredWarning(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + */ public function testTriggeredPhpWarning(Code\Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline): void; + /** + * @param non-empty-string $message + */ public function testTriggeredPhpunitError(Code\Test $test, string $message): void; + /** + * @param non-empty-string $message + */ public function testTriggeredPhpunitWarning(Code\Test $test, string $message): void; /** - * @psalm-param non-empty-string $output + * @param non-empty-string $output */ public function testPrintedUnexpectedOutput(string $output): void; + /** + * @param non-negative-int $numberOfAssertionsPerformed + */ public function testFinished(Code\Test $test, int $numberOfAssertionsPerformed): void; + public function postConditionCalled(TestMethod $test, ClassMethod $calledMethod): void; + + public function postConditionErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; + + public function postConditionFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; + + public function postConditionFinished(TestMethod $test, ClassMethod ...$calledMethods): void; + + public function afterTestMethodCalled(TestMethod $test, ClassMethod $calledMethod): void; + + public function afterTestMethodErrored(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; + + public function afterTestMethodFailed(TestMethod $test, ClassMethod $calledMethod, Throwable $throwable): void; + + public function afterTestMethodFinished(TestMethod $test, ClassMethod ...$calledMethods): void; + /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testPostConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void; + public function afterLastTestMethodCalled(string $testClassName, ClassMethod $calledMethod): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testPostConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void; + public function afterLastTestMethodErrored(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testAfterTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void; + public function afterLastTestMethodFailed(string $testClassName, ClassMethod $calledMethod, Throwable $throwable): void; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ - public function testAfterTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void; + public function afterLastTestMethodFinished(string $testClassName, ClassMethod ...$calledMethods): void; + + public function testSuiteFinished(TestSuite $testSuite): void; + + public function testRunnerStartedChildProcess(): void; + + public function testRunnerFinishedChildProcess(string $stdout, string $stderr): void; + + public function testRunnerStartedStaticAnalysisForCodeCoverage(): void; /** - * @psalm-param class-string $testClassName + * @param non-negative-int $cacheHits + * @param non-negative-int $cacheMisses */ - public function testAfterLastTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void; + public function testRunnerFinishedStaticAnalysisForCodeCoverage(int $cacheHits, int $cacheMisses): void; /** - * @psalm-param class-string $testClassName + * @param non-empty-string $message */ - public function testAfterLastTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void; - - public function testSuiteFinished(TestSuite $testSuite): void; + public function testRunnerTriggeredPhpunitDeprecation(string $message): void; - public function testRunnerTriggeredDeprecation(string $message): void; + /** + * @param non-empty-string $message + */ + public function testRunnerTriggeredPhpunitNotice(string $message): void; - public function testRunnerTriggeredWarning(string $message): void; + /** + * @param non-empty-string $message + */ + public function testRunnerTriggeredPhpunitWarning(string $message): void; public function testRunnerEnabledGarbageCollection(): void; diff --git a/src/Event/Events/Application/Finished.php b/src/Event/Events/Application/Finished.php index 1f1791b0910..6e94da2a957 100644 --- a/src/Event/Events/Application/Finished.php +++ b/src/Event/Events/Application/Finished.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -39,6 +39,9 @@ public function shellExitCode(): int return $this->shellExitCode; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Application/Started.php b/src/Event/Events/Application/Started.php index 21124322364..a9aa959a72a 100644 --- a/src/Event/Events/Application/Started.php +++ b/src/Event/Events/Application/Started.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -40,6 +40,9 @@ public function runtime(): Runtime return $this->runtime; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Event.php b/src/Event/Events/Event.php index 8aa014b0abc..443ca7294a5 100644 --- a/src/Event/Events/Event.php +++ b/src/Event/Events/Event.php @@ -16,5 +16,8 @@ interface Event { public function telemetryInfo(): Telemetry\Info; + /** + * @return non-empty-string + */ public function asString(): string; } diff --git a/src/Event/Events/EventCollection.php b/src/Event/Events/EventCollection.php index ecefb3763c6..7c691269294 100644 --- a/src/Event/Events/EventCollection.php +++ b/src/Event/Events/EventCollection.php @@ -21,7 +21,7 @@ final class EventCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $events = []; @@ -33,7 +33,7 @@ public function add(Event ...$events): void } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/Event/Events/EventCollectionIterator.php b/src/Event/Events/EventCollectionIterator.php index 7a09fa405c3..d121dea6e40 100644 --- a/src/Event/Events/EventCollectionIterator.php +++ b/src/Event/Events/EventCollectionIterator.php @@ -20,7 +20,7 @@ final class EventCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $events; private int $position = 0; diff --git a/src/Event/Events/Test/Assertion/AssertionFailed.php b/src/Event/Events/Test/Assertion/AssertionFailed.php deleted file mode 100644 index acc023565e5..00000000000 --- a/src/Event/Events/Test/Assertion/AssertionFailed.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use function sprintf; -use PHPUnit\Event\Event; -use PHPUnit\Event\Telemetry; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class AssertionFailed implements Event -{ - private Telemetry\Info $telemetryInfo; - private string $value; - private string $constraint; - private int $count; - private string $message; - - public function __construct(Telemetry\Info $telemetryInfo, string $value, string $constraint, int $count, string $message) - { - $this->telemetryInfo = $telemetryInfo; - $this->value = $value; - $this->constraint = $constraint; - $this->count = $count; - $this->message = $message; - } - - public function telemetryInfo(): Telemetry\Info - { - return $this->telemetryInfo; - } - - public function value(): string - { - return $this->value; - } - - public function count(): int - { - return $this->count; - } - - public function message(): string - { - return $this->message; - } - - public function asString(): string - { - $message = ''; - - if (!empty($this->message)) { - $message = sprintf( - ', Message: %s', - $this->message, - ); - } - - return sprintf( - 'Assertion Failed (Constraint: %s, Value: %s%s)', - $this->constraint, - $this->value, - $message, - ); - } -} diff --git a/src/Event/Events/Test/Assertion/AssertionFailedSubscriber.php b/src/Event/Events/Test/Assertion/AssertionFailedSubscriber.php deleted file mode 100644 index a14cd5f5e78..00000000000 --- a/src/Event/Events/Test/Assertion/AssertionFailedSubscriber.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\Subscriber; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface AssertionFailedSubscriber extends Subscriber -{ - public function notify(AssertionFailed $event): void; -} diff --git a/src/Event/Events/Test/Assertion/AssertionSucceeded.php b/src/Event/Events/Test/Assertion/AssertionSucceeded.php deleted file mode 100644 index 5163b298396..00000000000 --- a/src/Event/Events/Test/Assertion/AssertionSucceeded.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use function sprintf; -use PHPUnit\Event\Event; -use PHPUnit\Event\Telemetry; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class AssertionSucceeded implements Event -{ - private Telemetry\Info $telemetryInfo; - private string $value; - private string $constraint; - private int $count; - private string $message; - - public function __construct(Telemetry\Info $telemetryInfo, string $value, string $constraint, int $count, string $message) - { - $this->telemetryInfo = $telemetryInfo; - $this->value = $value; - $this->constraint = $constraint; - $this->count = $count; - $this->message = $message; - } - - public function telemetryInfo(): Telemetry\Info - { - return $this->telemetryInfo; - } - - public function value(): string - { - return $this->value; - } - - public function count(): int - { - return $this->count; - } - - public function message(): string - { - return $this->message; - } - - public function asString(): string - { - $message = ''; - - if (!empty($this->message)) { - $message = sprintf( - ', Message: %s', - $this->message, - ); - } - - return sprintf( - 'Assertion Succeeded (Constraint: %s, Value: %s%s)', - $this->constraint, - $this->value, - $message, - ); - } -} diff --git a/src/Event/Events/Test/Assertion/AssertionSucceededSubscriber.php b/src/Event/Events/Test/Assertion/AssertionSucceededSubscriber.php deleted file mode 100644 index 535860a382d..00000000000 --- a/src/Event/Events/Test/Assertion/AssertionSucceededSubscriber.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\Subscriber; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface AssertionSucceededSubscriber extends Subscriber -{ - public function notify(AssertionSucceeded $event): void; -} diff --git a/src/Event/Events/Test/ComparatorRegistered.php b/src/Event/Events/Test/ComparatorRegistered.php index dfbb5909fe9..893601c36f0 100644 --- a/src/Event/Events/Test/ComparatorRegistered.php +++ b/src/Event/Events/Test/ComparatorRegistered.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,12 +23,12 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(Telemetry\Info $telemetryInfo, string $className) { @@ -42,13 +42,16 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { return $this->className; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.php b/src/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.php index 9924ffb9af3..8643b206836 100644 --- a/src/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.php +++ b/src/Event/Events/Test/HookMethod/AfterLastTestMethodCalled.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -24,13 +24,13 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $testClassName; private Code\ClassMethod $calledMethod; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { @@ -45,7 +45,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function testClassName(): string { @@ -57,6 +57,9 @@ public function calledMethod(): Code\ClassMethod return $this->calledMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/AfterLastTestMethodErrored.php b/src/Event/Events/Test/HookMethod/AfterLastTestMethodErrored.php new file mode 100644 index 00000000000..3dffce257c2 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterLastTestMethodErrored.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class AfterLastTestMethodErrored implements Event +{ + private Telemetry\Info $telemetryInfo; + + /** + * @var class-string + */ + private string $testClassName; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + /** + * @param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + /** + * @return class-string + */ + public function testClassName(): string + { + return $this->testClassName; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'After Last Test Method Errored (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/AfterLastTestMethodErroredSubscriber.php b/src/Event/Events/Test/HookMethod/AfterLastTestMethodErroredSubscriber.php new file mode 100644 index 00000000000..b994fdeb2f1 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterLastTestMethodErroredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterLastTestMethodErroredSubscriber extends Subscriber +{ + public function notify(AfterLastTestMethodErrored $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/AfterLastTestMethodFailed.php b/src/Event/Events/Test/HookMethod/AfterLastTestMethodFailed.php new file mode 100644 index 00000000000..70b16853927 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterLastTestMethodFailed.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class AfterLastTestMethodFailed implements Event +{ + private Telemetry\Info $telemetryInfo; + + /** + * @var class-string + */ + private string $testClassName; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + /** + * @param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + /** + * @return class-string + */ + public function testClassName(): string + { + return $this->testClassName; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'After Last Test Method Failed (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/AfterLastTestMethodFailedSubscriber.php b/src/Event/Events/Test/HookMethod/AfterLastTestMethodFailedSubscriber.php new file mode 100644 index 00000000000..3b011bd9935 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterLastTestMethodFailedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterLastTestMethodFailedSubscriber extends Subscriber +{ + public function notify(AfterLastTestMethodFailed $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php b/src/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php index 1ba74612bcf..c4a2dd9555c 100644 --- a/src/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php +++ b/src/Event/Events/Test/HookMethod/AfterLastTestMethodFinished.php @@ -16,7 +16,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -25,17 +25,17 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $testClassName; /** - * @psalm-var list + * @var list */ private array $calledMethods; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { @@ -50,7 +50,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function testClassName(): string { @@ -58,13 +58,16 @@ public function testClassName(): string } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = 'After Last Test Method Finished:'; diff --git a/src/Event/Events/Test/HookMethod/AfterTestMethodCalled.php b/src/Event/Events/Test/HookMethod/AfterTestMethodCalled.php index d096b965857..93c4e12cb64 100644 --- a/src/Event/Events/Test/HookMethod/AfterTestMethodCalled.php +++ b/src/Event/Events/Test/HookMethod/AfterTestMethodCalled.php @@ -15,27 +15,20 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class AfterTestMethodCalled implements Event { private Telemetry\Info $telemetryInfo; - - /** - * @psalm-var class-string - */ - private string $testClassName; + private Code\TestMethod $test; private Code\ClassMethod $calledMethod; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethod = $calledMethod; } @@ -44,12 +37,19 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } public function calledMethod(): Code\ClassMethod @@ -57,6 +57,9 @@ public function calledMethod(): Code\ClassMethod return $this->calledMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/AfterTestMethodErrored.php b/src/Event/Events/Test/HookMethod/AfterTestMethodErrored.php new file mode 100644 index 00000000000..fd277e2d0eb --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterTestMethodErrored.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class AfterTestMethodErrored implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + /** + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 + */ + public function testClassName(): string + { + return $this->test->className(); + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'After Test Method Errored (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/AfterTestMethodErroredSubscriber.php b/src/Event/Events/Test/HookMethod/AfterTestMethodErroredSubscriber.php new file mode 100644 index 00000000000..622f91625d9 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterTestMethodErroredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterTestMethodErroredSubscriber extends Subscriber +{ + public function notify(AfterTestMethodErrored $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/AfterTestMethodFailed.php b/src/Event/Events/Test/HookMethod/AfterTestMethodFailed.php new file mode 100644 index 00000000000..e405066f7ef --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterTestMethodFailed.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class AfterTestMethodFailed implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'After Test Method Failed (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/AfterTestMethodFailedSubscriber.php b/src/Event/Events/Test/HookMethod/AfterTestMethodFailedSubscriber.php new file mode 100644 index 00000000000..16134322a63 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/AfterTestMethodFailedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface AfterTestMethodFailedSubscriber extends Subscriber +{ + public function notify(AfterTestMethodFailed $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/AfterTestMethodFinished.php b/src/Event/Events/Test/HookMethod/AfterTestMethodFinished.php index 5431d5f94d3..f897198e0df 100644 --- a/src/Event/Events/Test/HookMethod/AfterTestMethodFinished.php +++ b/src/Event/Events/Test/HookMethod/AfterTestMethodFinished.php @@ -16,31 +16,24 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class AfterTestMethodFinished implements Event { private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; /** - * @psalm-var class-string - */ - private string $testClassName; - - /** - * @psalm-var list + * @var list */ private array $calledMethods; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod ...$calledMethods) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethods = $calledMethods; } @@ -49,22 +42,32 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = 'After Test Method Finished:'; diff --git a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.php b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.php index 58381ec2448..3c6ee0102ca 100644 --- a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.php +++ b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodCalled.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -24,13 +24,13 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $testClassName; private Code\ClassMethod $calledMethod; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) { @@ -45,7 +45,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function testClassName(): string { @@ -57,6 +57,9 @@ public function calledMethod(): Code\ClassMethod return $this->calledMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.php b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.php index afe1002f021..f142b1bd526 100644 --- a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.php +++ b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodErrored.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Event\Test; +use const PHP_EOL; use function sprintf; use PHPUnit\Event\Code; use PHPUnit\Event\Code\Throwable; @@ -16,7 +17,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -25,14 +26,14 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $testClassName; private Code\ClassMethod $calledMethod; private Throwable $throwable; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable) { @@ -48,7 +49,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function testClassName(): string { @@ -65,11 +66,14 @@ public function throwable(): Throwable return $this->throwable; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->throwable->message(); - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailed.php b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailed.php new file mode 100644 index 00000000000..c400838f9c9 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailed.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class BeforeFirstTestMethodFailed implements Event +{ + private Telemetry\Info $telemetryInfo; + + /** + * @var class-string + */ + private string $testClassName; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + /** + * @param class-string $testClassName + */ + public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->testClassName = $testClassName; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + /** + * @return class-string + */ + public function testClassName(): string + { + return $this->testClassName; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Before First Test Method Failed (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailedSubscriber.php b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailedSubscriber.php new file mode 100644 index 00000000000..4e0b7eff7b9 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface BeforeFirstTestMethodFailedSubscriber extends Subscriber +{ + public function notify(BeforeFirstTestMethodFailed $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.php b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.php index b23857feb25..5a2cb762784 100644 --- a/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.php +++ b/src/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinished.php @@ -16,7 +16,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -25,17 +25,17 @@ private Telemetry\Info$telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $testClassName; /** - * @psalm-var list + * @var list */ private array $calledMethods; /** - * @psalm-param class-string $testClassName + * @param class-string $testClassName */ public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) { @@ -50,7 +50,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function testClassName(): string { @@ -58,13 +58,16 @@ public function testClassName(): string } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = 'Before First Test Method Finished:'; diff --git a/src/Event/Events/Test/HookMethod/BeforeTestMethodCalled.php b/src/Event/Events/Test/HookMethod/BeforeTestMethodCalled.php index f3b9846a636..2927a6851f2 100644 --- a/src/Event/Events/Test/HookMethod/BeforeTestMethodCalled.php +++ b/src/Event/Events/Test/HookMethod/BeforeTestMethodCalled.php @@ -15,27 +15,20 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class BeforeTestMethodCalled implements Event { private Telemetry\Info $telemetryInfo; - - /** - * @psalm-var class-string - */ - private string $testClassName; + private Code\TestMethod $test; private Code\ClassMethod $calledMethod; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethod = $calledMethod; } @@ -44,12 +37,19 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } public function calledMethod(): Code\ClassMethod @@ -57,6 +57,9 @@ public function calledMethod(): Code\ClassMethod return $this->calledMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/BeforeTestMethodErrored.php b/src/Event/Events/Test/HookMethod/BeforeTestMethodErrored.php new file mode 100644 index 00000000000..7e18a0c652b --- /dev/null +++ b/src/Event/Events/Test/HookMethod/BeforeTestMethodErrored.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class BeforeTestMethodErrored implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + /** + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 + */ + public function testClassName(): string + { + return $this->test->className(); + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Before Test Method Errored (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/BeforeTestMethodErroredSubscriber.php b/src/Event/Events/Test/HookMethod/BeforeTestMethodErroredSubscriber.php new file mode 100644 index 00000000000..e53771c4944 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/BeforeTestMethodErroredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface BeforeTestMethodErroredSubscriber extends Subscriber +{ + public function notify(BeforeTestMethodErrored $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/BeforeTestMethodFailed.php b/src/Event/Events/Test/HookMethod/BeforeTestMethodFailed.php new file mode 100644 index 00000000000..95b46e4107e --- /dev/null +++ b/src/Event/Events/Test/HookMethod/BeforeTestMethodFailed.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class BeforeTestMethodFailed implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Before Test Method Failed (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/BeforeTestMethodFailedSubscriber.php b/src/Event/Events/Test/HookMethod/BeforeTestMethodFailedSubscriber.php new file mode 100644 index 00000000000..0f9f071cea5 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/BeforeTestMethodFailedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface BeforeTestMethodFailedSubscriber extends Subscriber +{ + public function notify(BeforeTestMethodFailed $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/BeforeTestMethodFinished.php b/src/Event/Events/Test/HookMethod/BeforeTestMethodFinished.php index 034abd817ab..971ad06bbf5 100644 --- a/src/Event/Events/Test/HookMethod/BeforeTestMethodFinished.php +++ b/src/Event/Events/Test/HookMethod/BeforeTestMethodFinished.php @@ -16,31 +16,24 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class BeforeTestMethodFinished implements Event { private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; /** - * @psalm-var class-string - */ - private string $testClassName; - - /** - * @psalm-var list + * @var list */ private array $calledMethods; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod ...$calledMethods) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethods = $calledMethods; } @@ -49,22 +42,32 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = 'Before Test Method Finished:'; diff --git a/src/Event/Events/Test/HookMethod/PostConditionCalled.php b/src/Event/Events/Test/HookMethod/PostConditionCalled.php index ac7a1e3f1c1..2035f64322e 100644 --- a/src/Event/Events/Test/HookMethod/PostConditionCalled.php +++ b/src/Event/Events/Test/HookMethod/PostConditionCalled.php @@ -15,27 +15,20 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class PostConditionCalled implements Event { private Telemetry\Info $telemetryInfo; - - /** - * @psalm-var class-string - */ - private string $testClassName; + private Code\TestMethod $test; private Code\ClassMethod $calledMethod; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethod = $calledMethod; } @@ -44,12 +37,19 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } public function calledMethod(): Code\ClassMethod @@ -57,6 +57,9 @@ public function calledMethod(): Code\ClassMethod return $this->calledMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/PostConditionErrored.php b/src/Event/Events/Test/HookMethod/PostConditionErrored.php new file mode 100644 index 00000000000..29cfe0d64df --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PostConditionErrored.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PostConditionErrored implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + /** + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 + */ + public function testClassName(): string + { + return $this->test->className(); + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Post Condition Method Errored (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/PostConditionErroredSubscriber.php b/src/Event/Events/Test/HookMethod/PostConditionErroredSubscriber.php new file mode 100644 index 00000000000..7bd2c54ce70 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PostConditionErroredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PostConditionErroredSubscriber extends Subscriber +{ + public function notify(PostConditionErrored $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/PostConditionFailed.php b/src/Event/Events/Test/HookMethod/PostConditionFailed.php new file mode 100644 index 00000000000..cb48689730f --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PostConditionFailed.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PostConditionFailed implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Post Condition Method Failed (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/PostConditionFailedSubscriber.php b/src/Event/Events/Test/HookMethod/PostConditionFailedSubscriber.php new file mode 100644 index 00000000000..e6ff7557aa0 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PostConditionFailedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PostConditionFailedSubscriber extends Subscriber +{ + public function notify(PostConditionFailed $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/PostConditionFinished.php b/src/Event/Events/Test/HookMethod/PostConditionFinished.php index 52d9676e81e..0910a2a8bfd 100644 --- a/src/Event/Events/Test/HookMethod/PostConditionFinished.php +++ b/src/Event/Events/Test/HookMethod/PostConditionFinished.php @@ -16,31 +16,24 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class PostConditionFinished implements Event { private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; /** - * @psalm-var class-string - */ - private string $testClassName; - - /** - * @psalm-var list + * @var list */ private array $calledMethods; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod ...$calledMethods) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethods = $calledMethods; } @@ -49,22 +42,32 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = 'Post Condition Method Finished:'; diff --git a/src/Event/Events/Test/HookMethod/PreConditionCalled.php b/src/Event/Events/Test/HookMethod/PreConditionCalled.php index 41ab3855f78..80a10ee15b2 100644 --- a/src/Event/Events/Test/HookMethod/PreConditionCalled.php +++ b/src/Event/Events/Test/HookMethod/PreConditionCalled.php @@ -15,27 +15,20 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class PreConditionCalled implements Event { private Telemetry\Info$telemetryInfo; - - /** - * @psalm-var class-string - */ - private string $testClassName; + private Code\TestMethod $test; private Code\ClassMethod $calledMethod; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod $calledMethod) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethod = $calledMethod; } @@ -44,12 +37,19 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } public function calledMethod(): Code\ClassMethod @@ -57,6 +57,9 @@ public function calledMethod(): Code\ClassMethod return $this->calledMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/HookMethod/PreConditionErrored.php b/src/Event/Events/Test/HookMethod/PreConditionErrored.php new file mode 100644 index 00000000000..f2773d98103 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PreConditionErrored.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PreConditionErrored implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + /** + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 + */ + public function testClassName(): string + { + return $this->test->className(); + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Pre Condition Method Errored (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/PreConditionErroredSubscriber.php b/src/Event/Events/Test/HookMethod/PreConditionErroredSubscriber.php new file mode 100644 index 00000000000..3465040bbeb --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PreConditionErroredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PreConditionErroredSubscriber extends Subscriber +{ + public function notify(PreConditionErrored $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/PreConditionFailed.php b/src/Event/Events/Test/HookMethod/PreConditionFailed.php new file mode 100644 index 00000000000..bf94de21179 --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PreConditionFailed.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PreConditionFailed implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; + private Code\ClassMethod $calledMethod; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod $calledMethod, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->calledMethod = $calledMethod; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\TestMethod + { + return $this->test; + } + + public function calledMethod(): Code\ClassMethod + { + return $this->calledMethod; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Pre Condition Method Failed (%s::%s)%s', + $this->calledMethod->className(), + $this->calledMethod->methodName(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/HookMethod/PreConditionFailedSubscriber.php b/src/Event/Events/Test/HookMethod/PreConditionFailedSubscriber.php new file mode 100644 index 00000000000..26ce7cdce2c --- /dev/null +++ b/src/Event/Events/Test/HookMethod/PreConditionFailedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PreConditionFailedSubscriber extends Subscriber +{ + public function notify(PreConditionFailed $event): void; +} diff --git a/src/Event/Events/Test/HookMethod/PreConditionFinished.php b/src/Event/Events/Test/HookMethod/PreConditionFinished.php index f6e3ac80e05..8a708ee05cd 100644 --- a/src/Event/Events/Test/HookMethod/PreConditionFinished.php +++ b/src/Event/Events/Test/HookMethod/PreConditionFinished.php @@ -16,31 +16,24 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class PreConditionFinished implements Event { private Telemetry\Info $telemetryInfo; + private Code\TestMethod $test; /** - * @psalm-var class-string - */ - private string $testClassName; - - /** - * @psalm-var list + * @var list */ private array $calledMethods; - /** - * @psalm-param class-string $testClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $testClassName, Code\ClassMethod ...$calledMethods) + public function __construct(Telemetry\Info $telemetryInfo, Code\TestMethod $test, Code\ClassMethod ...$calledMethods) { $this->telemetryInfo = $telemetryInfo; - $this->testClassName = $testClassName; + $this->test = $test; $this->calledMethods = $calledMethods; } @@ -49,22 +42,32 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + public function test(): Code\TestMethod + { + return $this->test; + } + /** - * @psalm-return class-string + * @return class-string + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6140 */ public function testClassName(): string { - return $this->testClassName; + return $this->test->className(); } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = 'Pre Condition Method Finished:'; diff --git a/src/Event/Events/Test/Issue/ConsideredRisky.php b/src/Event/Events/Test/Issue/ConsideredRisky.php index 742efede266..306c04e7d50 100644 --- a/src/Event/Events/Test/Issue/ConsideredRisky.php +++ b/src/Event/Events/Test/Issue/ConsideredRisky.php @@ -16,7 +16,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,12 +26,12 @@ private Code\Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-param non-empty-string $message + * @param non-empty-string $message */ public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, string $message) { @@ -51,13 +51,16 @@ public function test(): Code\Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/Issue/DeprecationTriggered.php b/src/Event/Events/Test/Issue/DeprecationTriggered.php index 4d7c9792ac3..09ec56fc8c7 100644 --- a/src/Event/Events/Test/Issue/DeprecationTriggered.php +++ b/src/Event/Events/Test/Issue/DeprecationTriggered.php @@ -10,13 +10,15 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,29 +28,36 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; private bool $ignoredByBaseline; private bool $ignoredByTest; + private IssueTrigger $trigger; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @var non-empty-string */ - public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest) + private string $stackTrace; + + /** + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $stackTrace + */ + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest, IssueTrigger $trigger, string $stackTrace) { $this->telemetryInfo = $telemetryInfo; $this->test = $test; @@ -58,6 +67,8 @@ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $m $this->suppressed = $suppressed; $this->ignoredByBaseline = $ignoredByBaseline; $this->ignoredByTest = $ignoredByTest; + $this->trigger = $trigger; + $this->stackTrace = $stackTrace; } public function telemetryInfo(): Telemetry\Info @@ -71,7 +82,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -79,7 +90,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -87,7 +98,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -109,28 +120,49 @@ public function ignoredByTest(): bool return $this->ignoredByTest; } + public function trigger(): IssueTrigger + { + return $this->trigger; + } + + /** + * @return non-empty-string + */ + public function stackTrace(): string + { + return $this->stackTrace; + } + + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } - $status = ''; + $details = [$this->test->id(), $this->trigger->asString()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } if ($this->ignoredByTest) { - $status = 'Test-Ignored '; - } elseif ($this->ignoredByBaseline) { - $status = 'Baseline-Ignored '; - } elseif ($this->suppressed) { - $status = 'Suppressed '; + $details[] = 'ignored by test'; + } + + if ($this->ignoredByBaseline) { + $details[] = 'ignored by baseline'; } return sprintf( - 'Test Triggered %sDeprecation (%s)%s', - $status, - $this->test->id(), + 'Test Triggered Deprecation (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Issue/ErrorTriggered.php b/src/Event/Events/Test/Issue/ErrorTriggered.php index f98cb0b4b82..7faefc634ce 100644 --- a/src/Event/Events/Test/Issue/ErrorTriggered.php +++ b/src/Event/Events/Test/Issue/ErrorTriggered.php @@ -10,13 +10,14 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,25 +27,25 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed) { @@ -67,7 +68,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -75,7 +76,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -83,7 +84,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -95,18 +96,28 @@ public function wasSuppressed(): bool return $this->suppressed; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } + $details = [$this->test->id()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } + return sprintf( - 'Test Triggered %sError (%s)%s', - $this->wasSuppressed() ? 'Suppressed ' : '', - $this->test->id(), + 'Test Triggered Error (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Issue/NoticeTriggered.php b/src/Event/Events/Test/Issue/NoticeTriggered.php index cf507ade038..237e1f18ac8 100644 --- a/src/Event/Events/Test/Issue/NoticeTriggered.php +++ b/src/Event/Events/Test/Issue/NoticeTriggered.php @@ -10,13 +10,14 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,26 +27,26 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; private bool $ignoredByBaseline; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { @@ -69,7 +70,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -77,7 +78,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -85,7 +86,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -102,26 +103,32 @@ public function ignoredByBaseline(): bool return $this->ignoredByBaseline; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } - $status = ''; + $details = [$this->test->id()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } if ($this->ignoredByBaseline) { - $status = 'Baseline-Ignored '; - } elseif ($this->suppressed) { - $status = 'Suppressed '; + $details[] = 'ignored by baseline'; } return sprintf( - 'Test Triggered %sNotice (%s)%s', - $status, - $this->test->id(), + 'Test Triggered Notice (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Issue/PhpDeprecationTriggered.php b/src/Event/Events/Test/Issue/PhpDeprecationTriggered.php index f7205e55c9f..5b9878ad40f 100644 --- a/src/Event/Events/Test/Issue/PhpDeprecationTriggered.php +++ b/src/Event/Events/Test/Issue/PhpDeprecationTriggered.php @@ -10,13 +10,15 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,29 +28,30 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; private bool $ignoredByBaseline; private bool $ignoredByTest; + private IssueTrigger $trigger; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line */ - public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest) + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline, bool $ignoredByTest, IssueTrigger $trigger) { $this->telemetryInfo = $telemetryInfo; $this->test = $test; @@ -58,6 +61,7 @@ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $m $this->suppressed = $suppressed; $this->ignoredByBaseline = $ignoredByBaseline; $this->ignoredByTest = $ignoredByTest; + $this->trigger = $trigger; } public function telemetryInfo(): Telemetry\Info @@ -71,7 +75,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -79,7 +83,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -87,7 +91,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -109,28 +113,41 @@ public function ignoredByTest(): bool return $this->ignoredByTest; } + public function trigger(): IssueTrigger + { + return $this->trigger; + } + + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } - $status = ''; + $details = [$this->test->id(), $this->trigger->asString()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } if ($this->ignoredByTest) { - $status = 'Test-Ignored '; - } elseif ($this->ignoredByBaseline) { - $status = 'Baseline-Ignored '; - } elseif ($this->suppressed) { - $status = 'Suppressed '; + $details[] = 'ignored by test'; + } + + if ($this->ignoredByBaseline) { + $details[] = 'ignored by baseline'; } return sprintf( - 'Test Triggered %sPHP Deprecation (%s)%s', - $status, - $this->test->id(), + 'Test Triggered PHP Deprecation (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Issue/PhpNoticeTriggered.php b/src/Event/Events/Test/Issue/PhpNoticeTriggered.php index 41f77767809..ad976cfcff2 100644 --- a/src/Event/Events/Test/Issue/PhpNoticeTriggered.php +++ b/src/Event/Events/Test/Issue/PhpNoticeTriggered.php @@ -10,13 +10,14 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,26 +27,26 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; private bool $ignoredByBaseline; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { @@ -69,7 +70,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -77,7 +78,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -85,7 +86,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -102,26 +103,32 @@ public function ignoredByBaseline(): bool return $this->ignoredByBaseline; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } - $status = ''; + $details = [$this->test->id()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } if ($this->ignoredByBaseline) { - $status = 'Baseline-Ignored '; - } elseif ($this->suppressed) { - $status = 'Suppressed '; + $details[] = 'ignored by baseline'; } return sprintf( - 'Test Triggered %sPHP Notice (%s)%s', - $status, - $this->test->id(), + 'Test Triggered PHP Notice (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Issue/PhpWarningTriggered.php b/src/Event/Events/Test/Issue/PhpWarningTriggered.php index 6365ba237a8..3d65125f753 100644 --- a/src/Event/Events/Test/Issue/PhpWarningTriggered.php +++ b/src/Event/Events/Test/Issue/PhpWarningTriggered.php @@ -10,13 +10,14 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,26 +27,26 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; private bool $ignoredByBaseline; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { @@ -69,7 +70,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -77,7 +78,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -85,7 +86,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -102,26 +103,32 @@ public function ignoredByBaseline(): bool return $this->ignoredByBaseline; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } - $status = ''; + $details = [$this->test->id()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } if ($this->ignoredByBaseline) { - $status = 'Baseline-Ignored '; - } elseif ($this->suppressed) { - $status = 'Suppressed '; + $details[] = 'ignored by baseline'; } return sprintf( - 'Test Triggered %sPHP Warning (%s)%s', - $status, - $this->test->id(), + 'Test Triggered PHP Warning (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Issue/PhpunitDeprecationTriggered.php b/src/Event/Events/Test/Issue/PhpunitDeprecationTriggered.php index bd91860738f..4e1603f355b 100644 --- a/src/Event/Events/Test/Issue/PhpunitDeprecationTriggered.php +++ b/src/Event/Events/Test/Issue/PhpunitDeprecationTriggered.php @@ -16,7 +16,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -24,8 +24,15 @@ { private Telemetry\Info $telemetryInfo; private Test $test; + + /** + * @var non-empty-string + */ private string $message; + /** + * @param non-empty-string $message + */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) { $this->telemetryInfo = $telemetryInfo; @@ -43,16 +50,22 @@ public function test(): Test return $this->test; } + /** + * @return non-empty-string + */ public function message(): string { return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/Issue/PhpunitErrorTriggered.php b/src/Event/Events/Test/Issue/PhpunitErrorTriggered.php index f9aec03f2dd..abd5e8a1f0b 100644 --- a/src/Event/Events/Test/Issue/PhpunitErrorTriggered.php +++ b/src/Event/Events/Test/Issue/PhpunitErrorTriggered.php @@ -17,7 +17,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -25,8 +25,15 @@ { private Telemetry\Info $telemetryInfo; private Test $test; + + /** + * @var non-empty-string + */ private string $message; + /** + * @param non-empty-string $message + */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) { $this->telemetryInfo = $telemetryInfo; @@ -44,16 +51,22 @@ public function test(): Test return $this->test; } + /** + * @return non-empty-string + */ public function message(): string { return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { $message = trim($this->message); - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/Issue/PhpunitNoticeTriggered.php b/src/Event/Events/Test/Issue/PhpunitNoticeTriggered.php new file mode 100644 index 00000000000..33984ba426b --- /dev/null +++ b/src/Event/Events/Test/Issue/PhpunitNoticeTriggered.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use function trim; +use PHPUnit\Event\Code\Test; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PhpunitNoticeTriggered implements Event +{ + private Telemetry\Info $telemetryInfo; + private Test $test; + + /** + * @var non-empty-string + */ + private string $message; + + /** + * @param non-empty-string $message + */ + public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->message = $message; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Test + { + return $this->test; + } + + /** + * @return non-empty-string + */ + public function message(): string + { + return $this->message; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = trim($this->message); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Test Triggered PHPUnit Notice (%s)%s', + $this->test->id(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/Issue/PhpunitNoticeTriggeredSubscriber.php b/src/Event/Events/Test/Issue/PhpunitNoticeTriggeredSubscriber.php new file mode 100644 index 00000000000..0935c6dd350 --- /dev/null +++ b/src/Event/Events/Test/Issue/PhpunitNoticeTriggeredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PhpunitNoticeTriggeredSubscriber extends Subscriber +{ + public function notify(PhpunitNoticeTriggered $event): void; +} diff --git a/src/Event/Events/Test/Issue/PhpunitWarningTriggered.php b/src/Event/Events/Test/Issue/PhpunitWarningTriggered.php index 1bfcb4d90f4..48847d2202e 100644 --- a/src/Event/Events/Test/Issue/PhpunitWarningTriggered.php +++ b/src/Event/Events/Test/Issue/PhpunitWarningTriggered.php @@ -16,7 +16,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -24,8 +24,15 @@ { private Telemetry\Info $telemetryInfo; private Test $test; + + /** + * @var non-empty-string + */ private string $message; + /** + * @param non-empty-string $message + */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message) { $this->telemetryInfo = $telemetryInfo; @@ -43,16 +50,22 @@ public function test(): Test return $this->test; } + /** + * @return non-empty-string + */ public function message(): string { return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/Issue/WarningTriggered.php b/src/Event/Events/Test/Issue/WarningTriggered.php index b2c5caca2bc..7b3e313bf26 100644 --- a/src/Event/Events/Test/Issue/WarningTriggered.php +++ b/src/Event/Events/Test/Issue/WarningTriggered.php @@ -10,13 +10,14 @@ namespace PHPUnit\Event\Test; use const PHP_EOL; +use function implode; use function sprintf; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -26,26 +27,26 @@ private Test $test; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $message; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; private bool $suppressed; private bool $ignoredByBaseline; /** - * @psalm-param non-empty-string $message - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line + * @param non-empty-string $message + * @param non-empty-string $file + * @param positive-int $line */ public function __construct(Telemetry\Info $telemetryInfo, Test $test, string $message, string $file, int $line, bool $suppressed, bool $ignoredByBaseline) { @@ -69,7 +70,7 @@ public function test(): Test } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function message(): string { @@ -77,7 +78,7 @@ public function message(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -85,7 +86,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -102,26 +103,32 @@ public function ignoredByBaseline(): bool return $this->ignoredByBaseline; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } - $status = ''; + $details = [$this->test->id()]; + + if ($this->suppressed) { + $details[] = 'suppressed using operator'; + } if ($this->ignoredByBaseline) { - $status = 'Baseline-Ignored '; - } elseif ($this->suppressed) { - $status = 'Suppressed '; + $details[] = 'ignored by baseline'; } return sprintf( - 'Test Triggered %sWarning (%s)%s', - $status, - $this->test->id(), + 'Test Triggered Warning (%s) in %s:%d%s', + implode(', ', $details), + $this->file, + $this->line, $message, ); } diff --git a/src/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php b/src/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php index 5581f7dda04..5631e1cf2b1 100644 --- a/src/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php +++ b/src/Event/Events/Test/Lifecycle/DataProviderMethodCalled.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry\Info; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -47,6 +47,9 @@ public function dataProviderMethod(): ClassMethod return $this->dataProviderMethod; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php b/src/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php index f12c0f2b9e4..ec26779949b 100644 --- a/src/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php +++ b/src/Event/Events/Test/Lifecycle/DataProviderMethodFinished.php @@ -11,13 +11,12 @@ use const PHP_EOL; use function sprintf; -use PHPUnit\Event\Code; use PHPUnit\Event\Code\ClassMethod; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -27,7 +26,7 @@ private ClassMethod $testMethod; /** - * @psalm-var list + * @var list */ private array $calledMethods; @@ -49,13 +48,16 @@ public function testMethod(): ClassMethod } /** - * @psalm-return list + * @return list */ public function calledMethods(): array { return $this->calledMethods; } + /** + * @return non-empty-string + */ public function asString(): string { $buffer = sprintf( diff --git a/src/Event/Events/Test/Lifecycle/Finished.php b/src/Event/Events/Test/Lifecycle/Finished.php index 72032cf7137..3cc9a52ffd8 100644 --- a/src/Event/Events/Test/Lifecycle/Finished.php +++ b/src/Event/Events/Test/Lifecycle/Finished.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,8 +23,15 @@ { private Telemetry\Info $telemetryInfo; private Code\Test $test; + + /** + * @var non-negative-int + */ private int $numberOfAssertionsPerformed; + /** + * @param non-negative-int $numberOfAssertionsPerformed + */ public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, int $numberOfAssertionsPerformed) { $this->telemetryInfo = $telemetryInfo; @@ -42,11 +49,17 @@ public function test(): Code\Test return $this->test; } + /** + * @return non-negative-int + */ public function numberOfAssertionsPerformed(): int { return $this->numberOfAssertionsPerformed; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/Lifecycle/PreparationErrored.php b/src/Event/Events/Test/Lifecycle/PreparationErrored.php new file mode 100644 index 00000000000..866bf5f1c4b --- /dev/null +++ b/src/Event/Events/Test/Lifecycle/PreparationErrored.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PreparationErrored implements Event +{ + private Telemetry\Info $telemetryInfo; + private Code\Test $test; + private Throwable $throwable; + + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, Throwable $throwable) + { + $this->telemetryInfo = $telemetryInfo; + $this->test = $test; + $this->throwable = $throwable; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function test(): Code\Test + { + return $this->test; + } + + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + + return sprintf( + 'Test Preparation Errored (%s)%s', + $this->test->id(), + $message, + ); + } +} diff --git a/src/Event/Events/Test/Lifecycle/PreparationErroredSubscriber.php b/src/Event/Events/Test/Lifecycle/PreparationErroredSubscriber.php new file mode 100644 index 00000000000..2cb43d2e838 --- /dev/null +++ b/src/Event/Events/Test/Lifecycle/PreparationErroredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface PreparationErroredSubscriber extends Subscriber +{ + public function notify(PreparationErrored $event): void; +} diff --git a/src/Event/Events/Test/Lifecycle/PreparationFailed.php b/src/Event/Events/Test/Lifecycle/PreparationFailed.php index 7380be938f4..7a8b1d67fcc 100644 --- a/src/Event/Events/Test/Lifecycle/PreparationFailed.php +++ b/src/Event/Events/Test/Lifecycle/PreparationFailed.php @@ -9,13 +9,15 @@ */ namespace PHPUnit\Event\Test; +use const PHP_EOL; use function sprintf; use PHPUnit\Event\Code; +use PHPUnit\Event\Code\Throwable; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,11 +25,13 @@ { private Telemetry\Info $telemetryInfo; private Code\Test $test; + private Throwable $throwable; - public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test) + public function __construct(Telemetry\Info $telemetryInfo, Code\Test $test, Throwable $throwable) { $this->telemetryInfo = $telemetryInfo; $this->test = $test; + $this->throwable = $throwable; } public function telemetryInfo(): Telemetry\Info @@ -40,11 +44,26 @@ public function test(): Code\Test return $this->test; } + public function throwable(): Throwable + { + return $this->throwable; + } + + /** + * @return non-empty-string + */ public function asString(): string { + $message = $this->throwable->message(); + + if ($message !== '') { + $message = PHP_EOL . $message; + } + return sprintf( - 'Test Preparation Failed (%s)', + 'Test Preparation Failed (%s)%s', $this->test->id(), + $message, ); } } diff --git a/src/Event/Events/Test/Lifecycle/PreparationStarted.php b/src/Event/Events/Test/Lifecycle/PreparationStarted.php index 6e975a622ee..7c548b08b0c 100644 --- a/src/Event/Events/Test/Lifecycle/PreparationStarted.php +++ b/src/Event/Events/Test/Lifecycle/PreparationStarted.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -40,6 +40,9 @@ public function test(): Code\Test return $this->test; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/Lifecycle/Prepared.php b/src/Event/Events/Test/Lifecycle/Prepared.php index 9e7cc694934..d83f1d59570 100644 --- a/src/Event/Events/Test/Lifecycle/Prepared.php +++ b/src/Event/Events/Test/Lifecycle/Prepared.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -40,6 +40,9 @@ public function test(): Code\Test return $this->test; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/Outcome/Errored.php b/src/Event/Events/Test/Outcome/Errored.php index 2a2c81e9189..ef0684989f9 100644 --- a/src/Event/Events/Test/Outcome/Errored.php +++ b/src/Event/Events/Test/Outcome/Errored.php @@ -18,7 +18,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -50,11 +50,14 @@ public function throwable(): Throwable return $this->throwable; } + /** + * @return non-empty-string + */ public function asString(): string { $message = trim($this->throwable->message()); - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/Outcome/Failed.php b/src/Event/Events/Test/Outcome/Failed.php index 6a6b8904c02..bcc5867f04d 100644 --- a/src/Event/Events/Test/Outcome/Failed.php +++ b/src/Event/Events/Test/Outcome/Failed.php @@ -19,7 +19,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -54,7 +54,7 @@ public function throwable(): Throwable } /** - * @psalm-assert-if-true !null $this->comparisonFailure + * @phpstan-assert-if-true !null $this->comparisonFailure */ public function hasComparisonFailure(): bool { @@ -73,11 +73,14 @@ public function comparisonFailure(): ComparisonFailure return $this->comparisonFailure; } + /** + * @return non-empty-string + */ public function asString(): string { $message = trim($this->throwable->message()); - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/Outcome/MarkedIncomplete.php b/src/Event/Events/Test/Outcome/MarkedIncomplete.php index 0b7a5cf8c4d..a69b48a427b 100644 --- a/src/Event/Events/Test/Outcome/MarkedIncomplete.php +++ b/src/Event/Events/Test/Outcome/MarkedIncomplete.php @@ -18,7 +18,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -50,11 +50,14 @@ public function throwable(): Throwable return $this->throwable; } + /** + * @return non-empty-string + */ public function asString(): string { $message = trim($this->throwable->message()); - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/Outcome/Passed.php b/src/Event/Events/Test/Outcome/Passed.php index 8eed03daf24..38f2d9816ec 100644 --- a/src/Event/Events/Test/Outcome/Passed.php +++ b/src/Event/Events/Test/Outcome/Passed.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -40,6 +40,9 @@ public function test(): Code\Test return $this->test; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/Outcome/Skipped.php b/src/Event/Events/Test/Outcome/Skipped.php index 2070c0cfbd2..fe605fff47d 100644 --- a/src/Event/Events/Test/Outcome/Skipped.php +++ b/src/Event/Events/Test/Outcome/Skipped.php @@ -16,7 +16,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -48,11 +48,14 @@ public function message(): string return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { $message = $this->message; - if (!empty($message)) { + if ($message !== '') { $message = PHP_EOL . $message; } diff --git a/src/Event/Events/Test/PrintedUnexpectedOutput.php b/src/Event/Events/Test/PrintedUnexpectedOutput.php index e2479fbbfb1..4a0ceab39ac 100644 --- a/src/Event/Events/Test/PrintedUnexpectedOutput.php +++ b/src/Event/Events/Test/PrintedUnexpectedOutput.php @@ -9,12 +9,13 @@ */ namespace PHPUnit\Event\Test; +use const PHP_EOL; use function sprintf; use PHPUnit\Event\Event; use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,12 +24,12 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $output; /** - * @psalm-param non-empty-string $output + * @param non-empty-string $output */ public function __construct(Telemetry\Info $telemetryInfo, string $output) { @@ -42,13 +43,16 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function output(): string { return $this->output; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/TestDouble/MockObjectCreated.php b/src/Event/Events/Test/TestDouble/MockObjectCreated.php index 357d930aa3f..8e91237c4f7 100644 --- a/src/Event/Events/Test/TestDouble/MockObjectCreated.php +++ b/src/Event/Events/Test/TestDouble/MockObjectCreated.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,12 +23,12 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(Telemetry\Info $telemetryInfo, string $className) { @@ -42,13 +42,16 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { return $this->className; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreated.php b/src/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreated.php deleted file mode 100644 index 115f1be106d..00000000000 --- a/src/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreated.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use function sprintf; -use PHPUnit\Event\Event; -use PHPUnit\Event\Telemetry; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class MockObjectForAbstractClassCreated implements Event -{ - private Telemetry\Info $telemetryInfo; - - /** - * @psalm-var class-string - */ - private string $className; - - /** - * @psalm-param class-string $className - */ - public function __construct(Telemetry\Info $telemetryInfo, string $className) - { - $this->telemetryInfo = $telemetryInfo; - $this->className = $className; - } - - public function telemetryInfo(): Telemetry\Info - { - return $this->telemetryInfo; - } - - /** - * @psalm-return class-string - */ - public function className(): string - { - return $this->className; - } - - public function asString(): string - { - return sprintf( - 'Mock Object Created (%s)', - $this->className, - ); - } -} diff --git a/src/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreatedSubscriber.php b/src/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreatedSubscriber.php deleted file mode 100644 index c335d1917d7..00000000000 --- a/src/Event/Events/Test/TestDouble/MockObjectForAbstractClassCreatedSubscriber.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\Subscriber; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface MockObjectForAbstractClassCreatedSubscriber extends Subscriber -{ - public function notify(MockObjectForAbstractClassCreated $event): void; -} diff --git a/src/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.php b/src/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.php index 08176afc0ef..3548189e8ca 100644 --- a/src/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.php +++ b/src/Event/Events/Test/TestDouble/MockObjectForIntersectionOfInterfacesCreated.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -24,12 +24,12 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var list + * @var list */ private array $interfaces; /** - * @psalm-param list $interfaces + * @param list $interfaces */ public function __construct(Telemetry\Info $telemetryInfo, array $interfaces) { @@ -50,6 +50,9 @@ public function interfaces(): array return $this->interfaces; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php b/src/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php deleted file mode 100644 index fe58c57d715..00000000000 --- a/src/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use function sprintf; -use PHPUnit\Event\Event; -use PHPUnit\Event\Telemetry; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class MockObjectForTraitCreated implements Event -{ - private Telemetry\Info $telemetryInfo; - - /** - * @psalm-var trait-string - */ - private string $traitName; - - /** - * @psalm-param trait-string $traitName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $traitName) - { - $this->telemetryInfo = $telemetryInfo; - $this->traitName = $traitName; - } - - public function telemetryInfo(): Telemetry\Info - { - return $this->telemetryInfo; - } - - /** - * @psalm-return trait-string - */ - public function traitName(): string - { - return $this->traitName; - } - - public function asString(): string - { - return sprintf( - 'Mock Object Created (%s)', - $this->traitName, - ); - } -} diff --git a/src/Event/Events/Test/TestDouble/MockObjectForTraitCreatedSubscriber.php b/src/Event/Events/Test/TestDouble/MockObjectForTraitCreatedSubscriber.php deleted file mode 100644 index a7e7dd07c1c..00000000000 --- a/src/Event/Events/Test/TestDouble/MockObjectForTraitCreatedSubscriber.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\Subscriber; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface MockObjectForTraitCreatedSubscriber extends Subscriber -{ - public function notify(MockObjectForTraitCreated $event): void; -} diff --git a/src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php b/src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php deleted file mode 100644 index 5eda4b8a37d..00000000000 --- a/src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use function sprintf; -use PHPUnit\Event\Event; -use PHPUnit\Event\Telemetry; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class MockObjectFromWsdlCreated implements Event -{ - private Telemetry\Info $telemetryInfo; - private string $wsdlFile; - - /** - * @psalm-var class-string - */ - private string $originalClassName; - - /** - * @psalm-var class-string - */ - private string $mockClassName; - - /** - * @psalm-var list - */ - private array $methods; - private bool $callOriginalConstructor; - private array $options; - - /** - * @psalm-param class-string $originalClassName - * @psalm-param class-string $mockClassName - */ - public function __construct(Telemetry\Info $telemetryInfo, string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options) - { - $this->telemetryInfo = $telemetryInfo; - $this->wsdlFile = $wsdlFile; - $this->originalClassName = $originalClassName; - $this->mockClassName = $mockClassName; - $this->methods = $methods; - $this->callOriginalConstructor = $callOriginalConstructor; - $this->options = $options; - } - - public function telemetryInfo(): Telemetry\Info - { - return $this->telemetryInfo; - } - - public function wsdlFile(): string - { - return $this->wsdlFile; - } - - /** - * @psalm-return class-string - */ - public function originalClassName(): string - { - return $this->originalClassName; - } - - /** - * @psalm-return class-string - */ - public function mockClassName(): string - { - return $this->mockClassName; - } - - /** - * @psalm-return list - */ - public function methods(): array - { - return $this->methods; - } - - public function callOriginalConstructor(): bool - { - return $this->callOriginalConstructor; - } - - public function options(): array - { - return $this->options; - } - - public function asString(): string - { - return sprintf( - 'Mock Object Created (%s)', - $this->wsdlFile, - ); - } -} diff --git a/src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreatedSubscriber.php b/src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreatedSubscriber.php deleted file mode 100644 index fb0524b21a0..00000000000 --- a/src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreatedSubscriber.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\Subscriber; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface MockObjectFromWsdlCreatedSubscriber extends Subscriber -{ - public function notify(MockObjectFromWsdlCreated $event): void; -} diff --git a/src/Event/Events/Test/TestDouble/PartialMockObjectCreated.php b/src/Event/Events/Test/TestDouble/PartialMockObjectCreated.php index a6c97dc266e..625747816ce 100644 --- a/src/Event/Events/Test/TestDouble/PartialMockObjectCreated.php +++ b/src/Event/Events/Test/TestDouble/PartialMockObjectCreated.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,17 +23,17 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var list + * @var list */ private array $methodNames; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(Telemetry\Info $telemetryInfo, string $className, string ...$methodNames) { @@ -48,7 +48,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -56,13 +56,16 @@ public function className(): string } /** - * @psalm-return list + * @return list */ public function methodNames(): array { return $this->methodNames; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/TestDouble/TestProxyCreated.php b/src/Event/Events/Test/TestDouble/TestProxyCreated.php deleted file mode 100644 index 4cd4545b976..00000000000 --- a/src/Event/Events/Test/TestDouble/TestProxyCreated.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use function sprintf; -use PHPUnit\Event\Event; -use PHPUnit\Event\Telemetry; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final readonly class TestProxyCreated implements Event -{ - private Telemetry\Info $telemetryInfo; - - /** - * @psalm-var class-string - */ - private string $className; - private string $constructorArguments; - - /** - * @psalm-param class-string $className - */ - public function __construct(Telemetry\Info $telemetryInfo, string $className, string $constructorArguments) - { - $this->telemetryInfo = $telemetryInfo; - $this->className = $className; - $this->constructorArguments = $constructorArguments; - } - - public function telemetryInfo(): Telemetry\Info - { - return $this->telemetryInfo; - } - - /** - * @psalm-return class-string - */ - public function className(): string - { - return $this->className; - } - - public function constructorArguments(): string - { - return $this->constructorArguments; - } - - public function asString(): string - { - return sprintf( - 'Test Proxy Created (%s)', - $this->className, - ); - } -} diff --git a/src/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php b/src/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php deleted file mode 100644 index 8af83844a80..00000000000 --- a/src/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\Subscriber; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface TestProxyCreatedSubscriber extends Subscriber -{ - public function notify(TestProxyCreated $event): void; -} diff --git a/src/Event/Events/Test/TestDouble/TestStubCreated.php b/src/Event/Events/Test/TestDouble/TestStubCreated.php index 424d9f44f20..667fbad4b68 100644 --- a/src/Event/Events/Test/TestDouble/TestStubCreated.php +++ b/src/Event/Events/Test/TestDouble/TestStubCreated.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -28,7 +28,7 @@ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(Telemetry\Info $telemetryInfo, string $className) { @@ -49,6 +49,9 @@ public function className(): string return $this->className; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.php b/src/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.php index fb4dccdd5a3..bba93d9e02a 100644 --- a/src/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.php +++ b/src/Event/Events/Test/TestDouble/TestStubForIntersectionOfInterfacesCreated.php @@ -15,7 +15,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -24,12 +24,12 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var list + * @var list */ private array $interfaces; /** - * @psalm-param list $interfaces + * @param list $interfaces */ public function __construct(Telemetry\Info $telemetryInfo, array $interfaces) { @@ -50,6 +50,9 @@ public function interfaces(): array return $this->interfaces; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestRunner/BootstrapFinished.php b/src/Event/Events/TestRunner/BootstrapFinished.php index c92b7a647e1..8e46a00a836 100644 --- a/src/Event/Events/TestRunner/BootstrapFinished.php +++ b/src/Event/Events/TestRunner/BootstrapFinished.php @@ -14,15 +14,22 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class BootstrapFinished implements Event { private Telemetry\Info $telemetryInfo; + + /** + * @var non-empty-string + */ private string $filename; + /** + * @param non-empty-string $filename + */ public function __construct(Telemetry\Info $telemetryInfo, string $filename) { $this->telemetryInfo = $telemetryInfo; @@ -34,11 +41,17 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function filename(): string { return $this->filename; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestRunner/ChildProcessFinished.php b/src/Event/Events/TestRunner/ChildProcessFinished.php new file mode 100644 index 00000000000..705a0c63425 --- /dev/null +++ b/src/Event/Events/TestRunner/ChildProcessFinished.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class ChildProcessFinished implements Event +{ + private Telemetry\Info $telemetryInfo; + private string $stdout; + private string $stderr; + + public function __construct(Telemetry\Info $telemetryInfo, string $stdout, string $stderr) + { + $this->telemetryInfo = $telemetryInfo; + $this->stdout = $stdout; + $this->stderr = $stderr; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function stdout(): string + { + return $this->stdout; + } + + public function stderr(): string + { + return $this->stderr; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + return 'Child Process Finished'; + } +} diff --git a/src/Event/Events/TestRunner/ChildProcessFinishedSubscriber.php b/src/Event/Events/TestRunner/ChildProcessFinishedSubscriber.php new file mode 100644 index 00000000000..45fefa1827b --- /dev/null +++ b/src/Event/Events/TestRunner/ChildProcessFinishedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ChildProcessFinishedSubscriber extends Subscriber +{ + public function notify(ChildProcessFinished $event): void; +} diff --git a/src/Event/Events/TestRunner/ChildProcessStarted.php b/src/Event/Events/TestRunner/ChildProcessStarted.php new file mode 100644 index 00000000000..2c20471e27f --- /dev/null +++ b/src/Event/Events/TestRunner/ChildProcessStarted.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class ChildProcessStarted implements Event +{ + private Telemetry\Info $telemetryInfo; + + public function __construct(Telemetry\Info $telemetryInfo) + { + $this->telemetryInfo = $telemetryInfo; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + return 'Child Process Started'; + } +} diff --git a/src/Event/Events/TestRunner/ChildProcessStartedSubscriber.php b/src/Event/Events/TestRunner/ChildProcessStartedSubscriber.php new file mode 100644 index 00000000000..4ba549ce8a0 --- /dev/null +++ b/src/Event/Events/TestRunner/ChildProcessStartedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface ChildProcessStartedSubscriber extends Subscriber +{ + public function notify(ChildProcessStarted $event): void; +} diff --git a/src/Event/Events/TestRunner/Configured.php b/src/Event/Events/TestRunner/Configured.php index 4bbbc24be55..e0d14360a3b 100644 --- a/src/Event/Events/TestRunner/Configured.php +++ b/src/Event/Events/TestRunner/Configured.php @@ -37,6 +37,9 @@ public function configuration(): Configuration return $this->configuration; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Configured'; diff --git a/src/Event/Events/TestRunner/DeprecationTriggered.php b/src/Event/Events/TestRunner/DeprecationTriggered.php index 0ee029959fd..5cfef8f78cb 100644 --- a/src/Event/Events/TestRunner/DeprecationTriggered.php +++ b/src/Event/Events/TestRunner/DeprecationTriggered.php @@ -14,15 +14,22 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class DeprecationTriggered implements Event { private Telemetry\Info $telemetryInfo; + + /** + * @var non-empty-string + */ private string $message; + /** + * @param non-empty-string $message + */ public function __construct(Telemetry\Info $telemetryInfo, string $message) { $this->telemetryInfo = $telemetryInfo; @@ -34,11 +41,17 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function message(): string { return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestRunner/EventFacadeSealed.php b/src/Event/Events/TestRunner/EventFacadeSealed.php index bbcc3904c78..bd4f5f60305 100644 --- a/src/Event/Events/TestRunner/EventFacadeSealed.php +++ b/src/Event/Events/TestRunner/EventFacadeSealed.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Event Facade Sealed'; diff --git a/src/Event/Events/TestRunner/ExecutionAborted.php b/src/Event/Events/TestRunner/ExecutionAborted.php index 635a3b4308a..6107e099b57 100644 --- a/src/Event/Events/TestRunner/ExecutionAborted.php +++ b/src/Event/Events/TestRunner/ExecutionAborted.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Execution Aborted'; diff --git a/src/Event/Events/TestRunner/ExecutionFinished.php b/src/Event/Events/TestRunner/ExecutionFinished.php index c1d725aea3f..25789fe7ca1 100644 --- a/src/Event/Events/TestRunner/ExecutionFinished.php +++ b/src/Event/Events/TestRunner/ExecutionFinished.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Execution Finished'; diff --git a/src/Event/Events/TestRunner/ExecutionStarted.php b/src/Event/Events/TestRunner/ExecutionStarted.php index a3aa38a6018..e38a2a4d99f 100644 --- a/src/Event/Events/TestRunner/ExecutionStarted.php +++ b/src/Event/Events/TestRunner/ExecutionStarted.php @@ -15,7 +15,7 @@ use PHPUnit\Event\TestSuite\TestSuite; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -40,6 +40,9 @@ public function testSuite(): TestSuite return $this->testSuite; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestRunner/ExtensionBootstrapped.php b/src/Event/Events/TestRunner/ExtensionBootstrapped.php index 1760fa3a08c..4ae1a6d5bd4 100644 --- a/src/Event/Events/TestRunner/ExtensionBootstrapped.php +++ b/src/Event/Events/TestRunner/ExtensionBootstrapped.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -23,18 +23,18 @@ private Telemetry\Info $telemetryInfo; /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var array + * @var array */ private array $parameters; /** - * @psalm-param class-string $className - * @psalm-param array $parameters + * @param class-string $className + * @param array $parameters */ public function __construct(Telemetry\Info $telemetryInfo, string $className, array $parameters) { @@ -49,7 +49,7 @@ public function telemetryInfo(): Telemetry\Info } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -57,13 +57,16 @@ public function className(): string } /** - * @psalm-return array + * @return array */ public function parameters(): array { return $this->parameters; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestRunner/ExtensionLoadedFromPhar.php b/src/Event/Events/TestRunner/ExtensionLoadedFromPhar.php index 2e11f04d3f7..2ce358d5c75 100644 --- a/src/Event/Events/TestRunner/ExtensionLoadedFromPhar.php +++ b/src/Event/Events/TestRunner/ExtensionLoadedFromPhar.php @@ -14,17 +14,34 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class ExtensionLoadedFromPhar implements Event { private Telemetry\Info $telemetryInfo; + + /** + * @var non-empty-string + */ private string $filename; + + /** + * @var non-empty-string + */ private string $name; + + /** + * @var non-empty-string + */ private string $version; + /** + * @param non-empty-string $filename + * @param non-empty-string $name + * @param non-empty-string $version + */ public function __construct(Telemetry\Info $telemetryInfo, string $filename, string $name, string $version) { $this->telemetryInfo = $telemetryInfo; @@ -38,21 +55,33 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function filename(): string { return $this->filename; } + /** + * @return non-empty-string + */ public function name(): string { return $this->name; } + /** + * @return non-empty-string + */ public function version(): string { return $this->version; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestRunner/Finished.php b/src/Event/Events/TestRunner/Finished.php index 9133acc4418..2abc685bb9e 100644 --- a/src/Event/Events/TestRunner/Finished.php +++ b/src/Event/Events/TestRunner/Finished.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Finished'; diff --git a/src/Event/Events/TestRunner/GarbageCollectionDisabled.php b/src/Event/Events/TestRunner/GarbageCollectionDisabled.php index 2690f69c565..4324a5c157c 100644 --- a/src/Event/Events/TestRunner/GarbageCollectionDisabled.php +++ b/src/Event/Events/TestRunner/GarbageCollectionDisabled.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Disabled Garbage Collection'; diff --git a/src/Event/Events/TestRunner/GarbageCollectionEnabled.php b/src/Event/Events/TestRunner/GarbageCollectionEnabled.php index cabeed434c0..1c4e088874f 100644 --- a/src/Event/Events/TestRunner/GarbageCollectionEnabled.php +++ b/src/Event/Events/TestRunner/GarbageCollectionEnabled.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Enabled Garbage Collection'; diff --git a/src/Event/Events/TestRunner/GarbageCollectionTriggered.php b/src/Event/Events/TestRunner/GarbageCollectionTriggered.php index ade9ea5e861..d6a1ce643a9 100644 --- a/src/Event/Events/TestRunner/GarbageCollectionTriggered.php +++ b/src/Event/Events/TestRunner/GarbageCollectionTriggered.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Triggered Garbage Collection'; diff --git a/src/Event/Events/TestRunner/NoticeTriggered.php b/src/Event/Events/TestRunner/NoticeTriggered.php new file mode 100644 index 00000000000..a5bfa04f386 --- /dev/null +++ b/src/Event/Events/TestRunner/NoticeTriggered.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class NoticeTriggered implements Event +{ + private Telemetry\Info $telemetryInfo; + private string $message; + + public function __construct(Telemetry\Info $telemetryInfo, string $message) + { + $this->telemetryInfo = $telemetryInfo; + $this->message = $message; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + public function message(): string + { + return $this->message; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + return sprintf( + 'Test Runner Triggered Notice (%s)', + $this->message, + ); + } +} diff --git a/src/Event/Events/TestRunner/NoticeTriggeredSubscriber.php b/src/Event/Events/TestRunner/NoticeTriggeredSubscriber.php new file mode 100644 index 00000000000..be76b2c639e --- /dev/null +++ b/src/Event/Events/TestRunner/NoticeTriggeredSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface NoticeTriggeredSubscriber extends Subscriber +{ + public function notify(NoticeTriggered $event): void; +} diff --git a/src/Event/Events/TestRunner/Started.php b/src/Event/Events/TestRunner/Started.php index ec18d665482..a5840110850 100644 --- a/src/Event/Events/TestRunner/Started.php +++ b/src/Event/Events/TestRunner/Started.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -31,6 +31,9 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Runner Started'; diff --git a/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinished.php b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinished.php new file mode 100644 index 00000000000..d484528ec9c --- /dev/null +++ b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinished.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use function sprintf; +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class StaticAnalysisForCodeCoverageFinished implements Event +{ + private Telemetry\Info $telemetryInfo; + + /** + * @var non-negative-int + */ + private int $cacheHits; + + /** + * @var non-negative-int + */ + private int $cacheMisses; + + /** + * @param non-negative-int $cacheHits + * @param non-negative-int $cacheMisses + */ + public function __construct(Telemetry\Info $telemetryInfo, int $cacheHits, int $cacheMisses) + { + $this->telemetryInfo = $telemetryInfo; + $this->cacheHits = $cacheHits; + $this->cacheMisses = $cacheMisses; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + /** + * @return non-negative-int + */ + public function cacheHits(): int + { + return $this->cacheHits; + } + + /** + * @return non-negative-int + */ + public function cacheMisses(): int + { + return $this->cacheMisses; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + return sprintf( + 'Static Analysis for Code Coverage Finished (%d cache hits, %d cache misses)', + $this->cacheHits, + $this->cacheMisses, + ); + } +} diff --git a/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinishedSubscriber.php b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinishedSubscriber.php new file mode 100644 index 00000000000..eaf4f34856e --- /dev/null +++ b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinishedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface StaticAnalysisForCodeCoverageFinishedSubscriber extends Subscriber +{ + public function notify(StaticAnalysisForCodeCoverageFinished $event): void; +} diff --git a/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStarted.php b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStarted.php new file mode 100644 index 00000000000..d121097272e --- /dev/null +++ b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStarted.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Event; +use PHPUnit\Event\Telemetry; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class StaticAnalysisForCodeCoverageStarted implements Event +{ + private Telemetry\Info $telemetryInfo; + + public function __construct(Telemetry\Info $telemetryInfo) + { + $this->telemetryInfo = $telemetryInfo; + } + + public function telemetryInfo(): Telemetry\Info + { + return $this->telemetryInfo; + } + + /** + * @return non-empty-string + */ + public function asString(): string + { + return 'Static Analysis for Code Coverage Started'; + } +} diff --git a/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStartedSubscriber.php b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStartedSubscriber.php new file mode 100644 index 00000000000..642bf712c80 --- /dev/null +++ b/src/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStartedSubscriber.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\Subscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface StaticAnalysisForCodeCoverageStartedSubscriber extends Subscriber +{ + public function notify(StaticAnalysisForCodeCoverageStarted $event): void; +} diff --git a/src/Event/Events/TestRunner/WarningTriggered.php b/src/Event/Events/TestRunner/WarningTriggered.php index be424027545..e9df01be971 100644 --- a/src/Event/Events/TestRunner/WarningTriggered.php +++ b/src/Event/Events/TestRunner/WarningTriggered.php @@ -14,15 +14,22 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class WarningTriggered implements Event { private Telemetry\Info $telemetryInfo; + + /** + * @var non-empty-string + */ private string $message; + /** + * @param non-empty-string $message + */ public function __construct(Telemetry\Info $telemetryInfo, string $message) { $this->telemetryInfo = $telemetryInfo; @@ -34,11 +41,17 @@ public function telemetryInfo(): Telemetry\Info return $this->telemetryInfo; } + /** + * @return non-empty-string + */ public function message(): string { return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestSuite/Filtered.php b/src/Event/Events/TestSuite/Filtered.php index 29b6c8ee616..96d626ce4ef 100644 --- a/src/Event/Events/TestSuite/Filtered.php +++ b/src/Event/Events/TestSuite/Filtered.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -39,6 +39,9 @@ public function testSuite(): TestSuite return $this->testSuite; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestSuite/Finished.php b/src/Event/Events/TestSuite/Finished.php index e729b72b661..a24ca869296 100644 --- a/src/Event/Events/TestSuite/Finished.php +++ b/src/Event/Events/TestSuite/Finished.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -39,6 +39,9 @@ public function testSuite(): TestSuite return $this->testSuite; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestSuite/Loaded.php b/src/Event/Events/TestSuite/Loaded.php index 9f2aaa7479d..d278c0ddc04 100644 --- a/src/Event/Events/TestSuite/Loaded.php +++ b/src/Event/Events/TestSuite/Loaded.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -39,6 +39,9 @@ public function testSuite(): TestSuite return $this->testSuite; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestSuite/Skipped.php b/src/Event/Events/TestSuite/Skipped.php index c1692e4a3c8..efe9c1fff1a 100644 --- a/src/Event/Events/TestSuite/Skipped.php +++ b/src/Event/Events/TestSuite/Skipped.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -46,6 +46,9 @@ public function message(): string return $this->message; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Events/TestSuite/Sorted.php b/src/Event/Events/TestSuite/Sorted.php index e0dbf1b94c5..a73461db8bf 100644 --- a/src/Event/Events/TestSuite/Sorted.php +++ b/src/Event/Events/TestSuite/Sorted.php @@ -13,7 +13,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -52,6 +52,9 @@ public function resolveDependencies(): bool return $this->resolveDependencies; } + /** + * @return non-empty-string + */ public function asString(): string { return 'Test Suite Sorted'; diff --git a/src/Event/Events/TestSuite/Started.php b/src/Event/Events/TestSuite/Started.php index 25ba7be9657..36fee1f0065 100644 --- a/src/Event/Events/TestSuite/Started.php +++ b/src/Event/Events/TestSuite/Started.php @@ -14,7 +14,7 @@ use PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -39,6 +39,9 @@ public function testSuite(): TestSuite return $this->testSuite; } + /** + * @return non-empty-string + */ public function asString(): string { return sprintf( diff --git a/src/Event/Exception/NoTestCaseObjectOnCallStackException.php b/src/Event/Exception/NoTestCaseObjectOnCallStackException.php index 6e5b03422dc..35b4c25af75 100644 --- a/src/Event/Exception/NoTestCaseObjectOnCallStackException.php +++ b/src/Event/Exception/NoTestCaseObjectOnCallStackException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoTestCaseObjectOnCallStackException extends RuntimeException implements Exception diff --git a/src/Event/Facade.php b/src/Event/Facade.php index bda44484acd..64d39c535a5 100644 --- a/src/Event/Facade.php +++ b/src/Event/Facade.php @@ -9,12 +9,14 @@ */ namespace PHPUnit\Event; -use function gc_status; +use function assert; +use function interface_exists; use PHPUnit\Event\Telemetry\HRTime; -use PHPUnit\Event\Telemetry\Php81GarbageCollectorStatusProvider; -use PHPUnit\Event\Telemetry\Php83GarbageCollectorStatusProvider; +use PHPUnit\Event\Telemetry\SystemGarbageCollectorStatusProvider; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Facade @@ -22,7 +24,6 @@ final class Facade private static ?self $instance = null; private Emitter $emitter; private ?TypeMap $typeMap = null; - private ?Emitter $suspended = null; private ?DeferringDispatcher $deferringDispatcher = null; private bool $sealed = false; @@ -81,8 +82,12 @@ public function registerTracer(Tracer\Tracer $tracer): void $this->deferredDispatcher()->registerTracer($tracer); } - /** @noinspection PhpUnused */ - public function initForIsolation(HRTime $offset, bool $exportObjects): CollectingDispatcher + /** + * @codeCoverageIgnore + * + * @noinspection PhpUnused + */ + public function initForIsolation(HRTime $offset): CollectingDispatcher { $dispatcher = new CollectingDispatcher; @@ -91,14 +96,10 @@ public function initForIsolation(HRTime $offset, bool $exportObjects): Collectin new Telemetry\System( new Telemetry\SystemStopWatchWithOffset($offset), new Telemetry\SystemMemoryMeter, - $this->garbageCollectorStatusProvider(), + new SystemGarbageCollectorStatusProvider, ), ); - if ($exportObjects) { - $this->emitter->exportObjects(); - } - $this->sealed = true; return $dispatcher; @@ -106,10 +107,6 @@ public function initForIsolation(HRTime $offset, bool $exportObjects): Collectin public function forward(EventCollection $events): void { - if ($this->suspended !== null) { - return; - } - $dispatcher = $this->deferredDispatcher(); foreach ($events as $event) { @@ -139,7 +136,7 @@ private function createTelemetrySystem(): Telemetry\System return new Telemetry\System( new Telemetry\SystemStopWatch, new Telemetry\SystemMemoryMeter, - $this->garbageCollectorStatusProvider(), + new SystemGarbageCollectorStatusProvider, ); } @@ -177,15 +174,20 @@ private function registerDefaultTypes(TypeMap $typeMap): void Test\DataProviderMethodFinished::class, Test\MarkedIncomplete::class, Test\AfterLastTestMethodCalled::class, + Test\AfterLastTestMethodErrored::class, + Test\AfterLastTestMethodFailed::class, Test\AfterLastTestMethodFinished::class, Test\AfterTestMethodCalled::class, + Test\AfterTestMethodErrored::class, + Test\AfterTestMethodFailed::class, Test\AfterTestMethodFinished::class, - Test\AssertionSucceeded::class, - Test\AssertionFailed::class, Test\BeforeFirstTestMethodCalled::class, Test\BeforeFirstTestMethodErrored::class, + Test\BeforeFirstTestMethodFailed::class, Test\BeforeFirstTestMethodFinished::class, Test\BeforeTestMethodCalled::class, + Test\BeforeTestMethodErrored::class, + Test\BeforeTestMethodFailed::class, Test\BeforeTestMethodFinished::class, Test\ComparatorRegistered::class, Test\ConsideredRisky::class, @@ -199,27 +201,29 @@ private function registerDefaultTypes(TypeMap $typeMap): void Test\PhpDeprecationTriggered::class, Test\PhpNoticeTriggered::class, Test\PhpunitDeprecationTriggered::class, + Test\PhpunitNoticeTriggered::class, Test\PhpunitErrorTriggered::class, Test\PhpunitWarningTriggered::class, Test\PhpWarningTriggered::class, Test\PostConditionCalled::class, + Test\PostConditionErrored::class, + Test\PostConditionFailed::class, Test\PostConditionFinished::class, Test\PreConditionCalled::class, + Test\PreConditionErrored::class, + Test\PreConditionFailed::class, Test\PreConditionFinished::class, Test\PreparationStarted::class, Test\Prepared::class, + Test\PreparationErrored::class, Test\PreparationFailed::class, Test\PrintedUnexpectedOutput::class, Test\Skipped::class, Test\WarningTriggered::class, Test\MockObjectCreated::class, - Test\MockObjectForAbstractClassCreated::class, Test\MockObjectForIntersectionOfInterfacesCreated::class, - Test\MockObjectForTraitCreated::class, - Test\MockObjectFromWsdlCreated::class, Test\PartialMockObjectCreated::class, - Test\TestProxyCreated::class, Test\TestStubCreated::class, Test\TestStubForIntersectionOfInterfacesCreated::class, @@ -234,10 +238,15 @@ private function registerDefaultTypes(TypeMap $typeMap): void TestRunner\Finished::class, TestRunner\Started::class, TestRunner\DeprecationTriggered::class, + TestRunner\NoticeTriggered::class, TestRunner\WarningTriggered::class, TestRunner\GarbageCollectionDisabled::class, TestRunner\GarbageCollectionTriggered::class, TestRunner\GarbageCollectionEnabled::class, + TestRunner\ChildProcessFinished::class, + TestRunner\ChildProcessStarted::class, + TestRunner\StaticAnalysisForCodeCoverageFinished::class, + TestRunner\StaticAnalysisForCodeCoverageStarted::class, TestSuite\Filtered::class, TestSuite\Finished::class, @@ -248,19 +257,11 @@ private function registerDefaultTypes(TypeMap $typeMap): void ]; foreach ($defaultEvents as $eventClass) { - $typeMap->addMapping( - $eventClass . 'Subscriber', - $eventClass, - ); - } - } + $subscriberInterface = $eventClass . 'Subscriber'; - private function garbageCollectorStatusProvider(): Telemetry\GarbageCollectorStatusProvider - { - if (!isset(gc_status()['running'])) { - return new Php81GarbageCollectorStatusProvider; - } + assert(interface_exists($subscriberInterface)); - return new Php83GarbageCollectorStatusProvider; + $typeMap->addMapping($subscriberInterface, $eventClass); + } } } diff --git a/src/Event/TypeMap.php b/src/Event/TypeMap.php index b421732072b..e525e446d70 100644 --- a/src/Event/TypeMap.php +++ b/src/Event/TypeMap.php @@ -17,18 +17,20 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TypeMap { /** - * @psalm-var array + * @var array */ private array $mapping = []; /** - * @psalm-param class-string $subscriberInterface - * @psalm-param class-string $eventClass + * @param class-string $subscriberInterface + * @param class-string $eventClass * * @throws EventAlreadyAssignedException * @throws InvalidEventException @@ -66,9 +68,9 @@ public function isKnownEventType(Event $event): bool } /** - * @psalm-return class-string - * * @throws MapError + * + * @return class-string */ public function map(Subscriber $subscriber): string { @@ -87,7 +89,7 @@ public function map(Subscriber $subscriber): string } /** - * @psalm-param class-string $subscriberInterface + * @param class-string $subscriberInterface * * @throws UnknownSubscriberException */ @@ -104,7 +106,7 @@ private function ensureSubscriberInterfaceExists(string $subscriberInterface): v } /** - * @psalm-param class-string $eventClass + * @param class-string $eventClass * * @throws UnknownEventException */ @@ -121,7 +123,7 @@ private function ensureEventClassExists(string $eventClass): void } /** - * @psalm-param class-string $subscriberInterface + * @param class-string $subscriberInterface * * @throws InvalidSubscriberException */ @@ -138,7 +140,7 @@ private function ensureSubscriberInterfaceExtendsInterface(string $subscriberInt } /** - * @psalm-param class-string $eventClass + * @param class-string $eventClass * * @throws InvalidEventException */ @@ -155,7 +157,7 @@ private function ensureEventClassImplementsEventInterface(string $eventClass): v } /** - * @psalm-param class-string $subscriberInterface + * @param class-string $subscriberInterface * * @throws SubscriberTypeAlreadyRegisteredException */ @@ -172,7 +174,7 @@ private function ensureSubscriberWasNotAlreadyRegistered(string $subscriberInter } /** - * @psalm-param class-string $eventClass + * @param class-string $eventClass * * @throws EventAlreadyAssignedException */ diff --git a/src/Event/Value/ClassMethod.php b/src/Event/Value/ClassMethod.php index 5eb58ba72a8..2a94033a5db 100644 --- a/src/Event/Value/ClassMethod.php +++ b/src/Event/Value/ClassMethod.php @@ -10,25 +10,25 @@ namespace PHPUnit\Event\Code; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class ClassMethod { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { @@ -37,7 +37,7 @@ public function __construct(string $className, string $methodName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -45,7 +45,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Event/Value/ComparisonFailure.php b/src/Event/Value/ComparisonFailure.php index 9b88901a31f..c31f93e65cd 100644 --- a/src/Event/Value/ComparisonFailure.php +++ b/src/Event/Value/ComparisonFailure.php @@ -10,7 +10,7 @@ namespace PHPUnit\Event\Code; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/ComparisonFailureBuilder.php b/src/Event/Value/ComparisonFailureBuilder.php index be411a9d983..53fe0d09aec 100644 --- a/src/Event/Value/ComparisonFailureBuilder.php +++ b/src/Event/Value/ComparisonFailureBuilder.php @@ -16,9 +16,11 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ComparisonFailureBuilder +final readonly class ComparisonFailureBuilder { public static function from(Throwable $t): ?ComparisonFailure { @@ -26,19 +28,19 @@ public static function from(Throwable $t): ?ComparisonFailure return null; } - if (!$t->getComparisonFailure()) { + if ($t->getComparisonFailure() === null) { return null; } $expectedAsString = $t->getComparisonFailure()->getExpectedAsString(); - if (empty($expectedAsString)) { + if ($expectedAsString === '') { $expectedAsString = self::mapScalarValueToString($t->getComparisonFailure()->getExpected()); } $actualAsString = $t->getComparisonFailure()->getActualAsString(); - if (empty($actualAsString)) { + if ($actualAsString === '') { $actualAsString = self::mapScalarValueToString($t->getComparisonFailure()->getActual()); } diff --git a/src/Event/Value/Runtime/OperatingSystem.php b/src/Event/Value/Runtime/OperatingSystem.php index 628c439221e..508d809f103 100644 --- a/src/Event/Value/Runtime/OperatingSystem.php +++ b/src/Event/Value/Runtime/OperatingSystem.php @@ -13,7 +13,7 @@ use const PHP_OS_FAMILY; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Runtime/PHP.php b/src/Event/Value/Runtime/PHP.php index e81953d1f52..46004f98c65 100644 --- a/src/Event/Value/Runtime/PHP.php +++ b/src/Event/Value/Runtime/PHP.php @@ -21,7 +21,7 @@ use function sort; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -36,7 +36,7 @@ private string $sapi; /** - * @psalm-var list + * @var list */ private array $extensions; @@ -96,7 +96,7 @@ public function versionId(): int } /** - * @psalm-return list + * @return list */ public function extensions(): array { diff --git a/src/Event/Value/Runtime/PHPUnit.php b/src/Event/Value/Runtime/PHPUnit.php index c4006bc15c8..7f85133d2cc 100644 --- a/src/Event/Value/Runtime/PHPUnit.php +++ b/src/Event/Value/Runtime/PHPUnit.php @@ -12,7 +12,7 @@ use PHPUnit\Runner\Version; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Runtime/Runtime.php b/src/Event/Value/Runtime/Runtime.php index 406393c2698..552ec98878f 100644 --- a/src/Event/Value/Runtime/Runtime.php +++ b/src/Event/Value/Runtime/Runtime.php @@ -12,7 +12,7 @@ use function sprintf; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Telemetry/Duration.php b/src/Event/Value/Telemetry/Duration.php index 40ded7b1b73..230150a159b 100644 --- a/src/Event/Value/Telemetry/Duration.php +++ b/src/Event/Value/Telemetry/Duration.php @@ -14,7 +14,7 @@ use PHPUnit\Event\InvalidArgumentException; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Telemetry/GarbageCollectorStatus.php b/src/Event/Value/Telemetry/GarbageCollectorStatus.php index e1eec1bf7f7..f8bb77a74ec 100644 --- a/src/Event/Value/Telemetry/GarbageCollectorStatus.php +++ b/src/Event/Value/Telemetry/GarbageCollectorStatus.php @@ -9,10 +9,8 @@ */ namespace PHPUnit\Event\Telemetry; -use PHPUnit\Event\RuntimeException; - /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -22,16 +20,16 @@ private int $collected; private int $threshold; private int $roots; - private ?float $applicationTime; - private ?float $collectorTime; - private ?float $destructorTime; - private ?float $freeTime; - private ?bool $running; - private ?bool $protected; - private ?bool $full; - private ?int $bufferSize; - - public function __construct(int $runs, int $collected, int $threshold, int $roots, ?float $applicationTime, ?float $collectorTime, ?float $destructorTime, ?float $freeTime, ?bool $running, ?bool $protected, ?bool $full, ?int $bufferSize) + private float $applicationTime; + private float $collectorTime; + private float $destructorTime; + private float $freeTime; + private bool $running; + private bool $protected; + private bool $full; + private int $bufferSize; + + public function __construct(int $runs, int $collected, int $threshold, int $roots, float $applicationTime, float $collectorTime, float $destructorTime, float $freeTime, bool $running, bool $protected, bool $full, int $bufferSize) { $this->runs = $runs; $this->collected = $collected; @@ -67,114 +65,43 @@ public function roots(): int return $this->roots; } - /** - * @psalm-assert-if-true !null $this->applicationTime - * @psalm-assert-if-true !null $this->collectorTime - * @psalm-assert-if-true !null $this->destructorTime - * @psalm-assert-if-true !null $this->freeTime - * @psalm-assert-if-true !null $this->running - * @psalm-assert-if-true !null $this->protected - * @psalm-assert-if-true !null $this->full - * @psalm-assert-if-true !null $this->bufferSize - */ - public function hasExtendedInformation(): bool - { - return $this->running !== null; - } - - /** - * @throws RuntimeException on PHP < 8.3 - */ public function applicationTime(): float { - if ($this->applicationTime === null) { - throw new RuntimeException('Information not available'); - } - return $this->applicationTime; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function collectorTime(): float { - if ($this->collectorTime === null) { - throw new RuntimeException('Information not available'); - } - return $this->collectorTime; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function destructorTime(): float { - if ($this->destructorTime === null) { - throw new RuntimeException('Information not available'); - } - return $this->destructorTime; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function freeTime(): float { - if ($this->freeTime === null) { - throw new RuntimeException('Information not available'); - } - return $this->freeTime; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function isRunning(): bool { - if ($this->running === null) { - throw new RuntimeException('Information not available'); - } - return $this->running; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function isProtected(): bool { - if ($this->protected === null) { - throw new RuntimeException('Information not available'); - } - return $this->protected; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function isFull(): bool { - if ($this->full === null) { - throw new RuntimeException('Information not available'); - } - return $this->full; } - /** - * @throws RuntimeException on PHP < 8.3 - */ public function bufferSize(): int { - if ($this->bufferSize === null) { - throw new RuntimeException('Information not available'); - } - return $this->bufferSize; } } diff --git a/src/Event/Value/Telemetry/GarbageCollectorStatusProvider.php b/src/Event/Value/Telemetry/GarbageCollectorStatusProvider.php index 15651208e78..09bede2e539 100644 --- a/src/Event/Value/Telemetry/GarbageCollectorStatusProvider.php +++ b/src/Event/Value/Telemetry/GarbageCollectorStatusProvider.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event\Telemetry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface GarbageCollectorStatusProvider diff --git a/src/Event/Value/Telemetry/HRTime.php b/src/Event/Value/Telemetry/HRTime.php index 96e0e559a5a..8a7b97ebd04 100644 --- a/src/Event/Value/Telemetry/HRTime.php +++ b/src/Event/Value/Telemetry/HRTime.php @@ -13,7 +13,7 @@ use PHPUnit\Event\InvalidArgumentException; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -56,9 +56,6 @@ public function nanoseconds(): int return $this->nanoseconds; } - /** - * @throws InvalidArgumentException - */ public function duration(self $start): Duration { $seconds = $this->seconds - $start->seconds(); @@ -71,7 +68,7 @@ public function duration(self $start): Duration } if ($seconds < 0) { - throw new InvalidArgumentException('Start needs to be smaller.'); + return Duration::fromSecondsAndNanoseconds(0, 0); } return Duration::fromSecondsAndNanoseconds( diff --git a/src/Event/Value/Telemetry/Info.php b/src/Event/Value/Telemetry/Info.php index 94b2afea2e9..a0d0a99f169 100644 --- a/src/Event/Value/Telemetry/Info.php +++ b/src/Event/Value/Telemetry/Info.php @@ -12,7 +12,7 @@ use function sprintf; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -79,7 +79,7 @@ public function asString(): string '[%s / %s] [%d bytes]', $this->durationSinceStart()->asString(), $this->durationSincePrevious()->asString(), - $this->memoryUsage()->bytes(), + $this->peakMemoryUsage()->bytes(), ); } } diff --git a/src/Event/Value/Telemetry/MemoryMeter.php b/src/Event/Value/Telemetry/MemoryMeter.php index 4955397e5d8..4d116ff3ef3 100644 --- a/src/Event/Value/Telemetry/MemoryMeter.php +++ b/src/Event/Value/Telemetry/MemoryMeter.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event\Telemetry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface MemoryMeter diff --git a/src/Event/Value/Telemetry/MemoryUsage.php b/src/Event/Value/Telemetry/MemoryUsage.php index 384fa7d0d82..8ace32ea0bf 100644 --- a/src/Event/Value/Telemetry/MemoryUsage.php +++ b/src/Event/Value/Telemetry/MemoryUsage.php @@ -10,7 +10,7 @@ namespace PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Telemetry/Php81GarbageCollectorStatusProvider.php b/src/Event/Value/Telemetry/Php81GarbageCollectorStatusProvider.php deleted file mode 100644 index 335a7e245ef..00000000000 --- a/src/Event/Value/Telemetry/Php81GarbageCollectorStatusProvider.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Telemetry; - -use function gc_status; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Php81GarbageCollectorStatusProvider implements GarbageCollectorStatusProvider -{ - public function status(): GarbageCollectorStatus - { - $status = gc_status(); - - return new GarbageCollectorStatus( - $status['runs'], - $status['collected'], - $status['threshold'], - $status['roots'], - null, - null, - null, - null, - null, - null, - null, - null, - ); - } -} diff --git a/src/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.php b/src/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.php deleted file mode 100644 index c3808b6b99d..00000000000 --- a/src/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Telemetry; - -use function gc_status; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Php83GarbageCollectorStatusProvider implements GarbageCollectorStatusProvider -{ - public function status(): GarbageCollectorStatus - { - $status = gc_status(); - - return new GarbageCollectorStatus( - $status['runs'], - $status['collected'], - $status['threshold'], - $status['roots'], - $status['application_time'], - $status['collector_time'], - $status['destructor_time'], - $status['free_time'], - $status['running'], - $status['protected'], - $status['full'], - $status['buffer_size'], - ); - } -} diff --git a/src/Event/Value/Telemetry/Snapshot.php b/src/Event/Value/Telemetry/Snapshot.php index 29fc1407200..0f00f5d8054 100644 --- a/src/Event/Value/Telemetry/Snapshot.php +++ b/src/Event/Value/Telemetry/Snapshot.php @@ -10,7 +10,7 @@ namespace PHPUnit\Event\Telemetry; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Telemetry/StopWatch.php b/src/Event/Value/Telemetry/StopWatch.php index 8a1492571a4..07ce5227faa 100644 --- a/src/Event/Value/Telemetry/StopWatch.php +++ b/src/Event/Value/Telemetry/StopWatch.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event\Telemetry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface StopWatch diff --git a/src/Event/Value/Telemetry/System.php b/src/Event/Value/Telemetry/System.php index e8bcab255e1..0a178363e55 100644 --- a/src/Event/Value/Telemetry/System.php +++ b/src/Event/Value/Telemetry/System.php @@ -10,6 +10,8 @@ namespace PHPUnit\Event\Telemetry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class System diff --git a/src/Event/Value/Telemetry/SystemGarbageCollectorStatusProvider.php b/src/Event/Value/Telemetry/SystemGarbageCollectorStatusProvider.php new file mode 100644 index 00000000000..3f33d690d0e --- /dev/null +++ b/src/Event/Value/Telemetry/SystemGarbageCollectorStatusProvider.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Telemetry; + +use function gc_status; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class SystemGarbageCollectorStatusProvider implements GarbageCollectorStatusProvider +{ + public function status(): GarbageCollectorStatus + { + $status = gc_status(); + + return new GarbageCollectorStatus( + $status['runs'], + $status['collected'], + $status['threshold'], + $status['roots'], + $status['application_time'], + $status['collector_time'], + $status['destructor_time'], + $status['free_time'], + $status['running'], + $status['protected'], + $status['full'], + $status['buffer_size'], + ); + } +} diff --git a/src/Event/Value/Telemetry/SystemMemoryMeter.php b/src/Event/Value/Telemetry/SystemMemoryMeter.php index f052f6f4498..16d895a949b 100644 --- a/src/Event/Value/Telemetry/SystemMemoryMeter.php +++ b/src/Event/Value/Telemetry/SystemMemoryMeter.php @@ -13,17 +13,19 @@ use function memory_get_usage; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class SystemMemoryMeter implements MemoryMeter +final readonly class SystemMemoryMeter implements MemoryMeter { public function memoryUsage(): MemoryUsage { - return MemoryUsage::fromBytes(memory_get_usage(true)); + return MemoryUsage::fromBytes(memory_get_usage()); } public function peakMemoryUsage(): MemoryUsage { - return MemoryUsage::fromBytes(memory_get_peak_usage(true)); + return MemoryUsage::fromBytes(memory_get_peak_usage()); } } diff --git a/src/Event/Value/Telemetry/SystemStopWatch.php b/src/Event/Value/Telemetry/SystemStopWatch.php index bf37fb4edd7..a57c1032407 100644 --- a/src/Event/Value/Telemetry/SystemStopWatch.php +++ b/src/Event/Value/Telemetry/SystemStopWatch.php @@ -13,9 +13,11 @@ use PHPUnit\Event\InvalidArgumentException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class SystemStopWatch implements StopWatch +final readonly class SystemStopWatch implements StopWatch { /** * @throws InvalidArgumentException diff --git a/src/Event/Value/Telemetry/SystemStopWatchWithOffset.php b/src/Event/Value/Telemetry/SystemStopWatchWithOffset.php index f4515787f36..d27fd98c14b 100644 --- a/src/Event/Value/Telemetry/SystemStopWatchWithOffset.php +++ b/src/Event/Value/Telemetry/SystemStopWatchWithOffset.php @@ -13,7 +13,11 @@ use PHPUnit\Event\InvalidArgumentException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ final class SystemStopWatchWithOffset implements StopWatch { diff --git a/src/Event/Value/Test/Issue/DirectTrigger.php b/src/Event/Value/Test/Issue/DirectTrigger.php new file mode 100644 index 00000000000..bbb3c66a890 --- /dev/null +++ b/src/Event/Value/Test/Issue/DirectTrigger.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class DirectTrigger extends IssueTrigger +{ + /** + * Your own code triggers an issue in third-party code. + */ + public function isDirect(): true + { + return true; + } + + public function asString(): string + { + return 'issue triggered by first-party code calling into third-party code'; + } +} diff --git a/src/Event/Value/Test/Issue/IndirectTrigger.php b/src/Event/Value/Test/Issue/IndirectTrigger.php new file mode 100644 index 00000000000..81f76b45e9a --- /dev/null +++ b/src/Event/Value/Test/Issue/IndirectTrigger.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class IndirectTrigger extends IssueTrigger +{ + /** + * Third-party code triggers an issue either in your own code or in third-party code. + */ + public function isIndirect(): true + { + return true; + } + + public function asString(): string + { + return 'issue triggered by third-party code'; + } +} diff --git a/src/Event/Value/Test/Issue/IssueTrigger.php b/src/Event/Value/Test/Issue/IssueTrigger.php new file mode 100644 index 00000000000..93e42c72950 --- /dev/null +++ b/src/Event/Value/Test/Issue/IssueTrigger.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract class IssueTrigger +{ + public static function test(): TestTrigger + { + return new TestTrigger; + } + + public static function self(): SelfTrigger + { + return new SelfTrigger; + } + + public static function direct(): DirectTrigger + { + return new DirectTrigger; + } + + public static function indirect(): IndirectTrigger + { + return new IndirectTrigger; + } + + public static function unknown(): UnknownTrigger + { + return new UnknownTrigger; + } + + final private function __construct() + { + } + + /** + * Your test code triggers an issue. + * + * @phpstan-assert-if-true TestTrigger $this + */ + public function isTest(): bool + { + return false; + } + + /** + * Your own code triggers an issue in your own code. + * + * @phpstan-assert-if-true SelfTrigger $this + */ + public function isSelf(): bool + { + return false; + } + + /** + * Your own code triggers an issue in third-party code. + * + * @phpstan-assert-if-true DirectTrigger $this + */ + public function isDirect(): bool + { + return false; + } + + /** + * Third-party code triggers an issue either in your own code or in third-party code. + * + * @phpstan-assert-if-true IndirectTrigger $this + */ + public function isIndirect(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true UnknownTrigger $this + */ + public function isUnknown(): bool + { + return false; + } + + abstract public function asString(): string; +} diff --git a/src/Event/Value/Test/Issue/SelfTrigger.php b/src/Event/Value/Test/Issue/SelfTrigger.php new file mode 100644 index 00000000000..e569e72f578 --- /dev/null +++ b/src/Event/Value/Test/Issue/SelfTrigger.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class SelfTrigger extends IssueTrigger +{ + /** + * Your own code triggers an issue in your own code. + */ + public function isSelf(): true + { + return true; + } + + public function asString(): string + { + return 'issue triggered by first-party code calling into first-party code'; + } +} diff --git a/src/Event/Value/Test/Issue/TestTrigger.php b/src/Event/Value/Test/Issue/TestTrigger.php new file mode 100644 index 00000000000..4768baca6bd --- /dev/null +++ b/src/Event/Value/Test/Issue/TestTrigger.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class TestTrigger extends IssueTrigger +{ + /** + * Your test code triggers an issue. + */ + public function isTest(): true + { + return true; + } + + public function asString(): string + { + return 'issue triggered by test code'; + } +} diff --git a/src/Event/Value/Test/Issue/UnknownTrigger.php b/src/Event/Value/Test/Issue/UnknownTrigger.php new file mode 100644 index 00000000000..61f81148929 --- /dev/null +++ b/src/Event/Value/Test/Issue/UnknownTrigger.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownTrigger extends IssueTrigger +{ + public function isUnknown(): true + { + return true; + } + + public function asString(): string + { + return 'unknown if issue was triggered in first-party code or third-party code'; + } +} diff --git a/src/Event/Value/Test/Phpt.php b/src/Event/Value/Test/Phpt.php index 1056787140d..65a3aec8268 100644 --- a/src/Event/Value/Test/Phpt.php +++ b/src/Event/Value/Test/Phpt.php @@ -10,22 +10,19 @@ namespace PHPUnit\Event\Code; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Phpt extends Test +final readonly class Phpt extends Test { - /** - * @psalm-assert-if-true Phpt $this - */ - public function isPhpt(): bool + public function isPhpt(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function id(): string { @@ -33,7 +30,7 @@ public function id(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function name(): string { diff --git a/src/Event/Value/Test/Test.php b/src/Event/Value/Test/Test.php index 6ec899c6053..43ed73eb7b6 100644 --- a/src/Event/Value/Test/Test.php +++ b/src/Event/Value/Test/Test.php @@ -10,19 +10,19 @@ namespace PHPUnit\Event\Code; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class Test +abstract readonly class Test { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $file; + private string $file; /** - * @psalm-param non-empty-string $file + * @param non-empty-string $file */ public function __construct(string $file) { @@ -30,7 +30,7 @@ public function __construct(string $file) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -38,7 +38,7 @@ public function file(): string } /** - * @psalm-assert-if-true TestMethod $this + * @phpstan-assert-if-true TestMethod $this */ public function isTestMethod(): bool { @@ -46,7 +46,7 @@ public function isTestMethod(): bool } /** - * @psalm-assert-if-true Phpt $this + * @phpstan-assert-if-true Phpt $this */ public function isPhpt(): bool { @@ -54,12 +54,12 @@ public function isPhpt(): bool } /** - * @psalm-return non-empty-string + * @return non-empty-string */ abstract public function id(): string; /** - * @psalm-return non-empty-string + * @return non-empty-string */ abstract public function name(): string; } diff --git a/src/Event/Value/Test/TestCollection.php b/src/Event/Value/Test/TestCollection.php index e5710468432..1924b64b614 100644 --- a/src/Event/Value/Test/TestCollection.php +++ b/src/Event/Value/Test/TestCollection.php @@ -16,19 +16,19 @@ /** * @template-implements IteratorAggregate * - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class TestCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $tests; /** - * @psalm-param list $tests + * @param list $tests */ public static function fromArray(array $tests): self { @@ -41,7 +41,7 @@ private function __construct(Test ...$tests) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/Event/Value/Test/TestCollectionIterator.php b/src/Event/Value/Test/TestCollectionIterator.php index a392a60e7a3..7c96f3409e4 100644 --- a/src/Event/Value/Test/TestCollectionIterator.php +++ b/src/Event/Value/Test/TestCollectionIterator.php @@ -20,7 +20,7 @@ final class TestCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $tests; private int $position = 0; diff --git a/src/Event/Value/Test/TestData/DataFromDataProvider.php b/src/Event/Value/Test/TestData/DataFromDataProvider.php index 7ca96dd0e0e..981fd9e4f65 100644 --- a/src/Event/Value/Test/TestData/DataFromDataProvider.php +++ b/src/Event/Value/Test/TestData/DataFromDataProvider.php @@ -10,22 +10,24 @@ namespace PHPUnit\Event\TestData; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DataFromDataProvider extends TestData +final readonly class DataFromDataProvider extends TestData { - private readonly int|string $dataSetName; + private int|string $dataSetName; + private string $dataAsStringForResultOutput; - public static function from(int|string $dataSetName, string $data): self + public static function from(int|string $dataSetName, string $data, string $dataAsStringForResultOutput): self { - return new self($dataSetName, $data); + return new self($dataSetName, $data, $dataAsStringForResultOutput); } - protected function __construct(int|string $dataSetName, string $data) + protected function __construct(int|string $dataSetName, string $data, string $dataAsStringForResultOutput) { - $this->dataSetName = $dataSetName; + $this->dataSetName = $dataSetName; + $this->dataAsStringForResultOutput = $dataAsStringForResultOutput; parent::__construct($data); } @@ -36,9 +38,14 @@ public function dataSetName(): int|string } /** - * @psalm-assert-if-true DataFromDataProvider $this + * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function isFromDataProvider(): bool + public function dataAsStringForResultOutput(): string + { + return $this->dataAsStringForResultOutput; + } + + public function isFromDataProvider(): true { return true; } diff --git a/src/Event/Value/Test/TestData/DataFromTestDependency.php b/src/Event/Value/Test/TestData/DataFromTestDependency.php index 48fa5cdcd16..9fbf17ebf43 100644 --- a/src/Event/Value/Test/TestData/DataFromTestDependency.php +++ b/src/Event/Value/Test/TestData/DataFromTestDependency.php @@ -10,21 +10,18 @@ namespace PHPUnit\Event\TestData; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DataFromTestDependency extends TestData +final readonly class DataFromTestDependency extends TestData { public static function from(string $data): self { return new self($data); } - /** - * @psalm-assert-if-true DataFromTestDependency $this - */ - public function isFromTestDependency(): bool + public function isFromTestDependency(): true { return true; } diff --git a/src/Event/Value/Test/TestData/TestData.php b/src/Event/Value/Test/TestData/TestData.php index dd68d1f6f62..893444806c2 100644 --- a/src/Event/Value/Test/TestData/TestData.php +++ b/src/Event/Value/Test/TestData/TestData.php @@ -10,13 +10,13 @@ namespace PHPUnit\Event\TestData; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class TestData +abstract readonly class TestData { - private readonly string $data; + private string $data; protected function __construct(string $data) { @@ -29,7 +29,7 @@ public function data(): string } /** - * @psalm-assert-if-true DataFromDataProvider $this + * @phpstan-assert-if-true DataFromDataProvider $this */ public function isFromDataProvider(): bool { @@ -37,7 +37,7 @@ public function isFromDataProvider(): bool } /** - * @psalm-assert-if-true DataFromTestDependency $this + * @phpstan-assert-if-true DataFromTestDependency $this */ public function isFromTestDependency(): bool { diff --git a/src/Event/Value/Test/TestData/TestDataCollection.php b/src/Event/Value/Test/TestData/TestDataCollection.php index 8f8047a5319..460f0c0c59b 100644 --- a/src/Event/Value/Test/TestData/TestDataCollection.php +++ b/src/Event/Value/Test/TestData/TestDataCollection.php @@ -18,16 +18,16 @@ * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestDataCollection implements Countable, IteratorAggregate +final readonly class TestDataCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ - private readonly array $data; - private ?DataFromDataProvider $fromDataProvider = null; + private array $data; + private ?DataFromDataProvider $fromDataProvider; /** - * @psalm-param list $data + * @param list $data */ public static function fromArray(array $data): self { @@ -36,17 +36,20 @@ public static function fromArray(array $data): self private function __construct(TestData ...$data) { + $fromDataProvider = null; + foreach ($data as $_data) { if ($_data->isFromDataProvider()) { - $this->fromDataProvider = $_data; + $fromDataProvider = $_data; } } - $this->data = $data; + $this->data = $data; + $this->fromDataProvider = $fromDataProvider; } /** - * @psalm-return list + * @return list */ public function asArray(): array { @@ -59,7 +62,7 @@ public function count(): int } /** - * @psalm-assert-if-true !null $this->fromDataProvider + * @phpstan-assert-if-true !null $this->fromDataProvider */ public function hasDataFromDataProvider(): bool { diff --git a/src/Event/Value/Test/TestData/TestDataCollectionIterator.php b/src/Event/Value/Test/TestData/TestDataCollectionIterator.php index 93f02c490bb..aebed66ca17 100644 --- a/src/Event/Value/Test/TestData/TestDataCollectionIterator.php +++ b/src/Event/Value/Test/TestData/TestDataCollectionIterator.php @@ -20,7 +20,7 @@ final class TestDataCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $data; private int $position = 0; diff --git a/src/Event/Value/Test/TestDox.php b/src/Event/Value/Test/TestDox.php index 2703a368500..53604dbfc65 100644 --- a/src/Event/Value/Test/TestDox.php +++ b/src/Event/Value/Test/TestDox.php @@ -10,7 +10,7 @@ namespace PHPUnit\Event\Code; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Event/Value/Test/TestDoxBuilder.php b/src/Event/Value/Test/TestDoxBuilder.php index edad458748c..af7e8b5ba86 100644 --- a/src/Event/Value/Test/TestDoxBuilder.php +++ b/src/Event/Value/Test/TestDoxBuilder.php @@ -13,9 +13,11 @@ use PHPUnit\Logging\TestDox\NamePrettifier; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestDoxBuilder +final readonly class TestDoxBuilder { public static function fromTestCase(TestCase $testCase): TestDox { @@ -29,17 +31,19 @@ public static function fromTestCase(TestCase $testCase): TestDox } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public static function fromClassNameAndMethodName(string $className, string $methodName): TestDox { $prettifier = new NamePrettifier; + $prettifiedMethodName = $prettifier->prettifyTestMethodName($methodName); + return new TestDox( $prettifier->prettifyTestClassName($className), - $prettifier->prettifyTestMethodName($methodName), - $prettifier->prettifyTestMethodName($methodName), + $prettifiedMethodName, + $prettifiedMethodName, ); } } diff --git a/src/Event/Value/Test/TestMethod.php b/src/Event/Value/Test/TestMethod.php index 58b2e015297..4c972645436 100644 --- a/src/Event/Value/Test/TestMethod.php +++ b/src/Event/Value/Test/TestMethod.php @@ -9,43 +9,41 @@ */ namespace PHPUnit\Event\Code; -use function assert; use function is_int; use function sprintf; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Event\TestData\TestDataCollection; use PHPUnit\Metadata\MetadataCollection; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestMethod extends Test +final readonly class TestMethod extends Test { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $methodName; + private string $methodName; /** - * @psalm-var non-negative-int + * @var non-negative-int */ - private readonly int $line; - private readonly TestDox $testDox; - private readonly MetadataCollection $metadata; - private readonly TestDataCollection $testData; + private int $line; + private TestDox $testDox; + private MetadataCollection $metadata; + private TestDataCollection $testData; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * @psalm-param non-empty-string $file - * @psalm-param non-negative-int $line + * @param class-string $className + * @param non-empty-string $methodName + * @param non-empty-string $file + * @param non-negative-int $line */ public function __construct(string $className, string $methodName, string $file, int $line, TestDox $testDox, MetadataCollection $metadata, TestDataCollection $testData) { @@ -60,7 +58,7 @@ public function __construct(string $className, string $methodName, string $file, } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -68,7 +66,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { @@ -76,7 +74,7 @@ public function methodName(): string } /** - * @psalm-return non-negative-int + * @return non-negative-int */ public function line(): int { @@ -98,18 +96,13 @@ public function testData(): TestDataCollection return $this->testData; } - /** - * @psalm-assert-if-true TestMethod $this - */ - public function isTestMethod(): bool + public function isTestMethod(): true { return true; } /** - * @psalm-return non-empty-string - * - * @throws NoDataSetFromDataProviderException + * @return non-empty-string */ public function id(): string { @@ -123,9 +116,7 @@ public function id(): string } /** - * @psalm-return non-empty-string - * - * @throws NoDataSetFromDataProviderException + * @return non-empty-string */ public function nameWithClass(): string { @@ -133,9 +124,7 @@ public function nameWithClass(): string } /** - * @psalm-return non-empty-string - * - * @throws NoDataSetFromDataProviderException + * @return non-empty-string */ public function name(): string { diff --git a/src/Event/Value/Test/TestMethodBuilder.php b/src/Event/Value/Test/TestMethodBuilder.php index 8a276fec0bd..e88a5ae400b 100644 --- a/src/Event/Value/Test/TestMethodBuilder.php +++ b/src/Event/Value/Test/TestMethodBuilder.php @@ -9,10 +9,7 @@ */ namespace PHPUnit\Event\Code; -use function assert; -use function debug_backtrace; use function is_numeric; -use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Event\TestData\DataFromDataProvider; use PHPUnit\Event\TestData\DataFromTestDependency; use PHPUnit\Event\TestData\TestDataCollection; @@ -20,19 +17,19 @@ use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; use PHPUnit\Util\Exporter; use PHPUnit\Util\Reflection; +use PHPUnit\Util\Test as TestUtil; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestMethodBuilder +final readonly class TestMethodBuilder { public static function fromTestCase(TestCase $testCase): TestMethod { $methodName = $testCase->name(); - - assert(!empty($methodName)); - - $location = Reflection::sourceLocationFor($testCase::class, $methodName); + $location = Reflection::sourceLocationFor($testCase::class, $methodName); return new TestMethod( $testCase::class, @@ -50,13 +47,7 @@ public static function fromTestCase(TestCase $testCase): TestMethod */ public static function fromCallStack(): TestMethod { - foreach (debug_backtrace() as $frame) { - if (isset($frame['object']) && $frame['object'] instanceof TestCase) { - return $frame['object']->valueObjectForEvents(); - } - } - - throw new NoTestCaseObjectOnCallStackException; + return TestUtil::currentTestCase()->valueObjectForEvents(); } private static function dataFor(TestCase $testCase): TestDataCollection @@ -72,13 +63,14 @@ private static function dataFor(TestCase $testCase): TestDataCollection $testData[] = DataFromDataProvider::from( $dataSetName, - Exporter::export($testCase->providedData(), EventFacade::emitter()->exportsObjects()), + Exporter::export($testCase->providedData()), + $testCase->dataSetAsStringWithData(), ); } if ($testCase->hasDependencyInput()) { $testData[] = DataFromTestDependency::from( - Exporter::export($testCase->dependencyInput(), EventFacade::emitter()->exportsObjects()), + Exporter::export($testCase->dependencyInput()), ); } diff --git a/src/Event/Value/TestSuite/TestSuite.php b/src/Event/Value/TestSuite/TestSuite.php index 744f7095e0f..783de57bd24 100644 --- a/src/Event/Value/TestSuite/TestSuite.php +++ b/src/Event/Value/TestSuite/TestSuite.php @@ -12,21 +12,21 @@ use PHPUnit\Event\Code\TestCollection; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class TestSuite +abstract readonly class TestSuite { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $name; - private readonly int $count; - private readonly TestCollection $tests; + private string $name; + private int $count; + private TestCollection $tests; /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name */ public function __construct(string $name, int $size, TestCollection $tests) { @@ -36,7 +36,7 @@ public function __construct(string $name, int $size, TestCollection $tests) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function name(): string { @@ -54,7 +54,7 @@ public function tests(): TestCollection } /** - * @psalm-assert-if-true TestSuiteWithName $this + * @phpstan-assert-if-true TestSuiteWithName $this */ public function isWithName(): bool { @@ -62,7 +62,7 @@ public function isWithName(): bool } /** - * @psalm-assert-if-true TestSuiteForTestClass $this + * @phpstan-assert-if-true TestSuiteForTestClass $this */ public function isForTestClass(): bool { @@ -70,7 +70,7 @@ public function isForTestClass(): bool } /** - * @psalm-assert-if-true TestSuiteForTestMethodWithDataProvider $this + * @phpstan-assert-if-true TestSuiteForTestMethodWithDataProvider $this */ public function isForTestMethodWithDataProvider(): bool { diff --git a/src/Event/Value/TestSuite/TestSuiteBuilder.php b/src/Event/Value/TestSuite/TestSuiteBuilder.php index c8988693076..3192636baa9 100644 --- a/src/Event/Value/TestSuite/TestSuiteBuilder.php +++ b/src/Event/Value/TestSuite/TestSuiteBuilder.php @@ -9,90 +9,83 @@ */ namespace PHPUnit\Event\TestSuite; +use function assert; +use function class_exists; +use function count; use function explode; +use function method_exists; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Code\TestCollection; use PHPUnit\Event\RuntimeException; use PHPUnit\Framework\DataProviderTestSuite; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite as FrameworkTestSuite; -use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use ReflectionClass; -use ReflectionException; use ReflectionMethod; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteBuilder +final readonly class TestSuiteBuilder { /** * @throws RuntimeException */ public static function from(FrameworkTestSuite $testSuite): TestSuite { - $groups = []; - - foreach ($testSuite->groupDetails() as $groupName => $tests) { - if (!isset($groups[$groupName])) { - $groups[$groupName] = []; - } - - foreach ($tests as $test) { - $groups[$groupName][] = $test::class; - } - } - $tests = []; self::process($testSuite, $tests); if ($testSuite instanceof DataProviderTestSuite) { + assert(count(explode('::', $testSuite->name())) === 2); [$className, $methodName] = explode('::', $testSuite->name()); - try { - $reflector = new ReflectionMethod($className, $methodName); - - return new TestSuiteForTestMethodWithDataProvider( - $testSuite->name(), - $testSuite->count(), - TestCollection::fromArray($tests), - $className, - $methodName, - $reflector->getFileName(), - $reflector->getStartLine(), - ); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new RuntimeException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd + assert(class_exists($className)); + assert($methodName !== '' && method_exists($className, $methodName)); + + $reflector = new ReflectionMethod($className, $methodName); + + $file = $reflector->getFileName(); + $line = $reflector->getStartLine(); + + assert($file !== false); + assert($line !== false); + + return new TestSuiteForTestMethodWithDataProvider( + $testSuite->name(), + $testSuite->count(), + TestCollection::fromArray($tests), + $className, + $methodName, + $file, + $line, + ); } if ($testSuite->isForTestClass()) { - try { - $reflector = new ReflectionClass($testSuite->name()); - - return new TestSuiteForTestClass( - $testSuite->name(), - $testSuite->count(), - TestCollection::fromArray($tests), - $reflector->getFileName(), - $reflector->getStartLine(), - ); - // @codeCoverageIgnoreStart - } catch (ReflectionException $e) { - throw new RuntimeException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd + $testClassName = $testSuite->name(); + + assert(class_exists($testClassName)); + + $reflector = new ReflectionClass($testClassName); + + $file = $reflector->getFileName(); + $line = $reflector->getStartLine(); + + assert($file !== false); + assert($line !== false); + + return new TestSuiteForTestClass( + $testClassName, + $testSuite->count(), + TestCollection::fromArray($tests), + $file, + $line, + ); } return new TestSuiteWithName( @@ -103,11 +96,11 @@ public static function from(FrameworkTestSuite $testSuite): TestSuite } /** - * @psalm-param list $tests + * @param list $tests */ - private static function process(FrameworkTestSuite $testSuite, &$tests): void + private static function process(FrameworkTestSuite $testSuite, array &$tests): void { - foreach ($testSuite->tests() as $test) { + foreach ($testSuite->getIterator() as $test) { if ($test instanceof FrameworkTestSuite) { self::process($test, $tests); diff --git a/src/Event/Value/TestSuite/TestSuiteForTestClass.php b/src/Event/Value/TestSuite/TestSuiteForTestClass.php index fcfc9d0a296..34ad9d5eac3 100644 --- a/src/Event/Value/TestSuite/TestSuiteForTestClass.php +++ b/src/Event/Value/TestSuite/TestSuiteForTestClass.php @@ -12,21 +12,21 @@ use PHPUnit\Event\Code\TestCollection; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteForTestClass extends TestSuite +final readonly class TestSuiteForTestClass extends TestSuite { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; - private readonly string $file; - private readonly int $line; + private string $className; + private string $file; + private int $line; /** - * @psalm-param class-string $name + * @param class-string $name */ public function __construct(string $name, int $size, TestCollection $tests, string $file, int $line) { @@ -38,7 +38,7 @@ public function __construct(string $name, int $size, TestCollection $tests, stri } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -55,10 +55,7 @@ public function line(): int return $this->line; } - /** - * @psalm-assert-if-true TestSuiteForTestClass $this - */ - public function isForTestClass(): bool + public function isForTestClass(): true { return true; } diff --git a/src/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.php b/src/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.php index 29bebc6f42d..67a94391dd5 100644 --- a/src/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.php +++ b/src/Event/Value/TestSuite/TestSuiteForTestMethodWithDataProvider.php @@ -12,28 +12,28 @@ use PHPUnit\Event\Code\TestCollection; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteForTestMethodWithDataProvider extends TestSuite +final readonly class TestSuiteForTestMethodWithDataProvider extends TestSuite { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $methodName; - private readonly string $file; - private readonly int $line; + private string $methodName; + private string $file; + private int $line; /** - * @psalm-param non-empty-string $name - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param non-empty-string $name + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $name, int $size, TestCollection $tests, string $className, string $methodName, string $file, int $line) { @@ -46,7 +46,7 @@ public function __construct(string $name, int $size, TestCollection $tests, stri } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -54,7 +54,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { @@ -71,10 +71,7 @@ public function line(): int return $this->line; } - /** - * @psalm-assert-if-true TestSuiteForTestMethodWithDataProvider $this - */ - public function isForTestMethodWithDataProvider(): bool + public function isForTestMethodWithDataProvider(): true { return true; } diff --git a/src/Event/Value/TestSuite/TestSuiteWithName.php b/src/Event/Value/TestSuite/TestSuiteWithName.php index 68c2f706c49..4823fb26333 100644 --- a/src/Event/Value/TestSuite/TestSuiteWithName.php +++ b/src/Event/Value/TestSuite/TestSuiteWithName.php @@ -10,16 +10,13 @@ namespace PHPUnit\Event\TestSuite; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteWithName extends TestSuite +final readonly class TestSuiteWithName extends TestSuite { - /** - * @psalm-assert-if-true TestSuiteWithName $this - */ - public function isWithName(): bool + public function isWithName(): true { return true; } diff --git a/src/Event/Value/Throwable.php b/src/Event/Value/Throwable.php index c294de51e44..e48cf1a3ac6 100644 --- a/src/Event/Value/Throwable.php +++ b/src/Event/Value/Throwable.php @@ -13,14 +13,14 @@ use PHPUnit\Event\NoPreviousThrowableException; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class Throwable { /** - * @psalm-var class-string + * @var class-string */ private string $className; private string $message; @@ -29,7 +29,7 @@ private ?Throwable $previous; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(string $className, string $message, string $description, string $stackTrace, ?self $previous) { @@ -47,7 +47,7 @@ public function asString(): string { $buffer = $this->description(); - if (!empty($this->stackTrace())) { + if ($this->stackTrace() !== '') { $buffer .= PHP_EOL . $this->stackTrace(); } @@ -59,7 +59,7 @@ public function asString(): string } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -82,7 +82,7 @@ public function stackTrace(): string } /** - * @psalm-assert-if-true !null $this->previous + * @phpstan-assert-if-true !null $this->previous */ public function hasPrevious(): bool { diff --git a/src/Event/Value/ThrowableBuilder.php b/src/Event/Value/ThrowableBuilder.php index aed9144fb88..7db4beea721 100644 --- a/src/Event/Value/ThrowableBuilder.php +++ b/src/Event/Value/ThrowableBuilder.php @@ -15,9 +15,11 @@ use PHPUnit\Util\ThrowableToStringMapper; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ThrowableBuilder +final readonly class ThrowableBuilder { /** * @throws Exception @@ -35,7 +37,7 @@ public static function from(\Throwable $t): Throwable $t::class, $t->getMessage(), ThrowableToStringMapper::map($t), - Filter::getFilteredStacktrace($t), + Filter::stackTraceFromThrowableAsString($t, false), $previous, ); } diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index 83684b04d25..91f940af36b 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -9,15 +9,18 @@ */ namespace PHPUnit\Framework; +use function array_combine; +use function array_intersect_key; use function class_exists; use function count; use function file_get_contents; use function interface_exists; use function is_bool; +use function sprintf; use ArrayAccess; use Countable; use Generator; -use PHPUnit\Event; +use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Framework\Constraint\ArrayHasKey; use PHPUnit\Framework\Constraint\Callback; use PHPUnit\Framework\Constraint\Constraint; @@ -72,153 +75,891 @@ abstract class Assert { private static int $count = 0; + /** + * Asserts that two arrays are equal while only considering a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeConsidered + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertArrayIsEqualToArrayOnlyConsideringListOfKeys(array $expected, array $actual, array $keysToBeConsidered, string $message = ''): void + { + $filteredExpected = []; + + foreach ($keysToBeConsidered as $key) { + if (isset($expected[$key])) { + $filteredExpected[$key] = $expected[$key]; + } + } + + $filteredActual = []; + + foreach ($keysToBeConsidered as $key) { + if (isset($actual[$key])) { + $filteredActual[$key] = $actual[$key]; + } + } + + self::assertEquals($filteredExpected, $filteredActual, $message); + } + + /** + * Asserts that two arrays are equal while ignoring a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeIgnored + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertArrayIsEqualToArrayIgnoringListOfKeys(array $expected, array $actual, array $keysToBeIgnored, string $message = ''): void + { + foreach ($keysToBeIgnored as $key) { + unset($expected[$key], $actual[$key]); + } + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two arrays are identical while only considering a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeConsidered + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys(array $expected, array $actual, array $keysToBeConsidered, string $message = ''): void + { + $keysToBeConsidered = array_combine($keysToBeConsidered, $keysToBeConsidered); + $expected = array_intersect_key($expected, $keysToBeConsidered); + $actual = array_intersect_key($actual, $keysToBeConsidered); + + self::assertSame($expected, $actual, $message); + } + + /** + * Asserts that two arrays are equal while ignoring a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeIgnored + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertArrayIsIdenticalToArrayIgnoringListOfKeys(array $expected, array $actual, array $keysToBeIgnored, string $message = ''): void + { + foreach ($keysToBeIgnored as $key) { + unset($expected[$key], $actual[$key]); + } + + self::assertSame($expected, $actual, $message); + } + /** * Asserts that an array has a specified key. * + * @param array|ArrayAccess $array + * * @throws Exception * @throws ExpectationFailedException */ - final public static function assertArrayHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void + final public static function assertArrayHasKey(mixed $key, array|ArrayAccess $array, string $message = ''): void { $constraint = new ArrayHasKey($key); - static::assertThat($array, $constraint, $message); + self::assertThat($array, $constraint, $message); + } + + /** + * Asserts that an array does not have a specified key. + * + * @param array|ArrayAccess $array + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertArrayNotHasKey(mixed $key, array|ArrayAccess $array, string $message = ''): void + { + $constraint = new LogicalNot( + new ArrayHasKey($key), + ); + + self::assertThat($array, $constraint, $message); + } + + /** + * @phpstan-assert list $array + * + * @throws ExpectationFailedException + */ + final public static function assertIsList(mixed $array, string $message = ''): void + { + self::assertThat( + $array, + new IsList, + $message, + ); + } + + /** + * Asserts that a haystack contains a needle. + * + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertContains(mixed $needle, iterable $haystack, string $message = ''): void + { + $constraint = new TraversableContainsIdentical($needle); + + self::assertThat($haystack, $constraint, $message); + } + + /** + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + { + $constraint = new TraversableContainsEqual($needle); + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack does not contain a needle. + * + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertNotContains(mixed $needle, iterable $haystack, string $message = ''): void + { + $constraint = new LogicalNot( + new TraversableContainsIdentical($needle), + ); + + self::assertThat($haystack, $constraint, $message); + } + + /** + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertNotContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + { + $constraint = new LogicalNot(new TraversableContainsEqual($needle)); + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack contains only values of a given type. + * + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6056 + */ + final public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + { + if ($isNativeType === null) { + $isNativeType = self::isNativeType($type); + } + + if ($isNativeType) { + $replacement = match ($type) { + 'array' => 'assertContainsOnlyArray', + 'bool' => 'assertContainsOnlyBool', + 'boolean' => 'assertContainsOnlyBool', + 'callable' => 'assertContainsOnlyCallable', + 'double' => 'assertContainsOnlyFloat', + 'float' => 'assertContainsOnlyFloat', + 'int' => 'assertContainsOnlyInt', + 'integer' => 'assertContainsOnlyInt', + 'iterable' => 'assertContainsOnlyIterable', + 'null' => 'assertContainsOnlyNull', + 'numeric' => 'assertContainsOnlyNumeric', + 'object' => 'assertContainsOnlyObject', + 'real' => 'assertContainsOnlyFloat', + 'resource' => 'assertContainsOnlyResource', + 'resource (closed)' => 'assertContainsOnlyClosedResource', + 'scalar' => 'assertContainsOnlyScalar', + 'string' => 'assertContainsOnlyString', + }; + + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + null, + sprintf( + 'assertContainsOnly() is deprecated and will be removed in PHPUnit 13. ' . + 'Please use %s($haystack) instead of assertContainsOnly(\'%s\', $haystack).', + $replacement, + $type, + ), + ); + + $constraint = TraversableContainsOnly::forNativeType(self::mapNativeType($type)); + } else { + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + null, + sprintf( + 'assertContainsOnly() is deprecated and will be removed in PHPUnit 13. ' . + 'Please use assertContainsOnlyInstancesOf(\'%s\', $haystack) instead of assertContainsOnly(\'%s\', $haystack).', + $type, + $type, + ), + ); + + /** @phpstan-ignore argument.type */ + $constraint = TraversableContainsOnly::forClassOrInterface($type); + } + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack contains only values of type array. + * + * @phpstan-assert iterable> $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyArray(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Array, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type bool. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyBool(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Bool, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type callable. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyCallable(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Callable, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type float. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyFloat(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Float, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type int. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyInt(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Int, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type iterable. + * + * @phpstan-assert iterable> $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyIterable(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Iterable, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type null. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyNull(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Null, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type numeric. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyNumeric(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Numeric, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type object. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyObject(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Object, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type resource. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyResource(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Resource, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type closed resource. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyClosedResource(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::ClosedResource, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type scalar. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyScalar(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::Scalar, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only values of type string. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyString(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forNativeType( + NativeType::String, + ), + $message, + ); + } + + /** + * Asserts that a haystack contains only instances of a specified interface or class name. + * + * @template T + * + * @phpstan-assert iterable $haystack + * + * @param class-string $className + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + */ + final public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + TraversableContainsOnly::forClassOrInterface($className), + $message, + ); + } + + /** + * Asserts that a haystack does not contain only values of a given type. + * + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6056 + */ + final public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + { + if ($isNativeType === null) { + $isNativeType = self::isNativeType($type); + } + + if ($isNativeType) { + $replacement = match ($type) { + 'array' => 'assertContainsNotOnlyArray', + 'bool' => 'assertContainsNotOnlyBool', + 'boolean' => 'assertContainsNotOnlyBool', + 'callable' => 'assertContainsNotOnlyCallable', + 'double' => 'assertContainsNotOnlyFloat', + 'float' => 'assertContainsNotOnlyFloat', + 'int' => 'assertContainsNotOnlyInt', + 'integer' => 'assertContainsNotOnlyInt', + 'iterable' => 'assertContainsNotOnlyIterable', + 'null' => 'assertContainsNotOnlyNull', + 'numeric' => 'assertContainsNotOnlyNumeric', + 'object' => 'assertContainsNotOnlyObject', + 'real' => 'assertContainsNotOnlyFloat', + 'resource' => 'assertContainsNotOnlyResource', + 'resource (closed)' => 'assertContainsNotOnlyClosedResource', + 'scalar' => 'assertContainsNotOnlyScalar', + 'string' => 'assertContainsNotOnlyString', + }; + + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + null, + sprintf( + 'assertNotContainsOnly() is deprecated and will be removed in PHPUnit 13. ' . + 'Please use %s($haystack) instead of assertNotContainsOnly(\'%s\', $haystack).', + $replacement, + $type, + ), + ); + + $constraint = TraversableContainsOnly::forNativeType(self::mapNativeType($type)); + } else { + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + null, + sprintf( + 'assertNotContainsOnly() is deprecated and will be removed in PHPUnit 13. ' . + 'Please use assertContainsNotOnlyInstancesOf(\'%s\', $haystack) instead of assertNotContainsOnly(\'%s\', $haystack).', + $type, + $type, + ), + ); + + /** @phpstan-ignore argument.type */ + $constraint = TraversableContainsOnly::forClassOrInterface($type); + } + + self::assertThat( + $haystack, + new LogicalNot($constraint), + $message, + ); + } + + /** + * Asserts that a haystack does not contain only values of type array. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsNotOnlyArray(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Array, + ), + ), + $message, + ); + } + + /** + * Asserts that a haystack does not contain only values of type bool. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsNotOnlyBool(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Bool, + ), + ), + $message, + ); + } + + /** + * Asserts that a haystack does not contain only values of type callable. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsNotOnlyCallable(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Callable, + ), + ), + $message, + ); + } + + /** + * Asserts that a haystack does not contain only values of type float. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsNotOnlyFloat(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Float, + ), + ), + $message, + ); + } + + /** + * Asserts that a haystack does not contain only values of type int. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + */ + final public static function assertContainsNotOnlyInt(iterable $haystack, string $message = ''): void + { + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Int, + ), + ), + $message, + ); } /** - * Asserts that an array does not have a specified key. + * Asserts that a haystack does not contain only values of type iterable. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException */ - final public static function assertArrayNotHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void + final public static function assertContainsNotOnlyIterable(iterable $haystack, string $message = ''): void { - $constraint = new LogicalNot( - new ArrayHasKey($key), + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Iterable, + ), + ), + $message, ); - - static::assertThat($array, $constraint, $message); } /** + * Asserts that a haystack does not contain only values of type null. + * + * @param iterable $haystack + * * @throws ExpectationFailedException */ - final public static function assertIsList(mixed $array, string $message = ''): void + final public static function assertContainsNotOnlyNull(iterable $haystack, string $message = ''): void { - static::assertThat( - $array, - new IsList, + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Null, + ), + ), $message, ); } /** - * Asserts that a haystack contains a needle. + * Asserts that a haystack does not contain only values of type numeric. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException */ - final public static function assertContains(mixed $needle, iterable $haystack, string $message = ''): void + final public static function assertContainsNotOnlyNumeric(iterable $haystack, string $message = ''): void { - $constraint = new TraversableContainsIdentical($needle); - - static::assertThat($haystack, $constraint, $message); + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Numeric, + ), + ), + $message, + ); } /** + * Asserts that a haystack does not contain only values of type object. + * + * @param iterable $haystack + * * @throws ExpectationFailedException */ - final public static function assertContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + final public static function assertContainsNotOnlyObject(iterable $haystack, string $message = ''): void { - $constraint = new TraversableContainsEqual($needle); - - static::assertThat($haystack, $constraint, $message); + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Object, + ), + ), + $message, + ); } /** - * Asserts that a haystack does not contain a needle. + * Asserts that a haystack does not contain only values of type resource. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException */ - final public static function assertNotContains(mixed $needle, iterable $haystack, string $message = ''): void + final public static function assertContainsNotOnlyResource(iterable $haystack, string $message = ''): void { - $constraint = new LogicalNot( - new TraversableContainsIdentical($needle), + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Resource, + ), + ), + $message, ); - - static::assertThat($haystack, $constraint, $message); } /** + * Asserts that a haystack does not contain only values of type closed resource. + * + * @param iterable $haystack + * * @throws ExpectationFailedException */ - final public static function assertNotContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + final public static function assertContainsNotOnlyClosedResource(iterable $haystack, string $message = ''): void { - $constraint = new LogicalNot(new TraversableContainsEqual($needle)); - - static::assertThat($haystack, $constraint, $message); + self::assertThat( + $haystack, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::ClosedResource, + ), + ), + $message, + ); } /** - * Asserts that a haystack contains only values of a given type. + * Asserts that a haystack does not contain only values of type scalar. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException */ - final public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + final public static function assertContainsNotOnlyScalar(iterable $haystack, string $message = ''): void { - if ($isNativeType === null) { - $isNativeType = self::isNativeType($type); - } - - static::assertThat( + self::assertThat( $haystack, - new TraversableContainsOnly( - $type, - $isNativeType, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::Scalar, + ), ), $message, ); } /** - * Asserts that a haystack contains only instances of a given class name. + * Asserts that a haystack does not contain only values of type string. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException */ - final public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void + final public static function assertContainsNotOnlyString(iterable $haystack, string $message = ''): void { - static::assertThat( + self::assertThat( $haystack, - new TraversableContainsOnly( - $className, - false, + new LogicalNot( + TraversableContainsOnly::forNativeType( + NativeType::String, + ), ), $message, ); } /** - * Asserts that a haystack does not contain only values of a given type. + * Asserts that a haystack does not contain only instances of a specified interface or class name. + * + * @param class-string $className + * @param iterable $haystack * * @throws Exception * @throws ExpectationFailedException */ - final public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + final public static function assertContainsNotOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void { - if ($isNativeType === null) { - $isNativeType = self::isNativeType($type); - } - - static::assertThat( + self::assertThat( $haystack, new LogicalNot( - new TraversableContainsOnly( - $type, - $isNativeType, - ), + TraversableContainsOnly::forClassOrInterface($className), ), $message, ); @@ -227,6 +968,8 @@ final public static function assertNotContainsOnly(string $type, iterable $hayst /** * Asserts the number of elements of an array, Countable or Traversable. * + * @param Countable|iterable $haystack + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -237,7 +980,7 @@ final public static function assertCount(int $expectedCount, Countable|iterable throw GeneratorNotSupportedException::fromParameterName('$haystack'); } - static::assertThat( + self::assertThat( $haystack, new Count($expectedCount), $message, @@ -247,6 +990,8 @@ final public static function assertCount(int $expectedCount, Countable|iterable /** * Asserts the number of elements of an array, Countable or Traversable. * + * @param Countable|iterable $haystack + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -261,7 +1006,7 @@ final public static function assertNotCount(int $expectedCount, Countable|iterab new Count($expectedCount), ); - static::assertThat($haystack, $constraint, $message); + self::assertThat($haystack, $constraint, $message); } /** @@ -273,7 +1018,7 @@ final public static function assertEquals(mixed $expected, mixed $actual, string { $constraint = new IsEqual($expected); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -285,7 +1030,7 @@ final public static function assertEqualsCanonicalizing(mixed $expected, mixed $ { $constraint = new IsEqualCanonicalizing($expected); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -297,7 +1042,7 @@ final public static function assertEqualsIgnoringCase(mixed $expected, mixed $ac { $constraint = new IsEqualIgnoringCase($expected); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -312,7 +1057,7 @@ final public static function assertEqualsWithDelta(mixed $expected, mixed $actua $delta, ); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -326,7 +1071,7 @@ final public static function assertNotEquals(mixed $expected, mixed $actual, str new IsEqual($expected), ); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -340,7 +1085,7 @@ final public static function assertNotEqualsCanonicalizing(mixed $expected, mixe new IsEqualCanonicalizing($expected), ); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -354,7 +1099,7 @@ final public static function assertNotEqualsIgnoringCase(mixed $expected, mixed new IsEqualIgnoringCase($expected), ); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -371,7 +1116,7 @@ final public static function assertNotEqualsWithDelta(mixed $expected, mixed $ac ), ); - static::assertThat($actual, $constraint, $message); + self::assertThat($actual, $constraint, $message); } /** @@ -379,9 +1124,23 @@ final public static function assertNotEqualsWithDelta(mixed $expected, mixed $ac */ final public static function assertObjectEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void { - static::assertThat( + self::assertThat( + $actual, + self::objectEquals($expected, $method), + $message, + ); + } + + /** + * @throws ExpectationFailedException + */ + final public static function assertObjectNotEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void + { + self::assertThat( $actual, - static::objectEquals($expected, $method), + self::logicalNot( + self::objectEquals($expected, $method), + ), $message, ); } @@ -391,8 +1150,6 @@ final public static function assertObjectEquals(object $expected, object $actual * * @throws ExpectationFailedException * @throws GeneratorNotSupportedException - * - * @psalm-assert empty $actual */ final public static function assertEmpty(mixed $actual, string $message = ''): void { @@ -400,7 +1157,7 @@ final public static function assertEmpty(mixed $actual, string $message = ''): v throw GeneratorNotSupportedException::fromParameterName('$actual'); } - static::assertThat($actual, static::isEmpty(), $message); + self::assertThat($actual, self::isEmpty(), $message); } /** @@ -408,8 +1165,6 @@ final public static function assertEmpty(mixed $actual, string $message = ''): v * * @throws ExpectationFailedException * @throws GeneratorNotSupportedException - * - * @psalm-assert !empty $actual */ final public static function assertNotEmpty(mixed $actual, string $message = ''): void { @@ -417,7 +1172,7 @@ final public static function assertNotEmpty(mixed $actual, string $message = '') throw GeneratorNotSupportedException::fromParameterName('$actual'); } - static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); + self::assertThat($actual, self::logicalNot(self::isEmpty()), $message); } /** @@ -425,9 +1180,9 @@ final public static function assertNotEmpty(mixed $actual, string $message = '') * * @throws ExpectationFailedException */ - final public static function assertGreaterThan(mixed $expected, mixed $actual, string $message = ''): void + final public static function assertGreaterThan(mixed $minimum, mixed $actual, string $message = ''): void { - static::assertThat($actual, static::greaterThan($expected), $message); + self::assertThat($actual, self::greaterThan($minimum), $message); } /** @@ -435,11 +1190,11 @@ final public static function assertGreaterThan(mixed $expected, mixed $actual, s * * @throws ExpectationFailedException */ - final public static function assertGreaterThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void + final public static function assertGreaterThanOrEqual(mixed $minimum, mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - static::greaterThanOrEqual($expected), + self::greaterThanOrEqual($minimum), $message, ); } @@ -449,9 +1204,9 @@ final public static function assertGreaterThanOrEqual(mixed $expected, mixed $ac * * @throws ExpectationFailedException */ - final public static function assertLessThan(mixed $expected, mixed $actual, string $message = ''): void + final public static function assertLessThan(mixed $maximum, mixed $actual, string $message = ''): void { - static::assertThat($actual, static::lessThan($expected), $message); + self::assertThat($actual, self::lessThan($maximum), $message); } /** @@ -459,9 +1214,9 @@ final public static function assertLessThan(mixed $expected, mixed $actual, stri * * @throws ExpectationFailedException */ - final public static function assertLessThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void + final public static function assertLessThanOrEqual(mixed $maximum, mixed $actual, string $message = ''): void { - static::assertThat($actual, static::lessThanOrEqual($expected), $message); + self::assertThat($actual, self::lessThanOrEqual($maximum), $message); } /** @@ -472,12 +1227,12 @@ final public static function assertLessThanOrEqual(mixed $expected, mixed $actua */ final public static function assertFileEquals(string $expected, string $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); $constraint = new IsEqual(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); + self::assertThat(file_get_contents($actual), $constraint, $message); } /** @@ -488,14 +1243,14 @@ final public static function assertFileEquals(string $expected, string $actual, */ final public static function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); $constraint = new IsEqualCanonicalizing( file_get_contents($expected), ); - static::assertThat(file_get_contents($actual), $constraint, $message); + self::assertThat(file_get_contents($actual), $constraint, $message); } /** @@ -506,12 +1261,12 @@ final public static function assertFileEqualsCanonicalizing(string $expected, st */ final public static function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); $constraint = new IsEqualIgnoringCase(file_get_contents($expected)); - static::assertThat(file_get_contents($actual), $constraint, $message); + self::assertThat(file_get_contents($actual), $constraint, $message); } /** @@ -522,14 +1277,14 @@ final public static function assertFileEqualsIgnoringCase(string $expected, stri */ final public static function assertFileNotEquals(string $expected, string $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); $constraint = new LogicalNot( new IsEqual(file_get_contents($expected)), ); - static::assertThat(file_get_contents($actual), $constraint, $message); + self::assertThat(file_get_contents($actual), $constraint, $message); } /** @@ -540,14 +1295,14 @@ final public static function assertFileNotEquals(string $expected, string $actua */ final public static function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); $constraint = new LogicalNot( new IsEqualCanonicalizing(file_get_contents($expected)), ); - static::assertThat(file_get_contents($actual), $constraint, $message); + self::assertThat(file_get_contents($actual), $constraint, $message); } /** @@ -558,14 +1313,14 @@ final public static function assertFileNotEqualsCanonicalizing(string $expected, */ final public static function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); $constraint = new LogicalNot( new IsEqualIgnoringCase(file_get_contents($expected)), ); - static::assertThat(file_get_contents($actual), $constraint, $message); + self::assertThat(file_get_contents($actual), $constraint, $message); } /** @@ -576,11 +1331,11 @@ final public static function assertFileNotEqualsIgnoringCase(string $expected, s */ final public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); $constraint = new IsEqual(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + self::assertThat($actualString, $constraint, $message); } /** @@ -591,11 +1346,11 @@ final public static function assertStringEqualsFile(string $expectedFile, string */ final public static function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); $constraint = new IsEqualCanonicalizing(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + self::assertThat($actualString, $constraint, $message); } /** @@ -606,11 +1361,11 @@ final public static function assertStringEqualsFileCanonicalizing(string $expect */ final public static function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); $constraint = new IsEqualIgnoringCase(file_get_contents($expectedFile)); - static::assertThat($actualString, $constraint, $message); + self::assertThat($actualString, $constraint, $message); } /** @@ -621,13 +1376,13 @@ final public static function assertStringEqualsFileIgnoringCase(string $expected */ final public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); $constraint = new LogicalNot( new IsEqual(file_get_contents($expectedFile)), ); - static::assertThat($actualString, $constraint, $message); + self::assertThat($actualString, $constraint, $message); } /** @@ -638,13 +1393,13 @@ final public static function assertStringNotEqualsFile(string $expectedFile, str */ final public static function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); $constraint = new LogicalNot( new IsEqualCanonicalizing(file_get_contents($expectedFile)), ); - static::assertThat($actualString, $constraint, $message); + self::assertThat($actualString, $constraint, $message); } /** @@ -655,13 +1410,13 @@ final public static function assertStringNotEqualsFileCanonicalizing(string $exp */ final public static function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); $constraint = new LogicalNot( new IsEqualIgnoringCase(file_get_contents($expectedFile)), ); - static::assertThat($actualString, $constraint, $message); + self::assertThat($actualString, $constraint, $message); } /** @@ -671,7 +1426,7 @@ final public static function assertStringNotEqualsFileIgnoringCase(string $expec */ final public static function assertIsReadable(string $filename, string $message = ''): void { - static::assertThat($filename, new IsReadable, $message); + self::assertThat($filename, new IsReadable, $message); } /** @@ -681,7 +1436,7 @@ final public static function assertIsReadable(string $filename, string $message */ final public static function assertIsNotReadable(string $filename, string $message = ''): void { - static::assertThat($filename, new LogicalNot(new IsReadable), $message); + self::assertThat($filename, new LogicalNot(new IsReadable), $message); } /** @@ -691,7 +1446,7 @@ final public static function assertIsNotReadable(string $filename, string $messa */ final public static function assertIsWritable(string $filename, string $message = ''): void { - static::assertThat($filename, new IsWritable, $message); + self::assertThat($filename, new IsWritable, $message); } /** @@ -701,7 +1456,7 @@ final public static function assertIsWritable(string $filename, string $message */ final public static function assertIsNotWritable(string $filename, string $message = ''): void { - static::assertThat($filename, new LogicalNot(new IsWritable), $message); + self::assertThat($filename, new LogicalNot(new IsWritable), $message); } /** @@ -711,7 +1466,7 @@ final public static function assertIsNotWritable(string $filename, string $messa */ final public static function assertDirectoryExists(string $directory, string $message = ''): void { - static::assertThat($directory, new DirectoryExists, $message); + self::assertThat($directory, new DirectoryExists, $message); } /** @@ -721,7 +1476,7 @@ final public static function assertDirectoryExists(string $directory, string $me */ final public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void { - static::assertThat($directory, new LogicalNot(new DirectoryExists), $message); + self::assertThat($directory, new LogicalNot(new DirectoryExists), $message); } /** @@ -775,7 +1530,7 @@ final public static function assertDirectoryIsNotWritable(string $directory, str */ final public static function assertFileExists(string $filename, string $message = ''): void { - static::assertThat($filename, new FileExists, $message); + self::assertThat($filename, new FileExists, $message); } /** @@ -785,7 +1540,7 @@ final public static function assertFileExists(string $filename, string $message */ final public static function assertFileDoesNotExist(string $filename, string $message = ''): void { - static::assertThat($filename, new LogicalNot(new FileExists), $message); + self::assertThat($filename, new LogicalNot(new FileExists), $message); } /** @@ -837,11 +1592,11 @@ final public static function assertFileIsNotWritable(string $file, string $messa * * @throws ExpectationFailedException * - * @psalm-assert true $condition + * @phpstan-assert true $condition */ final public static function assertTrue(mixed $condition, string $message = ''): void { - static::assertThat($condition, static::isTrue(), $message); + self::assertThat($condition, self::isTrue(), $message); } /** @@ -849,11 +1604,11 @@ final public static function assertTrue(mixed $condition, string $message = ''): * * @throws ExpectationFailedException * - * @psalm-assert !true $condition + * @phpstan-assert !true $condition */ final public static function assertNotTrue(mixed $condition, string $message = ''): void { - static::assertThat($condition, static::logicalNot(static::isTrue()), $message); + self::assertThat($condition, self::logicalNot(self::isTrue()), $message); } /** @@ -861,11 +1616,11 @@ final public static function assertNotTrue(mixed $condition, string $message = ' * * @throws ExpectationFailedException * - * @psalm-assert false $condition + * @phpstan-assert false $condition */ final public static function assertFalse(mixed $condition, string $message = ''): void { - static::assertThat($condition, static::isFalse(), $message); + self::assertThat($condition, self::isFalse(), $message); } /** @@ -873,11 +1628,11 @@ final public static function assertFalse(mixed $condition, string $message = '') * * @throws ExpectationFailedException * - * @psalm-assert !false $condition + * @phpstan-assert !false $condition */ final public static function assertNotFalse(mixed $condition, string $message = ''): void { - static::assertThat($condition, static::logicalNot(static::isFalse()), $message); + self::assertThat($condition, self::logicalNot(self::isFalse()), $message); } /** @@ -885,11 +1640,11 @@ final public static function assertNotFalse(mixed $condition, string $message = * * @throws ExpectationFailedException * - * @psalm-assert null $actual + * @phpstan-assert null $actual */ final public static function assertNull(mixed $actual, string $message = ''): void { - static::assertThat($actual, static::isNull(), $message); + self::assertThat($actual, self::isNull(), $message); } /** @@ -897,11 +1652,11 @@ final public static function assertNull(mixed $actual, string $message = ''): vo * * @throws ExpectationFailedException * - * @psalm-assert !null $actual + * @phpstan-assert !null $actual */ final public static function assertNotNull(mixed $actual, string $message = ''): void { - static::assertThat($actual, static::logicalNot(static::isNull()), $message); + self::assertThat($actual, self::logicalNot(self::isNull()), $message); } /** @@ -911,7 +1666,7 @@ final public static function assertNotNull(mixed $actual, string $message = ''): */ final public static function assertFinite(mixed $actual, string $message = ''): void { - static::assertThat($actual, static::isFinite(), $message); + self::assertThat($actual, self::isFinite(), $message); } /** @@ -921,7 +1676,7 @@ final public static function assertFinite(mixed $actual, string $message = ''): */ final public static function assertInfinite(mixed $actual, string $message = ''): void { - static::assertThat($actual, static::isInfinite(), $message); + self::assertThat($actual, self::isInfinite(), $message); } /** @@ -931,7 +1686,7 @@ final public static function assertInfinite(mixed $actual, string $message = '') */ final public static function assertNan(mixed $actual, string $message = ''): void { - static::assertThat($actual, static::isNan(), $message); + self::assertThat($actual, self::isNan(), $message); } /** @@ -941,7 +1696,7 @@ final public static function assertNan(mixed $actual, string $message = ''): voi */ final public static function assertObjectHasProperty(string $propertyName, object $object, string $message = ''): void { - static::assertThat( + self::assertThat( $object, new ObjectHasProperty($propertyName), $message, @@ -955,7 +1710,7 @@ final public static function assertObjectHasProperty(string $propertyName, objec */ final public static function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void { - static::assertThat( + self::assertThat( $object, new LogicalNot( new ObjectHasProperty($propertyName), @@ -969,17 +1724,17 @@ final public static function assertObjectNotHasProperty(string $propertyName, ob * Used on objects, it asserts that two variables reference * the same object. * - * @throws ExpectationFailedException + * @template ExpectedType * - * @psalm-template ExpectedType + * @param ExpectedType $expected * - * @psalm-param ExpectedType $expected + * @throws ExpectationFailedException * - * @psalm-assert =ExpectedType $actual + * @phpstan-assert =ExpectedType $actual */ final public static function assertSame(mixed $expected, mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, new IsIdentical($expected), $message, @@ -996,10 +1751,10 @@ final public static function assertSame(mixed $expected, mixed $actual, string $ final public static function assertNotSame(mixed $expected, mixed $actual, string $message = ''): void { if (is_bool($expected) && is_bool($actual)) { - static::assertNotEquals($expected, $actual, $message); + self::assertNotEquals($expected, $actual, $message); } - static::assertThat( + self::assertThat( $actual, new LogicalNot( new IsIdentical($expected), @@ -1011,15 +1766,15 @@ final public static function assertNotSame(mixed $expected, mixed $actual, strin /** * Asserts that a variable is of a given type. * + * @template ExpectedType of object + * + * @param class-string $expected + * * @throws Exception * @throws ExpectationFailedException * @throws UnknownClassOrInterfaceException * - * @psalm-template ExpectedType of object - * - * @psalm-param class-string $expected - * - * @psalm-assert =ExpectedType $actual + * @phpstan-assert =ExpectedType $actual */ final public static function assertInstanceOf(string $expected, mixed $actual, string $message = ''): void { @@ -1027,7 +1782,7 @@ final public static function assertInstanceOf(string $expected, mixed $actual, s throw new UnknownClassOrInterfaceException($expected); } - static::assertThat( + self::assertThat( $actual, new IsInstanceOf($expected), $message, @@ -1037,14 +1792,14 @@ final public static function assertInstanceOf(string $expected, mixed $actual, s /** * Asserts that a variable is not of a given type. * - * @throws Exception - * @throws ExpectationFailedException + * @template ExpectedType of object * - * @psalm-template ExpectedType of object + * @param class-string $expected * - * @psalm-param class-string $expected + * @throws Exception + * @throws ExpectationFailedException * - * @psalm-assert !ExpectedType $actual + * @phpstan-assert !ExpectedType $actual */ final public static function assertNotInstanceOf(string $expected, mixed $actual, string $message = ''): void { @@ -1052,7 +1807,7 @@ final public static function assertNotInstanceOf(string $expected, mixed $actual throw new UnknownClassOrInterfaceException($expected); } - static::assertThat( + self::assertThat( $actual, new LogicalNot( new IsInstanceOf($expected), @@ -1067,13 +1822,13 @@ final public static function assertNotInstanceOf(string $expected, mixed $actual * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert array $actual + * @phpstan-assert array $actual */ final public static function assertIsArray(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_ARRAY), + new IsType(NativeType::Array), $message, ); } @@ -1084,13 +1839,13 @@ final public static function assertIsArray(mixed $actual, string $message = ''): * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert bool $actual + * @phpstan-assert bool $actual */ final public static function assertIsBool(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_BOOL), + new IsType(NativeType::Bool), $message, ); } @@ -1101,13 +1856,13 @@ final public static function assertIsBool(mixed $actual, string $message = ''): * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert float $actual + * @phpstan-assert float $actual */ final public static function assertIsFloat(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_FLOAT), + new IsType(NativeType::Float), $message, ); } @@ -1118,13 +1873,13 @@ final public static function assertIsFloat(mixed $actual, string $message = ''): * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert int $actual + * @phpstan-assert int $actual */ final public static function assertIsInt(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_INT), + new IsType(NativeType::Int), $message, ); } @@ -1135,13 +1890,13 @@ final public static function assertIsInt(mixed $actual, string $message = ''): v * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert numeric $actual + * @phpstan-assert numeric $actual */ final public static function assertIsNumeric(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_NUMERIC), + new IsType(NativeType::Numeric), $message, ); } @@ -1152,13 +1907,13 @@ final public static function assertIsNumeric(mixed $actual, string $message = '' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert object $actual + * @phpstan-assert object $actual */ final public static function assertIsObject(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_OBJECT), + new IsType(NativeType::Object), $message, ); } @@ -1169,13 +1924,13 @@ final public static function assertIsObject(mixed $actual, string $message = '') * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert resource $actual + * @phpstan-assert resource $actual */ final public static function assertIsResource(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_RESOURCE), + new IsType(NativeType::Resource), $message, ); } @@ -1186,13 +1941,13 @@ final public static function assertIsResource(mixed $actual, string $message = ' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert resource $actual + * @phpstan-assert resource $actual */ final public static function assertIsClosedResource(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_CLOSED_RESOURCE), + new IsType(NativeType::ClosedResource), $message, ); } @@ -1203,13 +1958,13 @@ final public static function assertIsClosedResource(mixed $actual, string $messa * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert string $actual + * @phpstan-assert string $actual */ final public static function assertIsString(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_STRING), + new IsType(NativeType::String), $message, ); } @@ -1220,13 +1975,13 @@ final public static function assertIsString(mixed $actual, string $message = '') * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert scalar $actual + * @phpstan-assert scalar $actual */ final public static function assertIsScalar(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_SCALAR), + new IsType(NativeType::Scalar), $message, ); } @@ -1237,13 +1992,13 @@ final public static function assertIsScalar(mixed $actual, string $message = '') * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert callable $actual + * @phpstan-assert callable $actual */ final public static function assertIsCallable(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_CALLABLE), + new IsType(NativeType::Callable), $message, ); } @@ -1254,13 +2009,13 @@ final public static function assertIsCallable(mixed $actual, string $message = ' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert iterable $actual + * @phpstan-assert iterable $actual */ final public static function assertIsIterable(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new IsType(IsType::TYPE_ITERABLE), + new IsType(NativeType::Iterable), $message, ); } @@ -1271,13 +2026,13 @@ final public static function assertIsIterable(mixed $actual, string $message = ' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !array $actual + * @phpstan-assert !array $actual */ final public static function assertIsNotArray(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_ARRAY)), + new LogicalNot(new IsType(NativeType::Array)), $message, ); } @@ -1288,13 +2043,13 @@ final public static function assertIsNotArray(mixed $actual, string $message = ' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !bool $actual + * @phpstan-assert !bool $actual */ final public static function assertIsNotBool(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_BOOL)), + new LogicalNot(new IsType(NativeType::Bool)), $message, ); } @@ -1305,13 +2060,13 @@ final public static function assertIsNotBool(mixed $actual, string $message = '' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !float $actual + * @phpstan-assert !float $actual */ final public static function assertIsNotFloat(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_FLOAT)), + new LogicalNot(new IsType(NativeType::Float)), $message, ); } @@ -1322,13 +2077,13 @@ final public static function assertIsNotFloat(mixed $actual, string $message = ' * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !int $actual + * @phpstan-assert !int $actual */ final public static function assertIsNotInt(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_INT)), + new LogicalNot(new IsType(NativeType::Int)), $message, ); } @@ -1339,13 +2094,13 @@ final public static function assertIsNotInt(mixed $actual, string $message = '') * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !numeric $actual + * @phpstan-assert !numeric $actual */ final public static function assertIsNotNumeric(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), + new LogicalNot(new IsType(NativeType::Numeric)), $message, ); } @@ -1356,13 +2111,13 @@ final public static function assertIsNotNumeric(mixed $actual, string $message = * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !object $actual + * @phpstan-assert !object $actual */ final public static function assertIsNotObject(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_OBJECT)), + new LogicalNot(new IsType(NativeType::Object)), $message, ); } @@ -1373,13 +2128,13 @@ final public static function assertIsNotObject(mixed $actual, string $message = * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !resource $actual + * @phpstan-assert !resource $actual */ final public static function assertIsNotResource(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), + new LogicalNot(new IsType(NativeType::Resource)), $message, ); } @@ -1390,13 +2145,13 @@ final public static function assertIsNotResource(mixed $actual, string $message * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !resource $actual + * @phpstan-assert !resource $actual */ final public static function assertIsNotClosedResource(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)), + new LogicalNot(new IsType(NativeType::ClosedResource)), $message, ); } @@ -1407,13 +2162,13 @@ final public static function assertIsNotClosedResource(mixed $actual, string $me * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !string $actual + * @phpstan-assert !string $actual */ final public static function assertIsNotString(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_STRING)), + new LogicalNot(new IsType(NativeType::String)), $message, ); } @@ -1424,13 +2179,13 @@ final public static function assertIsNotString(mixed $actual, string $message = * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !scalar $actual + * @phpstan-assert !scalar $actual */ final public static function assertIsNotScalar(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_SCALAR)), + new LogicalNot(new IsType(NativeType::Scalar)), $message, ); } @@ -1441,13 +2196,13 @@ final public static function assertIsNotScalar(mixed $actual, string $message = * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !callable $actual + * @phpstan-assert !callable $actual */ final public static function assertIsNotCallable(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), + new LogicalNot(new IsType(NativeType::Callable)), $message, ); } @@ -1458,13 +2213,13 @@ final public static function assertIsNotCallable(mixed $actual, string $message * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !iterable $actual + * @phpstan-assert !iterable $actual */ final public static function assertIsNotIterable(mixed $actual, string $message = ''): void { - static::assertThat( + self::assertThat( $actual, - new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), + new LogicalNot(new IsType(NativeType::Iterable)), $message, ); } @@ -1476,7 +2231,7 @@ final public static function assertIsNotIterable(mixed $actual, string $message */ final public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void { - static::assertThat($string, new RegularExpression($pattern), $message); + self::assertThat($string, new RegularExpression($pattern), $message); } /** @@ -1486,7 +2241,7 @@ final public static function assertMatchesRegularExpression(string $pattern, str */ final public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void { - static::assertThat( + self::assertThat( $string, new LogicalNot( new RegularExpression($pattern), @@ -1499,6 +2254,9 @@ final public static function assertDoesNotMatchRegularExpression(string $pattern * Assert that the size of two arrays (or `Countable` or `Traversable` objects) * is the same. * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -1513,7 +2271,7 @@ final public static function assertSameSize(Countable|iterable $expected, Counta throw GeneratorNotSupportedException::fromParameterName('$actual'); } - static::assertThat( + self::assertThat( $actual, new SameSize($expected), $message, @@ -1524,6 +2282,9 @@ final public static function assertSameSize(Countable|iterable $expected, Counta * Assert that the size of two arrays (or `Countable` or `Traversable` objects) * is not the same. * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -1538,7 +2299,7 @@ final public static function assertNotSameSize(Countable|iterable $expected, Cou throw GeneratorNotSupportedException::fromParameterName('$actual'); } - static::assertThat( + self::assertThat( $actual, new LogicalNot( new SameSize($expected), @@ -1552,7 +2313,7 @@ final public static function assertNotSameSize(Countable|iterable $expected, Cou */ final public static function assertStringContainsStringIgnoringLineEndings(string $needle, string $haystack, string $message = ''): void { - static::assertThat($haystack, new StringContains($needle, false, true), $message); + self::assertThat($haystack, new StringContains($needle, false, true), $message); } /** @@ -1562,7 +2323,7 @@ final public static function assertStringContainsStringIgnoringLineEndings(strin */ final public static function assertStringEqualsStringIgnoringLineEndings(string $expected, string $actual, string $message = ''): void { - static::assertThat($actual, new StringEqualsStringIgnoringLineEndings($expected), $message); + self::assertThat($actual, new StringEqualsStringIgnoringLineEndings($expected), $message); } /** @@ -1572,9 +2333,9 @@ final public static function assertStringEqualsStringIgnoringLineEndings(string */ final public static function assertFileMatchesFormat(string $format, string $actualFile, string $message = ''): void { - static::assertFileExists($actualFile, $message); + self::assertFileExists($actualFile, $message); - static::assertThat( + self::assertThat( file_get_contents($actualFile), new StringMatchesFormatDescription($format), $message, @@ -1588,12 +2349,16 @@ final public static function assertFileMatchesFormat(string $format, string $act */ final public static function assertFileMatchesFormatFile(string $formatFile, string $actualFile, string $message = ''): void { - static::assertFileExists($formatFile, $message); - static::assertFileExists($actualFile, $message); + self::assertFileExists($formatFile, $message); + self::assertFileExists($actualFile, $message); + + $formatDescription = file_get_contents($formatFile); + + self::assertIsString($formatDescription); - static::assertThat( + self::assertThat( file_get_contents($actualFile), - new StringMatchesFormatDescription(file_get_contents($formatFile)), + new StringMatchesFormatDescription($formatDescription), $message, ); } @@ -1605,30 +2370,7 @@ final public static function assertFileMatchesFormatFile(string $formatFile, str */ final public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void { - static::assertThat($string, new StringMatchesFormatDescription($format), $message); - } - - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 - */ - final public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - null, - 'assertStringNotMatchesFormat() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - static::assertThat( - $string, - new LogicalNot( - new StringMatchesFormatDescription($format), - ), - $message, - ); + self::assertThat($string, new StringMatchesFormatDescription($format), $message); } /** @@ -1638,39 +2380,16 @@ final public static function assertStringNotMatchesFormat(string $format, string */ final public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { - static::assertFileExists($formatFile, $message); + self::assertFileExists($formatFile, $message); - static::assertThat( - $string, - new StringMatchesFormatDescription( - file_get_contents($formatFile), - ), - $message, - ); - } - - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 - */ - final public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - null, - 'assertStringNotMatchesFormatFile() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); + $formatDescription = file_get_contents($formatFile); - static::assertFileExists($formatFile, $message); + self::assertIsString($formatDescription); - static::assertThat( + self::assertThat( $string, - new LogicalNot( - new StringMatchesFormatDescription( - file_get_contents($formatFile), - ), + new StringMatchesFormatDescription( + $formatDescription, ), $message, ); @@ -1679,27 +2398,27 @@ final public static function assertStringNotMatchesFormatFile(string $formatFile /** * Asserts that a string starts with a given prefix. * - * @psalm-param non-empty-string $prefix + * @param non-empty-string $prefix * * @throws ExpectationFailedException * @throws InvalidArgumentException */ final public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void { - static::assertThat($string, new StringStartsWith($prefix), $message); + self::assertThat($string, new StringStartsWith($prefix), $message); } /** * Asserts that a string starts not with a given prefix. * - * @psalm-param non-empty-string $prefix + * @param non-empty-string $prefix * * @throws ExpectationFailedException * @throws InvalidArgumentException */ final public static function assertStringStartsNotWith(string $prefix, string $string, string $message = ''): void { - static::assertThat( + self::assertThat( $string, new LogicalNot( new StringStartsWith($prefix), @@ -1715,7 +2434,7 @@ final public static function assertStringContainsString(string $needle, string $ { $constraint = new StringContains($needle); - static::assertThat($haystack, $constraint, $message); + self::assertThat($haystack, $constraint, $message); } /** @@ -1725,7 +2444,7 @@ final public static function assertStringContainsStringIgnoringCase(string $need { $constraint = new StringContains($needle, true); - static::assertThat($haystack, $constraint, $message); + self::assertThat($haystack, $constraint, $message); } /** @@ -1735,7 +2454,7 @@ final public static function assertStringNotContainsString(string $needle, strin { $constraint = new LogicalNot(new StringContains($needle)); - static::assertThat($haystack, $constraint, $message); + self::assertThat($haystack, $constraint, $message); } /** @@ -1745,33 +2464,33 @@ final public static function assertStringNotContainsStringIgnoringCase(string $n { $constraint = new LogicalNot(new StringContains($needle, true)); - static::assertThat($haystack, $constraint, $message); + self::assertThat($haystack, $constraint, $message); } /** * Asserts that a string ends with a given suffix. * - * @psalm-param non-empty-string $suffix + * @param non-empty-string $suffix * * @throws ExpectationFailedException * @throws InvalidArgumentException */ final public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void { - static::assertThat($string, new StringEndsWith($suffix), $message); + self::assertThat($string, new StringEndsWith($suffix), $message); } /** * Asserts that a string ends not with a given suffix. * - * @psalm-param non-empty-string $suffix + * @param non-empty-string $suffix * * @throws ExpectationFailedException * @throws InvalidArgumentException */ final public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void { - static::assertThat( + self::assertThat( $string, new LogicalNot( new StringEndsWith($suffix), @@ -1792,7 +2511,7 @@ final public static function assertXmlFileEqualsXmlFile(string $expectedFile, st $expected = (new XmlLoader)->loadFile($expectedFile); $actual = (new XmlLoader)->loadFile($actualFile); - static::assertEquals($expected, $actual, $message); + self::assertEquals($expected, $actual, $message); } /** @@ -1806,7 +2525,7 @@ final public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, $expected = (new XmlLoader)->loadFile($expectedFile); $actual = (new XmlLoader)->loadFile($actualFile); - static::assertNotEquals($expected, $actual, $message); + self::assertNotEquals($expected, $actual, $message); } /** @@ -1820,7 +2539,7 @@ final public static function assertXmlStringEqualsXmlFile(string $expectedFile, $expected = (new XmlLoader)->loadFile($expectedFile); $actual = (new XmlLoader)->load($actualXml); - static::assertEquals($expected, $actual, $message); + self::assertEquals($expected, $actual, $message); } /** @@ -1834,7 +2553,7 @@ final public static function assertXmlStringNotEqualsXmlFile(string $expectedFil $expected = (new XmlLoader)->loadFile($expectedFile); $actual = (new XmlLoader)->load($actualXml); - static::assertNotEquals($expected, $actual, $message); + self::assertNotEquals($expected, $actual, $message); } /** @@ -1848,7 +2567,7 @@ final public static function assertXmlStringEqualsXmlString(string $expectedXml, $expected = (new XmlLoader)->load($expectedXml); $actual = (new XmlLoader)->load($actualXml); - static::assertEquals($expected, $actual, $message); + self::assertEquals($expected, $actual, $message); } /** @@ -1862,7 +2581,7 @@ final public static function assertXmlStringNotEqualsXmlString(string $expectedX $expected = (new XmlLoader)->load($expectedXml); $actual = (new XmlLoader)->load($actualXml); - static::assertNotEquals($expected, $actual, $message); + self::assertNotEquals($expected, $actual, $message); } /** @@ -1874,27 +2593,7 @@ final public static function assertThat(mixed $value, Constraint $constraint, st { self::$count += count($constraint); - $hasFailed = true; - - try { - $constraint->evaluate($value, $message); - - $hasFailed = false; - } finally { - if ($hasFailed) { - Event\Facade::emitter()->testAssertionFailed( - $value, - $constraint, - $message, - ); - } else { - Event\Facade::emitter()->testAssertionSucceeded( - $value, - $constraint, - $message, - ); - } - } + $constraint->evaluate($value, $message); } /** @@ -1904,7 +2603,7 @@ final public static function assertThat(mixed $value, Constraint $constraint, st */ final public static function assertJson(string $actual, string $message = ''): void { - static::assertThat($actual, static::isJson(), $message); + self::assertThat($actual, self::isJson(), $message); } /** @@ -1914,10 +2613,10 @@ final public static function assertJson(string $actual, string $message = ''): v */ final public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); - static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + self::assertThat($actualJson, new JsonMatches($expectedJson), $message); } /** @@ -1927,10 +2626,10 @@ final public static function assertJsonStringEqualsJsonString(string $expectedJs */ final public static function assertJsonStringNotEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); - static::assertThat( + self::assertThat( $actualJson, new LogicalNot( new JsonMatches($expectedJson), @@ -1946,13 +2645,15 @@ final public static function assertJsonStringNotEqualsJsonString(string $expecte */ final public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); + self::assertIsString($expectedJson); + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); - static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + self::assertThat($actualJson, new JsonMatches($expectedJson), $message); } /** @@ -1962,13 +2663,15 @@ final public static function assertJsonStringEqualsJsonFile(string $expectedFile */ final public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { - static::assertFileExists($expectedFile, $message); + self::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); + self::assertIsString($expectedJson); + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); - static::assertThat( + self::assertThat( $actualJson, new LogicalNot( new JsonMatches($expectedJson), @@ -1984,23 +2687,21 @@ final public static function assertJsonStringNotEqualsJsonFile(string $expectedF */ final public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - static::assertFileExists($actualFile, $message); + self::assertFileExists($expectedFile, $message); - $actualJson = file_get_contents($actualFile); $expectedJson = file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); + self::assertIsString($expectedJson); + self::assertJson($expectedJson, $message); - $constraintExpected = new JsonMatches( - $expectedJson, - ); + self::assertFileExists($actualFile, $message); + + $actualJson = file_get_contents($actualFile); - $constraintActual = new JsonMatches($actualJson); + self::assertIsString($actualJson); + self::assertJson($actualJson, $message); - static::assertThat($expectedJson, $constraintActual, $message); - static::assertThat($actualJson, $constraintExpected, $message); + self::assertThat($actualJson, new JsonMatches($expectedJson), $message); } /** @@ -2010,23 +2711,21 @@ final public static function assertJsonFileEqualsJsonFile(string $expectedFile, */ final public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { - static::assertFileExists($expectedFile, $message); - static::assertFileExists($actualFile, $message); + self::assertFileExists($expectedFile, $message); - $actualJson = file_get_contents($actualFile); $expectedJson = file_get_contents($expectedFile); - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); + self::assertIsString($expectedJson); + self::assertJson($expectedJson, $message); - $constraintExpected = new JsonMatches( - $expectedJson, - ); + self::assertFileExists($actualFile, $message); + + $actualJson = file_get_contents($actualFile); - $constraintActual = new JsonMatches($actualJson); + self::assertIsString($actualJson); + self::assertJson($actualJson, $message); - static::assertThat($expectedJson, new LogicalNot($constraintActual), $message); - static::assertThat($actualJson, new LogicalNot($constraintExpected), $message); + self::assertThat($actualJson, self::logicalNot(new JsonMatches($expectedJson)), $message); } /** @@ -2063,11 +2762,11 @@ final public static function isTrue(): IsTrue } /** - * @psalm-template CallbackInput of mixed + * @template CallbackInput of mixed * - * @psalm-param callable(CallbackInput $callback): bool $callback + * @param callable(CallbackInput $callback): bool $callback * - * @psalm-return Callback + * @return Callback */ final public static function callback(callable $callback): Callback { @@ -2115,22 +2814,93 @@ final public static function containsIdentical(mixed $value): TraversableContain } /** + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type + * * @throws Exception + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6060 */ final public static function containsOnly(string $type): TraversableContainsOnly { - return new TraversableContainsOnly($type); + return TraversableContainsOnly::forNativeType(self::mapNativeType($type)); + } + + final public static function containsOnlyArray(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Array); + } + + final public static function containsOnlyBool(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Bool); + } + + final public static function containsOnlyCallable(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Callable); + } + + final public static function containsOnlyFloat(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Float); + } + + final public static function containsOnlyInt(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Int); + } + + final public static function containsOnlyIterable(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Iterable); + } + + final public static function containsOnlyNull(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Null); + } + + final public static function containsOnlyNumeric(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Numeric); + } + + final public static function containsOnlyObject(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Object); + } + + final public static function containsOnlyResource(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Resource); + } + + final public static function containsOnlyClosedResource(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::ClosedResource); + } + + final public static function containsOnlyScalar(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::Scalar); + } + + final public static function containsOnlyString(): TraversableContainsOnly + { + return TraversableContainsOnly::forNativeType(NativeType::String); } /** + * @param class-string $className + * * @throws Exception */ final public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly { - return new TraversableContainsOnly($className, false); + return TraversableContainsOnly::forClassOrInterface($className); } - final public static function arrayHasKey(int|string $key): ArrayHasKey + final public static function arrayHasKey(mixed $key): ArrayHasKey { return new ArrayHasKey($key); } @@ -2192,7 +2962,7 @@ final public static function greaterThan(mixed $value): GreaterThan final public static function greaterThanOrEqual(mixed $value): LogicalOr { - return static::logicalOr( + return self::logicalOr( new IsEqual($value), new GreaterThan($value), ); @@ -2211,14 +2981,108 @@ final public static function isInstanceOf(string $className): IsInstanceOf return new IsInstanceOf($className); } + final public static function isArray(): IsType + { + return new IsType(NativeType::Array); + } + + final public static function isBool(): IsType + { + return new IsType(NativeType::Bool); + } + + final public static function isCallable(): IsType + { + return new IsType(NativeType::Callable); + } + + final public static function isFloat(): IsType + { + return new IsType(NativeType::Float); + } + + final public static function isInt(): IsType + { + return new IsType(NativeType::Int); + } + + final public static function isIterable(): IsType + { + return new IsType(NativeType::Iterable); + } + + final public static function isNumeric(): IsType + { + return new IsType(NativeType::Numeric); + } + + final public static function isObject(): IsType + { + return new IsType(NativeType::Object); + } + + final public static function isResource(): IsType + { + return new IsType(NativeType::Resource); + } + + final public static function isClosedResource(): IsType + { + return new IsType(NativeType::ClosedResource); + } + + final public static function isScalar(): IsType + { + return new IsType(NativeType::Scalar); + } + + final public static function isString(): IsType + { + return new IsType(NativeType::String); + } + /** - * @psalm-param 'array'|'boolean'|'bool'|'double'|'float'|'integer'|'int'|'null'|'numeric'|'object'|'real'|'resource'|'resource (closed)'|'string'|'scalar'|'callable'|'iterable' $type + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type * - * @throws Exception + * @throws UnknownNativeTypeException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6053 */ final public static function isType(string $type): IsType { - return new IsType($type); + $constraint = new IsType(self::mapNativeType($type)); + + $replacement = match ($type) { + 'array' => 'isArray', + 'bool' => 'isBool', + 'boolean' => 'isBool', + 'callable' => 'isCallable', + 'double' => 'isFloat', + 'float' => 'isFloat', + 'int' => 'isInt', + 'integer' => 'isInt', + 'iterable' => 'isIterable', + 'null' => 'isNull', + 'numeric' => 'isNumeric', + 'object' => 'isObject', + 'real' => 'isFloat', + 'resource' => 'isResource', + 'resource (closed)' => 'isClosedResource', + 'scalar' => 'isScalar', + 'string' => 'isString', + }; + + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + null, + sprintf( + 'isType(\'%s\') is deprecated and will be removed in PHPUnit 13. ' . + 'Please use the %s() method instead.', + $type, + $replacement, + ), + ); + + return $constraint; } final public static function lessThan(mixed $value): LessThan @@ -2228,7 +3092,7 @@ final public static function lessThan(mixed $value): LessThan final public static function lessThanOrEqual(mixed $value): LogicalOr { - return static::logicalOr( + return self::logicalOr( new IsEqual($value), new LessThan($value), ); @@ -2245,7 +3109,7 @@ final public static function matches(string $string): StringMatchesFormatDescrip } /** - * @psalm-param non-empty-string $prefix + * @param non-empty-string $prefix * * @throws InvalidArgumentException */ @@ -2260,7 +3124,7 @@ final public static function stringContains(string $string, bool $case = true): } /** - * @psalm-param non-empty-string $suffix + * @param non-empty-string $suffix * * @throws InvalidArgumentException */ @@ -2334,9 +3198,53 @@ final public static function resetCount(): void private static function isNativeType(string $type): bool { + return $type === 'array' || + $type === 'bool' || + $type === 'boolean' || + $type === 'callable' || + $type === 'double' || + $type === 'float' || + $type === 'int' || + $type === 'integer' || + $type === 'iterable' || + $type === 'null' || + $type === 'numeric' || + $type === 'object' || + $type === 'real' || + $type === 'resource' || + $type === 'resource (closed)' || + $type === 'scalar' || + $type === 'string'; + } + + /** + * @throws UnknownNativeTypeException + */ + private static function mapNativeType(string $type): NativeType + { + if (!self::isNativeType($type)) { + throw new UnknownNativeTypeException($type); + } + + /** @phpstan-ignore match.unhandled */ return match ($type) { - 'numeric', 'integer', 'int', 'iterable', 'float', 'string', 'boolean', 'bool', 'null', 'array', 'object', 'resource', 'scalar' => true, - default => false, + 'array' => NativeType::Array, + 'bool' => NativeType::Bool, + 'boolean' => NativeType::Bool, + 'callable' => NativeType::Callable, + 'double' => NativeType::Float, + 'float' => NativeType::Float, + 'int' => NativeType::Int, + 'integer' => NativeType::Int, + 'iterable' => NativeType::Iterable, + 'null' => NativeType::Null, + 'numeric' => NativeType::Numeric, + 'object' => NativeType::Object, + 'real' => NativeType::Float, + 'resource' => NativeType::Resource, + 'resource (closed)' => NativeType::ClosedResource, + 'scalar' => NativeType::Scalar, + 'string' => NativeType::String, }; } } diff --git a/src/Framework/Assert/Functions.php b/src/Framework/Assert/Functions.php index b421d349f05..0645fd2629f 100644 --- a/src/Framework/Assert/Functions.php +++ b/src/Framework/Assert/Functions.php @@ -59,20 +59,100 @@ use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher; use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; use PHPUnit\Util\Xml\XmlException; use Throwable; +if (!function_exists('PHPUnit\Framework\assertArrayIsEqualToArrayOnlyConsideringListOfKeys')) { + /** + * Asserts that two arrays are equal while only considering a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeConsidered + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayIsEqualToArrayOnlyConsideringListOfKeys + */ + function assertArrayIsEqualToArrayOnlyConsideringListOfKeys(array $expected, array $actual, array $keysToBeConsidered, string $message = ''): void + { + Assert::assertArrayIsEqualToArrayOnlyConsideringListOfKeys(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertArrayIsEqualToArrayIgnoringListOfKeys')) { + /** + * Asserts that two arrays are equal while ignoring a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeIgnored + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayIsEqualToArrayIgnoringListOfKeys + */ + function assertArrayIsEqualToArrayIgnoringListOfKeys(array $expected, array $actual, array $keysToBeIgnored, string $message = ''): void + { + Assert::assertArrayIsEqualToArrayIgnoringListOfKeys(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys')) { + /** + * Asserts that two arrays are identical while only considering a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeConsidered + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys + */ + function assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys(array $expected, array $actual, array $keysToBeConsidered, string $message = ''): void + { + Assert::assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertArrayIsIdenticalToArrayIgnoringListOfKeys')) { + /** + * Asserts that two arrays are equal while ignoring a list of keys. + * + * @param array $expected + * @param array $actual + * @param non-empty-list $keysToBeIgnored + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertArrayIsIdenticalToArrayIgnoringListOfKeys + */ + function assertArrayIsIdenticalToArrayIgnoringListOfKeys(array $expected, array $actual, array $keysToBeIgnored, string $message = ''): void + { + Assert::assertArrayIsIdenticalToArrayIgnoringListOfKeys(...func_get_args()); + } +} + if (!function_exists('PHPUnit\Framework\assertArrayHasKey')) { /** * Asserts that an array has a specified key. * + * @param array|ArrayAccess $array + * * @throws Exception * @throws ExpectationFailedException * @@ -80,7 +160,7 @@ * * @see Assert::assertArrayHasKey */ - function assertArrayHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void + function assertArrayHasKey(mixed $key, array|ArrayAccess $array, string $message = ''): void { Assert::assertArrayHasKey(...func_get_args()); } @@ -90,6 +170,8 @@ function assertArrayHasKey(int|string $key, array|ArrayAccess $array, string $me /** * Asserts that an array does not have a specified key. * + * @param array|ArrayAccess $array + * * @throws Exception * @throws ExpectationFailedException * @@ -97,7 +179,7 @@ function assertArrayHasKey(int|string $key, array|ArrayAccess $array, string $me * * @see Assert::assertArrayNotHasKey */ - function assertArrayNotHasKey(int|string $key, array|ArrayAccess $array, string $message = ''): void + function assertArrayNotHasKey(mixed $key, array|ArrayAccess $array, string $message = ''): void { Assert::assertArrayNotHasKey(...func_get_args()); } @@ -105,6 +187,8 @@ function assertArrayNotHasKey(int|string $key, array|ArrayAccess $array, string if (!function_exists('PHPUnit\Framework\assertIsList')) { /** + * @phpstan-assert list $array + * * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit @@ -121,6 +205,8 @@ function assertIsList(mixed $array, string $message = ''): void /** * Asserts that a haystack contains a needle. * + * @param iterable $haystack + * * @throws Exception * @throws ExpectationFailedException * @@ -130,103 +216,640 @@ function assertIsList(mixed $array, string $message = ''): void */ function assertContains(mixed $needle, iterable $haystack, string $message = ''): void { - Assert::assertContains(...func_get_args()); + Assert::assertContains(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsEquals')) { + /** + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsEquals + */ + function assertContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + { + Assert::assertContainsEquals(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertNotContains')) { + /** + * Asserts that a haystack does not contain a needle. + * + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContains + */ + function assertNotContains(mixed $needle, iterable $haystack, string $message = ''): void + { + Assert::assertNotContains(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertNotContainsEquals')) { + /** + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContainsEquals + */ + function assertNotContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + { + Assert::assertNotContainsEquals(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnly')) { + /** + * Asserts that a haystack contains only values of a given type. + * + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6056 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnly + */ + function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + { + Assert::assertContainsOnly(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyArray')) { + /** + * Asserts that a haystack contains only values of type array. + * + * @phpstan-assert iterable> $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyArray + */ + function assertContainsOnlyArray(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyArray(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyBool')) { + /** + * Asserts that a haystack contains only values of type bool. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyBool + */ + function assertContainsOnlyBool(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyBool(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyCallable')) { + /** + * Asserts that a haystack contains only values of type callable. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyCallable + */ + function assertContainsOnlyCallable(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyCallable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyFloat')) { + /** + * Asserts that a haystack contains only values of type float. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyFloat + */ + function assertContainsOnlyFloat(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyFloat(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyInt')) { + /** + * Asserts that a haystack contains only values of type int. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyInt + */ + function assertContainsOnlyInt(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyInt(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyIterable')) { + /** + * Asserts that a haystack contains only values of type iterable. + * + * @phpstan-assert iterable> $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyIterable + */ + function assertContainsOnlyIterable(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyIterable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyNull')) { + /** + * Asserts that a haystack contains only values of type null. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyNull + */ + function assertContainsOnlyNull(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyNull(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyNumeric')) { + /** + * Asserts that a haystack contains only values of type numeric. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyNumeric + */ + function assertContainsOnlyNumeric(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyNumeric(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyObject')) { + /** + * Asserts that a haystack contains only values of type object. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyObject + */ + function assertContainsOnlyObject(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyObject(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyResource')) { + /** + * Asserts that a haystack contains only values of type resource. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyResource + */ + function assertContainsOnlyResource(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyResource(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyClosedResource')) { + /** + * Asserts that a haystack contains only values of type closed resource. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyClosedResource + */ + function assertContainsOnlyClosedResource(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyClosedResource(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyScalar')) { + /** + * Asserts that a haystack contains only values of type scalar. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyScalar + */ + function assertContainsOnlyScalar(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyScalar(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyString')) { + /** + * Asserts that a haystack contains only values of type string. + * + * @phpstan-assert iterable $haystack + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyString + */ + function assertContainsOnlyString(iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyString(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsOnlyInstancesOf')) { + /** + * Asserts that a haystack contains only instances of a specified interface or class name. + * + * @template T + * + * @phpstan-assert iterable $haystack + * + * @param class-string $className + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsOnlyInstancesOf + */ + function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void + { + Assert::assertContainsOnlyInstancesOf(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertNotContainsOnly')) { + /** + * Asserts that a haystack does not contain only values of a given type. + * + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type + * @param iterable $haystack + * + * @throws Exception + * @throws ExpectationFailedException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/6056 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertNotContainsOnly + */ + function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + { + Assert::assertNotContainsOnly(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyArray')) { + /** + * Asserts that a haystack does not contain only values of type array. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyArray + */ + function assertContainsNotOnlyArray(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyArray(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyBool')) { + /** + * Asserts that a haystack does not contain only values of type bool. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyBool + */ + function assertContainsNotOnlyBool(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyBool(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyCallable')) { + /** + * Asserts that a haystack does not contain only values of type callable. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyCallable + */ + function assertContainsNotOnlyCallable(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyCallable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyFloat')) { + /** + * Asserts that a haystack does not contain only values of type float. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyFloat + */ + function assertContainsNotOnlyFloat(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyFloat(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyInt')) { + /** + * Asserts that a haystack does not contain only values of type int. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyInt + */ + function assertContainsNotOnlyInt(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyInt(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyIterable')) { + /** + * Asserts that a haystack does not contain only values of type iterable. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyIterable + */ + function assertContainsNotOnlyIterable(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyIterable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyNull')) { + /** + * Asserts that a haystack does not contain only values of type null. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyNull + */ + function assertContainsNotOnlyNull(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyNull(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyNumeric')) { + /** + * Asserts that a haystack does not contain only values of type numeric. + * + * @param iterable $haystack + * + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertContainsNotOnlyNumeric + */ + function assertContainsNotOnlyNumeric(iterable $haystack, string $message = ''): void + { + Assert::assertContainsNotOnlyNumeric(...func_get_args()); } } -if (!function_exists('PHPUnit\Framework\assertContainsEquals')) { +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyObject')) { /** + * Asserts that a haystack does not contain only values of type object. + * + * @param iterable $haystack + * * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertContainsEquals + * @see Assert::assertContainsNotOnlyObject */ - function assertContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + function assertContainsNotOnlyObject(iterable $haystack, string $message = ''): void { - Assert::assertContainsEquals(...func_get_args()); + Assert::assertContainsNotOnlyObject(...func_get_args()); } } -if (!function_exists('PHPUnit\Framework\assertNotContains')) { +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyResource')) { /** - * Asserts that a haystack does not contain a needle. + * Asserts that a haystack does not contain only values of type resource. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertNotContains + * @see Assert::assertContainsNotOnlyResource */ - function assertNotContains(mixed $needle, iterable $haystack, string $message = ''): void + function assertContainsNotOnlyResource(iterable $haystack, string $message = ''): void { - Assert::assertNotContains(...func_get_args()); + Assert::assertContainsNotOnlyResource(...func_get_args()); } } -if (!function_exists('PHPUnit\Framework\assertNotContainsEquals')) { +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyClosedResource')) { /** + * Asserts that a haystack does not contain only values of type closed resource. + * + * @param iterable $haystack + * * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertNotContainsEquals + * @see Assert::assertContainsNotOnlyClosedResource */ - function assertNotContainsEquals(mixed $needle, iterable $haystack, string $message = ''): void + function assertContainsNotOnlyClosedResource(iterable $haystack, string $message = ''): void { - Assert::assertNotContainsEquals(...func_get_args()); + Assert::assertContainsNotOnlyClosedResource(...func_get_args()); } } -if (!function_exists('PHPUnit\Framework\assertContainsOnly')) { +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyScalar')) { /** - * Asserts that a haystack contains only values of a given type. + * Asserts that a haystack does not contain only values of type scalar. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertContainsOnly + * @see Assert::assertContainsNotOnlyScalar */ - function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + function assertContainsNotOnlyScalar(iterable $haystack, string $message = ''): void { - Assert::assertContainsOnly(...func_get_args()); + Assert::assertContainsNotOnlyScalar(...func_get_args()); } } -if (!function_exists('PHPUnit\Framework\assertContainsOnlyInstancesOf')) { +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyString')) { /** - * Asserts that a haystack contains only instances of a given class name. + * Asserts that a haystack does not contain only values of type string. + * + * @param iterable $haystack * - * @throws Exception * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertContainsOnlyInstancesOf + * @see Assert::assertContainsNotOnlyString */ - function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void + function assertContainsNotOnlyString(iterable $haystack, string $message = ''): void { - Assert::assertContainsOnlyInstancesOf(...func_get_args()); + Assert::assertContainsNotOnlyString(...func_get_args()); } } -if (!function_exists('PHPUnit\Framework\assertNotContainsOnly')) { +if (!function_exists('PHPUnit\Framework\assertContainsNotOnlyInstancesOf')) { /** - * Asserts that a haystack does not contain only values of a given type. + * Asserts that a haystack does not contain only instances of a specified interface or class name. + * + * @param class-string $className + * @param iterable $haystack * * @throws Exception * @throws ExpectationFailedException * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertNotContainsOnly + * @see Assert::assertContainsNotOnlyInstancesOf */ - function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + function assertContainsNotOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void { - Assert::assertNotContainsOnly(...func_get_args()); + Assert::assertContainsNotOnlyInstancesOf(...func_get_args()); } } @@ -234,6 +857,8 @@ function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNative /** * Asserts the number of elements of an array, Countable or Traversable. * + * @param Countable|iterable $haystack + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -252,6 +877,8 @@ function assertCount(int $expectedCount, Countable|iterable $haystack, string $m /** * Asserts the number of elements of an array, Countable or Traversable. * + * @param Countable|iterable $haystack + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -408,6 +1035,20 @@ function assertObjectEquals(object $expected, object $actual, string $method = ' } } +if (!function_exists('PHPUnit\Framework\assertObjectNotEquals')) { + /** + * @throws ExpectationFailedException + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @see Assert::assertObjectNotEquals + */ + function assertObjectNotEquals(object $expected, object $actual, string $method = 'equals', string $message = ''): void + { + Assert::assertObjectNotEquals(...func_get_args()); + } +} + if (!function_exists('PHPUnit\Framework\assertEmpty')) { /** * Asserts that a variable is empty. @@ -415,8 +1056,6 @@ function assertObjectEquals(object $expected, object $actual, string $method = ' * @throws ExpectationFailedException * @throws GeneratorNotSupportedException * - * @psalm-assert empty $actual - * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @see Assert::assertEmpty @@ -434,8 +1073,6 @@ function assertEmpty(mixed $actual, string $message = ''): void * @throws ExpectationFailedException * @throws GeneratorNotSupportedException * - * @psalm-assert !empty $actual - * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @see Assert::assertNotEmpty @@ -456,7 +1093,7 @@ function assertNotEmpty(mixed $actual, string $message = ''): void * * @see Assert::assertGreaterThan */ - function assertGreaterThan(mixed $expected, mixed $actual, string $message = ''): void + function assertGreaterThan(mixed $minimum, mixed $actual, string $message = ''): void { Assert::assertGreaterThan(...func_get_args()); } @@ -472,7 +1109,7 @@ function assertGreaterThan(mixed $expected, mixed $actual, string $message = '') * * @see Assert::assertGreaterThanOrEqual */ - function assertGreaterThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void + function assertGreaterThanOrEqual(mixed $minimum, mixed $actual, string $message = ''): void { Assert::assertGreaterThanOrEqual(...func_get_args()); } @@ -488,7 +1125,7 @@ function assertGreaterThanOrEqual(mixed $expected, mixed $actual, string $messag * * @see Assert::assertLessThan */ - function assertLessThan(mixed $expected, mixed $actual, string $message = ''): void + function assertLessThan(mixed $maximum, mixed $actual, string $message = ''): void { Assert::assertLessThan(...func_get_args()); } @@ -504,7 +1141,7 @@ function assertLessThan(mixed $expected, mixed $actual, string $message = ''): v * * @see Assert::assertLessThanOrEqual */ - function assertLessThanOrEqual(mixed $expected, mixed $actual, string $message = ''): void + function assertLessThanOrEqual(mixed $maximum, mixed $actual, string $message = ''): void { Assert::assertLessThanOrEqual(...func_get_args()); } @@ -976,7 +1613,7 @@ function assertFileIsNotWritable(string $file, string $message = ''): void * * @throws ExpectationFailedException * - * @psalm-assert true $condition + * @phpstan-assert true $condition * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -994,7 +1631,7 @@ function assertTrue(mixed $condition, string $message = ''): void * * @throws ExpectationFailedException * - * @psalm-assert !true $condition + * @phpstan-assert !true $condition * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1012,7 +1649,7 @@ function assertNotTrue(mixed $condition, string $message = ''): void * * @throws ExpectationFailedException * - * @psalm-assert false $condition + * @phpstan-assert false $condition * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1030,7 +1667,7 @@ function assertFalse(mixed $condition, string $message = ''): void * * @throws ExpectationFailedException * - * @psalm-assert !false $condition + * @phpstan-assert !false $condition * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1048,7 +1685,7 @@ function assertNotFalse(mixed $condition, string $message = ''): void * * @throws ExpectationFailedException * - * @psalm-assert null $actual + * @phpstan-assert null $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1066,7 +1703,7 @@ function assertNull(mixed $actual, string $message = ''): void * * @throws ExpectationFailedException * - * @psalm-assert !null $actual + * @phpstan-assert !null $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1134,9 +1771,9 @@ function assertNan(mixed $actual, string $message = ''): void * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertObjectHasProperty() + * @see Assert::assertObjectHasProperty */ - function assertObjectHasProperty(string $attributeName, object $object, string $message = ''): void + function assertObjectHasProperty(string $propertyName, object $object, string $message = ''): void { Assert::assertObjectHasProperty(...func_get_args()); } @@ -1150,9 +1787,9 @@ function assertObjectHasProperty(string $attributeName, object $object, string $ * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @see Assert::assertObjectNotHasProperty() + * @see Assert::assertObjectNotHasProperty */ - function assertObjectNotHasProperty(string $attributeName, object $object, string $message = ''): void + function assertObjectNotHasProperty(string $propertyName, object $object, string $message = ''): void { Assert::assertObjectNotHasProperty(...func_get_args()); } @@ -1164,13 +1801,13 @@ function assertObjectNotHasProperty(string $attributeName, object $object, strin * Used on objects, it asserts that two variables reference * the same object. * - * @throws ExpectationFailedException + * @template ExpectedType * - * @psalm-template ExpectedType + * @param ExpectedType $expected * - * @psalm-param ExpectedType $expected + * @throws ExpectationFailedException * - * @psalm-assert =ExpectedType $actual + * @phpstan-assert =ExpectedType $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1204,14 +1841,15 @@ function assertNotSame(mixed $expected, mixed $actual, string $message = ''): vo /** * Asserts that a variable is of a given type. * - * @throws Exception - * @throws ExpectationFailedException + * @template ExpectedType of object * - * @psalm-template ExpectedType of object + * @param class-string $expected * - * @psalm-param class-string $expected + * @throws Exception + * @throws ExpectationFailedException + * @throws UnknownClassOrInterfaceException * - * @psalm-assert =ExpectedType $actual + * @phpstan-assert =ExpectedType $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1227,14 +1865,14 @@ function assertInstanceOf(string $expected, mixed $actual, string $message = '') /** * Asserts that a variable is not of a given type. * - * @throws Exception - * @throws ExpectationFailedException + * @template ExpectedType of object * - * @psalm-template ExpectedType of object + * @param class-string $expected * - * @psalm-param class-string $expected + * @throws Exception + * @throws ExpectationFailedException * - * @psalm-assert !ExpectedType $actual + * @phpstan-assert !ExpectedType $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1253,7 +1891,7 @@ function assertNotInstanceOf(string $expected, mixed $actual, string $message = * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert array $actual + * @phpstan-assert array $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1272,7 +1910,7 @@ function assertIsArray(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert bool $actual + * @phpstan-assert bool $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1291,7 +1929,7 @@ function assertIsBool(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert float $actual + * @phpstan-assert float $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1310,7 +1948,7 @@ function assertIsFloat(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert int $actual + * @phpstan-assert int $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1329,7 +1967,7 @@ function assertIsInt(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert numeric $actual + * @phpstan-assert numeric $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1348,7 +1986,7 @@ function assertIsNumeric(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert object $actual + * @phpstan-assert object $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1367,7 +2005,7 @@ function assertIsObject(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert resource $actual + * @phpstan-assert resource $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1386,7 +2024,7 @@ function assertIsResource(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert resource $actual + * @phpstan-assert resource $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1405,7 +2043,7 @@ function assertIsClosedResource(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert string $actual + * @phpstan-assert string $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1424,7 +2062,7 @@ function assertIsString(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert scalar $actual + * @phpstan-assert scalar $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1443,7 +2081,7 @@ function assertIsScalar(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert callable $actual + * @phpstan-assert callable $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1462,7 +2100,7 @@ function assertIsCallable(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert iterable $actual + * @phpstan-assert iterable $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1481,7 +2119,7 @@ function assertIsIterable(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !array $actual + * @phpstan-assert !array $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1500,7 +2138,7 @@ function assertIsNotArray(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !bool $actual + * @phpstan-assert !bool $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1519,7 +2157,7 @@ function assertIsNotBool(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !float $actual + * @phpstan-assert !float $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1538,7 +2176,7 @@ function assertIsNotFloat(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !int $actual + * @phpstan-assert !int $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1557,7 +2195,7 @@ function assertIsNotInt(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !numeric $actual + * @phpstan-assert !numeric $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1576,7 +2214,7 @@ function assertIsNotNumeric(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !object $actual + * @phpstan-assert !object $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1595,7 +2233,7 @@ function assertIsNotObject(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !resource $actual + * @phpstan-assert !resource $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1614,7 +2252,7 @@ function assertIsNotResource(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !resource $actual + * @phpstan-assert !resource $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1633,7 +2271,7 @@ function assertIsNotClosedResource(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !string $actual + * @phpstan-assert !string $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1652,7 +2290,7 @@ function assertIsNotString(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !scalar $actual + * @phpstan-assert !scalar $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1671,7 +2309,7 @@ function assertIsNotScalar(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !callable $actual + * @phpstan-assert !callable $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1690,7 +2328,7 @@ function assertIsNotCallable(mixed $actual, string $message = ''): void * @throws Exception * @throws ExpectationFailedException * - * @psalm-assert !iterable $actual + * @phpstan-assert !iterable $actual * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * @@ -1739,6 +2377,9 @@ function assertDoesNotMatchRegularExpression(string $pattern, string $string, st * Assert that the size of two arrays (or `Countable` or `Traversable` objects) * is the same. * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -1758,6 +2399,9 @@ function assertSameSize(Countable|iterable $expected, Countable|iterable $actual * Assert that the size of two arrays (or `Countable` or `Traversable` objects) * is not the same. * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * * @throws Exception * @throws ExpectationFailedException * @throws GeneratorNotSupportedException @@ -1850,23 +2494,6 @@ function assertStringMatchesFormat(string $format, string $string, string $messa } } -if (!function_exists('PHPUnit\Framework\assertStringNotMatchesFormat')) { - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotMatchesFormat - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 - */ - function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void - { - Assert::assertStringNotMatchesFormat(...func_get_args()); - } -} - if (!function_exists('PHPUnit\Framework\assertStringMatchesFormatFile')) { /** * Asserts that a string matches a given format file. @@ -1883,28 +2510,11 @@ function assertStringMatchesFormatFile(string $formatFile, string $string, strin } } -if (!function_exists('PHPUnit\Framework\assertStringNotMatchesFormatFile')) { - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - * - * @see Assert::assertStringNotMatchesFormatFile - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472 - */ - function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void - { - Assert::assertStringNotMatchesFormatFile(...func_get_args()); - } -} - if (!function_exists('PHPUnit\Framework\assertStringStartsWith')) { /** * Asserts that a string starts with a given prefix. * - * @psalm-param non-empty-string $prefix + * @param non-empty-string $prefix * * @throws ExpectationFailedException * @throws InvalidArgumentException @@ -1923,7 +2533,7 @@ function assertStringStartsWith(string $prefix, string $string, string $message /** * Asserts that a string starts not with a given prefix. * - * @psalm-param non-empty-string $prefix + * @param non-empty-string $prefix * * @throws ExpectationFailedException * @throws InvalidArgumentException @@ -1998,7 +2608,7 @@ function assertStringNotContainsStringIgnoringCase(string $needle, string $hayst /** * Asserts that a string ends with a given suffix. * - * @psalm-param non-empty-string $suffix + * @param non-empty-string $suffix * * @throws ExpectationFailedException * @throws InvalidArgumentException @@ -2017,7 +2627,7 @@ function assertStringEndsWith(string $suffix, string $string, string $message = /** * Asserts that a string ends not with a given suffix. * - * @psalm-param non-empty-string $suffix + * @param non-empty-string $suffix * * @throws ExpectationFailedException * @throws InvalidArgumentException @@ -2264,9 +2874,6 @@ function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFil } if (!function_exists('PHPUnit\Framework\logicalAnd')) { - /** - * @throws Exception - */ function logicalAnd(mixed ...$constraints): LogicalAnd { return Assert::logicalAnd(...func_get_args()); @@ -2308,13 +2915,6 @@ function isTrue(): IsTrue } } -if (!function_exists('PHPUnit\Framework\callback')) { - function callback(callable $callback): Callback - { - return Assert::callback(...func_get_args()); - } -} - if (!function_exists('PHPUnit\Framework\isFalse')) { function isFalse(): IsFalse { @@ -2372,19 +2972,104 @@ function containsIdentical(mixed $value): TraversableContainsIdentical } if (!function_exists('PHPUnit\Framework\containsOnly')) { - /** - * @throws Exception - */ function containsOnly(string $type): TraversableContainsOnly { return Assert::containsOnly(...func_get_args()); } } +if (!function_exists('PHPUnit\Framework\containsOnlyArray')) { + function containsOnlyArray(): TraversableContainsOnly + { + return Assert::containsOnlyArray(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyBool')) { + function containsOnlyBool(): TraversableContainsOnly + { + return Assert::containsOnlyBool(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyCallable')) { + function containsOnlyCallable(): TraversableContainsOnly + { + return Assert::containsOnlyCallable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyFloat')) { + function containsOnlyFloat(): TraversableContainsOnly + { + return Assert::containsOnlyFloat(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyInt')) { + function containsOnlyInt(): TraversableContainsOnly + { + return Assert::containsOnlyInt(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyIterable')) { + function containsOnlyIterable(): TraversableContainsOnly + { + return Assert::containsOnlyIterable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyNull')) { + function containsOnlyNull(): TraversableContainsOnly + { + return Assert::containsOnlyNull(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyNumeric')) { + function containsOnlyNumeric(): TraversableContainsOnly + { + return Assert::containsOnlyNumeric(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyObject')) { + function containsOnlyObject(): TraversableContainsOnly + { + return Assert::containsOnlyObject(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyResource')) { + function containsOnlyResource(): TraversableContainsOnly + { + return Assert::containsOnlyResource(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyClosedResource')) { + function containsOnlyClosedResource(): TraversableContainsOnly + { + return Assert::containsOnlyClosedResource(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyScalar')) { + function containsOnlyScalar(): TraversableContainsOnly + { + return Assert::containsOnlyScalar(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\containsOnlyString')) { + function containsOnlyString(): TraversableContainsOnly + { + return Assert::containsOnlyString(...func_get_args()); + } +} + if (!function_exists('PHPUnit\Framework\containsOnlyInstancesOf')) { - /** - * @throws Exception - */ function containsOnlyInstancesOf(string $className): TraversableContainsOnly { return Assert::containsOnlyInstancesOf(...func_get_args()); @@ -2392,7 +3077,7 @@ function containsOnlyInstancesOf(string $className): TraversableContainsOnly } if (!function_exists('PHPUnit\Framework\arrayHasKey')) { - function arrayHasKey(int|string $key): ArrayHasKey + function arrayHasKey(mixed $key): ArrayHasKey { return Assert::arrayHasKey(...func_get_args()); } @@ -2490,19 +3175,97 @@ function identicalTo(mixed $value): IsIdentical } if (!function_exists('PHPUnit\Framework\isInstanceOf')) { - /** - * @throws UnknownClassOrInterfaceException - */ function isInstanceOf(string $className): IsInstanceOf { return Assert::isInstanceOf(...func_get_args()); } } +if (!function_exists('PHPUnit\Framework\isArray')) { + function isArray(): IsType + { + return Assert::isArray(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isBool')) { + function isBool(): IsType + { + return Assert::isBool(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isCallable')) { + function isCallable(): IsType + { + return Assert::isCallable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isFloat')) { + function isFloat(): IsType + { + return Assert::isFloat(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isInt')) { + function isInt(): IsType + { + return Assert::isInt(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isIterable')) { + function isIterable(): IsType + { + return Assert::isIterable(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isNumeric')) { + function isNumeric(): IsType + { + return Assert::isNumeric(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isObject')) { + function isObject(): IsType + { + return Assert::isObject(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isResource')) { + function isResource(): IsType + { + return Assert::isResource(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isClosedResource')) { + function isClosedResource(): IsType + { + return Assert::isClosedResource(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isScalar')) { + function isScalar(): IsType + { + return Assert::isScalar(...func_get_args()); + } +} + +if (!function_exists('PHPUnit\Framework\isString')) { + function isString(): IsType + { + return Assert::isString(...func_get_args()); + } +} + if (!function_exists('PHPUnit\Framework\isType')) { - /** - * @throws Exception - */ function isType(string $type): IsType { return Assert::isType(...func_get_args()); @@ -2538,9 +3301,6 @@ function matches(string $string): StringMatchesFormatDescription } if (!function_exists('PHPUnit\Framework\stringStartsWith')) { - /** - * @throws InvalidArgumentException - */ function stringStartsWith(string $prefix): StringStartsWith { return Assert::stringStartsWith(...func_get_args()); @@ -2548,9 +3308,6 @@ function stringStartsWith(string $prefix): StringStartsWith } if (!function_exists('PHPUnit\Framework\stringContains')) { - /** - * @throws InvalidArgumentException - */ function stringContains(string $string, bool $case = true): StringContains { return Assert::stringContains(...func_get_args()); @@ -2558,9 +3315,6 @@ function stringContains(string $string, bool $case = true): StringContains } if (!function_exists('PHPUnit\Framework\stringEndsWith')) { - /** - * @throws InvalidArgumentException - */ function stringEndsWith(string $suffix): StringEndsWith { return Assert::stringEndsWith(...func_get_args()); @@ -2588,6 +3342,20 @@ function objectEquals(object $object, string $method = 'equals'): ObjectEquals } } +if (!function_exists('PHPUnit\Framework\callback')) { + /** + * @template CallbackInput of mixed + * + * @param callable(CallbackInput $callback): bool $callback + * + * @return Callback + */ + function callback(callable $callback): Callback + { + return Assert::callback($callback); + } +} + if (!function_exists('PHPUnit\Framework\any')) { /** * Returns a matcher that matches when the method is executed @@ -2664,58 +3432,9 @@ function atMost(int $allowedInvocations): InvokedAtMostCountMatcher } } -if (!function_exists('PHPUnit\Framework\returnValue')) { - function returnValue(mixed $value): ReturnStub - { - return new ReturnStub($value); - } -} - -if (!function_exists('PHPUnit\Framework\returnValueMap')) { - function returnValueMap(array $valueMap): ReturnValueMapStub - { - return new ReturnValueMapStub($valueMap); - } -} - -if (!function_exists('PHPUnit\Framework\returnArgument')) { - function returnArgument(int $argumentIndex): ReturnArgumentStub - { - return new ReturnArgumentStub($argumentIndex); - } -} - -if (!function_exists('PHPUnit\Framework\returnCallback')) { - function returnCallback(callable $callback): ReturnCallbackStub - { - return new ReturnCallbackStub($callback); - } -} - -if (!function_exists('PHPUnit\Framework\returnSelf')) { - /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ - function returnSelf(): ReturnSelfStub - { - return new ReturnSelfStub; - } -} - if (!function_exists('PHPUnit\Framework\throwException')) { function throwException(Throwable $exception): ExceptionStub { return new ExceptionStub($exception); } } - -if (!function_exists('PHPUnit\Framework\onConsecutiveCalls')) { - function onConsecutiveCalls(): ConsecutiveCallsStub - { - $arguments = func_get_args(); - - return new ConsecutiveCallsStub($arguments); - } -} diff --git a/src/Framework/Attributes/After.php b/src/Framework/Attributes/After.php index 6fe0a3c83e1..6d36d29138f 100644 --- a/src/Framework/Attributes/After.php +++ b/src/Framework/Attributes/After.php @@ -12,11 +12,22 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class After +final readonly class After { + private int $priority; + + public function __construct(int $priority = 0) + { + $this->priority = $priority; + } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Framework/Attributes/AfterClass.php b/src/Framework/Attributes/AfterClass.php index 8ef97f2506e..d4a9d6f4c5e 100644 --- a/src/Framework/Attributes/AfterClass.php +++ b/src/Framework/Attributes/AfterClass.php @@ -12,11 +12,22 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class AfterClass +final readonly class AfterClass { + private int $priority; + + public function __construct(int $priority = 0) + { + $this->priority = $priority; + } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Framework/Attributes/BackupGlobals.php b/src/Framework/Attributes/BackupGlobals.php index c212cdbd0e1..f9526ca281f 100644 --- a/src/Framework/Attributes/BackupGlobals.php +++ b/src/Framework/Attributes/BackupGlobals.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Framework/Attributes/BackupStaticProperties.php b/src/Framework/Attributes/BackupStaticProperties.php index 6c80565bedf..e633cf88a95 100644 --- a/src/Framework/Attributes/BackupStaticProperties.php +++ b/src/Framework/Attributes/BackupStaticProperties.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Framework/Attributes/Before.php b/src/Framework/Attributes/Before.php index 39ecf437c0e..c2af00b7fa2 100644 --- a/src/Framework/Attributes/Before.php +++ b/src/Framework/Attributes/Before.php @@ -12,11 +12,22 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class Before +final readonly class Before { + private int $priority; + + public function __construct(int $priority = 0) + { + $this->priority = $priority; + } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Framework/Attributes/BeforeClass.php b/src/Framework/Attributes/BeforeClass.php index 4ea55e0d504..2bb5a07b5f3 100644 --- a/src/Framework/Attributes/BeforeClass.php +++ b/src/Framework/Attributes/BeforeClass.php @@ -12,11 +12,22 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class BeforeClass +final readonly class BeforeClass { + private int $priority; + + public function __construct(int $priority = 0) + { + $this->priority = $priority; + } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Framework/Attributes/CoversClass.php b/src/Framework/Attributes/CoversClass.php index ea2b49b98f5..2cf0b2dd267 100644 --- a/src/Framework/Attributes/CoversClass.php +++ b/src/Framework/Attributes/CoversClass.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class CoversClass { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(string $className) { @@ -33,7 +33,7 @@ public function __construct(string $className) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { diff --git a/src/Framework/Attributes/CoversClassesThatExtendClass.php b/src/Framework/Attributes/CoversClassesThatExtendClass.php new file mode 100644 index 00000000000..486fc5b0a7a --- /dev/null +++ b/src/Framework/Attributes/CoversClassesThatExtendClass.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class CoversClassesThatExtendClass +{ + /** + * @var class-string + */ + private string $className; + + /** + * @param class-string $className + */ + public function __construct(string $className) + { + $this->className = $className; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } +} diff --git a/src/Framework/Attributes/CoversClassesThatImplementInterface.php b/src/Framework/Attributes/CoversClassesThatImplementInterface.php new file mode 100644 index 00000000000..69bcd84603b --- /dev/null +++ b/src/Framework/Attributes/CoversClassesThatImplementInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class CoversClassesThatImplementInterface +{ + /** + * @var class-string + */ + private string $interfaceName; + + /** + * @param class-string $interfaceName + */ + public function __construct(string $interfaceName) + { + $this->interfaceName = $interfaceName; + } + + /** + * @return class-string + */ + public function interfaceName(): string + { + return $this->interfaceName; + } +} diff --git a/src/Framework/Attributes/CoversFunction.php b/src/Framework/Attributes/CoversFunction.php index 1a68f00a021..3b58b1914d0 100644 --- a/src/Framework/Attributes/CoversFunction.php +++ b/src/Framework/Attributes/CoversFunction.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class CoversFunction { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $functionName; /** - * @psalm-param non-empty-string $functionName + * @param non-empty-string $functionName */ public function __construct(string $functionName) { @@ -33,7 +33,7 @@ public function __construct(string $functionName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function functionName(): string { diff --git a/src/Framework/Attributes/CoversMethod.php b/src/Framework/Attributes/CoversMethod.php new file mode 100644 index 00000000000..4181239b9d8 --- /dev/null +++ b/src/Framework/Attributes/CoversMethod.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class CoversMethod +{ + /** + * @var class-string + */ + private string $className; + + /** + * @var non-empty-string + */ + private string $methodName; + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + public function __construct(string $className, string $methodName) + { + $this->className = $className; + $this->methodName = $methodName; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } +} diff --git a/src/Framework/Attributes/CoversNamespace.php b/src/Framework/Attributes/CoversNamespace.php new file mode 100644 index 00000000000..50d82b99473 --- /dev/null +++ b/src/Framework/Attributes/CoversNamespace.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class CoversNamespace +{ + /** + * @var non-empty-string + */ + private string $namespace; + + /** + * @param non-empty-string $namespace + */ + public function __construct(string $namespace) + { + $this->namespace = $namespace; + } + + /** + * @return non-empty-string + */ + public function namespace(): string + { + return $this->namespace; + } +} diff --git a/src/Framework/Attributes/CoversNothing.php b/src/Framework/Attributes/CoversNothing.php index 8ac6a2ed1f8..33ce31f30f1 100644 --- a/src/Framework/Attributes/CoversNothing.php +++ b/src/Framework/Attributes/CoversNothing.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] -final class CoversNothing +final readonly class CoversNothing { } diff --git a/src/Framework/Attributes/CoversTrait.php b/src/Framework/Attributes/CoversTrait.php new file mode 100644 index 00000000000..89927855053 --- /dev/null +++ b/src/Framework/Attributes/CoversTrait.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class CoversTrait +{ + /** + * @var trait-string + */ + private string $traitName; + + /** + * @param trait-string $traitName + */ + public function __construct(string $traitName) + { + $this->traitName = $traitName; + } + + /** + * @return trait-string + */ + public function traitName(): string + { + return $this->traitName; + } +} diff --git a/src/Framework/Attributes/DataProvider.php b/src/Framework/Attributes/DataProvider.php index d7ade647145..0e95c502094 100644 --- a/src/Framework/Attributes/DataProvider.php +++ b/src/Framework/Attributes/DataProvider.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class DataProvider { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param non-empty-string $methodName + * @param non-empty-string $methodName */ public function __construct(string $methodName) { @@ -33,7 +33,7 @@ public function __construct(string $methodName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DataProviderExternal.php b/src/Framework/Attributes/DataProviderExternal.php index d59d85fed3e..9d05cef27b0 100644 --- a/src/Framework/Attributes/DataProviderExternal.php +++ b/src/Framework/Attributes/DataProviderExternal.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class DataProviderExternal { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { @@ -40,7 +40,7 @@ public function __construct(string $className, string $methodName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -48,7 +48,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/Depends.php b/src/Framework/Attributes/Depends.php index 124566aac87..1375ae5bdaa 100644 --- a/src/Framework/Attributes/Depends.php +++ b/src/Framework/Attributes/Depends.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class Depends { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param non-empty-string $methodName + * @param non-empty-string $methodName */ public function __construct(string $methodName) { @@ -33,7 +33,7 @@ public function __construct(string $methodName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DependsExternal.php b/src/Framework/Attributes/DependsExternal.php index 31f62b44885..c2f9e39f0b4 100644 --- a/src/Framework/Attributes/DependsExternal.php +++ b/src/Framework/Attributes/DependsExternal.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class DependsExternal { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { @@ -40,7 +40,7 @@ public function __construct(string $className, string $methodName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -48,7 +48,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DependsExternalUsingDeepClone.php b/src/Framework/Attributes/DependsExternalUsingDeepClone.php index 4519ccb4fb0..d71452b8837 100644 --- a/src/Framework/Attributes/DependsExternalUsingDeepClone.php +++ b/src/Framework/Attributes/DependsExternalUsingDeepClone.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class DependsExternalUsingDeepClone { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { @@ -40,7 +40,7 @@ public function __construct(string $className, string $methodName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -48,7 +48,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DependsExternalUsingShallowClone.php b/src/Framework/Attributes/DependsExternalUsingShallowClone.php index 22b2539d8d8..9fb8fbf7d8b 100644 --- a/src/Framework/Attributes/DependsExternalUsingShallowClone.php +++ b/src/Framework/Attributes/DependsExternalUsingShallowClone.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class DependsExternalUsingShallowClone { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { @@ -40,7 +40,7 @@ public function __construct(string $className, string $methodName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -48,7 +48,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DependsOnClass.php b/src/Framework/Attributes/DependsOnClass.php index e179d18c97e..14465c77e68 100644 --- a/src/Framework/Attributes/DependsOnClass.php +++ b/src/Framework/Attributes/DependsOnClass.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class DependsOnClass { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(string $className) { @@ -33,7 +33,7 @@ public function __construct(string $className) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { diff --git a/src/Framework/Attributes/DependsOnClassUsingDeepClone.php b/src/Framework/Attributes/DependsOnClassUsingDeepClone.php index b737b0ad77b..dc46c39df58 100644 --- a/src/Framework/Attributes/DependsOnClassUsingDeepClone.php +++ b/src/Framework/Attributes/DependsOnClassUsingDeepClone.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class DependsOnClassUsingDeepClone { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(string $className) { @@ -33,7 +33,7 @@ public function __construct(string $className) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { diff --git a/src/Framework/Attributes/DependsOnClassUsingShallowClone.php b/src/Framework/Attributes/DependsOnClassUsingShallowClone.php index 91e88ee6928..5201f045d85 100644 --- a/src/Framework/Attributes/DependsOnClassUsingShallowClone.php +++ b/src/Framework/Attributes/DependsOnClassUsingShallowClone.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class DependsOnClassUsingShallowClone { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(string $className) { @@ -33,7 +33,7 @@ public function __construct(string $className) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { diff --git a/src/Framework/Attributes/DependsUsingDeepClone.php b/src/Framework/Attributes/DependsUsingDeepClone.php index 8879ecdee3e..173188ec6e0 100644 --- a/src/Framework/Attributes/DependsUsingDeepClone.php +++ b/src/Framework/Attributes/DependsUsingDeepClone.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class DependsUsingDeepClone { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param non-empty-string $methodName + * @param non-empty-string $methodName */ public function __construct(string $methodName) { @@ -33,7 +33,7 @@ public function __construct(string $methodName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DependsUsingShallowClone.php b/src/Framework/Attributes/DependsUsingShallowClone.php index c844066a528..8aff52a3a2c 100644 --- a/src/Framework/Attributes/DependsUsingShallowClone.php +++ b/src/Framework/Attributes/DependsUsingShallowClone.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class DependsUsingShallowClone { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param non-empty-string $methodName + * @param non-empty-string $methodName */ public function __construct(string $methodName) { @@ -33,7 +33,7 @@ public function __construct(string $methodName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/DisableReturnValueGenerationForTestDoubles.php b/src/Framework/Attributes/DisableReturnValueGenerationForTestDoubles.php new file mode 100644 index 00000000000..2709f569e75 --- /dev/null +++ b/src/Framework/Attributes/DisableReturnValueGenerationForTestDoubles.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS)] +final readonly class DisableReturnValueGenerationForTestDoubles +{ +} diff --git a/src/Framework/Attributes/DoesNotPerformAssertions.php b/src/Framework/Attributes/DoesNotPerformAssertions.php index 3d6045a227c..f193e5af359 100644 --- a/src/Framework/Attributes/DoesNotPerformAssertions.php +++ b/src/Framework/Attributes/DoesNotPerformAssertions.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] -final class DoesNotPerformAssertions +final readonly class DoesNotPerformAssertions { } diff --git a/src/Framework/Attributes/ExcludeGlobalVariableFromBackup.php b/src/Framework/Attributes/ExcludeGlobalVariableFromBackup.php index 4ad5b5ebc89..5d1ac72c83e 100644 --- a/src/Framework/Attributes/ExcludeGlobalVariableFromBackup.php +++ b/src/Framework/Attributes/ExcludeGlobalVariableFromBackup.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class ExcludeGlobalVariableFromBackup { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $globalVariableName; /** - * @psalm-param non-empty-string $globalVariableName + * @param non-empty-string $globalVariableName */ public function __construct(string $globalVariableName) { @@ -33,7 +33,7 @@ public function __construct(string $globalVariableName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function globalVariableName(): string { diff --git a/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php b/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php index d516f1a9f4a..ea572549185 100644 --- a/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php +++ b/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class ExcludeStaticPropertyFromBackup { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $propertyName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $propertyName + * @param class-string $className + * @param non-empty-string $propertyName */ public function __construct(string $className, string $propertyName) { @@ -40,7 +40,7 @@ public function __construct(string $className, string $propertyName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -48,7 +48,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function propertyName(): string { diff --git a/src/Framework/Attributes/Group.php b/src/Framework/Attributes/Group.php index d1127384344..5a6942bb802 100644 --- a/src/Framework/Attributes/Group.php +++ b/src/Framework/Attributes/Group.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class Group { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $name; /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name */ public function __construct(string $name) { @@ -33,7 +33,7 @@ public function __construct(string $name) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function name(): string { diff --git a/src/Framework/Attributes/IgnoreDeprecations.php b/src/Framework/Attributes/IgnoreDeprecations.php index cf48aac1eaa..6cde66b0975 100644 --- a/src/Framework/Attributes/IgnoreDeprecations.php +++ b/src/Framework/Attributes/IgnoreDeprecations.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] -final class IgnoreDeprecations +final readonly class IgnoreDeprecations { } diff --git a/src/Framework/Attributes/IgnorePhpunitDeprecations.php b/src/Framework/Attributes/IgnorePhpunitDeprecations.php index 35333cbb84d..1cebec04dce 100644 --- a/src/Framework/Attributes/IgnorePhpunitDeprecations.php +++ b/src/Framework/Attributes/IgnorePhpunitDeprecations.php @@ -12,11 +12,13 @@ use Attribute; /** - * @psalm-immutable + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] -final class IgnorePhpunitDeprecations +final readonly class IgnorePhpunitDeprecations { } diff --git a/src/Framework/Attributes/Large.php b/src/Framework/Attributes/Large.php index 6d46c1b441d..a751a934361 100644 --- a/src/Framework/Attributes/Large.php +++ b/src/Framework/Attributes/Large.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS)] -final class Large +final readonly class Large { } diff --git a/src/Framework/Attributes/Medium.php b/src/Framework/Attributes/Medium.php index 40e9c451831..debc4b0d319 100644 --- a/src/Framework/Attributes/Medium.php +++ b/src/Framework/Attributes/Medium.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS)] -final class Medium +final readonly class Medium { } diff --git a/src/Framework/Attributes/PostCondition.php b/src/Framework/Attributes/PostCondition.php index 461aab42db4..8eb40fe03d6 100644 --- a/src/Framework/Attributes/PostCondition.php +++ b/src/Framework/Attributes/PostCondition.php @@ -12,11 +12,22 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class PostCondition +final readonly class PostCondition { + private int $priority; + + public function __construct(int $priority = 0) + { + $this->priority = $priority; + } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Framework/Attributes/PreCondition.php b/src/Framework/Attributes/PreCondition.php index 120ed6c19b2..5f47fc599a0 100644 --- a/src/Framework/Attributes/PreCondition.php +++ b/src/Framework/Attributes/PreCondition.php @@ -12,11 +12,22 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class PreCondition +final readonly class PreCondition { + private int $priority; + + public function __construct(int $priority = 0) + { + $this->priority = $priority; + } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Framework/Attributes/PreserveGlobalState.php b/src/Framework/Attributes/PreserveGlobalState.php index 3a1ae5f04a4..fcd9c637246 100644 --- a/src/Framework/Attributes/PreserveGlobalState.php +++ b/src/Framework/Attributes/PreserveGlobalState.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ diff --git a/src/Framework/Attributes/RequiresEnvironmentVariable.php b/src/Framework/Attributes/RequiresEnvironmentVariable.php new file mode 100644 index 00000000000..7e460b99afb --- /dev/null +++ b/src/Framework/Attributes/RequiresEnvironmentVariable.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final readonly class RequiresEnvironmentVariable +{ + private string $environmentVariableName; + private null|string $value; + + public function __construct(string $environmentVariableName, null|string $value = null) + { + $this->environmentVariableName = $environmentVariableName; + $this->value = $value; + } + + public function environmentVariableName(): string + { + return $this->environmentVariableName; + } + + public function value(): null|string + { + return $this->value; + } +} diff --git a/src/Framework/Attributes/RequiresFunction.php b/src/Framework/Attributes/RequiresFunction.php index 710d3c77161..e358bdf116b 100644 --- a/src/Framework/Attributes/RequiresFunction.php +++ b/src/Framework/Attributes/RequiresFunction.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class RequiresFunction { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $functionName; /** - * @psalm-param non-empty-string $functionName + * @param non-empty-string $functionName */ public function __construct(string $functionName) { @@ -33,7 +33,7 @@ public function __construct(string $functionName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function functionName(): string { diff --git a/src/Framework/Attributes/RequiresMethod.php b/src/Framework/Attributes/RequiresMethod.php index 34ffec11704..713c1ee91a2 100644 --- a/src/Framework/Attributes/RequiresMethod.php +++ b/src/Framework/Attributes/RequiresMethod.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class RequiresMethod { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { @@ -40,7 +40,7 @@ public function __construct(string $className, string $methodName) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -48,7 +48,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Framework/Attributes/RequiresOperatingSystem.php b/src/Framework/Attributes/RequiresOperatingSystem.php index 42c3bac8e37..066d7d3c7c6 100644 --- a/src/Framework/Attributes/RequiresOperatingSystem.php +++ b/src/Framework/Attributes/RequiresOperatingSystem.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class RequiresOperatingSystem { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $regularExpression; /** - * @psalm-param non-empty-string $regularExpression + * @param non-empty-string $regularExpression */ public function __construct(string $regularExpression) { @@ -33,7 +33,7 @@ public function __construct(string $regularExpression) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function regularExpression(): string { diff --git a/src/Framework/Attributes/RequiresOperatingSystemFamily.php b/src/Framework/Attributes/RequiresOperatingSystemFamily.php index 824d930a70b..088ba85cea6 100644 --- a/src/Framework/Attributes/RequiresOperatingSystemFamily.php +++ b/src/Framework/Attributes/RequiresOperatingSystemFamily.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class RequiresOperatingSystemFamily { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $operatingSystemFamily; /** - * @psalm-param non-empty-string $operatingSystemFamily + * @param non-empty-string $operatingSystemFamily */ public function __construct(string $operatingSystemFamily) { @@ -33,7 +33,7 @@ public function __construct(string $operatingSystemFamily) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function operatingSystemFamily(): string { diff --git a/src/Framework/Attributes/RequiresPhp.php b/src/Framework/Attributes/RequiresPhp.php index 01e3a6c2a50..94fbe51b88f 100644 --- a/src/Framework/Attributes/RequiresPhp.php +++ b/src/Framework/Attributes/RequiresPhp.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class RequiresPhp { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $versionRequirement; /** - * @psalm-param non-empty-string $versionRequirement + * @param non-empty-string $versionRequirement */ public function __construct(string $versionRequirement) { @@ -33,7 +33,7 @@ public function __construct(string $versionRequirement) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function versionRequirement(): string { diff --git a/src/Framework/Attributes/RequiresPhpExtension.php b/src/Framework/Attributes/RequiresPhpExtension.php index 01a5d5769ca..61a81ec4781 100644 --- a/src/Framework/Attributes/RequiresPhpExtension.php +++ b/src/Framework/Attributes/RequiresPhpExtension.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class RequiresPhpExtension { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $extension; /** - * @psalm-var null|non-empty-string + * @var null|non-empty-string */ private ?string $versionRequirement; /** - * @psalm-param non-empty-string $extension - * @psalm-param null|non-empty-string $versionRequirement + * @param non-empty-string $extension + * @param null|non-empty-string $versionRequirement */ public function __construct(string $extension, ?string $versionRequirement = null) { @@ -40,7 +40,7 @@ public function __construct(string $extension, ?string $versionRequirement = nul } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function extension(): string { @@ -48,7 +48,7 @@ public function extension(): string } /** - * @psalm-return null|non-empty-string + * @return null|non-empty-string */ public function versionRequirement(): ?string { diff --git a/src/Framework/Attributes/RequiresPhpunit.php b/src/Framework/Attributes/RequiresPhpunit.php index 91eccf727e2..8b26405b759 100644 --- a/src/Framework/Attributes/RequiresPhpunit.php +++ b/src/Framework/Attributes/RequiresPhpunit.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class RequiresPhpunit { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $versionRequirement; /** - * @psalm-param non-empty-string $versionRequirement + * @param non-empty-string $versionRequirement */ public function __construct(string $versionRequirement) { @@ -33,7 +33,7 @@ public function __construct(string $versionRequirement) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function versionRequirement(): string { diff --git a/src/Framework/Attributes/RequiresPhpunitExtension.php b/src/Framework/Attributes/RequiresPhpunitExtension.php new file mode 100644 index 00000000000..9b6a164f248 --- /dev/null +++ b/src/Framework/Attributes/RequiresPhpunitExtension.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; +use PHPUnit\Runner\Extension\Extension; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final readonly class RequiresPhpunitExtension +{ + /** + * @var class-string + */ + private string $extensionClass; + + /** + * @param class-string $extensionClass + */ + public function __construct(string $extensionClass) + { + $this->extensionClass = $extensionClass; + } + + /** + * @return class-string + */ + public function extensionClass(): string + { + return $this->extensionClass; + } +} diff --git a/src/Framework/Attributes/RequiresSetting.php b/src/Framework/Attributes/RequiresSetting.php index cc5f0c6b095..86828dd0f01 100644 --- a/src/Framework/Attributes/RequiresSetting.php +++ b/src/Framework/Attributes/RequiresSetting.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,18 +20,18 @@ final readonly class RequiresSetting { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $setting; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $value; /** - * @psalm-param non-empty-string $setting - * @psalm-param non-empty-string $value + * @param non-empty-string $setting + * @param non-empty-string $value */ public function __construct(string $setting, string $value) { @@ -40,7 +40,7 @@ public function __construct(string $setting, string $value) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function setting(): string { @@ -48,7 +48,7 @@ public function setting(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function value(): string { diff --git a/src/Framework/Attributes/RunClassInSeparateProcess.php b/src/Framework/Attributes/RunClassInSeparateProcess.php index 621774fb48e..749ea3d9bd2 100644 --- a/src/Framework/Attributes/RunClassInSeparateProcess.php +++ b/src/Framework/Attributes/RunClassInSeparateProcess.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS)] -final class RunClassInSeparateProcess +final readonly class RunClassInSeparateProcess { } diff --git a/src/Framework/Attributes/RunInSeparateProcess.php b/src/Framework/Attributes/RunInSeparateProcess.php index dd0066bfa67..740d6f38746 100644 --- a/src/Framework/Attributes/RunInSeparateProcess.php +++ b/src/Framework/Attributes/RunInSeparateProcess.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class RunInSeparateProcess +final readonly class RunInSeparateProcess { } diff --git a/src/Framework/Attributes/RunTestsInSeparateProcesses.php b/src/Framework/Attributes/RunTestsInSeparateProcesses.php index b823ccb132f..0f8d4320012 100644 --- a/src/Framework/Attributes/RunTestsInSeparateProcesses.php +++ b/src/Framework/Attributes/RunTestsInSeparateProcesses.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS)] -final class RunTestsInSeparateProcesses +final readonly class RunTestsInSeparateProcesses { } diff --git a/src/Framework/Attributes/Small.php b/src/Framework/Attributes/Small.php index 598b6540b53..5ef284ab1ff 100644 --- a/src/Framework/Attributes/Small.php +++ b/src/Framework/Attributes/Small.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_CLASS)] -final class Small +final readonly class Small { } diff --git a/src/Framework/Attributes/Test.php b/src/Framework/Attributes/Test.php index 711d58d2406..a15f7f55770 100644 --- a/src/Framework/Attributes/Test.php +++ b/src/Framework/Attributes/Test.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class Test +final readonly class Test { } diff --git a/src/Framework/Attributes/TestDox.php b/src/Framework/Attributes/TestDox.php index 6a3576fe6fe..b0478506805 100644 --- a/src/Framework/Attributes/TestDox.php +++ b/src/Framework/Attributes/TestDox.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class TestDox { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $text; /** - * @psalm-param non-empty-string $text + * @param non-empty-string $text */ public function __construct(string $text) { @@ -33,7 +33,7 @@ public function __construct(string $text) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function text(): string { diff --git a/src/Framework/Attributes/TestWith.php b/src/Framework/Attributes/TestWith.php index a71af7fe335..b5c33a43e62 100644 --- a/src/Framework/Attributes/TestWith.php +++ b/src/Framework/Attributes/TestWith.php @@ -12,22 +12,46 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] final readonly class TestWith { + /** + * @var array + */ private array $data; - public function __construct(array $data) + /** + * @var ?non-empty-string + */ + private ?string $name; + + /** + * @param array $data + * @param ?non-empty-string $name + */ + public function __construct(array $data, ?string $name = null) { $this->data = $data; + $this->name = $name; } + /** + * @return array + */ public function data(): array { return $this->data; } + + /** + * @return ?non-empty-string + */ + public function name(): ?string + { + return $this->name; + } } diff --git a/src/Framework/Attributes/TestWithJson.php b/src/Framework/Attributes/TestWithJson.php index b23632f715e..bfe9c09226b 100644 --- a/src/Framework/Attributes/TestWithJson.php +++ b/src/Framework/Attributes/TestWithJson.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,23 +20,38 @@ final readonly class TestWithJson { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $json; /** - * @psalm-param non-empty-string $json + * @var ?non-empty-string */ - public function __construct(string $json) + private ?string $name; + + /** + * @param non-empty-string $json + * @param ?non-empty-string $name + */ + public function __construct(string $json, ?string $name = null) { $this->json = $json; + $this->name = $name; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function json(): string { return $this->json; } + + /** + * @return ?non-empty-string + */ + public function name(): ?string + { + return $this->name; + } } diff --git a/src/Framework/Attributes/Ticket.php b/src/Framework/Attributes/Ticket.php index b55f88e52b8..e463247e2ef 100644 --- a/src/Framework/Attributes/Ticket.php +++ b/src/Framework/Attributes/Ticket.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class Ticket { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $text; /** - * @psalm-param non-empty-string $text + * @param non-empty-string $text */ public function __construct(string $text) { @@ -33,7 +33,7 @@ public function __construct(string $text) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function text(): string { diff --git a/src/Framework/Attributes/UsesClass.php b/src/Framework/Attributes/UsesClass.php index 97135f5384c..f7078bce671 100644 --- a/src/Framework/Attributes/UsesClass.php +++ b/src/Framework/Attributes/UsesClass.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class UsesClass { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-param class-string $className + * @param class-string $className */ public function __construct(string $className) { @@ -33,7 +33,7 @@ public function __construct(string $className) } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { diff --git a/src/Framework/Attributes/UsesClassesThatExtendClass.php b/src/Framework/Attributes/UsesClassesThatExtendClass.php new file mode 100644 index 00000000000..d1aa73faa88 --- /dev/null +++ b/src/Framework/Attributes/UsesClassesThatExtendClass.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class UsesClassesThatExtendClass +{ + /** + * @var class-string + */ + private string $className; + + /** + * @param class-string $className + */ + public function __construct(string $className) + { + $this->className = $className; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } +} diff --git a/src/Framework/Attributes/UsesClassesThatImplementInterface.php b/src/Framework/Attributes/UsesClassesThatImplementInterface.php new file mode 100644 index 00000000000..0f2241c8675 --- /dev/null +++ b/src/Framework/Attributes/UsesClassesThatImplementInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class UsesClassesThatImplementInterface +{ + /** + * @var class-string + */ + private string $interfaceName; + + /** + * @param class-string $interfaceName + */ + public function __construct(string $interfaceName) + { + $this->interfaceName = $interfaceName; + } + + /** + * @return class-string + */ + public function interfaceName(): string + { + return $this->interfaceName; + } +} diff --git a/src/Framework/Attributes/UsesFunction.php b/src/Framework/Attributes/UsesFunction.php index 8df45fd9d13..12cc408b982 100644 --- a/src/Framework/Attributes/UsesFunction.php +++ b/src/Framework/Attributes/UsesFunction.php @@ -12,7 +12,7 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -20,12 +20,12 @@ final readonly class UsesFunction { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $functionName; /** - * @psalm-param non-empty-string $functionName + * @param non-empty-string $functionName */ public function __construct(string $functionName) { @@ -33,7 +33,7 @@ public function __construct(string $functionName) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function functionName(): string { diff --git a/src/Framework/Attributes/UsesMethod.php b/src/Framework/Attributes/UsesMethod.php new file mode 100644 index 00000000000..b1ee19b54d9 --- /dev/null +++ b/src/Framework/Attributes/UsesMethod.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class UsesMethod +{ + /** + * @var class-string + */ + private string $className; + + /** + * @var non-empty-string + */ + private string $methodName; + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + public function __construct(string $className, string $methodName) + { + $this->className = $className; + $this->methodName = $methodName; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } +} diff --git a/src/Framework/Attributes/UsesNamespace.php b/src/Framework/Attributes/UsesNamespace.php new file mode 100644 index 00000000000..ad929cdb128 --- /dev/null +++ b/src/Framework/Attributes/UsesNamespace.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class UsesNamespace +{ + /** + * @var non-empty-string + */ + private string $namespace; + + /** + * @param non-empty-string $namespace + */ + public function __construct(string $namespace) + { + $this->namespace = $namespace; + } + + /** + * @return non-empty-string + */ + public function namespace(): string + { + return $this->namespace; + } +} diff --git a/src/Framework/Attributes/UsesTrait.php b/src/Framework/Attributes/UsesTrait.php new file mode 100644 index 00000000000..469e79c6866 --- /dev/null +++ b/src/Framework/Attributes/UsesTrait.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +final readonly class UsesTrait +{ + /** + * @var trait-string + */ + private string $traitName; + + /** + * @param trait-string $traitName + */ + public function __construct(string $traitName) + { + $this->traitName = $traitName; + } + + /** + * @return trait-string + */ + public function traitName(): string + { + return $this->traitName; + } +} diff --git a/src/Framework/Attributes/WithEnvironmentVariable.php b/src/Framework/Attributes/WithEnvironmentVariable.php new file mode 100644 index 00000000000..6d8e2d305ad --- /dev/null +++ b/src/Framework/Attributes/WithEnvironmentVariable.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Attributes; + +use Attribute; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +final readonly class WithEnvironmentVariable +{ + /** + * @var non-empty-string + */ + private string $environmentVariableName; + private null|string $value; + + /** + * @param non-empty-string $environmentVariableName + */ + public function __construct(string $environmentVariableName, null|string $value = null) + { + $this->environmentVariableName = $environmentVariableName; + $this->value = $value; + } + + /** + * @return non-empty-string + */ + public function environmentVariableName(): string + { + return $this->environmentVariableName; + } + + public function value(): null|string + { + return $this->value; + } +} diff --git a/src/Framework/Attributes/WithoutErrorHandler.php b/src/Framework/Attributes/WithoutErrorHandler.php index 2dce1e98d15..a10f0fc2f16 100644 --- a/src/Framework/Attributes/WithoutErrorHandler.php +++ b/src/Framework/Attributes/WithoutErrorHandler.php @@ -12,11 +12,11 @@ use Attribute; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ #[Attribute(Attribute::TARGET_METHOD)] -final class WithoutErrorHandler +final readonly class WithoutErrorHandler { } diff --git a/src/Framework/Constraint/Callback.php b/src/Framework/Constraint/Callback.php index 665ed5432c9..753213d6edd 100644 --- a/src/Framework/Constraint/Callback.php +++ b/src/Framework/Constraint/Callback.php @@ -9,20 +9,23 @@ */ namespace PHPUnit\Framework\Constraint; +use Closure; +use ReflectionFunction; + /** - * @psalm-template CallbackInput of mixed + * @template CallbackInput of mixed * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final class Callback extends Constraint { /** - * @psalm-var callable(CallbackInput $input): bool + * @var callable(CallbackInput): bool */ private readonly mixed $callback; /** - * @psalm-param callable(CallbackInput $input): bool $callback + * @param callable(CallbackInput $input): bool $callback */ public function __construct(callable $callback) { @@ -37,14 +40,29 @@ public function toString(): string return 'is accepted by specified callback'; } + public function isVariadic(): bool + { + foreach ((new ReflectionFunction(Closure::fromCallable($this->callback)))->getParameters() as $parameter) { + if ($parameter->isVariadic()) { + return true; + } + } + + return false; + } + /** * Evaluates the constraint for parameter $value. Returns true if the * constraint is met, false otherwise. * - * @psalm-param CallbackInput $other + * @param CallbackInput $other */ protected function matches(mixed $other): bool { + if ($this->isVariadic()) { + return ($this->callback)(...$other); + } + return ($this->callback)($other); } } diff --git a/src/Framework/Constraint/Cardinality/GreaterThan.php b/src/Framework/Constraint/Cardinality/GreaterThan.php index 7e8f0ee64b2..7b252dd9c86 100644 --- a/src/Framework/Constraint/Cardinality/GreaterThan.php +++ b/src/Framework/Constraint/Cardinality/GreaterThan.php @@ -26,9 +26,9 @@ public function __construct(mixed $value) /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { - return 'is greater than ' . Exporter::export($this->value, $exportObjects); + return 'is greater than ' . Exporter::export($this->value); } /** diff --git a/src/Framework/Constraint/Cardinality/IsEmpty.php b/src/Framework/Constraint/Cardinality/IsEmpty.php index ddf4a9f7a5c..31942d9f000 100644 --- a/src/Framework/Constraint/Cardinality/IsEmpty.php +++ b/src/Framework/Constraint/Cardinality/IsEmpty.php @@ -43,6 +43,7 @@ protected function matches(mixed $other): bool return count($other) === 0; } + /** @phpstan-ignore empty.notAllowed */ return empty($other); } @@ -60,7 +61,7 @@ protected function failureDescription(mixed $other): string '%s %s %s', str_starts_with($type, 'a') || str_starts_with($type, 'o') ? 'an' : 'a', $type, - $this->toString(true), + $this->toString(), ); } } diff --git a/src/Framework/Constraint/Cardinality/LessThan.php b/src/Framework/Constraint/Cardinality/LessThan.php index cc3ec344029..122dd7347eb 100644 --- a/src/Framework/Constraint/Cardinality/LessThan.php +++ b/src/Framework/Constraint/Cardinality/LessThan.php @@ -26,9 +26,9 @@ public function __construct(mixed $value) /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { - return 'is less than ' . Exporter::export($this->value, $exportObjects); + return 'is less than ' . Exporter::export($this->value); } /** diff --git a/src/Framework/Constraint/Cardinality/SameSize.php b/src/Framework/Constraint/Cardinality/SameSize.php index e4e9f3c9efc..a2417a28d78 100644 --- a/src/Framework/Constraint/Cardinality/SameSize.php +++ b/src/Framework/Constraint/Cardinality/SameSize.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Framework\Constraint; +use Countable; use PHPUnit\Framework\Exception; /** @@ -17,11 +18,11 @@ final class SameSize extends Count { /** - * @psalm-param \Countable|iterable $expected + * @param Countable|iterable $expected * * @throws Exception */ - public function __construct($expected) + public function __construct(Countable|iterable $expected) { parent::__construct((int) $this->getCountOf($expected)); } diff --git a/src/Framework/Constraint/Constraint.php b/src/Framework/Constraint/Constraint.php index 40ffb820568..3a3483e3f0b 100644 --- a/src/Framework/Constraint/Constraint.php +++ b/src/Framework/Constraint/Constraint.php @@ -9,13 +9,20 @@ */ namespace PHPUnit\Framework\Constraint; +use function assert; use function gettype; +use function is_int; +use function is_object; use function sprintf; +use function str_replace; +use function strpos; use function strtolower; +use function substr; use Countable; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\SelfDescribing; use PHPUnit\Util\Exporter; +use ReflectionObject; use SebastianBergmann\Comparator\ComparisonFailure; /** @@ -62,14 +69,6 @@ public function count(): int return 1; } - /** - * @deprecated - */ - protected function exporter(): \SebastianBergmann\Exporter\Exporter - { - return new \SebastianBergmann\Exporter\Exporter; - } - /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. @@ -86,7 +85,7 @@ protected function matches(mixed $other): bool * * @throws ExpectationFailedException */ - protected function fail(mixed $other, string $description, ComparisonFailure $comparisonFailure = null): never + protected function fail(mixed $other, string $description, ?ComparisonFailure $comparisonFailure = null): never { $failureDescription = sprintf( 'Failed asserting that %s.', @@ -95,11 +94,11 @@ protected function fail(mixed $other, string $description, ComparisonFailure $co $additionalFailureDescription = $this->additionalFailureDescription($other); - if ($additionalFailureDescription) { + if ($additionalFailureDescription !== '') { $failureDescription .= "\n" . $additionalFailureDescription; } - if (!empty($description)) { + if ($description !== '') { $failureDescription = $description . "\n" . $failureDescription; } @@ -131,7 +130,7 @@ protected function additionalFailureDescription(mixed $other): string */ protected function failureDescription(mixed $other): string { - return Exporter::export($other, true) . ' ' . $this->toString(true); + return Exporter::export($other) . ' ' . $this->toString(); } /** @@ -171,7 +170,7 @@ protected function failureDescriptionInContext(Operator $operator, mixed $role, return ''; } - return Exporter::export($other, true) . ' ' . $string; + return Exporter::export($other) . ' ' . $string; } /** @@ -240,10 +239,28 @@ protected function reduce(): self } /** - * @psalm-return non-empty-string + * @return non-empty-string */ protected function valueToTypeStringFragment(mixed $value): string { + if (is_object($value)) { + $reflector = new ReflectionObject($value); + + if ($reflector->isAnonymous()) { + $name = str_replace('class@anonymous', '', $reflector->getName()); + + $length = strpos($name, '$'); + + assert(is_int($length)); + + $name = substr($name, 0, $length); + + return 'an instance of anonymous class created at ' . $name . ' '; + } + + return 'an instance of class ' . $reflector->getName() . ' '; + } + $type = strtolower(gettype($value)); if ($type === 'double') { @@ -255,7 +272,7 @@ protected function valueToTypeStringFragment(mixed $value): string } return match ($type) { - 'array', 'integer', 'object' => 'an ' . $type . ' ', + 'array', 'integer' => 'an ' . $type . ' ', 'boolean', 'closed resource', 'float', 'resource', 'string' => 'a ' . $type . ' ', 'null' => 'null ', default => 'a value of ' . $type . ' ', diff --git a/src/Framework/Constraint/Equality/IsEqual.php b/src/Framework/Constraint/Equality/IsEqual.php index 4415e36e9fd..2051bfce3e7 100644 --- a/src/Framework/Constraint/Equality/IsEqual.php +++ b/src/Framework/Constraint/Equality/IsEqual.php @@ -42,7 +42,7 @@ public function __construct(mixed $value) * * @throws ExpectationFailedException */ - public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool + public function evaluate(mixed $other, string $description = '', bool $returnResult = false): bool { // If $this->value and $other are identical, they are also equal. // This is the most common path and will allow us to skip @@ -80,7 +80,7 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { $delta = ''; @@ -97,7 +97,7 @@ public function toString(bool $exportObjects = false): string return sprintf( 'is equal to %s%s', - Exporter::export($this->value, $exportObjects), + Exporter::export($this->value), $delta, ); } diff --git a/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php b/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php index 109438ecebc..b826464db5c 100644 --- a/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php +++ b/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php @@ -42,7 +42,7 @@ public function __construct(mixed $value) * * @throws ExpectationFailedException */ - public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool + public function evaluate(mixed $other, string $description = '', bool $returnResult = false): bool { // If $this->value and $other are identical, they are also equal. // This is the most common path and will allow us to skip @@ -64,7 +64,6 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes $other, 0.0, true, - false, ); } catch (ComparisonFailure $f) { if ($returnResult) { @@ -83,7 +82,7 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { if (is_string($this->value)) { if (str_contains($this->value, "\n")) { @@ -98,7 +97,7 @@ public function toString(bool $exportObjects = false): string return sprintf( 'is equal to %s', - Exporter::export($this->value, $exportObjects), + Exporter::export($this->value), ); } } diff --git a/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php b/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php index f5b9b99d279..c7b2c31da54 100644 --- a/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php +++ b/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php @@ -42,7 +42,7 @@ public function __construct(mixed $value) * * @throws ExpectationFailedException */ - public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool + public function evaluate(mixed $other, string $description = '', bool $returnResult = false): bool { // If $this->value and $other are identical, they are also equal. // This is the most common path and will allow us to skip @@ -83,7 +83,7 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { if (is_string($this->value)) { if (str_contains($this->value, "\n")) { @@ -98,7 +98,7 @@ public function toString(bool $exportObjects = false): string return sprintf( 'is equal to %s', - Exporter::export($this->value, $exportObjects), + Exporter::export($this->value), ); } } diff --git a/src/Framework/Constraint/Equality/IsEqualWithDelta.php b/src/Framework/Constraint/Equality/IsEqualWithDelta.php index d587f6f70a0..7f18b5bbee0 100644 --- a/src/Framework/Constraint/Equality/IsEqualWithDelta.php +++ b/src/Framework/Constraint/Equality/IsEqualWithDelta.php @@ -42,7 +42,7 @@ public function __construct(mixed $value, float $delta) * * @throws ExpectationFailedException */ - public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool + public function evaluate(mixed $other, string $description = '', bool $returnResult = false): bool { // If $this->value and $other are identical, they are also equal. // This is the most common path and will allow us to skip @@ -81,11 +81,11 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { return sprintf( 'is equal to %s with delta <%F>', - Exporter::export($this->value, $exportObjects), + Exporter::export($this->value), $this->delta, ); } diff --git a/src/Framework/Constraint/Exception/Exception.php b/src/Framework/Constraint/Exception/Exception.php index 14905e77130..a84f6eb988f 100644 --- a/src/Framework/Constraint/Exception/Exception.php +++ b/src/Framework/Constraint/Exception/Exception.php @@ -14,6 +14,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Exception extends Constraint @@ -66,7 +68,7 @@ protected function failureDescription(mixed $other): string if ($other instanceof Throwable) { $message = '. Message was: "' . $other->getMessage() . '" at' - . "\n" . Filter::getFilteredStacktrace($other); + . "\n" . Filter::stackTraceFromThrowableAsString($other); } return sprintf( diff --git a/src/Framework/Constraint/Exception/ExceptionCode.php b/src/Framework/Constraint/Exception/ExceptionCode.php index 255aff334b8..666f733728f 100644 --- a/src/Framework/Constraint/Exception/ExceptionCode.php +++ b/src/Framework/Constraint/Exception/ExceptionCode.php @@ -13,6 +13,8 @@ use PHPUnit\Util\Exporter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ExceptionCode extends Constraint @@ -48,8 +50,8 @@ protected function failureDescription(mixed $other): string { return sprintf( '%s is equal to expected exception code %s', - Exporter::export($other, true), - Exporter::export($this->expectedCode, true), + Exporter::export($other), + Exporter::export($this->expectedCode), ); } } diff --git a/src/Framework/Constraint/Exception/ExceptionMessageIsOrContains.php b/src/Framework/Constraint/Exception/ExceptionMessageIsOrContains.php index 56ab6f0d489..ae92090396c 100644 --- a/src/Framework/Constraint/Exception/ExceptionMessageIsOrContains.php +++ b/src/Framework/Constraint/Exception/ExceptionMessageIsOrContains.php @@ -14,6 +14,8 @@ use PHPUnit\Util\Exporter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ExceptionMessageIsOrContains extends Constraint diff --git a/src/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.php b/src/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.php index dfb477d0939..611af03746f 100644 --- a/src/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.php +++ b/src/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpression.php @@ -15,6 +15,8 @@ use PHPUnit\Util\Exporter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ExceptionMessageMatchesRegularExpression extends Constraint diff --git a/src/Framework/Constraint/IsAnything.php b/src/Framework/Constraint/IsAnything.php index ef1b8bdf143..85df5f92f41 100644 --- a/src/Framework/Constraint/IsAnything.php +++ b/src/Framework/Constraint/IsAnything.php @@ -9,8 +9,6 @@ */ namespace PHPUnit\Framework\Constraint; -use PHPUnit\Framework\ExpectationFailedException; - /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -25,8 +23,6 @@ final class IsAnything extends Constraint * If $returnResult is true, the result of the evaluation is returned as * a boolean value instead: true in case of success, false in case of a * failure. - * - * @throws ExpectationFailedException */ public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool { diff --git a/src/Framework/Constraint/IsIdentical.php b/src/Framework/Constraint/IsIdentical.php index e3fa7a40e22..03ba16f7777 100644 --- a/src/Framework/Constraint/IsIdentical.php +++ b/src/Framework/Constraint/IsIdentical.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\Framework\Constraint; +use function explode; +use function gettype; use function is_array; use function is_object; use function is_string; @@ -16,6 +18,7 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Util\Exporter; use SebastianBergmann\Comparator\ComparisonFailure; +use UnitEnum; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit @@ -62,13 +65,13 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes ); } - // if both values are array, make sure a diff is generated - if (is_array($this->value) && is_array($other)) { + // if both values are array or enums, make sure a diff is generated + if ((is_array($this->value) && is_array($other)) || ($this->value instanceof UnitEnum && $other instanceof UnitEnum)) { $f = new ComparisonFailure( $this->value, $other, - Exporter::export($this->value, true), - Exporter::export($other, true), + Exporter::export($this->value), + Exporter::export($other), ); } @@ -81,14 +84,14 @@ public function evaluate(mixed $other, string $description = '', bool $returnRes /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { if (is_object($this->value)) { return 'is identical to an object of class "' . $this->value::class . '"'; } - return 'is identical to ' . Exporter::export($this->value, $exportObjects); + return 'is identical to ' . Exporter::export($this->value); } /** @@ -103,6 +106,10 @@ protected function failureDescription(mixed $other): string return 'two variables reference the same object'; } + if (explode(' ', gettype($this->value), 2)[0] === 'resource' && explode(' ', gettype($other), 2)[0] === 'resource') { + return 'two variables reference the same resource'; + } + if (is_string($this->value) && is_string($other)) { return 'two strings are identical'; } diff --git a/src/Framework/Constraint/JsonMatches.php b/src/Framework/Constraint/JsonMatches.php index 0000c8ef5a7..3fd6736a43c 100644 --- a/src/Framework/Constraint/JsonMatches.php +++ b/src/Framework/Constraint/JsonMatches.php @@ -59,7 +59,7 @@ protected function matches(mixed $other): bool return false; } - return $recodedOther == $recodedValue; + return $recodedOther === $recodedValue; } /** @@ -68,7 +68,7 @@ protected function matches(mixed $other): bool * @throws ExpectationFailedException * @throws InvalidJsonException */ - protected function fail(mixed $other, string $description, ComparisonFailure $comparisonFailure = null): never + protected function fail(mixed $other, string $description, ?ComparisonFailure $comparisonFailure = null): never { if ($comparisonFailure === null) { [$error, $recodedOther] = Json::canonicalize($other); diff --git a/src/Framework/Constraint/Object/ObjectEquals.php b/src/Framework/Constraint/Object/ObjectEquals.php index b513545fad2..2c78ea42759 100644 --- a/src/Framework/Constraint/Object/ObjectEquals.php +++ b/src/Framework/Constraint/Object/ObjectEquals.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\Framework\Constraint; +use function assert; +use function count; use function is_object; use PHPUnit\Framework\ActualValueIsNotAnObjectException; use PHPUnit\Framework\ComparisonMethodDoesNotAcceptParameterTypeException; @@ -100,6 +102,7 @@ protected function matches(mixed $other): bool ); } + assert(count($method->getParameters()) > 0); $parameter = $method->getParameters()[0]; if (!$parameter->hasType()) { @@ -132,11 +135,12 @@ protected function matches(mixed $other): bool ); } + /** @phpstan-ignore method.dynamicName */ return $other->{$this->method}($this->expected); } protected function failureDescription(mixed $other): string { - return $this->toString(true); + return $this->toString(); } } diff --git a/src/Framework/Constraint/Object/ObjectHasProperty.php b/src/Framework/Constraint/Object/ObjectHasProperty.php index 6c8bc415e1a..74c40c75bbe 100644 --- a/src/Framework/Constraint/Object/ObjectHasProperty.php +++ b/src/Framework/Constraint/Object/ObjectHasProperty.php @@ -66,7 +66,7 @@ protected function failureDescription(mixed $other): string return sprintf( 'object of class "%s" %s', $other::class, - $this->toString(true), + $this->toString(), ); } @@ -74,7 +74,7 @@ protected function failureDescription(mixed $other): string '"%s" (%s) %s', $other, gettype($other), - $this->toString(true), + $this->toString(), ); } } diff --git a/src/Framework/Constraint/Operator/BinaryOperator.php b/src/Framework/Constraint/Operator/BinaryOperator.php index 4c3d8e4b928..feb6d040899 100644 --- a/src/Framework/Constraint/Operator/BinaryOperator.php +++ b/src/Framework/Constraint/Operator/BinaryOperator.php @@ -18,14 +18,14 @@ abstract class BinaryOperator extends Operator { /** - * @psalm-var list + * @var list */ private readonly array $constraints; protected function __construct(mixed ...$constraints) { $this->constraints = array_map( - fn ($constraint): Constraint => $this->checkConstraint($constraint), + fn (mixed $constraint): Constraint => $this->checkConstraint($constraint), $constraints, ); } @@ -75,7 +75,7 @@ public function count(): int } /** - * @psalm-return list + * @return list */ final protected function constraints(): array { @@ -99,7 +99,7 @@ final protected function constraintNeedsParentheses(Constraint $constraint): boo */ protected function reduce(): Constraint { - if ($this->arity() === 1 && $this->constraints[0] instanceof Operator) { + if (count($this->constraints) === 1 && $this->constraints[0] instanceof Operator) { return $this->constraints[0]->reduce(); } diff --git a/src/Framework/Constraint/Operator/LogicalNot.php b/src/Framework/Constraint/Operator/LogicalNot.php index 021afddd517..224260909f2 100644 --- a/src/Framework/Constraint/Operator/LogicalNot.php +++ b/src/Framework/Constraint/Operator/LogicalNot.php @@ -51,12 +51,16 @@ public static function negate(string $string): string preg_match('/(\'[\w\W]*\')([\w\W]*)("[\w\W]*")/i', $string, $matches); + if (count($matches) === 0) { + preg_match('/(\'[\w\W]*\')([\w\W]*)(\'[\w\W]*\')/i', $string, $matches); + } + $positives = array_map( static fn (string $s) => '/\\b' . preg_quote($s, '/') . '/', $positives, ); - if (count($matches) > 0) { + if (count($matches) >= 3) { $nonInput = $matches[2]; $negatedString = preg_replace( diff --git a/src/Framework/Constraint/Operator/LogicalXor.php b/src/Framework/Constraint/Operator/LogicalXor.php index e8ffc34d1ea..3b40a126190 100644 --- a/src/Framework/Constraint/Operator/LogicalXor.php +++ b/src/Framework/Constraint/Operator/LogicalXor.php @@ -59,7 +59,7 @@ public function matches(mixed $other): bool return array_reduce( $constraints, - static fn (bool $matches, Constraint $constraint): bool => $matches xor $constraint->evaluate($other, '', true), + static fn (?bool $matches, Constraint $constraint): bool => $matches xor $constraint->evaluate($other, '', true), $initial->evaluate($other, '', true), ); } diff --git a/src/Framework/Constraint/Operator/UnaryOperator.php b/src/Framework/Constraint/Operator/UnaryOperator.php index 10b0cc62a5d..d6ac6e3f6c8 100644 --- a/src/Framework/Constraint/Operator/UnaryOperator.php +++ b/src/Framework/Constraint/Operator/UnaryOperator.php @@ -95,7 +95,7 @@ protected function failureDescription(mixed $other): string } /** - * Transforms string returned by the memeber constraint's toString() or + * Transforms string returned by the member constraint's toString() or * failureDescription() such that it reflects constraint's participation in * this expression. * diff --git a/src/Framework/Constraint/String/IsJson.php b/src/Framework/Constraint/String/IsJson.php index fdd663cd9f2..582b58e8bdc 100644 --- a/src/Framework/Constraint/String/IsJson.php +++ b/src/Framework/Constraint/String/IsJson.php @@ -9,6 +9,12 @@ */ namespace PHPUnit\Framework\Constraint; +use const JSON_ERROR_CTRL_CHAR; +use const JSON_ERROR_DEPTH; +use const JSON_ERROR_NONE; +use const JSON_ERROR_STATE_MISMATCH; +use const JSON_ERROR_SYNTAX; +use const JSON_ERROR_UTF8; use function is_string; use function json_decode; use function json_last_error; @@ -39,7 +45,7 @@ protected function matches(mixed $other): bool json_decode($other); - if (json_last_error()) { + if (json_last_error() !== JSON_ERROR_NONE) { return false; } diff --git a/src/Framework/Constraint/String/StringContains.php b/src/Framework/Constraint/String/StringContains.php index 7ba71587e58..0f801a605fd 100644 --- a/src/Framework/Constraint/String/StringContains.php +++ b/src/Framework/Constraint/String/StringContains.php @@ -53,16 +53,16 @@ public function toString(): string return sprintf( 'contains "%s" [%s](length: %s)', $needle, - $this->getDetectedEncoding($needle), + $this->detectedEncoding($needle), strlen($needle), ); } public function failureDescription(mixed $other): string { - $stringifiedHaystack = Exporter::export($other, true); - $haystackEncoding = $this->getDetectedEncoding($other); - $haystackLength = $this->getHaystackLength($other); + $stringifiedHaystack = Exporter::export($other); + $haystackEncoding = $this->detectedEncoding($other); + $haystackLength = $this->haystackLength($other); $haystackInformation = sprintf( '%s [%s](length: %s) ', @@ -71,7 +71,7 @@ public function failureDescription(mixed $other): string $haystackLength, ); - $needleInformation = $this->toString(true); + $needleInformation = $this->toString(); return $haystackInformation . $needleInformation; } @@ -115,7 +115,7 @@ protected function matches(mixed $other): bool return str_contains($haystack, $this->needle); } - private function getDetectedEncoding(mixed $other): string + private function detectedEncoding(mixed $other): string { if ($this->ignoreCase) { return 'Encoding ignored'; @@ -127,14 +127,14 @@ private function getDetectedEncoding(mixed $other): string $detectedEncoding = mb_detect_encoding($other, null, true); - if (!$detectedEncoding) { + if ($detectedEncoding === false) { return 'Encoding detection failed'; } return $detectedEncoding; } - private function getHaystackLength(mixed $haystack): int + private function haystackLength(mixed $haystack): int { if (!is_string($haystack)) { return 0; diff --git a/src/Framework/Constraint/String/StringMatchesFormatDescription.php b/src/Framework/Constraint/String/StringMatchesFormatDescription.php index 8e5072db35e..4df81715f82 100644 --- a/src/Framework/Constraint/String/StringMatchesFormatDescription.php +++ b/src/Framework/Constraint/String/StringMatchesFormatDescription.php @@ -10,6 +10,7 @@ namespace PHPUnit\Framework\Constraint; use const DIRECTORY_SEPARATOR; +use const PHP_EOL; use function explode; use function implode; use function preg_match; @@ -59,15 +60,33 @@ protected function failureDescription(mixed $other): string return 'string matches format description'; } + /** + * Returns a cleaned up diff. + * + * The expected string can contain placeholders like %s and %d. + * By using 'diff' such placeholders compared to the real output will + * always be different, although we don't want to show them as different. + * This method removes the expected differences by figuring out if a difference + * is allowed by the use of a placeholder. + * + * The problem here are %A and %a multiline placeholders since we look at the + * expected and actual output line by line. If differences allowed by those placeholders + * stretch over multiple lines they will still end up in the final diff. + * And since they mess up the line sync between the expected and actual output + * all following allowed changes will not be detected/removed anymore. + */ protected function additionalFailureDescription(mixed $other): string { $from = explode("\n", $this->formatDescription); $to = explode("\n", $this->convertNewlines($other)); foreach ($from as $index => $line) { + // is the expected output line different from the actual output line if (isset($to[$index]) && $line !== $to[$index]) { $line = $this->regularExpressionForFormatDescription($line); + // if the difference is allowed by a placeholder + // overwrite the expected line with the actual line to prevent it from showing up in the diff if (preg_match($line, $to[$index]) > 0) { $from[$index] = $to[$index]; } @@ -86,17 +105,18 @@ private function regularExpressionForFormatDescription(string $string): string preg_quote($string, '/'), [ '%%' => '%', - '%e' => '\\' . DIRECTORY_SEPARATOR, + '%e' => preg_quote(DIRECTORY_SEPARATOR, '/'), '%s' => '[^\r\n]+', '%S' => '[^\r\n]*', - '%a' => '.+', - '%A' => '.*', + '%a' => '.+?', + '%A' => '.*?', '%w' => '\s*', '%i' => '[+-]?\d+', '%d' => '\d+', '%x' => '[0-9a-fA-F]+', - '%f' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', + '%f' => '[+-]?(?:\d+|(?=\.\d))(?:\.\d+)?(?:[Ee][+-]?\d+)?', '%c' => '.', + '%0' => '\x00', ], ); diff --git a/src/Framework/Constraint/Traversable/ArrayHasKey.php b/src/Framework/Constraint/Traversable/ArrayHasKey.php index 07bfa4fd734..fa8d2744111 100644 --- a/src/Framework/Constraint/Traversable/ArrayHasKey.php +++ b/src/Framework/Constraint/Traversable/ArrayHasKey.php @@ -19,9 +19,9 @@ */ final class ArrayHasKey extends Constraint { - private readonly int|string $key; + private readonly mixed $key; - public function __construct(int|string $key) + public function __construct(mixed $key) { $this->key = $key; } @@ -59,6 +59,6 @@ protected function matches(mixed $other): bool */ protected function failureDescription(mixed $other): string { - return 'an array ' . $this->toString(true); + return 'an array ' . $this->toString(); } } diff --git a/src/Framework/Constraint/Traversable/IsList.php b/src/Framework/Constraint/Traversable/IsList.php index 2e2f60cb0a8..f6ac30618e0 100644 --- a/src/Framework/Constraint/Traversable/IsList.php +++ b/src/Framework/Constraint/Traversable/IsList.php @@ -46,6 +46,6 @@ protected function matches(mixed $other): bool */ protected function failureDescription(mixed $other): string { - return $this->valueToTypeStringFragment($other) . $this->toString(true); + return $this->valueToTypeStringFragment($other) . $this->toString(); } } diff --git a/src/Framework/Constraint/Traversable/TraversableContains.php b/src/Framework/Constraint/Traversable/TraversableContains.php index 770bdb8a55a..6f1a298bf1a 100644 --- a/src/Framework/Constraint/Traversable/TraversableContains.php +++ b/src/Framework/Constraint/Traversable/TraversableContains.php @@ -28,9 +28,9 @@ public function __construct(mixed $value) /** * Returns a string representation of the constraint. */ - public function toString(bool $exportObjects = false): string + public function toString(): string { - return 'contains ' . Exporter::export($this->value, $exportObjects); + return 'contains ' . Exporter::export($this->value); } /** @@ -44,7 +44,7 @@ protected function failureDescription(mixed $other): string return sprintf( '%s %s', is_array($other) ? 'an array' : 'a traversable', - $this->toString(true), + $this->toString(), ); } diff --git a/src/Framework/Constraint/Traversable/TraversableContainsEqual.php b/src/Framework/Constraint/Traversable/TraversableContainsEqual.php index 9bb0f3c085e..c3555532c30 100644 --- a/src/Framework/Constraint/Traversable/TraversableContainsEqual.php +++ b/src/Framework/Constraint/Traversable/TraversableContainsEqual.php @@ -27,7 +27,7 @@ protected function matches(mixed $other): bool } foreach ($other as $element) { - /* @noinspection TypeUnsafeComparisonInspection */ + /** @phpstan-ignore equal.notAllowed */ if ($this->value() == $element) { return true; } diff --git a/src/Framework/Constraint/Traversable/TraversableContainsOnly.php b/src/Framework/Constraint/Traversable/TraversableContainsOnly.php index 72e3ccc0f6e..50da87f298d 100644 --- a/src/Framework/Constraint/Traversable/TraversableContainsOnly.php +++ b/src/Framework/Constraint/Traversable/TraversableContainsOnly.php @@ -10,27 +10,33 @@ namespace PHPUnit\Framework\Constraint; use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\NativeType; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final class TraversableContainsOnly extends Constraint { - private Constraint $constraint; + private readonly Constraint $constraint; private readonly string $type; + public static function forNativeType(NativeType $type): self + { + return new self(new IsType($type), $type->value); + } + /** - * @throws \PHPUnit\Framework\Exception + * @param class-string $type */ - public function __construct(string $type, bool $isNativeType = true) + public static function forClassOrInterface(string $type): self { - if ($isNativeType) { - $this->constraint = new IsType($type); - } else { - $this->constraint = new IsInstanceOf($type); - } + return new self(new IsInstanceOf($type), $type); + } - $this->type = $type; + private function __construct(IsInstanceOf|IsType $constraint, string $type) + { + $this->constraint = $constraint; + $this->type = $type; } /** diff --git a/src/Framework/Constraint/Type/IsInstanceOf.php b/src/Framework/Constraint/Type/IsInstanceOf.php index e69f4f862fa..df7ebf1e2c3 100644 --- a/src/Framework/Constraint/Type/IsInstanceOf.php +++ b/src/Framework/Constraint/Type/IsInstanceOf.php @@ -20,12 +20,12 @@ final class IsInstanceOf extends Constraint { /** - * @psalm-var class-string + * @var class-string */ private readonly string $name; /** - * @psalm-var 'class'|'interface' + * @var 'class'|'interface' */ private readonly string $type; @@ -74,6 +74,6 @@ protected function matches(mixed $other): bool */ protected function failureDescription(mixed $other): string { - return $this->valueToTypeStringFragment($other) . $this->toString(true); + return $this->valueToTypeStringFragment($other) . $this->toString(); } } diff --git a/src/Framework/Constraint/Type/IsType.php b/src/Framework/Constraint/Type/IsType.php index 84c422f8121..c2bff8eda98 100644 --- a/src/Framework/Constraint/Type/IsType.php +++ b/src/Framework/Constraint/Type/IsType.php @@ -21,117 +21,17 @@ use function is_scalar; use function is_string; use function sprintf; -use PHPUnit\Framework\UnknownTypeException; +use PHPUnit\Framework\NativeType; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final class IsType extends Constraint { - /** - * @var string - */ - public const TYPE_ARRAY = 'array'; - - /** - * @var string - */ - public const TYPE_BOOL = 'bool'; - - /** - * @var string - */ - public const TYPE_FLOAT = 'float'; - - /** - * @var string - */ - public const TYPE_INT = 'int'; - - /** - * @var string - */ - public const TYPE_NULL = 'null'; - - /** - * @var string - */ - public const TYPE_NUMERIC = 'numeric'; - - /** - * @var string - */ - public const TYPE_OBJECT = 'object'; - - /** - * @var string - */ - public const TYPE_RESOURCE = 'resource'; - - /** - * @var string - */ - public const TYPE_CLOSED_RESOURCE = 'resource (closed)'; - - /** - * @var string - */ - public const TYPE_STRING = 'string'; - - /** - * @var string - */ - public const TYPE_SCALAR = 'scalar'; - - /** - * @var string - */ - public const TYPE_CALLABLE = 'callable'; - - /** - * @var string - */ - public const TYPE_ITERABLE = 'iterable'; - - /** - * @psalm-var array - */ - private const KNOWN_TYPES = [ - 'array' => true, - 'boolean' => true, - 'bool' => true, - 'double' => true, - 'float' => true, - 'integer' => true, - 'int' => true, - 'null' => true, - 'numeric' => true, - 'object' => true, - 'real' => true, - 'resource' => true, - 'resource (closed)' => true, - 'string' => true, - 'scalar' => true, - 'callable' => true, - 'iterable' => true, - ]; - - /** - * @psalm-var 'array'|'boolean'|'bool'|'double'|'float'|'integer'|'int'|'null'|'numeric'|'object'|'real'|'resource'|'resource (closed)'|'string'|'scalar'|'callable'|'iterable' - */ - private readonly string $type; + private readonly NativeType $type; - /** - * @psalm-param 'array'|'boolean'|'bool'|'double'|'float'|'integer'|'int'|'null'|'numeric'|'object'|'real'|'resource'|'resource (closed)'|'string'|'scalar'|'callable'|'iterable' $type - * - * @throws UnknownTypeException - */ - public function __construct(string $type) + public function __construct(NativeType $type) { - if (!isset(self::KNOWN_TYPES[$type])) { - throw new UnknownTypeException($type); - } - $this->type = $type; } @@ -142,7 +42,7 @@ public function toString(): string { return sprintf( 'is of type %s', - $this->type, + $this->type->value, ); } @@ -153,49 +53,45 @@ public function toString(): string protected function matches(mixed $other): bool { switch ($this->type) { - case 'numeric': + case NativeType::Numeric: return is_numeric($other); - case 'integer': - case 'int': + case NativeType::Int: return is_int($other); - case 'double': - case 'float': - case 'real': + case NativeType::Float: return is_float($other); - case 'string': + case NativeType::String: return is_string($other); - case 'boolean': - case 'bool': + case NativeType::Bool: return is_bool($other); - case 'null': + case NativeType::Null: return null === $other; - case 'array': + case NativeType::Array: return is_array($other); - case 'object': + case NativeType::Object: return is_object($other); - case 'resource': + case NativeType::Resource: $type = gettype($other); return $type === 'resource' || $type === 'resource (closed)'; - case 'resource (closed)': + case NativeType::ClosedResource: return gettype($other) === 'resource (closed)'; - case 'scalar': + case NativeType::Scalar: return is_scalar($other); - case 'callable': + case NativeType::Callable: return is_callable($other); - case 'iterable': + case NativeType::Iterable: return is_iterable($other); default: diff --git a/src/Framework/DataProviderTestSuite.php b/src/Framework/DataProviderTestSuite.php index 6975d1c6c09..262530b7a58 100644 --- a/src/Framework/DataProviderTestSuite.php +++ b/src/Framework/DataProviderTestSuite.php @@ -9,23 +9,32 @@ */ namespace PHPUnit\Framework; +use function assert; +use function class_exists; +use function count; use function explode; use PHPUnit\Framework\TestSize\TestSize; use PHPUnit\Metadata\Api\Groups; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DataProviderTestSuite extends TestSuite { /** - * @psalm-var list + * @var list + */ + private array $dependencies = []; + + /** + * @var ?non-empty-list */ - private array $dependencies = []; private ?array $providedTests = null; /** - * @psalm-param list $dependencies + * @param list $dependencies */ public function setDependencies(array $dependencies): void { @@ -41,7 +50,7 @@ public function setDependencies(array $dependencies): void } /** - * @psalm-return list + * @return non-empty-list */ public function provides(): array { @@ -53,7 +62,7 @@ public function provides(): array } /** - * @psalm-return list + * @return list */ public function requires(): array { @@ -67,8 +76,12 @@ public function requires(): array */ public function size(): TestSize { + assert(count(explode('::', $this->name())) === 2); [$className, $methodName] = explode('::', $this->name()); + assert(class_exists($className)); + assert($methodName !== ''); + return (new Groups)->size($className, $methodName); } } diff --git a/src/Framework/Exception/AssertionFailedError.php b/src/Framework/Exception/AssertionFailedError.php index 0ba25286f6c..6bd59c4aea1 100644 --- a/src/Framework/Exception/AssertionFailedError.php +++ b/src/Framework/Exception/AssertionFailedError.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ class AssertionFailedError extends Exception implements SelfDescribing diff --git a/src/Framework/Exception/CodeCoverageException.php b/src/Framework/Exception/CodeCoverageException.php deleted file mode 100644 index 36b07231301..00000000000 --- a/src/Framework/Exception/CodeCoverageException.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -class CodeCoverageException extends Exception -{ -} diff --git a/src/Framework/Exception/EmptyStringException.php b/src/Framework/Exception/EmptyStringException.php index e7f4fa9f0fa..f5980cd71ab 100644 --- a/src/Framework/Exception/EmptyStringException.php +++ b/src/Framework/Exception/EmptyStringException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class EmptyStringException extends InvalidArgumentException diff --git a/src/Framework/Exception/Exception.php b/src/Framework/Exception/Exception.php index 01531f5ec57..e35b0d50520 100644 --- a/src/Framework/Exception/Exception.php +++ b/src/Framework/Exception/Exception.php @@ -11,6 +11,8 @@ use function array_keys; use function get_object_vars; +use function is_int; +use function sprintf; use RuntimeException; use Throwable; @@ -34,14 +36,31 @@ * * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ class Exception extends RuntimeException implements \PHPUnit\Exception { + /** + * @var list + */ protected array $serializableTrace; - public function __construct(string $message = '', int $code = 0, Throwable $previous = null) + public function __construct(string $message = '', int|string $code = 0, ?Throwable $previous = null) { + /** + * @see https://github.com/sebastianbergmann/phpunit/issues/5965 + */ + if (!is_int($code)) { + $message .= sprintf( + ' (exception code: %s)', + $code, + ); + + $code = 0; + } + parent::__construct($message, $code, $previous); $this->serializableTrace = $this->getTrace(); @@ -58,6 +77,8 @@ public function __sleep(): array /** * Returns the serializable trace (without 'args'). + * + * @return list */ public function getSerializableTrace(): array { diff --git a/src/Framework/Exception/ExpectationFailedException.php b/src/Framework/Exception/ExpectationFailedException.php index c1a4c043fd4..bff863f9f13 100644 --- a/src/Framework/Exception/ExpectationFailedException.php +++ b/src/Framework/Exception/ExpectationFailedException.php @@ -19,13 +19,15 @@ * SebastianBergmann\Comparator\ComparisonFailure which is used to * generate diff output of the failed expectations. * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ExpectationFailedException extends AssertionFailedError { protected ?ComparisonFailure $comparisonFailure = null; - public function __construct(string $message, ComparisonFailure $comparisonFailure = null, Exception $previous = null) + public function __construct(string $message, ?ComparisonFailure $comparisonFailure = null, ?Exception $previous = null) { $this->comparisonFailure = $comparisonFailure; diff --git a/src/Framework/Exception/GeneratorNotSupportedException.php b/src/Framework/Exception/GeneratorNotSupportedException.php index 2a6ff542c3d..b3b179531a6 100644 --- a/src/Framework/Exception/GeneratorNotSupportedException.php +++ b/src/Framework/Exception/GeneratorNotSupportedException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class GeneratorNotSupportedException extends InvalidArgumentException diff --git a/src/Framework/Exception/Incomplete/IncompleteTest.php b/src/Framework/Exception/Incomplete/IncompleteTest.php index b77b1afff2c..4492ef226f6 100644 --- a/src/Framework/Exception/Incomplete/IncompleteTest.php +++ b/src/Framework/Exception/Incomplete/IncompleteTest.php @@ -12,6 +12,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface IncompleteTest extends Throwable diff --git a/src/Framework/Exception/Incomplete/IncompleteTestError.php b/src/Framework/Exception/Incomplete/IncompleteTestError.php index 65f9c8bc343..a45564da8fb 100644 --- a/src/Framework/Exception/Incomplete/IncompleteTestError.php +++ b/src/Framework/Exception/Incomplete/IncompleteTestError.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class IncompleteTestError extends AssertionFailedError implements IncompleteTest diff --git a/src/Framework/Exception/InvalidArgumentException.php b/src/Framework/Exception/InvalidArgumentException.php index 12a94ec1e5e..700abf03889 100644 --- a/src/Framework/Exception/InvalidArgumentException.php +++ b/src/Framework/Exception/InvalidArgumentException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ abstract class InvalidArgumentException extends Exception diff --git a/src/Framework/Exception/InvalidCoversTargetException.php b/src/Framework/Exception/InvalidCoversTargetException.php deleted file mode 100644 index ebf2994a94e..00000000000 --- a/src/Framework/Exception/InvalidCoversTargetException.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvalidCoversTargetException extends CodeCoverageException -{ -} diff --git a/src/Framework/Exception/InvalidDataProviderException.php b/src/Framework/Exception/InvalidDataProviderException.php index 7e2ef24c637..a29f4bde38c 100644 --- a/src/Framework/Exception/InvalidDataProviderException.php +++ b/src/Framework/Exception/InvalidDataProviderException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidDataProviderException extends Exception diff --git a/src/Framework/Exception/InvalidDependencyException.php b/src/Framework/Exception/InvalidDependencyException.php index 3a949554c9d..8a636fd4851 100644 --- a/src/Framework/Exception/InvalidDependencyException.php +++ b/src/Framework/Exception/InvalidDependencyException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidDependencyException extends AssertionFailedError implements SkippedTest diff --git a/src/Framework/Exception/NoChildTestSuiteException.php b/src/Framework/Exception/NoChildTestSuiteException.php index 7ef4153b0f0..e59df81df12 100644 --- a/src/Framework/Exception/NoChildTestSuiteException.php +++ b/src/Framework/Exception/NoChildTestSuiteException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoChildTestSuiteException extends Exception diff --git a/src/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.php b/src/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.php index c2579df37a7..258b940ae21 100644 --- a/src/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.php +++ b/src/Framework/Exception/ObjectEquals/ActualValueIsNotAnObjectException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ActualValueIsNotAnObjectException extends Exception diff --git a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.php b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.php index 19f4a490c94..74d00a17d76 100644 --- a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.php +++ b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotAcceptParameterTypeException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ComparisonMethodDoesNotAcceptParameterTypeException extends Exception diff --git a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php index 87cd241d3f6..62dc7e8cbbb 100644 --- a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php +++ b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ComparisonMethodDoesNotDeclareBoolReturnTypeException extends Exception diff --git a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php index b8d61933c5f..d57607447ae 100644 --- a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php +++ b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ComparisonMethodDoesNotDeclareExactlyOneParameterException extends Exception diff --git a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.php b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.php index 937a3b6971a..65718682913 100644 --- a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.php +++ b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotDeclareParameterTypeException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ComparisonMethodDoesNotDeclareParameterTypeException extends Exception diff --git a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.php b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.php index a482fb4f908..94590b51062 100644 --- a/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.php +++ b/src/Framework/Exception/ObjectEquals/ComparisonMethodDoesNotExistException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ComparisonMethodDoesNotExistException extends Exception diff --git a/src/Framework/Exception/PhptAssertionFailedError.php b/src/Framework/Exception/PhptAssertionFailedError.php index e645e69f62a..3742abbbc6c 100644 --- a/src/Framework/Exception/PhptAssertionFailedError.php +++ b/src/Framework/Exception/PhptAssertionFailedError.php @@ -10,7 +10,9 @@ namespace PHPUnit\Framework; /** - * @psalm-immutable + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ @@ -18,9 +20,16 @@ final class PhptAssertionFailedError extends AssertionFailedError { private readonly string $syntheticFile; private readonly int $syntheticLine; + + /** + * @var list + */ private readonly array $syntheticTrace; private readonly string $diff; + /** + * @param list $trace + */ public function __construct(string $message, int $code, string $file, int $line, array $trace, string $diff) { parent::__construct($message, $code); @@ -41,6 +50,9 @@ public function syntheticLine(): int return $this->syntheticLine; } + /** + * @return list + */ public function syntheticTrace(): array { return $this->syntheticTrace; diff --git a/src/Framework/Exception/ProcessIsolationException.php b/src/Framework/Exception/ProcessIsolationException.php index c4086fbb79c..e59c9c60358 100644 --- a/src/Framework/Exception/ProcessIsolationException.php +++ b/src/Framework/Exception/ProcessIsolationException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ProcessIsolationException extends Exception diff --git a/src/Framework/Exception/Skipped/SkippedTest.php b/src/Framework/Exception/Skipped/SkippedTest.php index a12aa402da5..ab2f6749635 100644 --- a/src/Framework/Exception/Skipped/SkippedTest.php +++ b/src/Framework/Exception/Skipped/SkippedTest.php @@ -12,6 +12,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface SkippedTest extends Throwable diff --git a/src/Framework/Exception/Skipped/SkippedTestSuiteError.php b/src/Framework/Exception/Skipped/SkippedTestSuiteError.php index 5448508a1ea..d3a4788b0cf 100644 --- a/src/Framework/Exception/Skipped/SkippedTestSuiteError.php +++ b/src/Framework/Exception/Skipped/SkippedTestSuiteError.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class SkippedTestSuiteError extends AssertionFailedError implements SkippedTest diff --git a/src/Framework/Exception/Skipped/SkippedWithMessageException.php b/src/Framework/Exception/Skipped/SkippedWithMessageException.php index 22b73a1cfbb..d09a760a37c 100644 --- a/src/Framework/Exception/Skipped/SkippedWithMessageException.php +++ b/src/Framework/Exception/Skipped/SkippedWithMessageException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class SkippedWithMessageException extends AssertionFailedError implements SkippedTest diff --git a/src/Framework/Exception/UnknownClassOrInterfaceException.php b/src/Framework/Exception/UnknownClassOrInterfaceException.php index c3cab6c6b67..6a10f97fb1a 100644 --- a/src/Framework/Exception/UnknownClassOrInterfaceException.php +++ b/src/Framework/Exception/UnknownClassOrInterfaceException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class UnknownClassOrInterfaceException extends InvalidArgumentException diff --git a/src/Framework/Exception/UnknownNativeTypeException.php b/src/Framework/Exception/UnknownNativeTypeException.php new file mode 100644 index 00000000000..09da4609328 --- /dev/null +++ b/src/Framework/Exception/UnknownNativeTypeException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnknownNativeTypeException extends InvalidArgumentException +{ + public function __construct(string $type) + { + parent::__construct( + sprintf( + 'Native type "%s" is not known', + $type, + ), + ); + } +} diff --git a/src/Framework/Exception/UnknownTypeException.php b/src/Framework/Exception/UnknownTypeException.php deleted file mode 100644 index b5f9debf3f1..00000000000 --- a/src/Framework/Exception/UnknownTypeException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use function sprintf; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class UnknownTypeException extends InvalidArgumentException -{ - public function __construct(string $name) - { - parent::__construct( - sprintf( - 'Type "%s" is not known', - $name, - ), - ); - } -} diff --git a/src/Framework/ExecutionOrderDependency.php b/src/Framework/ExecutionOrderDependency.php index 9d5419b3e75..896d2560a4a 100644 --- a/src/Framework/ExecutionOrderDependency.php +++ b/src/Framework/ExecutionOrderDependency.php @@ -12,6 +12,8 @@ use function array_filter; use function array_map; use function array_values; +use function assert; +use function count; use function explode; use function in_array; use function str_contains; @@ -20,6 +22,8 @@ use Stringable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ExecutionOrderDependency implements Stringable @@ -60,9 +64,9 @@ public static function forMethod(DependsOnMethod $metadata): self } /** - * @psalm-param list $dependencies + * @param list $dependencies * - * @psalm-return list + * @return list */ public static function filterInvalid(array $dependencies): array { @@ -75,24 +79,26 @@ public static function filterInvalid(array $dependencies): array } /** - * @psalm-param list $existing - * @psalm-param list $additional + * @param list $existing + * @param list $additional * - * @psalm-return list + * @return list */ public static function mergeUnique(array $existing, array $additional): array { $existingTargets = array_map( - static fn ($dependency) => $dependency->getTarget(), + static fn (ExecutionOrderDependency $dependency) => $dependency->getTarget(), $existing, ); foreach ($additional as $dependency) { - if (in_array($dependency->getTarget(), $existingTargets, true)) { + $additionalTarget = $dependency->getTarget(); + + if (in_array($additionalTarget, $existingTargets, true)) { continue; } - $existingTargets[] = $dependency->getTarget(); + $existingTargets[] = $additionalTarget; $existing[] = $dependency; } @@ -100,10 +106,10 @@ public static function mergeUnique(array $existing, array $additional): array } /** - * @psalm-param list $left - * @psalm-param list $right + * @param list $left + * @param list $right * - * @psalm-return list + * @return list */ public static function diff(array $left, array $right): array { @@ -117,7 +123,7 @@ public static function diff(array $left, array $right): array $diff = []; $rightTargets = array_map( - static fn ($dependency) => $dependency->getTarget(), + static fn (ExecutionOrderDependency $dependency) => $dependency->getTarget(), $right, ); @@ -134,19 +140,20 @@ public static function diff(array $left, array $right): array public function __construct(string $classOrCallableName, ?string $methodName = null, bool $deepClone = false, bool $shallowClone = false) { + $this->deepClone = $deepClone; + $this->shallowClone = $shallowClone; + if ($classOrCallableName === '') { return; } if (str_contains($classOrCallableName, '::')) { + assert(count(explode('::', $classOrCallableName)) === 2); [$this->className, $this->methodName] = explode('::', $classOrCallableName); } else { $this->className = $classOrCallableName; - $this->methodName = !empty($methodName) ? $methodName : 'class'; + $this->methodName = $methodName !== null && $methodName !== '' ? $methodName : 'class'; } - - $this->deepClone = $deepClone; - $this->shallowClone = $shallowClone; } public function __toString(): string @@ -154,6 +161,9 @@ public function __toString(): string return $this->getTarget(); } + /** + * @phpstan-assert-if-true non-empty-string $this->getTarget() + */ public function isValid(): bool { // Invalid dependencies can be declared and are skipped by the runner diff --git a/src/Framework/MockObject/ConfigurableMethod.php b/src/Framework/MockObject/ConfigurableMethod.php index 7c57a460106..7cce22c3814 100644 --- a/src/Framework/MockObject/ConfigurableMethod.php +++ b/src/Framework/MockObject/ConfigurableMethod.php @@ -12,33 +12,65 @@ use SebastianBergmann\Type\Type; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ConfigurableMethod { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $name; + + /** + * @var array + */ + private array $defaultParameterValues; + + /** + * @var non-negative-int + */ + private int $numberOfParameters; private Type $returnType; /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name + * @param array $defaultParameterValues + * @param non-negative-int $numberOfParameters */ - public function __construct(string $name, Type $returnType) + public function __construct(string $name, array $defaultParameterValues, int $numberOfParameters, Type $returnType) { - $this->name = $name; - $this->returnType = $returnType; + $this->name = $name; + $this->defaultParameterValues = $defaultParameterValues; + $this->numberOfParameters = $numberOfParameters; + $this->returnType = $returnType; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function name(): string { return $this->name; } + /** + * @return array + */ + public function defaultParameterValues(): array + { + return $this->defaultParameterValues; + } + + /** + * @return non-negative-int + */ + public function numberOfParameters(): int + { + return $this->numberOfParameters; + } + public function mayReturn(mixed $value): bool { return $this->returnType->isAssignable(Type::fromValue($value, false)); diff --git a/src/Framework/MockObject/Exception/BadMethodCallException.php b/src/Framework/MockObject/Exception/BadMethodCallException.php index 7e655e235a9..e8ddadda5e1 100644 --- a/src/Framework/MockObject/Exception/BadMethodCallException.php +++ b/src/Framework/MockObject/Exception/BadMethodCallException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class BadMethodCallException extends \BadMethodCallException implements Exception diff --git a/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php b/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php deleted file mode 100644 index 848746b523a..00000000000 --- a/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function sprintf; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class CannotUseAddMethodsException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct(string $type, string $methodName) - { - parent::__construct( - sprintf( - 'Trying to configure method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class', - $methodName, - $type, - ), - ); - } -} diff --git a/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php b/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php index 61fe0cc580b..6cb399e53a6 100644 --- a/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php +++ b/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CannotUseOnlyMethodsException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/Exception.php b/src/Framework/MockObject/Exception/Exception.php index 5880bc033ec..f7994f20f77 100644 --- a/src/Framework/MockObject/Exception/Exception.php +++ b/src/Framework/MockObject/Exception/Exception.php @@ -12,6 +12,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends Throwable diff --git a/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php b/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php index 6409204a857..faf8a498d95 100644 --- a/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php +++ b/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php @@ -13,6 +13,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php b/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php index f2e1a31e8e5..8bf8967b066 100644 --- a/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php +++ b/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MatchBuilderNotFoundException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php b/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php index 0972ffaf85b..de62b867922 100644 --- a/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php +++ b/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MatcherAlreadyRegisteredException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php b/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php index 2f0bb5a65a9..4d39b5d9250 100644 --- a/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php +++ b/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MethodCannotBeConfiguredException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php b/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php index 1e9f2c04cac..e4a375927bc 100644 --- a/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php +++ b/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MethodNameAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php b/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php index 89565b77ee2..25c11341660 100644 --- a/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php +++ b/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MethodNameNotConfiguredException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php b/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php index 1609c6ffb6b..fba96cf45c9 100644 --- a/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php +++ b/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MethodParametersAlreadyConfiguredException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/NeverReturningMethodException.php b/src/Framework/MockObject/Exception/NeverReturningMethodException.php index 21aa6982c89..3e565cea836 100644 --- a/src/Framework/MockObject/Exception/NeverReturningMethodException.php +++ b/src/Framework/MockObject/Exception/NeverReturningMethodException.php @@ -18,8 +18,8 @@ final class NeverReturningMethodException extends RuntimeException implements Exception { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function __construct(string $className, string $methodName) { diff --git a/src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php b/src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php new file mode 100644 index 00000000000..c4b181653f9 --- /dev/null +++ b/src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoMoreReturnValuesConfiguredException extends \PHPUnit\Framework\Exception implements Exception +{ + public function __construct(Invocation $invocation, int $numberOfConfiguredReturnValues) + { + parent::__construct( + sprintf( + 'Only %d return values have been configured for %s::%s()', + $numberOfConfiguredReturnValues, + $invocation->className(), + $invocation->methodName(), + ), + ); + } +} diff --git a/src/Framework/MockObject/Exception/ReflectionException.php b/src/Framework/MockObject/Exception/ReflectionException.php deleted file mode 100644 index d6319c694bb..00000000000 --- a/src/Framework/MockObject/Exception/ReflectionException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReflectionException extends RuntimeException implements Exception -{ -} diff --git a/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php b/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php index 0b7bceb3fa9..cf193f10c14 100644 --- a/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php +++ b/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ReturnValueNotConfiguredException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Exception/RuntimeException.php b/src/Framework/MockObject/Exception/RuntimeException.php index 33b6a5be32f..b99a903e5d1 100644 --- a/src/Framework/MockObject/Exception/RuntimeException.php +++ b/src/Framework/MockObject/Exception/RuntimeException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class RuntimeException extends \RuntimeException implements Exception diff --git a/src/Framework/MockObject/Generator/DoubledClass.php b/src/Framework/MockObject/Generator/DoubledClass.php new file mode 100644 index 00000000000..3d88c72e331 --- /dev/null +++ b/src/Framework/MockObject/Generator/DoubledClass.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use function class_exists; +use PHPUnit\Framework\MockObject\ConfigurableMethod; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class DoubledClass +{ + private string $classCode; + + /** + * @var class-string + */ + private string $mockName; + + /** + * @var list + */ + private array $configurableMethods; + + /** + * @param class-string $mockName + * @param list $configurableMethods + */ + public function __construct(string $classCode, string $mockName, array $configurableMethods) + { + $this->classCode = $classCode; + $this->mockName = $mockName; + $this->configurableMethods = $configurableMethods; + } + + /** + * @return class-string + */ + public function generate(): string + { + if (!class_exists($this->mockName, false)) { + eval($this->classCode); + } + + return $this->mockName; + } + + public function classCode(): string + { + return $this->classCode; + } + + /** + * @return list + */ + public function configurableMethods(): array + { + return $this->configurableMethods; + } +} diff --git a/src/Framework/MockObject/Generator/DoubledMethod.php b/src/Framework/MockObject/Generator/DoubledMethod.php new file mode 100644 index 00000000000..09006b257c2 --- /dev/null +++ b/src/Framework/MockObject/Generator/DoubledMethod.php @@ -0,0 +1,395 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use function array_key_exists; +use function assert; +use function count; +use function explode; +use function implode; +use function is_object; +use function is_string; +use function preg_match; +use function preg_replace; +use function str_contains; +use function strlen; +use function strpos; +use function substr; +use function substr_count; +use function trim; +use function var_export; +use ReflectionMethod; +use ReflectionParameter; +use SebastianBergmann\Type\ReflectionMapper; +use SebastianBergmann\Type\Type; +use SebastianBergmann\Type\UnknownType; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DoubledMethod +{ + use TemplateLoader; + + /** + * @var class-string + */ + private readonly string $className; + + /** + * @var non-empty-string + */ + private readonly string $methodName; + private readonly string $modifier; + private readonly string $argumentsForDeclaration; + private readonly string $argumentsForCall; + private readonly Type $returnType; + private readonly string $reference; + private readonly bool $static; + private readonly ?string $deprecation; + + /** + * @var array + */ + private readonly array $defaultParameterValues; + + /** + * @var non-negative-int + */ + private readonly int $numberOfParameters; + + /** + * @throws ReflectionException + * @throws RuntimeException + */ + public static function fromReflection(ReflectionMethod $method): self + { + if ($method->isPrivate()) { + $modifier = 'private'; + } elseif ($method->isProtected()) { + $modifier = 'protected'; + } else { + $modifier = 'public'; + } + + if ($method->isStatic()) { + $modifier .= ' static'; + } + + if ($method->returnsReference()) { + $reference = '&'; + } else { + $reference = ''; + } + + $docComment = $method->getDocComment(); + + if (is_string($docComment) && + preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation) > 0 + ) { + $deprecation = trim(preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1])); + } else { + $deprecation = null; + } + + return new self( + $method->getDeclaringClass()->getName(), + $method->getName(), + $modifier, + self::methodParametersForDeclaration($method), + self::methodParametersForCall($method), + self::methodParametersDefaultValues($method), + count($method->getParameters()), + (new ReflectionMapper)->fromReturnType($method), + $reference, + $method->isStatic(), + $deprecation, + ); + } + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + public static function fromName(string $className, string $methodName): self + { + return new self( + $className, + $methodName, + 'public', + '', + '', + [], + 0, + new UnknownType, + '', + false, + null, + ); + } + + /** + * @param class-string $className + * @param non-empty-string $methodName + * @param array $defaultParameterValues + * @param non-negative-int $numberOfParameters + */ + private function __construct(string $className, string $methodName, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, array $defaultParameterValues, int $numberOfParameters, Type $returnType, string $reference, bool $static, ?string $deprecation) + { + $this->className = $className; + $this->methodName = $methodName; + $this->modifier = $modifier; + $this->argumentsForDeclaration = $argumentsForDeclaration; + $this->argumentsForCall = $argumentsForCall; + $this->defaultParameterValues = $defaultParameterValues; + $this->numberOfParameters = $numberOfParameters; + $this->returnType = $returnType; + $this->reference = $reference; + $this->static = $static; + $this->deprecation = $deprecation; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } + + /** + * @throws RuntimeException + */ + public function generateCode(): string + { + if ($this->static) { + $templateFile = 'doubled_static_method.tpl'; + } else { + $templateFile = 'doubled_method.tpl'; + } + + $deprecation = $this->deprecation; + $returnResult = ''; + + if (!$this->returnType->isNever() && !$this->returnType->isVoid()) { + $returnResult = <<<'EOT' + + + return $__phpunit_result; +EOT; + } + + if (null !== $this->deprecation) { + $deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation})."; + $deprecationTemplate = $this->loadTemplate('deprecation.tpl'); + + $deprecationTemplate->setVar( + [ + 'deprecation' => var_export($deprecation, true), + ], + ); + + $deprecation = $deprecationTemplate->render(); + } + + $template = $this->loadTemplate($templateFile); + + $argumentsCount = 0; + + if (str_contains($this->argumentsForCall, '...')) { + $argumentsCount = null; + } elseif ($this->argumentsForCall !== '') { + $argumentsCount = substr_count($this->argumentsForCall, ',') + 1; + } + + $returnDeclaration = ''; + $returnTypeAsString = $this->returnType->asString(); + + if ($returnTypeAsString !== '') { + $returnDeclaration = ': ' . $returnTypeAsString; + } + + $template->setVar( + [ + 'arguments_decl' => $this->argumentsForDeclaration, + 'arguments_call' => $this->argumentsForCall, + 'return_declaration' => $returnDeclaration, + 'return_type' => $this->returnType->asString(), + 'arguments_count' => (string) $argumentsCount, + 'class_name' => $this->className, + 'method_name' => $this->methodName, + 'modifier' => $this->modifier, + 'reference' => $this->reference, + 'deprecation' => $deprecation, + 'return_result' => $returnResult, + ], + ); + + return $template->render(); + } + + public function returnType(): Type + { + return $this->returnType; + } + + /** + * @return array + */ + public function defaultParameterValues(): array + { + return $this->defaultParameterValues; + } + + /** + * @return non-negative-int + */ + public function numberOfParameters(): int + { + return $this->numberOfParameters; + } + + /** + * Returns the parameters of a function or method. + * + * @throws RuntimeException + */ + private static function methodParametersForDeclaration(ReflectionMethod $method): string + { + $parameters = []; + $types = (new ReflectionMapper)->fromParameterTypes($method); + + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + + $default = ''; + $reference = ''; + $typeDeclaration = ''; + + assert(array_key_exists($i, $types)); + + if (!$types[$i]->type()->isUnknown()) { + $typeDeclaration = $types[$i]->type()->asString() . ' '; + } + + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + + if ($parameter->isVariadic()) { + $name = '...' . $name; + } elseif ($parameter->isDefaultValueAvailable()) { + $default = ' = ' . self::exportDefaultValue($parameter); + } elseif ($parameter->isOptional()) { + $default = ' = null'; + } + + $parameters[] = $typeDeclaration . $reference . $name . $default; + } + + return implode(', ', $parameters); + } + + /** + * Returns the parameters of a function or method. + * + * @throws ReflectionException + */ + private static function methodParametersForCall(ReflectionMethod $method): string + { + $parameters = []; + + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + + if ($parameter->isVariadic()) { + continue; + } + + if ($parameter->isPassedByReference()) { + $parameters[] = '&' . $name; + } else { + $parameters[] = $name; + } + } + + return implode(', ', $parameters); + } + + /** + * @throws ReflectionException + */ + private static function exportDefaultValue(ReflectionParameter $parameter): string + { + try { + $defaultValue = $parameter->getDefaultValue(); + + if (!is_object($defaultValue)) { + return var_export($defaultValue, true); + } + + $parameterAsString = $parameter->__toString(); + + return explode( + ' = ', + substr( + substr( + $parameterAsString, + strpos($parameterAsString, ' ') + strlen(' '), + ), + 0, + -2, + ), + )[1]; + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException( + $e->getMessage(), + $e->getCode(), + $e, + ); + } + // @codeCoverageIgnoreEnd + } + + /** + * @return array + */ + private static function methodParametersDefaultValues(ReflectionMethod $method): array + { + $result = []; + + foreach ($method->getParameters() as $i => $parameter) { + if (!$parameter->isDefaultValueAvailable()) { + continue; + } + + $result[$i] = $parameter->getDefaultValue(); + } + + return $result; + } +} diff --git a/src/Framework/MockObject/Generator/DoubledMethodSet.php b/src/Framework/MockObject/Generator/DoubledMethodSet.php new file mode 100644 index 00000000000..6a2d29f4f7d --- /dev/null +++ b/src/Framework/MockObject/Generator/DoubledMethodSet.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use function array_key_exists; +use function array_values; +use function strtolower; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class DoubledMethodSet +{ + /** + * @var array + */ + private array $methods = []; + + public function addMethods(DoubledMethod ...$methods): void + { + foreach ($methods as $method) { + $this->methods[strtolower($method->methodName())] = $method; + } + } + + /** + * @return list + */ + public function asArray(): array + { + return array_values($this->methods); + } + + public function hasMethod(string $methodName): bool + { + return array_key_exists(strtolower($methodName), $this->methods); + } +} diff --git a/src/Framework/MockObject/Generator/Exception/ClassAlreadyExistsException.php b/src/Framework/MockObject/Generator/Exception/ClassAlreadyExistsException.php deleted file mode 100644 index 4ff72ab5c7b..00000000000 --- a/src/Framework/MockObject/Generator/Exception/ClassAlreadyExistsException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function sprintf; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ClassAlreadyExistsException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct(string $className) - { - parent::__construct( - sprintf( - 'Class "%s" already exists', - $className, - ), - ); - } -} diff --git a/src/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.php b/src/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.php index 96137c416a3..e2cde18b68b 100644 --- a/src/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.php +++ b/src/Framework/MockObject/Generator/Exception/ClassIsEnumerationException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ClassIsEnumerationException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Generator/Exception/ClassIsFinalException.php b/src/Framework/MockObject/Generator/Exception/ClassIsFinalException.php index de9e9430a8b..f10100b90de 100644 --- a/src/Framework/MockObject/Generator/Exception/ClassIsFinalException.php +++ b/src/Framework/MockObject/Generator/Exception/ClassIsFinalException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ClassIsFinalException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Generator/Exception/ClassIsReadonlyException.php b/src/Framework/MockObject/Generator/Exception/ClassIsReadonlyException.php deleted file mode 100644 index 7cf07fe9c79..00000000000 --- a/src/Framework/MockObject/Generator/Exception/ClassIsReadonlyException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function sprintf; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ClassIsReadonlyException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct(string $className) - { - parent::__construct( - sprintf( - 'Class "%s" is declared "readonly" and cannot be doubled', - $className, - ), - ); - } -} diff --git a/src/Framework/MockObject/Generator/Exception/DuplicateMethodException.php b/src/Framework/MockObject/Generator/Exception/DuplicateMethodException.php index 71bed992233..b18ed4f561e 100644 --- a/src/Framework/MockObject/Generator/Exception/DuplicateMethodException.php +++ b/src/Framework/MockObject/Generator/Exception/DuplicateMethodException.php @@ -15,12 +15,14 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DuplicateMethodException extends \PHPUnit\Framework\Exception implements Exception { /** - * @psalm-param list $methods + * @param list $methods */ public function __construct(array $methods) { diff --git a/src/Framework/MockObject/Generator/Exception/Exception.php b/src/Framework/MockObject/Generator/Exception/Exception.php index 9a3c25884d2..8d62606fca2 100644 --- a/src/Framework/MockObject/Generator/Exception/Exception.php +++ b/src/Framework/MockObject/Generator/Exception/Exception.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Exception as BaseException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends BaseException diff --git a/src/Framework/MockObject/Generator/Exception/InvalidMethodNameException.php b/src/Framework/MockObject/Generator/Exception/InvalidMethodNameException.php index 4a7bd68e9b2..32296ce3940 100644 --- a/src/Framework/MockObject/Generator/Exception/InvalidMethodNameException.php +++ b/src/Framework/MockObject/Generator/Exception/InvalidMethodNameException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidMethodNameException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Generator/Exception/MethodNamedMethodException.php b/src/Framework/MockObject/Generator/Exception/MethodNamedMethodException.php new file mode 100644 index 00000000000..78586fe1067 --- /dev/null +++ b/src/Framework/MockObject/Generator/Exception/MethodNamedMethodException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +/** + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class MethodNamedMethodException extends \PHPUnit\Framework\Exception implements Exception +{ + public function __construct() + { + parent::__construct('Doubling interfaces (or classes) that have a method named "method" is not supported.'); + } +} diff --git a/src/Framework/MockObject/Generator/Exception/NameAlreadyInUseException.php b/src/Framework/MockObject/Generator/Exception/NameAlreadyInUseException.php new file mode 100644 index 00000000000..eec81c065d5 --- /dev/null +++ b/src/Framework/MockObject/Generator/Exception/NameAlreadyInUseException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NameAlreadyInUseException extends \PHPUnit\Framework\Exception implements Exception +{ + /** + * @param class-string|trait-string $name + */ + public function __construct(string $name) + { + parent::__construct( + sprintf( + 'The name "%s" is already in use', + $name, + ), + ); + } +} diff --git a/src/Framework/MockObject/Generator/Exception/OriginalConstructorInvocationRequiredException.php b/src/Framework/MockObject/Generator/Exception/OriginalConstructorInvocationRequiredException.php deleted file mode 100644 index b91e26149de..00000000000 --- a/src/Framework/MockObject/Generator/Exception/OriginalConstructorInvocationRequiredException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class OriginalConstructorInvocationRequiredException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct() - { - parent::__construct('Proxying to original methods requires invoking the original constructor'); - } -} diff --git a/src/Framework/MockObject/Generator/Exception/ReflectionException.php b/src/Framework/MockObject/Generator/Exception/ReflectionException.php index 03efd52d808..f4a84f18e6e 100644 --- a/src/Framework/MockObject/Generator/Exception/ReflectionException.php +++ b/src/Framework/MockObject/Generator/Exception/ReflectionException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject\Generator; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ReflectionException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Generator/Exception/RuntimeException.php b/src/Framework/MockObject/Generator/Exception/RuntimeException.php index 0cca5fa5ab1..eed41c37a33 100644 --- a/src/Framework/MockObject/Generator/Exception/RuntimeException.php +++ b/src/Framework/MockObject/Generator/Exception/RuntimeException.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework\MockObject\Generator; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class RuntimeException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Generator/Exception/SoapExtensionNotAvailableException.php b/src/Framework/MockObject/Generator/Exception/SoapExtensionNotAvailableException.php deleted file mode 100644 index 37af4913145..00000000000 --- a/src/Framework/MockObject/Generator/Exception/SoapExtensionNotAvailableException.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class SoapExtensionNotAvailableException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct() - { - parent::__construct( - 'The SOAP extension is required to generate a test double from WSDL', - ); - } -} diff --git a/src/Framework/MockObject/Generator/Exception/UnknownClassException.php b/src/Framework/MockObject/Generator/Exception/UnknownClassException.php deleted file mode 100644 index 64c79e0a233..00000000000 --- a/src/Framework/MockObject/Generator/Exception/UnknownClassException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function sprintf; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class UnknownClassException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct(string $className) - { - parent::__construct( - sprintf( - 'Class "%s" does not exist', - $className, - ), - ); - } -} diff --git a/src/Framework/MockObject/Generator/Exception/UnknownTraitException.php b/src/Framework/MockObject/Generator/Exception/UnknownTraitException.php deleted file mode 100644 index 67cecfa9009..00000000000 --- a/src/Framework/MockObject/Generator/Exception/UnknownTraitException.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function sprintf; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5243 - */ -final class UnknownTraitException extends \PHPUnit\Framework\Exception implements Exception -{ - public function __construct(string $traitName) - { - parent::__construct( - sprintf( - 'Trait "%s" does not exist', - $traitName, - ), - ); - } -} diff --git a/src/Framework/MockObject/Generator/Exception/UnknownTypeException.php b/src/Framework/MockObject/Generator/Exception/UnknownTypeException.php index ccda69bbd80..cd1e1e072b1 100644 --- a/src/Framework/MockObject/Generator/Exception/UnknownTypeException.php +++ b/src/Framework/MockObject/Generator/Exception/UnknownTypeException.php @@ -12,6 +12,8 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class UnknownTypeException extends \PHPUnit\Framework\Exception implements Exception diff --git a/src/Framework/MockObject/Generator/Generator.php b/src/Framework/MockObject/Generator/Generator.php index ace914a3884..5175c158b12 100644 --- a/src/Framework/MockObject/Generator/Generator.php +++ b/src/Framework/MockObject/Generator/Generator.php @@ -10,8 +10,6 @@ namespace PHPUnit\Framework\MockObject\Generator; use const PHP_EOL; -use const PREG_OFFSET_CAPTURE; -use const WSDL_CACHE_NONE; use function array_merge; use function array_pop; use function array_unique; @@ -19,38 +17,24 @@ use function class_exists; use function count; use function explode; -use function extension_loaded; use function implode; use function in_array; use function interface_exists; use function is_array; -use function is_object; use function md5; use function method_exists; use function mt_rand; use function preg_match; -use function preg_match_all; -use function range; use function serialize; use function sort; use function sprintf; -use function str_contains; -use function str_replace; -use function strlen; -use function strpos; use function substr; use function trait_exists; use Exception; use Iterator; use IteratorAggregate; -use PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException; -use PHPUnit\Event\Code\TestMethodBuilder; -use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Framework\InvalidArgumentException; use PHPUnit\Framework\MockObject\ConfigurableMethod; use PHPUnit\Framework\MockObject\DoubledCloneMethod; -use PHPUnit\Framework\MockObject\GeneratedAsMockObject; -use PHPUnit\Framework\MockObject\GeneratedAsTestStub; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObjectApi; @@ -59,14 +43,20 @@ use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\StubInternal; +use PHPUnit\Framework\MockObject\TestDoubleState; +use PropertyHookType; use ReflectionClass; use ReflectionMethod; -use SoapClient; -use SoapFault; +use ReflectionObject; +use ReflectionProperty; +use SebastianBergmann\Type\ReflectionMapper; +use SebastianBergmann\Type\Type; use Throwable; use Traversable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Generator @@ -74,9 +64,9 @@ final class Generator use TemplateLoader; /** - * @var array + * @var non-empty-array */ - private const EXCLUDED_METHOD_NAMES = [ + private const array EXCLUDED_METHOD_NAMES = [ '__CLASS__' => true, '__DIR__' => true, '__FILE__' => true, @@ -90,60 +80,48 @@ final class Generator ]; /** - * @psalm-var array + * @var array */ private static array $cache = []; /** * Returns a test double for the specified class. * - * @throws ClassAlreadyExistsException + * @param class-string $type + * @param ?list $methods + * @param array $arguments + * * @throws ClassIsEnumerationException * @throws ClassIsFinalException - * @throws ClassIsReadonlyException * @throws DuplicateMethodException * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException + * @throws NameAlreadyInUseException * @throws ReflectionException * @throws RuntimeException * @throws UnknownTypeException */ - public function testDouble(string $type, bool $mockObject, bool $markAsMockObject, ?array $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false, object $proxyTarget = null, bool $allowMockingUnknownTypes = true, bool $returnValueGeneration = true): MockObject|Stub + public function testDouble(string $type, bool $mockObject, ?array $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $returnValueGeneration = true): MockObject|Stub { if ($type === Traversable::class) { $type = Iterator::class; } - if (!$allowMockingUnknownTypes) { - $this->ensureKnownType($type, $callAutoload); - } - + $this->ensureKnownType($type); $this->ensureValidMethods($methods); - $this->ensureMockedClassDoesNotAlreadyExist($mockClassName); - - if (!$callOriginalConstructor && $callOriginalMethods) { - throw new OriginalConstructorInvocationRequiredException; - } + $this->ensureNameForTestDoubleClassIsAvailable($mockClassName); $mock = $this->generate( $type, $mockObject, - $markAsMockObject, $methods, $mockClassName, $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods, ); - $object = $this->getObject( + $object = $this->instantiate( $mock, - $type, $callOriginalConstructor, $arguments, - $callOriginalMethods, - $proxyTarget, $returnValueGeneration, ); @@ -159,19 +137,19 @@ public function testDouble(string $type, bool $mockObject, bool $markAsMockObjec } /** - * @psalm-param list $interfaces + * @param list $interfaces * * @throws RuntimeException * @throws UnknownTypeException */ - public function testDoubleForInterfaceIntersection(array $interfaces, bool $mockObject, bool $callAutoload = true): MockObject|Stub + public function testDoubleForInterfaceIntersection(array $interfaces, bool $mockObject, bool $returnValueGeneration = true): MockObject|Stub { if (count($interfaces) < 2) { throw new RuntimeException('At least two interfaces must be specified'); } foreach ($interfaces as $interface) { - if (!interface_exists($interface, $callAutoload)) { + if (!interface_exists($interface)) { throw new UnknownTypeException($interface); } } @@ -216,168 +194,21 @@ public function testDoubleForInterfaceIntersection(array $interfaces, bool $mock eval($template->render()); - return $this->testDouble($intersectionName, $mockObject, $mockObject); - } + assert(interface_exists($intersectionName)); - /** - * Returns a mock object for the specified abstract class with all abstract - * methods of the class mocked. - * - * Concrete methods to mock can be specified with the $mockedMethods parameter. - * - * @throws ClassAlreadyExistsException - * @throws ClassIsEnumerationException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidArgumentException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownClassException - * @throws UnknownTypeException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5241 - */ - public function mockObjectForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = null, bool $cloneArguments = true): MockObject - { - if (class_exists($originalClassName, $callAutoload) || - interface_exists($originalClassName, $callAutoload)) { - $reflector = $this->reflectClass($originalClassName); - $methods = $mockedMethods; - - foreach ($reflector->getMethods() as $method) { - if ($method->isAbstract() && !in_array($method->getName(), $methods ?? [], true)) { - $methods[] = $method->getName(); - } - } - - if (empty($methods)) { - $methods = null; - } - - $mockObject = $this->testDouble( - $originalClassName, - true, - true, - $methods, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $cloneArguments, - ); - - assert($mockObject instanceof $originalClassName); - assert($mockObject instanceof MockObject); - - return $mockObject; - } - - throw new UnknownClassException($originalClassName); - } - - /** - * Returns a mock object for the specified trait with all abstract methods - * of the trait mocked. Concrete methods to mock can be specified with the - * `$mockedMethods` parameter. - * - * @psalm-param trait-string $traitName - * - * @throws ClassAlreadyExistsException - * @throws ClassIsEnumerationException - * @throws ClassIsFinalException - * @throws ClassIsReadonlyException - * @throws DuplicateMethodException - * @throws InvalidArgumentException - * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownClassException - * @throws UnknownTraitException - * @throws UnknownTypeException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5243 - */ - public function mockObjectForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = null, bool $cloneArguments = true): MockObject - { - if (!trait_exists($traitName, $callAutoload)) { - throw new UnknownTraitException($traitName); - } - - $className = $this->generateClassName( - $traitName, - '', - 'Trait_', - ); - - $classTemplate = $this->loadTemplate('trait_class.tpl'); - - $classTemplate->setVar( - [ - 'prologue' => 'abstract ', - 'class_name' => $className['className'], - 'trait_name' => $traitName, - ], + return $this->testDouble( + $intersectionName, + $mockObject, + returnValueGeneration: $returnValueGeneration, ); - - $mockTrait = new MockTrait($classTemplate->render(), $className['className']); - $mockTrait->generate(); - - return $this->mockObjectForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); } /** - * Returns an object for the specified trait. - * - * @psalm-param trait-string $traitName - * - * @throws ReflectionException - * @throws RuntimeException - * @throws UnknownTraitException + * @param class-string $type + * @param ?list $methods * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5244 - */ - public function objectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = true, bool $callOriginalConstructor = false, array $arguments = []): object - { - if (!trait_exists($traitName, $callAutoload)) { - throw new UnknownTraitException($traitName); - } - - $className = $this->generateClassName( - $traitName, - $traitClassName, - 'Trait_', - ); - - $classTemplate = $this->loadTemplate('trait_class.tpl'); - - $classTemplate->setVar( - [ - 'prologue' => '', - 'class_name' => $className['className'], - 'trait_name' => $traitName, - ], - ); - - return $this->getObject( - new MockTrait( - $classTemplate->render(), - $className['className'], - ), - '', - $callOriginalConstructor, - $arguments, - ); - } - - /** * @throws ClassIsEnumerationException * @throws ClassIsFinalException - * @throws ClassIsReadonlyException * @throws ReflectionException * @throws RuntimeException * @@ -385,43 +216,32 @@ public function objectForTrait(string $traitName, string $traitClassName = '', b * * @see https://github.com/sebastianbergmann/phpunit/issues/5476 */ - public function generate(string $type, bool $mockObject, bool $markAsMockObject, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): MockClass + public function generate(string $type, bool $mockObject, ?array $methods = null, string $mockClassName = '', bool $callOriginalClone = true): DoubledClass { if ($mockClassName !== '') { return $this->generateCodeForTestDoubleClass( $type, $mockObject, - $markAsMockObject, $methods, $mockClassName, $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods, ); } $key = md5( $type . ($mockObject ? 'MockObject' : 'TestStub') . - ($markAsMockObject ? 'MockObject' : 'TestStub') . serialize($methods) . - serialize($callOriginalClone) . - serialize($cloneArguments) . - serialize($callOriginalMethods), + serialize($callOriginalClone), ); if (!isset(self::$cache[$key])) { self::$cache[$key] = $this->generateCodeForTestDoubleClass( $type, $mockObject, - $markAsMockObject, $methods, $mockClassName, $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods, ); } @@ -429,115 +249,20 @@ public function generate(string $type, bool $mockObject, bool $markAsMockObject, } /** - * @throws RuntimeException - * @throws SoapExtensionNotAvailableException + * @param class-string $className * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5242 - */ - public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []): string - { - if (!extension_loaded('soap')) { - throw new SoapExtensionNotAvailableException; - } - - $options['cache_wsdl'] = WSDL_CACHE_NONE; - - try { - $client = new SoapClient($wsdlFile, $options); - $_methods = array_unique($client->__getFunctions()); - - unset($client); - } catch (SoapFault $e) { - throw new RuntimeException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - - sort($_methods); - - $methodTemplate = $this->loadTemplate('wsdl_method.tpl'); - $methodsBuffer = ''; - - foreach ($_methods as $method) { - preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(/', $method, $matches, PREG_OFFSET_CAPTURE); - - $lastFunction = array_pop($matches[0]); - $nameStart = $lastFunction[1]; - $nameEnd = $nameStart + strlen($lastFunction[0]) - 1; - $name = str_replace('(', '', $lastFunction[0]); - - if (empty($methods) || in_array($name, $methods, true)) { - $arguments = explode( - ',', - str_replace(')', '', substr($method, $nameEnd + 1)), - ); - - foreach (range(0, count($arguments) - 1) as $i) { - $parameterStart = strpos($arguments[$i], '$'); - - if (!$parameterStart) { - continue; - } - - $arguments[$i] = substr($arguments[$i], $parameterStart); - } - - $methodTemplate->setVar( - [ - 'method_name' => $name, - 'arguments' => implode(', ', $arguments), - ], - ); - - $methodsBuffer .= $methodTemplate->render(); - } - } - - $optionsBuffer = '['; - - foreach ($options as $key => $value) { - $optionsBuffer .= $key . ' => ' . $value; - } - - $optionsBuffer .= ']'; - - $classTemplate = $this->loadTemplate('wsdl_class.tpl'); - $namespace = ''; - - if (str_contains($className, '\\')) { - $parts = explode('\\', $className); - $className = array_pop($parts); - $namespace = 'namespace ' . implode('\\', $parts) . ';' . "\n\n"; - } - - $classTemplate->setVar( - [ - 'namespace' => $namespace, - 'class_name' => $className, - 'wsdl' => $wsdlFile, - 'options' => $optionsBuffer, - 'methods' => $methodsBuffer, - ], - ); - - return $classTemplate->render(); - } - - /** * @throws ReflectionException * - * @psalm-return list + * @return list */ - public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments): array + private function mockClassMethods(string $className): array { $class = $this->reflectClass($className); $methods = []; foreach ($class->getMethods() as $method) { if (($method->isPublic() || $method->isAbstract()) && $this->canMethodBeDoubled($method)) { - $methods[] = MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); + $methods[] = DoubledMethod::fromReflection($method); } } @@ -545,11 +270,11 @@ public function mockClassMethods(string $className, bool $callOriginalMethods, b } /** - * @psalm-param class-string $interfaceName + * @param class-string $interfaceName * * @throws ReflectionException * - * @psalm-return list + * @return list */ private function userDefinedInterfaceMethods(string $interfaceName): array { @@ -568,42 +293,74 @@ private function userDefinedInterfaceMethods(string $interfaceName): array } /** + * @param array $arguments + * * @throws ReflectionException * @throws RuntimeException */ - private function getObject(MockType $mockClass, string $type = '', bool $callOriginalConstructor = false, array $arguments = [], bool $callOriginalMethods = false, object $proxyTarget = null, bool $returnValueGeneration = true): object + private function instantiate(DoubledClass $mockClass, bool $callOriginalConstructor = false, array $arguments = [], bool $returnValueGeneration = true): object { $className = $mockClass->generate(); - $object = $this->instantiate($className, $callOriginalConstructor, $arguments); - if ($callOriginalMethods) { - $this->instantiateProxyTarget($proxyTarget, $object, $type, $arguments); + try { + $object = (new ReflectionClass($className))->newInstanceWithoutConstructor(); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException( + $e->getMessage(), + $e->getCode(), + $e, + ); + // @codeCoverageIgnoreEnd } - if ($object instanceof StubInternal) { - $object->__phpunit_setReturnValueGeneration($returnValueGeneration); + $reflector = new ReflectionObject($object); + + /** + * @noinspection PhpUnhandledExceptionInspection + */ + $reflector->getProperty('__phpunit_state')->setValue( + $object, + new TestDoubleState($mockClass->configurableMethods(), $returnValueGeneration), + ); + + if ($callOriginalConstructor && $reflector->getConstructor() !== null) { + try { + $reflector->getConstructor()->invokeArgs($object, $arguments); + // @codeCoverageIgnoreStart + } catch (\ReflectionException $e) { + throw new ReflectionException( + $e->getMessage(), + $e->getCode(), + $e, + ); + // @codeCoverageIgnoreEnd + } } return $object; } /** + * @param class-string $type + * @param ?list $explicitMethods + * * @throws ClassIsEnumerationException * @throws ClassIsFinalException - * @throws ClassIsReadonlyException + * @throws MethodNamedMethodException * @throws ReflectionException * @throws RuntimeException */ - private function generateCodeForTestDoubleClass(string $type, bool $mockObject, bool $markAsMockObject, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): MockClass + private function generateCodeForTestDoubleClass(string $type, bool $mockObject, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone): DoubledClass { $classTemplate = $this->loadTemplate('test_double_class.tpl'); $additionalInterfaces = []; $doubledCloneMethod = false; $proxiedCloneMethod = false; $isClass = false; + $isReadonly = false; $isInterface = false; - $class = null; - $mockMethods = new MockMethodSet; + $mockMethods = new DoubledMethodSet; $testDoubleClassPrefix = $mockObject ? 'MockObject_' : 'TestStub_'; $_mockClassName = $this->generateClassName( @@ -612,172 +369,139 @@ private function generateCodeForTestDoubleClass(string $type, bool $mockObject, $testDoubleClassPrefix, ); - if (class_exists($_mockClassName['fullClassName'], $callAutoload)) { + if (class_exists($_mockClassName['fullClassName'])) { $isClass = true; - } elseif (interface_exists($_mockClassName['fullClassName'], $callAutoload)) { + } elseif (interface_exists($_mockClassName['fullClassName'])) { $isInterface = true; } - if (!$isClass && !$isInterface) { - $prologue = 'class ' . $_mockClassName['originalClassName'] . "\n{\n}\n\n"; - - if (!empty($_mockClassName['namespaceName'])) { - $prologue = 'namespace ' . $_mockClassName['namespaceName'] . - " {\n\n" . $prologue . "}\n\n" . - "namespace {\n\n"; + $class = $this->reflectClass($_mockClassName['fullClassName']); - $epilogue = "\n\n}"; - } - - $doubledCloneMethod = true; - } else { - $class = $this->reflectClass($_mockClassName['fullClassName']); - - if ($class->isEnum()) { - throw new ClassIsEnumerationException($_mockClassName['fullClassName']); - } + if ($class->isEnum()) { + throw new ClassIsEnumerationException($_mockClassName['fullClassName']); + } - if ($class->isFinal()) { - throw new ClassIsFinalException($_mockClassName['fullClassName']); - } + if ($class->isFinal()) { + throw new ClassIsFinalException($_mockClassName['fullClassName']); + } - if (method_exists($class, 'isReadOnly') && $class->isReadOnly()) { - throw new ClassIsReadonlyException($_mockClassName['fullClassName']); - } + if ($class->isReadOnly()) { + $isReadonly = true; + } - // @see https://github.com/sebastianbergmann/phpunit/issues/2995 - if ($isInterface && $class->implementsInterface(Throwable::class)) { - $actualClassName = Exception::class; - $additionalInterfaces[] = $class->getName(); - $isInterface = false; - $class = $this->reflectClass($actualClassName); + // @see https://github.com/sebastianbergmann/phpunit/issues/2995 + if ($isInterface && $class->implementsInterface(Throwable::class)) { + $actualClassName = Exception::class; + $additionalInterfaces[] = $class->getName(); + $isInterface = false; + $class = $this->reflectClass($actualClassName); - foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { - $methodName = $method->getName(); + foreach ($this->userDefinedInterfaceMethods($_mockClassName['fullClassName']) as $method) { + $methodName = $method->getName(); - if ($class->hasMethod($methodName)) { - $classMethod = $class->getMethod($methodName); + if ($class->hasMethod($methodName)) { + $classMethod = $class->getMethod($methodName); - if (!$this->canMethodBeDoubled($classMethod)) { - continue; - } + if (!$this->canMethodBeDoubled($classMethod)) { + continue; } - - $mockMethods->addMethods( - MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments), - ); } - $_mockClassName = $this->generateClassName( - $actualClassName, - $_mockClassName['className'], - $testDoubleClassPrefix, + $mockMethods->addMethods( + DoubledMethod::fromReflection($method), ); } - // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 - if ($isInterface && $class->implementsInterface(Traversable::class) && - !$class->implementsInterface(Iterator::class) && - !$class->implementsInterface(IteratorAggregate::class)) { - $additionalInterfaces[] = Iterator::class; + $_mockClassName = $this->generateClassName( + $actualClassName, + $_mockClassName['className'], + $testDoubleClassPrefix, + ); + } + + // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 + if ($isInterface && $class->implementsInterface(Traversable::class) && + !$class->implementsInterface(Iterator::class) && + !$class->implementsInterface(IteratorAggregate::class)) { + $additionalInterfaces[] = Iterator::class; - $mockMethods->addMethods( - ...$this->mockClassMethods(Iterator::class, $callOriginalMethods, $cloneArguments), - ); - } + $mockMethods->addMethods( + ...$this->mockClassMethods(Iterator::class), + ); + } - if ($class->hasMethod('__clone')) { - $cloneMethod = $class->getMethod('__clone'); + if ($class->hasMethod('__clone')) { + $cloneMethod = $class->getMethod('__clone'); - if (!$cloneMethod->isFinal()) { - if ($callOriginalClone && !$isInterface) { - $proxiedCloneMethod = true; - } else { - $doubledCloneMethod = true; - } + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $proxiedCloneMethod = true; + } else { + $doubledCloneMethod = true; } - } else { - $doubledCloneMethod = true; } + } else { + $doubledCloneMethod = true; } if ($isClass && $explicitMethods === []) { $mockMethods->addMethods( - ...$this->mockClassMethods($_mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments), + ...$this->mockClassMethods($_mockClassName['fullClassName']), ); } if ($isInterface && ($explicitMethods === [] || $explicitMethods === null)) { $mockMethods->addMethods( - ...$this->interfaceMethods($_mockClassName['fullClassName'], $cloneArguments), + ...$this->interfaceMethods($_mockClassName['fullClassName']), ); } if (is_array($explicitMethods)) { foreach ($explicitMethods as $methodName) { - if ($class !== null && $class->hasMethod($methodName)) { + if ($class->hasMethod($methodName)) { $method = $class->getMethod($methodName); if ($this->canMethodBeDoubled($method)) { $mockMethods->addMethods( - MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments), + DoubledMethod::fromReflection($method), ); } } else { $mockMethods->addMethods( - MockMethod::fromName( + DoubledMethod::fromName( $_mockClassName['fullClassName'], $methodName, - $cloneArguments, ), ); } } } + $propertiesWithHooks = $this->properties($class); + $configurableMethods = $this->configurableMethods($mockMethods, $propertiesWithHooks); + $mockedMethods = ''; - $configurable = []; foreach ($mockMethods->asArray() as $mockMethod) { $mockedMethods .= $mockMethod->generateCode(); - $configurable[] = new ConfigurableMethod($mockMethod->methodName(), $mockMethod->returnType()); } - /** @psalm-var trait-string[] $traits */ + /** @var trait-string[] $traits */ $traits = [StubApi::class]; if ($mockObject) { $traits[] = MockObjectApi::class; } - if ($markAsMockObject) { - $traits[] = GeneratedAsMockObject::class; - } else { - $traits[] = GeneratedAsTestStub::class; - } - - if ($mockMethods->hasMethod('method') || (isset($class) && $class->hasMethod('method'))) { - $message = 'Doubling interfaces (or classes) that have a method named "method" is deprecated. Support for this will be removed in PHPUnit 12.'; - - try { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - TestMethodBuilder::fromCallStack(), - $message, - ); - } catch (NoTestCaseObjectOnCallStackException) { - EventFacade::emitter()->testRunnerTriggeredDeprecation($message); - } + if ($mockMethods->hasMethod('method') || $class->hasMethod('method')) { + throw new MethodNamedMethodException; } - if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { - $traits[] = Method::class; - } + $traits[] = Method::class; if ($doubledCloneMethod) { $traits[] = DoubledCloneMethod::class; - } - - if ($proxiedCloneMethod) { + } elseif ($proxiedCloneMethod) { $traits[] = ProxiedCloneMethod::class; } @@ -794,27 +518,35 @@ private function generateCodeForTestDoubleClass(string $type, bool $mockObject, $classTemplate->setVar( [ - 'prologue' => $prologue ?? '', - 'epilogue' => $epilogue ?? '', 'class_declaration' => $this->generateTestDoubleClassDeclaration( $mockObject, $_mockClassName, $isInterface, $additionalInterfaces, + $isReadonly, ), 'use_statements' => $useStatements, 'mock_class_name' => $_mockClassName['className'], - 'mocked_methods' => $mockedMethods, + 'methods' => $mockedMethods, + 'property_hooks' => (new HookedPropertyGenerator)->generate( + $_mockClassName['className'], + $propertiesWithHooks, + ), ], ); - return new MockClass( + return new DoubledClass( $classTemplate->render(), $_mockClassName['className'], - $configurable, + $configurableMethods, ); } + /** + * @param class-string $type + * + * @return array{className: class-string, originalClassName: class-string, fullClassName: class-string, namespaceName: string} + */ private function generateClassName(string $type, string $className, string $prefix): array { if ($type[0] === '\\') { @@ -847,7 +579,11 @@ private function generateClassName(string $type, string $className, string $pref ]; } - private function generateTestDoubleClassDeclaration(bool $mockObject, array $mockClassName, bool $isInterface, array $additionalInterfaces = []): string + /** + * @param array{className: non-empty-string, originalClassName: non-empty-string, fullClassName: non-empty-string, namespaceName: string} $mockClassName + * @param list $additionalInterfaces + */ + private function generateTestDoubleClassDeclaration(bool $mockObject, array $mockClassName, bool $isInterface, array $additionalInterfaces, bool $isReadonly): string { if ($mockObject) { $additionalInterfaces[] = MockObjectInternal::class; @@ -855,7 +591,12 @@ private function generateTestDoubleClassDeclaration(bool $mockObject, array $moc $additionalInterfaces[] = StubInternal::class; } - $buffer = 'class '; + if ($isReadonly) { + $buffer = 'readonly class '; + } else { + $buffer = 'class '; + } + $interfaces = implode(', ', $additionalInterfaces); if ($isInterface) { @@ -868,7 +609,7 @@ private function generateTestDoubleClassDeclaration(bool $mockObject, array $moc if (!in_array($mockClassName['originalClassName'], $additionalInterfaces, true)) { $buffer .= ', '; - if (!empty($mockClassName['namespaceName'])) { + if ($mockClassName['namespaceName'] !== '') { $buffer .= $mockClassName['namespaceName'] . '\\'; } @@ -878,7 +619,7 @@ private function generateTestDoubleClassDeclaration(bool $mockObject, array $moc $buffer .= sprintf( '%s extends %s%s implements %s', $mockClassName['className'], - !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', + $mockClassName['namespaceName'] !== '' ? $mockClassName['namespaceName'] . '\\' : '', $mockClassName['originalClassName'], $interfaces, ); @@ -916,14 +657,16 @@ private function isMethodNameExcluded(string $name): bool /** * @throws UnknownTypeException */ - private function ensureKnownType(string $type, bool $callAutoload): void + private function ensureKnownType(string $type): void { - if (!class_exists($type, $callAutoload) && !interface_exists($type, $callAutoload)) { + if (!class_exists($type) && !interface_exists($type)) { throw new UnknownTypeException($type); } } /** + * @param ?list $methods + * * @throws DuplicateMethodException * @throws InvalidMethodNameException */ @@ -945,100 +688,36 @@ private function ensureValidMethods(?array $methods): void } /** - * @throws ClassAlreadyExistsException - * @throws ReflectionException - */ - private function ensureMockedClassDoesNotAlreadyExist(string $mockClassName): void - { - if ($mockClassName !== '' && class_exists($mockClassName, false)) { - $reflector = $this->reflectClass($mockClassName); - - if (!$reflector->implementsInterface(MockObject::class)) { - throw new ClassAlreadyExistsException($mockClassName); - } - } - } - - /** - * @psalm-param class-string $className - * + * @throws NameAlreadyInUseException * @throws ReflectionException */ - private function instantiate(string $className, bool $callOriginalConstructor, array $arguments): object + private function ensureNameForTestDoubleClassIsAvailable(string $className): void { - if ($callOriginalConstructor) { - if (count($arguments) === 0) { - return new $className; - } - - try { - return (new ReflectionClass($className))->newInstanceArgs($arguments); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd + if ($className === '') { + return; } - try { - return (new ReflectionClass($className))->newInstanceWithoutConstructor(); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - // @codeCoverageIgnoreEnd + if (class_exists($className, false) || + interface_exists($className, false) || + trait_exists($className, false)) { + throw new NameAlreadyInUseException($className); } } /** - * @psalm-param class-string $type + * @param class-string $className * * @throws ReflectionException - */ - private function instantiateProxyTarget(?object $proxyTarget, object $object, string $type, array $arguments): void - { - if (!is_object($proxyTarget)) { - assert(class_exists($type)); - - if (count($arguments) === 0) { - $proxyTarget = new $type; - } else { - $class = new ReflectionClass($type); - - try { - $proxyTarget = $class->newInstanceArgs($arguments); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd - } - } - - $object->__phpunit_setOriginalObject($proxyTarget); - } - - /** - * @psalm-param class-string $className * - * @throws ReflectionException + * @phpstan-ignore missingType.generics, throws.unusedType */ private function reflectClass(string $className): ReflectionClass { try { $class = new ReflectionClass($className); + // @codeCoverageIgnoreStart + /** @phpstan-ignore catch.neverThrown */ } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), @@ -1052,11 +731,11 @@ private function reflectClass(string $className): ReflectionClass } /** - * @psalm-param class-string $classOrInterfaceName - * - * @psalm-return list + * @param class-string $classOrInterfaceName * * @throws ReflectionException + * + * @return list */ private function namesOfMethodsIn(string $classOrInterfaceName): array { @@ -1073,21 +752,135 @@ private function namesOfMethodsIn(string $classOrInterfaceName): array } /** - * @psalm-param class-string $interfaceName - * - * @psalm-return list + * @param class-string $interfaceName * * @throws ReflectionException + * + * @return list */ - private function interfaceMethods(string $interfaceName, bool $cloneArguments): array + private function interfaceMethods(string $interfaceName): array { $class = $this->reflectClass($interfaceName); $methods = []; foreach ($class->getMethods() as $method) { - $methods[] = MockMethod::fromReflection($method, false, $cloneArguments); + $methods[] = DoubledMethod::fromReflection($method); } return $methods; } + + /** + * @param list $propertiesWithHooks + * + * @return list + */ + private function configurableMethods(DoubledMethodSet $methods, array $propertiesWithHooks): array + { + $configurable = []; + + foreach ($methods->asArray() as $method) { + $configurable[] = new ConfigurableMethod( + $method->methodName(), + $method->defaultParameterValues(), + $method->numberOfParameters(), + $method->returnType(), + ); + } + + foreach ($propertiesWithHooks as $property) { + if ($property->hasGetHook()) { + $configurable[] = new ConfigurableMethod( + sprintf( + '$%s::get', + $property->name(), + ), + [], + 0, + $property->type(), + ); + } + + if ($property->hasSetHook()) { + $configurable[] = new ConfigurableMethod( + sprintf( + '$%s::set', + $property->name(), + ), + [], + 1, + Type::fromName('void', false), + ); + } + } + + return $configurable; + } + + /** + * @param ?ReflectionClass $class + * + * @return list + */ + private function properties(?ReflectionClass $class): array + { + if (!method_exists(ReflectionProperty::class, 'isFinal')) { + // @codeCoverageIgnoreStart + return []; + // @codeCoverageIgnoreEnd + } + + if ($class === null) { + return []; + } + + $mapper = new ReflectionMapper; + $properties = []; + + foreach ($class->getProperties() as $property) { + assert(method_exists($property, 'getHook')); + assert(method_exists($property, 'hasHooks')); + assert(method_exists($property, 'hasHook')); + assert(method_exists($property, 'isFinal')); + assert(class_exists(PropertyHookType::class)); + + if (!$property->isPublic()) { + continue; + } + + if ($property->isFinal()) { + continue; + } + + if (!$property->hasHooks()) { + continue; + } + + $hasGetHook = false; + $hasSetHook = false; + + if ($property->hasHook(PropertyHookType::Get) && + !$property->getHook(PropertyHookType::Get)->isFinal()) { + $hasGetHook = true; + } + + if ($property->hasHook(PropertyHookType::Set) && + !$property->getHook(PropertyHookType::Set)->isFinal()) { + $hasSetHook = true; + } + + if (!$hasGetHook && !$hasSetHook) { + continue; + } + + $properties[] = new HookedProperty( + $property->getName(), + $mapper->fromPropertyType($property), + $hasGetHook, + $hasSetHook, + ); + } + + return $properties; + } } diff --git a/src/Framework/MockObject/Generator/HookedProperty.php b/src/Framework/MockObject/Generator/HookedProperty.php new file mode 100644 index 00000000000..e43d589d7e7 --- /dev/null +++ b/src/Framework/MockObject/Generator/HookedProperty.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use SebastianBergmann\Type\Type; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class HookedProperty +{ + /** + * @var non-empty-string + */ + private string $name; + private Type $type; + private bool $getHook; + private bool $setHook; + + /** + * @param non-empty-string $name + */ + public function __construct(string $name, Type $type, bool $getHook, bool $setHook) + { + $this->name = $name; + $this->type = $type; + $this->getHook = $getHook; + $this->setHook = $setHook; + } + + public function name(): string + { + return $this->name; + } + + public function type(): Type + { + return $this->type; + } + + public function hasGetHook(): bool + { + return $this->getHook; + } + + public function hasSetHook(): bool + { + return $this->setHook; + } +} diff --git a/src/Framework/MockObject/Generator/HookedPropertyGenerator.php b/src/Framework/MockObject/Generator/HookedPropertyGenerator.php new file mode 100644 index 00000000000..4fcff6c824d --- /dev/null +++ b/src/Framework/MockObject/Generator/HookedPropertyGenerator.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class HookedPropertyGenerator +{ + /** + * @param class-string $className + * @param list $properties + */ + public function generate(string $className, array $properties): string + { + $code = ''; + + foreach ($properties as $property) { + $code .= sprintf( + <<<'EOT' + + public %s $%s { +EOT, + $property->type()->asString(), + $property->name(), + ); + + if ($property->hasGetHook()) { + $code .= sprintf( + <<<'EOT' + + get { + return $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '%s', '$%s::get', [], '%s', $this + ) + ); + } + +EOT, + $className, + $property->name(), + $property->type()->asString(), + ); + } + + if ($property->hasSetHook()) { + $code .= sprintf( + <<<'EOT' + + set (%s $value) { + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + '%s', '$%s::set', [$value], 'void', $this + ) + ); + } + +EOT, + $property->type()->asString(), + $className, + $property->name(), + ); + } + + $code .= <<<'EOT' + } + +EOT; + } + + return $code; + } +} diff --git a/src/Framework/MockObject/Generator/MockClass.php b/src/Framework/MockObject/Generator/MockClass.php deleted file mode 100644 index 99bfe3ca749..00000000000 --- a/src/Framework/MockObject/Generator/MockClass.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function call_user_func; -use function class_exists; -use PHPUnit\Framework\MockObject\ConfigurableMethod; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final readonly class MockClass implements MockType -{ - private string $classCode; - - /** - * @psalm-var class-string - */ - private string $mockName; - - /** - * @psalm-var list - */ - private array $configurableMethods; - - /** - * @psalm-param class-string $mockName - * @psalm-param list $configurableMethods - */ - public function __construct(string $classCode, string $mockName, array $configurableMethods) - { - $this->classCode = $classCode; - $this->mockName = $mockName; - $this->configurableMethods = $configurableMethods; - } - - /** - * @psalm-return class-string - */ - public function generate(): string - { - if (!class_exists($this->mockName, false)) { - eval($this->classCode); - - call_user_func( - [ - $this->mockName, - '__phpunit_initConfigurableMethods', - ], - ...$this->configurableMethods, - ); - } - - return $this->mockName; - } - - public function classCode(): string - { - return $this->classCode; - } -} diff --git a/src/Framework/MockObject/Generator/MockMethod.php b/src/Framework/MockObject/Generator/MockMethod.php deleted file mode 100644 index e6955e51bba..00000000000 --- a/src/Framework/MockObject/Generator/MockMethod.php +++ /dev/null @@ -1,332 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function explode; -use function implode; -use function is_object; -use function is_string; -use function preg_match; -use function preg_replace; -use function sprintf; -use function strlen; -use function strpos; -use function substr; -use function substr_count; -use function trim; -use function var_export; -use ReflectionMethod; -use ReflectionParameter; -use SebastianBergmann\Type\ReflectionMapper; -use SebastianBergmann\Type\Type; -use SebastianBergmann\Type\UnknownType; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MockMethod -{ - use TemplateLoader; - - /** - * @psalm-var class-string - */ - private readonly string $className; - - /** - * @psalm-var non-empty-string - */ - private readonly string $methodName; - private readonly bool $cloneArguments; - private readonly string $modifier; - private readonly string $argumentsForDeclaration; - private readonly string $argumentsForCall; - private readonly Type $returnType; - private readonly string $reference; - private readonly bool $callOriginalMethod; - private readonly bool $static; - private readonly ?string $deprecation; - - /** - * @throws ReflectionException - * @throws RuntimeException - */ - public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self - { - if ($method->isPrivate()) { - $modifier = 'private'; - } elseif ($method->isProtected()) { - $modifier = 'protected'; - } else { - $modifier = 'public'; - } - - if ($method->isStatic()) { - $modifier .= ' static'; - } - - if ($method->returnsReference()) { - $reference = '&'; - } else { - $reference = ''; - } - - $docComment = $method->getDocComment(); - - if (is_string($docComment) && - preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation)) { - $deprecation = trim(preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1])); - } else { - $deprecation = null; - } - - return new self( - $method->getDeclaringClass()->getName(), - $method->getName(), - $cloneArguments, - $modifier, - self::methodParametersForDeclaration($method), - self::methodParametersForCall($method), - (new ReflectionMapper)->fromReturnType($method), - $reference, - $callOriginalMethod, - $method->isStatic(), - $deprecation, - ); - } - - /** - * @param class-string $className - * @param non-empty-string $methodName - */ - public static function fromName(string $className, string $methodName, bool $cloneArguments): self - { - return new self( - $className, - $methodName, - $cloneArguments, - 'public', - '', - '', - new UnknownType, - '', - false, - false, - null, - ); - } - - /** - * @param class-string $className - * @param non-empty-string $methodName - */ - private function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation) - { - $this->className = $className; - $this->methodName = $methodName; - $this->cloneArguments = $cloneArguments; - $this->modifier = $modifier; - $this->argumentsForDeclaration = $argumentsForDeclaration; - $this->argumentsForCall = $argumentsForCall; - $this->returnType = $returnType; - $this->reference = $reference; - $this->callOriginalMethod = $callOriginalMethod; - $this->static = $static; - $this->deprecation = $deprecation; - } - - /** - * @psalm-return non-empty-string - */ - public function methodName(): string - { - return $this->methodName; - } - - /** - * @throws RuntimeException - */ - public function generateCode(): string - { - if ($this->static) { - $templateFile = 'doubled_static_method.tpl'; - } else { - $templateFile = sprintf( - '%s_method.tpl', - $this->callOriginalMethod ? 'proxied' : 'doubled', - ); - } - - $deprecation = $this->deprecation; - $returnResult = ''; - - if (!$this->returnType->isNever() && !$this->returnType->isVoid()) { - $returnResult = <<<'EOT' - - - return $__phpunit_result; -EOT; - } - - if (null !== $this->deprecation) { - $deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation})."; - $deprecationTemplate = $this->loadTemplate('deprecation.tpl'); - - $deprecationTemplate->setVar( - [ - 'deprecation' => var_export($deprecation, true), - ], - ); - - $deprecation = $deprecationTemplate->render(); - } - - $template = $this->loadTemplate($templateFile); - - $template->setVar( - [ - 'arguments_decl' => $this->argumentsForDeclaration, - 'arguments_call' => $this->argumentsForCall, - 'return_declaration' => !empty($this->returnType->asString()) ? (': ' . $this->returnType->asString()) : '', - 'return_type' => $this->returnType->asString(), - 'arguments_count' => !empty($this->argumentsForCall) ? substr_count($this->argumentsForCall, ',') + 1 : 0, - 'class_name' => $this->className, - 'method_name' => $this->methodName, - 'modifier' => $this->modifier, - 'reference' => $this->reference, - 'clone_arguments' => $this->cloneArguments ? 'true' : 'false', - 'deprecation' => $deprecation, - 'return_result' => $returnResult, - ], - ); - - return $template->render(); - } - - public function returnType(): Type - { - return $this->returnType; - } - - /** - * Returns the parameters of a function or method. - * - * @throws RuntimeException - */ - private static function methodParametersForDeclaration(ReflectionMethod $method): string - { - $parameters = []; - $types = (new ReflectionMapper)->fromParameterTypes($method); - - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - - $default = ''; - $reference = ''; - $typeDeclaration = ''; - - if (!$types[$i]->type()->isUnknown()) { - $typeDeclaration = $types[$i]->type()->asString() . ' '; - } - - if ($parameter->isPassedByReference()) { - $reference = '&'; - } - - if ($parameter->isVariadic()) { - $name = '...' . $name; - } elseif ($parameter->isDefaultValueAvailable()) { - $default = ' = ' . self::exportDefaultValue($parameter); - } elseif ($parameter->isOptional()) { - $default = ' = null'; - } - - $parameters[] = $typeDeclaration . $reference . $name . $default; - } - - return implode(', ', $parameters); - } - - /** - * Returns the parameters of a function or method. - * - * @throws ReflectionException - */ - private static function methodParametersForCall(ReflectionMethod $method): string - { - $parameters = []; - - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - - if ($parameter->isVariadic()) { - continue; - } - - if ($parameter->isPassedByReference()) { - $parameters[] = '&' . $name; - } else { - $parameters[] = $name; - } - } - - return implode(', ', $parameters); - } - - /** - * @throws ReflectionException - */ - private static function exportDefaultValue(ReflectionParameter $parameter): string - { - try { - $defaultValue = $parameter->getDefaultValue(); - - if (!is_object($defaultValue)) { - return var_export($defaultValue, true); - } - - $parameterAsString = $parameter->__toString(); - - return explode( - ' = ', - substr( - substr( - $parameterAsString, - strpos($parameterAsString, ' ') + strlen(' '), - ), - 0, - -2, - ), - )[1]; - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd - } -} diff --git a/src/Framework/MockObject/Generator/MockMethodSet.php b/src/Framework/MockObject/Generator/MockMethodSet.php deleted file mode 100644 index 31f48a6a167..00000000000 --- a/src/Framework/MockObject/Generator/MockMethodSet.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function array_key_exists; -use function array_values; -use function strtolower; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MockMethodSet -{ - /** - * @psalm-var array - */ - private array $methods = []; - - public function addMethods(MockMethod ...$methods): void - { - foreach ($methods as $method) { - $this->methods[strtolower($method->methodName())] = $method; - } - } - - /** - * @psalm-return list - */ - public function asArray(): array - { - return array_values($this->methods); - } - - public function hasMethod(string $methodName): bool - { - return array_key_exists(strtolower($methodName), $this->methods); - } -} diff --git a/src/Framework/MockObject/Generator/MockTrait.php b/src/Framework/MockObject/Generator/MockTrait.php deleted file mode 100644 index dc77ca134b6..00000000000 --- a/src/Framework/MockObject/Generator/MockTrait.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -use function class_exists; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5243 - */ -final readonly class MockTrait implements MockType -{ - private string $classCode; - - /** - * @psalm-var class-string - */ - private string $mockName; - - /** - * @psalm-param class-string $mockName - */ - public function __construct(string $classCode, string $mockName) - { - $this->classCode = $classCode; - $this->mockName = $mockName; - } - - /** - * @psalm-return class-string - */ - public function generate(): string - { - if (!class_exists($this->mockName, false)) { - eval($this->classCode); - } - - return $this->mockName; - } -} diff --git a/src/Framework/MockObject/Generator/MockType.php b/src/Framework/MockObject/Generator/MockType.php deleted file mode 100644 index 3f7799f0b4d..00000000000 --- a/src/Framework/MockObject/Generator/MockType.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Generator; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface MockType -{ - /** - * @psalm-return class-string - */ - public function generate(): string; -} diff --git a/src/Framework/MockObject/Generator/TemplateLoader.php b/src/Framework/MockObject/Generator/TemplateLoader.php index fc3b73a10f0..8106ce59cf4 100644 --- a/src/Framework/MockObject/Generator/TemplateLoader.php +++ b/src/Framework/MockObject/Generator/TemplateLoader.php @@ -12,18 +12,17 @@ use SebastianBergmann\Template\Template; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ trait TemplateLoader { /** - * @psalm-var array + * @var array */ private static array $templates = []; - /** - * @psalm-suppress MissingThrowsDocblock - */ private function loadTemplate(string $template): Template { $filename = __DIR__ . '/templates/' . $template; diff --git a/src/Framework/MockObject/Generator/templates/doubled_method.tpl b/src/Framework/MockObject/Generator/templates/doubled_method.tpl index 8b4af38b3ed..bb6fb761d1a 100644 --- a/src/Framework/MockObject/Generator/templates/doubled_method.tpl +++ b/src/Framework/MockObject/Generator/templates/doubled_method.tpl @@ -1,10 +1,23 @@ {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} {{deprecation} + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [{arguments_call}]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > {arguments_count}) { + if ({arguments_count} !== null && $__phpunit_count > {arguments_count}) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -12,9 +25,11 @@ } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this ) );{return_result} } diff --git a/src/Framework/MockObject/Generator/templates/proxied_method.tpl b/src/Framework/MockObject/Generator/templates/proxied_method.tpl deleted file mode 100644 index 330cdcfc0b3..00000000000 --- a/src/Framework/MockObject/Generator/templates/proxied_method.tpl +++ /dev/null @@ -1,22 +0,0 @@ - - {modifier} function {reference}{method_name}({arguments_decl}){return_declaration} - { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "{method_name}"], $__phpunit_arguments);{return_result} - } diff --git a/src/Framework/MockObject/Generator/templates/test_double_class.tpl b/src/Framework/MockObject/Generator/templates/test_double_class.tpl index 369a47a6810..5d015e3f961 100644 --- a/src/Framework/MockObject/Generator/templates/test_double_class.tpl +++ b/src/Framework/MockObject/Generator/templates/test_double_class.tpl @@ -1,5 +1,5 @@ declare(strict_types=1); -{prologue}{class_declaration} +{class_declaration} { -{use_statements}{mocked_methods}}{epilogue} +{use_statements}{property_hooks}{methods}} diff --git a/src/Framework/MockObject/Generator/templates/trait_class.tpl b/src/Framework/MockObject/Generator/templates/trait_class.tpl deleted file mode 100644 index a8fe470fdc4..00000000000 --- a/src/Framework/MockObject/Generator/templates/trait_class.tpl +++ /dev/null @@ -1,6 +0,0 @@ -declare(strict_types=1); - -{prologue}class {class_name} -{ - use {trait_name}; -} diff --git a/src/Framework/MockObject/Generator/templates/wsdl_class.tpl b/src/Framework/MockObject/Generator/templates/wsdl_class.tpl deleted file mode 100644 index b3100b41417..00000000000 --- a/src/Framework/MockObject/Generator/templates/wsdl_class.tpl +++ /dev/null @@ -1,9 +0,0 @@ -declare(strict_types=1); - -{namespace}class {class_name} extends \SoapClient -{ - public function __construct($wsdl, array $options) - { - parent::__construct('{wsdl}', $options); - } -{methods}} diff --git a/src/Framework/MockObject/Generator/templates/wsdl_method.tpl b/src/Framework/MockObject/Generator/templates/wsdl_method.tpl deleted file mode 100644 index bb16e763eba..00000000000 --- a/src/Framework/MockObject/Generator/templates/wsdl_method.tpl +++ /dev/null @@ -1,4 +0,0 @@ - - public function {method_name}({arguments}) - { - } diff --git a/src/Framework/MockObject/MockBuilder.php b/src/Framework/MockObject/MockBuilder.php index 1e4ab368a82..02620db59e6 100644 --- a/src/Framework/MockObject/MockBuilder.php +++ b/src/Framework/MockObject/MockBuilder.php @@ -11,18 +11,13 @@ use function array_merge; use function assert; -use function debug_backtrace; -use function trait_exists; -use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Framework\InvalidArgumentException; -use PHPUnit\Framework\MockObject\Generator\ClassAlreadyExistsException; use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException; use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException; -use PHPUnit\Framework\MockObject\Generator\ClassIsReadonlyException; use PHPUnit\Framework\MockObject\Generator\DuplicateMethodException; use PHPUnit\Framework\MockObject\Generator\Generator; use PHPUnit\Framework\MockObject\Generator\InvalidMethodNameException; -use PHPUnit\Framework\MockObject\Generator\OriginalConstructorInvocationRequiredException; +use PHPUnit\Framework\MockObject\Generator\NameAlreadyInUseException; use PHPUnit\Framework\MockObject\Generator\ReflectionException; use PHPUnit\Framework\MockObject\Generator\RuntimeException; use PHPUnit\Framework\MockObject\Generator\UnknownTypeException; @@ -30,7 +25,7 @@ use ReflectionClass; /** - * @psalm-template MockedType + * @template MockedType * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ @@ -39,33 +34,32 @@ final class MockBuilder private readonly TestCase $testCase; /** - * @psalm-var class-string|trait-string + * @var class-string|trait-string */ private readonly string $type; /** - * @psalm-var list + * @var list */ private array $methods = []; private bool $emptyMethodsArray = false; /** - * @psalm-var ?class-string + * @var ?class-string */ - private ?string $mockClassName = null; - private array $constructorArgs = []; - private bool $originalConstructor = true; - private bool $originalClone = true; - private bool $autoload = true; - private bool $cloneArguments = false; - private bool $callOriginalMethods = false; - private ?object $proxyTarget = null; - private bool $allowMockingUnknownTypes = true; - private bool $returnValueGeneration = true; + private ?string $mockClassName = null; + + /** + * @var array + */ + private array $constructorArgs = []; + private bool $originalConstructor = true; + private bool $originalClone = true; + private bool $returnValueGeneration = true; private readonly Generator $generator; /** - * @psalm-param class-string|trait-string $type + * @param class-string|trait-string $type */ public function __construct(TestCase $testCase, string $type) { @@ -77,36 +71,28 @@ public function __construct(TestCase $testCase, string $type) /** * Creates a mock object using a fluent interface. * - * @throws ClassAlreadyExistsException * @throws ClassIsEnumerationException * @throws ClassIsFinalException - * @throws ClassIsReadonlyException * @throws DuplicateMethodException * @throws InvalidArgumentException * @throws InvalidMethodNameException - * @throws OriginalConstructorInvocationRequiredException + * @throws NameAlreadyInUseException * @throws ReflectionException * @throws RuntimeException * @throws UnknownTypeException * - * @psalm-return MockObject&MockedType + * @return MockedType&MockObject */ public function getMock(): MockObject { $object = $this->generator->testDouble( $this->type, true, - true, !$this->emptyMethodsArray ? $this->methods : null, $this->constructorArgs, $this->mockClassName ?? '', $this->originalConstructor, $this->originalClone, - $this->autoload, - $this->cloneArguments, - $this->callOriginalMethods, - $this->proxyTarget, - $this->allowMockingUnknownTypes, $this->returnValueGeneration, ); @@ -118,84 +104,10 @@ public function getMock(): MockObject return $object; } - /** - * Creates a mock object for an abstract class using a fluent interface. - * - * @psalm-return MockObject&MockedType - * - * @throws \PHPUnit\Framework\Exception - * @throws ReflectionException - * @throws RuntimeException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5305 - */ - public function getMockForAbstractClass(): MockObject - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::getMockForAbstractClass() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $object = $this->generator->mockObjectForAbstractClass( - $this->type, - $this->constructorArgs, - $this->mockClassName ?? '', - $this->originalConstructor, - $this->originalClone, - $this->autoload, - $this->methods, - $this->cloneArguments, - ); - - assert($object instanceof MockObject); - - $this->testCase->registerMockObject($object); - - return $object; - } - - /** - * Creates a mock object for a trait using a fluent interface. - * - * @psalm-return MockObject&MockedType - * - * @throws \PHPUnit\Framework\Exception - * @throws ReflectionException - * @throws RuntimeException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5306 - */ - public function getMockForTrait(): MockObject - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::getMockForTrait() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - assert(trait_exists($this->type)); - - $object = $this->generator->mockObjectForTrait( - $this->type, - $this->constructorArgs, - $this->mockClassName ?? '', - $this->originalConstructor, - $this->originalClone, - $this->autoload, - $this->methods, - $this->cloneArguments, - ); - - assert($object instanceof MockObject); - - $this->testCase->registerMockObject($object); - - return $object; - } - /** * Specifies the subset of methods to mock, requiring each to exist in the class. * - * @psalm-param list $methods + * @param list $methods * * @throws CannotUseOnlyMethodsException * @throws ReflectionException @@ -204,7 +116,7 @@ public function getMockForTrait(): MockObject */ public function onlyMethods(array $methods): self { - if (empty($methods)) { + if ($methods === []) { $this->emptyMethodsArray = true; return $this; @@ -212,7 +124,9 @@ public function onlyMethods(array $methods): self try { $reflector = new ReflectionClass($this->type); + // @codeCoverageIgnoreStart + /** @phpstan-ignore catch.neverThrown */ } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), @@ -233,58 +147,11 @@ public function onlyMethods(array $methods): self return $this; } - /** - * Specifies methods that don't exist in the class which you want to mock. - * - * @psalm-param list $methods - * - * @throws CannotUseAddMethodsException - * @throws ReflectionException - * @throws RuntimeException - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5320 - */ - public function addMethods(array $methods): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::addMethods() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - if (empty($methods)) { - $this->emptyMethodsArray = true; - - return $this; - } - - try { - $reflector = new ReflectionClass($this->type); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - // @codeCoverageIgnoreEnd - } - - foreach ($methods as $method) { - if ($reflector->hasMethod($method)) { - throw new CannotUseAddMethodsException($this->type, $method); - } - } - - $this->methods = array_merge($this->methods, $methods); - - return $this; - } - /** * Specifies the arguments for the constructor. * + * @param array $arguments + * * @return $this */ public function setConstructorArgs(array $arguments): self @@ -297,7 +164,7 @@ public function setConstructorArgs(array $arguments): self /** * Specifies the name for the mock class. * - * @psalm-param class-string $name + * @param class-string $name * * @return $this */ @@ -357,191 +224,10 @@ public function enableOriginalClone(): self } /** - * Disables the use of class autoloading while creating the mock object. - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5309 - */ - public function disableAutoload(): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::disableAutoload() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $this->autoload = false; - - return $this; - } - - /** - * Enables the use of class autoloading while creating the mock object. - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5309 - */ - public function enableAutoload(): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::enableAutoload() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $this->autoload = true; - - return $this; - } - - /** - * Disables the cloning of arguments passed to mocked methods. - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5315 - */ - public function disableArgumentCloning(): self - { - if (!$this->calledFromTestCase()) { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::disableArgumentCloning() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - } - - $this->cloneArguments = false; - - return $this; - } - - /** - * Enables the cloning of arguments passed to mocked methods. - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5315 - */ - public function enableArgumentCloning(): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::enableArgumentCloning() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $this->cloneArguments = true; - - return $this; - } - - /** - * Enables the invocation of the original methods. - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5307 - */ - public function enableProxyingToOriginalMethods(): self - { - if (!$this->calledFromTestCase()) { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::enableProxyingToOriginalMethods() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - } - - $this->callOriginalMethods = true; - - return $this; - } - - /** - * Disables the invocation of the original methods. - * - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5307 - */ - public function disableProxyingToOriginalMethods(): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::disableProxyingToOriginalMethods() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $this->callOriginalMethods = false; - $this->proxyTarget = null; - - return $this; - } - - /** - * Sets the proxy target. - * * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5307 - */ - public function setProxyTarget(object $object): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::setProxyTarget() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $this->proxyTarget = $object; - - return $this; - } - - /** - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5308 - */ - public function allowMockingUnknownTypes(): self - { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::allowMockingUnknownTypes() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $this->allowMockingUnknownTypes = true; - - return $this; - } - - /** - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5308 - */ - public function disallowMockingUnknownTypes(): self - { - if (!$this->calledFromTestCase()) { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::disallowMockingUnknownTypes() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - } - - $this->allowMockingUnknownTypes = false; - - return $this; - } - - /** - * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5421 */ public function enableAutoReturnValueGeneration(): self { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::enableAutoReturnValueGeneration() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - $this->returnValueGeneration = true; return $this; @@ -549,25 +235,11 @@ public function enableAutoReturnValueGeneration(): self /** * @return $this - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5421 */ public function disableAutoReturnValueGeneration(): self { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $this->testCase->valueObjectForEvents(), - 'MockBuilder::disableAutoReturnValueGeneration() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - $this->returnValueGeneration = false; return $this; } - - private function calledFromTestCase(): bool - { - $caller = debug_backtrace(limit: 3)[2]; - - return isset($caller['class']) && $caller['class'] === TestCase::class; - } } diff --git a/src/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php b/src/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php index bb02daf8743..4da35c0c176 100644 --- a/src/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php +++ b/src/Framework/MockObject/Runtime/Api/DoubledCloneMethod.php @@ -10,12 +10,18 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ trait DoubledCloneMethod { public function __clone(): void { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); + $this->__phpunit_state = clone $this->__phpunit_state; + + $this->__phpunit_state()->cloneInvocationHandler(); } + + abstract public function __phpunit_state(): TestDoubleState; } diff --git a/src/Framework/MockObject/Runtime/Api/GeneratedAsMockObject.php b/src/Framework/MockObject/Runtime/Api/GeneratedAsMockObject.php deleted file mode 100644 index 66ed17679cd..00000000000 --- a/src/Framework/MockObject/Runtime/Api/GeneratedAsMockObject.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This trait is not covered by the backward compatibility promise for PHPUnit - */ -trait GeneratedAsMockObject -{ - public function __phpunit_wasGeneratedAsMockObject(): true - { - return true; - } -} diff --git a/src/Framework/MockObject/Runtime/Api/GeneratedAsTestStub.php b/src/Framework/MockObject/Runtime/Api/GeneratedAsTestStub.php deleted file mode 100644 index 9009bece743..00000000000 --- a/src/Framework/MockObject/Runtime/Api/GeneratedAsTestStub.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * @internal This trait is not covered by the backward compatibility promise for PHPUnit - */ -trait GeneratedAsTestStub -{ - public function __phpunit_wasGeneratedAsMockObject(): false - { - return false; - } -} diff --git a/src/Framework/MockObject/Runtime/Api/Method.php b/src/Framework/MockObject/Runtime/Api/Method.php index 2112fb3edbb..c9b4e42e55a 100644 --- a/src/Framework/MockObject/Runtime/Api/Method.php +++ b/src/Framework/MockObject/Runtime/Api/Method.php @@ -9,23 +9,24 @@ */ namespace PHPUnit\Framework\MockObject; -use function call_user_func_array; -use function func_get_args; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ trait Method { - public function method(): InvocationMocker - { - $expects = $this->__phpunit_getInvocationHandler()->expects(new AnyInvokedCount); + abstract public function __phpunit_getInvocationHandler(): InvocationHandler; - return call_user_func_array( - [$expects, 'method'], - func_get_args(), - ); + public function method(Constraint|PropertyHook|string $constraint): InvocationStubber + { + return $this + ->__phpunit_getInvocationHandler() + ->expects(new AnyInvokedCount) + ->method($constraint); } } diff --git a/src/Framework/MockObject/Runtime/Api/MockObjectApi.php b/src/Framework/MockObject/Runtime/Api/MockObjectApi.php index 92121546698..a7369567686 100644 --- a/src/Framework/MockObject/Runtime/Api/MockObjectApi.php +++ b/src/Framework/MockObject/Runtime/Api/MockObjectApi.php @@ -9,33 +9,21 @@ */ namespace PHPUnit\Framework\MockObject; -use function assert; -use PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException; -use PHPUnit\Event\Code\TestMethodBuilder; -use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker as InvocationMockerBuilder; use PHPUnit\Framework\MockObject\Rule\InvocationOrder; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ trait MockObjectApi { - private static array $__phpunit_deprecation_emitted_for_test = []; - private object $__phpunit_originalObject; - /** @noinspection MagicMethodsValidityInspection */ public function __phpunit_hasMatchers(): bool { return $this->__phpunit_getInvocationHandler()->hasMatchers(); } - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_setOriginalObject(object $originalObject): void - { - $this->__phpunit_originalObject = $originalObject; - } - /** @noinspection MagicMethodsValidityInspection */ public function __phpunit_verify(bool $unsetInvocationMocker = true): void { @@ -46,33 +34,14 @@ public function __phpunit_verify(bool $unsetInvocationMocker = true): void } } + abstract public function __phpunit_state(): TestDoubleState; + abstract public function __phpunit_getInvocationHandler(): InvocationHandler; abstract public function __phpunit_unsetInvocationMocker(): void; - public function expects(InvocationOrder $matcher): InvocationMockerBuilder + public function expects(InvocationOrder $matcher): InvocationStubber { - assert($this instanceof StubInternal); - - if (!$this->__phpunit_wasGeneratedAsMockObject()) { - $message = 'Configuring expectations on test doubles that were created as test stubs is deprecated. Support for this will be removed in PHPUnit 12.'; - - try { - $test = TestMethodBuilder::fromCallStack(); - - if (!isset(self::$__phpunit_deprecation_emitted_for_test[$test->id()])) { - EventFacade::emitter()->testTriggeredPhpunitDeprecation( - $test, - $message, - ); - - self::$__phpunit_deprecation_emitted_for_test[$test->id()] = true; - } - } catch (NoTestCaseObjectOnCallStackException) { - EventFacade::emitter()->testRunnerTriggeredDeprecation($message); - } - } - return $this->__phpunit_getInvocationHandler()->expects($matcher); } } diff --git a/src/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.php b/src/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.php index ae0dbc78c08..88797884d77 100644 --- a/src/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.php +++ b/src/Framework/MockObject/Runtime/Api/ProxiedCloneMethod.php @@ -10,14 +10,20 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ trait ProxiedCloneMethod { public function __clone(): void { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler(); + $this->__phpunit_state = clone $this->__phpunit_state; + + $this->__phpunit_state()->cloneInvocationHandler(); parent::__clone(); } + + abstract public function __phpunit_state(): TestDoubleState; } diff --git a/src/Framework/MockObject/Runtime/Api/StubApi.php b/src/Framework/MockObject/Runtime/Api/StubApi.php index 64bed690164..faba8d40fbb 100644 --- a/src/Framework/MockObject/Runtime/Api/StubApi.php +++ b/src/Framework/MockObject/Runtime/Api/StubApi.php @@ -10,45 +10,28 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This trait is not covered by the backward compatibility promise for PHPUnit */ trait StubApi { - /** - * @psalm-var list - */ - private static array $__phpunit_configurableMethods; - private bool $__phpunit_returnValueGeneration = true; - private ?InvocationHandler $__phpunit_invocationMocker = null; + private readonly TestDoubleState $__phpunit_state; - /** @noinspection MagicMethodsValidityInspection */ - public static function __phpunit_initConfigurableMethods(ConfigurableMethod ...$configurableMethods): void + public function __phpunit_state(): TestDoubleState { - static::$__phpunit_configurableMethods = $configurableMethods; - } - - /** @noinspection MagicMethodsValidityInspection */ - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void - { - $this->__phpunit_returnValueGeneration = $returnValueGeneration; + return $this->__phpunit_state; } /** @noinspection MagicMethodsValidityInspection */ public function __phpunit_getInvocationHandler(): InvocationHandler { - if ($this->__phpunit_invocationMocker === null) { - $this->__phpunit_invocationMocker = new InvocationHandler( - static::$__phpunit_configurableMethods, - $this->__phpunit_returnValueGeneration, - ); - } - - return $this->__phpunit_invocationMocker; + return $this->__phpunit_state()->invocationHandler(); } /** @noinspection MagicMethodsValidityInspection */ public function __phpunit_unsetInvocationMocker(): void { - $this->__phpunit_invocationMocker = null; + $this->__phpunit_state()->unsetInvocationHandler(); } } diff --git a/src/Framework/MockObject/Runtime/Api/TestDoubleState.php b/src/Framework/MockObject/Runtime/Api/TestDoubleState.php new file mode 100644 index 00000000000..93b10c14da5 --- /dev/null +++ b/src/Framework/MockObject/Runtime/Api/TestDoubleState.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestDoubleState +{ + /** + * @var list + */ + private readonly array $configurableMethods; + private readonly bool $generateReturnValues; + private ?InvocationHandler $invocationHandler = null; + + /** + * @param list $configurableMethods + */ + public function __construct(array $configurableMethods, bool $generateReturnValues) + { + $this->configurableMethods = $configurableMethods; + $this->generateReturnValues = $generateReturnValues; + } + + public function invocationHandler(): InvocationHandler + { + if ($this->invocationHandler !== null) { + return $this->invocationHandler; + } + + $this->invocationHandler = new InvocationHandler( + $this->configurableMethods, + $this->generateReturnValues, + ); + + return $this->invocationHandler; + } + + public function cloneInvocationHandler(): void + { + if ($this->invocationHandler === null) { + return; + } + + $this->invocationHandler = clone $this->invocationHandler; + } + + public function unsetInvocationHandler(): void + { + $this->invocationHandler = null; + } + + /** + * @return list + */ + public function configurableMethods(): array + { + return $this->configurableMethods; + } + + public function generateReturnValues(): bool + { + return $this->generateReturnValues; + } +} diff --git a/src/Framework/MockObject/Runtime/Builder/Identity.php b/src/Framework/MockObject/Runtime/Builder/Identity.php deleted file mode 100644 index 28c0bc035b8..00000000000 --- a/src/Framework/MockObject/Runtime/Builder/Identity.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Identity -{ - /** - * Sets the identification of the expectation to $id. - * - * @note The identifier is unique per mock object. - */ - public function id(string $id): self; -} diff --git a/src/Framework/MockObject/Runtime/Builder/InvocationMocker.php b/src/Framework/MockObject/Runtime/Builder/InvocationMocker.php deleted file mode 100644 index f7d1d16863e..00000000000 --- a/src/Framework/MockObject/Runtime/Builder/InvocationMocker.php +++ /dev/null @@ -1,284 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use function array_flip; -use function array_key_exists; -use function array_map; -use function array_merge; -use function count; -use function is_string; -use function strtolower; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\InvalidArgumentException; -use PHPUnit\Framework\MockObject\ConfigurableMethod; -use PHPUnit\Framework\MockObject\IncompatibleReturnValueException; -use PHPUnit\Framework\MockObject\InvocationHandler; -use PHPUnit\Framework\MockObject\Matcher; -use PHPUnit\Framework\MockObject\MatcherAlreadyRegisteredException; -use PHPUnit\Framework\MockObject\MethodCannotBeConfiguredException; -use PHPUnit\Framework\MockObject\MethodNameAlreadyConfiguredException; -use PHPUnit\Framework\MockObject\MethodNameNotConfiguredException; -use PHPUnit\Framework\MockObject\MethodParametersAlreadyConfiguredException; -use PHPUnit\Framework\MockObject\Rule; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls; -use PHPUnit\Framework\MockObject\Stub\Exception; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback; -use PHPUnit\Framework\MockObject\Stub\ReturnReference; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap; -use PHPUnit\Framework\MockObject\Stub\Stub; -use Throwable; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class InvocationMocker implements InvocationStubber, MethodNameMatch -{ - private readonly InvocationHandler $invocationHandler; - private readonly Matcher $matcher; - - /** - * @psalm-var list - */ - private readonly array $configurableMethods; - - /** - * @psalm-var ?array - */ - private ?array $configurableMethodNames = null; - - public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods) - { - $this->invocationHandler = $handler; - $this->matcher = $matcher; - $this->configurableMethods = $configurableMethods; - } - - /** - * @throws MatcherAlreadyRegisteredException - * - * @return $this - */ - public function id(string $id): self - { - $this->invocationHandler->registerMatcher($id, $this->matcher); - - return $this; - } - - /** - * @return $this - */ - public function will(Stub $stub): Identity - { - $this->matcher->setStub($stub); - - return $this; - } - - /** - * @throws IncompatibleReturnValueException - */ - public function willReturn(mixed $value, mixed ...$nextValues): self - { - if (count($nextValues) === 0) { - $this->ensureTypeOfReturnValues([$value]); - - $stub = $value instanceof Stub ? $value : new ReturnStub($value); - - return $this->will($stub); - } - - $values = array_merge([$value], $nextValues); - - $this->ensureTypeOfReturnValues($values); - - $stub = new ConsecutiveCalls($values); - - return $this->will($stub); - } - - public function willReturnReference(mixed &$reference): self - { - $stub = new ReturnReference($reference); - - return $this->will($stub); - } - - public function willReturnMap(array $valueMap): self - { - $stub = new ReturnValueMap($valueMap); - - return $this->will($stub); - } - - public function willReturnArgument(int $argumentIndex): self - { - $stub = new ReturnArgument($argumentIndex); - - return $this->will($stub); - } - - public function willReturnCallback(callable $callback): self - { - $stub = new ReturnCallback($callback); - - return $this->will($stub); - } - - public function willReturnSelf(): self - { - $stub = new ReturnSelf; - - return $this->will($stub); - } - - public function willReturnOnConsecutiveCalls(mixed ...$values): self - { - $stub = new ConsecutiveCalls($values); - - return $this->will($stub); - } - - public function willThrowException(Throwable $exception): self - { - $stub = new Exception($exception); - - return $this->will($stub); - } - - /** - * @return $this - */ - public function after(string $id): self - { - $this->matcher->setAfterMatchBuilderId($id); - - return $this; - } - - /** - * @throws \PHPUnit\Framework\Exception - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException - * - * @return $this - */ - public function with(mixed ...$arguments): self - { - $this->ensureParametersCanBeConfigured(); - - $this->matcher->setParametersRule(new Rule\Parameters($arguments)); - - return $this; - } - - /** - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException - * - * @return $this - */ - public function withAnyParameters(): self - { - $this->ensureParametersCanBeConfigured(); - - $this->matcher->setParametersRule(new Rule\AnyParameters); - - return $this; - } - - /** - * @throws InvalidArgumentException - * @throws MethodCannotBeConfiguredException - * @throws MethodNameAlreadyConfiguredException - * - * @return $this - */ - public function method(Constraint|string $constraint): self - { - if ($this->matcher->hasMethodNameRule()) { - throw new MethodNameAlreadyConfiguredException; - } - - if (is_string($constraint)) { - $this->configurableMethodNames ??= array_flip( - array_map( - static fn (ConfigurableMethod $configurable) => strtolower($configurable->name()), - $this->configurableMethods, - ), - ); - - if (!array_key_exists(strtolower($constraint), $this->configurableMethodNames)) { - throw new MethodCannotBeConfiguredException($constraint); - } - } - - $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); - - return $this; - } - - /** - * @throws MethodNameNotConfiguredException - * @throws MethodParametersAlreadyConfiguredException - */ - private function ensureParametersCanBeConfigured(): void - { - if (!$this->matcher->hasMethodNameRule()) { - throw new MethodNameNotConfiguredException; - } - - if ($this->matcher->hasParametersRule()) { - throw new MethodParametersAlreadyConfiguredException; - } - } - - private function configuredMethod(): ?ConfigurableMethod - { - $configuredMethod = null; - - foreach ($this->configurableMethods as $configurableMethod) { - if ($this->matcher->methodNameRule()->matchesName($configurableMethod->name())) { - if ($configuredMethod !== null) { - return null; - } - - $configuredMethod = $configurableMethod; - } - } - - return $configuredMethod; - } - - /** - * @throws IncompatibleReturnValueException - */ - private function ensureTypeOfReturnValues(array $values): void - { - $configuredMethod = $this->configuredMethod(); - - if ($configuredMethod === null) { - return; - } - - foreach ($values as $value) { - if (!$configuredMethod->mayReturn($value)) { - throw new IncompatibleReturnValueException( - $configuredMethod, - $value, - ); - } - } - } -} diff --git a/src/Framework/MockObject/Runtime/Builder/InvocationStubber.php b/src/Framework/MockObject/Runtime/Builder/InvocationStubber.php deleted file mode 100644 index 9918b77f823..00000000000 --- a/src/Framework/MockObject/Runtime/Builder/InvocationStubber.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\MockObject\Stub\Stub; -use Throwable; - -/** - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -interface InvocationStubber -{ - public function will(Stub $stub): Identity; - - public function willReturn(mixed $value, mixed ...$nextValues): self; - - public function willReturnReference(mixed &$reference): self; - - /** - * @psalm-param array> $valueMap - */ - public function willReturnMap(array $valueMap): self; - - public function willReturnArgument(int $argumentIndex): self; - - public function willReturnCallback(callable $callback): self; - - public function willReturnSelf(): self; - - public function willReturnOnConsecutiveCalls(mixed ...$values): self; - - public function willThrowException(Throwable $exception): self; -} diff --git a/src/Framework/MockObject/Runtime/Builder/MethodNameMatch.php b/src/Framework/MockObject/Runtime/Builder/MethodNameMatch.php deleted file mode 100644 index 46b35e629b0..00000000000 --- a/src/Framework/MockObject/Runtime/Builder/MethodNameMatch.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\Constraint\Constraint; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface MethodNameMatch extends ParametersMatch -{ - /** - * Adds a new method name match and returns the parameter match object for - * further matching possibilities. - */ - public function method(Constraint|string $constraint): self; -} diff --git a/src/Framework/MockObject/Runtime/Builder/ParametersMatch.php b/src/Framework/MockObject/Runtime/Builder/ParametersMatch.php deleted file mode 100644 index 656d21b2b6a..00000000000 --- a/src/Framework/MockObject/Runtime/Builder/ParametersMatch.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface ParametersMatch extends Stub -{ - /** - * Defines the expectation which must occur before the current is valid. - */ - public function after(string $id): Stub; - - /** - * Sets the parameters to match for, each parameter to this function will - * be part of match. To perform specific matches or constraints create a - * new PHPUnit\Framework\Constraint\Constraint and use it for the parameter. - * If the parameter value is not a constraint it will use the - * PHPUnit\Framework\Constraint\IsEqual for the value. - * - * Some examples: - * - * // match first parameter with value 2 - * $b->with(2); - * // match first parameter with value 'smock' and second identical to 42 - * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); - * - */ - public function with(mixed ...$arguments): self; - - /** - * Sets a rule which allows any kind of parameters. - * - * Some examples: - * - * // match any number of parameters - * $b->withAnyParameters(); - * - */ - public function withAnyParameters(): self; -} diff --git a/src/Framework/MockObject/Runtime/Builder/Stub.php b/src/Framework/MockObject/Runtime/Builder/Stub.php deleted file mode 100644 index d7cb78fc4e3..00000000000 --- a/src/Framework/MockObject/Runtime/Builder/Stub.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\MockObject\Stub\Stub as BaseStub; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -interface Stub extends Identity -{ - /** - * Stubs the matching method with the stub object $stub. Any invocations of - * the matched method will now be handled by the stub instead. - */ - public function will(BaseStub $stub): Identity; -} diff --git a/src/Framework/MockObject/Runtime/Interface/InvocationStubber.php b/src/Framework/MockObject/Runtime/Interface/InvocationStubber.php new file mode 100644 index 00000000000..e621bfbc0aa --- /dev/null +++ b/src/Framework/MockObject/Runtime/Interface/InvocationStubber.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; +use PHPUnit\Framework\MockObject\Stub\Stub; +use Throwable; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +interface InvocationStubber +{ + /** + * @param Constraint|non-empty-string|PropertyHook $constraint + * + * @return $this + */ + public function method(Constraint|PropertyHook|string $constraint): self; + + /** + * @param non-empty-string $id + * + * @return $this + */ + public function id(string $id): self; + + /** + * @param non-empty-string $id + * + * @return $this + */ + public function after(string $id): self; + + /** + * @return $this + */ + public function with(mixed ...$arguments): self; + + /** + * @return $this + */ + public function withAnyParameters(): self; + + /** + * @return $this + */ + public function will(Stub $stub): self; + + /** + * @return $this + */ + public function willReturn(mixed $value, mixed ...$nextValues): self; + + /** + * @return $this + */ + public function willReturnReference(mixed &$reference): self; + + /** + * @param array> $valueMap + * + * @return $this + */ + public function willReturnMap(array $valueMap): self; + + /** + * @return $this + */ + public function willReturnArgument(int $argumentIndex): self; + + /** + * @return $this + */ + public function willReturnCallback(callable $callback): self; + + /** + * @return $this + */ + public function willReturnSelf(): self; + + /** + * @return $this + */ + public function willReturnOnConsecutiveCalls(mixed ...$values): self; + + /** + * @return $this + */ + public function willThrowException(Throwable $exception): self; +} diff --git a/src/Framework/MockObject/Runtime/Interface/MockObject.php b/src/Framework/MockObject/Runtime/Interface/MockObject.php index 4096d73f922..0ddc46dc4fe 100644 --- a/src/Framework/MockObject/Runtime/Interface/MockObject.php +++ b/src/Framework/MockObject/Runtime/Interface/MockObject.php @@ -9,15 +9,12 @@ */ namespace PHPUnit\Framework\MockObject; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\Rule\InvocationOrder; /** - * @method InvocationMocker method($constraint) - * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ interface MockObject extends Stub { - public function expects(InvocationOrder $invocationRule): InvocationMocker; + public function expects(InvocationOrder $invocationRule): InvocationStubber; } diff --git a/src/Framework/MockObject/Runtime/Interface/MockObjectInternal.php b/src/Framework/MockObject/Runtime/Interface/MockObjectInternal.php index bb84ffaccb6..167d2466db4 100644 --- a/src/Framework/MockObject/Runtime/Interface/MockObjectInternal.php +++ b/src/Framework/MockObject/Runtime/Interface/MockObjectInternal.php @@ -10,13 +10,13 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface MockObjectInternal extends MockObject, StubInternal { public function __phpunit_hasMatchers(): bool; - public function __phpunit_setOriginalObject(object $originalObject): void; - public function __phpunit_verify(bool $unsetInvocationMocker = true): void; } diff --git a/src/Framework/MockObject/Runtime/Interface/Stub.php b/src/Framework/MockObject/Runtime/Interface/Stub.php index 96f84d23134..6321c98bb39 100644 --- a/src/Framework/MockObject/Runtime/Interface/Stub.php +++ b/src/Framework/MockObject/Runtime/Interface/Stub.php @@ -9,13 +9,13 @@ */ namespace PHPUnit\Framework\MockObject; -use PHPUnit\Framework\MockObject\Builder\InvocationStubber; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; /** - * @method InvocationStubber method($constraint) - * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ interface Stub { + public function method(Constraint|PropertyHook|string $constraint): InvocationStubber; } diff --git a/src/Framework/MockObject/Runtime/Interface/StubInternal.php b/src/Framework/MockObject/Runtime/Interface/StubInternal.php index 10445265ada..6e428ea5ea1 100644 --- a/src/Framework/MockObject/Runtime/Interface/StubInternal.php +++ b/src/Framework/MockObject/Runtime/Interface/StubInternal.php @@ -10,17 +10,15 @@ namespace PHPUnit\Framework\MockObject; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface StubInternal extends Stub { - public static function __phpunit_initConfigurableMethods(ConfigurableMethod ...$configurableMethods): void; + public function __phpunit_state(): TestDoubleState; public function __phpunit_getInvocationHandler(): InvocationHandler; - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void; - public function __phpunit_unsetInvocationMocker(): void; - - public function __phpunit_wasGeneratedAsMockObject(): bool; } diff --git a/src/Framework/MockObject/Runtime/Invocation.php b/src/Framework/MockObject/Runtime/Invocation.php index f99ae28937b..d9e3c3f66ff 100644 --- a/src/Framework/MockObject/Runtime/Invocation.php +++ b/src/Framework/MockObject/Runtime/Invocation.php @@ -11,45 +11,49 @@ use function array_map; use function implode; -use function is_object; use function sprintf; use function str_starts_with; use function strtolower; use function substr; use PHPUnit\Framework\SelfDescribing; -use PHPUnit\Util\Cloner; -use SebastianBergmann\Exporter\Exporter; +use PHPUnit\Util\Exporter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class Invocation implements SelfDescribing { /** - * @psalm-var class-string + * @var class-string */ private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $methodName; + + /** + * @var array + */ private array $parameters; private string $returnType; private bool $isReturnTypeNullable; - private bool $proxiedCall; private MockObjectInternal|StubInternal $object; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName + * @param array $parameters */ - public function __construct(string $className, string $methodName, array $parameters, string $returnType, MockObjectInternal|StubInternal $object, bool $cloneObjects = false, bool $proxiedCall = false) + public function __construct(string $className, string $methodName, array $parameters, string $returnType, MockObjectInternal|StubInternal $object) { - $this->className = $className; - $this->methodName = $methodName; - $this->object = $object; - $this->proxiedCall = $proxiedCall; + $this->className = $className; + $this->methodName = $methodName; + $this->parameters = $parameters; + $this->object = $object; if (strtolower($methodName) === '__tostring') { $returnType = 'string'; @@ -63,24 +67,10 @@ public function __construct(string $className, string $methodName, array $parame } $this->returnType = $returnType; - - if (!$cloneObjects) { - $this->parameters = $parameters; - - return; - } - - foreach ($parameters as $key => $value) { - if (is_object($value)) { - $parameters[$key] = Cloner::clone($value); - } - } - - $this->parameters = $parameters; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -88,13 +78,16 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { return $this->methodName; } + /** + * @return array + */ public function parameters(): array { return $this->parameters; @@ -112,22 +105,20 @@ public function generateReturnValue(): mixed ); } - if ($this->isReturnTypeNullable || $this->proxiedCall) { + if ($this->isReturnTypeNullable) { return null; } return (new ReturnValueGenerator)->generate( $this->className, $this->methodName, - $this->object::class, + $this->object, $this->returnType, ); } public function toString(): string { - $exporter = new Exporter; - return sprintf( '%s::%s(%s)%s', $this->className, @@ -135,11 +126,11 @@ public function toString(): string implode( ', ', array_map( - [$exporter, 'shortenedExport'], + [Exporter::class, 'shortenedExport'], $this->parameters, ), ), - $this->returnType ? sprintf(': %s', $this->returnType) : '', + $this->returnType !== '' ? sprintf(': %s', $this->returnType) : '', ); } diff --git a/src/Framework/MockObject/Runtime/InvocationHandler.php b/src/Framework/MockObject/Runtime/InvocationHandler.php index 45baebbbf15..50f8283adce 100644 --- a/src/Framework/MockObject/Runtime/InvocationHandler.php +++ b/src/Framework/MockObject/Runtime/InvocationHandler.php @@ -11,33 +11,34 @@ use function strtolower; use Exception; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\Rule\InvocationOrder; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvocationHandler { /** - * @psalm-var list + * @var list */ private array $matchers = []; /** - * @psalm-var array + * @var array */ private array $matcherMap = []; /** - * @psalm-var list + * @var list */ private readonly array $configurableMethods; private readonly bool $returnValueGeneration; /** - * @psalm-param list $configurableMethods + * @param list $configurableMethods */ public function __construct(array $configurableMethods, bool $returnValueGeneration) { @@ -58,6 +59,8 @@ public function hasMatchers(): bool /** * Looks up the match builder with identification $id and returns it. + * + * @param non-empty-string $id */ public function lookupMatcher(string $id): ?Matcher { @@ -68,6 +71,8 @@ public function lookupMatcher(string $id): ?Matcher * Registers a matcher with the identification $id. The matcher can later be * looked up using lookupMatcher() to figure out if it has been invoked. * + * @param non-empty-string $id + * * @throws MatcherAlreadyRegisteredException */ public function registerMatcher(string $id, Matcher $matcher): void @@ -79,12 +84,12 @@ public function registerMatcher(string $id, Matcher $matcher): void $this->matcherMap[$id] = $matcher; } - public function expects(InvocationOrder $rule): InvocationMocker + public function expects(InvocationOrder $rule): InvocationStubber { $matcher = new Matcher($rule); $this->addMatcher($matcher); - return new InvocationMocker( + return new InvocationStubberImplementation( $this, $matcher, ...$this->configurableMethods, diff --git a/src/Framework/MockObject/Runtime/InvocationStubberImplementation.php b/src/Framework/MockObject/Runtime/InvocationStubberImplementation.php new file mode 100644 index 00000000000..3aca45a294b --- /dev/null +++ b/src/Framework/MockObject/Runtime/InvocationStubberImplementation.php @@ -0,0 +1,330 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function array_flip; +use function array_key_exists; +use function array_map; +use function array_merge; +use function array_pop; +use function assert; +use function count; +use function is_string; +use function range; +use function strtolower; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\InvalidArgumentException; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; +use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls; +use PHPUnit\Framework\MockObject\Stub\Exception; +use PHPUnit\Framework\MockObject\Stub\ReturnArgument; +use PHPUnit\Framework\MockObject\Stub\ReturnCallback; +use PHPUnit\Framework\MockObject\Stub\ReturnReference; +use PHPUnit\Framework\MockObject\Stub\ReturnSelf; +use PHPUnit\Framework\MockObject\Stub\ReturnStub; +use PHPUnit\Framework\MockObject\Stub\ReturnValueMap; +use PHPUnit\Framework\MockObject\Stub\Stub; +use Throwable; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvocationStubberImplementation implements InvocationStubber +{ + private readonly InvocationHandler $invocationHandler; + private readonly Matcher $matcher; + + /** + * @var list + */ + private readonly array $configurableMethods; + + /** + * @var ?array + */ + private ?array $configurableMethodNames = null; + + public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods) + { + $this->invocationHandler = $handler; + $this->matcher = $matcher; + $this->configurableMethods = $configurableMethods; + } + + /** + * @param Constraint|non-empty-string|PropertyHook $constraint + * + * @throws InvalidArgumentException + * @throws MethodCannotBeConfiguredException + * @throws MethodNameAlreadyConfiguredException + * + * @return $this + */ + public function method(Constraint|PropertyHook|string $constraint): InvocationStubber + { + if ($this->matcher->hasMethodNameRule()) { + throw new MethodNameAlreadyConfiguredException; + } + + if ($constraint instanceof PropertyHook) { + $constraint = $constraint->asString(); + } + + if (is_string($constraint)) { + $this->configurableMethodNames ??= array_flip( + array_map( + static fn (ConfigurableMethod $configurable) => strtolower($configurable->name()), + $this->configurableMethods, + ), + ); + + if (!array_key_exists(strtolower($constraint), $this->configurableMethodNames)) { + throw new MethodCannotBeConfiguredException($constraint); + } + } + + $this->matcher->setMethodNameRule(new Rule\MethodName($constraint)); + + return $this; + } + + /** + * @param non-empty-string $id + * + * @throws MatcherAlreadyRegisteredException + * + * @return $this + */ + public function id(string $id): InvocationStubber + { + $this->invocationHandler->registerMatcher($id, $this->matcher); + + return $this; + } + + /** + * @param non-empty-string $id + * + * @return $this + */ + public function after(string $id): InvocationStubber + { + $this->matcher->setAfterMatchBuilderId($id); + + return $this; + } + + /** + * @throws \PHPUnit\Framework\Exception + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function with(mixed ...$arguments): InvocationStubber + { + $this->ensureParametersCanBeConfigured(); + + $this->matcher->setParametersRule(new Rule\Parameters($arguments)); + + return $this; + } + + /** + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + * + * @return $this + */ + public function withAnyParameters(): InvocationStubber + { + $this->ensureParametersCanBeConfigured(); + + $this->matcher->setParametersRule(new Rule\AnyParameters); + + return $this; + } + + /** + * @return $this + */ + public function will(Stub $stub): InvocationStubber + { + $this->matcher->setStub($stub); + + return $this; + } + + /** + * @throws IncompatibleReturnValueException + */ + public function willReturn(mixed $value, mixed ...$nextValues): InvocationStubber + { + if (count($nextValues) === 0) { + $this->ensureTypeOfReturnValues([$value]); + + $stub = $value instanceof Stub ? $value : new ReturnStub($value); + + return $this->will($stub); + } + + $values = array_merge([$value], $nextValues); + + $this->ensureTypeOfReturnValues($values); + + $stub = new ConsecutiveCalls($values); + + return $this->will($stub); + } + + public function willReturnReference(mixed &$reference): InvocationStubber + { + $stub = new ReturnReference($reference); + + return $this->will($stub); + } + + public function willReturnMap(array $valueMap): InvocationStubber + { + $method = $this->configuredMethod(); + + assert($method instanceof ConfigurableMethod); + + $numberOfParameters = $method->numberOfParameters(); + $defaultValues = $method->defaultParameterValues(); + $hasDefaultValues = $defaultValues !== []; + + $_valueMap = []; + + foreach ($valueMap as $mapping) { + $numberOfConfiguredParameters = count($mapping) - 1; + + if ($numberOfConfiguredParameters === $numberOfParameters || !$hasDefaultValues) { + $_valueMap[] = $mapping; + + continue; + } + + $_mapping = []; + $returnValue = array_pop($mapping); + + foreach (range(0, $numberOfParameters - 1) as $i) { + if (array_key_exists($i, $mapping)) { + $_mapping[] = $mapping[$i]; + + continue; + } + + if (array_key_exists($i, $defaultValues)) { + $_mapping[] = $defaultValues[$i]; + } + } + + $_mapping[] = $returnValue; + $_valueMap[] = $_mapping; + } + + $stub = new ReturnValueMap($_valueMap); + + return $this->will($stub); + } + + public function willReturnArgument(int $argumentIndex): InvocationStubber + { + $stub = new ReturnArgument($argumentIndex); + + return $this->will($stub); + } + + public function willReturnCallback(callable $callback): InvocationStubber + { + $stub = new ReturnCallback($callback); + + return $this->will($stub); + } + + public function willReturnSelf(): InvocationStubber + { + $stub = new ReturnSelf; + + return $this->will($stub); + } + + public function willReturnOnConsecutiveCalls(mixed ...$values): InvocationStubber + { + $stub = new ConsecutiveCalls($values); + + return $this->will($stub); + } + + public function willThrowException(Throwable $exception): InvocationStubber + { + $stub = new Exception($exception); + + return $this->will($stub); + } + + /** + * @throws MethodNameNotConfiguredException + * @throws MethodParametersAlreadyConfiguredException + */ + private function ensureParametersCanBeConfigured(): void + { + if (!$this->matcher->hasMethodNameRule()) { + throw new MethodNameNotConfiguredException; + } + + if ($this->matcher->hasParametersRule()) { + throw new MethodParametersAlreadyConfiguredException; + } + } + + private function configuredMethod(): ?ConfigurableMethod + { + $configuredMethod = null; + + foreach ($this->configurableMethods as $configurableMethod) { + if ($this->matcher->methodNameRule()->matchesName($configurableMethod->name())) { + if ($configuredMethod !== null) { + return null; + } + + $configuredMethod = $configurableMethod; + } + } + + return $configuredMethod; + } + + /** + * @param array $values + * + * @throws IncompatibleReturnValueException + */ + private function ensureTypeOfReturnValues(array $values): void + { + $configuredMethod = $this->configuredMethod(); + + if ($configuredMethod === null) { + return; + } + + foreach ($values as $value) { + if (!$configuredMethod->mayReturn($value)) { + throw new IncompatibleReturnValueException( + $configuredMethod, + $value, + ); + } + } + } +} diff --git a/src/Framework/MockObject/Runtime/Matcher.php b/src/Framework/MockObject/Runtime/Matcher.php index f53c1d9fee2..f1587f3b3f1 100644 --- a/src/Framework/MockObject/Runtime/Matcher.php +++ b/src/Framework/MockObject/Runtime/Matcher.php @@ -22,11 +22,17 @@ use PHPUnit\Util\ThrowableToStringMapper; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Matcher { private readonly InvocationOrder $invocationRule; + + /** + * @var ?non-empty-string + */ private ?string $afterMatchBuilderId = null; private ?MethodName $methodNameRule = null; private ?ParametersRule $parametersRule = null; @@ -72,6 +78,9 @@ public function setStub(Stub $stub): void $this->stub = $stub; } + /** + * @param non-empty-string $id + */ public function setAfterMatchBuilderId(string $id): void { $this->afterMatchBuilderId = $id; @@ -95,7 +104,7 @@ public function invoked(Invocation $invocation): mixed ->__phpunit_getInvocationHandler() ->lookupMatcher($this->afterMatchBuilderId); - if (!$matcher) { + if ($matcher === null) { throw new MatchBuilderNotFoundException($this->afterMatchBuilderId); } } @@ -116,7 +125,7 @@ public function invoked(Invocation $invocation): mixed ); } - if ($this->stub) { + if ($this->stub !== null) { return $this->stub->invoke($invocation); } @@ -136,7 +145,7 @@ public function matches(Invocation $invocation): bool ->__phpunit_getInvocationHandler() ->lookupMatcher($this->afterMatchBuilderId); - if (!$matcher) { + if ($matcher === null) { throw new MatchBuilderNotFoundException($this->afterMatchBuilderId); } diff --git a/src/Framework/MockObject/Runtime/MethodNameConstraint.php b/src/Framework/MockObject/Runtime/MethodNameConstraint.php index 450331ecdb7..bb6bf60bdde 100644 --- a/src/Framework/MockObject/Runtime/MethodNameConstraint.php +++ b/src/Framework/MockObject/Runtime/MethodNameConstraint.php @@ -14,11 +14,13 @@ use PHPUnit\Framework\Constraint\Constraint; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class MethodNameConstraint extends Constraint { - private readonly string $methodName; + private string $methodName; public function __construct(string $methodName) { diff --git a/src/Framework/MockObject/Runtime/PropertyHook/PropertyGetHook.php b/src/Framework/MockObject/Runtime/PropertyHook/PropertyGetHook.php new file mode 100644 index 00000000000..14266f55731 --- /dev/null +++ b/src/Framework/MockObject/Runtime/PropertyHook/PropertyGetHook.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Runtime; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PropertyGetHook extends PropertyHook +{ + /** + * @return non-empty-string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function asString(): string + { + return sprintf( + '$%s::get', + $this->propertyName(), + ); + } +} diff --git a/src/Framework/MockObject/Runtime/PropertyHook/PropertyHook.php b/src/Framework/MockObject/Runtime/PropertyHook/PropertyHook.php new file mode 100644 index 00000000000..a7664f8ed76 --- /dev/null +++ b/src/Framework/MockObject/Runtime/PropertyHook/PropertyHook.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Runtime; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +abstract readonly class PropertyHook +{ + /** + * @var non-empty-string + */ + private string $propertyName; + + /** + * @param non-empty-string $propertyName + */ + public static function get(string $propertyName): PropertyGetHook + { + return new PropertyGetHook($propertyName); + } + + /** + * @param non-empty-string $propertyName + */ + public static function set(string $propertyName): PropertySetHook + { + return new PropertySetHook($propertyName); + } + + /** + * @param non-empty-string $propertyName + */ + protected function __construct(string $propertyName) + { + $this->propertyName = $propertyName; + } + + /** + * @return non-empty-string + */ + public function propertyName(): string + { + return $this->propertyName; + } + + /** + * @return non-empty-string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + abstract public function asString(): string; +} diff --git a/src/Framework/MockObject/Runtime/PropertyHook/PropertySetHook.php b/src/Framework/MockObject/Runtime/PropertyHook/PropertySetHook.php new file mode 100644 index 00000000000..7d4918eb7c6 --- /dev/null +++ b/src/Framework/MockObject/Runtime/PropertyHook/PropertySetHook.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Runtime; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class PropertySetHook extends PropertyHook +{ + /** + * @return non-empty-string + * + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function asString(): string + { + return sprintf( + '$%s::set', + $this->propertyName(), + ); + } +} diff --git a/src/Framework/MockObject/Runtime/ReturnValueGenerator.php b/src/Framework/MockObject/Runtime/ReturnValueGenerator.php index 2c2807d30e4..e429bc984f1 100644 --- a/src/Framework/MockObject/Runtime/ReturnValueGenerator.php +++ b/src/Framework/MockObject/Runtime/ReturnValueGenerator.php @@ -9,7 +9,6 @@ */ namespace PHPUnit\Framework\MockObject; -use function array_keys; use function array_map; use function explode; use function in_array; @@ -21,22 +20,24 @@ use function substr; use PHPUnit\Framework\MockObject\Generator\Generator; use ReflectionClass; +use ReflectionObject; use stdClass; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ReturnValueGenerator { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * @psalm-param class-string $stubClassName + * @param class-string $className + * @param non-empty-string $methodName * * @throws Exception */ - public function generate(string $className, string $methodName, string $stubClassName, string $returnType): mixed + public function generate(string $className, string $methodName, StubInternal $testStub, string $returnType): mixed { $intersection = false; $union = false; @@ -45,9 +46,9 @@ public function generate(string $className, string $methodName, string $stubClas $types = explode('|', $returnType); $union = true; - foreach (array_keys($types) as $key) { - if (str_starts_with($types[$key], '(') && str_ends_with($types[$key], ')')) { - $types[$key] = substr($types[$key], 1, -1); + foreach ($types as $key => $type) { + if (str_starts_with($type, '(') && str_ends_with($type, ')')) { + $types[$key] = substr($type, 1, -1); } } } elseif (str_contains($returnType, '&')) { @@ -57,59 +58,59 @@ public function generate(string $className, string $methodName, string $stubClas $types = [$returnType]; } - $types = array_map('strtolower', $types); - if (!$intersection) { - if (in_array('', $types, true) || - in_array('null', $types, true) || - in_array('mixed', $types, true) || - in_array('void', $types, true)) { + $lowerTypes = array_map('strtolower', $types); + + if (in_array('', $lowerTypes, true) || + in_array('null', $lowerTypes, true) || + in_array('mixed', $lowerTypes, true) || + in_array('void', $lowerTypes, true)) { return null; } - if (in_array('true', $types, true)) { + if (in_array('true', $lowerTypes, true)) { return true; } - if (in_array('false', $types, true) || - in_array('bool', $types, true)) { + if (in_array('false', $lowerTypes, true) || + in_array('bool', $lowerTypes, true)) { return false; } - if (in_array('float', $types, true)) { + if (in_array('float', $lowerTypes, true)) { return 0.0; } - if (in_array('int', $types, true)) { + if (in_array('int', $lowerTypes, true)) { return 0; } - if (in_array('string', $types, true)) { + if (in_array('string', $lowerTypes, true)) { return ''; } - if (in_array('array', $types, true)) { + if (in_array('array', $lowerTypes, true)) { return []; } - if (in_array('static', $types, true)) { - return $this->newInstanceOf($stubClassName, $className, $methodName); + if (in_array('static', $lowerTypes, true)) { + return $this->newInstanceOf($testStub, $className, $methodName); } - if (in_array('object', $types, true)) { + if (in_array('object', $lowerTypes, true)) { return new stdClass; } - if (in_array('callable', $types, true) || - in_array('closure', $types, true)) { + if (in_array('callable', $lowerTypes, true) || + in_array('closure', $lowerTypes, true)) { return static function (): void { }; } - if (in_array('traversable', $types, true) || - in_array('generator', $types, true) || - in_array('iterable', $types, true)) { + if (in_array('traversable', $lowerTypes, true) || + in_array('generator', $lowerTypes, true) || + in_array('iterable', $lowerTypes, true)) { $generator = static function (): \Generator { yield from []; @@ -158,7 +159,7 @@ public function generate(string $className, string $methodName, string $stubClas } /** - * @psalm-param non-empty-list $types + * @param non-empty-list $types */ private function onlyInterfaces(array $types): bool { @@ -172,16 +173,27 @@ private function onlyInterfaces(array $types): bool } /** - * @psalm-param class-string $stubClassName - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * * @throws RuntimeException */ - private function newInstanceOf(string $stubClassName, string $className, string $methodName): MockObject + private function newInstanceOf(StubInternal $testStub, string $className, string $methodName): Stub { try { - return (new ReflectionClass($stubClassName))->newInstanceWithoutConstructor(); + $object = (new ReflectionClass($testStub::class))->newInstanceWithoutConstructor(); + $reflector = new ReflectionObject($object); + + $reflector->getProperty('__phpunit_state')->setValue( + $object, + new TestDoubleState( + $testStub->__phpunit_state()->configurableMethods(), + $testStub->__phpunit_state()->generateReturnValues(), + ), + ); + + return $object; + // @codeCoverageIgnoreStart } catch (Throwable $t) { throw new RuntimeException( sprintf( @@ -191,20 +203,22 @@ private function newInstanceOf(string $stubClassName, string $className, string $t->getMessage(), ), ); + // @codeCoverageIgnoreEnd } } /** - * @psalm-param class-string $type - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $type + * @param class-string $className + * @param non-empty-string $methodName * * @throws RuntimeException */ private function testDoubleFor(string $type, string $className, string $methodName): Stub { try { - return (new Generator)->testDouble($type, false, false, [], [], '', false); + return (new Generator)->testDouble($type, false, [], [], '', false); + // @codeCoverageIgnoreStart } catch (Throwable $t) { throw new RuntimeException( sprintf( @@ -214,13 +228,14 @@ private function testDoubleFor(string $type, string $className, string $methodNa $t->getMessage(), ), ); + // @codeCoverageIgnoreEnd } } /** - * @psalm-param non-empty-list $types - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param non-empty-list $types + * @param class-string $className + * @param non-empty-string $methodName * * @throws RuntimeException */ @@ -228,6 +243,7 @@ private function testDoubleForIntersectionOfInterfaces(array $types, string $cla { try { return (new Generator)->testDoubleForInterfaceIntersection($types, false); + // @codeCoverageIgnoreStart } catch (Throwable $t) { throw new RuntimeException( sprintf( @@ -237,6 +253,7 @@ private function testDoubleForIntersectionOfInterfaces(array $types, string $cla $t->getMessage(), ), ); + // @codeCoverageIgnoreEnd } } } diff --git a/src/Framework/MockObject/Runtime/Rule/AnyInvokedCount.php b/src/Framework/MockObject/Runtime/Rule/AnyInvokedCount.php index 1dd52081182..382f9308116 100644 --- a/src/Framework/MockObject/Runtime/Rule/AnyInvokedCount.php +++ b/src/Framework/MockObject/Runtime/Rule/AnyInvokedCount.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class AnyInvokedCount extends InvocationOrder diff --git a/src/Framework/MockObject/Runtime/Rule/AnyParameters.php b/src/Framework/MockObject/Runtime/Rule/AnyParameters.php index 75232027835..01a54d1b425 100644 --- a/src/Framework/MockObject/Runtime/Rule/AnyParameters.php +++ b/src/Framework/MockObject/Runtime/Rule/AnyParameters.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class AnyParameters implements ParametersRule diff --git a/src/Framework/MockObject/Runtime/Rule/InvocationOrder.php b/src/Framework/MockObject/Runtime/Rule/InvocationOrder.php index 2d7a7d25e46..c3fb8f29d83 100644 --- a/src/Framework/MockObject/Runtime/Rule/InvocationOrder.php +++ b/src/Framework/MockObject/Runtime/Rule/InvocationOrder.php @@ -14,12 +14,14 @@ use PHPUnit\Framework\SelfDescribing; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ abstract class InvocationOrder implements SelfDescribing { /** - * @psalm-var list + * @var list */ private array $invocations = []; diff --git a/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.php b/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.php index d6d251bd860..a78d933d194 100644 --- a/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.php +++ b/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastCount.php @@ -14,6 +14,8 @@ use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvokedAtLeastCount extends InvocationOrder diff --git a/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php b/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php index efe51838600..91026f53d3f 100644 --- a/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php +++ b/src/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvokedAtLeastOnce extends InvocationOrder diff --git a/src/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.php b/src/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.php index 64ed9403aa5..0cfda5e182d 100644 --- a/src/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.php +++ b/src/Framework/MockObject/Runtime/Rule/InvokedAtMostCount.php @@ -14,6 +14,8 @@ use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvokedAtMostCount extends InvocationOrder diff --git a/src/Framework/MockObject/Runtime/Rule/InvokedCount.php b/src/Framework/MockObject/Runtime/Rule/InvokedCount.php index 638317cac4d..3f0e505a0db 100644 --- a/src/Framework/MockObject/Runtime/Rule/InvokedCount.php +++ b/src/Framework/MockObject/Runtime/Rule/InvokedCount.php @@ -14,6 +14,8 @@ use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvokedCount extends InvocationOrder diff --git a/src/Framework/MockObject/Runtime/Rule/MethodName.php b/src/Framework/MockObject/Runtime/Rule/MethodName.php index ed4b65a9af2..219a8087d8d 100644 --- a/src/Framework/MockObject/Runtime/Rule/MethodName.php +++ b/src/Framework/MockObject/Runtime/Rule/MethodName.php @@ -11,11 +11,14 @@ use function is_string; use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\InvalidArgumentException; use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; use PHPUnit\Framework\MockObject\MethodNameConstraint; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class MethodName @@ -40,7 +43,7 @@ public function toString(): string } /** - * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws ExpectationFailedException */ public function matches(BaseInvocation $invocation): bool { @@ -48,7 +51,7 @@ public function matches(BaseInvocation $invocation): bool } /** - * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws ExpectationFailedException */ public function matchesName(string $methodName): bool { diff --git a/src/Framework/MockObject/Runtime/Rule/Parameters.php b/src/Framework/MockObject/Runtime/Rule/Parameters.php index 4119a0ec961..17df3f0a73a 100644 --- a/src/Framework/MockObject/Runtime/Rule/Parameters.php +++ b/src/Framework/MockObject/Runtime/Rule/Parameters.php @@ -12,31 +12,37 @@ use function count; use function sprintf; use Exception; +use PHPUnit\Framework\Constraint\Callback; use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\Constraint\IsAnything; use PHPUnit\Framework\Constraint\IsEqual; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Util\Test; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Parameters implements ParametersRule { /** - * @psalm-var list + * @var list */ private array $parameters = []; private ?BaseInvocation $invocation = null; private null|bool|ExpectationFailedException $parameterVerificationResult; /** + * @param array $parameters + * * @throws \PHPUnit\Framework\Exception */ public function __construct(array $parameters) { foreach ($parameters as $parameter) { - if (!($parameter instanceof Constraint)) { + if (!$parameter instanceof Constraint) { $parameter = new IsEqual( $parameter, ); @@ -85,7 +91,7 @@ private function doVerify(): bool } if ($this->invocation === null) { - throw new ExpectationFailedException('Mocked method does not exist.'); + throw new ExpectationFailedException('Doubled method does not exist.'); } if (count($this->invocation->parameters()) < count($this->parameters)) { @@ -100,17 +106,26 @@ private function doVerify(): bool $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead."; } + $this->incrementAssertionCount(); + throw new ExpectationFailedException( sprintf($message, $this->invocation->toString()), ); } foreach ($this->parameters as $i => $parameter) { + if ($parameter instanceof Callback && $parameter->isVariadic()) { + $other = $this->invocation->parameters(); + } else { + $other = $this->invocation->parameters()[$i]; + } + + $this->incrementAssertionCount(); + $parameter->evaluate( - $this->invocation->parameters()[$i], + $other, sprintf( - 'Parameter %s for invocation %s does not match expected ' . - 'value.', + 'Parameter %s for invocation %s does not match expected value.', $i, $this->invocation->toString(), ), @@ -131,4 +146,9 @@ private function guardAgainstDuplicateEvaluationOfParameterConstraints(): bool return (bool) $this->parameterVerificationResult; } + + private function incrementAssertionCount(): void + { + Test::currentTestCase()->addToAssertionCount(1); + } } diff --git a/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php b/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php index 155226ee7e9..b518f1cf4b3 100644 --- a/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php +++ b/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php @@ -10,22 +10,44 @@ namespace PHPUnit\Framework\MockObject\Stub; use function array_shift; +use function count; use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\NoMoreReturnValuesConfiguredException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ConsecutiveCalls implements Stub { + /** + * @var array + */ private array $stack; + private int $numberOfConfiguredReturnValues; + /** + * @param array $stack + */ public function __construct(array $stack) { - $this->stack = $stack; + $this->stack = $stack; + $this->numberOfConfiguredReturnValues = count($stack); } + /** + * @throws NoMoreReturnValuesConfiguredException + */ public function invoke(Invocation $invocation): mixed { + if ($this->stack === []) { + throw new NoMoreReturnValuesConfiguredException( + $invocation, + $this->numberOfConfiguredReturnValues, + ); + } + $value = array_shift($this->stack); if ($value instanceof Stub) { diff --git a/src/Framework/MockObject/Runtime/Stub/Exception.php b/src/Framework/MockObject/Runtime/Stub/Exception.php index 04bd1303802..ce0a6804a2c 100644 --- a/src/Framework/MockObject/Runtime/Stub/Exception.php +++ b/src/Framework/MockObject/Runtime/Stub/Exception.php @@ -13,6 +13,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class Exception implements Stub diff --git a/src/Framework/MockObject/Runtime/Stub/ReturnArgument.php b/src/Framework/MockObject/Runtime/Stub/ReturnArgument.php index 5172f64b45d..daca5099d03 100644 --- a/src/Framework/MockObject/Runtime/Stub/ReturnArgument.php +++ b/src/Framework/MockObject/Runtime/Stub/ReturnArgument.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Invocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ReturnArgument implements Stub diff --git a/src/Framework/MockObject/Runtime/Stub/ReturnCallback.php b/src/Framework/MockObject/Runtime/Stub/ReturnCallback.php index be4a01512d0..4e4cd531025 100644 --- a/src/Framework/MockObject/Runtime/Stub/ReturnCallback.php +++ b/src/Framework/MockObject/Runtime/Stub/ReturnCallback.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\MockObject\Invocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ReturnCallback implements Stub diff --git a/src/Framework/MockObject/Runtime/Stub/ReturnReference.php b/src/Framework/MockObject/Runtime/Stub/ReturnReference.php index fd9e34debb1..448df45273d 100644 --- a/src/Framework/MockObject/Runtime/Stub/ReturnReference.php +++ b/src/Framework/MockObject/Runtime/Stub/ReturnReference.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Invocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ReturnReference implements Stub diff --git a/src/Framework/MockObject/Runtime/Stub/ReturnSelf.php b/src/Framework/MockObject/Runtime/Stub/ReturnSelf.php index 059ef18b2c2..4101d71acd1 100644 --- a/src/Framework/MockObject/Runtime/Stub/ReturnSelf.php +++ b/src/Framework/MockObject/Runtime/Stub/ReturnSelf.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\MockObject\RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ReturnSelf implements Stub diff --git a/src/Framework/MockObject/Runtime/Stub/ReturnStub.php b/src/Framework/MockObject/Runtime/Stub/ReturnStub.php index eb888c38dff..a2d881c3344 100644 --- a/src/Framework/MockObject/Runtime/Stub/ReturnStub.php +++ b/src/Framework/MockObject/Runtime/Stub/ReturnStub.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Invocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ReturnStub implements Stub diff --git a/src/Framework/MockObject/Runtime/Stub/ReturnValueMap.php b/src/Framework/MockObject/Runtime/Stub/ReturnValueMap.php index 68db6cb92c0..c54abc35370 100644 --- a/src/Framework/MockObject/Runtime/Stub/ReturnValueMap.php +++ b/src/Framework/MockObject/Runtime/Stub/ReturnValueMap.php @@ -15,12 +15,20 @@ use PHPUnit\Framework\MockObject\Invocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ReturnValueMap implements Stub { + /** + * @var array + */ private array $valueMap; + /** + * @param array $valueMap + */ public function __construct(array $valueMap) { $this->valueMap = $valueMap; diff --git a/src/Framework/MockObject/Runtime/Stub/Stub.php b/src/Framework/MockObject/Runtime/Stub/Stub.php index e889adfe74f..46d9e53a933 100644 --- a/src/Framework/MockObject/Runtime/Stub/Stub.php +++ b/src/Framework/MockObject/Runtime/Stub/Stub.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\MockObject\Invocation; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Stub diff --git a/src/Framework/NativeType.php b/src/Framework/NativeType.php new file mode 100644 index 00000000000..0220e8845be --- /dev/null +++ b/src/Framework/NativeType.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +enum NativeType: string +{ + case Array = 'array'; + case Bool = 'bool'; + case Callable = 'callable'; + case ClosedResource = 'resource (closed)'; + case Float = 'float'; + case Int = 'int'; + case Iterable = 'iterable'; + case Null = 'null'; + case Numeric = 'numeric'; + case Object = 'object'; + case Resource = 'resource'; + case Scalar = 'scalar'; + case String = 'string'; +} diff --git a/src/Framework/Reorderable.php b/src/Framework/Reorderable.php index a75e8b946f5..10f6e9431e1 100644 --- a/src/Framework/Reorderable.php +++ b/src/Framework/Reorderable.php @@ -10,6 +10,8 @@ namespace PHPUnit\Framework; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Reorderable @@ -17,12 +19,12 @@ interface Reorderable public function sortId(): string; /** - * @psalm-return list + * @return list */ public function provides(): array; /** - * @psalm-return list + * @return list */ public function requires(): array; } diff --git a/src/Framework/SelfDescribing.php b/src/Framework/SelfDescribing.php index 73034f650ab..bdf91a4d118 100644 --- a/src/Framework/SelfDescribing.php +++ b/src/Framework/SelfDescribing.php @@ -10,7 +10,9 @@ namespace PHPUnit\Framework; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface SelfDescribing { diff --git a/src/Framework/TestBuilder.php b/src/Framework/TestBuilder.php index 8e5e019dfe0..dba8e6096e9 100644 --- a/src/Framework/TestBuilder.php +++ b/src/Framework/TestBuilder.php @@ -9,9 +9,11 @@ */ namespace PHPUnit\Framework; +use function array_merge; use function assert; use PHPUnit\Metadata\Api\DataProvider; use PHPUnit\Metadata\Api\Groups; +use PHPUnit\Metadata\Api\Requirements; use PHPUnit\Metadata\BackupGlobals; use PHPUnit\Metadata\BackupStaticProperties; use PHPUnit\Metadata\ExcludeGlobalVariableFromBackup; @@ -22,23 +24,28 @@ use ReflectionClass; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestBuilder +final readonly class TestBuilder { /** - * @psalm-param non-empty-string $methodName + * @param ReflectionClass $theClass + * @param non-empty-string $methodName + * @param list $groups * * @throws InvalidDataProviderException */ - public function build(ReflectionClass $theClass, string $methodName): Test + public function build(ReflectionClass $theClass, string $methodName, array $groups = []): Test { $className = $theClass->getName(); - $data = (new DataProvider)->providedData( - $className, - $methodName, - ); + $data = null; + + if ($this->requirementsSatisfied($className, $methodName)) { + $data = (new DataProvider)->providedData($className, $methodName); + } if ($data !== null) { return $this->buildDataProviderTestSuite( @@ -49,13 +56,12 @@ public function build(ReflectionClass $theClass, string $methodName): Test $this->shouldGlobalStateBePreserved($className, $methodName), $this->shouldAllTestMethodsOfTestClassBeRunInSingleSeparateProcess($className), $this->backupSettings($className, $methodName), + $groups, ); } $test = new $className($methodName); - assert($test instanceof TestCase); - $this->configureTestCase( $test, $this->shouldTestMethodBeRunInSeparateProcess($className, $methodName), @@ -68,23 +74,26 @@ public function build(ReflectionClass $theClass, string $methodName): Test } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * @psalm-param array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} $backupSettings + * @param non-empty-string $methodName + * @param class-string $className + * @param array> $data + * @param array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} $backupSettings + * @param list $groups */ - private function buildDataProviderTestSuite(string $methodName, string $className, array $data, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings): DataProviderTestSuite + private function buildDataProviderTestSuite(string $methodName, string $className, array $data, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings, array $groups): DataProviderTestSuite { $dataProviderTestSuite = DataProviderTestSuite::empty( $className . '::' . $methodName, ); - $groups = (new Groups)->groups($className, $methodName); + $groups = array_merge( + $groups, + (new Groups)->groups($className, $methodName), + ); foreach ($data as $_dataName => $_data) { $_test = new $className($methodName); - assert($_test instanceof TestCase); - $_test->setData($_dataName, $_data); $this->configureTestCase( @@ -102,7 +111,7 @@ private function buildDataProviderTestSuite(string $methodName, string $classNam } /** - * @psalm-param array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} $backupSettings + * @param array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} $backupSettings */ private function configureTestCase(TestCase $test, bool $runTestInSeparateProcess, ?bool $preserveGlobalState, bool $runClassInSeparateProcess, array $backupSettings): void { @@ -136,10 +145,10 @@ private function configureTestCase(TestCase $test, bool $runTestInSeparateProces } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * - * @psalm-return array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} + * @return array{backupGlobals: ?bool, backupGlobalsExcludeList: list, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array>} */ private function backupSettings(string $className, string $methodName): array { @@ -214,8 +223,8 @@ private function backupSettings(string $className, string $methodName): array } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ private function shouldGlobalStateBePreserved(string $className, string $methodName): ?bool { @@ -243,8 +252,8 @@ private function shouldGlobalStateBePreserved(string $className, string $methodN } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ private function shouldTestMethodBeRunInSeparateProcess(string $className, string $methodName): bool { @@ -260,10 +269,19 @@ private function shouldTestMethodBeRunInSeparateProcess(string $className, strin } /** - * @psalm-param class-string $className + * @param class-string $className */ private function shouldAllTestMethodsOfTestClassBeRunInSingleSeparateProcess(string $className): bool { return MetadataRegistry::parser()->forClass($className)->isRunClassInSeparateProcess()->isNotEmpty(); } + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + private function requirementsSatisfied(string $className, string $methodName): bool + { + return (new Requirements)->requirementsNotSatisfiedFor($className, $methodName) === []; + } } diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index e2bc0f4cf38..20baa121ba5 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -9,26 +9,20 @@ */ namespace PHPUnit\Framework; -use const LC_ALL; -use const LC_COLLATE; -use const LC_CTYPE; -use const LC_MONETARY; -use const LC_NUMERIC; -use const LC_TIME; -use const PATHINFO_FILENAME; use const PHP_EOL; -use const PHP_URL_PATH; use function array_keys; use function array_merge; +use function array_reverse; use function array_values; use function assert; -use function basename; use function chdir; use function class_exists; use function clearstatcache; use function count; use function defined; +use function error_clear_last; use function explode; +use function fclose; use function getcwd; use function implode; use function in_array; @@ -45,22 +39,28 @@ use function ob_get_contents; use function ob_get_level; use function ob_start; -use function parse_url; -use function pathinfo; +use function preg_match; use function preg_replace; -use function setlocale; +use function putenv; +use function restore_error_handler; +use function restore_exception_handler; +use function set_error_handler; +use function set_exception_handler; use function sprintf; use function str_contains; +use function stream_get_contents; +use function stream_get_meta_data; +use function tmpfile; use function trim; use AssertionError; use DeepCopy\DeepCopy; use PHPUnit\Event; use PHPUnit\Event\NoPreviousThrowableException; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint; use PHPUnit\Framework\Constraint\ExceptionCode; use PHPUnit\Framework\Constraint\ExceptionMessageIsOrContains; use PHPUnit\Framework\Constraint\ExceptionMessageMatchesRegularExpression; +use PHPUnit\Framework\MockObject\Exception as MockObjectException; use PHPUnit\Framework\MockObject\Generator\Generator as MockGenerator; use PHPUnit\Framework\MockObject\MockBuilder; use PHPUnit\Framework\MockObject\MockObject; @@ -69,41 +69,37 @@ use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher; use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher; +use PHPUnit\Framework\MockObject\Rule\InvokedCount; use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher; use PHPUnit\Framework\MockObject\Stub; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; use PHPUnit\Framework\TestSize\TestSize; use PHPUnit\Framework\TestStatus\TestStatus; use PHPUnit\Metadata\Api\Groups; use PHPUnit\Metadata\Api\HookMethods; use PHPUnit\Metadata\Api\Requirements; use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; +use PHPUnit\Metadata\WithEnvironmentVariable; +use PHPUnit\Runner\BackedUpEnvironmentVariable; +use PHPUnit\Runner\DeprecationCollector\Facade as DeprecationCollector; +use PHPUnit\Runner\HookMethodCollection; use PHPUnit\TestRunner\TestResult\PassedTests; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; -use PHPUnit\Util\Cloner; +use PHPUnit\Util\Exporter; use PHPUnit\Util\Test as TestUtil; use ReflectionClass; use ReflectionException; use ReflectionObject; -use SebastianBergmann\CodeCoverage\StaticAnalysisCacheNotConfiguredException; use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; use SebastianBergmann\Comparator\Comparator; use SebastianBergmann\Comparator\Factory as ComparatorFactory; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; -use SebastianBergmann\Exporter\Exporter; use SebastianBergmann\GlobalState\ExcludeList as GlobalStateExcludeList; use SebastianBergmann\GlobalState\Restorer; use SebastianBergmann\GlobalState\Snapshot; use SebastianBergmann\Invoker\TimeoutException; use SebastianBergmann\ObjectEnumerator\Enumerator; -use SebastianBergmann\RecursionContext\Context; use Throwable; /** @@ -111,64 +107,83 @@ */ abstract class TestCase extends Assert implements Reorderable, SelfDescribing, Test { - private const LOCALE_CATEGORIES = [LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME]; - private ?bool $backupGlobals = null; + private ?bool $backupGlobals = null; /** - * @psalm-var list + * @var list */ private array $backupGlobalsExcludeList = []; private ?bool $backupStaticProperties = null; /** - * @psalm-var array> + * @var array> */ private array $backupStaticPropertiesExcludeList = []; private ?Snapshot $snapshot = null; - private ?bool $runClassInSeparateProcess = null; - private ?bool $runTestInSeparateProcess = null; - private bool $preserveGlobalState = false; - private bool $inIsolation = false; - private ?string $expectedException = null; - private ?string $expectedExceptionMessage = null; - private ?string $expectedExceptionMessageRegExp = null; - private null|int|string $expectedExceptionCode = null; /** - * @psalm-var list + * @var list + */ + private ?array $backupGlobalErrorHandlers = null; + + /** + * @var list + */ + private ?array $backupGlobalExceptionHandlers = null; + private ?bool $runClassInSeparateProcess = null; + private ?bool $runTestInSeparateProcess = null; + private bool $preserveGlobalState = false; + private bool $inIsolation = false; + private ?string $expectedException = null; + private ?string $expectedExceptionMessage = null; + private ?string $expectedExceptionMessageRegExp = null; + private null|int|string $expectedExceptionCode = null; + + /** + * @var list + */ + private array $backupEnvironmentVariables = []; + + /** + * @var list */ private array $providedTests = []; + + /** + * @var array + */ private array $data = []; private int|string $dataName = ''; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private string $name; + private string $methodName; /** - * @psalm-var list + * @var list */ private array $groups = []; /** - * @psalm-var list + * @var list */ - private array $dependencies = []; - private array $dependencyInput = []; + private array $dependencies = []; /** - * @psalm-var array + * @var array> */ - private array $iniSettings = []; - private array $locale = []; + private array $dependencyInput = []; /** - * @psalm-var list + * @var list */ - private array $mockObjects = []; - private bool $registerMockObjectsFromTestArgumentsRecursively = false; + private array $mockObjects = []; private TestStatus $status; + + /** + * @var 0|positive-int + */ private int $numberOfAssertionsPerformed = 0; private mixed $testResult = null; private string $output = ''; @@ -178,33 +193,49 @@ abstract class TestCase extends Assert implements Reorderable, SelfDescribing, T private int $outputBufferingLevel; private bool $outputRetrievedForAssertion = false; private bool $doesNotPerformAssertions = false; + private bool $expectErrorLog = false; /** - * @psalm-var list + * @var list */ private array $customComparators = []; private ?Event\Code\TestMethod $testValueObjectForEvents = null; private bool $wasPrepared = false; /** - * @psalm-var array + * @var array */ private array $failureTypes = []; /** - * @psalm-param non-empty-string $name + * @var list + */ + private array $expectedUserDeprecationMessage = []; + + /** + * @var list + */ + private array $expectedUserDeprecationMessageRegularExpression = []; + + /** + * @param non-empty-string $name * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ - public function __construct(string $name) + final public function __construct(string $name) { - $this->setName($name); + $this->methodName = $name; + $this->status = TestStatus::unknown(); - $this->status = TestStatus::unknown(); + if (is_callable($this->sortId(), true)) { + $this->providedTests = [new ExecutionOrderDependency($this->sortId())]; + } } /** * This method is called before the first test of this test class is run. + * + * @codeCoverageIgnore */ public static function setUpBeforeClass(): void { @@ -212,6 +243,8 @@ public static function setUpBeforeClass(): void /** * This method is called after the last test of this test class is run. + * + * @codeCoverageIgnore */ public static function tearDownAfterClass(): void { @@ -219,6 +252,8 @@ public static function tearDownAfterClass(): void /** * This method is called before each test. + * + * @codeCoverageIgnore */ protected function setUp(): void { @@ -228,6 +263,8 @@ protected function setUp(): void * Performs assertions shared by all tests of a test case. * * This method is called between setUp() and test. + * + * @codeCoverageIgnore */ protected function assertPreConditions(): void { @@ -237,6 +274,8 @@ protected function assertPreConditions(): void * Performs assertions shared by all tests of a test case. * * This method is called between test and tearDown(). + * + * @codeCoverageIgnore */ protected function assertPostConditions(): void { @@ -244,6 +283,8 @@ protected function assertPostConditions(): void /** * This method is called after each test. + * + * @codeCoverageIgnore */ protected function tearDown(): void { @@ -261,7 +302,7 @@ public function toString(): string $buffer = sprintf( '%s::%s', (new ReflectionClass($this))->getName(), - $this->name, + $this->methodName, ); return $buffer . $this->dataSetAsStringWithData(); @@ -288,12 +329,9 @@ final public function status(): TestStatus * @throws \PHPUnit\Util\Exception * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException * @throws \SebastianBergmann\Template\InvalidArgumentException - * @throws CodeCoverageException * @throws Exception - * @throws NoDataSetFromDataProviderException * @throws NoPreviousThrowableException * @throws ProcessIsolationException - * @throws StaticAnalysisCacheNotConfiguredException * @throws UnintentionallyCoveredCodeException * * @internal This method is not covered by the backward compatibility promise for PHPUnit @@ -304,18 +342,22 @@ final public function run(): void return; } - if (!$this->shouldRunInSeparateProcess()) { + if (!$this->shouldRunInSeparateProcess() || $this->requirementsNotSatisfied()) { (new TestRunner)->run($this); - } else { - (new TestRunner)->runInSeparateProcess( - $this, - $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess, - $this->preserveGlobalState, - ); + + return; } + + IsolatedTestRunnerRegistry::run( + $this, + $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess, + $this->preserveGlobalState, + ); } /** + * @return list + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function groups(): array @@ -324,6 +366,8 @@ final public function groups(): array } /** + * @param list $groups + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function setGroups(array $groups): void @@ -336,17 +380,17 @@ final public function setGroups(array $groups): void */ final public function nameWithDataSet(): string { - return $this->name . $this->dataSetAsString(); + return $this->methodName . $this->dataSetAsString(); } /** - * @psalm-return non-empty-string + * @return non-empty-string * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function name(): string { - return $this->name; + return $this->methodName; } /** @@ -356,12 +400,14 @@ final public function size(): TestSize { return (new Groups)->size( static::class, - $this->name, + $this->methodName, ); } /** * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @phpstan-assert-if-true non-empty-string $this->output() */ final public function hasUnexpectedOutput(): bool { @@ -404,14 +450,6 @@ final public function expectsOutput(): bool return $this->hasExpectationOnOutput() || $this->outputRetrievedForAssertion; } - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - final public function registerMockObjectsFromTestArgumentsRecursively(): void - { - $this->registerMockObjectsFromTestArgumentsRecursively = true; - } - /** * @throws Throwable * @@ -421,13 +459,17 @@ final public function runBare(): void { $emitter = Event\Facade::emitter(); + error_clear_last(); + clearstatcache(); + $emitter->testPreparationStarted( $this->valueObjectForEvents(), ); $this->snapshotGlobalState(); + $this->snapshotGlobalErrorExceptionHandlers(); + $this->handleEnvironmentVariables(); $this->startOutputBuffering(); - clearstatcache(); $hookMethods = (new HookMethods)->hookMethods(static::class); $hasMetRequirements = false; @@ -439,11 +481,13 @@ final public function runBare(): void $hasMetRequirements = true; if ($this->inIsolation) { + // @codeCoverageIgnoreStart $this->invokeBeforeClassHookMethods($hookMethods, $emitter); + // @codeCoverageIgnoreEnd } - if (method_exists(static::class, $this->name) && - MetadataRegistry::parser()->forClassAndMethod(static::class, $this->name)->isDoesNotPerformAssertions()->isNotEmpty()) { + if (method_exists(static::class, $this->methodName) && + MetadataRegistry::parser()->forClassAndMethod(static::class, $this->methodName)->isDoesNotPerformAssertions()->isNotEmpty()) { $this->doesNotPerformAssertions = true; } @@ -457,6 +501,7 @@ final public function runBare(): void $this->wasPrepared = true; $this->testResult = $this->runTest(); + $this->verifyDeprecationExpectations(); $this->verifyMockObjects(); $this->invokePostConditionHookMethods($hookMethods, $emitter); @@ -476,11 +521,14 @@ final public function runBare(): void $e->getMessage(), ); } catch (AssertionError|AssertionFailedError $e) { + $this->handleExceptionFromInvokedCountMockObjectRule($e); + if (!$this->wasPrepared) { $this->wasPrepared = true; $emitter->testPreparationFailed( $this->valueObjectForEvents(), + Event\Code\ThrowableBuilder::from($e), ); } @@ -492,7 +540,6 @@ final public function runBare(): void Event\Code\ComparisonFailureBuilder::from($e), ); } catch (TimeoutException $e) { - $this->status = TestStatus::risky($e->getMessage()); } catch (Throwable $_e) { if ($this->isRegisteredFailure($_e)) { $this->status = TestStatus::failure($_e->getMessage()); @@ -507,6 +554,20 @@ final public function runBare(): void $this->status = TestStatus::error($e->getMessage()); + if (!$this->wasPrepared) { + if ($e instanceof AssertionFailedError) { + $emitter->testPreparationFailed( + $this->valueObjectForEvents(), + Event\Code\ThrowableBuilder::from($e), + ); + } else { + $emitter->testPreparationErrored( + $this->valueObjectForEvents(), + Event\Code\ThrowableBuilder::from($e), + ); + } + } + $emitter->testErrored( $this->valueObjectForEvents(), Event\Code\ThrowableBuilder::from($e), @@ -524,21 +585,17 @@ final public function runBare(): void $this->performAssertionsOnOutput(); } - if ($this->status->isSuccess()) { - Event\Facade::emitter()->testPassed( + try { + $this->mockObjects = []; + + /** @phpstan-ignore catch.neverThrown */ + } catch (Throwable $e) { + Event\Facade::emitter()->testErrored( $this->valueObjectForEvents(), + Event\Code\ThrowableBuilder::from($e), ); - - if (!$this->usesDataProvider()) { - PassedTests::instance()->testMethodPassed( - $this->valueObjectForEvents(), - $this->testResult, - ); - } } - $this->mockObjects = []; - // Tear down the fixture. An exception raised in tearDown() will be // caught and passed on when no exception was raised before. try { @@ -546,7 +603,9 @@ final public function runBare(): void $this->invokeAfterTestHookMethods($hookMethods, $emitter); if ($this->inIsolation) { + // @codeCoverageIgnoreStart $this->invokeAfterClassHookMethods($hookMethods, $emitter); + // @codeCoverageIgnoreEnd } } } catch (AssertionError|AssertionFailedError $e) { @@ -569,20 +628,33 @@ final public function runBare(): void } } + if (!isset($e) && !isset($_e)) { + $emitter->testPassed( + $this->valueObjectForEvents(), + ); + + if (!$this->usesDataProvider()) { + PassedTests::instance()->testMethodPassed( + $this->valueObjectForEvents(), + $this->testResult, + ); + } + } + if (!$outputBufferingStopped) { $this->stopOutputBuffering(); } clearstatcache(); - if ($currentWorkingDirectory !== getcwd()) { + if ($currentWorkingDirectory !== false && $currentWorkingDirectory !== getcwd()) { chdir($currentWorkingDirectory); } + $this->restoreEnvironmentVariables(); + $this->restoreGlobalErrorExceptionHandlers(); $this->restoreGlobalState(); $this->unregisterCustomComparators(); - $this->cleanupIniSettings(); - $this->cleanupLocaleSettings(); libxml_clear_errors(); $this->testValueObjectForEvents = null; @@ -593,21 +665,7 @@ final public function runBare(): void } /** - * @psalm-param non-empty-string $name - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - final public function setName(string $name): void - { - $this->name = $name; - - if (is_callable($this->sortId(), true)) { - $this->providedTests = [new ExecutionOrderDependency($this->sortId())]; - } - } - - /** - * @psalm-param list $dependencies + * @param list $dependencies * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ @@ -617,7 +675,11 @@ final public function setDependencies(array $dependencies): void } /** + * @param array> $dependencyInput + * * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ final public function setDependencyInput(array $dependencyInput): void { @@ -625,6 +687,8 @@ final public function setDependencyInput(array $dependencyInput): void } /** + * @return array> + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function dependencyInput(): array @@ -637,7 +701,7 @@ final public function dependencyInput(): array */ final public function hasDependencyInput(): bool { - return !empty($this->dependencyInput); + return $this->dependencyInput !== []; } /** @@ -649,6 +713,8 @@ final public function setBackupGlobals(bool $backupGlobals): void } /** + * @param list $backupGlobalsExcludeList + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function setBackupGlobalsExcludeList(array $backupGlobalsExcludeList): void @@ -665,6 +731,8 @@ final public function setBackupStaticProperties(bool $backupStaticProperties): v } /** + * @param array> $backupStaticPropertiesExcludeList + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function setBackupStaticPropertiesExcludeList(array $backupStaticPropertiesExcludeList): void @@ -700,6 +768,8 @@ final public function setPreserveGlobalState(bool $preserveGlobalState): void /** * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ final public function setInIsolation(bool $inIsolation): void { @@ -708,14 +778,8 @@ final public function setInIsolation(bool $inIsolation): void /** * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - final public function isInIsolation(): bool - { - return $this->inIsolation; - } - - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ final public function result(): mixed { @@ -750,6 +814,8 @@ final public function addToAssertionCount(int $count): void /** * @internal This method is not covered by the backward compatibility promise for PHPUnit + * + * @return 0|positive-int */ final public function numberOfAssertionsPerformed(): int { @@ -761,7 +827,7 @@ final public function numberOfAssertionsPerformed(): int */ final public function usesDataProvider(): bool { - return !empty($this->data); + return $this->data !== []; } /** @@ -779,7 +845,7 @@ final public function dataSetAsString(): string { $buffer = ''; - if (!empty($this->data)) { + if ($this->data !== []) { if (is_int($this->dataName)) { $buffer .= sprintf(' with data set #%d', $this->dataName); } else { @@ -795,17 +861,19 @@ final public function dataSetAsString(): string */ final public function dataSetAsStringWithData(): string { - if (empty($this->data)) { + if ($this->data === []) { return ''; } return $this->dataSetAsString() . sprintf( ' (%s)', - (new Exporter)->shortenedRecursiveExport($this->data), + Exporter::shortenedRecursiveExport($this->data), ); } /** + * @return array + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function providedData(): array @@ -818,7 +886,7 @@ final public function providedData(): array */ final public function sortId(): string { - $id = $this->name; + $id = $this->methodName; if (!str_contains($id, '::')) { $id = static::class . '::' . $id; @@ -832,7 +900,7 @@ final public function sortId(): string } /** - * @psalm-return list + * @return list * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ @@ -842,7 +910,7 @@ final public function provides(): array } /** - * @psalm-return list + * @return list * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ @@ -852,6 +920,8 @@ final public function requires(): array } /** + * @param array $data + * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ final public function setData(int|string $dataName, array $data): void @@ -944,96 +1014,11 @@ final protected function atMost(int $allowedInvocations): InvokedAtMostCountMatc return new InvokedAtMostCountMatcher($allowedInvocations); } - /** - * @deprecated Use $double->willReturn() instead of $double->will($this->returnValue()) - * @see https://github.com/sebastianbergmann/phpunit/issues/5423 - */ - final protected function returnValue(mixed $value): ReturnStub - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'returnValue() is deprecated and will be removed in PHPUnit 12. Use $double->willReturn() instead of $double->will($this->returnValue())', - ); - - return new ReturnStub($value); - } - - /** - * @deprecated Use $double->willReturnMap() instead of $double->will($this->returnValueMap()) - * @see https://github.com/sebastianbergmann/phpunit/issues/5423 - */ - final protected function returnValueMap(array $valueMap): ReturnValueMapStub - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'returnValueMap() is deprecated and will be removed in PHPUnit 12. Use $double->willReturnMap() instead of $double->will($this->returnValueMap())', - ); - - return new ReturnValueMapStub($valueMap); - } - - /** - * @deprecated Use $double->willReturnArgument() instead of $double->will($this->returnArgument()) - * @see https://github.com/sebastianbergmann/phpunit/issues/5423 - */ - final protected function returnArgument(int $argumentIndex): ReturnArgumentStub - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'returnArgument() is deprecated and will be removed in PHPUnit 12. Use $double->willReturnArgument() instead of $double->will($this->returnArgument())', - ); - - return new ReturnArgumentStub($argumentIndex); - } - - /** - * @deprecated Use $double->willReturnCallback() instead of $double->will($this->returnCallback()) - * @see https://github.com/sebastianbergmann/phpunit/issues/5423 - */ - final protected function returnCallback(callable $callback): ReturnCallbackStub - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'returnCallback() is deprecated and will be removed in PHPUnit 12. Use $double->willReturnCallback() instead of $double->will($this->returnCallback())', - ); - - return new ReturnCallbackStub($callback); - } - - /** - * @deprecated Use $double->willReturnSelf() instead of $double->will($this->returnSelf()) - * @see https://github.com/sebastianbergmann/phpunit/issues/5423 - */ - final protected function returnSelf(): ReturnSelfStub - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'returnSelf() is deprecated and will be removed in PHPUnit 12. Use $double->willReturnSelf() instead of $double->will($this->returnSelf())', - ); - - return new ReturnSelfStub; - } - final protected function throwException(Throwable $exception): ExceptionStub { return new ExceptionStub($exception); } - /** - * @deprecated Use $double->willReturn() instead of $double->will($this->onConsecutiveCalls()) - * @see https://github.com/sebastianbergmann/phpunit/issues/5423 - * @see https://github.com/sebastianbergmann/phpunit/issues/5425 - */ - final protected function onConsecutiveCalls(mixed ...$arguments): ConsecutiveCallsStub - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'onConsecutiveCalls() is deprecated and will be removed in PHPUnit 12. Use $double->willReturn() instead of $double->will($this->onConsecutiveCalls())', - ); - - return new ConsecutiveCallsStub($arguments); - } - final protected function getActualOutputForAssertion(): string { $this->outputRetrievedForAssertion = true; @@ -1051,8 +1036,13 @@ final protected function expectOutputString(string $expectedString): void $this->outputExpectedString = $expectedString; } + final protected function expectErrorLog(): void + { + $this->expectErrorLog = true; + } + /** - * @psalm-param class-string $exception + * @param class-string $exception */ final protected function expectException(string $exception): void { @@ -1091,14 +1081,30 @@ final protected function expectNotToPerformAssertions(): void $this->doesNotPerformAssertions = true; } + /** + * @param non-empty-string $expectedUserDeprecationMessage + */ + final protected function expectUserDeprecationMessage(string $expectedUserDeprecationMessage): void + { + $this->expectedUserDeprecationMessage[] = $expectedUserDeprecationMessage; + } + + /** + * @param non-empty-string $expectedUserDeprecationMessageRegularExpression + */ + final protected function expectUserDeprecationMessageMatches(string $expectedUserDeprecationMessageRegularExpression): void + { + $this->expectedUserDeprecationMessageRegularExpression[] = $expectedUserDeprecationMessageRegularExpression; + } + /** * Returns a builder object to create mock objects using a fluent interface. * - * @psalm-template RealInstanceType of object + * @template RealInstanceType of object * - * @psalm-param class-string $className + * @param class-string $className * - * @psalm-return MockBuilder + * @return MockBuilder */ final protected function getMockBuilder(string $className): MockBuilder { @@ -1115,184 +1121,85 @@ final protected function registerComparator(Comparator $comparator): void } /** - * @psalm-param class-string $classOrInterface + * @param class-string $classOrInterface */ final protected function registerFailureType(string $classOrInterface): void { $this->failureTypes[$classOrInterface] = true; } - /** - * @throws AssertionFailedError - * @throws Exception - * @throws ExpectationFailedException - * @throws Throwable - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - final protected function runTest(): mixed - { - $testArguments = array_merge($this->data, $this->dependencyInput); - - $this->registerMockObjectsFromTestArguments($testArguments); - - try { - $testResult = $this->{$this->name}(...array_values($testArguments)); - } catch (Throwable $exception) { - if (!$this->shouldExceptionExpectationsBeVerified($exception)) { - throw $exception; - } - - $this->verifyExceptionExpectations($exception); - - return null; - } - - $this->expectedExceptionWasNotRaised(); - - return $testResult; - } - - /** - * This method is a wrapper for the ini_set() function that automatically - * resets the modified php.ini setting to its original value after the - * test is run. - * - * @throws Exception - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5214 - */ - final protected function iniSet(string $varName, string $newValue): void - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'iniSet() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $currentValue = ini_set($varName, $newValue); - - if ($currentValue !== false) { - $this->iniSettings[$varName] = $currentValue; - } else { - throw new Exception( - sprintf( - 'INI setting "%s" could not be set to "%s".', - $varName, - $newValue, - ), - ); - } - } - - /** - * This method is a wrapper for the setlocale() function that automatically - * resets the locale to its original value after the test is run. - * - * @throws Exception - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5216 - */ - final protected function setLocale(mixed ...$arguments): void - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'setLocale() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - if (count($arguments) < 2) { - throw new Exception; - } - - [$category, $locale] = $arguments; - - if (!in_array($category, self::LOCALE_CATEGORIES, true)) { - throw new Exception; - } - - if (!is_array($locale) && !is_string($locale)) { - throw new Exception; - } - - $this->locale[$category] = setlocale($category, 0); - - $result = setlocale(...$arguments); - - if ($result === false) { - throw new Exception( - 'The locale functionality is not implemented on your platform, ' . - 'the specified locale does not exist or the category name is ' . - 'invalid.', - ); - } - } - /** * Creates a mock object for the specified interface or class. * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName + * @template RealInstanceType of object * - * @psalm-return MockObject&RealInstanceType + * @param class-string $type * - * @throws \PHPUnit\Framework\MockObject\Exception * @throws InvalidArgumentException + * @throws MockObjectException * @throws NoPreviousThrowableException + * + * @return MockObject&RealInstanceType */ - final protected function createMock(string $originalClassName): MockObject + final protected function createMock(string $type): MockObject { $mock = (new MockGenerator)->testDouble( - $originalClassName, - true, + $type, true, callOriginalConstructor: false, callOriginalClone: false, - cloneArguments: false, - allowMockingUnknownTypes: false, + returnValueGeneration: self::generateReturnValuesForTestDoubles(), ); - assert($mock instanceof $originalClassName); + assert($mock instanceof $type); assert($mock instanceof MockObject); $this->registerMockObject($mock); - Event\Facade::emitter()->testCreatedMockObject($originalClassName); + Event\Facade::emitter()->testCreatedMockObject($type); return $mock; } /** - * @psalm-param list $interfaces + * @param list $interfaces * - * @throws \PHPUnit\Framework\MockObject\Exception + * @throws MockObjectException */ final protected function createMockForIntersectionOfInterfaces(array $interfaces): MockObject { - $mock = (new MockGenerator)->testDoubleForInterfaceIntersection($interfaces, true); - - Event\Facade::emitter()->testCreatedMockObjectForIntersectionOfInterfaces($interfaces); + $mock = (new MockGenerator)->testDoubleForInterfaceIntersection( + $interfaces, + true, + returnValueGeneration: self::generateReturnValuesForTestDoubles(), + ); assert($mock instanceof MockObject); + $this->registerMockObject($mock); + + Event\Facade::emitter()->testCreatedMockObjectForIntersectionOfInterfaces($interfaces); + return $mock; } /** * Creates (and configures) a mock object for the specified interface or class. * - * @psalm-template RealInstanceType of object + * @template RealInstanceType of object * - * @psalm-param class-string $originalClassName + * @param class-string $type + * @param array $configuration * - * @psalm-return MockObject&RealInstanceType - * - * @throws \PHPUnit\Framework\MockObject\Exception * @throws InvalidArgumentException + * @throws MockObjectException * @throws NoPreviousThrowableException + * + * @return MockObject&RealInstanceType */ - final protected function createConfiguredMock(string $originalClassName, array $configuration): MockObject + final protected function createConfiguredMock(string $type, array $configuration): MockObject { - $o = $this->createMock($originalClassName); + $o = $this->createMock($type); foreach ($configuration as $method => $return) { $o->method($method)->willReturn($return); @@ -1304,246 +1211,149 @@ final protected function createConfiguredMock(string $originalClassName, array $ /** * Creates a partial mock object for the specified interface or class. * - * @psalm-param list $methods + * @param class-string $type + * @param list $methods * - * @psalm-template RealInstanceType of object + * @template RealInstanceType of object * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - * - * @throws \PHPUnit\Framework\MockObject\Exception * @throws InvalidArgumentException + * @throws MockObjectException + * + * @return MockObject&RealInstanceType */ - final protected function createPartialMock(string $originalClassName, array $methods): MockObject + final protected function createPartialMock(string $type, array $methods): MockObject { - $partialMock = $this->getMockBuilder($originalClassName) + $mockBuilder = $this->getMockBuilder($type) ->disableOriginalConstructor() ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() - ->onlyMethods($methods) - ->getMock(); + ->onlyMethods($methods); + + if (!self::generateReturnValuesForTestDoubles()) { + $mockBuilder->disableAutoReturnValueGeneration(); + } + + $partialMock = $mockBuilder->getMock(); Event\Facade::emitter()->testCreatedPartialMockObject( - $originalClassName, + $type, ...$methods, ); return $partialMock; } + protected function transformException(Throwable $t): Throwable + { + return $t; + } + /** - * Creates a test proxy for the specified class. + * This method is called when a test method did not execute successfully. * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - * - * @throws \PHPUnit\Framework\MockObject\Exception - * @throws InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5240 + * @throws Throwable */ - final protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject + protected function onNotSuccessfulTest(Throwable $t): never { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'createTestProxy() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $testProxy = $this->getMockBuilder($originalClassName) - ->setConstructorArgs($constructorArguments) - ->enableProxyingToOriginalMethods() - ->getMock(); - - Event\Facade::emitter()->testCreatedTestProxy( - $originalClassName, - $constructorArguments, - ); - - return $testProxy; + throw $t; } /** - * Creates a mock object for the specified abstract class with all abstract - * methods of the class mocked. Concrete methods are not mocked by default. - * To mock concrete methods, use the 7th parameter ($mockedMethods). - * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName - * - * @psalm-return MockObject&RealInstanceType - * - * @throws \PHPUnit\Framework\MockObject\Exception - * @throws InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5241 + * @throws AssertionFailedError + * @throws Exception + * @throws ExpectationFailedException + * @throws Throwable */ - final protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject + private function runTest(): mixed { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'getMockForAbstractClass() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $mockObject = (new MockGenerator)->mockObjectForAbstractClass( - $originalClassName, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $mockedMethods, - $cloneArguments, - ); + $testArguments = array_merge($this->data, array_values($this->dependencyInput)); - $this->registerMockObject($mockObject); + $capture = tmpfile(); + $errorLogPrevious = ini_set('error_log', stream_get_meta_data($capture)['uri']); - Event\Facade::emitter()->testCreatedMockObjectForAbstractClass($originalClassName); + try { + /** @phpstan-ignore method.dynamicName */ + $testResult = $this->{$this->methodName}(...$testArguments); - assert($mockObject instanceof $originalClassName); - assert($mockObject instanceof MockObject); + $errorLogOutput = stream_get_contents($capture); - return $mockObject; - } - - /** - * Creates a mock object based on the given WSDL file. - * - * @throws \PHPUnit\Framework\MockObject\Exception - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5242 - */ - final protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = true, array $options = []): MockObject - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'getMockFromWsdl() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); + if ($this->expectErrorLog) { + $this->assertNotEmpty($errorLogOutput, 'Test did not call error_log().'); + } else { + if ($errorLogOutput !== false) { + // strip date from logged error, see https://github.com/php/php-src/blob/c696087e323263e941774ebbf902ac249774ec9f/main/main.c#L905 + print preg_replace('/\[.+\] /', '', $errorLogOutput); + } + } + } catch (Throwable $exception) { + if (!$this->expectErrorLog) { + $errorLogOutput = stream_get_contents($capture); - if ($originalClassName === '') { - $fileName = pathinfo(basename(parse_url(http://github.com/$wsdlFile,%20PHP_URL_PATH)), PATHINFO_FILENAME); - $originalClassName = preg_replace('/\W/', '', $fileName); - } + if ($errorLogOutput !== false) { + // strip date from logged error, see https://github.com/php/php-src/blob/c696087e323263e941774ebbf902ac249774ec9f/main/main.c#L905 + print preg_replace('/\[.+\] /', '', $errorLogOutput); + } + } - if (!class_exists($originalClassName)) { - eval( - (new MockGenerator)->generateClassFromWsdl( - $wsdlFile, - $originalClassName, - $methods, - $options, - ) - ); - } + if (!$this->shouldExceptionExpectationsBeVerified($exception)) { + throw $exception; + } - $mockObject = (new MockGenerator)->testDouble( - $originalClassName, - true, - true, - $methods, - ['', $options], - $mockClassName, - $callOriginalConstructor, - false, - false, - ); + $this->verifyExceptionExpectations($exception); - Event\Facade::emitter()->testCreatedMockObjectFromWsdl( - $wsdlFile, - $originalClassName, - $mockClassName, - $methods, - $callOriginalConstructor, - $options, - ); + return null; + } finally { + if ($capture !== false) { + fclose($capture); + } - assert($mockObject instanceof MockObject); + ini_set('error_log', $errorLogPrevious); + } - $this->registerMockObject($mockObject); + $this->expectedExceptionWasNotRaised(); - return $mockObject; + return $testResult; } /** - * Creates a mock object for the specified trait with all abstract methods - * of the trait mocked. Concrete methods to mock can be specified with the - * `$mockedMethods` parameter. - * - * @psalm-param trait-string $traitName - * - * @throws \PHPUnit\Framework\MockObject\Exception - * @throws InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5243 + * @throws ExpectationFailedException */ - final protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject + private function verifyDeprecationExpectations(): void { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'getMockForTrait() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); - - $mockObject = (new MockGenerator)->mockObjectForTrait( - $traitName, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $mockedMethods, - $cloneArguments, - ); - - $this->registerMockObject($mockObject); + foreach ($this->expectedUserDeprecationMessage as $deprecationExpectation) { + $this->numberOfAssertionsPerformed++; - Event\Facade::emitter()->testCreatedMockObjectForTrait($traitName); + if (!in_array($deprecationExpectation, DeprecationCollector::deprecations(), true)) { + throw new ExpectationFailedException( + sprintf( + 'Expected deprecation with message "%s" was not triggered', + $deprecationExpectation, + ), + ); + } + } - return $mockObject; - } + foreach ($this->expectedUserDeprecationMessageRegularExpression as $deprecationExpectation) { + $this->numberOfAssertionsPerformed++; - /** - * Creates an object that uses the specified trait. - * - * @psalm-param trait-string $traitName - * - * @throws \PHPUnit\Framework\MockObject\Exception - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5244 - */ - final protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true): object - { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - $this->valueObjectForEvents(), - 'getObjectForTrait() is deprecated and will be removed in PHPUnit 12. No replacement is/will be provided.', - ); + $expectedDeprecationTriggered = false; - return (new MockGenerator)->objectForTrait( - $traitName, - $traitClassName, - $callAutoload, - $callOriginalConstructor, - $arguments, - ); - } + foreach (DeprecationCollector::deprecations() as $deprecation) { + if (@preg_match($deprecationExpectation, $deprecation) > 0) { + $expectedDeprecationTriggered = true; - protected function transformException(Throwable $t): Throwable - { - return $t; - } + break; + } + } - /** - * This method is called when a test method did not execute successfully. - * - * @throws Throwable - */ - protected function onNotSuccessfulTest(Throwable $t): never - { - throw $t; + if (!$expectedDeprecationTriggered) { + throw new ExpectationFailedException( + sprintf( + 'Expected deprecation with message matching regular expression "%s" was not triggered', + $deprecationExpectation, + ), + ); + } + } } /** @@ -1567,16 +1377,16 @@ private function verifyMockObjects(): void */ private function checkRequirements(): void { - if (!$this->name || !method_exists($this, $this->name)) { + if ($this->methodName === '' || !method_exists($this, $this->methodName)) { return; } $missingRequirements = (new Requirements)->requirementsNotSatisfiedFor( static::class, - $this->name, + $this->methodName, ); - if (!empty($missingRequirements)) { + if ($missingRequirements !== []) { $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); } } @@ -1632,7 +1442,7 @@ private function handleDependencies(): bool 'This test depends on a test that is larger than itself', ); - return false; + return true; } $returnValue = $passedTests->returnValue($dependencyTarget); @@ -1722,8 +1532,6 @@ private function stopOutputBuffering(): bool $message, ); - $this->status = TestStatus::risky($message); - return false; } @@ -1735,6 +1543,147 @@ private function stopOutputBuffering(): bool return true; } + private function snapshotGlobalErrorExceptionHandlers(): void + { + $this->backupGlobalErrorHandlers = $this->activeErrorHandlers(); + $this->backupGlobalExceptionHandlers = $this->activeExceptionHandlers(); + } + + private function restoreGlobalErrorExceptionHandlers(): void + { + $activeErrorHandlers = $this->activeErrorHandlers(); + $activeExceptionHandlers = $this->activeExceptionHandlers(); + + $message = null; + + if ($activeErrorHandlers !== $this->backupGlobalErrorHandlers) { + if (count($activeErrorHandlers) > count($this->backupGlobalErrorHandlers)) { + if (!$this->inIsolation) { + $message = 'Test code or tested code did not remove its own error handlers'; + } + } else { + $message = 'Test code or tested code removed error handlers other than its own'; + } + + foreach ($activeErrorHandlers as $handler) { + restore_error_handler(); + } + + foreach ($this->backupGlobalErrorHandlers as $handler) { + set_error_handler($handler); + } + } + + if ($message !== null) { + Event\Facade::emitter()->testConsideredRisky( + $this->valueObjectForEvents(), + $message, + ); + } + + $message = null; + + if ($activeExceptionHandlers !== $this->backupGlobalExceptionHandlers) { + if (count($activeExceptionHandlers) > count($this->backupGlobalExceptionHandlers)) { + if (!$this->inIsolation) { + $message = 'Test code or tested code did not remove its own exception handlers'; + } + } else { + $message = 'Test code or tested code removed exception handlers other than its own'; + } + + foreach ($activeExceptionHandlers as $handler) { + restore_exception_handler(); + } + + foreach ($this->backupGlobalExceptionHandlers as $handler) { + set_exception_handler($handler); + } + } + + $this->backupGlobalErrorHandlers = null; + $this->backupGlobalExceptionHandlers = null; + + if ($message !== null) { + Event\Facade::emitter()->testConsideredRisky( + $this->valueObjectForEvents(), + $message, + ); + } + } + + /** + * @return list + */ + private function activeErrorHandlers(): array + { + $activeErrorHandlers = []; + + while (true) { + $previousHandler = set_error_handler(static fn () => false); + + restore_error_handler(); + + if ($previousHandler === null) { + break; + } + + $activeErrorHandlers[] = $previousHandler; + + restore_error_handler(); + } + + $activeErrorHandlers = array_reverse($activeErrorHandlers); + $invalidErrorHandlerStack = false; + + foreach ($activeErrorHandlers as $handler) { + if (!is_callable($handler)) { + $invalidErrorHandlerStack = true; + + continue; + } + + set_error_handler($handler); + } + + if ($invalidErrorHandlerStack) { + $message = 'At least one error handler is not callable outside the scope it was registered in'; + + Event\Facade::emitter()->testConsideredRisky( + $this->valueObjectForEvents(), + $message, + ); + } + + return $activeErrorHandlers; + } + + /** + * @return list + */ + private function activeExceptionHandlers(): array + { + $res = []; + + while (true) { + $previousHandler = set_exception_handler(static fn () => null); + restore_exception_handler(); + + if ($previousHandler === null) { + break; + } + $res[] = $previousHandler; + restore_exception_handler(); + } + $res = array_reverse($res); + + foreach ($res as $handler) { + set_exception_handler($handler); + } + + return $res; + } + private function snapshotGlobalState(): void { if ($this->runTestInSeparateProcess || $this->inIsolation || @@ -1838,25 +1787,62 @@ private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): } } + /** + * @param array $before + * @param array $after + */ private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void { - if ($before != $after) { - $differ = new Differ(new UnifiedDiffOutputBuilder($header)); - $exporter = new Exporter; + if ($before !== $after) { + $differ = new Differ(new UnifiedDiffOutputBuilder($header)); Event\Facade::emitter()->testConsideredRisky( $this->valueObjectForEvents(), 'This test modified global state but was not expected to do so' . PHP_EOL . trim( $differ->diff( - $exporter->export($before), - $exporter->export($after), + Exporter::export($before), + Exporter::export($after), ), ), ); } } + private function handleEnvironmentVariables(): void + { + $withEnvironmentVariables = MetadataRegistry::parser()->forClassAndMethod(static::class, $this->methodName)->isWithEnvironmentVariable(); + + $environmentVariables = []; + + foreach ($withEnvironmentVariables as $metadata) { + assert($metadata instanceof WithEnvironmentVariable); + + $environmentVariables[$metadata->environmentVariableName()] = $metadata->value(); + } + + foreach ($environmentVariables as $environmentVariableName => $environmentVariableValue) { + $this->backupEnvironmentVariables = [...$this->backupEnvironmentVariables, ...BackedUpEnvironmentVariable::create($environmentVariableName)]; + + if ($environmentVariableValue === null) { + unset($_ENV[$environmentVariableName]); + putenv($environmentVariableName); + } else { + $_ENV[$environmentVariableName] = $environmentVariableValue; + putenv("{$environmentVariableName}={$environmentVariableValue}"); + } + } + } + + private function restoreEnvironmentVariables(): void + { + foreach ($this->backupEnvironmentVariables as $backupEnvironmentVariable) { + $backupEnvironmentVariable->restore(); + } + + $this->backupEnvironmentVariables = []; + } + private function shouldInvocationMockerBeReset(MockObject $mock): bool { $enumerator = new Enumerator; @@ -1872,32 +1858,6 @@ private function shouldInvocationMockerBeReset(MockObject $mock): bool return !in_array($mock, $enumerator->enumerate($this->testResult), true); } - private function registerMockObjectsFromTestArguments(array $testArguments, Context $context = new Context): void - { - if ($this->registerMockObjectsFromTestArgumentsRecursively) { - foreach ((new Enumerator)->enumerate($testArguments) as $object) { - if ($object instanceof MockObject) { - $this->registerMockObject($object); - } - } - } else { - foreach ($testArguments as $testArgument) { - if ($testArgument instanceof MockObject) { - $testArgument = Cloner::clone($testArgument); - - $this->registerMockObject($testArgument); - } elseif (is_array($testArgument) && !$context->contains($testArgument)) { - $context->add($testArgument); - - $this->registerMockObjectsFromTestArguments( - $testArgument, - $context, - ); - } - } - } - } - private function unregisterCustomComparators(): void { $factory = ComparatorFactory::getInstance(); @@ -1909,24 +1869,6 @@ private function unregisterCustomComparators(): void $this->customComparators = []; } - private function cleanupIniSettings(): void - { - foreach ($this->iniSettings as $varName => $oldValue) { - ini_set($varName, $oldValue); - } - - $this->iniSettings = []; - } - - private function cleanupLocaleSettings(): void - { - foreach ($this->locale as $category => $locale) { - setlocale($category, $locale); - } - - $this->locale = []; - } - /** * @throws Exception */ @@ -2032,19 +1974,28 @@ private function performAssertionsOnOutput(): void } /** + * @param array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} $hookMethods + * * @throws Throwable + * + * @codeCoverageIgnore */ private function invokeBeforeClassHookMethods(array $hookMethods, Event\Emitter $emitter): void { $this->invokeHookMethods( $hookMethods['beforeClass'], $emitter, - 'testBeforeFirstTestMethodCalled', - 'testBeforeFirstTestMethodFinished', + 'beforeFirstTestMethodCalled', + 'beforeFirstTestMethodErrored', + 'beforeFirstTestMethodFailed', + 'beforeFirstTestMethodFinished', + false, ); } /** + * @param array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} $hookMethods + * * @throws Throwable */ private function invokeBeforeTestHookMethods(array $hookMethods, Event\Emitter $emitter): void @@ -2052,12 +2003,16 @@ private function invokeBeforeTestHookMethods(array $hookMethods, Event\Emitter $ $this->invokeHookMethods( $hookMethods['before'], $emitter, - 'testBeforeTestMethodCalled', - 'testBeforeTestMethodFinished', + 'beforeTestMethodCalled', + 'beforeTestMethodErrored', + 'beforeTestMethodFailed', + 'beforeTestMethodFinished', ); } /** + * @param array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} $hookMethods + * * @throws Throwable */ private function invokePreConditionHookMethods(array $hookMethods, Event\Emitter $emitter): void @@ -2065,12 +2020,16 @@ private function invokePreConditionHookMethods(array $hookMethods, Event\Emitter $this->invokeHookMethods( $hookMethods['preCondition'], $emitter, - 'testPreConditionCalled', - 'testPreConditionFinished', + 'preConditionCalled', + 'preConditionErrored', + 'preConditionFailed', + 'preConditionFinished', ); } /** + * @param array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} $hookMethods + * * @throws Throwable */ private function invokePostConditionHookMethods(array $hookMethods, Event\Emitter $emitter): void @@ -2078,12 +2037,16 @@ private function invokePostConditionHookMethods(array $hookMethods, Event\Emitte $this->invokeHookMethods( $hookMethods['postCondition'], $emitter, - 'testPostConditionCalled', - 'testPostConditionFinished', + 'postConditionCalled', + 'postConditionErrored', + 'postConditionFailed', + 'postConditionFinished', ); } /** + * @param array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} $hookMethods + * * @throws Throwable */ private function invokeAfterTestHookMethods(array $hookMethods, Event\Emitter $emitter): void @@ -2091,65 +2054,97 @@ private function invokeAfterTestHookMethods(array $hookMethods, Event\Emitter $e $this->invokeHookMethods( $hookMethods['after'], $emitter, - 'testAfterTestMethodCalled', - 'testAfterTestMethodFinished', + 'afterTestMethodCalled', + 'afterTestMethodErrored', + 'afterTestMethodFailed', + 'afterTestMethodFinished', ); } /** + * @param array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} $hookMethods + * * @throws Throwable + * + * @codeCoverageIgnore */ private function invokeAfterClassHookMethods(array $hookMethods, Event\Emitter $emitter): void { $this->invokeHookMethods( $hookMethods['afterClass'], $emitter, - 'testAfterLastTestMethodCalled', - 'testAfterLastTestMethodFinished', + 'afterLastTestMethodCalled', + 'afterLastTestMethodErrored', + 'afterLastTestMethodFailed', + 'afterLastTestMethodFinished', + false, ); } /** - * @psalm-param list $hookMethods - * @psalm-param 'testBeforeFirstTestMethodCalled'|'testBeforeTestMethodCalled'|'testPreConditionCalled'|'testPostConditionCalled'|'testAfterTestMethodCalled'|'testAfterLastTestMethodCalled' $calledMethod - * @psalm-param 'testBeforeFirstTestMethodFinished'|'testBeforeTestMethodFinished'|'testPreConditionFinished'|'testPostConditionFinished'|'testAfterTestMethodFinished'|'testAfterLastTestMethodFinished' $finishedMethod + * @param 'afterLastTestMethodCalled'|'afterTestMethodCalled'|'beforeFirstTestMethodCalled'|'beforeTestMethodCalled'|'postConditionCalled'|'preConditionCalled' $calledMethod + * @param 'afterLastTestMethodErrored'|'afterTestMethodErrored'|'beforeFirstTestMethodErrored'|'beforeTestMethodErrored'|'postConditionErrored'|'preConditionErrored' $erroredMethod + * @param 'afterLastTestMethodFailed'|'afterTestMethodFailed'|'beforeFirstTestMethodFailed'|'beforeTestMethodFailed'|'postConditionFailed'|'preConditionFailed' $failedMethod + * @param 'afterLastTestMethodFinished'|'afterTestMethodFinished'|'beforeFirstTestMethodFinished'|'beforeTestMethodFinished'|'postConditionFinished'|'preConditionFinished' $finishedMethod * * @throws Throwable */ - private function invokeHookMethods(array $hookMethods, Event\Emitter $emitter, string $calledMethod, string $finishedMethod): void + private function invokeHookMethods(HookMethodCollection $hookMethods, Event\Emitter $emitter, string $calledMethod, string $erroredMethod, string $failedMethod, string $finishedMethod, bool $forTestCase = true): void { + if ($forTestCase) { + $test = $this->valueObjectForEvents(); + } else { + $test = static::class; + } + $methodsInvoked = []; - foreach ($hookMethods as $methodName) { + foreach ($hookMethods->methodNamesSortedByPriority() as $methodName) { if ($this->methodDoesNotExistOrIsDeclaredInTestCase($methodName)) { continue; } - try { - $this->{$methodName}(); - } catch (Throwable $t) { - } - $methodInvoked = new Event\Code\ClassMethod( static::class, $methodName, ); + try { + /** @phpstan-ignore method.dynamicName */ + $this->{$methodName}(); + } catch (Throwable $t) { + } + + /** @phpstan-ignore method.dynamicName */ $emitter->{$calledMethod}( - static::class, + $test, $methodInvoked ); $methodsInvoked[] = $methodInvoked; - if (isset($t)) { + if (isset($t) && !$t instanceof SkippedTest) { + if ($t instanceof AssertionFailedError) { + $method = $failedMethod; + } else { + $method = $erroredMethod; + } + + /** @phpstan-ignore method.dynamicName */ + $emitter->{$method}( + $test, + $methodInvoked, + Event\Code\ThrowableBuilder::from($t), + ); + break; } } - if (!empty($methodsInvoked)) { + if ($methodsInvoked !== []) { + /** @phpstan-ignore method.dynamicName */ $emitter->{$finishedMethod}( - static::class, + $test, ...$methodsInvoked ); } @@ -2159,6 +2154,9 @@ private function invokeHookMethods(array $hookMethods, Event\Emitter $emitter, s } } + /** + * @param non-empty-string $methodName + */ private function methodDoesNotExistOrIsDeclaredInTestCase(string $methodName): bool { $reflector = new ReflectionObject($this); @@ -2268,47 +2266,70 @@ private function hasExpectationOnOutput(): bool return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex); } + private function requirementsNotSatisfied(): bool + { + return (new Requirements)->requirementsNotSatisfiedFor(static::class, $this->methodName) !== []; + } + + /** + * @see https://github.com/sebastianbergmann/phpunit/issues/6095 + */ + private function handleExceptionFromInvokedCountMockObjectRule(Throwable $t): void + { + if (!$t instanceof ExpectationFailedException) { + return; + } + + $trace = $t->getTrace(); + + if (isset($trace[0]['class']) && $trace[0]['class'] === InvokedCount::class) { + $this->numberOfAssertionsPerformed++; + } + } + /** * Creates a test stub for the specified interface or class. * - * @psalm-template RealInstanceType of object + * @template RealInstanceType of object * - * @psalm-param class-string $originalClassName + * @param class-string $type * - * @psalm-return Stub&RealInstanceType - * - * @throws \PHPUnit\Framework\MockObject\Exception * @throws InvalidArgumentException + * @throws MockObjectException * @throws NoPreviousThrowableException + * + * @return RealInstanceType&Stub */ - final protected static function createStub(string $originalClassName): Stub + final protected static function createStub(string $type): Stub { $stub = (new MockGenerator)->testDouble( - $originalClassName, - true, + $type, false, callOriginalConstructor: false, callOriginalClone: false, - cloneArguments: false, - allowMockingUnknownTypes: false, + returnValueGeneration: self::generateReturnValuesForTestDoubles(), ); - Event\Facade::emitter()->testCreatedStub($originalClassName); + Event\Facade::emitter()->testCreatedStub($type); - assert($stub instanceof $originalClassName); + assert($stub instanceof $type); assert($stub instanceof Stub); return $stub; } /** - * @psalm-param list $interfaces + * @param list $interfaces * - * @throws \PHPUnit\Framework\MockObject\Exception + * @throws MockObjectException */ final protected static function createStubForIntersectionOfInterfaces(array $interfaces): Stub { - $stub = (new MockGenerator)->testDoubleForInterfaceIntersection($interfaces, false); + $stub = (new MockGenerator)->testDoubleForInterfaceIntersection( + $interfaces, + false, + returnValueGeneration: self::generateReturnValuesForTestDoubles(), + ); Event\Facade::emitter()->testCreatedStubForIntersectionOfInterfaces($interfaces); @@ -2318,19 +2339,20 @@ final protected static function createStubForIntersectionOfInterfaces(array $int /** * Creates (and configures) a test stub for the specified interface or class. * - * @psalm-template RealInstanceType of object - * - * @psalm-param class-string $originalClassName + * @template RealInstanceType of object * - * @psalm-return Stub&RealInstanceType + * @param class-string $type + * @param array $configuration * - * @throws \PHPUnit\Framework\MockObject\Exception * @throws InvalidArgumentException + * @throws MockObjectException * @throws NoPreviousThrowableException + * + * @return RealInstanceType&Stub */ - final protected static function createConfiguredStub(string $originalClassName, array $configuration): Stub + final protected static function createConfiguredStub(string $type, array $configuration): Stub { - $o = self::createStub($originalClassName); + $o = self::createStub($type); foreach ($configuration as $method => $return) { $o->method($method)->willReturn($return); @@ -2338,4 +2360,9 @@ final protected static function createConfiguredStub(string $originalClassName, return $o; } + + private static function generateReturnValuesForTestDoubles(): bool + { + return MetadataRegistry::parser()->forClass(static::class)->isDisableReturnValueGenerationForTestDoubles()->isEmpty(); + } } diff --git a/src/Framework/TestRunner.php b/src/Framework/TestRunner.php deleted file mode 100644 index 97ad391828d..00000000000 --- a/src/Framework/TestRunner.php +++ /dev/null @@ -1,469 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use const PHP_EOL; -use function assert; -use function class_exists; -use function defined; -use function extension_loaded; -use function get_include_path; -use function hrtime; -use function serialize; -use function sprintf; -use function sys_get_temp_dir; -use function tempnam; -use function unlink; -use function var_export; -use AssertionError; -use PHPUnit\Event; -use PHPUnit\Event\NoPreviousThrowableException; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; -use PHPUnit\Metadata\Api\CodeCoverage as CodeCoverageMetadataApi; -use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; -use PHPUnit\Runner\CodeCoverage; -use PHPUnit\Runner\ErrorHandler; -use PHPUnit\TextUI\Configuration\Configuration; -use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; -use PHPUnit\Util\GlobalState; -use PHPUnit\Util\PHP\AbstractPhpProcess; -use ReflectionClass; -use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; -use SebastianBergmann\CodeCoverage\StaticAnalysisCacheNotConfiguredException; -use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; -use SebastianBergmann\Invoker\Invoker; -use SebastianBergmann\Invoker\TimeoutException; -use SebastianBergmann\Template\Template; -use Throwable; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestRunner -{ - private ?bool $timeLimitCanBeEnforced = null; - private readonly Configuration $configuration; - - public function __construct() - { - $this->configuration = ConfigurationRegistry::get(); - } - - /** - * @throws \PHPUnit\Runner\Exception - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws CodeCoverageException - * @throws NoDataSetFromDataProviderException - * @throws UnintentionallyCoveredCodeException - */ - public function run(TestCase $test): void - { - Assert::resetCount(); - - if ($this->configuration->registerMockObjectsFromTestArgumentsRecursively()) { - $test->registerMockObjectsFromTestArgumentsRecursively(); - } - - $shouldCodeCoverageBeCollected = (new CodeCoverageMetadataApi)->shouldCodeCoverageBeCollectedFor( - $test::class, - $test->name(), - ); - - $error = false; - $failure = false; - $incomplete = false; - $risky = false; - $skipped = false; - - if ($this->shouldErrorHandlerBeUsed($test)) { - ErrorHandler::instance()->enable(); - } - - $collectCodeCoverage = CodeCoverage::instance()->isActive() && - $shouldCodeCoverageBeCollected; - - if ($collectCodeCoverage) { - CodeCoverage::instance()->start($test); - } - - try { - if ($this->canTimeLimitBeEnforced() && - $this->shouldTimeLimitBeEnforced($test)) { - $risky = $this->runTestWithTimeout($test); - } else { - $test->runBare(); - } - } catch (AssertionFailedError $e) { - $failure = true; - - if ($e instanceof IncompleteTestError) { - $incomplete = true; - } elseif ($e instanceof SkippedTest) { - $skipped = true; - } - } catch (AssertionError $e) { - $test->addToAssertionCount(1); - - $failure = true; - $frame = $e->getTrace()[0]; - - assert(isset($frame['file'])); - assert(isset($frame['line'])); - - $e = new AssertionFailedError( - sprintf( - '%s in %s:%s', - $e->getMessage(), - $frame['file'], - $frame['line'], - ), - ); - } catch (Throwable $e) { - $error = true; - } - - $test->addToAssertionCount(Assert::getCount()); - - if ($this->configuration->reportUselessTests() && - !$test->doesNotPerformAssertions() && - $test->numberOfAssertionsPerformed() === 0) { - $risky = true; - } - - if (!$error && !$failure && !$incomplete && !$skipped && !$risky && - $this->configuration->requireCoverageMetadata() && - !$this->hasCoverageMetadata($test::class, $test->name())) { - Event\Facade::emitter()->testConsideredRisky( - $test->valueObjectForEvents(), - 'This test does not define a code coverage target but is expected to do so', - ); - - $risky = true; - } - - if ($collectCodeCoverage) { - $append = !$risky && !$incomplete && !$skipped; - $linesToBeCovered = []; - $linesToBeUsed = []; - - if ($append) { - try { - $linesToBeCovered = (new CodeCoverageMetadataApi)->linesToBeCovered( - $test::class, - $test->name(), - ); - - $linesToBeUsed = (new CodeCoverageMetadataApi)->linesToBeUsed( - $test::class, - $test->name(), - ); - } catch (InvalidCoversTargetException $cce) { - Event\Facade::emitter()->testTriggeredPhpunitWarning( - $test->valueObjectForEvents(), - $cce->getMessage(), - ); - } - } - - try { - CodeCoverage::instance()->stop( - $append, - $linesToBeCovered, - $linesToBeUsed, - ); - } catch (UnintentionallyCoveredCodeException $cce) { - Event\Facade::emitter()->testConsideredRisky( - $test->valueObjectForEvents(), - 'This test executed code that is not listed as code to be covered or used:' . - PHP_EOL . - $cce->getMessage(), - ); - } catch (OriginalCodeCoverageException $cce) { - $error = true; - - $e = $e ?? $cce; - } - } - - ErrorHandler::instance()->disable(); - - if (!$error && - !$incomplete && - !$skipped && - $this->configuration->reportUselessTests() && - !$test->doesNotPerformAssertions() && - $test->numberOfAssertionsPerformed() === 0) { - Event\Facade::emitter()->testConsideredRisky( - $test->valueObjectForEvents(), - 'This test did not perform any assertions', - ); - } - - if ($test->doesNotPerformAssertions() && - $test->numberOfAssertionsPerformed() > 0) { - Event\Facade::emitter()->testConsideredRisky( - $test->valueObjectForEvents(), - sprintf( - 'This test is not expected to perform assertions but performed %d assertion%s', - $test->numberOfAssertionsPerformed(), - $test->numberOfAssertionsPerformed() > 1 ? 's' : '', - ), - ); - } - - if ($test->hasUnexpectedOutput()) { - Event\Facade::emitter()->testPrintedUnexpectedOutput($test->output()); - } - - if ($this->configuration->disallowTestOutput() && $test->hasUnexpectedOutput()) { - Event\Facade::emitter()->testConsideredRisky( - $test->valueObjectForEvents(), - sprintf( - 'This test printed output: %s', - $test->output(), - ), - ); - } - - if ($test->wasPrepared()) { - Event\Facade::emitter()->testFinished( - $test->valueObjectForEvents(), - $test->numberOfAssertionsPerformed(), - ); - } - } - - /** - * @throws \PHPUnit\Runner\Exception - * @throws \PHPUnit\Util\Exception - * @throws \SebastianBergmann\Template\InvalidArgumentException - * @throws Exception - * @throws NoPreviousThrowableException - * @throws ProcessIsolationException - * @throws StaticAnalysisCacheNotConfiguredException - */ - public function runInSeparateProcess(TestCase $test, bool $runEntireClass, bool $preserveGlobalState): void - { - $class = new ReflectionClass($test); - - if ($runEntireClass) { - $template = new Template( - __DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl', - ); - } else { - $template = new Template( - __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl', - ); - } - - $bootstrap = ''; - $constants = ''; - $globals = ''; - $includedFiles = ''; - $iniSettings = ''; - - if (ConfigurationRegistry::get()->hasBootstrap()) { - $bootstrap = ConfigurationRegistry::get()->bootstrap(); - } - - if ($preserveGlobalState) { - $constants = GlobalState::getConstantsAsString(); - $globals = GlobalState::getGlobalsAsString(); - $includedFiles = GlobalState::getIncludedFilesAsString(); - $iniSettings = GlobalState::getIniSettingsAsString(); - } - - $exportObjects = Event\Facade::emitter()->exportsObjects() ? 'true' : 'false'; - $coverage = CodeCoverage::instance()->isActive() ? 'true' : 'false'; - $linesToBeIgnored = var_export(CodeCoverage::instance()->linesToBeIgnored(), true); - - if (defined('PHPUNIT_COMPOSER_INSTALL')) { - $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true); - } else { - $composerAutoload = '\'\''; - } - - if (defined('__PHPUNIT_PHAR__')) { - $phar = var_export(__PHPUNIT_PHAR__, true); - } else { - $phar = '\'\''; - } - - $data = var_export(serialize($test->providedData()), true); - $dataName = var_export($test->dataName(), true); - $dependencyInput = var_export(serialize($test->dependencyInput()), true); - $includePath = var_export(get_include_path(), true); - // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC - // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences - $data = "'." . $data . ".'"; - $dataName = "'.(" . $dataName . ").'"; - $dependencyInput = "'." . $dependencyInput . ".'"; - $includePath = "'." . $includePath . ".'"; - $offset = hrtime(); - $serializedConfiguration = $this->saveConfigurationForChildProcess(); - $processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_'); - - $var = [ - 'bootstrap' => $bootstrap, - 'composerAutoload' => $composerAutoload, - 'phar' => $phar, - 'filename' => $class->getFileName(), - 'className' => $class->getName(), - 'collectCodeCoverageInformation' => $coverage, - 'linesToBeIgnored' => $linesToBeIgnored, - 'data' => $data, - 'dataName' => $dataName, - 'dependencyInput' => $dependencyInput, - 'constants' => $constants, - 'globals' => $globals, - 'include_path' => $includePath, - 'included_files' => $includedFiles, - 'iniSettings' => $iniSettings, - 'name' => $test->name(), - 'offsetSeconds' => $offset[0], - 'offsetNanoseconds' => $offset[1], - 'serializedConfiguration' => $serializedConfiguration, - 'processResultFile' => $processResultFile, - 'exportObjects' => $exportObjects, - ]; - - if (!$runEntireClass) { - $var['methodName'] = $test->name(); - } - - $template->setVar($var); - - $php = AbstractPhpProcess::factory(); - $php->runTestJob($template->render(), $test, $processResultFile); - - @unlink($serializedConfiguration); - } - - /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - */ - private function hasCoverageMetadata(string $className, string $methodName): bool - { - $metadata = MetadataRegistry::parser()->forClassAndMethod($className, $methodName); - - if ($metadata->isCovers()->isNotEmpty()) { - return true; - } - - if ($metadata->isCoversClass()->isNotEmpty()) { - return true; - } - - if ($metadata->isCoversFunction()->isNotEmpty()) { - return true; - } - - if ($metadata->isCoversNothing()->isNotEmpty()) { - return true; - } - - return false; - } - - private function canTimeLimitBeEnforced(): bool - { - if ($this->timeLimitCanBeEnforced !== null) { - return $this->timeLimitCanBeEnforced; - } - - if (!class_exists(Invoker::class)) { - $this->timeLimitCanBeEnforced = false; - - return $this->timeLimitCanBeEnforced; - } - - $this->timeLimitCanBeEnforced = (new Invoker)->canInvokeWithTimeout(); - - return $this->timeLimitCanBeEnforced; - } - - private function shouldTimeLimitBeEnforced(TestCase $test): bool - { - if (!$this->configuration->enforceTimeLimit()) { - return false; - } - - if (!(($this->configuration->defaultTimeLimit() || $test->size()->isKnown()))) { - return false; - } - - if (extension_loaded('xdebug') && xdebug_is_debugger_active()) { - return false; - } - - return true; - } - - /** - * @throws Throwable - */ - private function runTestWithTimeout(TestCase $test): bool - { - $_timeout = $this->configuration->defaultTimeLimit(); - - if ($test->size()->isSmall()) { - $_timeout = $this->configuration->timeoutForSmallTests(); - } elseif ($test->size()->isMedium()) { - $_timeout = $this->configuration->timeoutForMediumTests(); - } elseif ($test->size()->isLarge()) { - $_timeout = $this->configuration->timeoutForLargeTests(); - } - - try { - (new Invoker)->invoke([$test, 'runBare'], [], $_timeout); - } catch (TimeoutException) { - Event\Facade::emitter()->testConsideredRisky( - $test->valueObjectForEvents(), - sprintf( - 'This test was aborted after %d second%s', - $_timeout, - $_timeout !== 1 ? 's' : '', - ), - ); - - return true; - } - - return false; - } - - /** - * @throws ProcessIsolationException - */ - private function saveConfigurationForChildProcess(): string - { - $path = tempnam(sys_get_temp_dir(), 'phpunit_'); - - if (!$path) { - throw new ProcessIsolationException; - } - - if (!ConfigurationRegistry::saveTo($path)) { - throw new ProcessIsolationException; - } - - return $path; - } - - private function shouldErrorHandlerBeUsed(TestCase $test): bool - { - if (MetadataRegistry::parser()->forMethod($test::class, $test->name())->isWithoutErrorHandler()->isNotEmpty()) { - return false; - } - - return true; - } -} diff --git a/src/Framework/TestRunner/ChildProcessResultProcessor.php b/src/Framework/TestRunner/ChildProcessResultProcessor.php new file mode 100644 index 00000000000..7e13fc96a3b --- /dev/null +++ b/src/Framework/TestRunner/ChildProcessResultProcessor.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function assert; +use function trim; +use function unserialize; +use PHPUnit\Event\Code\TestMethodBuilder; +use PHPUnit\Event\Code\ThrowableBuilder; +use PHPUnit\Event\Emitter; +use PHPUnit\Event\Facade; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\TestRunner\TestResult\PassedTests; + +final readonly class ChildProcessResultProcessor +{ + private Facade $eventFacade; + private Emitter $emitter; + private PassedTests $passedTests; + private CodeCoverage $codeCoverage; + + public function __construct(Facade $eventFacade, Emitter $emitter, PassedTests $passedTests, CodeCoverage $codeCoverage) + { + $this->eventFacade = $eventFacade; + $this->emitter = $emitter; + $this->passedTests = $passedTests; + $this->codeCoverage = $codeCoverage; + } + + public function process(Test $test, string $serializedProcessResult, string $stderr): void + { + if ($stderr !== '') { + $exception = new Exception(trim($stderr)); + + assert($test instanceof TestCase); + + $this->emitter->testErrored( + TestMethodBuilder::fromTestCase($test), + ThrowableBuilder::from($exception), + ); + + return; + } + + $childResult = @unserialize($serializedProcessResult); + + if ($childResult === false) { + $exception = new AssertionFailedError('Test was run in child process and ended unexpectedly'); + + assert($test instanceof TestCase); + + $this->emitter->testErrored( + TestMethodBuilder::fromTestCase($test), + ThrowableBuilder::from($exception), + ); + + $this->emitter->testFinished( + TestMethodBuilder::fromTestCase($test), + 0, + ); + + return; + } + + $this->eventFacade->forward($childResult->events); + $this->passedTests->import($childResult->passedTests); + + assert($test instanceof TestCase); + + $test->setResult($childResult->testResult); + $test->addToAssertionCount($childResult->numAssertions); + + if (!$this->codeCoverage->isActive()) { + return; + } + + // @codeCoverageIgnoreStart + if (!$childResult->codeCoverage instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) { + return; + } + + CodeCoverage::instance()->codeCoverage()->merge( + $childResult->codeCoverage, + ); + // @codeCoverageIgnoreEnd + } +} diff --git a/src/Framework/TestRunner/IsolatedTestRunner.php b/src/Framework/TestRunner/IsolatedTestRunner.php new file mode 100644 index 00000000000..0322a6046f8 --- /dev/null +++ b/src/Framework/TestRunner/IsolatedTestRunner.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This interface is not covered by the backward compatibility promise for PHPUnit + */ +interface IsolatedTestRunner +{ + public function run(TestCase $test, bool $runEntireClass, bool $preserveGlobalState): void; +} diff --git a/src/Framework/TestRunner/IsolatedTestRunnerRegistry.php b/src/Framework/TestRunner/IsolatedTestRunnerRegistry.php new file mode 100644 index 00000000000..058246543a5 --- /dev/null +++ b/src/Framework/TestRunner/IsolatedTestRunnerRegistry.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IsolatedTestRunnerRegistry +{ + private static ?IsolatedTestRunner $runner = null; + + public static function run(TestCase $test, bool $runEntireClass, bool $preserveGlobalState): void + { + if (self::$runner === null) { + self::$runner = new SeparateProcessTestRunner; + } + + self::$runner->run($test, $runEntireClass, $preserveGlobalState); + } + + public static function set(IsolatedTestRunner $runner): void + { + self::$runner = $runner; + } +} diff --git a/src/Framework/TestRunner/SeparateProcessTestRunner.php b/src/Framework/TestRunner/SeparateProcessTestRunner.php new file mode 100644 index 00000000000..6a6e2adeb7f --- /dev/null +++ b/src/Framework/TestRunner/SeparateProcessTestRunner.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function assert; +use function defined; +use function get_include_path; +use function hrtime; +use function serialize; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use function unserialize; +use function var_export; +use PHPUnit\Event\NoPreviousThrowableException; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; +use PHPUnit\Util\GlobalState; +use PHPUnit\Util\PHP\Job; +use PHPUnit\Util\PHP\JobRunnerRegistry; +use ReflectionClass; +use SebastianBergmann\Template\InvalidArgumentException; +use SebastianBergmann\Template\Template; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SeparateProcessTestRunner implements IsolatedTestRunner +{ + /** + * @throws \PHPUnit\Runner\Exception + * @throws \PHPUnit\Util\Exception + * @throws Exception + * @throws InvalidArgumentException + * @throws NoPreviousThrowableException + * @throws ProcessIsolationException + */ + public function run(TestCase $test, bool $runEntireClass, bool $preserveGlobalState): void + { + $class = new ReflectionClass($test); + + if ($runEntireClass) { + $template = new Template( + __DIR__ . '/templates/class.tpl', + ); + } else { + $template = new Template( + __DIR__ . '/templates/method.tpl', + ); + } + + $bootstrap = ''; + $constants = ''; + $globals = ''; + $includedFiles = ''; + $iniSettings = ''; + + if (ConfigurationRegistry::get()->hasBootstrap()) { + $bootstrap = ConfigurationRegistry::get()->bootstrap(); + } + + if ($preserveGlobalState) { + $constants = GlobalState::getConstantsAsString(); + $globals = GlobalState::getGlobalsAsString(); + $includedFiles = GlobalState::getIncludedFilesAsString(); + $iniSettings = GlobalState::getIniSettingsAsString(); + } + + $coverage = CodeCoverage::instance()->isActive() ? 'true' : 'false'; + + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true); + } else { + $composerAutoload = '\'\''; + } + + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, true); + } else { + $phar = '\'\''; + } + + $data = var_export(serialize($test->providedData()), true); + $dataName = var_export($test->dataName(), true); + $dependencyInput = var_export(serialize($test->dependencyInput()), true); + $includePath = var_export(get_include_path(), true); + // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC + // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences + $data = "'." . $data . ".'"; + $dataName = "'.(" . $dataName . ").'"; + $dependencyInput = "'." . $dependencyInput . ".'"; + $includePath = "'." . $includePath . ".'"; + $offset = hrtime(); + $serializedConfiguration = $this->saveConfigurationForChildProcess(); + $processResultFile = tempnam(sys_get_temp_dir(), 'phpunit_'); + + $file = $class->getFileName(); + + assert($file !== false); + + $var = [ + 'bootstrap' => $bootstrap, + 'composerAutoload' => $composerAutoload, + 'phar' => $phar, + 'filename' => $file, + 'className' => $class->getName(), + 'collectCodeCoverageInformation' => $coverage, + 'data' => $data, + 'dataName' => $dataName, + 'dependencyInput' => $dependencyInput, + 'constants' => $constants, + 'globals' => $globals, + 'include_path' => $includePath, + 'included_files' => $includedFiles, + 'iniSettings' => $iniSettings, + 'name' => $test->name(), + 'offsetSeconds' => (string) $offset[0], + 'offsetNanoseconds' => (string) $offset[1], + 'serializedConfiguration' => $serializedConfiguration, + 'processResultFile' => $processResultFile, + ]; + + if (!$runEntireClass) { + $var['methodName'] = $test->name(); + } + + $template->setVar($var); + + $code = $template->render(); + + assert($code !== ''); + + JobRunnerRegistry::runTestJob(new Job($code), $processResultFile, $test); + + @unlink($serializedConfiguration); + } + + /** + * @throws ProcessIsolationException + */ + private function saveConfigurationForChildProcess(): string + { + $path = tempnam(sys_get_temp_dir(), 'phpunit_'); + + if ($path === false) { + throw new ProcessIsolationException; + } + + if (!ConfigurationRegistry::saveTo($path)) { + throw new ProcessIsolationException; + } + + return $path; + } +} diff --git a/src/Framework/TestRunner/TestRunner.php b/src/Framework/TestRunner/TestRunner.php new file mode 100644 index 00000000000..3dee54444f3 --- /dev/null +++ b/src/Framework/TestRunner/TestRunner.php @@ -0,0 +1,338 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_EOL; +use function assert; +use function extension_loaded; +use function sprintf; +use function xdebug_is_debugger_active; +use AssertionError; +use PHPUnit\Event\Facade; +use PHPUnit\Metadata\Api\CodeCoverage as CodeCoverageMetadataApi; +use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\Runner\ErrorHandler; +use PHPUnit\Runner\Exception; +use PHPUnit\TextUI\Configuration\Configuration; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; +use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; +use SebastianBergmann\CodeCoverage\InvalidArgumentException; +use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use SebastianBergmann\Invoker\Invoker; +use SebastianBergmann\Invoker\TimeoutException; +use Throwable; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestRunner +{ + private ?bool $timeLimitCanBeEnforced = null; + private readonly Configuration $configuration; + + public function __construct() + { + $this->configuration = ConfigurationRegistry::get(); + } + + /** + * @throws Exception + * @throws InvalidArgumentException + * @throws UnintentionallyCoveredCodeException + */ + public function run(TestCase $test): void + { + Assert::resetCount(); + + $codeCoverageMetadataApi = new CodeCoverageMetadataApi; + + $shouldCodeCoverageBeCollected = $codeCoverageMetadataApi->shouldCodeCoverageBeCollectedFor( + $test::class, + $test->name(), + ); + + $error = false; + $failure = false; + $incomplete = false; + $risky = false; + $skipped = false; + + if ($this->shouldErrorHandlerBeUsed($test)) { + ErrorHandler::instance()->enable(); + } + + $collectCodeCoverage = CodeCoverage::instance()->isActive() && + $shouldCodeCoverageBeCollected; + + if ($collectCodeCoverage) { + CodeCoverage::instance()->start($test); + } + + try { + if ($this->canTimeLimitBeEnforced() && + $this->shouldTimeLimitBeEnforced($test)) { + $risky = $this->runTestWithTimeout($test); + } else { + $test->runBare(); + } + } catch (AssertionFailedError $e) { + $failure = true; + + if ($e instanceof IncompleteTestError) { + $incomplete = true; + } elseif ($e instanceof SkippedTest) { + $skipped = true; + } + } catch (AssertionError $e) { + $test->addToAssertionCount(1); + + $failure = true; + $frame = $e->getTrace()[0]; + + assert(isset($frame['file'])); + assert(isset($frame['line'])); + + $e = new AssertionFailedError( + sprintf( + '%s in %s:%s', + $e->getMessage(), + $frame['file'], + $frame['line'], + ), + ); + } catch (Throwable $e) { + $error = true; + } + + $test->addToAssertionCount(Assert::getCount()); + + if ($this->configuration->reportUselessTests() && + !$test->doesNotPerformAssertions() && + $test->numberOfAssertionsPerformed() === 0) { + $risky = true; + } + + if (!$error && !$failure && !$incomplete && !$skipped && !$risky && + $this->configuration->requireCoverageMetadata() && + !$this->hasCoverageMetadata($test::class, $test->name())) { + Facade::emitter()->testConsideredRisky( + $test->valueObjectForEvents(), + 'This test does not define a code coverage target but is expected to do so', + ); + + $risky = true; + } + + if ($collectCodeCoverage) { + $append = !$risky && !$incomplete && !$skipped; + $covers = null; + $uses = null; + + if (!$append) { + $covers = false; + } + + if ($append) { + $covers = $codeCoverageMetadataApi->coversTargets( + $test::class, + $test->name(), + ); + + $uses = $codeCoverageMetadataApi->usesTargets( + $test::class, + $test->name(), + ); + } + + try { + CodeCoverage::instance()->stop( + $append, + $covers, + $uses, + ); + } catch (UnintentionallyCoveredCodeException $cce) { + Facade::emitter()->testConsideredRisky( + $test->valueObjectForEvents(), + 'This test executed code that is not listed as code to be covered or used:' . + PHP_EOL . + $cce->getMessage(), + ); + } catch (CodeCoverageException $cce) { + $error = true; + + $e = $e ?? $cce; + } + } + + ErrorHandler::instance()->disable(); + + if (!$error && + !$incomplete && + !$skipped && + $this->configuration->reportUselessTests() && + !$test->doesNotPerformAssertions() && + $test->numberOfAssertionsPerformed() === 0) { + Facade::emitter()->testConsideredRisky( + $test->valueObjectForEvents(), + 'This test did not perform any assertions', + ); + } + + if ($test->doesNotPerformAssertions() && + $test->numberOfAssertionsPerformed() > 0) { + Facade::emitter()->testConsideredRisky( + $test->valueObjectForEvents(), + sprintf( + 'This test is not expected to perform assertions but performed %d assertion%s', + $test->numberOfAssertionsPerformed(), + $test->numberOfAssertionsPerformed() > 1 ? 's' : '', + ), + ); + } + + if ($test->hasUnexpectedOutput()) { + Facade::emitter()->testPrintedUnexpectedOutput($test->output()); + } + + if ($this->configuration->disallowTestOutput() && $test->hasUnexpectedOutput()) { + Facade::emitter()->testConsideredRisky( + $test->valueObjectForEvents(), + sprintf( + 'Test code or tested code printed unexpected output: %s', + $test->output(), + ), + ); + } + + if ($test->wasPrepared()) { + Facade::emitter()->testFinished( + $test->valueObjectForEvents(), + $test->numberOfAssertionsPerformed(), + ); + } + } + + /** + * @param class-string $className + * @param non-empty-string $methodName + */ + private function hasCoverageMetadata(string $className, string $methodName): bool + { + foreach (MetadataRegistry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if ($metadata->isCoversNamespace()) { + return true; + } + + if ($metadata->isCoversTrait()) { + return true; + } + + if ($metadata->isCoversClass()) { + return true; + } + + if ($metadata->isCoversClassesThatExtendClass()) { + return true; + } + + if ($metadata->isCoversClassesThatImplementInterface()) { + return true; + } + + if ($metadata->isCoversMethod()) { + return true; + } + + if ($metadata->isCoversFunction()) { + return true; + } + + if ($metadata->isCoversNothing()) { + return true; + } + } + + return false; + } + + private function canTimeLimitBeEnforced(): bool + { + if ($this->timeLimitCanBeEnforced !== null) { + return $this->timeLimitCanBeEnforced; + } + + $this->timeLimitCanBeEnforced = (new Invoker)->canInvokeWithTimeout(); + + return $this->timeLimitCanBeEnforced; + } + + private function shouldTimeLimitBeEnforced(TestCase $test): bool + { + if (!$this->configuration->enforceTimeLimit()) { + return false; + } + + if (!(($this->configuration->defaultTimeLimit() > 0 || $test->size()->isKnown()))) { + return false; + } + + if (extension_loaded('xdebug') && xdebug_is_debugger_active()) { + return false; + } + + return true; + } + + /** + * @throws Throwable + */ + private function runTestWithTimeout(TestCase $test): bool + { + $_timeout = $this->configuration->defaultTimeLimit(); + $testSize = $test->size(); + + if ($testSize->isSmall()) { + $_timeout = $this->configuration->timeoutForSmallTests(); + } elseif ($testSize->isMedium()) { + $_timeout = $this->configuration->timeoutForMediumTests(); + } elseif ($testSize->isLarge()) { + $_timeout = $this->configuration->timeoutForLargeTests(); + } + + try { + (new Invoker)->invoke([$test, 'runBare'], [], $_timeout); + } catch (TimeoutException) { + Facade::emitter()->testConsideredRisky( + $test->valueObjectForEvents(), + sprintf( + 'This test was aborted after %d second%s', + $_timeout, + $_timeout !== 1 ? 's' : '', + ), + ); + + return true; + } + + return false; + } + + private function shouldErrorHandlerBeUsed(TestCase $test): bool + { + if (MetadataRegistry::parser()->forMethod($test::class, $test->name())->isWithoutErrorHandler()->isNotEmpty()) { + return false; + } + + return true; + } +} diff --git a/src/Framework/TestRunner/templates/class.tpl b/src/Framework/TestRunner/templates/class.tpl new file mode 100644 index 00000000000..5edd7ee2943 --- /dev/null +++ b/src/Framework/TestRunner/templates/class.tpl @@ -0,0 +1,137 @@ +initForIsolation( + PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( + {offsetSeconds}, + {offsetNanoseconds} + ), + ); + + require_once '{filename}'; + + $configuration = ConfigurationRegistry::get(); + + if ({collectCodeCoverageInformation}) { + CodeCoverage::instance()->init($configuration, CodeCoverageFilterRegistry::instance(), true); + } + + $deprecationTriggers = [ + 'functions' => [], + 'methods' => [], + ]; + + foreach ($configuration->source()->deprecationTriggers()['functions'] as $function) { + $deprecationTriggers['functions'][] = $function; + } + + foreach ($configuration->source()->deprecationTriggers()['methods'] as $method) { + [$className, $methodName] = explode('::', $method); + + $deprecationTriggers['methods'][] = [ + 'className' => $className, + 'methodName' => $methodName, + ]; + } + + ErrorHandler::instance()->useDeprecationTriggers($deprecationTriggers); + + $test = new {className}('{name}'); + + $test->setData('{dataName}', unserialize('{data}')); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + + $test->run(); + + $output = ''; + + if (!$test->expectsOutput()) { + $output = $test->output(); + } + + ini_set('xdebug.scream', '0'); + + // Not every STDOUT target stream is rewindable + @rewind(STDOUT); + + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } + } + + file_put_contents( + '{processResultFile}', + serialize( + (object)[ + 'testResult' => $test->result(), + 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, + 'numAssertions' => $test->numberOfAssertionsPerformed(), + 'output' => $output, + 'events' => $dispatcher->flush(), + 'passedTests' => PassedTests::instance() + ] + ) + ); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +ConfigurationRegistry::loadFrom('{serializedConfiguration}'); +(new PhpHandler)->handle(ConfigurationRegistry::get()->php()); + +if ('{bootstrap}' !== '') { + require_once '{bootstrap}'; +} + +__phpunit_run_isolated_test(); diff --git a/src/Framework/TestRunner/templates/method.tpl b/src/Framework/TestRunner/templates/method.tpl new file mode 100644 index 00000000000..8d4f38439df --- /dev/null +++ b/src/Framework/TestRunner/templates/method.tpl @@ -0,0 +1,137 @@ +initForIsolation( + PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( + {offsetSeconds}, + {offsetNanoseconds} + ), + ); + + require_once '{filename}'; + + $configuration = ConfigurationRegistry::get(); + + if ({collectCodeCoverageInformation}) { + CodeCoverage::instance()->init($configuration, CodeCoverageFilterRegistry::instance(), true); + } + + $deprecationTriggers = [ + 'functions' => [], + 'methods' => [], + ]; + + foreach ($configuration->source()->deprecationTriggers()['functions'] as $function) { + $deprecationTriggers['functions'][] = $function; + } + + foreach ($configuration->source()->deprecationTriggers()['methods'] as $method) { + [$className, $methodName] = explode('::', $method); + + $deprecationTriggers['methods'][] = [ + 'className' => $className, + 'methodName' => $methodName, + ]; + } + + ErrorHandler::instance()->useDeprecationTriggers($deprecationTriggers); + + $test = new {className}('{methodName}'); + + $test->setData('{dataName}', unserialize('{data}')); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(true); + + ob_end_clean(); + + $test->run(); + + $output = ''; + + if (!$test->expectsOutput()) { + $output = $test->output(); + } + + ini_set('xdebug.scream', '0'); + + // Not every STDOUT target stream is rewindable + @rewind(STDOUT); + + if ($stdout = @stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } + } + + file_put_contents( + '{processResultFile}', + serialize( + (object)[ + 'testResult' => $test->result(), + 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, + 'numAssertions' => $test->numberOfAssertionsPerformed(), + 'output' => $output, + 'events' => $dispatcher->flush(), + 'passedTests' => PassedTests::instance() + ] + ) + ); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +ConfigurationRegistry::loadFrom('{serializedConfiguration}'); +(new PhpHandler)->handle(ConfigurationRegistry::get()->php()); + +if ('{bootstrap}' !== '') { + require_once '{bootstrap}'; +} + +__phpunit_run_isolated_test(); diff --git a/src/Framework/TestSize/Known.php b/src/Framework/TestSize/Known.php index d7dca96e916..a61a05a0017 100644 --- a/src/Framework/TestSize/Known.php +++ b/src/Framework/TestSize/Known.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestSize; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -abstract class Known extends TestSize +abstract readonly class Known extends TestSize { - /** - * @psalm-assert-if-true Known $this - */ - public function isKnown(): bool + public function isKnown(): true { return true; } diff --git a/src/Framework/TestSize/Large.php b/src/Framework/TestSize/Large.php index bdf001843b5..71a254ed470 100644 --- a/src/Framework/TestSize/Large.php +++ b/src/Framework/TestSize/Large.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestSize; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class Large extends Known +final readonly class Large extends Known { - /** - * @psalm-assert-if-true Large $this - */ - public function isLarge(): bool + public function isLarge(): true { return true; } diff --git a/src/Framework/TestSize/Medium.php b/src/Framework/TestSize/Medium.php index b16edaa5511..beb574463dd 100644 --- a/src/Framework/TestSize/Medium.php +++ b/src/Framework/TestSize/Medium.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestSize; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class Medium extends Known +final readonly class Medium extends Known { - /** - * @psalm-assert-if-true Medium $this - */ - public function isMedium(): bool + public function isMedium(): true { return true; } diff --git a/src/Framework/TestSize/Small.php b/src/Framework/TestSize/Small.php index 1387e93088b..76bdec64344 100644 --- a/src/Framework/TestSize/Small.php +++ b/src/Framework/TestSize/Small.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestSize; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class Small extends Known +final readonly class Small extends Known { - /** - * @psalm-assert-if-true Small $this - */ - public function isSmall(): bool + public function isSmall(): true { return true; } diff --git a/src/Framework/TestSize/TestSize.php b/src/Framework/TestSize/TestSize.php index 7d876ea4248..c341d8b934b 100644 --- a/src/Framework/TestSize/TestSize.php +++ b/src/Framework/TestSize/TestSize.php @@ -10,11 +10,13 @@ namespace PHPUnit\Framework\TestSize; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -abstract class TestSize +abstract readonly class TestSize { public static function unknown(): self { @@ -37,7 +39,7 @@ public static function large(): self } /** - * @psalm-assert-if-true Known $this + * @phpstan-assert-if-true Known $this */ public function isKnown(): bool { @@ -45,7 +47,7 @@ public function isKnown(): bool } /** - * @psalm-assert-if-true Unknown $this + * @phpstan-assert-if-true Unknown $this */ public function isUnknown(): bool { @@ -53,7 +55,7 @@ public function isUnknown(): bool } /** - * @psalm-assert-if-true Small $this + * @phpstan-assert-if-true Small $this */ public function isSmall(): bool { @@ -61,7 +63,7 @@ public function isSmall(): bool } /** - * @psalm-assert-if-true Medium $this + * @phpstan-assert-if-true Medium $this */ public function isMedium(): bool { @@ -69,7 +71,7 @@ public function isMedium(): bool } /** - * @psalm-assert-if-true Large $this + * @phpstan-assert-if-true Large $this */ public function isLarge(): bool { diff --git a/src/Framework/TestSize/Unknown.php b/src/Framework/TestSize/Unknown.php index d0884e928d9..daf1e7d35ba 100644 --- a/src/Framework/TestSize/Unknown.php +++ b/src/Framework/TestSize/Unknown.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestSize; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class Unknown extends TestSize +final readonly class Unknown extends TestSize { - /** - * @psalm-assert-if-true Unknown $this - */ - public function isUnknown(): bool + public function isUnknown(): true { return true; } diff --git a/src/Framework/TestStatus/Deprecation.php b/src/Framework/TestStatus/Deprecation.php index 2711760dc15..0bc4957ca56 100644 --- a/src/Framework/TestStatus/Deprecation.php +++ b/src/Framework/TestStatus/Deprecation.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Deprecation extends Known +final readonly class Deprecation extends Known { - /** - * @psalm-assert-if-true Deprecation $this - */ - public function isDeprecation(): bool + public function isDeprecation(): true { return true; } diff --git a/src/Framework/TestStatus/Error.php b/src/Framework/TestStatus/Error.php index 6ddb3b1b35d..35e368e813c 100644 --- a/src/Framework/TestStatus/Error.php +++ b/src/Framework/TestStatus/Error.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Error extends Known +final readonly class Error extends Known { - /** - * @psalm-assert-if-true Error $this - */ - public function isError(): bool + public function isError(): true { return true; } diff --git a/src/Framework/TestStatus/Failure.php b/src/Framework/TestStatus/Failure.php index 1bb1b0379a3..ec5996c90cf 100644 --- a/src/Framework/TestStatus/Failure.php +++ b/src/Framework/TestStatus/Failure.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Failure extends Known +final readonly class Failure extends Known { - /** - * @psalm-assert-if-true Failure $this - */ - public function isFailure(): bool + public function isFailure(): true { return true; } diff --git a/src/Framework/TestStatus/Incomplete.php b/src/Framework/TestStatus/Incomplete.php index eff26721f1b..92f86fba86d 100644 --- a/src/Framework/TestStatus/Incomplete.php +++ b/src/Framework/TestStatus/Incomplete.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Incomplete extends Known +final readonly class Incomplete extends Known { - /** - * @psalm-assert-if-true Incomplete $this - */ - public function isIncomplete(): bool + public function isIncomplete(): true { return true; } diff --git a/src/Framework/TestStatus/Known.php b/src/Framework/TestStatus/Known.php index b5d92b39857..4d9e5e8b4b7 100644 --- a/src/Framework/TestStatus/Known.php +++ b/src/Framework/TestStatus/Known.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Known extends TestStatus +abstract readonly class Known extends TestStatus { - /** - * @psalm-assert-if-true Known $this - */ - public function isKnown(): bool + public function isKnown(): true { return true; } diff --git a/src/Framework/TestStatus/Notice.php b/src/Framework/TestStatus/Notice.php index 1eb10761e22..be9bd4c03d7 100644 --- a/src/Framework/TestStatus/Notice.php +++ b/src/Framework/TestStatus/Notice.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Notice extends Known +final readonly class Notice extends Known { - /** - * @psalm-assert-if-true Notice $this - */ - public function isNotice(): bool + public function isNotice(): true { return true; } diff --git a/src/Framework/TestStatus/Risky.php b/src/Framework/TestStatus/Risky.php index bd0cb889028..63bde2972c2 100644 --- a/src/Framework/TestStatus/Risky.php +++ b/src/Framework/TestStatus/Risky.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Risky extends Known +final readonly class Risky extends Known { - /** - * @psalm-assert-if-true Risky $this - */ - public function isRisky(): bool + public function isRisky(): true { return true; } diff --git a/src/Framework/TestStatus/Skipped.php b/src/Framework/TestStatus/Skipped.php index 4c8aeaf9f5b..9539e9e0d82 100644 --- a/src/Framework/TestStatus/Skipped.php +++ b/src/Framework/TestStatus/Skipped.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Skipped extends Known +final readonly class Skipped extends Known { - /** - * @psalm-assert-if-true Skipped $this - */ - public function isSkipped(): bool + public function isSkipped(): true { return true; } diff --git a/src/Framework/TestStatus/Success.php b/src/Framework/TestStatus/Success.php index fb5c945ed69..7891505165e 100644 --- a/src/Framework/TestStatus/Success.php +++ b/src/Framework/TestStatus/Success.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Success extends Known +final readonly class Success extends Known { - /** - * @psalm-assert-if-true Success $this - */ - public function isSuccess(): bool + public function isSuccess(): true { return true; } diff --git a/src/Framework/TestStatus/TestStatus.php b/src/Framework/TestStatus/TestStatus.php index ab957de130e..81870826bd8 100644 --- a/src/Framework/TestStatus/TestStatus.php +++ b/src/Framework/TestStatus/TestStatus.php @@ -10,13 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class TestStatus +abstract readonly class TestStatus { - private readonly string $message; + private string $message; public static function from(int $status): self { @@ -90,7 +92,7 @@ private function __construct(string $message = '') } /** - * @psalm-assert-if-true Known $this + * @phpstan-assert-if-true Known $this */ public function isKnown(): bool { @@ -98,7 +100,7 @@ public function isKnown(): bool } /** - * @psalm-assert-if-true Unknown $this + * @phpstan-assert-if-true Unknown $this */ public function isUnknown(): bool { @@ -106,7 +108,7 @@ public function isUnknown(): bool } /** - * @psalm-assert-if-true Success $this + * @phpstan-assert-if-true Success $this */ public function isSuccess(): bool { @@ -114,7 +116,7 @@ public function isSuccess(): bool } /** - * @psalm-assert-if-true Skipped $this + * @phpstan-assert-if-true Skipped $this */ public function isSkipped(): bool { @@ -122,7 +124,7 @@ public function isSkipped(): bool } /** - * @psalm-assert-if-true Incomplete $this + * @phpstan-assert-if-true Incomplete $this */ public function isIncomplete(): bool { @@ -130,7 +132,7 @@ public function isIncomplete(): bool } /** - * @psalm-assert-if-true Notice $this + * @phpstan-assert-if-true Notice $this */ public function isNotice(): bool { @@ -138,7 +140,7 @@ public function isNotice(): bool } /** - * @psalm-assert-if-true Deprecation $this + * @phpstan-assert-if-true Deprecation $this */ public function isDeprecation(): bool { @@ -146,7 +148,7 @@ public function isDeprecation(): bool } /** - * @psalm-assert-if-true Failure $this + * @phpstan-assert-if-true Failure $this */ public function isFailure(): bool { @@ -154,7 +156,7 @@ public function isFailure(): bool } /** - * @psalm-assert-if-true Error $this + * @phpstan-assert-if-true Error $this */ public function isError(): bool { @@ -162,7 +164,7 @@ public function isError(): bool } /** - * @psalm-assert-if-true Warning $this + * @phpstan-assert-if-true Warning $this */ public function isWarning(): bool { @@ -170,7 +172,7 @@ public function isWarning(): bool } /** - * @psalm-assert-if-true Risky $this + * @phpstan-assert-if-true Risky $this */ public function isRisky(): bool { diff --git a/src/Framework/TestStatus/Unknown.php b/src/Framework/TestStatus/Unknown.php index 245ec67ee30..a5c1309b563 100644 --- a/src/Framework/TestStatus/Unknown.php +++ b/src/Framework/TestStatus/Unknown.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Unknown extends TestStatus +final readonly class Unknown extends TestStatus { - /** - * @psalm-assert-if-true Unknown $this - */ - public function isUnknown(): bool + public function isUnknown(): true { return true; } diff --git a/src/Framework/TestStatus/Warning.php b/src/Framework/TestStatus/Warning.php index 7c31fa8f6bb..c091262b8b8 100644 --- a/src/Framework/TestStatus/Warning.php +++ b/src/Framework/TestStatus/Warning.php @@ -10,16 +10,15 @@ namespace PHPUnit\Framework\TestStatus; /** + * @immutable + * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Warning extends Known +final readonly class Warning extends Known { - /** - * @psalm-assert-if-true Warning $this - */ - public function isWarning(): bool + public function isWarning(): true { return true; } diff --git a/src/Framework/TestSuite.php b/src/Framework/TestSuite.php index 84cdb6ea4c3..a4c5886704a 100644 --- a/src/Framework/TestSuite.php +++ b/src/Framework/TestSuite.php @@ -10,8 +10,9 @@ namespace PHPUnit\Framework; use const PHP_EOL; -use function array_keys; -use function array_map; +use function array_merge; +use function array_pop; +use function array_reverse; use function assert; use function call_user_func; use function class_exists; @@ -36,7 +37,7 @@ use PHPUnit\Metadata\MetadataCollection; use PHPUnit\Runner\Exception as RunnerException; use PHPUnit\Runner\Filter\Factory; -use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use PHPUnit\Runner\TestSuiteLoader; use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade; use PHPUnit\Util\Filter; @@ -44,36 +45,48 @@ use PHPUnit\Util\Test as TestUtil; use ReflectionClass; use ReflectionMethod; +use SebastianBergmann\CodeCoverage\InvalidArgumentException; use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; use Throwable; /** * @template-implements IteratorAggregate * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -class TestSuite implements IteratorAggregate, Reorderable, SelfDescribing, Test +class TestSuite implements IteratorAggregate, Reorderable, Test { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $name; /** - * @psalm-var array> + * @var array> + */ + private array $groups = []; + + /** + * @var ?list */ - private array $groups = []; private ?array $requiredTests = null; /** - * @psalm-var list + * @var list + */ + private array $tests = []; + + /** + * @var ?list */ - private array $tests = []; private ?array $providedTests = null; private ?Factory $iteratorFilter = null; + private bool $wasRun = false; /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name */ public static function empty(string $name): static { @@ -81,52 +94,23 @@ public static function empty(string $name): static } /** - * @psalm-param class-string $className + * @param ReflectionClass $class + * @param list $groups */ - public static function fromClassName(string $className): static - { - assert(class_exists($className)); - - $class = new ReflectionClass($className); - - return static::fromClassReflector($class); - } - - public static function fromClassReflector(ReflectionClass $class): static + public static function fromClassReflector(ReflectionClass $class, array $groups = []): static { $testSuite = new static($class->getName()); - $constructor = $class->getConstructor(); - - if ($constructor !== null && !$constructor->isPublic()) { - Event\Facade::emitter()->testRunnerTriggeredWarning( - sprintf( - 'Class "%s" has no public constructor.', - $class->getName(), - ), - ); - - return $testSuite; - } - - foreach (Reflection::publicMethodsInTestClass($class) as $method) { - if ($method->getDeclaringClass()->getName() === Assert::class) { - continue; - } - - if ($method->getDeclaringClass()->getName() === TestCase::class) { - continue; - } - + foreach (Reflection::publicMethodsDeclaredDirectlyInTestClass($class) as $method) { if (!TestUtil::isTestMethod($method)) { continue; } - $testSuite->addTestMethod($class, $method); + $testSuite->addTestMethod($class, $method, $groups); } - if (count($testSuite) === 0) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + if ($testSuite->isEmpty()) { + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'No tests found in class "%s".', $class->getName(), @@ -138,50 +122,51 @@ public static function fromClassReflector(ReflectionClass $class): static } /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name */ final private function __construct(string $name) { $this->name = $name; } - /** - * Returns a string representation of the test suite. - */ - public function toString(): string - { - return $this->name(); - } - /** * Adds a test to the suite. + * + * @param list $groups */ public function addTest(Test $test, array $groups = []): void { - $class = new ReflectionClass($test); - - if (!$class->isAbstract()) { + if ($test instanceof self) { $this->tests[] = $test; + $this->clearCaches(); - if ($test instanceof self && empty($groups)) { - $groups = $test->groups(); - } + return; + } - if ($this->containsOnlyVirtualGroups($groups)) { - $groups[] = 'default'; - } + assert($test instanceof TestCase || $test instanceof PhptTestCase); - foreach ($groups as $group) { - if (!isset($this->groups[$group])) { - $this->groups[$group] = [$test]; - } else { - $this->groups[$group][] = $test; - } - } + $this->tests[] = $test; + + $this->clearCaches(); + + if ($this->containsOnlyVirtualGroups($groups)) { + $groups[] = 'default'; + } - if ($test instanceof TestCase) { - $test->setGroups($groups); + if ($test instanceof TestCase) { + $id = $test->valueObjectForEvents()->id(); + + $test->setGroups($groups); + } else { + $id = $test->valueObjectForEvents()->id(); + } + + foreach ($groups as $group) { + if (!isset($this->groups[$group])) { + $this->groups[$group] = [$id]; + } else { + $this->groups[$group][] = $id; } } } @@ -189,9 +174,12 @@ public function addTest(Test $test, array $groups = []): void /** * Adds the tests from the given class to the suite. * + * @param ReflectionClass $testClass + * @param list $groups + * * @throws Exception */ - public function addTestSuite(ReflectionClass $testClass): void + public function addTestSuite(ReflectionClass $testClass, array $groups = []): void { if ($testClass->isAbstract()) { throw new Exception( @@ -212,7 +200,7 @@ public function addTestSuite(ReflectionClass $testClass): void ); } - $this->addTest(self::fromClassReflector($testClass)); + $this->addTest(self::fromClassReflector($testClass, $groups), $groups); } /** @@ -223,28 +211,23 @@ public function addTestSuite(ReflectionClass $testClass): void * added, a PHPUnit\Framework\WarningTestCase will be created instead, * leaving the current test run untouched. * + * @param list $groups + * * @throws Exception */ - public function addTestFile(string $filename): void + public function addTestFile(string $filename, array $groups = []): void { - if (is_file($filename) && str_ends_with($filename, '.phpt')) { - try { + try { + if (str_ends_with($filename, '.phpt') && is_file($filename)) { $this->addTest(new PhptTestCase($filename)); - } catch (RunnerException $e) { - Event\Facade::emitter()->testRunnerTriggeredWarning( - $e->getMessage(), + } else { + $this->addTestSuite( + (new TestSuiteLoader)->load($filename), + $groups, ); } - - return; - } - - try { - $this->addTestSuite( - (new TestSuiteLoader)->load($filename), - ); } catch (RunnerException $e) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( $e->getMessage(), ); } @@ -253,6 +236,8 @@ public function addTestFile(string $filename): void /** * Wrapper for addTestFile() that adds multiple test files. * + * @param iterable $fileNames + * * @throws Exception */ public function addTestFiles(iterable $fileNames): void @@ -278,11 +263,17 @@ public function count(): int public function isEmpty(): bool { - return empty($this->tests); + foreach ($this as $test) { + if (count($test) !== 0) { + return false; + } + } + + return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function name(): string { @@ -290,34 +281,53 @@ public function name(): string } /** - * Returns the test groups of the suite. - * - * @psalm-return list + * @return array> */ public function groups(): array { - return array_map( - 'strval', - array_keys($this->groups), - ); + return $this->groups; } - public function groupDetails(): array + /** + * @return list + */ + public function collect(): array { - return $this->groups; + $tests = []; + + foreach ($this as $test) { + if ($test instanceof self) { + $tests = array_merge($tests, $test->collect()); + + continue; + } + + assert($test instanceof TestCase || $test instanceof PhptTestCase); + + $tests[] = $test; + } + + return $tests; } /** - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws CodeCoverageException * @throws Event\RuntimeException * @throws Exception + * @throws InvalidArgumentException * @throws NoPreviousThrowableException * @throws UnintentionallyCoveredCodeException */ public function run(): void { - if (count($this) === 0) { + if ($this->wasRun) { + // @codeCoverageIgnoreStart + throw new Exception('The tests aggregated by this TestSuite were already run'); + // @codeCoverageIgnoreEnd + } + + $this->wasRun = true; + + if ($this->isEmpty()) { return; } @@ -330,7 +340,19 @@ public function run(): void return; } + /** @var list $tests */ + $tests = []; + foreach ($this as $test) { + $tests[] = $test; + } + + $tests = array_reverse($tests); + + $this->tests = []; + $this->groups = []; + + while (($test = array_pop($tests)) !== null) { if (TestResultFacade::shouldStop()) { $emitter->testRunnerExecutionAborted(); @@ -348,7 +370,7 @@ public function run(): void /** * Returns the tests as an enumeration. * - * @psalm-return list + * @return list */ public function tests(): array { @@ -358,7 +380,7 @@ public function tests(): array /** * Set tests of the test suite. * - * @psalm-param list $tests + * @param list $tests */ public function setTests(array $tests): void { @@ -401,7 +423,7 @@ public function injectFilter(Factory $filter): void } /** - * @psalm-return list + * @return list */ public function provides(): array { @@ -413,8 +435,10 @@ public function provides(): array } foreach ($this->tests as $test) { - if (!($test instanceof Reorderable)) { + if (!$test instanceof Reorderable) { + // @codeCoverageIgnoreStart continue; + // @codeCoverageIgnoreEnd } $this->providedTests = ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); @@ -425,7 +449,7 @@ public function provides(): array } /** - * @psalm-return list + * @return list */ public function requires(): array { @@ -433,8 +457,10 @@ public function requires(): array $this->requiredTests = []; foreach ($this->tests as $test) { - if (!($test instanceof Reorderable)) { + if (!$test instanceof Reorderable) { + // @codeCoverageIgnoreStart continue; + // @codeCoverageIgnoreEnd } $this->requiredTests = ExecutionOrderDependency::mergeUnique( @@ -455,7 +481,7 @@ public function sortId(): string } /** - * @psalm-assert-if-true class-string $this->name + * @phpstan-assert-if-true class-string $this->name */ public function isForTestClass(): bool { @@ -463,17 +489,18 @@ public function isForTestClass(): bool } /** + * @param ReflectionClass $class + * @param list $groups + * * @throws Exception */ - protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method): void + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method, array $groups): void { $className = $class->getName(); $methodName = $method->getName(); - assert(!empty($methodName)); - try { - $test = (new TestBuilder)->build($class, $methodName); + $test = (new TestBuilder)->build($class, $methodName, $groups); } catch (InvalidDataProviderException $e) { Event\Facade::emitter()->testTriggeredPhpunitError( new TestMethod( @@ -492,7 +519,7 @@ protected function addTestMethod(ReflectionClass $class, ReflectionMethod $metho "The data provider specified for %s::%s is invalid\n%s", $className, $methodName, - $this->throwableToString($e), + $this->exceptionToString($e), ), ); @@ -507,7 +534,10 @@ protected function addTestMethod(ReflectionClass $class, ReflectionMethod $metho $this->addTest( $test, - (new Groups)->groups($class->getName(), $methodName), + array_merge( + $groups, + (new Groups)->groups($class->getName(), $methodName), + ), ); } @@ -517,6 +547,9 @@ private function clearCaches(): void $this->requiredTests = null; } + /** + * @param list $groups + */ private function containsOnlyVirtualGroups(array $groups): bool { foreach ($groups as $group) { @@ -539,27 +572,18 @@ private function methodDoesNotExistOrIsDeclaredInTestCase(string $methodName): b /** * @throws Exception */ - private function throwableToString(Throwable $t): string + private function exceptionToString(InvalidDataProviderException $e): string { - $message = $t->getMessage(); + $message = $e->getMessage(); - if (empty(trim($message))) { + if (trim($message) === '') { $message = ''; } - if ($t instanceof InvalidDataProviderException) { - return sprintf( - "%s\n%s", - $message, - Filter::getFilteredStacktrace($t), - ); - } - return sprintf( - "%s: %s\n%s", - $t::class, + "%s\n%s", $message, - Filter::getFilteredStacktrace($t), + Filter::stackTraceFromThrowableAsString($e), ); } @@ -573,68 +597,83 @@ private function invokeMethodsBeforeFirstTest(Event\Emitter $emitter, Event\Test return true; } - $methodsCalledBeforeFirstTest = []; + $methods = (new HookMethods)->hookMethods($this->name)['beforeClass']->methodNamesSortedByPriority(); + $calledMethods = []; + $emitCalledEvent = true; + $result = true; - $beforeClassMethods = (new HookMethods)->hookMethods($this->name)['beforeClass']; + foreach ($methods as $method) { + if ($this->methodDoesNotExistOrIsDeclaredInTestCase($method)) { + continue; + } - try { - foreach ($beforeClassMethods as $beforeClassMethod) { - if ($this->methodDoesNotExistOrIsDeclaredInTestCase($beforeClassMethod)) { - continue; - } + $calledMethod = new Event\Code\ClassMethod( + $this->name, + $method, + ); + + try { + $missingRequirements = (new Requirements)->requirementsNotSatisfiedFor($this->name, $method); + + if ($missingRequirements !== []) { + $emitCalledEvent = false; - if ($missingRequirements = (new Requirements)->requirementsNotSatisfiedFor($this->name, $beforeClassMethod)) { $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements)); } - $methodCalledBeforeFirstTest = new Event\Code\ClassMethod( - $this->name, - $beforeClassMethod, - ); + call_user_func([$this->name, $method]); + } catch (Throwable $t) { + } - $emitter->testBeforeFirstTestMethodCalled( + if ($emitCalledEvent) { + $emitter->beforeFirstTestMethodCalled( $this->name, - $methodCalledBeforeFirstTest, + $calledMethod, ); - $methodsCalledBeforeFirstTest[] = $methodCalledBeforeFirstTest; - - call_user_func([$this->name, $beforeClassMethod]); + $calledMethods[] = $calledMethod; } - } catch (SkippedTest|SkippedTestSuiteError $e) { - $emitter->testSuiteSkipped( - $testSuiteValueObjectForEvents, - $e->getMessage(), - ); - return false; - } catch (Throwable $t) { - assert(isset($methodCalledBeforeFirstTest)); - - $emitter->testBeforeFirstTestMethodErrored( - $this->name, - $methodCalledBeforeFirstTest, - Event\Code\ThrowableBuilder::from($t), - ); - - if (!empty($methodsCalledBeforeFirstTest)) { - $emitter->testBeforeFirstTestMethodFinished( - $this->name, - ...$methodsCalledBeforeFirstTest, + if (isset($t) && $t instanceof SkippedTest) { + $emitter->testSuiteSkipped( + $testSuiteValueObjectForEvents, + $t->getMessage(), ); + + return false; } - return false; + if (isset($t)) { + if ($t instanceof AssertionFailedError) { + $emitter->beforeFirstTestMethodFailed( + $this->name, + $calledMethod, + Event\Code\ThrowableBuilder::from($t), + ); + } else { + $emitter->beforeFirstTestMethodErrored( + $this->name, + $calledMethod, + Event\Code\ThrowableBuilder::from($t), + ); + } + + $result = false; + } } - if (!empty($methodsCalledBeforeFirstTest)) { - $emitter->testBeforeFirstTestMethodFinished( + if ($calledMethods !== []) { + $emitter->beforeFirstTestMethodFinished( $this->name, - ...$methodsCalledBeforeFirstTest, + ...$calledMethods, ); } - return true; + if (!$result) { + $emitter->testSuiteFinished($testSuiteValueObjectForEvents); + } + + return $result; } private function invokeMethodsAfterLastTest(Event\Emitter $emitter): void @@ -643,38 +682,52 @@ private function invokeMethodsAfterLastTest(Event\Emitter $emitter): void return; } - $methodsCalledAfterLastTest = []; - - $afterClassMethods = (new HookMethods)->hookMethods($this->name)['afterClass']; + $methods = (new HookMethods)->hookMethods($this->name)['afterClass']->methodNamesSortedByPriority(); + $calledMethods = []; - foreach ($afterClassMethods as $afterClassMethod) { - if ($this->methodDoesNotExistOrIsDeclaredInTestCase($afterClassMethod)) { + foreach ($methods as $method) { + if ($this->methodDoesNotExistOrIsDeclaredInTestCase($method)) { continue; } + $calledMethod = new Event\Code\ClassMethod( + $this->name, + $method, + ); + try { - call_user_func([$this->name, $afterClassMethod]); + call_user_func([$this->name, $method]); + } catch (Throwable $t) { + } - $methodCalledAfterLastTest = new Event\Code\ClassMethod( - $this->name, - $afterClassMethod, - ); + $emitter->afterLastTestMethodCalled( + $this->name, + $calledMethod, + ); - $emitter->testAfterLastTestMethodCalled( - $this->name, - $methodCalledAfterLastTest, - ); + $calledMethods[] = $calledMethod; - $methodsCalledAfterLastTest[] = $methodCalledAfterLastTest; - } catch (Throwable) { - // @todo + if (isset($t)) { + if ($t instanceof AssertionFailedError) { + $emitter->afterLastTestMethodFailed( + $this->name, + $calledMethod, + Event\Code\ThrowableBuilder::from($t), + ); + } else { + $emitter->afterLastTestMethodErrored( + $this->name, + $calledMethod, + Event\Code\ThrowableBuilder::from($t), + ); + } } } - if (!empty($methodsCalledAfterLastTest)) { - $emitter->testAfterLastTestMethodFinished( + if ($calledMethods !== []) { + $emitter->afterLastTestMethodFinished( $this->name, - ...$methodsCalledAfterLastTest, + ...$calledMethods, ); } } diff --git a/src/Framework/TestSuiteIterator.php b/src/Framework/TestSuiteIterator.php index 32ee7c6d62c..3cbf431164a 100644 --- a/src/Framework/TestSuiteIterator.php +++ b/src/Framework/TestSuiteIterator.php @@ -16,6 +16,8 @@ /** * @template-implements RecursiveIterator * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestSuiteIterator implements RecursiveIterator @@ -23,7 +25,7 @@ final class TestSuiteIterator implements RecursiveIterator private int $position = 0; /** - * @psalm-var list + * @var list */ private readonly array $tests; diff --git a/src/Logging/EventLogger.php b/src/Logging/EventLogger.php index 53ccadbfc63..738f3037799 100644 --- a/src/Logging/EventLogger.php +++ b/src/Logging/EventLogger.php @@ -12,15 +12,18 @@ use const FILE_APPEND; use const LOCK_EX; use const PHP_EOL; -use function explode; +use const PHP_OS_FAMILY; use function file_put_contents; use function implode; +use function preg_split; use function str_repeat; use function strlen; use PHPUnit\Event\Event; use PHPUnit\Event\Tracer\Tracer; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class EventLogger implements Tracer @@ -38,12 +41,23 @@ public function trace(Event $event): void { $telemetryInfo = $this->telemetryInfo($event); $indentation = PHP_EOL . str_repeat(' ', strlen($telemetryInfo)); - $lines = explode(PHP_EOL, $event->asString()); + $flags = FILE_APPEND; + + if (!(PHP_OS_FAMILY === 'Windows' || PHP_OS_FAMILY === 'Darwin') || + $this->path !== 'php://stdout') { + $flags |= LOCK_EX; + } + + $lines = preg_split('/\r\n|\r|\n/', $event->asString()); + + if ($lines === false) { + $lines = []; + } file_put_contents( $this->path, $telemetryInfo . implode($indentation, $lines) . PHP_EOL, - FILE_APPEND | LOCK_EX, + $flags, ); } diff --git a/src/Logging/Exception.php b/src/Logging/Exception.php deleted file mode 100644 index f2e4f8bc734..00000000000 --- a/src/Logging/Exception.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Exception extends RuntimeException implements \PHPUnit\Exception -{ -} diff --git a/src/Logging/JUnit/JunitXmlLogger.php b/src/Logging/JUnit/JunitXmlLogger.php index d200713d79c..14a71b769de 100644 --- a/src/Logging/JUnit/JunitXmlLogger.php +++ b/src/Logging/JUnit/JunitXmlLogger.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Logging\JUnit; +use const PHP_EOL; use function assert; use function basename; use function is_int; @@ -19,7 +20,6 @@ use DOMElement; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Code\TestMethod; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Telemetry\HRTime; @@ -28,15 +28,17 @@ use PHPUnit\Event\Test\Failed; use PHPUnit\Event\Test\Finished; use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\PreparationStarted; use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\PrintedUnexpectedOutput; use PHPUnit\Event\Test\Skipped; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Event\TestSuite\Started; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\TextUI\Output\Printer; use PHPUnit\Util\Xml; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class JunitXmlLogger @@ -46,48 +48,46 @@ final class JunitXmlLogger private DOMElement $root; /** - * @var DOMElement[] + * @var array */ private array $testSuites = []; /** - * @psalm-var array + * @var array */ private array $testSuiteTests = [0]; /** - * @psalm-var array + * @var array */ private array $testSuiteAssertions = [0]; /** - * @psalm-var array + * @var array */ private array $testSuiteErrors = [0]; /** - * @psalm-var array + * @var array */ private array $testSuiteFailures = [0]; /** - * @psalm-var array + * @var array */ private array $testSuiteSkipped = [0]; /** - * @psalm-var array + * @var array */ - private array $testSuiteTimes = [0]; + private array $testSuiteTimes = [0.0]; private int $testSuiteLevel = 0; private ?DOMElement $currentTestCase = null; private ?HRTime $time = null; private bool $prepared = false; + private bool $preparationFailed = false; + private ?string $unexpectedOutput = null; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(Printer $printer, Facade $facade) { $this->printer = $printer; @@ -98,8 +98,13 @@ public function __construct(Printer $printer, Facade $facade) public function flush(): void { - $this->printer->print($this->document->saveXML()); + $xml = $this->document->saveXML(); + if ($xml === false) { + $xml = ''; + } + + $this->printer->print($xml); $this->printer->flush(); } @@ -125,7 +130,7 @@ public function testSuiteStarted(Started $event): void $this->testSuiteErrors[$this->testSuiteLevel] = 0; $this->testSuiteFailures[$this->testSuiteLevel] = 0; $this->testSuiteSkipped[$this->testSuiteLevel] = 0; - $this->testSuiteTimes[$this->testSuiteLevel] = 0; + $this->testSuiteTimes[$this->testSuiteLevel] = 0.0; } public function testSuiteFinished(): void @@ -174,25 +179,48 @@ public function testSuiteFinished(): void /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ - public function testPrepared(Prepared $event): void + public function testPreparationStarted(PreparationStarted $event): void { $this->createTestCase($event); + + $this->preparationFailed = false; + } + + public function testPreparationErrored(): void + { + $this->preparationFailed = true; + } + + public function testPreparationFailed(): void + { + $this->preparationFailed = true; + } + + public function testPrepared(): void + { $this->prepared = true; } + public function testPrintedUnexpectedOutput(PrintedUnexpectedOutput $event): void + { + $this->unexpectedOutput = $event->output(); + } + /** * @throws InvalidArgumentException */ public function testFinished(Finished $event): void { + if (!$this->prepared || $this->preparationFailed) { + return; + } + $this->handleFinish($event->telemetryInfo(), $event->numberOfAssertionsPerformed()); } /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function testMarkedIncomplete(MarkedIncomplete $event): void { @@ -201,7 +229,6 @@ public function testMarkedIncomplete(MarkedIncomplete $event): void /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function testSkipped(Skipped $event): void { @@ -210,7 +237,6 @@ public function testSkipped(Skipped $event): void /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function testErrored(Errored $event): void { @@ -221,7 +247,6 @@ public function testErrored(Errored $event): void /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function testFailed(Failed $event): void { @@ -252,6 +277,15 @@ private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerfor sprintf('%F', $time), ); + if ($this->unexpectedOutput !== null) { + $systemOut = $this->document->createElement( + 'system-out', + Xml::prepareString($this->unexpectedOutput), + ); + + $this->currentTestCase->appendChild($systemOut); + } + $this->testSuites[$this->testSuiteLevel]->appendChild( $this->currentTestCase, ); @@ -259,21 +293,23 @@ private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerfor $this->testSuiteTests[$this->testSuiteLevel]++; $this->testSuiteTimes[$this->testSuiteLevel] += $time; - $this->currentTestCase = null; - $this->time = null; - $this->prepared = false; + $this->currentTestCase = null; + $this->time = null; + $this->preparationFailed = false; + $this->prepared = false; + $this->unexpectedOutput = null; } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function registerSubscribers(Facade $facade): void { $facade->registerSubscribers( new TestSuiteStartedSubscriber($this), new TestSuiteFinishedSubscriber($this), + new TestPreparationStartedSubscriber($this), + new TestPreparationErroredSubscriber($this), + new TestPreparationFailedSubscriber($this), new TestPreparedSubscriber($this), + new TestPrintedUnexpectedOutputSubscriber($this), new TestFinishedSubscriber($this), new TestErroredSubscriber($this), new TestFailedSubscriber($this), @@ -294,7 +330,6 @@ private function createDocument(): void /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ private function handleFault(Errored|Failed $event, string $type): void { @@ -328,7 +363,6 @@ private function handleFault(Errored|Failed $event, string $type): void /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ private function handleIncompleteOrSkipped(MarkedIncomplete|Skipped $event): void { @@ -351,7 +385,6 @@ private function handleIncompleteOrSkipped(MarkedIncomplete|Skipped $event): voi /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ private function testAsString(Test $test): string { @@ -371,7 +404,6 @@ private function testAsString(Test $test): string /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ private function name(Test $test): string { @@ -404,11 +436,10 @@ private function name(Test $test): string /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException * - * @psalm-assert !null $this->currentTestCase + * @phpstan-assert !null $this->currentTestCase */ - private function createTestCase(Errored|Failed|MarkedIncomplete|Prepared|Skipped $event): void + private function createTestCase(Errored|Failed|MarkedIncomplete|PreparationStarted|Prepared|Skipped $event): void { $testCase = $this->document->createElement('testcase'); diff --git a/src/Logging/JUnit/Subscriber/Subscriber.php b/src/Logging/JUnit/Subscriber/Subscriber.php index c3a7c624e5e..b81f30da409 100644 --- a/src/Logging/JUnit/Subscriber/Subscriber.php +++ b/src/Logging/JUnit/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\Logging\JUnit; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly JunitXmlLogger $logger; + private JunitXmlLogger $logger; public function __construct(JunitXmlLogger $logger) { diff --git a/src/Logging/JUnit/Subscriber/TestErroredSubscriber.php b/src/Logging/JUnit/Subscriber/TestErroredSubscriber.php index a7127525753..114b1c84d24 100644 --- a/src/Logging/JUnit/Subscriber/TestErroredSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestErroredSubscriber.php @@ -12,16 +12,16 @@ use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Test\Errored; use PHPUnit\Event\Test\ErroredSubscriber; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber +final readonly class TestErroredSubscriber extends Subscriber implements ErroredSubscriber { /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function notify(Errored $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php b/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php index 6876fa654e3..e8050784329 100644 --- a/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php @@ -12,16 +12,16 @@ use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Test\Failed; use PHPUnit\Event\Test\FailedSubscriber; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFailedSubscriber extends Subscriber implements FailedSubscriber +final readonly class TestFailedSubscriber extends Subscriber implements FailedSubscriber { /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function notify(Failed $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php b/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php index 7539070756e..55aed8c6614 100644 --- a/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php @@ -12,16 +12,16 @@ use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Test\Finished; use PHPUnit\Event\Test\FinishedSubscriber; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function notify(Finished $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php b/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php index 9517b3e7b17..8732af9dd4b 100644 --- a/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php @@ -12,16 +12,16 @@ use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Test\MarkedIncomplete; use PHPUnit\Event\Test\MarkedIncompleteSubscriber; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber +final readonly class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber { /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function notify(MarkedIncomplete $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestPreparationErroredSubscriber.php b/src/Logging/JUnit/Subscriber/TestPreparationErroredSubscriber.php new file mode 100644 index 00000000000..c6fb388f885 --- /dev/null +++ b/src/Logging/JUnit/Subscriber/TestPreparationErroredSubscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\PreparationErrored; +use PHPUnit\Event\Test\PreparationErroredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestPreparationErroredSubscriber extends Subscriber implements PreparationErroredSubscriber +{ + /** + * @throws InvalidArgumentException + */ + public function notify(PreparationErrored $event): void + { + $this->logger()->testPreparationErrored(); + } +} diff --git a/src/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php b/src/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php new file mode 100644 index 00000000000..456466a189f --- /dev/null +++ b/src/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\PreparationFailed; +use PHPUnit\Event\Test\PreparationFailedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestPreparationFailedSubscriber extends Subscriber implements PreparationFailedSubscriber +{ + /** + * @throws InvalidArgumentException + */ + public function notify(PreparationFailed $event): void + { + $this->logger()->testPreparationFailed(); + } +} diff --git a/src/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php b/src/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php new file mode 100644 index 00000000000..8d24d65546a --- /dev/null +++ b/src/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\PreparationStarted; +use PHPUnit\Event\Test\PreparationStartedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestPreparationStartedSubscriber extends Subscriber implements PreparationStartedSubscriber +{ + /** + * @throws InvalidArgumentException + */ + public function notify(PreparationStarted $event): void + { + $this->logger()->testPreparationStarted($event); + } +} diff --git a/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php b/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php index 98027dc9c4d..2a80b8af55d 100644 --- a/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php @@ -12,19 +12,19 @@ use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Test\Prepared; use PHPUnit\Event\Test\PreparedSubscriber; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +final readonly class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber { /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function notify(Prepared $event): void { - $this->logger()->testPrepared($event); + $this->logger()->testPrepared(); } } diff --git a/src/Logging/JUnit/Subscriber/TestPrintedUnexpectedOutputSubscriber.php b/src/Logging/JUnit/Subscriber/TestPrintedUnexpectedOutputSubscriber.php new file mode 100644 index 00000000000..186bf1502e4 --- /dev/null +++ b/src/Logging/JUnit/Subscriber/TestPrintedUnexpectedOutputSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\JUnit; + +use PHPUnit\Event\Test\PrintedUnexpectedOutput; +use PHPUnit\Event\Test\PrintedUnexpectedOutputSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestPrintedUnexpectedOutputSubscriber extends Subscriber implements PrintedUnexpectedOutputSubscriber +{ + public function notify(PrintedUnexpectedOutput $event): void + { + $this->logger()->testPrintedUnexpectedOutput($event); + } +} diff --git a/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php b/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php index 148ff2371df..00617621aba 100644 --- a/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestRunnerExecutionFinishedSubscriber extends Subscriber implements ExecutionFinishedSubscriber +final readonly class TestRunnerExecutionFinishedSubscriber extends Subscriber implements ExecutionFinishedSubscriber { public function notify(ExecutionFinished $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php b/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php index 90df5c048b4..c6ee84ac69e 100644 --- a/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php @@ -12,16 +12,16 @@ use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Test\Skipped; use PHPUnit\Event\Test\SkippedSubscriber; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber +final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber { /** * @throws InvalidArgumentException - * @throws NoDataSetFromDataProviderException */ public function notify(Skipped $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php b/src/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php index 26e1981f872..47691770731 100644 --- a/src/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber { public function notify(Finished $event): void { diff --git a/src/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php b/src/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php index dfec98b4e2d..30e350d3419 100644 --- a/src/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php +++ b/src/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\StartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber +final readonly class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber { public function notify(Started $event): void { diff --git a/src/Logging/TeamCity/Subscriber/Subscriber.php b/src/Logging/TeamCity/Subscriber/Subscriber.php index a24289620ba..b1ad46d833d 100644 --- a/src/Logging/TeamCity/Subscriber/Subscriber.php +++ b/src/Logging/TeamCity/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\Logging\TeamCity; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly TeamCityLogger $logger; + private TeamCityLogger $logger; public function __construct(TeamCityLogger $logger) { diff --git a/src/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php b/src/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php index 431d1b2894b..9482ccb22d1 100644 --- a/src/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestConsideredRiskySubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\ConsideredRiskySubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber +final readonly class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TeamCity/Subscriber/TestErroredSubscriber.php b/src/Logging/TeamCity/Subscriber/TestErroredSubscriber.php index 318534a426e..4ce8d0cb774 100644 --- a/src/Logging/TeamCity/Subscriber/TestErroredSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestErroredSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\ErroredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber +final readonly class TestErroredSubscriber extends Subscriber implements ErroredSubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TeamCity/Subscriber/TestFailedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestFailedSubscriber.php index 1ada154a3f2..8d8caa6308e 100644 --- a/src/Logging/TeamCity/Subscriber/TestFailedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestFailedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\FailedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFailedSubscriber extends Subscriber implements FailedSubscriber +final readonly class TestFailedSubscriber extends Subscriber implements FailedSubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TeamCity/Subscriber/TestFinishedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestFinishedSubscriber.php index 22ba518f5b4..6b4bef3dcc1 100644 --- a/src/Logging/TeamCity/Subscriber/TestFinishedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestFinishedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.php b/src/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.php index c3505f16a16..b38d54c44a2 100644 --- a/src/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestMarkedIncompleteSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\MarkedIncompleteSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber +final readonly class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TeamCity/Subscriber/TestPreparedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestPreparedSubscriber.php index c1bb537ee82..85476a059d4 100644 --- a/src/Logging/TeamCity/Subscriber/TestPreparedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestPreparedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PreparedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +final readonly class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber { public function notify(Prepared $event): void { diff --git a/src/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.php index ccec88d9891..824ea424438 100644 --- a/src/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestRunnerExecutionFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestRunnerExecutionFinishedSubscriber extends Subscriber implements ExecutionFinishedSubscriber +final readonly class TestRunnerExecutionFinishedSubscriber extends Subscriber implements ExecutionFinishedSubscriber { public function notify(ExecutionFinished $event): void { diff --git a/src/Logging/TeamCity/Subscriber/TestSkippedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestSkippedSubscriber.php index e6ef6ae93e1..0f55795fc5a 100644 --- a/src/Logging/TeamCity/Subscriber/TestSkippedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestSkippedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\SkippedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber +final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TeamCity/Subscriber/TestSuiteBeforeFirstTestMethodErroredSubscriber.php b/src/Logging/TeamCity/Subscriber/TestSuiteBeforeFirstTestMethodErroredSubscriber.php new file mode 100644 index 00000000000..25459197b96 --- /dev/null +++ b/src/Logging/TeamCity/Subscriber/TestSuiteBeforeFirstTestMethodErroredSubscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TeamCity; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\BeforeFirstTestMethodErroredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestSuiteBeforeFirstTestMethodErroredSubscriber extends Subscriber implements BeforeFirstTestMethodErroredSubscriber +{ + /** + * @throws InvalidArgumentException + */ + public function notify(BeforeFirstTestMethodErrored $event): void + { + $this->logger()->beforeFirstTestMethodErrored($event); + } +} diff --git a/src/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.php index d8e73e432ba..71889d8ce65 100644 --- a/src/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestSuiteFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber { public function notify(Finished $event): void { diff --git a/src/Logging/TeamCity/Subscriber/TestSuiteSkippedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestSuiteSkippedSubscriber.php new file mode 100644 index 00000000000..2d6a3f3d248 --- /dev/null +++ b/src/Logging/TeamCity/Subscriber/TestSuiteSkippedSubscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TeamCity; + +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\TestSuite\Skipped; +use PHPUnit\Event\TestSuite\SkippedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestSuiteSkippedSubscriber extends Subscriber implements SkippedSubscriber +{ + /** + * @throws InvalidArgumentException + */ + public function notify(Skipped $event): void + { + $this->logger()->testSuiteSkipped($event); + } +} diff --git a/src/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.php b/src/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.php index 2eb764784f9..7caba60b6a5 100644 --- a/src/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.php +++ b/src/Logging/TeamCity/Subscriber/TestSuiteStartedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\StartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber +final readonly class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber { public function notify(Started $event): void { diff --git a/src/Logging/TeamCity/TeamCityLogger.php b/src/Logging/TeamCity/TeamCityLogger.php index 203e2337046..9a56fc3832d 100644 --- a/src/Logging/TeamCity/TeamCityLogger.php +++ b/src/Logging/TeamCity/TeamCityLogger.php @@ -20,10 +20,10 @@ use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\Throwable; use PHPUnit\Event\Event; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\Telemetry\HRTime; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; use PHPUnit\Event\Test\ConsideredRisky; use PHPUnit\Event\Test\Errored; use PHPUnit\Event\Test\Failed; @@ -32,14 +32,16 @@ use PHPUnit\Event\Test\Prepared; use PHPUnit\Event\Test\Skipped; use PHPUnit\Event\TestSuite\Finished as TestSuiteFinished; +use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; use PHPUnit\Event\TestSuite\Started as TestSuiteStarted; use PHPUnit\Event\TestSuite\TestSuiteForTestClass; use PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\Framework\Exception as FrameworkException; use PHPUnit\TextUI\Output\Printer; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TeamCityLogger @@ -47,12 +49,8 @@ final class TeamCityLogger private readonly Printer $printer; private bool $isSummaryTestCountPrinted = false; private ?HRTime $time = null; - private ?int $flowId; + private ?int $flowId = null; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(Printer $printer, Facade $facade) { $this->printer = $printer; @@ -129,7 +127,7 @@ public function testPrepared(Prepared $event): void 'php_qn://%s::\\%s::%s', $test->file(), $test->className(), - $test->methodName(), + $test->name(), ); } @@ -144,7 +142,9 @@ public function testPrepared(Prepared $event): void public function testMarkedIncomplete(MarkedIncomplete $event): void { if ($this->time === null) { + // @codeCoverageIgnoreStart $this->time = $event->telemetryInfo()->time(); + // @codeCoverageIgnoreEnd } $this->writeMessage( @@ -177,6 +177,46 @@ public function testSkipped(Skipped $event): void $this->writeMessage('testIgnored', $parameters); } + /** + * @throws InvalidArgumentException + */ + public function testSuiteSkipped(TestSuiteSkipped $event): void + { + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); + } + + $parameters = [ + 'name' => $event->testSuite()->name(), + 'message' => $event->message(), + ]; + + $parameters['duration'] = $this->duration($event); + + $this->writeMessage('testIgnored', $parameters); + $this->writeMessage('testSuiteFinished', $parameters); + } + + /** + * @throws InvalidArgumentException + */ + public function beforeFirstTestMethodErrored(BeforeFirstTestMethodErrored $event): void + { + if ($this->time === null) { + $this->time = $event->telemetryInfo()->time(); + } + + $parameters = [ + 'name' => $event->testClassName(), + 'message' => $this->message($event->throwable()), + 'details' => $this->details($event->throwable()), + 'duration' => $this->duration($event), + ]; + + $this->writeMessage('testFailed', $parameters); + $this->writeMessage('testSuiteFinished', $parameters); + } + /** * @throws InvalidArgumentException */ @@ -203,7 +243,9 @@ public function testErrored(Errored $event): void public function testFailed(Failed $event): void { if ($this->time === null) { + // @codeCoverageIgnoreStart $this->time = $event->telemetryInfo()->time(); + // @codeCoverageIgnoreEnd } $parameters = [ @@ -228,7 +270,9 @@ public function testFailed(Failed $event): void public function testConsideredRisky(ConsideredRisky $event): void { if ($this->time === null) { + // @codeCoverageIgnoreStart $this->time = $event->telemetryInfo()->time(); + // @codeCoverageIgnoreEnd } $this->writeMessage( @@ -263,10 +307,6 @@ public function flush(): void $this->printer->flush(); } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function registerSubscribers(Facade $facade): void { $facade->registerSubscribers( @@ -278,8 +318,10 @@ private function registerSubscribers(Facade $facade): void new TestFailedSubscriber($this), new TestMarkedIncompleteSubscriber($this), new TestSkippedSubscriber($this), + new TestSuiteSkippedSubscriber($this), new TestConsideredRiskySubscriber($this), new TestRunnerExecutionFinishedSubscriber($this), + new TestSuiteBeforeFirstTestMethodErroredSubscriber($this), ); } @@ -290,11 +332,14 @@ private function setFlowId(): void } } + /** + * @param array $parameters + */ private function writeMessage(string $eventName, array $parameters = []): void { $this->printer->print( sprintf( - "\n##teamcity[%s", + '##teamcity[%s', $eventName, ), ); @@ -322,7 +367,9 @@ private function writeMessage(string $eventName, array $parameters = []): void private function duration(Event $event): int { if ($this->time === null) { + // @codeCoverageIgnoreStart return 0; + // @codeCoverageIgnoreEnd } return (int) round($event->telemetryInfo()->time()->duration($this->time)->asFloat() * 1000); @@ -345,7 +392,7 @@ private function message(Throwable $throwable): string $buffer = $throwable->className(); - if (!empty($throwable->message())) { + if ($throwable->message() !== '') { $buffer .= ': ' . $throwable->message(); } diff --git a/src/Logging/TestDox/HtmlRenderer.php b/src/Logging/TestDox/HtmlRenderer.php index fd65a30916e..3d74d593616 100644 --- a/src/Logging/TestDox/HtmlRenderer.php +++ b/src/Logging/TestDox/HtmlRenderer.php @@ -12,14 +12,13 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class HtmlRenderer +final readonly class HtmlRenderer { - /** - * @var string - */ - private const PAGE_HEADER = <<<'EOT' + private const string PAGE_HEADER = <<<'EOT' @@ -74,35 +73,23 @@ final class HtmlRenderer EOT; - - /** - * @var string - */ - private const CLASS_HEADER = <<<'EOT' + private const string CLASS_HEADER = <<<'EOT'

%s

    EOT; - - /** - * @var string - */ - private const CLASS_FOOTER = <<<'EOT' + private const string CLASS_FOOTER = <<<'EOT'
EOT; - - /** - * @var string - */ - private const PAGE_FOOTER = <<<'EOT' + private const string PAGE_FOOTER = <<<'EOT' EOT; /** - * @psalm-param array $tests + * @param array $tests */ public function render(array $tests): string { @@ -129,7 +116,7 @@ public function render(array $tests): string } /** - * @psalm-return array + * @return array */ private function reduce(TestResultCollection $tests): array { diff --git a/src/Logging/TestDox/NamePrettifier.php b/src/Logging/TestDox/NamePrettifier.php index 4d0485ff42e..3c797f5e1d5 100644 --- a/src/Logging/TestDox/NamePrettifier.php +++ b/src/Logging/TestDox/NamePrettifier.php @@ -19,18 +19,15 @@ use function explode; use function gettype; use function implode; -use function in_array; use function is_bool; use function is_float; use function is_int; -use function is_numeric; use function is_object; use function is_scalar; use function method_exists; -use function ord; use function preg_quote; use function preg_replace; -use function range; +use function rtrim; use function sprintf; use function str_contains; use function str_ends_with; @@ -45,23 +42,25 @@ use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; use PHPUnit\Metadata\TestDox; use PHPUnit\Util\Color; +use PHPUnit\Util\Exporter; use ReflectionEnum; use ReflectionMethod; use ReflectionObject; -use SebastianBergmann\Exporter\Exporter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NamePrettifier { /** - * @psalm-var list + * @var array */ private static array $strings = []; /** - * @psalm-param class-string $className + * @param class-string $className */ public function prettifyTestClassName(string $className): string { @@ -90,11 +89,11 @@ public function prettifyTestClassName(string $className): string $className = substr($className, strlen('Test')); } - if (empty($className)) { + if ($className === '') { $className = 'UnnamedTests'; } - if (!empty($parts)) { + if ($parts !== []) { $parts[] = $className; $fullyQualifiedName = implode('\\', $parts); } else { @@ -110,20 +109,19 @@ public function prettifyTestClassName(string $className): string return $result; } + // NOTE: this method is on a hot path and very performance sensitive. change with care. public function prettifyTestMethodName(string $name): string { - $buffer = ''; - if ($name === '') { - return $buffer; + return ''; } - $string = (string) preg_replace('#\d+$#', '', $name, -1, $count); + $string = rtrim($name, '0123456789'); - if (in_array($string, self::$strings, true)) { + if (array_key_exists($string, self::$strings)) { $name = $string; - } elseif ($count === 0) { - self::$strings[] = $string; + } elseif ($string === $name) { + self::$strings[$string] = 1; } if (str_starts_with($name, 'test_')) { @@ -133,22 +131,28 @@ public function prettifyTestMethodName(string $name): string } if ($name === '') { - return $buffer; + return ''; } $name[0] = strtoupper($name[0]); - if (str_contains($name, '_')) { - return trim(str_replace('_', ' ', $name)); + $noUnderscore = str_replace('_', ' ', $name); + + if ($noUnderscore !== $name) { + return trim($noUnderscore); } $wasNumeric = false; - foreach (range(0, strlen($name) - 1) as $i) { - if ($i > 0 && ord($name[$i]) >= 65 && ord($name[$i]) <= 90) { + $buffer = ''; + + $len = strlen($name); + + for ($i = 0; $i < $len; $i++) { + if ($i > 0 && $name[$i] >= 'A' && $name[$i] <= 'Z') { $buffer .= ' ' . strtolower($name[$i]); } else { - $isNumeric = is_numeric($name[$i]); + $isNumeric = $name[$i] >= '0' && $name[$i] <= '9'; if (!$wasNumeric && $isNumeric) { $buffer .= ' '; @@ -190,7 +194,7 @@ public function prettifyTestCase(TestCase $test, bool $colorize): string array_keys($providedData), ); - $result = trim(preg_replace($variables, $providedData, $annotation)); + $result = preg_replace($variables, $providedData, $annotation); $annotationWithPlaceholders = true; } @@ -218,6 +222,9 @@ public function prettifyDataSet(TestCase $test, bool $colorize): string return Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace($test->dataName())); } + /** + * @return array + */ private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test, bool $colorize): array { assert(method_exists($test, $test->name())); @@ -239,21 +246,7 @@ private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test, $value = $providedDataValues[$i++] ?? null; if (is_object($value)) { - $reflector = new ReflectionObject($value); - - if ($reflector->isEnum()) { - $enumReflector = new ReflectionEnum($value); - - if ($enumReflector->isBacked()) { - $value = $value->value; - } else { - $value = $value->name; - } - } elseif ($reflector->hasMethod('__toString')) { - $value = (string) $value; - } else { - $value = $value::class; - } + $value = $this->objectToString($value); } if (!is_scalar($value)) { @@ -265,7 +258,7 @@ private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test, } if (is_bool($value) || is_int($value) || is_float($value)) { - $value = (new Exporter)->export($value); + $value = Exporter::export($value); } if ($value === '') { @@ -276,16 +269,40 @@ private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test, } } - $providedData['$' . $parameter->getName()] = $value; + $providedData['$' . $parameter->getName()] = str_replace('$', '\\$', $value); } if ($colorize) { $providedData = array_map( - static fn ($value) => Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, true)), + static fn (mixed $value) => Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, true)), $providedData, ); } return $providedData; } + + /** + * @return non-empty-string + */ + private function objectToString(object $value): string + { + $reflector = new ReflectionObject($value); + + if ($reflector->isEnum()) { + $enumReflector = new ReflectionEnum($value); + + if ($enumReflector->isBacked()) { + return (string) $value->value; + } + + return $value->name; + } + + if ($reflector->hasMethod('__toString')) { + return $value->__toString(); + } + + return $value::class; + } } diff --git a/src/Logging/TestDox/PlainTextRenderer.php b/src/Logging/TestDox/PlainTextRenderer.php index 45d0c81df0d..db591ca9fe1 100644 --- a/src/Logging/TestDox/PlainTextRenderer.php +++ b/src/Logging/TestDox/PlainTextRenderer.php @@ -12,12 +12,14 @@ use function sprintf; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class PlainTextRenderer +final readonly class PlainTextRenderer { /** - * @psalm-param array $tests + * @param array $tests */ public function render(array $tests): string { @@ -41,7 +43,7 @@ public function render(array $tests): string } /** - * @psalm-return array + * @return array */ private function reduce(TestResultCollection $tests): array { @@ -50,13 +52,22 @@ private function reduce(TestResultCollection $tests): array foreach ($tests as $test) { $prettifiedMethodName = $test->test()->testDox()->prettifiedMethodName(); + $success = true; + + if ($test->status()->isError() || + $test->status()->isFailure() || + $test->status()->isIncomplete() || + $test->status()->isSkipped()) { + $success = false; + } + if (!isset($result[$prettifiedMethodName])) { - $result[$prettifiedMethodName] = $test->status()->isSuccess() ? 'x' : ' '; + $result[$prettifiedMethodName] = $success ? 'x' : ' '; continue; } - if ($test->status()->isSuccess()) { + if ($success) { continue; } diff --git a/src/Logging/TestDox/TestMethod/Subscriber/Subscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/Subscriber.php deleted file mode 100644 index d2779f19e60..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/Subscriber.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class Subscriber -{ - private readonly TestResultCollector $collector; - - public function __construct(TestResultCollector $collector) - { - $this->collector = $collector; - } - - protected function collector(): TestResultCollector - { - return $this->collector; - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestConsideredRiskySubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestConsideredRiskySubscriber.php deleted file mode 100644 index 5bfccedad76..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestConsideredRiskySubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\ConsideredRisky; -use PHPUnit\Event\Test\ConsideredRiskySubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber -{ - public function notify(ConsideredRisky $event): void - { - $this->collector()->testConsideredRisky($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForAbstractClassSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForAbstractClassSubscriber.php deleted file mode 100644 index 0445a43c1a6..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForAbstractClassSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\MockObjectForAbstractClassCreated; -use PHPUnit\Event\Test\MockObjectForAbstractClassCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedMockObjectForAbstractClassSubscriber extends Subscriber implements MockObjectForAbstractClassCreatedSubscriber -{ - public function notify(MockObjectForAbstractClassCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForTraitSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForTraitSubscriber.php deleted file mode 100644 index 86bb377c9c1..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForTraitSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\MockObjectForTraitCreated; -use PHPUnit\Event\Test\MockObjectForTraitCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedMockObjectForTraitSubscriber extends Subscriber implements MockObjectForTraitCreatedSubscriber -{ - public function notify(MockObjectForTraitCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectFromWsdlSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectFromWsdlSubscriber.php deleted file mode 100644 index e0994bf2864..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectFromWsdlSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\MockObjectFromWsdlCreated; -use PHPUnit\Event\Test\MockObjectFromWsdlCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedMockObjectFromWsdlSubscriber extends Subscriber implements MockObjectFromWsdlCreatedSubscriber -{ - public function notify(MockObjectFromWsdlCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectSubscriber.php deleted file mode 100644 index f95327b68cf..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\MockObjectCreated; -use PHPUnit\Event\Test\MockObjectCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedMockObjectSubscriber extends Subscriber implements MockObjectCreatedSubscriber -{ - public function notify(MockObjectCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedPartialMockObjectSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedPartialMockObjectSubscriber.php deleted file mode 100644 index eb14c24baad..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedPartialMockObjectSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\PartialMockObjectCreated; -use PHPUnit\Event\Test\PartialMockObjectCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedPartialMockObjectSubscriber extends Subscriber implements PartialMockObjectCreatedSubscriber -{ - public function notify(PartialMockObjectCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestProxySubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestProxySubscriber.php deleted file mode 100644 index acf05fd78ad..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestProxySubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\TestProxyCreated; -use PHPUnit\Event\Test\TestProxyCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedTestProxySubscriber extends Subscriber implements TestProxyCreatedSubscriber -{ - public function notify(TestProxyCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestStubSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestStubSubscriber.php deleted file mode 100644 index ab5a48a5571..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestStubSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\TestStubCreated; -use PHPUnit\Event\Test\TestStubCreatedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestCreatedTestStubSubscriber extends Subscriber implements TestStubCreatedSubscriber -{ - public function notify(TestStubCreated $event): void - { - $this->collector()->testCreatedTestDouble($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestErroredSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestErroredSubscriber.php deleted file mode 100644 index 3a24981d6ab..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestErroredSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\Errored; -use PHPUnit\Event\Test\ErroredSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber -{ - public function notify(Errored $event): void - { - $this->collector()->testErrored($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestFailedSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestFailedSubscriber.php deleted file mode 100644 index a5647f950cf..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestFailedSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\Failed; -use PHPUnit\Event\Test\FailedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestFailedSubscriber extends Subscriber implements FailedSubscriber -{ - public function notify(Failed $event): void - { - $this->collector()->testFailed($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestMarkedIncompleteSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestMarkedIncompleteSubscriber.php deleted file mode 100644 index 1846712e9e9..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestMarkedIncompleteSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\MarkedIncomplete; -use PHPUnit\Event\Test\MarkedIncompleteSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber -{ - public function notify(MarkedIncomplete $event): void - { - $this->collector()->testMarkedIncomplete($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestPassedSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestPassedSubscriber.php deleted file mode 100644 index df39fca584f..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestPassedSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\Passed; -use PHPUnit\Event\Test\PassedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestPassedSubscriber extends Subscriber implements PassedSubscriber -{ - public function notify(Passed $event): void - { - $this->collector()->testPassed($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestPreparedSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestPreparedSubscriber.php deleted file mode 100644 index c21e8c17dae..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestPreparedSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\Prepared; -use PHPUnit\Event\Test\PreparedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber -{ - public function notify(Prepared $event): void - { - $this->collector()->testPrepared($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestSkippedSubscriber.php b/src/Logging/TestDox/TestMethod/Subscriber/TestSkippedSubscriber.php deleted file mode 100644 index 5f291ab32ee..00000000000 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestSkippedSubscriber.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Test\Skipped; -use PHPUnit\Event\Test\SkippedSubscriber; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber -{ - public function notify(Skipped $event): void - { - $this->collector()->testSkipped($event); - } -} diff --git a/src/Logging/TestDox/TestMethod/TestResult.php b/src/Logging/TestDox/TestMethod/TestResult.php deleted file mode 100644 index 4bde8ee1da6..00000000000 --- a/src/Logging/TestDox/TestMethod/TestResult.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use PHPUnit\Event\Code\TestMethod; -use PHPUnit\Event\Code\Throwable; -use PHPUnit\Event\Telemetry\Duration; -use PHPUnit\Framework\TestStatus\TestStatus; - -/** - * @psalm-immutable - * - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final readonly class TestResult -{ - private TestMethod $test; - private Duration $duration; - private TestStatus $status; - private ?Throwable $throwable; - - /** - * @psalm-var list - */ - private array $testDoubles; - - /** - * @psalm-param list $testDoubles - */ - public function __construct(TestMethod $test, Duration $duration, TestStatus $status, ?Throwable $throwable, array $testDoubles) - { - $this->test = $test; - $this->duration = $duration; - $this->status = $status; - $this->throwable = $throwable; - $this->testDoubles = $testDoubles; - } - - public function test(): TestMethod - { - return $this->test; - } - - public function duration(): Duration - { - return $this->duration; - } - - public function status(): TestStatus - { - return $this->status; - } - - /** - * @psalm-assert-if-true !null $this->throwable - */ - public function hasThrowable(): bool - { - return $this->throwable !== null; - } - - public function throwable(): ?Throwable - { - return $this->throwable; - } - - /** - * @psalm-return list - */ - public function testDoubles(): array - { - return $this->testDoubles; - } -} diff --git a/src/Logging/TestDox/TestMethod/TestResultCollector.php b/src/Logging/TestDox/TestMethod/TestResultCollector.php deleted file mode 100644 index cba5ef21509..00000000000 --- a/src/Logging/TestDox/TestMethod/TestResultCollector.php +++ /dev/null @@ -1,266 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Logging\TestDox; - -use function array_keys; -use function array_merge; -use function assert; -use function is_subclass_of; -use function ksort; -use function uksort; -use function usort; -use PHPUnit\Event\Code\TestMethod; -use PHPUnit\Event\Code\Throwable; -use PHPUnit\Event\EventFacadeIsSealedException; -use PHPUnit\Event\Facade; -use PHPUnit\Event\InvalidArgumentException; -use PHPUnit\Event\Telemetry\HRTime; -use PHPUnit\Event\Test\ConsideredRisky; -use PHPUnit\Event\Test\Errored; -use PHPUnit\Event\Test\Failed; -use PHPUnit\Event\Test\Finished; -use PHPUnit\Event\Test\MarkedIncomplete; -use PHPUnit\Event\Test\MockObjectCreated; -use PHPUnit\Event\Test\MockObjectForAbstractClassCreated; -use PHPUnit\Event\Test\MockObjectForTraitCreated; -use PHPUnit\Event\Test\MockObjectFromWsdlCreated; -use PHPUnit\Event\Test\PartialMockObjectCreated; -use PHPUnit\Event\Test\Passed; -use PHPUnit\Event\Test\Prepared; -use PHPUnit\Event\Test\Skipped; -use PHPUnit\Event\Test\TestProxyCreated; -use PHPUnit\Event\Test\TestStubCreated; -use PHPUnit\Event\UnknownSubscriberTypeException; -use PHPUnit\Framework\TestStatus\TestStatus; -use PHPUnit\Logging\TestDox\TestResult as TestDoxTestMethod; -use ReflectionMethod; -use SoapClient; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class TestResultCollector -{ - /** - * @psalm-var array> - */ - private array $tests = []; - private ?HRTime $time = null; - private ?TestStatus $status = null; - private ?Throwable $throwable = null; - - /** - * @psalm-var list - */ - private array $testDoubles = []; - - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ - public function __construct(Facade $facade) - { - $this->registerSubscribers($facade); - } - - /** - * @psalm-return array - */ - public function testMethodsGroupedByClass(): array - { - $result = []; - - foreach ($this->tests as $prettifiedClassName => $tests) { - $testsByDeclaringClass = []; - - foreach ($tests as $test) { - $declaringClassName = (new ReflectionMethod($test->test()->className(), $test->test()->methodName()))->getDeclaringClass()->getName(); - - if (!isset($testsByDeclaringClass[$declaringClassName])) { - $testsByDeclaringClass[$declaringClassName] = []; - } - - $testsByDeclaringClass[$declaringClassName][] = $test; - } - - foreach (array_keys($testsByDeclaringClass) as $declaringClassName) { - usort( - $testsByDeclaringClass[$declaringClassName], - static function (TestDoxTestMethod $a, TestDoxTestMethod $b): int - { - return $a->test()->line() <=> $b->test()->line(); - }, - ); - } - - uksort( - $testsByDeclaringClass, - /** - * @psalm-param class-string $a - * @psalm-param class-string $b - */ - static function (string $a, string $b): int - { - if (is_subclass_of($b, $a)) { - return -1; - } - - if (is_subclass_of($a, $b)) { - return 1; - } - - return 0; - }, - ); - - $tests = []; - - foreach ($testsByDeclaringClass as $_tests) { - $tests = array_merge($tests, $_tests); - } - - $result[$prettifiedClassName] = TestResultCollection::fromArray($tests); - } - - ksort($result); - - return $result; - } - - public function testPrepared(Prepared $event): void - { - if (!$event->test()->isTestMethod()) { - return; - } - - $this->time = $event->telemetryInfo()->time(); - $this->status = TestStatus::unknown(); - $this->throwable = null; - $this->testDoubles = []; - } - - public function testErrored(Errored $event): void - { - if (!$event->test()->isTestMethod()) { - return; - } - - $this->status = TestStatus::error($event->throwable()->message()); - $this->throwable = $event->throwable(); - } - - public function testFailed(Failed $event): void - { - if (!$event->test()->isTestMethod()) { - return; - } - - $this->status = TestStatus::failure($event->throwable()->message()); - $this->throwable = $event->throwable(); - } - - public function testPassed(Passed $event): void - { - if (!$event->test()->isTestMethod()) { - return; - } - - $this->status = TestStatus::success(); - } - - public function testSkipped(Skipped $event): void - { - $this->status = TestStatus::skipped($event->message()); - } - - public function testMarkedIncomplete(MarkedIncomplete $event): void - { - $this->status = TestStatus::incomplete($event->throwable()->message()); - $this->throwable = $event->throwable(); - } - - public function testConsideredRisky(ConsideredRisky $event): void - { - $this->status = TestStatus::risky($event->message()); - } - - public function testCreatedTestDouble(MockObjectCreated|MockObjectForAbstractClassCreated|MockObjectForTraitCreated|MockObjectFromWsdlCreated|PartialMockObjectCreated|TestProxyCreated|TestStubCreated $event): void - { - if ($event instanceof MockObjectForTraitCreated) { - $this->testDoubles[] = $event->traitName(); - - return; - } - - if ($event instanceof MockObjectFromWsdlCreated) { - $this->testDoubles[] = SoapClient::class; - - return; - } - - $this->testDoubles[] = $event->className(); - } - - /** - * @throws InvalidArgumentException - */ - public function testFinished(Finished $event): void - { - if (!$event->test()->isTestMethod()) { - return; - } - - $test = $event->test(); - - assert($test instanceof TestMethod); - - if (!isset($this->tests[$test->testDox()->prettifiedClassName()])) { - $this->tests[$test->testDox()->prettifiedClassName()] = []; - } - - $this->tests[$test->testDox()->prettifiedClassName()][] = new TestDoxTestMethod( - $test, - $event->telemetryInfo()->time()->duration($this->time), - $this->status, - $this->throwable, - $this->testDoubles, - ); - - $this->time = null; - $this->status = null; - $this->throwable = null; - $this->testDoubles = []; - } - - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ - private function registerSubscribers(Facade $facade): void - { - $facade->registerSubscribers( - new TestConsideredRiskySubscriber($this), - new TestCreatedMockObjectForAbstractClassSubscriber($this), - new TestCreatedMockObjectForTraitSubscriber($this), - new TestCreatedMockObjectFromWsdlSubscriber($this), - new TestCreatedMockObjectSubscriber($this), - new TestCreatedPartialMockObjectSubscriber($this), - new TestCreatedTestProxySubscriber($this), - new TestCreatedTestStubSubscriber($this), - new TestErroredSubscriber($this), - new TestFailedSubscriber($this), - new TestFinishedSubscriber($this), - new TestMarkedIncompleteSubscriber($this), - new TestPassedSubscriber($this), - new TestPreparedSubscriber($this), - new TestSkippedSubscriber($this), - ); - } -} diff --git a/src/Logging/TestDox/TestResult/Subscriber/Subscriber.php b/src/Logging/TestDox/TestResult/Subscriber/Subscriber.php new file mode 100644 index 00000000000..41fc465a120 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/Subscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract readonly class Subscriber +{ + private TestResultCollector $collector; + + public function __construct(TestResultCollector $collector) + { + $this->collector = $collector; + } + + protected function collector(): TestResultCollector + { + return $this->collector; + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php new file mode 100644 index 00000000000..150a486350b --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\ConsideredRiskySubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber +{ + public function notify(ConsideredRisky $event): void + { + $this->collector()->testConsideredRisky($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php new file mode 100644 index 00000000000..b210ffa3c32 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\ErroredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestErroredSubscriber extends Subscriber implements ErroredSubscriber +{ + public function notify(Errored $event): void + { + $this->collector()->testErrored($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php new file mode 100644 index 00000000000..b776227c38b --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\FailedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestFailedSubscriber extends Subscriber implements FailedSubscriber +{ + public function notify(Failed $event): void + { + $this->collector()->testFailed($event); + } +} diff --git a/src/Logging/TestDox/TestMethod/Subscriber/TestFinishedSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php similarity index 77% rename from src/Logging/TestDox/TestMethod/Subscriber/TestFinishedSubscriber.php rename to src/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php index c7dbdc15fb2..14ddea33ea9 100644 --- a/src/Logging/TestDox/TestMethod/Subscriber/TestFinishedSubscriber.php +++ b/src/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { /** * @throws InvalidArgumentException diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php new file mode 100644 index 00000000000..7e21545cb10 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\MarkedIncompleteSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber +{ + public function notify(MarkedIncomplete $event): void + { + $this->collector()->testMarkedIncomplete($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php new file mode 100644 index 00000000000..1eb1a5777cd --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Passed; +use PHPUnit\Event\Test\PassedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestPassedSubscriber extends Subscriber implements PassedSubscriber +{ + public function notify(Passed $event): void + { + $this->collector()->testPassed($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php new file mode 100644 index 00000000000..cdaddb09a13 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\PreparedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +{ + public function notify(Prepared $event): void + { + $this->collector()->testPrepared($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php new file mode 100644 index 00000000000..76d7e3bb094 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\Skipped; +use PHPUnit\Event\Test\SkippedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber +{ + public function notify(Skipped $event): void + { + $this->collector()->testSkipped($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php new file mode 100644 index 00000000000..8e080296632 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\DeprecationTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber +{ + public function notify(DeprecationTriggered $event): void + { + $this->collector()->testTriggeredDeprecation($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php new file mode 100644 index 00000000000..18eff3a9d05 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\NoticeTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber +{ + public function notify(NoticeTriggered $event): void + { + $this->collector()->testTriggeredNotice($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php new file mode 100644 index 00000000000..082bb3c334a --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber +{ + public function notify(PhpDeprecationTriggered $event): void + { + $this->collector()->testTriggeredPhpDeprecation($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php new file mode 100644 index 00000000000..b743b64a3f8 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber +{ + public function notify(PhpNoticeTriggered $event): void + { + $this->collector()->testTriggeredPhpNotice($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php new file mode 100644 index 00000000000..4e9c6ac1aeb --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber +{ + public function notify(PhpWarningTriggered $event): void + { + $this->collector()->testTriggeredPhpWarning($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php new file mode 100644 index 00000000000..4423ff98cfc --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpunitDeprecationSubscriber extends Subscriber implements PhpunitDeprecationTriggeredSubscriber +{ + public function notify(PhpunitDeprecationTriggered $event): void + { + $this->collector()->testTriggeredPhpunitDeprecation($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php new file mode 100644 index 00000000000..e4e90f18d91 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitErrorTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpunitErrorSubscriber extends Subscriber implements PhpunitErrorTriggeredSubscriber +{ + public function notify(PhpunitErrorTriggered $event): void + { + $this->collector()->testTriggeredPhpunitError($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php new file mode 100644 index 00000000000..72cb8af22c6 --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpunitWarningSubscriber extends Subscriber implements PhpunitWarningTriggeredSubscriber +{ + public function notify(PhpunitWarningTriggered $event): void + { + $this->collector()->testTriggeredPhpunitWarning($event); + } +} diff --git a/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredWarningSubscriber.php b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredWarningSubscriber.php new file mode 100644 index 00000000000..d44f4005c6a --- /dev/null +++ b/src/Logging/TestDox/TestResult/Subscriber/TestTriggeredWarningSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Event\Test\WarningTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber +{ + public function notify(WarningTriggered $event): void + { + $this->collector()->testTriggeredWarning($event); + } +} diff --git a/src/Logging/TestDox/TestResult/TestResult.php b/src/Logging/TestDox/TestResult/TestResult.php new file mode 100644 index 00000000000..2648a0dbcde --- /dev/null +++ b/src/Logging/TestDox/TestResult/TestResult.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Framework\TestStatus\TestStatus; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestResult +{ + private TestMethod $test; + private TestStatus $status; + private ?Throwable $throwable; + + public function __construct(TestMethod $test, TestStatus $status, ?Throwable $throwable) + { + $this->test = $test; + $this->status = $status; + $this->throwable = $throwable; + } + + public function test(): TestMethod + { + return $this->test; + } + + public function status(): TestStatus + { + return $this->status; + } + + /** + * @phpstan-assert-if-true !null $this->throwable + */ + public function hasThrowable(): bool + { + return $this->throwable !== null; + } + + public function throwable(): ?Throwable + { + return $this->throwable; + } +} diff --git a/src/Logging/TestDox/TestMethod/TestResultCollection.php b/src/Logging/TestDox/TestResult/TestResultCollection.php similarity index 82% rename from src/Logging/TestDox/TestMethod/TestResultCollection.php rename to src/Logging/TestDox/TestResult/TestResultCollection.php index aedb7d0fec0..5c4a4d64435 100644 --- a/src/Logging/TestDox/TestMethod/TestResultCollection.php +++ b/src/Logging/TestDox/TestResult/TestResultCollection.php @@ -14,19 +14,21 @@ /** * @template-implements IteratorAggregate * - * @psalm-immutable + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class TestResultCollection implements IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $testResults; /** - * @psalm-param list $testResults + * @param list $testResults */ public static function fromArray(array $testResults): self { @@ -39,7 +41,7 @@ private function __construct(TestResult ...$testResults) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/Logging/TestDox/TestMethod/TestResultCollectionIterator.php b/src/Logging/TestDox/TestResult/TestResultCollectionIterator.php similarity index 89% rename from src/Logging/TestDox/TestMethod/TestResultCollectionIterator.php rename to src/Logging/TestDox/TestResult/TestResultCollectionIterator.php index 7f07cb5da04..cf8ae7ac8b1 100644 --- a/src/Logging/TestDox/TestMethod/TestResultCollectionIterator.php +++ b/src/Logging/TestDox/TestResult/TestResultCollectionIterator.php @@ -15,12 +15,14 @@ /** * @template-implements Iterator * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestResultCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $testResults; private int $position = 0; diff --git a/src/Logging/TestDox/TestResult/TestResultCollector.php b/src/Logging/TestDox/TestResult/TestResultCollector.php new file mode 100644 index 00000000000..510fa095d4d --- /dev/null +++ b/src/Logging/TestDox/TestResult/TestResultCollector.php @@ -0,0 +1,379 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Logging\TestDox; + +use function array_merge; +use function assert; +use function is_subclass_of; +use function ksort; +use function uksort; +use function usort; +use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\Code\Throwable; +use PHPUnit\Event\Facade; +use PHPUnit\Event\InvalidArgumentException; +use PHPUnit\Event\Test\ConsideredRisky; +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\Errored; +use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\Finished; +use PHPUnit\Event\Test\MarkedIncomplete; +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\Passed; +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpunitDeprecationTriggered; +use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitWarningTriggered; +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\Skipped; +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\Framework\TestStatus\TestStatus; +use PHPUnit\Logging\TestDox\TestResult as TestDoxTestMethod; +use PHPUnit\TestRunner\IssueFilter; +use ReflectionMethod; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestResultCollector +{ + private readonly IssueFilter $issueFilter; + + /** + * @var array> + */ + private array $tests = []; + private ?TestStatus $status = null; + private ?Throwable $throwable = null; + private bool $prepared = false; + + public function __construct(Facade $facade, IssueFilter $issueFilter) + { + $this->issueFilter = $issueFilter; + + $this->registerSubscribers($facade); + } + + /** + * @return array + */ + public function testMethodsGroupedByClass(): array + { + $result = []; + + foreach ($this->tests as $prettifiedClassName => $tests) { + $testsByDeclaringClass = []; + + foreach ($tests as $test) { + $declaringClassName = (new ReflectionMethod($test->test()->className(), $test->test()->methodName()))->getDeclaringClass()->getName(); + + if (!isset($testsByDeclaringClass[$declaringClassName])) { + $testsByDeclaringClass[$declaringClassName] = []; + } + + $testsByDeclaringClass[$declaringClassName][] = $test; + } + + foreach ($testsByDeclaringClass as $declaringClassName) { + usort( + $declaringClassName, + static function (TestDoxTestMethod $a, TestDoxTestMethod $b): int + { + return $a->test()->line() <=> $b->test()->line(); + }, + ); + } + + uksort( + $testsByDeclaringClass, + /** + * @param class-string $a + * @param class-string $b + */ + static function (string $a, string $b): int + { + if (is_subclass_of($b, $a)) { + return -1; + } + + if (is_subclass_of($a, $b)) { + return 1; + } + + return 0; + }, + ); + + $tests = []; + + foreach ($testsByDeclaringClass as $_tests) { + $tests = array_merge($tests, $_tests); + } + + $result[$prettifiedClassName] = TestResultCollection::fromArray($tests); + } + + ksort($result); + + return $result; + } + + public function testPrepared(Prepared $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->status = TestStatus::unknown(); + $this->throwable = null; + $this->prepared = true; + } + + public function testErrored(Errored $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->status = TestStatus::error($event->throwable()->message()); + $this->throwable = $event->throwable(); + + if (!$this->prepared) { + $test = $event->test(); + + assert($test instanceof TestMethod); + + $this->process($test); + } + } + + public function testFailed(Failed $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->status = TestStatus::failure($event->throwable()->message()); + $this->throwable = $event->throwable(); + } + + public function testPassed(Passed $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::success()); + } + + public function testSkipped(Skipped $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::skipped($event->message())); + } + + public function testMarkedIncomplete(MarkedIncomplete $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::incomplete($event->throwable()->message())); + + $this->throwable = $event->throwable(); + } + + public function testConsideredRisky(ConsideredRisky $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::risky()); + } + + public function testTriggeredDeprecation(DeprecationTriggered $event): void + { + if (!$this->issueFilter->shouldBeProcessed($event, true)) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + $this->updateTestStatus(TestStatus::deprecation()); + } + + public function testTriggeredNotice(NoticeTriggered $event): void + { + if (!$this->issueFilter->shouldBeProcessed($event, true)) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + $this->updateTestStatus(TestStatus::notice()); + } + + public function testTriggeredWarning(WarningTriggered $event): void + { + if (!$this->issueFilter->shouldBeProcessed($event, true)) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + $this->updateTestStatus(TestStatus::warning()); + } + + public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): void + { + if (!$this->issueFilter->shouldBeProcessed($event, true)) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + $this->updateTestStatus(TestStatus::deprecation()); + } + + public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void + { + if (!$this->issueFilter->shouldBeProcessed($event, true)) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + $this->updateTestStatus(TestStatus::notice()); + } + + public function testTriggeredPhpWarning(PhpWarningTriggered $event): void + { + if (!$this->issueFilter->shouldBeProcessed($event, true)) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + $this->updateTestStatus(TestStatus::warning()); + } + + public function testTriggeredPhpunitDeprecation(PhpunitDeprecationTriggered $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::deprecation()); + } + + public function testTriggeredPhpunitError(PhpunitErrorTriggered $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::error()); + } + + public function testTriggeredPhpunitWarning(PhpunitWarningTriggered $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $this->updateTestStatus(TestStatus::warning()); + } + + /** + * @throws InvalidArgumentException + */ + public function testFinished(Finished $event): void + { + if (!$event->test()->isTestMethod()) { + return; + } + + $test = $event->test(); + + assert($test instanceof TestMethod); + + $this->process($test); + + $this->status = null; + $this->throwable = null; + $this->prepared = false; + } + + private function registerSubscribers(Facade $facade): void + { + $facade->registerSubscribers( + new TestConsideredRiskySubscriber($this), + new TestErroredSubscriber($this), + new TestFailedSubscriber($this), + new TestFinishedSubscriber($this), + new TestMarkedIncompleteSubscriber($this), + new TestPassedSubscriber($this), + new TestPreparedSubscriber($this), + new TestSkippedSubscriber($this), + new TestTriggeredDeprecationSubscriber($this), + new TestTriggeredNoticeSubscriber($this), + new TestTriggeredPhpDeprecationSubscriber($this), + new TestTriggeredPhpNoticeSubscriber($this), + new TestTriggeredPhpunitDeprecationSubscriber($this), + new TestTriggeredPhpunitErrorSubscriber($this), + new TestTriggeredPhpunitWarningSubscriber($this), + new TestTriggeredPhpWarningSubscriber($this), + new TestTriggeredWarningSubscriber($this), + ); + } + + private function updateTestStatus(TestStatus $status): void + { + if ($this->status !== null && + $this->status->isMoreImportantThan($status)) { + return; + } + + $this->status = $status; + } + + private function process(TestMethod $test): void + { + if (!isset($this->tests[$test->testDox()->prettifiedClassName()])) { + $this->tests[$test->testDox()->prettifiedClassName()] = []; + } + + $this->tests[$test->testDox()->prettifiedClassName()][] = new TestDoxTestMethod( + $test, + $this->status, + $this->throwable, + ); + } +} diff --git a/src/Metadata/After.php b/src/Metadata/After.php index 8790059049b..14413639064 100644 --- a/src/Metadata/After.php +++ b/src/Metadata/After.php @@ -10,17 +10,31 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class After extends Metadata +final readonly class After extends Metadata { + private int $priority; + /** - * @psalm-assert-if-true After $this + * @param int<0, 1> $level */ - public function isAfter(): bool + protected function __construct(int $level, int $priority) + { + parent::__construct($level); + + $this->priority = $priority; + } + + public function isAfter(): true { return true; } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Metadata/AfterClass.php b/src/Metadata/AfterClass.php index 16bb5cc0a4a..7dcb96fd7c2 100644 --- a/src/Metadata/AfterClass.php +++ b/src/Metadata/AfterClass.php @@ -10,17 +10,31 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class AfterClass extends Metadata +final readonly class AfterClass extends Metadata { + private int $priority; + /** - * @psalm-assert-if-true AfterClass $this + * @param int<0, 1> $level */ - public function isAfterClass(): bool + protected function __construct(int $level, int $priority) + { + parent::__construct($level); + + $this->priority = $priority; + } + + public function isAfterClass(): true { return true; } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Metadata/Api/CodeCoverage.php b/src/Metadata/Api/CodeCoverage.php index 0741aeb1cb9..e9ac4769317 100644 --- a/src/Metadata/Api/CodeCoverage.php +++ b/src/Metadata/Api/CodeCoverage.php @@ -10,216 +10,144 @@ namespace PHPUnit\Metadata\Api; use function assert; -use function count; -use function interface_exists; -use function sprintf; -use function str_starts_with; -use PHPUnit\Framework\CodeCoverageException; -use PHPUnit\Framework\InvalidCoversTargetException; -use PHPUnit\Metadata\Covers; use PHPUnit\Metadata\CoversClass; -use PHPUnit\Metadata\CoversDefaultClass; +use PHPUnit\Metadata\CoversClassesThatExtendClass; +use PHPUnit\Metadata\CoversClassesThatImplementInterface; use PHPUnit\Metadata\CoversFunction; +use PHPUnit\Metadata\CoversMethod; +use PHPUnit\Metadata\CoversNamespace; +use PHPUnit\Metadata\CoversTrait; use PHPUnit\Metadata\Parser\Registry; -use PHPUnit\Metadata\Uses; use PHPUnit\Metadata\UsesClass; -use PHPUnit\Metadata\UsesDefaultClass; +use PHPUnit\Metadata\UsesClassesThatExtendClass; +use PHPUnit\Metadata\UsesClassesThatImplementInterface; use PHPUnit\Metadata\UsesFunction; -use SebastianBergmann\CodeUnit\CodeUnitCollection; -use SebastianBergmann\CodeUnit\InvalidCodeUnitException; -use SebastianBergmann\CodeUnit\Mapper; +use PHPUnit\Metadata\UsesMethod; +use PHPUnit\Metadata\UsesNamespace; +use PHPUnit\Metadata\UsesTrait; +use SebastianBergmann\CodeCoverage\Test\Target\Target; +use SebastianBergmann\CodeCoverage\Test\Target\TargetCollection; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CodeCoverage { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * - * @psalm-return array>|false - * - * @throws CodeCoverageException + * @param class-string $className + * @param non-empty-string $methodName */ - public function linesToBeCovered(string $className, string $methodName): array|false + public function coversTargets(string $className, string $methodName): TargetCollection { - if (!$this->shouldCodeCoverageBeCollectedFor($className, $methodName)) { - return false; - } + $targets = []; + + foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if ($metadata->isCoversNamespace()) { + assert($metadata instanceof CoversNamespace); - $metadataForClass = Registry::parser()->forClass($className); - $classShortcut = null; - - if ($metadataForClass->isCoversDefaultClass()->isNotEmpty()) { - if (count($metadataForClass->isCoversDefaultClass()) > 1) { - throw new CodeCoverageException( - sprintf( - 'More than one @coversDefaultClass annotation for class or interface "%s"', - $className, - ), - ); + $targets[] = Target::forNamespace($metadata->namespace()); } - $metadata = $metadataForClass->isCoversDefaultClass()->asArray()[0]; + if ($metadata->isCoversClass()) { + assert($metadata instanceof CoversClass); - assert($metadata instanceof CoversDefaultClass); + $targets[] = Target::forClass($metadata->className()); + } - $classShortcut = $metadata->className(); - } + if ($metadata->isCoversClassesThatExtendClass()) { + assert($metadata instanceof CoversClassesThatExtendClass); - $codeUnits = CodeUnitCollection::fromList(); - $mapper = new Mapper; + $targets[] = Target::forClassesThatExtendClass($metadata->className()); + } - foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { - if ($metadata->isCoversClass() || $metadata->isCoversFunction()) { - assert($metadata instanceof CoversClass || $metadata instanceof CoversFunction); - - try { - $codeUnits = $codeUnits->mergeWith( - $mapper->stringToCodeUnits($metadata->asStringForCodeUnitMapper()), - ); - } catch (InvalidCodeUnitException $e) { - if ($metadata->isCoversClass()) { - $type = 'Class'; - } else { - $type = 'Function'; - } - - throw new InvalidCoversTargetException( - sprintf( - '%s "%s" is not a valid target for code coverage', - $type, - $metadata->asStringForCodeUnitMapper(), - ), - $e->getCode(), - $e, - ); - } - } elseif ($metadata->isCovers()) { - assert($metadata instanceof Covers); - - $target = $metadata->target(); - - if (interface_exists($target)) { - throw new InvalidCoversTargetException( - sprintf( - 'Trying to @cover interface "%s".', - $target, - ), - ); - } - - if ($classShortcut !== null && str_starts_with($target, '::')) { - $target = $classShortcut . $target; - } - - try { - $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($target)); - } catch (InvalidCodeUnitException $e) { - throw new InvalidCoversTargetException( - sprintf( - '"@covers %s" is invalid', - $target, - ), - $e->getCode(), - $e, - ); - } + if ($metadata->isCoversClassesThatImplementInterface()) { + assert($metadata instanceof CoversClassesThatImplementInterface); + + $targets[] = Target::forClassesThatImplementInterface($metadata->interfaceName()); + } + + if ($metadata->isCoversMethod()) { + assert($metadata instanceof CoversMethod); + + $targets[] = Target::forMethod($metadata->className(), $metadata->methodName()); + } + + if ($metadata->isCoversFunction()) { + assert($metadata instanceof CoversFunction); + + $targets[] = Target::forFunction($metadata->functionName()); + } + + if ($metadata->isCoversTrait()) { + assert($metadata instanceof CoversTrait); + + $targets[] = Target::forTrait($metadata->traitName()); } } - return $mapper->codeUnitsToSourceLines($codeUnits); + return TargetCollection::fromArray($targets); } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * - * @psalm-return array> - * - * @throws CodeCoverageException + * @param class-string $className + * @param non-empty-string $methodName */ - public function linesToBeUsed(string $className, string $methodName): array + public function usesTargets(string $className, string $methodName): TargetCollection { - $metadataForClass = Registry::parser()->forClass($className); - $classShortcut = null; - - if ($metadataForClass->isUsesDefaultClass()->isNotEmpty()) { - if (count($metadataForClass->isUsesDefaultClass()) > 1) { - throw new CodeCoverageException( - sprintf( - 'More than one @usesDefaultClass annotation for class or interface "%s"', - $className, - ), - ); + $targets = []; + + foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { + if ($metadata->isUsesNamespace()) { + assert($metadata instanceof UsesNamespace); + + $targets[] = Target::forNamespace($metadata->namespace()); } - $metadata = $metadataForClass->isUsesDefaultClass()->asArray()[0]; + if ($metadata->isUsesClass()) { + assert($metadata instanceof UsesClass); - assert($metadata instanceof UsesDefaultClass); + $targets[] = Target::forClass($metadata->className()); + } - $classShortcut = $metadata->className(); - } + if ($metadata->isUsesClassesThatExtendClass()) { + assert($metadata instanceof UsesClassesThatExtendClass); - $codeUnits = CodeUnitCollection::fromList(); - $mapper = new Mapper; + $targets[] = Target::forClassesThatExtendClass($metadata->className()); + } - foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { - if ($metadata->isUsesClass() || $metadata->isUsesFunction()) { - assert($metadata instanceof UsesClass || $metadata instanceof UsesFunction); - - try { - $codeUnits = $codeUnits->mergeWith( - $mapper->stringToCodeUnits($metadata->asStringForCodeUnitMapper()), - ); - } catch (InvalidCodeUnitException $e) { - if ($metadata->isUsesClass()) { - $type = 'Class'; - } else { - $type = 'Function'; - } - - throw new InvalidCoversTargetException( - sprintf( - '%s "%s" is not a valid target for code coverage', - $type, - $metadata->asStringForCodeUnitMapper(), - ), - $e->getCode(), - $e, - ); - } - } elseif ($metadata->isUses()) { - assert($metadata instanceof Uses); - - $target = $metadata->target(); - - if ($classShortcut !== null && str_starts_with($target, '::')) { - $target = $classShortcut . $target; - } - - try { - $codeUnits = $codeUnits->mergeWith($mapper->stringToCodeUnits($target)); - } catch (InvalidCodeUnitException $e) { - throw new InvalidCoversTargetException( - sprintf( - '"@uses %s" is invalid', - $target, - ), - $e->getCode(), - $e, - ); - } + if ($metadata->isUsesClassesThatImplementInterface()) { + assert($metadata instanceof UsesClassesThatImplementInterface); + + $targets[] = Target::forClassesThatImplementInterface($metadata->interfaceName()); + } + + if ($metadata->isUsesMethod()) { + assert($metadata instanceof UsesMethod); + + $targets[] = Target::forMethod($metadata->className(), $metadata->methodName()); + } + + if ($metadata->isUsesFunction()) { + assert($metadata instanceof UsesFunction); + + $targets[] = Target::forFunction($metadata->functionName()); + } + + if ($metadata->isUsesTrait()) { + assert($metadata instanceof UsesTrait); + + $targets[] = Target::forTrait($metadata->traitName()); } } - return $mapper->codeUnitsToSourceLines($codeUnits); + return TargetCollection::fromArray($targets); } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function shouldCodeCoverageBeCollectedFor(string $className, string $methodName): bool { @@ -230,12 +158,6 @@ public function shouldCodeCoverageBeCollectedFor(string $className, string $meth return false; } - if ($metadataForMethod->isCovers()->isNotEmpty() || - $metadataForMethod->isCoversClass()->isNotEmpty() || - $metadataForMethod->isCoversFunction()->isNotEmpty()) { - return true; - } - if ($metadataForClass->isCoversNothing()->isNotEmpty()) { return false; } diff --git a/src/Metadata/Api/DataProvider.php b/src/Metadata/Api/DataProvider.php index db8d00ce9a0..ae4f995d7aa 100644 --- a/src/Metadata/Api/DataProvider.php +++ b/src/Metadata/Api/DataProvider.php @@ -10,22 +10,12 @@ namespace PHPUnit\Metadata\Api; use function array_key_exists; -use function array_merge; use function assert; -use function explode; +use function get_debug_type; use function is_array; use function is_int; -use function json_decode; -use function json_last_error; -use function json_last_error_msg; -use function preg_match; -use function preg_replace; -use function rtrim; +use function is_string; use function sprintf; -use function str_replace; -use function strlen; -use function substr; -use function trim; use PHPUnit\Event; use PHPUnit\Framework\InvalidDataProviderException; use PHPUnit\Metadata\DataProvider as DataProviderMetadata; @@ -33,20 +23,22 @@ use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; use PHPUnit\Metadata\TestWith; use ReflectionClass; -use ReflectionMethod; use Throwable; -use Traversable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class DataProvider +final readonly class DataProvider { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * * @throws InvalidDataProviderException + * + * @return ?array> */ public function providedData(string $className, string $methodName): ?array { @@ -54,7 +46,7 @@ public function providedData(string $className, string $methodName): ?array $testWith = MetadataRegistry::parser()->forMethod($className, $methodName)->isTestWith(); if ($dataProvider->isEmpty() && $testWith->isEmpty()) { - return $this->dataProvidedByTestWithAnnotation($className, $methodName); + return null; } if ($dataProvider->isNotEmpty()) { @@ -73,8 +65,9 @@ public function providedData(string $className, string $methodName): ?array if (!is_array($value)) { throw new InvalidDataProviderException( sprintf( - 'Data set %s is invalid', + 'Data set %s is invalid, expected array but got %s', is_int($key) ? '#' . $key : '"' . $key . '"', + get_debug_type($value), ), ); } @@ -84,10 +77,12 @@ public function providedData(string $className, string $methodName): ?array } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * * @throws InvalidDataProviderException + * + * @return array> */ private function dataProvidedByMethods(string $className, string $methodName, MetadataCollection $dataProvider): array { @@ -110,7 +105,6 @@ private function dataProvidedByMethods(string $className, string $methodName, Me try { $class = new ReflectionClass($_dataProvider->className()); $method = $class->getMethod($_dataProvider->methodName()); - $object = null; if (!$method->isPublic()) { throw new InvalidDataProviderException( @@ -142,7 +136,11 @@ private function dataProvidedByMethods(string $className, string $methodName, Me ); } - $data = $method->invoke($object); + $className = $_dataProvider->className(); + $methodName = $_dataProvider->methodName(); + + /** @phpstan-ignore staticMethod.dynamicName */ + $data = $className::$methodName(); } catch (Throwable $e) { Event\Facade::emitter()->dataProviderMethodFinished( $testMethod, @@ -156,14 +154,11 @@ private function dataProvidedByMethods(string $className, string $methodName, Me ); } - if ($data instanceof Traversable) { - $origData = $data; - $data = []; - - foreach ($origData as $key => $value) { - if (is_int($key)) { - $data[] = $value; - } elseif (array_key_exists($key, $data)) { + foreach ($data as $key => $value) { + if (is_int($key)) { + $result[] = $value; + } elseif (is_string($key)) { + if (array_key_exists($key, $result)) { Event\Facade::emitter()->dataProviderMethodFinished( $testMethod, ...$methodsCalled, @@ -175,14 +170,19 @@ private function dataProvidedByMethods(string $className, string $methodName, Me $key, ), ); - } else { - $data[$key] = $value; } - } - } - if (is_array($data)) { - $result = array_merge($result, $data); + $result[$key] = $value; + } else { + // @codeCoverageIgnoreStart + throw new InvalidDataProviderException( + sprintf( + 'The key must be an integer or a string, %s given', + get_debug_type($key), + ), + ); + // @codeCoverageIgnoreEnd + } } } @@ -194,6 +194,9 @@ private function dataProvidedByMethods(string $className, string $methodName, Me return $result; } + /** + * @return array> + */ private function dataProvidedByMetadata(MetadataCollection $testWith): array { $result = []; @@ -201,62 +204,24 @@ private function dataProvidedByMetadata(MetadataCollection $testWith): array foreach ($testWith as $_testWith) { assert($_testWith instanceof TestWith); - $result[] = $_testWith->data(); - } - - return $result; - } - - /** - * @psalm-param class-string $className - * - * @throws InvalidDataProviderException - */ - private function dataProvidedByTestWithAnnotation(string $className, string $methodName): ?array - { - $docComment = (new ReflectionMethod($className, $methodName))->getDocComment(); - - if (!$docComment) { - return null; - } - - $docComment = str_replace("\r\n", "\n", $docComment); - $docComment = preg_replace('/\n\s*\*\s?/', "\n", $docComment); - $docComment = substr($docComment, 0, -1); - $docComment = rtrim($docComment, "\n"); - - if (!preg_match('/@testWith\s+/', $docComment, $matches, PREG_OFFSET_CAPTURE)) { - return null; - } + if ($_testWith->hasName()) { + $key = $_testWith->name(); - $offset = strlen($matches[0][0]) + (int) $matches[0][1]; - $annotationContent = substr($docComment, $offset); - $data = []; - - foreach (explode("\n", $annotationContent) as $candidateRow) { - $candidateRow = trim($candidateRow); - - if ($candidateRow[0] !== '[') { - break; - } - - $dataSet = json_decode($candidateRow, true); + if (array_key_exists($key, $result)) { + throw new InvalidDataProviderException( + sprintf( + 'The key "%s" has already been defined by a previous TestWith attribute', + $key, + ), + ); + } - if (json_last_error() !== JSON_ERROR_NONE) { - throw new InvalidDataProviderException( - 'The data set for the @testWith annotation cannot be parsed: ' . json_last_error_msg(), - ); + $result[$key] = $_testWith->data(); + } else { + $result[] = $_testWith->data(); } - - $data[] = $dataSet; } - if (!$data) { - throw new InvalidDataProviderException( - 'The data set for the @testWith annotation cannot be parsed.', - ); - } - - return $data; + return $result; } } diff --git a/src/Metadata/Api/Dependencies.php b/src/Metadata/Api/Dependencies.php index cda6e099859..3ba35568fb9 100644 --- a/src/Metadata/Api/Dependencies.php +++ b/src/Metadata/Api/Dependencies.php @@ -16,15 +16,17 @@ use PHPUnit\Metadata\Parser\Registry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Dependencies +final readonly class Dependencies { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * - * @psalm-return list + * @return list */ public static function dependencies(string $className, string $methodName): array { @@ -41,7 +43,7 @@ public static function dependencies(string $className, string $methodName): arra assert($metadata instanceof DependsOnMethod); - if (empty($metadata->methodName())) { + if ($metadata->methodName() === '') { $dependencies[] = ExecutionOrderDependency::invalid(); continue; diff --git a/src/Metadata/Api/Groups.php b/src/Metadata/Api/Groups.php index 73341851a4b..aa2cbf43fd9 100644 --- a/src/Metadata/Api/Groups.php +++ b/src/Metadata/Api/Groups.php @@ -10,33 +10,46 @@ namespace PHPUnit\Metadata\Api; use function array_flip; +use function array_key_exists; use function array_unique; use function assert; use function strtolower; use function trim; use PHPUnit\Framework\TestSize\TestSize; -use PHPUnit\Metadata\Covers; use PHPUnit\Metadata\CoversClass; use PHPUnit\Metadata\CoversFunction; use PHPUnit\Metadata\Group; use PHPUnit\Metadata\Parser\Registry; -use PHPUnit\Metadata\Uses; +use PHPUnit\Metadata\RequiresPhpExtension; use PHPUnit\Metadata\UsesClass; use PHPUnit\Metadata\UsesFunction; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Groups { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @var array> + */ + private static array $groupCache = []; + + /** + * @param class-string $className + * @param non-empty-string $methodName * - * @psalm-return list + * @return list */ public function groups(string $className, string $methodName, bool $includeVirtual = true): array { + $key = $className . '::' . $methodName . '::' . $includeVirtual; + + if (array_key_exists($key, self::$groupCache)) { + return self::$groupCache[$key]; + } + $groups = []; foreach (Registry::parser()->forClassAndMethod($className, $methodName)->isGroup() as $group) { @@ -45,52 +58,56 @@ public function groups(string $className, string $methodName, bool $includeVirtu $groups[] = $group->groupName(); } - if ($groups === []) { - $groups[] = 'default'; - } - if (!$includeVirtual) { - return array_unique($groups); + return self::$groupCache[$key] = array_unique($groups); } foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) { - if ($metadata->isCoversClass() || $metadata->isCoversFunction()) { - assert($metadata instanceof CoversClass || $metadata instanceof CoversFunction); + if ($metadata->isCoversClass()) { + assert($metadata instanceof CoversClass); + + $groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->className()); + + continue; + } + + if ($metadata->isCoversFunction()) { + assert($metadata instanceof CoversFunction); - $groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->asStringForCodeUnitMapper()); + $groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->functionName()); continue; } - if ($metadata->isCovers()) { - assert($metadata instanceof Covers); + if ($metadata->isUsesClass()) { + assert($metadata instanceof UsesClass); - $groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->target()); + $groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->className()); continue; } - if ($metadata->isUsesClass() || $metadata->isUsesFunction()) { - assert($metadata instanceof UsesClass || $metadata instanceof UsesFunction); + if ($metadata->isUsesFunction()) { + assert($metadata instanceof UsesFunction); - $groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->asStringForCodeUnitMapper()); + $groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->functionName()); continue; } - if ($metadata->isUses()) { - assert($metadata instanceof Uses); + if ($metadata->isRequiresPhpExtension()) { + assert($metadata instanceof RequiresPhpExtension); - $groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->target()); + $groups[] = '__phpunit_requires_php_extension' . $this->canonicalizeName($metadata->extension()); } } - return array_unique($groups); + return self::$groupCache[$key] = array_unique($groups); } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function size(string $className, string $methodName): TestSize { diff --git a/src/Metadata/Api/HookMethods.php b/src/Metadata/Api/HookMethods.php index 1b085597c13..94d429704b8 100644 --- a/src/Metadata/Api/HookMethods.php +++ b/src/Metadata/Api/HookMethods.php @@ -9,27 +9,37 @@ */ namespace PHPUnit\Metadata\Api; -use function array_unshift; use function assert; use function class_exists; +use PHPUnit\Framework\TestCase; +use PHPUnit\Metadata\After; +use PHPUnit\Metadata\AfterClass; +use PHPUnit\Metadata\Before; +use PHPUnit\Metadata\BeforeClass; use PHPUnit\Metadata\Parser\Registry; +use PHPUnit\Metadata\PostCondition; +use PHPUnit\Metadata\PreCondition; +use PHPUnit\Runner\HookMethod; +use PHPUnit\Runner\HookMethodCollection; use PHPUnit\Util\Reflection; use ReflectionClass; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class HookMethods { /** - * @psalm-var array, before: list, preCondition: list, postCondition: list, after: list, afterClass: list}> + * @var array */ private static array $hookMethods = []; /** - * @psalm-param class-string $className + * @param class-string $className * - * @psalm-return array{beforeClass: list, before: list, preCondition: list, postCondition: list, after: list, afterClass: list} + * @return array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} */ public function hookMethods(string $className): array { @@ -43,46 +53,64 @@ public function hookMethods(string $className): array self::$hookMethods[$className] = self::emptyHookMethodsArray(); - foreach (Reflection::methodsInTestClass(new ReflectionClass($className)) as $method) { + foreach (Reflection::methodsDeclaredDirectlyInTestClass(new ReflectionClass($className)) as $method) { $methodName = $method->getName(); - - assert(!empty($methodName)); - - $metadata = Registry::parser()->forMethod($className, $methodName); + $metadata = Registry::parser()->forMethod($className, $methodName); if ($method->isStatic()) { if ($metadata->isBeforeClass()->isNotEmpty()) { - array_unshift( - self::$hookMethods[$className]['beforeClass'], - $methodName, + $beforeClass = $metadata->isBeforeClass()->asArray()[0]; + assert($beforeClass instanceof BeforeClass); + + self::$hookMethods[$className]['beforeClass']->add( + new HookMethod($methodName, $beforeClass->priority()), ); } if ($metadata->isAfterClass()->isNotEmpty()) { - self::$hookMethods[$className]['afterClass'][] = $methodName; + $afterClass = $metadata->isAfterClass()->asArray()[0]; + assert($afterClass instanceof AfterClass); + + self::$hookMethods[$className]['afterClass']->add( + new HookMethod($methodName, $afterClass->priority()), + ); } } if ($metadata->isBefore()->isNotEmpty()) { - array_unshift( - self::$hookMethods[$className]['before'], - $methodName, + $before = $metadata->isBefore()->asArray()[0]; + assert($before instanceof Before); + + self::$hookMethods[$className]['before']->add( + new HookMethod($methodName, $before->priority()), ); } if ($metadata->isPreCondition()->isNotEmpty()) { - array_unshift( - self::$hookMethods[$className]['preCondition'], - $methodName, + $preCondition = $metadata->isPreCondition()->asArray()[0]; + assert($preCondition instanceof PreCondition); + + self::$hookMethods[$className]['preCondition']->add( + new HookMethod($methodName, $preCondition->priority()), ); } if ($metadata->isPostCondition()->isNotEmpty()) { - self::$hookMethods[$className]['postCondition'][] = $methodName; + $postCondition = $metadata->isPostCondition()->asArray()[0]; + assert($postCondition instanceof PostCondition); + + self::$hookMethods[$className]['postCondition']->add( + new HookMethod($methodName, $postCondition->priority()), + ); } if ($metadata->isAfter()->isNotEmpty()) { - self::$hookMethods[$className]['after'][] = $methodName; + $after = $metadata->isAfter()->asArray()[0]; + assert($after instanceof After); + + self::$hookMethods[$className]['after']->add( + new HookMethod($methodName, $after->priority()), + ); } } @@ -90,17 +118,17 @@ public function hookMethods(string $className): array } /** - * @psalm-return array{beforeClass: list, before: list, preCondition: list, postCondition: list, after: list, afterClass: list} + * @return array{beforeClass: HookMethodCollection, before: HookMethodCollection, preCondition: HookMethodCollection, postCondition: HookMethodCollection, after: HookMethodCollection, afterClass: HookMethodCollection} */ private function emptyHookMethodsArray(): array { return [ - 'beforeClass' => ['setUpBeforeClass'], - 'before' => ['setUp'], - 'preCondition' => ['assertPreConditions'], - 'postCondition' => ['assertPostConditions'], - 'after' => ['tearDown'], - 'afterClass' => ['tearDownAfterClass'], + 'beforeClass' => HookMethodCollection::defaultBeforeClass(), + 'before' => HookMethodCollection::defaultBefore(), + 'preCondition' => HookMethodCollection::defaultPreCondition(), + 'postCondition' => HookMethodCollection::defaultPostCondition(), + 'after' => HookMethodCollection::defaultAfter(), + 'afterClass' => HookMethodCollection::defaultAfterClass(), ]; } } diff --git a/src/Metadata/Api/Requirements.php b/src/Metadata/Api/Requirements.php index 70688a11086..4a6abe76fff 100644 --- a/src/Metadata/Api/Requirements.php +++ b/src/Metadata/Api/Requirements.php @@ -13,15 +13,19 @@ use const PHP_OS_FAMILY; use const PHP_VERSION; use function addcslashes; +use function array_column; +use function array_key_exists; use function assert; use function extension_loaded; use function function_exists; +use function in_array; use function ini_get; use function method_exists; use function phpversion; use function preg_match; use function sprintf; use PHPUnit\Metadata\Parser\Registry; +use PHPUnit\Metadata\RequiresEnvironmentVariable; use PHPUnit\Metadata\RequiresFunction; use PHPUnit\Metadata\RequiresMethod; use PHPUnit\Metadata\RequiresOperatingSystem; @@ -29,19 +33,23 @@ use PHPUnit\Metadata\RequiresPhp; use PHPUnit\Metadata\RequiresPhpExtension; use PHPUnit\Metadata\RequiresPhpunit; +use PHPUnit\Metadata\RequiresPhpunitExtension; use PHPUnit\Metadata\RequiresSetting; use PHPUnit\Runner\Version; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Requirements +final readonly class Requirements { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * - * @psalm-return list + * @return list */ public function requirementsNotSatisfiedFor(string $className, string $methodName): array { @@ -62,9 +70,15 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam if ($metadata->isRequiresPhpExtension()) { assert($metadata instanceof RequiresPhpExtension); + $extensionVersion = phpversion($metadata->extension()); + + if ($extensionVersion === false) { + $extensionVersion = ''; + } + if (!extension_loaded($metadata->extension()) || ($metadata->hasVersionRequirement() && - !$metadata->versionRequirement()->isSatisfiedBy(phpversion($metadata->extension())))) { + !$metadata->versionRequirement()->isSatisfiedBy($extensionVersion))) { $notSatisfied[] = sprintf( 'PHP extension %s%s is required.', $metadata->extension(), @@ -84,6 +98,40 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam } } + if ($metadata->isRequiresPhpunitExtension()) { + assert($metadata instanceof RequiresPhpunitExtension); + + $configuration = ConfigurationRegistry::get(); + + $extensionBootstrappers = array_column($configuration->extensionBootstrappers(), 'className'); + + if ($configuration->noExtensions() || !in_array($metadata->extensionClass(), $extensionBootstrappers, true)) { + $notSatisfied[] = sprintf( + 'PHPUnit extension "%s" is required.', + $metadata->extensionClass(), + ); + } + } + + if ($metadata->isRequiresEnvironmentVariable()) { + assert($metadata instanceof RequiresEnvironmentVariable); + + if (!array_key_exists($metadata->environmentVariableName(), $_ENV) || + $metadata->value() === null && $_ENV[$metadata->environmentVariableName()] === '') { + $notSatisfied[] = sprintf('Environment variable "%s" is required.', $metadata->environmentVariableName()); + + continue; + } + + if ($metadata->value() !== null && $_ENV[$metadata->environmentVariableName()] !== $metadata->value()) { + $notSatisfied[] = sprintf( + 'Environment variable "%s" is required to be "%s".', + $metadata->environmentVariableName(), + $metadata->value(), + ); + } + } + if ($metadata->isRequiresOperatingSystemFamily()) { assert($metadata instanceof RequiresOperatingSystemFamily); @@ -103,7 +151,7 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam addcslashes($metadata->operatingSystem(), '/'), ); - if (!preg_match($pattern, PHP_OS)) { + if (preg_match($pattern, PHP_OS) === 0) { $notSatisfied[] = sprintf( 'Operating system %s is required.', $metadata->operatingSystem(), diff --git a/src/Metadata/BackupGlobals.php b/src/Metadata/BackupGlobals.php index b6c485c8815..5d9478443df 100644 --- a/src/Metadata/BackupGlobals.php +++ b/src/Metadata/BackupGlobals.php @@ -10,16 +10,16 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class BackupGlobals extends Metadata +final readonly class BackupGlobals extends Metadata { - private readonly bool $enabled; + private bool $enabled; /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level */ protected function __construct(int $level, bool $enabled) { @@ -28,10 +28,7 @@ protected function __construct(int $level, bool $enabled) $this->enabled = $enabled; } - /** - * @psalm-assert-if-true BackupGlobals $this - */ - public function isBackupGlobals(): bool + public function isBackupGlobals(): true { return true; } diff --git a/src/Metadata/BackupStaticProperties.php b/src/Metadata/BackupStaticProperties.php index 30cb9c4d6a8..a07698eeb13 100644 --- a/src/Metadata/BackupStaticProperties.php +++ b/src/Metadata/BackupStaticProperties.php @@ -10,16 +10,16 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class BackupStaticProperties extends Metadata +final readonly class BackupStaticProperties extends Metadata { - private readonly bool $enabled; + private bool $enabled; /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level */ protected function __construct(int $level, bool $enabled) { @@ -28,10 +28,7 @@ protected function __construct(int $level, bool $enabled) $this->enabled = $enabled; } - /** - * @psalm-assert-if-true BackupStaticProperties $this - */ - public function isBackupStaticProperties(): bool + public function isBackupStaticProperties(): true { return true; } diff --git a/src/Metadata/Before.php b/src/Metadata/Before.php index 08a72b47f83..08372f76ca3 100644 --- a/src/Metadata/Before.php +++ b/src/Metadata/Before.php @@ -10,17 +10,31 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Before extends Metadata +final readonly class Before extends Metadata { + private int $priority; + /** - * @psalm-assert-if-true Before $this + * @param int<0, 1> $level */ - public function isBefore(): bool + protected function __construct(int $level, int $priority) + { + parent::__construct($level); + + $this->priority = $priority; + } + + public function isBefore(): true { return true; } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Metadata/BeforeClass.php b/src/Metadata/BeforeClass.php index 9ae35e51452..c5646320789 100644 --- a/src/Metadata/BeforeClass.php +++ b/src/Metadata/BeforeClass.php @@ -10,17 +10,31 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class BeforeClass extends Metadata +final readonly class BeforeClass extends Metadata { + private int $priority; + /** - * @psalm-assert-if-true BeforeClass $this + * @param int<0, 1> $level */ - public function isBeforeClass(): bool + protected function __construct(int $level, int $priority) + { + parent::__construct($level); + + $this->priority = $priority; + } + + public function isBeforeClass(): true { return true; } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Metadata/Covers.php b/src/Metadata/Covers.php deleted file mode 100644 index b4c72345333..00000000000 --- a/src/Metadata/Covers.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class Covers extends Metadata -{ - /** - * @psalm-var non-empty-string - */ - private readonly string $target; - - /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $target - */ - protected function __construct(int $level, string $target) - { - parent::__construct($level); - - $this->target = $target; - } - - /** - * @psalm-assert-if-true Covers $this - */ - public function isCovers(): bool - { - return true; - } - - /** - * @psalm-return non-empty-string - */ - public function target(): string - { - return $this->target; - } -} diff --git a/src/Metadata/CoversClass.php b/src/Metadata/CoversClass.php index 22889adf404..e573952b639 100644 --- a/src/Metadata/CoversClass.php +++ b/src/Metadata/CoversClass.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class CoversClass extends Metadata +final readonly class CoversClass extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className + * @param int<0, 1> $level + * @param class-string $className */ protected function __construct(int $level, string $className) { @@ -32,29 +32,16 @@ protected function __construct(int $level, string $className) $this->className = $className; } - /** - * @psalm-assert-if-true CoversClass $this - */ - public function isCoversClass(): bool + public function isCoversClass(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { return $this->className; } - - /** - * @psalm-return class-string - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function asStringForCodeUnitMapper(): string - { - return $this->className; - } } diff --git a/src/Metadata/CoversClassesThatExtendClass.php b/src/Metadata/CoversClassesThatExtendClass.php new file mode 100644 index 00000000000..7feb40d7098 --- /dev/null +++ b/src/Metadata/CoversClassesThatExtendClass.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class CoversClassesThatExtendClass extends Metadata +{ + /** + * @var class-string + */ + private string $className; + + /** + * @param int<0, 1> $level + * @param class-string $className + */ + protected function __construct(int $level, string $className) + { + parent::__construct($level); + + $this->className = $className; + } + + public function isCoversClassesThatExtendClass(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } +} diff --git a/src/Metadata/CoversClassesThatImplementInterface.php b/src/Metadata/CoversClassesThatImplementInterface.php new file mode 100644 index 00000000000..e980801ecb3 --- /dev/null +++ b/src/Metadata/CoversClassesThatImplementInterface.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class CoversClassesThatImplementInterface extends Metadata +{ + /** + * @var class-string + */ + private string $interfaceName; + + /** + * @param int<0, 1> $level + * @param class-string $interfaceName + */ + protected function __construct(int $level, string $interfaceName) + { + parent::__construct($level); + + $this->interfaceName = $interfaceName; + } + + public function isCoversClassesThatImplementInterface(): true + { + return true; + } + + /** + * @return class-string + */ + public function interfaceName(): string + { + return $this->interfaceName; + } +} diff --git a/src/Metadata/CoversDefaultClass.php b/src/Metadata/CoversDefaultClass.php deleted file mode 100644 index 3f7b57c4423..00000000000 --- a/src/Metadata/CoversDefaultClass.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class CoversDefaultClass extends Metadata -{ - /** - * @psalm-var class-string - */ - private readonly string $className; - - /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className - */ - protected function __construct(int $level, string $className) - { - parent::__construct($level); - - $this->className = $className; - } - - /** - * @psalm-assert-if-true CoversDefaultClass $this - */ - public function isCoversDefaultClass(): bool - { - return true; - } - - /** - * @psalm-return class-string - */ - public function className(): string - { - return $this->className; - } -} diff --git a/src/Metadata/CoversFunction.php b/src/Metadata/CoversFunction.php index ee9f858ea43..4e953601f8e 100644 --- a/src/Metadata/CoversFunction.php +++ b/src/Metadata/CoversFunction.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class CoversFunction extends Metadata +final readonly class CoversFunction extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $functionName; + private string $functionName; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $functionName + * @param int<0, 1> $level + * @param non-empty-string $functionName */ protected function __construct(int $level, string $functionName) { @@ -32,27 +32,16 @@ protected function __construct(int $level, string $functionName) $this->functionName = $functionName; } - /** - * @psalm-assert-if-true CoversFunction $this - */ - public function isCoversFunction(): bool + public function isCoversFunction(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function functionName(): string { return $this->functionName; } - - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function asStringForCodeUnitMapper(): string - { - return '::' . $this->functionName; - } } diff --git a/src/Metadata/CoversMethod.php b/src/Metadata/CoversMethod.php new file mode 100644 index 00000000000..73092ff780e --- /dev/null +++ b/src/Metadata/CoversMethod.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class CoversMethod extends Metadata +{ + /** + * @var class-string + */ + private string $className; + + /** + * @var non-empty-string + */ + private string $methodName; + + /** + * @param int<0, 1> $level + * @param class-string $className + * @param non-empty-string $methodName + */ + protected function __construct(int $level, string $className, string $methodName) + { + parent::__construct($level); + + $this->className = $className; + $this->methodName = $methodName; + } + + public function isCoversMethod(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } +} diff --git a/src/Metadata/CoversNamespace.php b/src/Metadata/CoversNamespace.php new file mode 100644 index 00000000000..ef7452a66a4 --- /dev/null +++ b/src/Metadata/CoversNamespace.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class CoversNamespace extends Metadata +{ + /** + * @var non-empty-string + */ + private string $namespace; + + /** + * @param int<0, 1> $level + * @param non-empty-string $namespace + */ + protected function __construct(int $level, string $namespace) + { + parent::__construct($level); + + $this->namespace = $namespace; + } + + public function isCoversNamespace(): true + { + return true; + } + + /** + * @return class-string + */ + public function namespace(): string + { + return $this->namespace; + } +} diff --git a/src/Metadata/CoversNothing.php b/src/Metadata/CoversNothing.php index 4703964e5d0..c81e72765b4 100644 --- a/src/Metadata/CoversNothing.php +++ b/src/Metadata/CoversNothing.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class CoversNothing extends Metadata +final readonly class CoversNothing extends Metadata { - /** - * @psalm-assert-if-true CoversNothing $this - */ - public function isCoversNothing(): bool + public function isCoversNothing(): true { return true; } diff --git a/src/Metadata/CoversTrait.php b/src/Metadata/CoversTrait.php new file mode 100644 index 00000000000..2cad9dfb4ab --- /dev/null +++ b/src/Metadata/CoversTrait.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class CoversTrait extends Metadata +{ + /** + * @var trait-string + */ + private string $traitName; + + /** + * @param 0|1 $level + * @param trait-string $traitName + */ + protected function __construct(int $level, string $traitName) + { + parent::__construct($level); + + $this->traitName = $traitName; + } + + public function isCoversTrait(): true + { + return true; + } + + /** + * @return trait-string + */ + public function traitName(): string + { + return $this->traitName; + } +} diff --git a/src/Metadata/DataProvider.php b/src/Metadata/DataProvider.php index d1a0bfd592a..57b821e4819 100644 --- a/src/Metadata/DataProvider.php +++ b/src/Metadata/DataProvider.php @@ -10,26 +10,26 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DataProvider extends Metadata +final readonly class DataProvider extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $methodName; + private string $methodName; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param int<0, 1> $level + * @param class-string $className + * @param non-empty-string $methodName */ protected function __construct(int $level, string $className, string $methodName) { @@ -39,16 +39,13 @@ protected function __construct(int $level, string $className, string $methodName $this->methodName = $methodName; } - /** - * @psalm-assert-if-true DataProvider $this - */ - public function isDataProvider(): bool + public function isDataProvider(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -56,7 +53,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Metadata/DependsOnClass.php b/src/Metadata/DependsOnClass.php index c146afd9594..fbc40fd144e 100644 --- a/src/Metadata/DependsOnClass.php +++ b/src/Metadata/DependsOnClass.php @@ -10,22 +10,22 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DependsOnClass extends Metadata +final readonly class DependsOnClass extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; - private readonly bool $deepClone; - private readonly bool $shallowClone; + private string $className; + private bool $deepClone; + private bool $shallowClone; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className + * @param int<0, 1> $level + * @param class-string $className */ protected function __construct(int $level, string $className, bool $deepClone, bool $shallowClone) { @@ -36,16 +36,13 @@ protected function __construct(int $level, string $className, bool $deepClone, b $this->shallowClone = $shallowClone; } - /** - * @psalm-assert-if-true DependsOnClass $this - */ - public function isDependsOnClass(): bool + public function isDependsOnClass(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { diff --git a/src/Metadata/DependsOnMethod.php b/src/Metadata/DependsOnMethod.php index 1db27c7bfd0..82056a996f2 100644 --- a/src/Metadata/DependsOnMethod.php +++ b/src/Metadata/DependsOnMethod.php @@ -10,28 +10,28 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DependsOnMethod extends Metadata +final readonly class DependsOnMethod extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $methodName; - private readonly bool $deepClone; - private readonly bool $shallowClone; + private string $methodName; + private bool $deepClone; + private bool $shallowClone; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param int<0, 1> $level + * @param class-string $className + * @param non-empty-string $methodName */ protected function __construct(int $level, string $className, string $methodName, bool $deepClone, bool $shallowClone) { @@ -43,16 +43,13 @@ protected function __construct(int $level, string $className, string $methodName $this->shallowClone = $shallowClone; } - /** - * @psalm-assert-if-true DependsOnMethod $this - */ - public function isDependsOnMethod(): bool + public function isDependsOnMethod(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -60,7 +57,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Metadata/DisableReturnValueGenerationForTestDoubles.php b/src/Metadata/DisableReturnValueGenerationForTestDoubles.php new file mode 100644 index 00000000000..59cf34e2833 --- /dev/null +++ b/src/Metadata/DisableReturnValueGenerationForTestDoubles.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class DisableReturnValueGenerationForTestDoubles extends Metadata +{ + public function isDisableReturnValueGenerationForTestDoubles(): true + { + return true; + } +} diff --git a/src/Metadata/DoesNotPerformAssertions.php b/src/Metadata/DoesNotPerformAssertions.php index 0e1ac0e7451..e2925c8576b 100644 --- a/src/Metadata/DoesNotPerformAssertions.php +++ b/src/Metadata/DoesNotPerformAssertions.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class DoesNotPerformAssertions extends Metadata +final readonly class DoesNotPerformAssertions extends Metadata { - /** - * @psalm-assert-if-true DoesNotPerformAssertions $this - */ - public function isDoesNotPerformAssertions(): bool + public function isDoesNotPerformAssertions(): true { return true; } diff --git a/src/Metadata/Exception/AnnotationsAreNotSupportedForInternalClassesException.php b/src/Metadata/Exception/AnnotationsAreNotSupportedForInternalClassesException.php deleted file mode 100644 index 5693dfb4fbc..00000000000 --- a/src/Metadata/Exception/AnnotationsAreNotSupportedForInternalClassesException.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -use function sprintf; -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class AnnotationsAreNotSupportedForInternalClassesException extends RuntimeException implements \PHPUnit\Exception -{ - /** - * @psalm-param class-string $className - */ - public function __construct(string $className) - { - parent::__construct( - sprintf( - 'Annotations can only be parsed for user-defined classes, trying to parse annotations for class "%s"', - $className, - ), - ); - } -} diff --git a/src/Metadata/Exception/InvalidAttributeException.php b/src/Metadata/Exception/InvalidAttributeException.php new file mode 100644 index 00000000000..9158de32851 --- /dev/null +++ b/src/Metadata/Exception/InvalidAttributeException.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use const PHP_EOL; +use function sprintf; +use PHPUnit\Exception; +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidAttributeException extends RuntimeException implements Exception +{ + /** + * @param non-empty-string $attributeName + * @param non-empty-string $target + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $message + */ + public function __construct(string $attributeName, string $target, string $file, int $line, string $message) + { + parent::__construct( + sprintf( + 'Invalid attribute %s for %s in %s:%d%s%s', + $attributeName, + $target, + $file, + $line, + PHP_EOL, + $message, + ), + ); + } +} diff --git a/src/Metadata/Exception/ReflectionException.php b/src/Metadata/Exception/ReflectionException.php deleted file mode 100644 index 4311e83b423..00000000000 --- a/src/Metadata/Exception/ReflectionException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReflectionException extends RuntimeException implements \PHPUnit\Exception -{ -} diff --git a/src/Metadata/ExcludeGlobalVariableFromBackup.php b/src/Metadata/ExcludeGlobalVariableFromBackup.php index a096d676b2d..4f646dbdea7 100644 --- a/src/Metadata/ExcludeGlobalVariableFromBackup.php +++ b/src/Metadata/ExcludeGlobalVariableFromBackup.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ExcludeGlobalVariableFromBackup extends Metadata +final readonly class ExcludeGlobalVariableFromBackup extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $globalVariableName; + private string $globalVariableName; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $globalVariableName + * @param int<0, 1> $level + * @param non-empty-string $globalVariableName */ protected function __construct(int $level, string $globalVariableName) { @@ -32,16 +32,13 @@ protected function __construct(int $level, string $globalVariableName) $this->globalVariableName = $globalVariableName; } - /** - * @psalm-assert-if-true ExcludeGlobalVariableFromBackup $this - */ - public function isExcludeGlobalVariableFromBackup(): bool + public function isExcludeGlobalVariableFromBackup(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function globalVariableName(): string { diff --git a/src/Metadata/ExcludeStaticPropertyFromBackup.php b/src/Metadata/ExcludeStaticPropertyFromBackup.php index 53a3c523aeb..21725e939dd 100644 --- a/src/Metadata/ExcludeStaticPropertyFromBackup.php +++ b/src/Metadata/ExcludeStaticPropertyFromBackup.php @@ -10,26 +10,26 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ExcludeStaticPropertyFromBackup extends Metadata +final readonly class ExcludeStaticPropertyFromBackup extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $propertyName; + private string $propertyName; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className - * @psalm-param non-empty-string $propertyName + * @param int<0, 1> $level + * @param class-string $className + * @param non-empty-string $propertyName */ protected function __construct(int $level, string $className, string $propertyName) { @@ -39,16 +39,13 @@ protected function __construct(int $level, string $className, string $propertyNa $this->propertyName = $propertyName; } - /** - * @psalm-assert-if-true ExcludeStaticPropertyFromBackup $this - */ - public function isExcludeStaticPropertyFromBackup(): bool + public function isExcludeStaticPropertyFromBackup(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -56,7 +53,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function propertyName(): string { diff --git a/src/Metadata/Group.php b/src/Metadata/Group.php index e609d2ed6ef..2ee2784f275 100644 --- a/src/Metadata/Group.php +++ b/src/Metadata/Group.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Group extends Metadata +final readonly class Group extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $groupName; + private string $groupName; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $groupName + * @param int<0, 1> $level + * @param non-empty-string $groupName */ protected function __construct(int $level, string $groupName) { @@ -32,16 +32,13 @@ protected function __construct(int $level, string $groupName) $this->groupName = $groupName; } - /** - * @psalm-assert-if-true Group $this - */ - public function isGroup(): bool + public function isGroup(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function groupName(): string { diff --git a/src/Metadata/IgnoreDeprecations.php b/src/Metadata/IgnoreDeprecations.php index b84a868c77d..e1f85a8d8a8 100644 --- a/src/Metadata/IgnoreDeprecations.php +++ b/src/Metadata/IgnoreDeprecations.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class IgnoreDeprecations extends Metadata +final readonly class IgnoreDeprecations extends Metadata { - /** - * @psalm-assert-if-true IgnoreDeprecations $this - */ - public function isIgnoreDeprecations(): bool + public function isIgnoreDeprecations(): true { return true; } diff --git a/src/Metadata/IgnorePhpunitDeprecations.php b/src/Metadata/IgnorePhpunitDeprecations.php index 30ef3fe64f3..5abcfa48521 100644 --- a/src/Metadata/IgnorePhpunitDeprecations.php +++ b/src/Metadata/IgnorePhpunitDeprecations.php @@ -10,16 +10,15 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class IgnorePhpunitDeprecations extends Metadata +final readonly class IgnorePhpunitDeprecations extends Metadata { - /** - * @psalm-assert-if-true IgnorePhpunitDeprecations $this - */ - public function isIgnorePhpunitDeprecations(): bool + public function isIgnorePhpunitDeprecations(): true { return true; } diff --git a/src/Metadata/Metadata.php b/src/Metadata/Metadata.php index 0e463a5050b..94b37716a49 100644 --- a/src/Metadata/Metadata.php +++ b/src/Metadata/Metadata.php @@ -10,30 +10,31 @@ namespace PHPUnit\Metadata; use PHPUnit\Metadata\Version\Requirement; +use PHPUnit\Runner\Extension\Extension; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class Metadata +abstract readonly class Metadata { - private const CLASS_LEVEL = 0; - private const METHOD_LEVEL = 1; + private const int CLASS_LEVEL = 0; + private const int METHOD_LEVEL = 1; /** - * @psalm-var 0|1 + * @var int<0, 1> */ - private readonly int $level; + private int $level; - public static function after(): After + public static function after(int $priority): After { - return new After(self::METHOD_LEVEL); + return new After(self::METHOD_LEVEL, $priority); } - public static function afterClass(): AfterClass + public static function afterClass(int $priority): AfterClass { - return new AfterClass(self::METHOD_LEVEL); + return new AfterClass(self::METHOD_LEVEL, $priority); } public static function backupGlobalsOnClass(bool $enabled): BackupGlobals @@ -56,18 +57,26 @@ public static function backupStaticPropertiesOnMethod(bool $enabled): BackupStat return new BackupStaticProperties(self::METHOD_LEVEL, $enabled); } - public static function before(): Before + public static function before(int $priority): Before { - return new Before(self::METHOD_LEVEL); + return new Before(self::METHOD_LEVEL, $priority); } - public static function beforeClass(): BeforeClass + public static function beforeClass(int $priority): BeforeClass { - return new BeforeClass(self::METHOD_LEVEL); + return new BeforeClass(self::METHOD_LEVEL, $priority); } /** - * @psalm-param class-string $className + * @param non-empty-string $namespace + */ + public static function coversNamespace(string $namespace): CoversNamespace + { + return new CoversNamespace(self::CLASS_LEVEL, $namespace); + } + + /** + * @param class-string $className */ public static function coversClass(string $className): CoversClass { @@ -75,35 +84,44 @@ public static function coversClass(string $className): CoversClass } /** - * @psalm-param non-empty-string $functionName + * @param class-string $className */ - public static function coversFunction(string $functionName): CoversFunction + public static function coversClassesThatExtendClass(string $className): CoversClassesThatExtendClass { - return new CoversFunction(self::CLASS_LEVEL, $functionName); + return new CoversClassesThatExtendClass(self::CLASS_LEVEL, $className); } /** - * @psalm-param non-empty-string $target + * @param class-string $interfaceName */ - public static function coversOnClass(string $target): Covers + public static function coversClassesThatImplementInterface(string $interfaceName): CoversClassesThatImplementInterface { - return new Covers(self::CLASS_LEVEL, $target); + return new CoversClassesThatImplementInterface(self::CLASS_LEVEL, $interfaceName); } /** - * @psalm-param non-empty-string $target + * @param trait-string $traitName */ - public static function coversOnMethod(string $target): Covers + public static function coversTrait(string $traitName): CoversTrait { - return new Covers(self::METHOD_LEVEL, $target); + return new CoversTrait(self::CLASS_LEVEL, $traitName); } /** - * @psalm-param class-string $className + * @param class-string $className + * @param non-empty-string $methodName */ - public static function coversDefaultClass(string $className): CoversDefaultClass + public static function coversMethod(string $className, string $methodName): CoversMethod { - return new CoversDefaultClass(self::CLASS_LEVEL, $className); + return new CoversMethod(self::CLASS_LEVEL, $className, $methodName); + } + + /** + * @param non-empty-string $functionName + */ + public static function coversFunction(string $functionName): CoversFunction + { + return new CoversFunction(self::CLASS_LEVEL, $functionName); } public static function coversNothingOnClass(): CoversNothing @@ -117,8 +135,8 @@ public static function coversNothingOnMethod(): CoversNothing } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public static function dataProvider(string $className, string $methodName): DataProvider { @@ -126,7 +144,7 @@ public static function dataProvider(string $className, string $methodName): Data } /** - * @psalm-param class-string $className + * @param class-string $className */ public static function dependsOnClass(string $className, bool $deepClone, bool $shallowClone): DependsOnClass { @@ -134,14 +152,19 @@ public static function dependsOnClass(string $className, bool $deepClone, bool $ } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public static function dependsOnMethod(string $className, string $methodName, bool $deepClone, bool $shallowClone): DependsOnMethod { return new DependsOnMethod(self::METHOD_LEVEL, $className, $methodName, $deepClone, $shallowClone); } + public static function disableReturnValueGenerationForTestDoubles(): DisableReturnValueGenerationForTestDoubles + { + return new DisableReturnValueGenerationForTestDoubles(self::CLASS_LEVEL); + } + public static function doesNotPerformAssertionsOnClass(): DoesNotPerformAssertions { return new DoesNotPerformAssertions(self::CLASS_LEVEL); @@ -153,7 +176,7 @@ public static function doesNotPerformAssertionsOnMethod(): DoesNotPerformAsserti } /** - * @psalm-param non-empty-string $globalVariableName + * @param non-empty-string $globalVariableName */ public static function excludeGlobalVariableFromBackupOnClass(string $globalVariableName): ExcludeGlobalVariableFromBackup { @@ -161,7 +184,7 @@ public static function excludeGlobalVariableFromBackupOnClass(string $globalVari } /** - * @psalm-param non-empty-string $globalVariableName + * @param non-empty-string $globalVariableName */ public static function excludeGlobalVariableFromBackupOnMethod(string $globalVariableName): ExcludeGlobalVariableFromBackup { @@ -169,8 +192,8 @@ public static function excludeGlobalVariableFromBackupOnMethod(string $globalVar } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $propertyName + * @param class-string $className + * @param non-empty-string $propertyName */ public static function excludeStaticPropertyFromBackupOnClass(string $className, string $propertyName): ExcludeStaticPropertyFromBackup { @@ -178,8 +201,8 @@ public static function excludeStaticPropertyFromBackupOnClass(string $className, } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $propertyName + * @param class-string $className + * @param non-empty-string $propertyName */ public static function excludeStaticPropertyFromBackupOnMethod(string $className, string $propertyName): ExcludeStaticPropertyFromBackup { @@ -187,7 +210,7 @@ public static function excludeStaticPropertyFromBackupOnMethod(string $className } /** - * @psalm-param non-empty-string $groupName + * @param non-empty-string $groupName */ public static function groupOnClass(string $groupName): Group { @@ -195,7 +218,7 @@ public static function groupOnClass(string $groupName): Group } /** - * @psalm-param non-empty-string $groupName + * @param non-empty-string $groupName */ public static function groupOnMethod(string $groupName): Group { @@ -228,14 +251,14 @@ public static function ignorePhpunitDeprecationsOnMethod(): IgnorePhpunitDepreca return new IgnorePhpunitDeprecations(self::METHOD_LEVEL); } - public static function postCondition(): PostCondition + public static function postCondition(int $priority): PostCondition { - return new PostCondition(self::METHOD_LEVEL); + return new PostCondition(self::METHOD_LEVEL, $priority); } - public static function preCondition(): PreCondition + public static function preCondition(int $priority): PreCondition { - return new PreCondition(self::METHOD_LEVEL); + return new PreCondition(self::METHOD_LEVEL, $priority); } public static function preserveGlobalStateOnClass(bool $enabled): PreserveGlobalState @@ -249,7 +272,7 @@ public static function preserveGlobalStateOnMethod(bool $enabled): PreserveGloba } /** - * @psalm-param non-empty-string $functionName + * @param non-empty-string $functionName */ public static function requiresFunctionOnClass(string $functionName): RequiresFunction { @@ -257,7 +280,7 @@ public static function requiresFunctionOnClass(string $functionName): RequiresFu } /** - * @psalm-param non-empty-string $functionName + * @param non-empty-string $functionName */ public static function requiresFunctionOnMethod(string $functionName): RequiresFunction { @@ -265,8 +288,8 @@ public static function requiresFunctionOnMethod(string $functionName): RequiresF } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public static function requiresMethodOnClass(string $className, string $methodName): RequiresMethod { @@ -274,8 +297,8 @@ public static function requiresMethodOnClass(string $className, string $methodNa } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public static function requiresMethodOnMethod(string $className, string $methodName): RequiresMethod { @@ -283,7 +306,7 @@ public static function requiresMethodOnMethod(string $className, string $methodN } /** - * @psalm-param non-empty-string $operatingSystem + * @param non-empty-string $operatingSystem */ public static function requiresOperatingSystemOnClass(string $operatingSystem): RequiresOperatingSystem { @@ -291,7 +314,7 @@ public static function requiresOperatingSystemOnClass(string $operatingSystem): } /** - * @psalm-param non-empty-string $operatingSystem + * @param non-empty-string $operatingSystem */ public static function requiresOperatingSystemOnMethod(string $operatingSystem): RequiresOperatingSystem { @@ -299,7 +322,7 @@ public static function requiresOperatingSystemOnMethod(string $operatingSystem): } /** - * @psalm-param non-empty-string $operatingSystemFamily + * @param non-empty-string $operatingSystemFamily */ public static function requiresOperatingSystemFamilyOnClass(string $operatingSystemFamily): RequiresOperatingSystemFamily { @@ -307,7 +330,7 @@ public static function requiresOperatingSystemFamilyOnClass(string $operatingSys } /** - * @psalm-param non-empty-string $operatingSystemFamily + * @param non-empty-string $operatingSystemFamily */ public static function requiresOperatingSystemFamilyOnMethod(string $operatingSystemFamily): RequiresOperatingSystemFamily { @@ -325,7 +348,7 @@ public static function requiresPhpOnMethod(Requirement $versionRequirement): Req } /** - * @psalm-param non-empty-string $extension + * @param non-empty-string $extension */ public static function requiresPhpExtensionOnClass(string $extension, ?Requirement $versionRequirement): RequiresPhpExtension { @@ -333,7 +356,7 @@ public static function requiresPhpExtensionOnClass(string $extension, ?Requireme } /** - * @psalm-param non-empty-string $extension + * @param non-empty-string $extension */ public static function requiresPhpExtensionOnMethod(string $extension, ?Requirement $versionRequirement): RequiresPhpExtension { @@ -351,8 +374,44 @@ public static function requiresPhpunitOnMethod(Requirement $versionRequirement): } /** - * @psalm-param non-empty-string $setting - * @psalm-param non-empty-string $value + * @param class-string $extensionClass + */ + public static function requiresPhpunitExtensionOnClass(string $extensionClass): RequiresPhpunitExtension + { + return new RequiresPhpunitExtension(self::CLASS_LEVEL, $extensionClass); + } + + /** + * @param class-string $extensionClass + */ + public static function requiresPhpunitExtensionOnMethod(string $extensionClass): RequiresPhpunitExtension + { + return new RequiresPhpunitExtension(self::METHOD_LEVEL, $extensionClass); + } + + public static function requiresEnvironmentVariableOnClass(string $environmentVariableName, null|string $value): RequiresEnvironmentVariable + { + return new RequiresEnvironmentVariable(self::CLASS_LEVEL, $environmentVariableName, $value); + } + + public static function requiresEnvironmentVariableOnMethod(string $environmentVariableName, null|string $value): RequiresEnvironmentVariable + { + return new RequiresEnvironmentVariable(self::METHOD_LEVEL, $environmentVariableName, $value); + } + + public static function withEnvironmentVariableOnClass(string $environmentVariableName, null|string $value): WithEnvironmentVariable + { + return new WithEnvironmentVariable(self::CLASS_LEVEL, $environmentVariableName, $value); + } + + public static function withEnvironmentVariableOnMethod(string $environmentVariableName, null|string $value): WithEnvironmentVariable + { + return new WithEnvironmentVariable(self::METHOD_LEVEL, $environmentVariableName, $value); + } + + /** + * @param non-empty-string $setting + * @param non-empty-string $value */ public static function requiresSettingOnClass(string $setting, string $value): RequiresSetting { @@ -360,8 +419,8 @@ public static function requiresSettingOnClass(string $setting, string $value): R } /** - * @psalm-param non-empty-string $setting - * @psalm-param non-empty-string $value + * @param non-empty-string $setting + * @param non-empty-string $value */ public static function requiresSettingOnMethod(string $setting, string $value): RequiresSetting { @@ -389,7 +448,7 @@ public static function test(): Test } /** - * @psalm-param non-empty-string $text + * @param non-empty-string $text */ public static function testDoxOnClass(string $text): TestDox { @@ -397,20 +456,32 @@ public static function testDoxOnClass(string $text): TestDox } /** - * @psalm-param non-empty-string $text + * @param non-empty-string $text */ public static function testDoxOnMethod(string $text): TestDox { return new TestDox(self::METHOD_LEVEL, $text); } - public static function testWith(array $data): TestWith + /** + * @param array> $data + * @param ?non-empty-string $name + */ + public static function testWith(array $data, ?string $name = null): TestWith { - return new TestWith(self::METHOD_LEVEL, $data); + return new TestWith(self::METHOD_LEVEL, $data, $name); } /** - * @psalm-param class-string $className + * @param non-empty-string $namespace + */ + public static function usesNamespace(string $namespace): UsesNamespace + { + return new UsesNamespace(self::CLASS_LEVEL, $namespace); + } + + /** + * @param class-string $className */ public static function usesClass(string $className): UsesClass { @@ -418,35 +489,44 @@ public static function usesClass(string $className): UsesClass } /** - * @psalm-param non-empty-string $functionName + * @param class-string $className */ - public static function usesFunction(string $functionName): UsesFunction + public static function usesClassesThatExtendClass(string $className): UsesClassesThatExtendClass { - return new UsesFunction(self::CLASS_LEVEL, $functionName); + return new UsesClassesThatExtendClass(self::CLASS_LEVEL, $className); + } + + /** + * @param class-string $interfaceName + */ + public static function usesClassesThatImplementInterface(string $interfaceName): UsesClassesThatImplementInterface + { + return new UsesClassesThatImplementInterface(self::CLASS_LEVEL, $interfaceName); } /** - * @psalm-param non-empty-string $target + * @param trait-string $traitName */ - public static function usesOnClass(string $target): Uses + public static function usesTrait(string $traitName): UsesTrait { - return new Uses(self::CLASS_LEVEL, $target); + return new UsesTrait(self::CLASS_LEVEL, $traitName); } /** - * @psalm-param non-empty-string $target + * @param non-empty-string $functionName */ - public static function usesOnMethod(string $target): Uses + public static function usesFunction(string $functionName): UsesFunction { - return new Uses(self::METHOD_LEVEL, $target); + return new UsesFunction(self::CLASS_LEVEL, $functionName); } /** - * @psalm-param class-string $className + * @param class-string $className + * @param non-empty-string $methodName */ - public static function usesDefaultClass(string $className): UsesDefaultClass + public static function usesMethod(string $className, string $methodName): UsesMethod { - return new UsesDefaultClass(self::CLASS_LEVEL, $className); + return new UsesMethod(self::CLASS_LEVEL, $className, $methodName); } public static function withoutErrorHandler(): WithoutErrorHandler @@ -455,7 +535,7 @@ public static function withoutErrorHandler(): WithoutErrorHandler } /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level */ protected function __construct(int $level) { @@ -473,7 +553,7 @@ public function isMethodLevel(): bool } /** - * @psalm-assert-if-true After $this + * @phpstan-assert-if-true After $this */ public function isAfter(): bool { @@ -481,7 +561,7 @@ public function isAfter(): bool } /** - * @psalm-assert-if-true AfterClass $this + * @phpstan-assert-if-true AfterClass $this */ public function isAfterClass(): bool { @@ -489,7 +569,7 @@ public function isAfterClass(): bool } /** - * @psalm-assert-if-true BackupGlobals $this + * @phpstan-assert-if-true BackupGlobals $this */ public function isBackupGlobals(): bool { @@ -497,7 +577,7 @@ public function isBackupGlobals(): bool } /** - * @psalm-assert-if-true BackupStaticProperties $this + * @phpstan-assert-if-true BackupStaticProperties $this */ public function isBackupStaticProperties(): bool { @@ -505,7 +585,7 @@ public function isBackupStaticProperties(): bool } /** - * @psalm-assert-if-true BeforeClass $this + * @phpstan-assert-if-true BeforeClass $this */ public function isBeforeClass(): bool { @@ -513,7 +593,7 @@ public function isBeforeClass(): bool } /** - * @psalm-assert-if-true Before $this + * @phpstan-assert-if-true Before $this */ public function isBefore(): bool { @@ -521,15 +601,15 @@ public function isBefore(): bool } /** - * @psalm-assert-if-true Covers $this + * @phpstan-assert-if-true CoversNamespace $this */ - public function isCovers(): bool + public function isCoversNamespace(): bool { return false; } /** - * @psalm-assert-if-true CoversClass $this + * @phpstan-assert-if-true CoversClass $this */ public function isCoversClass(): bool { @@ -537,15 +617,31 @@ public function isCoversClass(): bool } /** - * @psalm-assert-if-true CoversDefaultClass $this + * @phpstan-assert-if-true CoversClassesThatExtendClass $this */ - public function isCoversDefaultClass(): bool + public function isCoversClassesThatExtendClass(): bool { return false; } /** - * @psalm-assert-if-true CoversFunction $this + * @phpstan-assert-if-true CoversClassesThatImplementInterface $this + */ + public function isCoversClassesThatImplementInterface(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true CoversTrait $this + */ + public function isCoversTrait(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true CoversFunction $this */ public function isCoversFunction(): bool { @@ -553,7 +649,15 @@ public function isCoversFunction(): bool } /** - * @psalm-assert-if-true CoversNothing $this + * @phpstan-assert-if-true CoversMethod $this + */ + public function isCoversMethod(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true CoversNothing $this */ public function isCoversNothing(): bool { @@ -561,7 +665,7 @@ public function isCoversNothing(): bool } /** - * @psalm-assert-if-true DataProvider $this + * @phpstan-assert-if-true DataProvider $this */ public function isDataProvider(): bool { @@ -569,7 +673,7 @@ public function isDataProvider(): bool } /** - * @psalm-assert-if-true DependsOnClass $this + * @phpstan-assert-if-true DependsOnClass $this */ public function isDependsOnClass(): bool { @@ -577,7 +681,7 @@ public function isDependsOnClass(): bool } /** - * @psalm-assert-if-true DependsOnMethod $this + * @phpstan-assert-if-true DependsOnMethod $this */ public function isDependsOnMethod(): bool { @@ -585,7 +689,15 @@ public function isDependsOnMethod(): bool } /** - * @psalm-assert-if-true DoesNotPerformAssertions $this + * @phpstan-assert-if-true DisableReturnValueGenerationForTestDoubles $this + */ + public function isDisableReturnValueGenerationForTestDoubles(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true DoesNotPerformAssertions $this */ public function isDoesNotPerformAssertions(): bool { @@ -593,7 +705,7 @@ public function isDoesNotPerformAssertions(): bool } /** - * @psalm-assert-if-true ExcludeGlobalVariableFromBackup $this + * @phpstan-assert-if-true ExcludeGlobalVariableFromBackup $this */ public function isExcludeGlobalVariableFromBackup(): bool { @@ -601,7 +713,7 @@ public function isExcludeGlobalVariableFromBackup(): bool } /** - * @psalm-assert-if-true ExcludeStaticPropertyFromBackup $this + * @phpstan-assert-if-true ExcludeStaticPropertyFromBackup $this */ public function isExcludeStaticPropertyFromBackup(): bool { @@ -609,7 +721,7 @@ public function isExcludeStaticPropertyFromBackup(): bool } /** - * @psalm-assert-if-true Group $this + * @phpstan-assert-if-true Group $this */ public function isGroup(): bool { @@ -617,7 +729,7 @@ public function isGroup(): bool } /** - * @psalm-assert-if-true IgnoreDeprecations $this + * @phpstan-assert-if-true IgnoreDeprecations $this */ public function isIgnoreDeprecations(): bool { @@ -625,7 +737,7 @@ public function isIgnoreDeprecations(): bool } /** - * @psalm-assert-if-true IgnorePhpunitDeprecations $this + * @phpstan-assert-if-true IgnorePhpunitDeprecations $this * * @internal This method is not covered by the backward compatibility promise for PHPUnit */ @@ -635,7 +747,7 @@ public function isIgnorePhpunitDeprecations(): bool } /** - * @psalm-assert-if-true RunClassInSeparateProcess $this + * @phpstan-assert-if-true RunClassInSeparateProcess $this */ public function isRunClassInSeparateProcess(): bool { @@ -643,7 +755,7 @@ public function isRunClassInSeparateProcess(): bool } /** - * @psalm-assert-if-true RunInSeparateProcess $this + * @phpstan-assert-if-true RunInSeparateProcess $this */ public function isRunInSeparateProcess(): bool { @@ -651,7 +763,7 @@ public function isRunInSeparateProcess(): bool } /** - * @psalm-assert-if-true RunTestsInSeparateProcesses $this + * @phpstan-assert-if-true RunTestsInSeparateProcesses $this */ public function isRunTestsInSeparateProcesses(): bool { @@ -659,7 +771,7 @@ public function isRunTestsInSeparateProcesses(): bool } /** - * @psalm-assert-if-true Test $this + * @phpstan-assert-if-true Test $this */ public function isTest(): bool { @@ -667,7 +779,7 @@ public function isTest(): bool } /** - * @psalm-assert-if-true PreCondition $this + * @phpstan-assert-if-true PreCondition $this */ public function isPreCondition(): bool { @@ -675,7 +787,7 @@ public function isPreCondition(): bool } /** - * @psalm-assert-if-true PostCondition $this + * @phpstan-assert-if-true PostCondition $this */ public function isPostCondition(): bool { @@ -683,7 +795,7 @@ public function isPostCondition(): bool } /** - * @psalm-assert-if-true PreserveGlobalState $this + * @phpstan-assert-if-true PreserveGlobalState $this */ public function isPreserveGlobalState(): bool { @@ -691,7 +803,7 @@ public function isPreserveGlobalState(): bool } /** - * @psalm-assert-if-true RequiresMethod $this + * @phpstan-assert-if-true RequiresMethod $this */ public function isRequiresMethod(): bool { @@ -699,7 +811,7 @@ public function isRequiresMethod(): bool } /** - * @psalm-assert-if-true RequiresFunction $this + * @phpstan-assert-if-true RequiresFunction $this */ public function isRequiresFunction(): bool { @@ -707,7 +819,7 @@ public function isRequiresFunction(): bool } /** - * @psalm-assert-if-true RequiresOperatingSystem $this + * @phpstan-assert-if-true RequiresOperatingSystem $this */ public function isRequiresOperatingSystem(): bool { @@ -715,7 +827,7 @@ public function isRequiresOperatingSystem(): bool } /** - * @psalm-assert-if-true RequiresOperatingSystemFamily $this + * @phpstan-assert-if-true RequiresOperatingSystemFamily $this */ public function isRequiresOperatingSystemFamily(): bool { @@ -723,7 +835,7 @@ public function isRequiresOperatingSystemFamily(): bool } /** - * @psalm-assert-if-true RequiresPhp $this + * @phpstan-assert-if-true RequiresPhp $this */ public function isRequiresPhp(): bool { @@ -731,7 +843,7 @@ public function isRequiresPhp(): bool } /** - * @psalm-assert-if-true RequiresPhpExtension $this + * @phpstan-assert-if-true RequiresPhpExtension $this */ public function isRequiresPhpExtension(): bool { @@ -739,7 +851,7 @@ public function isRequiresPhpExtension(): bool } /** - * @psalm-assert-if-true RequiresPhpunit $this + * @phpstan-assert-if-true RequiresPhpunit $this */ public function isRequiresPhpunit(): bool { @@ -747,7 +859,31 @@ public function isRequiresPhpunit(): bool } /** - * @psalm-assert-if-true RequiresSetting $this + * @phpstan-assert-if-true RequiresPhpunitExtension $this + */ + public function isRequiresPhpunitExtension(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true RequiresEnvironmentVariable $this + */ + public function isRequiresEnvironmentVariable(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true WithEnvironmentVariable $this + */ + public function isWithEnvironmentVariable(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true RequiresSetting $this */ public function isRequiresSetting(): bool { @@ -755,7 +891,7 @@ public function isRequiresSetting(): bool } /** - * @psalm-assert-if-true TestDox $this + * @phpstan-assert-if-true TestDox $this */ public function isTestDox(): bool { @@ -763,7 +899,7 @@ public function isTestDox(): bool } /** - * @psalm-assert-if-true TestWith $this + * @phpstan-assert-if-true TestWith $this */ public function isTestWith(): bool { @@ -771,15 +907,15 @@ public function isTestWith(): bool } /** - * @psalm-assert-if-true Uses $this + * @phpstan-assert-if-true UsesNamespace $this */ - public function isUses(): bool + public function isUsesNamespace(): bool { return false; } /** - * @psalm-assert-if-true UsesClass $this + * @phpstan-assert-if-true UsesClass $this */ public function isUsesClass(): bool { @@ -787,15 +923,31 @@ public function isUsesClass(): bool } /** - * @psalm-assert-if-true UsesDefaultClass $this + * @phpstan-assert-if-true UsesClassesThatExtendClass $this + */ + public function isUsesClassesThatExtendClass(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true UsesClassesThatImplementInterface $this + */ + public function isUsesClassesThatImplementInterface(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true UsesTrait $this */ - public function isUsesDefaultClass(): bool + public function isUsesTrait(): bool { return false; } /** - * @psalm-assert-if-true UsesFunction $this + * @phpstan-assert-if-true UsesFunction $this */ public function isUsesFunction(): bool { @@ -803,7 +955,15 @@ public function isUsesFunction(): bool } /** - * @psalm-assert-if-true WithoutErrorHandler $this + * @phpstan-assert-if-true UsesMethod $this + */ + public function isUsesMethod(): bool + { + return false; + } + + /** + * @phpstan-assert-if-true WithoutErrorHandler $this */ public function isWithoutErrorHandler(): bool { diff --git a/src/Metadata/MetadataCollection.php b/src/Metadata/MetadataCollection.php index ec39cf83020..f104df1cfa8 100644 --- a/src/Metadata/MetadataCollection.php +++ b/src/Metadata/MetadataCollection.php @@ -18,19 +18,19 @@ /** * @template-implements IteratorAggregate * - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class MetadataCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $metadata; /** - * @psalm-param list $metadata + * @param list $metadata */ public static function fromArray(array $metadata): self { @@ -43,7 +43,7 @@ private function __construct(Metadata ...$metadata) } /** - * @psalm-return list + * @return list */ public function asArray(): array { @@ -55,11 +55,19 @@ public function count(): int return count($this->metadata); } + /** + * @phpstan-assert-if-true 0 $this->count() + * @phpstan-assert-if-true array{} $this->asArray() + */ public function isEmpty(): bool { return $this->count() === 0; } + /** + * @phpstan-assert-if-true positive-int $this->count() + * @phpstan-assert-if-true non-empty-list $this->asArray() + */ public function isNotEmpty(): bool { return $this->count() > 0; @@ -160,12 +168,12 @@ public function isBefore(): self ); } - public function isCovers(): self + public function isCoversNamespace(): self { return new self( ...array_filter( $this->metadata, - static fn (Metadata $metadata): bool => $metadata->isCovers(), + static fn (Metadata $metadata): bool => $metadata->isCoversNamespace(), ), ); } @@ -180,12 +188,32 @@ public function isCoversClass(): self ); } - public function isCoversDefaultClass(): self + public function isCoversClassesThatExtendClass(): self { return new self( ...array_filter( $this->metadata, - static fn (Metadata $metadata): bool => $metadata->isCoversDefaultClass(), + static fn (Metadata $metadata): bool => $metadata->isCoversClassesThatExtendClass(), + ), + ); + } + + public function isCoversClassesThatImplementInterface(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isCoversClassesThatImplementInterface(), + ), + ); + } + + public function isCoversTrait(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isCoversTrait(), ), ); } @@ -200,6 +228,16 @@ public function isCoversFunction(): self ); } + public function isCoversMethod(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isCoversMethod(), + ), + ); + } + public function isExcludeGlobalVariableFromBackup(): self { return new self( @@ -270,6 +308,16 @@ public function isDependsOnMethod(): self ); } + public function isDisableReturnValueGenerationForTestDoubles(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isDisableReturnValueGenerationForTestDoubles(), + ), + ); + } + public function isDoesNotPerformAssertions(): self { return new self( @@ -453,6 +501,36 @@ public function isRequiresPhpunit(): self ); } + public function isRequiresPhpunitExtension(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isRequiresPhpunitExtension(), + ), + ); + } + + public function isRequiresEnvironmentVariable(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isRequiresEnvironmentVariable(), + ), + ); + } + + public function isWithEnvironmentVariable(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isWithEnvironmentVariable(), + ), + ); + } + public function isRequiresSetting(): self { return new self( @@ -483,12 +561,12 @@ public function isTestWith(): self ); } - public function isUses(): self + public function isUsesNamespace(): self { return new self( ...array_filter( $this->metadata, - static fn (Metadata $metadata): bool => $metadata->isUses(), + static fn (Metadata $metadata): bool => $metadata->isUsesNamespace(), ), ); } @@ -503,12 +581,32 @@ public function isUsesClass(): self ); } - public function isUsesDefaultClass(): self + public function isUsesClassesThatExtendClass(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isUsesClassesThatExtendClass(), + ), + ); + } + + public function isUsesClassesThatImplementInterface(): self { return new self( ...array_filter( $this->metadata, - static fn (Metadata $metadata): bool => $metadata->isUsesDefaultClass(), + static fn (Metadata $metadata): bool => $metadata->isUsesClassesThatImplementInterface(), + ), + ); + } + + public function isUsesTrait(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isUsesTrait(), ), ); } @@ -523,6 +621,16 @@ public function isUsesFunction(): self ); } + public function isUsesMethod(): self + { + return new self( + ...array_filter( + $this->metadata, + static fn (Metadata $metadata): bool => $metadata->isUsesMethod(), + ), + ); + } + public function isWithoutErrorHandler(): self { return new self( diff --git a/src/Metadata/MetadataCollectionIterator.php b/src/Metadata/MetadataCollectionIterator.php index 4d4aa57fafc..bfe398999e3 100644 --- a/src/Metadata/MetadataCollectionIterator.php +++ b/src/Metadata/MetadataCollectionIterator.php @@ -20,7 +20,7 @@ final class MetadataCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $metadata; private int $position = 0; diff --git a/src/Metadata/Parser/Annotation/DocBlock.php b/src/Metadata/Parser/Annotation/DocBlock.php deleted file mode 100644 index b2bcc4358e6..00000000000 --- a/src/Metadata/Parser/Annotation/DocBlock.php +++ /dev/null @@ -1,263 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Annotation\Parser; - -use function array_filter; -use function array_map; -use function array_merge; -use function array_values; -use function count; -use function preg_match; -use function preg_match_all; -use function preg_replace; -use function preg_split; -use function realpath; -use function substr; -use function trim; -use PharIo\Version\Exception as PharIoVersionException; -use PharIo\Version\VersionConstraintParser; -use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException; -use PHPUnit\Metadata\InvalidVersionRequirementException; -use ReflectionClass; -use ReflectionFunctionAbstract; -use ReflectionMethod; - -/** - * This is an abstraction around a PHPUnit-specific docBlock, - * allowing us to ask meaningful questions about a specific - * reflection symbol. - * - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DocBlock -{ - private const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; - private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\t \-.|~^]+)[ \t]*\r?$/m'; - private const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m'; - private const REGEX_REQUIRES_SETTING = '/@requires\s+(?Psetting)\s+(?P([^ ]+?))\s*(?P[\w\.-]+[\w\.]?)?[ \t]*\r?$/m'; - private const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^\s<>=!]+))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; - private readonly string $docComment; - - /** - * @psalm-var array> pre-parsed annotations indexed by name and occurrence index - */ - private readonly array $symbolAnnotations; - - /** - * @psalm-var null|(array{ - * __OFFSET: array&array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * >) - */ - private ?array $parsedRequirements = null; - private readonly int $startLine; - private readonly string $fileName; - - /** - * @throws AnnotationsAreNotSupportedForInternalClassesException - */ - public static function ofClass(ReflectionClass $class): self - { - if ($class->isInternal()) { - throw new AnnotationsAreNotSupportedForInternalClassesException($class->getName()); - } - - return new self( - (string) $class->getDocComment(), - self::extractAnnotationsFromReflector($class), - $class->getStartLine(), - $class->getFileName(), - ); - } - - /** - * @throws AnnotationsAreNotSupportedForInternalClassesException - */ - public static function ofMethod(ReflectionMethod $method): self - { - if ($method->getDeclaringClass()->isInternal()) { - throw new AnnotationsAreNotSupportedForInternalClassesException($method->getDeclaringClass()->getName()); - } - - return new self( - (string) $method->getDocComment(), - self::extractAnnotationsFromReflector($method), - $method->getStartLine(), - $method->getFileName(), - ); - } - - /** - * Note: we do not preserve an instance of the reflection object, since it cannot be safely (de-)serialized. - * - * @param array> $symbolAnnotations - */ - private function __construct(string $docComment, array $symbolAnnotations, int $startLine, string $fileName) - { - $this->docComment = $docComment; - $this->symbolAnnotations = $symbolAnnotations; - $this->startLine = $startLine; - $this->fileName = $fileName; - } - - /** - * @psalm-return array{ - * __OFFSET: array&array{__FILE: string}, - * setting?: array, - * extension_versions?: array - * }&array< - * string, - * string|array{version: string, operator: string}|array{constraint: string}|array - * > - */ - public function requirements(): array - { - if ($this->parsedRequirements !== null) { - return $this->parsedRequirements; - } - - $offset = $this->startLine; - $requires = []; - $recordedSettings = []; - $extensionVersions = []; - $recordedOffsets = [ - '__FILE' => realpath($this->fileName), - ]; - - // Trim docblock markers, split it into lines and rewind offset to start of docblock - $lines = preg_replace(['#^/\*{2}#', '#\*/$#'], '', preg_split('/\r\n|\r|\n/', $this->docComment)); - $offset -= count($lines); - - foreach ($lines as $line) { - if (preg_match(self::REGEX_REQUIRES_OS, $line, $matches)) { - $requires[$matches['name']] = $matches['value']; - $recordedOffsets[$matches['name']] = $offset; - } - - if (preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) { - $requires[$matches['name']] = [ - 'version' => $matches['version'], - 'operator' => $matches['operator'], - ]; - - $recordedOffsets[$matches['name']] = $offset; - } - - if (preg_match(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $line, $matches)) { - if (!empty($requires[$matches['name']])) { - $offset++; - - continue; - } - - try { - $versionConstraintParser = new VersionConstraintParser; - - $requires[$matches['name'] . '_constraint'] = [ - 'constraint' => $versionConstraintParser->parse(trim($matches['constraint'])), - ]; - - $recordedOffsets[$matches['name'] . '_constraint'] = $offset; - } catch (PharIoVersionException $e) { - throw new InvalidVersionRequirementException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - } - - if (preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) { - $recordedSettings[$matches['setting']] = $matches['value']; - $recordedOffsets['__SETTING_' . $matches['setting']] = $offset; - } - - if (preg_match(self::REGEX_REQUIRES, $line, $matches)) { - $name = $matches['name'] . 's'; - - if (!isset($requires[$name])) { - $requires[$name] = []; - } - - $requires[$name][] = $matches['value']; - $recordedOffsets[$matches['name'] . '_' . $matches['value']] = $offset; - - if ($name === 'extensions' && !empty($matches['version'])) { - $extensionVersions[$matches['value']] = [ - 'version' => $matches['version'], - 'operator' => $matches['operator'], - ]; - } - } - - $offset++; - } - - return $this->parsedRequirements = array_merge( - $requires, - ['__OFFSET' => $recordedOffsets], - array_filter( - [ - 'setting' => $recordedSettings, - 'extension_versions' => $extensionVersions, - ], - ), - ); - } - - public function symbolAnnotations(): array - { - return $this->symbolAnnotations; - } - - /** - * @psalm-return array> - */ - private static function parseDocBlock(string $docBlock): array - { - // Strip away the docblock header and footer to ease parsing of one line annotations - $docBlock = substr($docBlock, 3, -2); - $annotations = []; - - if (preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docBlock, $matches)) { - $numMatches = count($matches[0]); - - for ($i = 0; $i < $numMatches; $i++) { - $annotations[$matches['name'][$i]][] = $matches['value'][$i]; - } - } - - return $annotations; - } - - private static function extractAnnotationsFromReflector(ReflectionClass|ReflectionFunctionAbstract $reflector): array - { - $annotations = []; - - if ($reflector instanceof ReflectionClass) { - $annotations = array_merge( - $annotations, - ...array_map( - static fn (ReflectionClass $trait): array => self::parseDocBlock((string) $trait->getDocComment()), - array_values($reflector->getTraits()), - ), - ); - } - - return array_merge( - $annotations, - self::parseDocBlock((string) $reflector->getDocComment()), - ); - } -} diff --git a/src/Metadata/Parser/Annotation/Registry.php b/src/Metadata/Parser/Annotation/Registry.php deleted file mode 100644 index 53a69daefe8..00000000000 --- a/src/Metadata/Parser/Annotation/Registry.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Annotation\Parser; - -use function array_key_exists; -use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException; -use PHPUnit\Metadata\ReflectionException; -use ReflectionClass; -use ReflectionMethod; - -/** - * Reflection information, and therefore DocBlock information, is static within - * a single PHP process. It is therefore okay to use a Singleton registry here. - * - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Registry -{ - private static ?Registry $instance = null; - - /** - * @psalm-var array indexed by class name - */ - private array $classDocBlocks = []; - - /** - * @psalm-var array> indexed by class name and method name - */ - private array $methodDocBlocks = []; - - public static function getInstance(): self - { - return self::$instance ?? self::$instance = new self; - } - - private function __construct() - { - } - - /** - * @psalm-param class-string $class - * - * @throws AnnotationsAreNotSupportedForInternalClassesException - * @throws ReflectionException - */ - public function forClassName(string $class): DocBlock - { - if (array_key_exists($class, $this->classDocBlocks)) { - return $this->classDocBlocks[$class]; - } - - try { - $reflection = new ReflectionClass($class); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd - - return $this->classDocBlocks[$class] = DocBlock::ofClass($reflection); - } - - /** - * @psalm-param class-string $classInHierarchy - * - * @throws AnnotationsAreNotSupportedForInternalClassesException - * @throws ReflectionException - */ - public function forMethod(string $classInHierarchy, string $method): DocBlock - { - if (isset($this->methodDocBlocks[$classInHierarchy][$method])) { - return $this->methodDocBlocks[$classInHierarchy][$method]; - } - - try { - $reflection = new ReflectionMethod($classInHierarchy, $method); - // @codeCoverageIgnoreStart - } catch (\ReflectionException $e) { - throw new ReflectionException( - $e->getMessage(), - $e->getCode(), - $e, - ); - } - // @codeCoverageIgnoreEnd - - return $this->methodDocBlocks[$classInHierarchy][$method] = DocBlock::ofMethod($reflection); - } -} diff --git a/src/Metadata/Parser/AnnotationParser.php b/src/Metadata/Parser/AnnotationParser.php deleted file mode 100644 index 040b8a8a40d..00000000000 --- a/src/Metadata/Parser/AnnotationParser.php +++ /dev/null @@ -1,575 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use function array_merge; -use function assert; -use function count; -use function explode; -use function method_exists; -use function preg_replace; -use function rtrim; -use function sprintf; -use function str_contains; -use function str_starts_with; -use function strlen; -use function substr; -use function trim; -use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Metadata\Annotation\Parser\Registry as AnnotationRegistry; -use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException; -use PHPUnit\Metadata\Metadata; -use PHPUnit\Metadata\MetadataCollection; -use PHPUnit\Metadata\ReflectionException; -use PHPUnit\Metadata\Version\ComparisonRequirement; -use PHPUnit\Metadata\Version\ConstraintRequirement; -use PHPUnit\Util\InvalidVersionOperatorException; -use PHPUnit\Util\VersionComparisonOperator; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class AnnotationParser implements Parser -{ - /** - * @psalm-var array - */ - private static array $deprecationEmittedForClass = []; - - /** - * @psalm-var array - */ - private static array $deprecationEmittedForMethod = []; - - /** - * @psalm-param class-string $className - * - * @throws AnnotationsAreNotSupportedForInternalClassesException - * @throws InvalidVersionOperatorException - * @throws ReflectionException - */ - public function forClass(string $className): MetadataCollection - { - $result = []; - - foreach (AnnotationRegistry::getInstance()->forClassName($className)->symbolAnnotations() as $annotation => $values) { - switch ($annotation) { - case 'backupGlobals': - $result[] = Metadata::backupGlobalsOnClass($this->stringToBool($values[0])); - - break; - - case 'backupStaticAttributes': - case 'backupStaticProperties': - $result[] = Metadata::backupStaticPropertiesOnClass($this->stringToBool($values[0])); - - break; - - case 'covers': - foreach ($values as $value) { - $value = $this->cleanUpCoversOrUsesTarget($value); - - $result[] = Metadata::coversOnClass($value); - } - - break; - - case 'coversDefaultClass': - foreach ($values as $value) { - $result[] = Metadata::coversDefaultClass($value); - } - - break; - - case 'coversNothing': - $result[] = Metadata::coversNothingOnClass(); - - break; - - case 'doesNotPerformAssertions': - $result[] = Metadata::doesNotPerformAssertionsOnClass(); - - break; - - case 'group': - case 'ticket': - foreach ($values as $value) { - $result[] = Metadata::groupOnClass($value); - } - - break; - - case 'large': - $result[] = Metadata::groupOnClass('large'); - - break; - - case 'medium': - $result[] = Metadata::groupOnClass('medium'); - - break; - - case 'preserveGlobalState': - $result[] = Metadata::preserveGlobalStateOnClass($this->stringToBool($values[0])); - - break; - - case 'runClassInSeparateProcess': - $result[] = Metadata::runClassInSeparateProcess(); - - break; - - case 'runTestsInSeparateProcesses': - $result[] = Metadata::runTestsInSeparateProcesses(); - - break; - - case 'small': - $result[] = Metadata::groupOnClass('small'); - - break; - - case 'testdox': - $result[] = Metadata::testDoxOnClass($values[0]); - - break; - - case 'uses': - foreach ($values as $value) { - $value = $this->cleanUpCoversOrUsesTarget($value); - - $result[] = Metadata::usesOnClass($value); - } - - break; - - case 'usesDefaultClass': - foreach ($values as $value) { - $result[] = Metadata::usesDefaultClass($value); - } - - break; - } - } - - $result = array_merge( - $result, - $this->parseRequirements( - AnnotationRegistry::getInstance()->forClassName($className)->requirements(), - 'class', - ), - ); - - if (!empty($result) && - !isset(self::$deprecationEmittedForClass[$className]) && - !str_starts_with($className, 'PHPUnit\TestFixture')) { - EventFacade::emitter()->testRunnerTriggeredDeprecation( - sprintf( - 'Metadata found in doc-comment for class %s. Metadata in doc-comments is deprecated and will no longer be supported in PHPUnit 12. Update your test code to use attributes instead.', - $className, - ), - ); - - self::$deprecationEmittedForClass[$className] = true; - } - - return MetadataCollection::fromArray($result); - } - - /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * - * @throws AnnotationsAreNotSupportedForInternalClassesException - * @throws InvalidVersionOperatorException - * @throws ReflectionException - */ - public function forMethod(string $className, string $methodName): MetadataCollection - { - $result = []; - - foreach (AnnotationRegistry::getInstance()->forMethod($className, $methodName)->symbolAnnotations() as $annotation => $values) { - switch ($annotation) { - case 'after': - $result[] = Metadata::after(); - - break; - - case 'afterClass': - $result[] = Metadata::afterClass(); - - break; - - case 'backupGlobals': - $result[] = Metadata::backupGlobalsOnMethod($this->stringToBool($values[0])); - - break; - - case 'backupStaticAttributes': - case 'backupStaticProperties': - $result[] = Metadata::backupStaticPropertiesOnMethod($this->stringToBool($values[0])); - - break; - - case 'before': - $result[] = Metadata::before(); - - break; - - case 'beforeClass': - $result[] = Metadata::beforeClass(); - - break; - - case 'covers': - foreach ($values as $value) { - $value = $this->cleanUpCoversOrUsesTarget($value); - - $result[] = Metadata::coversOnMethod($value); - } - - break; - - case 'coversNothing': - $result[] = Metadata::coversNothingOnMethod(); - - break; - - case 'dataProvider': - foreach ($values as $value) { - $value = rtrim($value, " ()\n\r\t\v\x00"); - - if (str_contains($value, '::')) { - $result[] = Metadata::dataProvider(...explode('::', $value)); - - continue; - } - - $result[] = Metadata::dataProvider($className, $value); - } - - break; - - case 'depends': - foreach ($values as $value) { - $deepClone = false; - $shallowClone = false; - - if (str_starts_with($value, 'clone ')) { - $deepClone = true; - $value = substr($value, strlen('clone ')); - } elseif (str_starts_with($value, '!clone ')) { - $value = substr($value, strlen('!clone ')); - } elseif (str_starts_with($value, 'shallowClone ')) { - $shallowClone = true; - $value = substr($value, strlen('shallowClone ')); - } elseif (str_starts_with($value, '!shallowClone ')) { - $value = substr($value, strlen('!shallowClone ')); - } - - if (str_contains($value, '::')) { - [$_className, $_methodName] = explode('::', $value); - - assert($_className !== ''); - assert($_methodName !== ''); - - if ($_methodName === 'class') { - $result[] = Metadata::dependsOnClass($_className, $deepClone, $shallowClone); - - continue; - } - - $result[] = Metadata::dependsOnMethod($_className, $_methodName, $deepClone, $shallowClone); - - continue; - } - - $result[] = Metadata::dependsOnMethod($className, $value, $deepClone, $shallowClone); - } - - break; - - case 'doesNotPerformAssertions': - $result[] = Metadata::doesNotPerformAssertionsOnMethod(); - - break; - - case 'excludeGlobalVariableFromBackup': - foreach ($values as $value) { - $result[] = Metadata::excludeGlobalVariableFromBackupOnMethod($value); - } - - break; - - case 'excludeStaticPropertyFromBackup': - foreach ($values as $value) { - $tmp = explode(' ', $value); - - if (count($tmp) !== 2) { - continue; - } - - $result[] = Metadata::excludeStaticPropertyFromBackupOnMethod( - trim($tmp[0]), - trim($tmp[1]), - ); - } - - break; - - case 'group': - case 'ticket': - foreach ($values as $value) { - $result[] = Metadata::groupOnMethod($value); - } - - break; - - case 'large': - $result[] = Metadata::groupOnMethod('large'); - - break; - - case 'medium': - $result[] = Metadata::groupOnMethod('medium'); - - break; - - case 'postCondition': - $result[] = Metadata::postCondition(); - - break; - - case 'preCondition': - $result[] = Metadata::preCondition(); - - break; - - case 'preserveGlobalState': - $result[] = Metadata::preserveGlobalStateOnMethod($this->stringToBool($values[0])); - - break; - - case 'runInSeparateProcess': - $result[] = Metadata::runInSeparateProcess(); - - break; - - case 'small': - $result[] = Metadata::groupOnMethod('small'); - - break; - - case 'test': - $result[] = Metadata::test(); - - break; - - case 'testdox': - $result[] = Metadata::testDoxOnMethod($values[0]); - - break; - - case 'uses': - foreach ($values as $value) { - $value = $this->cleanUpCoversOrUsesTarget($value); - - $result[] = Metadata::usesOnMethod($value); - } - - break; - } - } - - if (method_exists($className, $methodName)) { - $result = array_merge( - $result, - $this->parseRequirements( - AnnotationRegistry::getInstance()->forMethod($className, $methodName)->requirements(), - 'method', - ), - ); - } - - if (!empty($result) && - !isset(self::$deprecationEmittedForMethod[$className . '::' . $methodName]) && - !str_starts_with($className, 'PHPUnit\TestFixture')) { - EventFacade::emitter()->testRunnerTriggeredDeprecation( - sprintf( - 'Metadata found in doc-comment for method %s::%s(). Metadata in doc-comments is deprecated and will no longer be supported in PHPUnit 12. Update your test code to use attributes instead.', - $className, - $methodName, - ), - ); - - self::$deprecationEmittedForMethod[$className . '::' . $methodName] = true; - } - - return MetadataCollection::fromArray($result); - } - - /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - * - * @throws AnnotationsAreNotSupportedForInternalClassesException - * @throws InvalidVersionOperatorException - * @throws ReflectionException - */ - public function forClassAndMethod(string $className, string $methodName): MetadataCollection - { - return $this->forClass($className)->mergeWith( - $this->forMethod($className, $methodName), - ); - } - - private function stringToBool(string $value): bool - { - if ($value === 'enabled') { - return true; - } - - return false; - } - - private function cleanUpCoversOrUsesTarget(string $value): string - { - $value = preg_replace('/[\s()]+$/', '', $value); - - return explode(' ', $value, 2)[0]; - } - - /** - * @psalm-return list - * - * @throws InvalidVersionOperatorException - */ - private function parseRequirements(array $requirements, string $level): array - { - $result = []; - - if (!empty($requirements['PHP'])) { - $versionRequirement = new ComparisonRequirement( - $requirements['PHP']['version'], - new VersionComparisonOperator(empty($requirements['PHP']['operator']) ? '>=' : $requirements['PHP']['operator']), - ); - - if ($level === 'class') { - $result[] = Metadata::requiresPhpOnClass($versionRequirement); - } else { - $result[] = Metadata::requiresPhpOnMethod($versionRequirement); - } - } elseif (!empty($requirements['PHP_constraint'])) { - $versionRequirement = new ConstraintRequirement($requirements['PHP_constraint']['constraint']); - - if ($level === 'class') { - $result[] = Metadata::requiresPhpOnClass($versionRequirement); - } else { - $result[] = Metadata::requiresPhpOnMethod($versionRequirement); - } - } - - if (!empty($requirements['extensions'])) { - foreach ($requirements['extensions'] as $extension) { - if (isset($requirements['extension_versions'][$extension])) { - continue; - } - - if ($level === 'class') { - $result[] = Metadata::requiresPhpExtensionOnClass($extension, null); - } else { - $result[] = Metadata::requiresPhpExtensionOnMethod($extension, null); - } - } - } - - if (!empty($requirements['extension_versions'])) { - foreach ($requirements['extension_versions'] as $extension => $version) { - $versionRequirement = new ComparisonRequirement( - $version['version'], - new VersionComparisonOperator(empty($version['operator']) ? '>=' : $version['operator']), - ); - - if ($level === 'class') { - $result[] = Metadata::requiresPhpExtensionOnClass($extension, $versionRequirement); - } else { - $result[] = Metadata::requiresPhpExtensionOnMethod($extension, $versionRequirement); - } - } - } - - if (!empty($requirements['PHPUnit'])) { - $versionRequirement = new ComparisonRequirement( - $requirements['PHPUnit']['version'], - new VersionComparisonOperator(empty($requirements['PHPUnit']['operator']) ? '>=' : $requirements['PHPUnit']['operator']), - ); - - if ($level === 'class') { - $result[] = Metadata::requiresPhpunitOnClass($versionRequirement); - } else { - $result[] = Metadata::requiresPhpunitOnMethod($versionRequirement); - } - } elseif (!empty($requirements['PHPUnit_constraint'])) { - $versionRequirement = new ConstraintRequirement($requirements['PHPUnit_constraint']['constraint']); - - if ($level === 'class') { - $result[] = Metadata::requiresPhpunitOnClass($versionRequirement); - } else { - $result[] = Metadata::requiresPhpunitOnMethod($versionRequirement); - } - } - - if (!empty($requirements['OSFAMILY'])) { - if ($level === 'class') { - $result[] = Metadata::requiresOperatingSystemFamilyOnClass($requirements['OSFAMILY']); - } else { - $result[] = Metadata::requiresOperatingSystemFamilyOnMethod($requirements['OSFAMILY']); - } - } - - if (!empty($requirements['OS'])) { - if ($level === 'class') { - $result[] = Metadata::requiresOperatingSystemOnClass($requirements['OS']); - } else { - $result[] = Metadata::requiresOperatingSystemOnMethod($requirements['OS']); - } - } - - if (!empty($requirements['functions'])) { - foreach ($requirements['functions'] as $function) { - $pieces = explode('::', $function); - - if (count($pieces) === 2) { - if ($level === 'class') { - $result[] = Metadata::requiresMethodOnClass($pieces[0], $pieces[1]); - } else { - $result[] = Metadata::requiresMethodOnMethod($pieces[0], $pieces[1]); - } - } elseif ($level === 'class') { - $result[] = Metadata::requiresFunctionOnClass($function); - } else { - $result[] = Metadata::requiresFunctionOnMethod($function); - } - } - } - - if (!empty($requirements['setting'])) { - foreach ($requirements['setting'] as $setting => $value) { - if ($level === 'class') { - $result[] = Metadata::requiresSettingOnClass($setting, $value); - } else { - $result[] = Metadata::requiresSettingOnMethod($setting, $value); - } - } - } - - return $result; - } -} diff --git a/src/Metadata/Parser/AttributeParser.php b/src/Metadata/Parser/AttributeParser.php index 82ddea9dfa9..971122d8086 100644 --- a/src/Metadata/Parser/AttributeParser.php +++ b/src/Metadata/Parser/AttributeParser.php @@ -11,8 +11,15 @@ use const JSON_THROW_ON_ERROR; use function assert; +use function class_exists; use function json_decode; +use function method_exists; +use function sprintf; use function str_starts_with; +use function strtolower; +use function trim; +use Error; +use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Framework\Attributes\After; use PHPUnit\Framework\Attributes\AfterClass; use PHPUnit\Framework\Attributes\BackupGlobals; @@ -20,8 +27,13 @@ use PHPUnit\Framework\Attributes\Before; use PHPUnit\Framework\Attributes\BeforeClass; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\CoversClassesThatImplementInterface; use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\CoversNamespace; use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\CoversTrait; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\Attributes\Depends; @@ -33,6 +45,7 @@ use PHPUnit\Framework\Attributes\DependsOnClassUsingShallowClone; use PHPUnit\Framework\Attributes\DependsUsingDeepClone; use PHPUnit\Framework\Attributes\DependsUsingShallowClone; +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\Attributes\ExcludeGlobalVariableFromBackup; use PHPUnit\Framework\Attributes\ExcludeStaticPropertyFromBackup; @@ -44,6 +57,7 @@ use PHPUnit\Framework\Attributes\PostCondition; use PHPUnit\Framework\Attributes\PreCondition; use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable; use PHPUnit\Framework\Attributes\RequiresFunction; use PHPUnit\Framework\Attributes\RequiresMethod; use PHPUnit\Framework\Attributes\RequiresOperatingSystem; @@ -51,6 +65,7 @@ use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RequiresPhpunitExtension; use PHPUnit\Framework\Attributes\RequiresSetting; use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; use PHPUnit\Framework\Attributes\RunInSeparateProcess; @@ -62,8 +77,15 @@ use PHPUnit\Framework\Attributes\TestWithJson; use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\Attributes\UsesClassesThatExtendClass; +use PHPUnit\Framework\Attributes\UsesClassesThatImplementInterface; use PHPUnit\Framework\Attributes\UsesFunction; +use PHPUnit\Framework\Attributes\UsesMethod; +use PHPUnit\Framework\Attributes\UsesNamespace; +use PHPUnit\Framework\Attributes\UsesTrait; +use PHPUnit\Framework\Attributes\WithEnvironmentVariable; use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use PHPUnit\Metadata\InvalidAttributeException; use PHPUnit\Metadata\Metadata; use PHPUnit\Metadata\MetadataCollection; use PHPUnit\Metadata\Version\ConstraintRequirement; @@ -71,23 +93,42 @@ use ReflectionMethod; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class AttributeParser implements Parser +final readonly class AttributeParser implements Parser { /** - * @psalm-param class-string $className + * @param class-string $className */ public function forClass(string $className): MetadataCollection { - $result = []; + assert(class_exists($className)); + + $reflector = new ReflectionClass($className); + $result = []; - foreach ((new ReflectionClass($className))->getAttributes() as $attribute) { + foreach ($reflector->getAttributes() as $attribute) { if (!str_starts_with($attribute->getName(), 'PHPUnit\\Framework\\Attributes\\')) { continue; } - $attributeInstance = $attribute->newInstance(); + if (!class_exists($attribute->getName())) { + continue; + } + + try { + $attributeInstance = $attribute->newInstance(); + } catch (Error $e) { + throw new InvalidAttributeException( + $attribute->getName(), + 'class ' . $className, + $reflector->getFileName(), + $reflector->getStartLine(), + $e->getMessage(), + ); + } switch ($attribute->getName()) { case BackupGlobals::class: @@ -104,6 +145,13 @@ public function forClass(string $className): MetadataCollection break; + case CoversNamespace::class: + assert($attributeInstance instanceof CoversNamespace); + + $result[] = Metadata::coversNamespace($attributeInstance->namespace()); + + break; + case CoversClass::class: assert($attributeInstance instanceof CoversClass); @@ -111,6 +159,27 @@ public function forClass(string $className): MetadataCollection break; + case CoversClassesThatExtendClass::class: + assert($attributeInstance instanceof CoversClassesThatExtendClass); + + $result[] = Metadata::coversClassesThatExtendClass($attributeInstance->className()); + + break; + + case CoversClassesThatImplementInterface::class: + assert($attributeInstance instanceof CoversClassesThatImplementInterface); + + $result[] = Metadata::coversClassesThatImplementInterface($attributeInstance->interfaceName()); + + break; + + case CoversTrait::class: + assert($attributeInstance instanceof CoversTrait); + + $result[] = Metadata::coversTrait($attributeInstance->traitName()); + + break; + case CoversFunction::class: assert($attributeInstance instanceof CoversFunction); @@ -118,11 +187,26 @@ public function forClass(string $className): MetadataCollection break; + case CoversMethod::class: + assert($attributeInstance instanceof CoversMethod); + + $result[] = Metadata::coversMethod( + $attributeInstance->className(), + $attributeInstance->methodName(), + ); + + break; + case CoversNothing::class: $result[] = Metadata::coversNothingOnClass(); break; + case DisableReturnValueGenerationForTestDoubles::class: + $result[] = Metadata::disableReturnValueGenerationForTestDoubles(); + + break; + case DoesNotPerformAssertions::class: $result[] = Metadata::doesNotPerformAssertionsOnClass(); @@ -148,7 +232,9 @@ public function forClass(string $className): MetadataCollection case Group::class: assert($attributeInstance instanceof Group); - $result[] = Metadata::groupOnClass($attributeInstance->name()); + if (!$this->isSizeGroup($attributeInstance->name(), $className)) { + $result[] = Metadata::groupOnClass($attributeInstance->name()); + } break; @@ -253,6 +339,35 @@ public function forClass(string $className): MetadataCollection break; + case RequiresPhpunitExtension::class: + assert($attributeInstance instanceof RequiresPhpunitExtension); + + $result[] = Metadata::requiresPhpunitExtensionOnClass( + $attributeInstance->extensionClass(), + ); + + break; + + case RequiresEnvironmentVariable::class: + assert($attributeInstance instanceof RequiresEnvironmentVariable); + + $result[] = Metadata::requiresEnvironmentVariableOnClass( + $attributeInstance->environmentVariableName(), + $attributeInstance->value(), + ); + + break; + + case WithEnvironmentVariable::class: + assert($attributeInstance instanceof WithEnvironmentVariable); + + $result[] = Metadata::withEnvironmentVariableOnClass( + $attributeInstance->environmentVariableName(), + $attributeInstance->value(), + ); + + break; + case RequiresSetting::class: assert($attributeInstance instanceof RequiresSetting); @@ -292,6 +407,13 @@ public function forClass(string $className): MetadataCollection break; + case UsesNamespace::class: + assert($attributeInstance instanceof UsesNamespace); + + $result[] = Metadata::usesNamespace($attributeInstance->namespace()); + + break; + case UsesClass::class: assert($attributeInstance instanceof UsesClass); @@ -299,11 +421,42 @@ public function forClass(string $className): MetadataCollection break; + case UsesClassesThatExtendClass::class: + assert($attributeInstance instanceof UsesClassesThatExtendClass); + + $result[] = Metadata::usesClassesThatExtendClass($attributeInstance->className()); + + break; + + case UsesClassesThatImplementInterface::class: + assert($attributeInstance instanceof UsesClassesThatImplementInterface); + + $result[] = Metadata::usesClassesThatImplementInterface($attributeInstance->interfaceName()); + + break; + + case UsesTrait::class: + assert($attributeInstance instanceof UsesTrait); + + $result[] = Metadata::usesTrait($attributeInstance->traitName()); + + break; + case UsesFunction::class: assert($attributeInstance instanceof UsesFunction); $result[] = Metadata::usesFunction($attributeInstance->functionName()); + break; + + case UsesMethod::class: + assert($attributeInstance instanceof UsesMethod); + + $result[] = Metadata::usesMethod( + $attributeInstance->className(), + $attributeInstance->methodName(), + ); + break; } } @@ -312,28 +465,50 @@ public function forClass(string $className): MetadataCollection } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function forMethod(string $className, string $methodName): MetadataCollection { - $result = []; + assert(class_exists($className)); + assert(method_exists($className, $methodName)); - foreach ((new ReflectionMethod($className, $methodName))->getAttributes() as $attribute) { + $reflector = new ReflectionMethod($className, $methodName); + $result = []; + + foreach ($reflector->getAttributes() as $attribute) { if (!str_starts_with($attribute->getName(), 'PHPUnit\\Framework\\Attributes\\')) { continue; } - $attributeInstance = $attribute->newInstance(); + if (!class_exists($attribute->getName())) { + continue; + } + + try { + $attributeInstance = $attribute->newInstance(); + } catch (Error $e) { + throw new InvalidAttributeException( + $attribute->getName(), + 'method ' . $className . '::' . $methodName . '()', + $reflector->getFileName(), + $reflector->getStartLine(), + $e->getMessage(), + ); + } switch ($attribute->getName()) { case After::class: - $result[] = Metadata::after(); + assert($attributeInstance instanceof After); + + $result[] = Metadata::after($attributeInstance->priority()); break; case AfterClass::class: - $result[] = Metadata::afterClass(); + assert($attributeInstance instanceof AfterClass); + + $result[] = Metadata::afterClass($attributeInstance->priority()); break; @@ -352,12 +527,16 @@ public function forMethod(string $className, string $methodName): MetadataCollec break; case Before::class: - $result[] = Metadata::before(); + assert($attributeInstance instanceof Before); + + $result[] = Metadata::before($attributeInstance->priority()); break; case BeforeClass::class: - $result[] = Metadata::beforeClass(); + assert($attributeInstance instanceof BeforeClass); + + $result[] = Metadata::beforeClass($attributeInstance->priority()); break; @@ -470,7 +649,9 @@ public function forMethod(string $className, string $methodName): MetadataCollec case Group::class: assert($attributeInstance instanceof Group); - $result[] = Metadata::groupOnMethod($attributeInstance->name()); + if (!$this->isSizeGroup($attributeInstance->name(), $className, $methodName)) { + $result[] = Metadata::groupOnMethod($attributeInstance->name()); + } break; @@ -489,12 +670,16 @@ public function forMethod(string $className, string $methodName): MetadataCollec break; case PostCondition::class: - $result[] = Metadata::postCondition(); + assert($attributeInstance instanceof PostCondition); + + $result[] = Metadata::postCondition($attributeInstance->priority()); break; case PreCondition::class: - $result[] = Metadata::preCondition(); + assert($attributeInstance instanceof PreCondition); + + $result[] = Metadata::preCondition($attributeInstance->priority()); break; @@ -575,6 +760,35 @@ public function forMethod(string $className, string $methodName): MetadataCollec break; + case RequiresPhpunitExtension::class: + assert($attributeInstance instanceof RequiresPhpunitExtension); + + $result[] = Metadata::requiresPhpunitExtensionOnMethod( + $attributeInstance->extensionClass(), + ); + + break; + + case RequiresEnvironmentVariable::class: + assert($attributeInstance instanceof RequiresEnvironmentVariable); + + $result[] = Metadata::requiresEnvironmentVariableOnMethod( + $attributeInstance->environmentVariableName(), + $attributeInstance->value(), + ); + + break; + + case WithEnvironmentVariable::class: + assert($attributeInstance instanceof WithEnvironmentVariable); + + $result[] = Metadata::withEnvironmentVariableOnMethod( + $attributeInstance->environmentVariableName(), + $attributeInstance->value(), + ); + + break; + case RequiresSetting::class: assert($attributeInstance instanceof RequiresSetting); @@ -605,14 +819,17 @@ public function forMethod(string $className, string $methodName): MetadataCollec case TestWith::class: assert($attributeInstance instanceof TestWith); - $result[] = Metadata::testWith($attributeInstance->data()); + $result[] = Metadata::testWith($attributeInstance->data(), $attributeInstance->name()); break; case TestWithJson::class: assert($attributeInstance instanceof TestWithJson); - $result[] = Metadata::testWith(json_decode($attributeInstance->json(), true, 512, JSON_THROW_ON_ERROR)); + $result[] = Metadata::testWith( + json_decode($attributeInstance->json(), true, 512, JSON_THROW_ON_ERROR), + $attributeInstance->name(), + ); break; @@ -636,8 +853,8 @@ public function forMethod(string $className, string $methodName): MetadataCollec } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function forClassAndMethod(string $className, string $methodName): MetadataCollection { @@ -645,4 +862,31 @@ public function forClassAndMethod(string $className, string $methodName): Metada $this->forMethod($className, $methodName), ); } + + /** + * @param non-empty-string $groupName + * @param class-string $testClassName + * @param ?non-empty-string $testMethodName + */ + private function isSizeGroup(string $groupName, string $testClassName, ?string $testMethodName = null): bool + { + $_groupName = strtolower(trim($groupName)); + + if ($_groupName !== 'small' && $_groupName !== 'medium' && $_groupName !== 'large') { + return false; + } + + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Group name "%s" is not allowed for %s %s%s%s', + $_groupName, + $testMethodName !== null ? 'method' : 'class', + $testClassName, + $testMethodName !== null ? '::' : '', + $testMethodName !== null ? $testMethodName : '', + ), + ); + + return true; + } } diff --git a/src/Metadata/Parser/CachingParser.php b/src/Metadata/Parser/CachingParser.php index ac5363b4dd4..7c274c15e14 100644 --- a/src/Metadata/Parser/CachingParser.php +++ b/src/Metadata/Parser/CachingParser.php @@ -9,16 +9,33 @@ */ namespace PHPUnit\Metadata\Parser; +use function assert; +use function class_exists; +use function method_exists; use PHPUnit\Metadata\MetadataCollection; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CachingParser implements Parser { private readonly Parser $reader; - private array $classCache = []; - private array $methodCache = []; + + /** + * @var array + */ + private array $classCache = []; + + /** + * @var array + */ + private array $methodCache = []; + + /** + * @var array + */ private array $classAndMethodCache = []; public function __construct(Parser $reader) @@ -27,10 +44,12 @@ public function __construct(Parser $reader) } /** - * @psalm-param class-string $className + * @param class-string $className */ public function forClass(string $className): MetadataCollection { + assert(class_exists($className)); + if (isset($this->classCache[$className])) { return $this->classCache[$className]; } @@ -41,11 +60,14 @@ public function forClass(string $className): MetadataCollection } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function forMethod(string $className, string $methodName): MetadataCollection { + assert(class_exists($className)); + assert(method_exists($className, $methodName)); + $key = $className . '::' . $methodName; if (isset($this->methodCache[$key])) { @@ -58,8 +80,8 @@ public function forMethod(string $className, string $methodName): MetadataCollec } /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function forClassAndMethod(string $className, string $methodName): MetadataCollection { diff --git a/src/Metadata/Parser/Parser.php b/src/Metadata/Parser/Parser.php index ea61d18f25e..edc2d87f473 100644 --- a/src/Metadata/Parser/Parser.php +++ b/src/Metadata/Parser/Parser.php @@ -12,24 +12,26 @@ use PHPUnit\Metadata\MetadataCollection; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Parser { /** - * @psalm-param class-string $className + * @param class-string $className */ public function forClass(string $className): MetadataCollection; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function forMethod(string $className, string $methodName): MetadataCollection; /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName */ public function forClassAndMethod(string $className, string $methodName): MetadataCollection; } diff --git a/src/Metadata/Parser/ParserChain.php b/src/Metadata/Parser/ParserChain.php deleted file mode 100644 index f094e9463fb..00000000000 --- a/src/Metadata/Parser/ParserChain.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use PHPUnit\Metadata\MetadataCollection; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final readonly class ParserChain implements Parser -{ - private Parser $attributeReader; - private Parser $annotationReader; - - public function __construct(Parser $attributeReader, Parser $annotationReader) - { - $this->attributeReader = $attributeReader; - $this->annotationReader = $annotationReader; - } - - /** - * @psalm-param class-string $className - */ - public function forClass(string $className): MetadataCollection - { - $metadata = $this->attributeReader->forClass($className); - - if (!$metadata->isEmpty()) { - return $metadata; - } - - return $this->annotationReader->forClass($className); - } - - /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - */ - public function forMethod(string $className, string $methodName): MetadataCollection - { - $metadata = $this->attributeReader->forMethod($className, $methodName); - - if (!$metadata->isEmpty()) { - return $metadata; - } - - return $this->annotationReader->forMethod($className, $methodName); - } - - /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - */ - public function forClassAndMethod(string $className, string $methodName): MetadataCollection - { - return $this->forClass($className)->mergeWith( - $this->forMethod($className, $methodName), - ); - } -} diff --git a/src/Metadata/Parser/Registry.php b/src/Metadata/Parser/Registry.php index 0b005cf5548..0b30782ec09 100644 --- a/src/Metadata/Parser/Registry.php +++ b/src/Metadata/Parser/Registry.php @@ -10,9 +10,11 @@ namespace PHPUnit\Metadata\Parser; /** - * Attribute and annotation information is static within a single PHP process. + * Attribute information is static within a single PHP process. * It is therefore okay to use a Singleton registry here. * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Registry @@ -24,17 +26,8 @@ public static function parser(): Parser return self::$instance ?? self::$instance = self::build(); } - private function __construct() - { - } - private static function build(): Parser { - return new CachingParser( - new ParserChain( - new AttributeParser, - new AnnotationParser, - ), - ); + return new CachingParser(new AttributeParser); } } diff --git a/src/Metadata/PostCondition.php b/src/Metadata/PostCondition.php index 1e30f550fb1..d52ae0628bd 100644 --- a/src/Metadata/PostCondition.php +++ b/src/Metadata/PostCondition.php @@ -10,17 +10,31 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class PostCondition extends Metadata +final readonly class PostCondition extends Metadata { + private int $priority; + /** - * @psalm-assert-if-true PostCondition $this + * @param int<0, 1> $level */ - public function isPostCondition(): bool + protected function __construct(int $level, int $priority) + { + parent::__construct($level); + + $this->priority = $priority; + } + + public function isPostCondition(): true { return true; } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Metadata/PreCondition.php b/src/Metadata/PreCondition.php index 4691f113d16..9243122bcb6 100644 --- a/src/Metadata/PreCondition.php +++ b/src/Metadata/PreCondition.php @@ -10,17 +10,31 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class PreCondition extends Metadata +final readonly class PreCondition extends Metadata { + private int $priority; + /** - * @psalm-assert-if-true PreCondition $this + * @param int<0, 1> $level */ - public function isPreCondition(): bool + protected function __construct(int $level, int $priority) + { + parent::__construct($level); + + $this->priority = $priority; + } + + public function isPreCondition(): true { return true; } + + public function priority(): int + { + return $this->priority; + } } diff --git a/src/Metadata/PreserveGlobalState.php b/src/Metadata/PreserveGlobalState.php index 521af6d63c9..f888119159c 100644 --- a/src/Metadata/PreserveGlobalState.php +++ b/src/Metadata/PreserveGlobalState.php @@ -10,16 +10,16 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class PreserveGlobalState extends Metadata +final readonly class PreserveGlobalState extends Metadata { - private readonly bool $enabled; + private bool $enabled; /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level */ protected function __construct(int $level, bool $enabled) { @@ -28,10 +28,7 @@ protected function __construct(int $level, bool $enabled) $this->enabled = $enabled; } - /** - * @psalm-assert-if-true PreserveGlobalState $this - */ - public function isPreserveGlobalState(): bool + public function isPreserveGlobalState(): true { return true; } diff --git a/src/Metadata/RequiresEnvironmentVariable.php b/src/Metadata/RequiresEnvironmentVariable.php new file mode 100644 index 00000000000..0888c296d33 --- /dev/null +++ b/src/Metadata/RequiresEnvironmentVariable.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class RequiresEnvironmentVariable extends Metadata +{ + private string $environmentVariableName; + private null|string $value; + + public function __construct(int $level, string $environmentVariableName, null|string $value) + { + parent::__construct($level); + + $this->environmentVariableName = $environmentVariableName; + $this->value = $value; + } + + public function isRequiresEnvironmentVariable(): true + { + return true; + } + + public function environmentVariableName(): string + { + return $this->environmentVariableName; + } + + public function value(): null|string + { + return $this->value; + } +} diff --git a/src/Metadata/RequiresFunction.php b/src/Metadata/RequiresFunction.php index 638d35c0711..f66c5180c47 100644 --- a/src/Metadata/RequiresFunction.php +++ b/src/Metadata/RequiresFunction.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresFunction extends Metadata +final readonly class RequiresFunction extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $functionName; + private string $functionName; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $functionName + * @param int<0, 1> $level + * @param non-empty-string $functionName */ protected function __construct(int $level, string $functionName) { @@ -32,16 +32,13 @@ protected function __construct(int $level, string $functionName) $this->functionName = $functionName; } - /** - * @psalm-assert-if-true RequiresFunction $this - */ - public function isRequiresFunction(): bool + public function isRequiresFunction(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function functionName(): string { diff --git a/src/Metadata/RequiresMethod.php b/src/Metadata/RequiresMethod.php index 45d611ffa88..2ad7fbb9abc 100644 --- a/src/Metadata/RequiresMethod.php +++ b/src/Metadata/RequiresMethod.php @@ -10,26 +10,26 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresMethod extends Metadata +final readonly class RequiresMethod extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $methodName; + private string $methodName; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param int<0, 1> $level + * @param class-string $className + * @param non-empty-string $methodName */ protected function __construct(int $level, string $className, string $methodName) { @@ -39,16 +39,13 @@ protected function __construct(int $level, string $className, string $methodName $this->methodName = $methodName; } - /** - * @psalm-assert-if-true RequiresMethod $this - */ - public function isRequiresMethod(): bool + public function isRequiresMethod(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { @@ -56,7 +53,7 @@ public function className(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function methodName(): string { diff --git a/src/Metadata/RequiresOperatingSystem.php b/src/Metadata/RequiresOperatingSystem.php index a2b0de4f473..8719d296120 100644 --- a/src/Metadata/RequiresOperatingSystem.php +++ b/src/Metadata/RequiresOperatingSystem.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresOperatingSystem extends Metadata +final readonly class RequiresOperatingSystem extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $operatingSystem; + private string $operatingSystem; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $operatingSystem + * @param int<0, 1> $level + * @param non-empty-string $operatingSystem */ public function __construct(int $level, string $operatingSystem) { @@ -32,16 +32,13 @@ public function __construct(int $level, string $operatingSystem) $this->operatingSystem = $operatingSystem; } - /** - * @psalm-assert-if-true RequiresOperatingSystem $this - */ - public function isRequiresOperatingSystem(): bool + public function isRequiresOperatingSystem(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function operatingSystem(): string { diff --git a/src/Metadata/RequiresOperatingSystemFamily.php b/src/Metadata/RequiresOperatingSystemFamily.php index 8418274800b..4481dcf8ce5 100644 --- a/src/Metadata/RequiresOperatingSystemFamily.php +++ b/src/Metadata/RequiresOperatingSystemFamily.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresOperatingSystemFamily extends Metadata +final readonly class RequiresOperatingSystemFamily extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $operatingSystemFamily; + private string $operatingSystemFamily; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $operatingSystemFamily + * @param int<0, 1> $level + * @param non-empty-string $operatingSystemFamily */ protected function __construct(int $level, string $operatingSystemFamily) { @@ -32,16 +32,13 @@ protected function __construct(int $level, string $operatingSystemFamily) $this->operatingSystemFamily = $operatingSystemFamily; } - /** - * @psalm-assert-if-true RequiresOperatingSystemFamily $this - */ - public function isRequiresOperatingSystemFamily(): bool + public function isRequiresOperatingSystemFamily(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function operatingSystemFamily(): string { diff --git a/src/Metadata/RequiresPhp.php b/src/Metadata/RequiresPhp.php index e73bfb40294..c64a396281e 100644 --- a/src/Metadata/RequiresPhp.php +++ b/src/Metadata/RequiresPhp.php @@ -12,16 +12,16 @@ use PHPUnit\Metadata\Version\Requirement; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresPhp extends Metadata +final readonly class RequiresPhp extends Metadata { - private readonly Requirement $versionRequirement; + private Requirement $versionRequirement; /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level */ protected function __construct(int $level, Requirement $versionRequirement) { @@ -30,10 +30,7 @@ protected function __construct(int $level, Requirement $versionRequirement) $this->versionRequirement = $versionRequirement; } - /** - * @psalm-assert-if-true RequiresPhp $this - */ - public function isRequiresPhp(): bool + public function isRequiresPhp(): true { return true; } diff --git a/src/Metadata/RequiresPhpExtension.php b/src/Metadata/RequiresPhpExtension.php index 053b89665c5..5c547ea9ec8 100644 --- a/src/Metadata/RequiresPhpExtension.php +++ b/src/Metadata/RequiresPhpExtension.php @@ -12,21 +12,21 @@ use PHPUnit\Metadata\Version\Requirement; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresPhpExtension extends Metadata +final readonly class RequiresPhpExtension extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $extension; - private readonly ?Requirement $versionRequirement; + private string $extension; + private ?Requirement $versionRequirement; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $extension + * @param int<0, 1> $level + * @param non-empty-string $extension */ protected function __construct(int $level, string $extension, ?Requirement $versionRequirement) { @@ -36,16 +36,13 @@ protected function __construct(int $level, string $extension, ?Requirement $vers $this->versionRequirement = $versionRequirement; } - /** - * @psalm-assert-if-true RequiresPhpExtension $this - */ - public function isRequiresPhpExtension(): bool + public function isRequiresPhpExtension(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function extension(): string { @@ -53,7 +50,7 @@ public function extension(): string } /** - * @psalm-assert-if-true !null $this->versionRequirement + * @phpstan-assert-if-true !null $this->versionRequirement */ public function hasVersionRequirement(): bool { diff --git a/src/Metadata/RequiresPhpunit.php b/src/Metadata/RequiresPhpunit.php index 250190e58de..dc8ae80e2b4 100644 --- a/src/Metadata/RequiresPhpunit.php +++ b/src/Metadata/RequiresPhpunit.php @@ -12,16 +12,16 @@ use PHPUnit\Metadata\Version\Requirement; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresPhpunit extends Metadata +final readonly class RequiresPhpunit extends Metadata { - private readonly Requirement $versionRequirement; + private Requirement $versionRequirement; /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level */ protected function __construct(int $level, Requirement $versionRequirement) { @@ -30,10 +30,7 @@ protected function __construct(int $level, Requirement $versionRequirement) $this->versionRequirement = $versionRequirement; } - /** - * @psalm-assert-if-true RequiresPhpunit $this - */ - public function isRequiresPhpunit(): bool + public function isRequiresPhpunit(): true { return true; } diff --git a/src/Metadata/RequiresPhpunitExtension.php b/src/Metadata/RequiresPhpunitExtension.php new file mode 100644 index 00000000000..726c3b961a8 --- /dev/null +++ b/src/Metadata/RequiresPhpunitExtension.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +use PHPUnit\Runner\Extension\Extension; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class RequiresPhpunitExtension extends Metadata +{ + /** + * @var class-string + */ + private string $extensionClass; + + /** + * @param class-string $extensionClass + */ + public function __construct(int $level, string $extensionClass) + { + parent::__construct($level); + + $this->extensionClass = $extensionClass; + } + + public function isRequiresPhpunitExtension(): true + { + return true; + } + + /** + * @return class-string + */ + public function extensionClass(): string + { + return $this->extensionClass; + } +} diff --git a/src/Metadata/RequiresSetting.php b/src/Metadata/RequiresSetting.php index 03f710d713a..0d0b0230a40 100644 --- a/src/Metadata/RequiresSetting.php +++ b/src/Metadata/RequiresSetting.php @@ -10,26 +10,26 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RequiresSetting extends Metadata +final readonly class RequiresSetting extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $setting; + private string $setting; /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $value; + private string $value; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $setting - * @psalm-param non-empty-string $value + * @param int<0, 1> $level + * @param non-empty-string $setting + * @param non-empty-string $value */ protected function __construct(int $level, string $setting, string $value) { @@ -39,16 +39,13 @@ protected function __construct(int $level, string $setting, string $value) $this->value = $value; } - /** - * @psalm-assert-if-true RequiresSetting $this - */ - public function isRequiresSetting(): bool + public function isRequiresSetting(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function setting(): string { @@ -56,7 +53,7 @@ public function setting(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function value(): string { diff --git a/src/Metadata/RunClassInSeparateProcess.php b/src/Metadata/RunClassInSeparateProcess.php index eb0b7742f60..6f7927cece6 100644 --- a/src/Metadata/RunClassInSeparateProcess.php +++ b/src/Metadata/RunClassInSeparateProcess.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RunClassInSeparateProcess extends Metadata +final readonly class RunClassInSeparateProcess extends Metadata { - /** - * @psalm-assert-if-true RunClassInSeparateProcess $this - */ - public function isRunClassInSeparateProcess(): bool + public function isRunClassInSeparateProcess(): true { return true; } diff --git a/src/Metadata/RunInSeparateProcess.php b/src/Metadata/RunInSeparateProcess.php index 62d552aece6..dc755219ce4 100644 --- a/src/Metadata/RunInSeparateProcess.php +++ b/src/Metadata/RunInSeparateProcess.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RunInSeparateProcess extends Metadata +final readonly class RunInSeparateProcess extends Metadata { - /** - * @psalm-assert-if-true RunInSeparateProcess $this - */ - public function isRunInSeparateProcess(): bool + public function isRunInSeparateProcess(): true { return true; } diff --git a/src/Metadata/RunTestsInSeparateProcesses.php b/src/Metadata/RunTestsInSeparateProcesses.php index ea0807a5f2a..3a544a792c8 100644 --- a/src/Metadata/RunTestsInSeparateProcesses.php +++ b/src/Metadata/RunTestsInSeparateProcesses.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class RunTestsInSeparateProcesses extends Metadata +final readonly class RunTestsInSeparateProcesses extends Metadata { - /** - * @psalm-assert-if-true RunTestsInSeparateProcesses $this - */ - public function isRunTestsInSeparateProcesses(): bool + public function isRunTestsInSeparateProcesses(): true { return true; } diff --git a/src/Metadata/Test.php b/src/Metadata/Test.php index 6d2aeecdd3f..04007610a76 100644 --- a/src/Metadata/Test.php +++ b/src/Metadata/Test.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Test extends Metadata +final readonly class Test extends Metadata { - /** - * @psalm-assert-if-true Test $this - */ - public function isTest(): bool + public function isTest(): true { return true; } diff --git a/src/Metadata/TestDox.php b/src/Metadata/TestDox.php index d78d86cf5f1..6e2944e5fe9 100644 --- a/src/Metadata/TestDox.php +++ b/src/Metadata/TestDox.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestDox extends Metadata +final readonly class TestDox extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $text; + private string $text; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $text + * @param int<0, 1> $level + * @param non-empty-string $text */ protected function __construct(int $level, string $text) { @@ -32,16 +32,13 @@ protected function __construct(int $level, string $text) $this->text = $text; } - /** - * @psalm-assert-if-true TestDox $this - */ - public function isTestDox(): bool + public function isTestDox(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function text(): string { diff --git a/src/Metadata/TestWith.php b/src/Metadata/TestWith.php index 98bef73d53f..d7cd190747f 100644 --- a/src/Metadata/TestWith.php +++ b/src/Metadata/TestWith.php @@ -10,34 +10,61 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class TestWith extends Metadata +final readonly class TestWith extends Metadata { - private readonly array $data; + /** + * @var array> + */ + private array $data; + + /** + * @var ?non-empty-string + */ + private ?string $name; /** - * @psalm-param 0|1 $level + * @param int<0, 1> $level + * @param array> $data + * @param ?non-empty-string $name */ - protected function __construct(int $level, array $data) + protected function __construct(int $level, array $data, ?string $name = null) { parent::__construct($level); $this->data = $data; + $this->name = $name; } - /** - * @psalm-assert-if-true TestWith $this - */ - public function isTestWith(): bool + public function isTestWith(): true { return true; } + /** + * @return array> + */ public function data(): array { return $this->data; } + + /** + * @phpstan-assert-if-true !null $this->name + */ + public function hasName(): bool + { + return $this->name !== null; + } + + /** + * @return ?non-empty-string + */ + public function name(): ?string + { + return $this->name; + } } diff --git a/src/Metadata/Uses.php b/src/Metadata/Uses.php deleted file mode 100644 index d3fa216bb62..00000000000 --- a/src/Metadata/Uses.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class Uses extends Metadata -{ - /** - * @psalm-var non-empty-string - */ - private readonly string $target; - - /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $target - */ - protected function __construct(int $level, string $target) - { - parent::__construct($level); - - $this->target = $target; - } - - /** - * @psalm-assert-if-true Uses $this - */ - public function isUses(): bool - { - return true; - } - - /** - * @psalm-return non-empty-string - */ - public function target(): string - { - return $this->target; - } -} diff --git a/src/Metadata/UsesClass.php b/src/Metadata/UsesClass.php index 01e7741e6bd..edbd03ac59b 100644 --- a/src/Metadata/UsesClass.php +++ b/src/Metadata/UsesClass.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class UsesClass extends Metadata +final readonly class UsesClass extends Metadata { /** - * @psalm-var class-string + * @var class-string */ - private readonly string $className; + private string $className; /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className + * @param int<0, 1> $level + * @param class-string $className */ protected function __construct(int $level, string $className) { @@ -32,29 +32,16 @@ protected function __construct(int $level, string $className) $this->className = $className; } - /** - * @psalm-assert-if-true UsesClass $this - */ - public function isUsesClass(): bool + public function isUsesClass(): true { return true; } /** - * @psalm-return class-string + * @return class-string */ public function className(): string { return $this->className; } - - /** - * @psalm-return class-string - * - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function asStringForCodeUnitMapper(): string - { - return $this->className; - } } diff --git a/src/Metadata/UsesClassesThatExtendClass.php b/src/Metadata/UsesClassesThatExtendClass.php new file mode 100644 index 00000000000..baddfeb6b6f --- /dev/null +++ b/src/Metadata/UsesClassesThatExtendClass.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class UsesClassesThatExtendClass extends Metadata +{ + /** + * @var class-string + */ + private string $className; + + /** + * @param int<0, 1> $level + * @param class-string $className + */ + protected function __construct(int $level, string $className) + { + parent::__construct($level); + + $this->className = $className; + } + + public function isUsesClassesThatExtendClass(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } +} diff --git a/src/Metadata/UsesClassesThatImplementInterface.php b/src/Metadata/UsesClassesThatImplementInterface.php new file mode 100644 index 00000000000..5cdc6f629a3 --- /dev/null +++ b/src/Metadata/UsesClassesThatImplementInterface.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class UsesClassesThatImplementInterface extends Metadata +{ + /** + * @var class-string + */ + private string $interfaceName; + + /** + * @param int<0, 1> $level + * @param class-string $interfaceName + */ + protected function __construct(int $level, string $interfaceName) + { + parent::__construct($level); + + $this->interfaceName = $interfaceName; + } + + public function isUsesClassesThatImplementInterface(): true + { + return true; + } + + /** + * @return class-string + */ + public function interfaceName(): string + { + return $this->interfaceName; + } +} diff --git a/src/Metadata/UsesDefaultClass.php b/src/Metadata/UsesDefaultClass.php deleted file mode 100644 index 48a82471037..00000000000 --- a/src/Metadata/UsesDefaultClass.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -/** - * @psalm-immutable - * - * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit - */ -final class UsesDefaultClass extends Metadata -{ - /** - * @psalm-var class-string - */ - private readonly string $className; - - /** - * @psalm-param 0|1 $level - * @psalm-param class-string $className - */ - protected function __construct(int $level, string $className) - { - parent::__construct($level); - - $this->className = $className; - } - - /** - * @psalm-assert-if-true UsesDefaultClass $this - */ - public function isUsesDefaultClass(): bool - { - return true; - } - - /** - * @psalm-return class-string - */ - public function className(): string - { - return $this->className; - } -} diff --git a/src/Metadata/UsesFunction.php b/src/Metadata/UsesFunction.php index 8c46e76bfda..ec6cafee8e4 100644 --- a/src/Metadata/UsesFunction.php +++ b/src/Metadata/UsesFunction.php @@ -10,20 +10,20 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class UsesFunction extends Metadata +final readonly class UsesFunction extends Metadata { /** - * @psalm-var non-empty-string + * @var non-empty-string */ - private readonly string $functionName; + private string $functionName; /** - * @psalm-param 0|1 $level - * @psalm-param non-empty-string $functionName + * @param int<0, 1> $level + * @param non-empty-string $functionName */ public function __construct(int $level, string $functionName) { @@ -32,27 +32,16 @@ public function __construct(int $level, string $functionName) $this->functionName = $functionName; } - /** - * @psalm-assert-if-true UsesFunction $this - */ - public function isUsesFunction(): bool + public function isUsesFunction(): true { return true; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function functionName(): string { return $this->functionName; } - - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function asStringForCodeUnitMapper(): string - { - return '::' . $this->functionName; - } } diff --git a/src/Metadata/UsesMethod.php b/src/Metadata/UsesMethod.php new file mode 100644 index 00000000000..6fc78f8f2c7 --- /dev/null +++ b/src/Metadata/UsesMethod.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class UsesMethod extends Metadata +{ + /** + * @var class-string + */ + private string $className; + + /** + * @var non-empty-string + */ + private string $methodName; + + /** + * @param int<0, 1> $level + * @param class-string $className + * @param non-empty-string $methodName + */ + protected function __construct(int $level, string $className, string $methodName) + { + parent::__construct($level); + + $this->className = $className; + $this->methodName = $methodName; + } + + public function isUsesMethod(): true + { + return true; + } + + /** + * @return class-string + */ + public function className(): string + { + return $this->className; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } +} diff --git a/src/Metadata/UsesNamespace.php b/src/Metadata/UsesNamespace.php new file mode 100644 index 00000000000..f5e5d01ecd6 --- /dev/null +++ b/src/Metadata/UsesNamespace.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class UsesNamespace extends Metadata +{ + /** + * @var non-empty-string + */ + private string $namespace; + + /** + * @param int<0, 1> $level + * @param non-empty-string $namespace + */ + protected function __construct(int $level, string $namespace) + { + parent::__construct($level); + + $this->namespace = $namespace; + } + + public function isUsesNamespace(): true + { + return true; + } + + /** + * @return class-string + */ + public function namespace(): string + { + return $this->namespace; + } +} diff --git a/src/Metadata/UsesTrait.php b/src/Metadata/UsesTrait.php new file mode 100644 index 00000000000..19490f26c2f --- /dev/null +++ b/src/Metadata/UsesTrait.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class UsesTrait extends Metadata +{ + /** + * @var trait-string + */ + private string $traitName; + + /** + * @param 0|1 $level + * @param trait-string $traitName + */ + protected function __construct(int $level, string $traitName) + { + parent::__construct($level); + + $this->traitName = $traitName; + } + + public function isUsesTrait(): true + { + return true; + } + + /** + * @return trait-string + */ + public function traitName(): string + { + return $this->traitName; + } +} diff --git a/src/Metadata/Version/ComparisonRequirement.php b/src/Metadata/Version/ComparisonRequirement.php index 86aefaf281d..b3b6a1fcadf 100644 --- a/src/Metadata/Version/ComparisonRequirement.php +++ b/src/Metadata/Version/ComparisonRequirement.php @@ -13,14 +13,14 @@ use PHPUnit\Util\VersionComparisonOperator; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ComparisonRequirement extends Requirement +final readonly class ComparisonRequirement extends Requirement { - private readonly string $version; - private readonly VersionComparisonOperator $operator; + private string $version; + private VersionComparisonOperator $operator; public function __construct(string $version, VersionComparisonOperator $operator) { diff --git a/src/Metadata/Version/ConstraintRequirement.php b/src/Metadata/Version/ConstraintRequirement.php index 32857899c5c..107544503b7 100644 --- a/src/Metadata/Version/ConstraintRequirement.php +++ b/src/Metadata/Version/ConstraintRequirement.php @@ -14,22 +14,19 @@ use PharIo\Version\VersionConstraint; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class ConstraintRequirement extends Requirement +final readonly class ConstraintRequirement extends Requirement { - private readonly VersionConstraint $constraint; + private VersionConstraint $constraint; public function __construct(VersionConstraint $constraint) { $this->constraint = $constraint; } - /** - * @psalm-suppress ImpureMethodCall - */ public function isSatisfiedBy(string $version): bool { return $this->constraint->complies( @@ -37,9 +34,6 @@ public function isSatisfiedBy(string $version): bool ); } - /** - * @psalm-suppress ImpureMethodCall - */ public function asString(): string { return $this->constraint->asString(); diff --git a/src/Metadata/Version/Requirement.php b/src/Metadata/Version/Requirement.php index a61e500306b..01f98f7310f 100644 --- a/src/Metadata/Version/Requirement.php +++ b/src/Metadata/Version/Requirement.php @@ -17,13 +17,13 @@ use PHPUnit\Util\VersionComparisonOperator; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -abstract class Requirement +abstract readonly class Requirement { - private const VERSION_COMPARISON = '/(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; + private const string VERSION_COMPARISON = "/(?P!=|<|<=|<>|=|==|>|>=)?\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m"; /** * @throws InvalidVersionOperatorException @@ -38,11 +38,11 @@ public static function from(string $versionRequirement): self ), ); } catch (UnsupportedVersionConstraintException) { - if (preg_match(self::VERSION_COMPARISON, $versionRequirement, $matches)) { + if (preg_match(self::VERSION_COMPARISON, $versionRequirement, $matches) > 0) { return new ComparisonRequirement( $matches['version'], new VersionComparisonOperator( - !empty($matches['operator']) ? $matches['operator'] : '>=', + $matches['operator'] !== '' ? $matches['operator'] : '>=', ), ); } diff --git a/src/Metadata/WithEnvironmentVariable.php b/src/Metadata/WithEnvironmentVariable.php new file mode 100644 index 00000000000..cc4d0fe7dd0 --- /dev/null +++ b/src/Metadata/WithEnvironmentVariable.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Metadata; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ +final readonly class WithEnvironmentVariable extends Metadata +{ + /** + * @var non-empty-string + */ + private string $environmentVariableName; + private null|string $value; + + /** + * @param non-empty-string $environmentVariableName + */ + public function __construct(int $level, string $environmentVariableName, null|string $value) + { + parent::__construct($level); + + $this->environmentVariableName = $environmentVariableName; + $this->value = $value; + } + + public function isWithEnvironmentVariable(): true + { + return true; + } + + /** + * @return non-empty-string + */ + public function environmentVariableName(): string + { + return $this->environmentVariableName; + } + + public function value(): null|string + { + return $this->value; + } +} diff --git a/src/Metadata/WithoutErrorHandler.php b/src/Metadata/WithoutErrorHandler.php index d10f41e680f..a8bf001475d 100644 --- a/src/Metadata/WithoutErrorHandler.php +++ b/src/Metadata/WithoutErrorHandler.php @@ -10,16 +10,13 @@ namespace PHPUnit\Metadata; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class WithoutErrorHandler extends Metadata +final readonly class WithoutErrorHandler extends Metadata { - /** - * @psalm-assert-if-true WithoutErrorHandler $this - */ - public function isWithoutErrorHandler(): bool + public function isWithoutErrorHandler(): true { return true; } diff --git a/src/Runner/BackedUpEnvironmentVariable.php b/src/Runner/BackedUpEnvironmentVariable.php new file mode 100644 index 00000000000..a62ebe95c17 --- /dev/null +++ b/src/Runner/BackedUpEnvironmentVariable.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function getenv; +use function putenv; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class BackedUpEnvironmentVariable +{ + private const string FROM_GETENV = 'getenv'; + private const string FROM_SUPERGLOBAL = 'superglobal'; + + /** + * @var self::FROM_GETENV|self::FROM_SUPERGLOBAL + */ + private string $from; + + /** + * @var non-empty-string + */ + private string $name; + private null|string $value; + + /** + * @param non-empty-string $name + * + * @return array{0: self, 1: self} + */ + public static function create(string $name): array + { + $getenv = getenv($name); + + if ($getenv === false) { + $getenv = null; + } + + return [ + new self(self::FROM_SUPERGLOBAL, $name, $_ENV[$name] ?? null), + new self(self::FROM_GETENV, $name, $getenv), + ]; + } + + /** + * @param self::FROM_GETENV|self::FROM_SUPERGLOBAL $from + * @param non-empty-string $name + */ + private function __construct(string $from, string $name, null|string $value) + { + $this->from = $from; + $this->name = $name; + $this->value = $value; + } + + public function restore(): void + { + if ($this->from === self::FROM_GETENV) { + $this->restoreGetEnv(); + } else { + $this->restoreSuperGlobal(); + } + } + + private function restoreGetEnv(): void + { + if ($this->value === null) { + putenv($this->name); + } else { + putenv("{$this->name}={$this->value}"); + } + } + + private function restoreSuperGlobal(): void + { + if ($this->value === null) { + unset($_ENV[$this->name]); + } else { + $_ENV[$this->name] = $this->value; + } + } +} diff --git a/src/Runner/Baseline/Baseline.php b/src/Runner/Baseline/Baseline.php index 4921f3188fe..cffc3791d58 100644 --- a/src/Runner/Baseline/Baseline.php +++ b/src/Runner/Baseline/Baseline.php @@ -10,14 +10,16 @@ namespace PHPUnit\Runner\Baseline; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Baseline { - public const VERSION = 1; + public const int VERSION = 1; /** - * @psalm-var array>> + * @var array>> */ private array $issues = []; @@ -50,7 +52,7 @@ public function has(Issue $issue): bool } /** - * @psalm-return array>> + * @return array>> */ public function groupedByFileAndLine(): array { diff --git a/src/Runner/Baseline/Exception/CannotLoadBaselineException.php b/src/Runner/Baseline/Exception/CannotLoadBaselineException.php index c05e803e545..c55901365e6 100644 --- a/src/Runner/Baseline/Exception/CannotLoadBaselineException.php +++ b/src/Runner/Baseline/Exception/CannotLoadBaselineException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CannotLoadBaselineException extends RuntimeException implements Exception diff --git a/src/Runner/Baseline/Exception/FileDoesNotHaveLineException.php b/src/Runner/Baseline/Exception/FileDoesNotHaveLineException.php index 1121fa3949e..20c6ca03056 100644 --- a/src/Runner/Baseline/Exception/FileDoesNotHaveLineException.php +++ b/src/Runner/Baseline/Exception/FileDoesNotHaveLineException.php @@ -14,6 +14,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class FileDoesNotHaveLineException extends RuntimeException implements Exception diff --git a/src/Runner/Baseline/Generator.php b/src/Runner/Baseline/Generator.php index 6a94baebc66..c5ce074de98 100644 --- a/src/Runner/Baseline/Generator.php +++ b/src/Runner/Baseline/Generator.php @@ -9,7 +9,6 @@ */ namespace PHPUnit\Runner\Baseline; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; use PHPUnit\Event\Test\DeprecationTriggered; use PHPUnit\Event\Test\NoticeTriggered; @@ -17,23 +16,20 @@ use PHPUnit\Event\Test\PhpNoticeTriggered; use PHPUnit\Event\Test\PhpWarningTriggered; use PHPUnit\Event\Test\WarningTriggered; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\Runner\FileDoesNotExistException; use PHPUnit\TextUI\Configuration\Source; use PHPUnit\TextUI\Configuration\SourceFilter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Generator +final readonly class Generator { private Baseline $baseline; - private readonly Source $source; + private Source $source; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(Facade $facade, Source $source) { $facade->registerSubscribers( @@ -60,11 +56,11 @@ public function baseline(): Baseline */ public function testTriggeredIssue(DeprecationTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event): void { - if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { + if ($event->wasSuppressed() && !$this->isSuppressionIgnored($event)) { return; } - if ($this->source->restrictWarnings() && !(new SourceFilter)->includes($this->source, $event->file())) { + if ($this->restrict($event) && !SourceFilter::instance()->includes($event->file())) { return; } @@ -77,4 +73,42 @@ public function testTriggeredIssue(DeprecationTriggered|NoticeTriggered|PhpDepre ), ); } + + private function restrict(DeprecationTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event): bool + { + if ($event instanceof WarningTriggered || $event instanceof PhpWarningTriggered) { + return $this->source->restrictWarnings(); + } + + if ($event instanceof NoticeTriggered || $event instanceof PhpNoticeTriggered) { + return $this->source->restrictNotices(); + } + + return false; + } + + private function isSuppressionIgnored(DeprecationTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event): bool + { + if ($event instanceof WarningTriggered) { + return $this->source->ignoreSuppressionOfWarnings(); + } + + if ($event instanceof PhpWarningTriggered) { + return $this->source->ignoreSuppressionOfPhpWarnings(); + } + + if ($event instanceof PhpNoticeTriggered) { + return $this->source->ignoreSuppressionOfPhpNotices(); + } + + if ($event instanceof NoticeTriggered) { + return $this->source->ignoreSuppressionOfNotices(); + } + + if ($event instanceof PhpDeprecationTriggered) { + return $this->source->ignoreSuppressionOfPhpDeprecations(); + } + + return $this->source->ignoreSuppressionOfDeprecations(); + } } diff --git a/src/Runner/Baseline/Issue.php b/src/Runner/Baseline/Issue.php index 22ab54f3a74..869ea26a4bc 100644 --- a/src/Runner/Baseline/Issue.php +++ b/src/Runner/Baseline/Issue.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Runner\Baseline; +use const FILE_IGNORE_NEW_LINES; use function assert; use function file; use function is_file; @@ -16,35 +17,37 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class Issue { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $file; /** - * @psalm-var positive-int + * @var positive-int */ private int $line; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $hash; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $description; /** - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line - * @psalm-param ?non-empty-string $hash - * @psalm-param non-empty-string $description + * @param non-empty-string $file + * @param positive-int $line + * @param ?non-empty-string $hash + * @param non-empty-string $description * * @throws FileDoesNotExistException * @throws FileDoesNotHaveLineException @@ -59,10 +62,10 @@ public static function from(string $file, int $line, ?string $hash, string $desc } /** - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line - * @psalm-param non-empty-string $hash - * @psalm-param non-empty-string $description + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $hash + * @param non-empty-string $description */ private function __construct(string $file, int $line, string $hash, string $description) { @@ -73,7 +76,7 @@ private function __construct(string $file, int $line, string $hash, string $desc } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -81,7 +84,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -89,7 +92,7 @@ public function line(): int } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function hash(): string { @@ -97,7 +100,7 @@ public function hash(): string } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function description(): string { @@ -113,22 +116,23 @@ public function equals(self $other): bool } /** - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line - * - * @psalm-return non-empty-string + * @param non-empty-string $file + * @param positive-int $line * * @throws FileDoesNotExistException * @throws FileDoesNotHaveLineException + * + * @return non-empty-string */ private static function calculateHash(string $file, int $line): string { - if (!is_file($file)) { + $lines = @file($file, FILE_IGNORE_NEW_LINES); + + if ($lines === false && !is_file($file)) { throw new FileDoesNotExistException($file); } - $lines = file($file, FILE_IGNORE_NEW_LINES); - $key = $line - 1; + $key = $line - 1; if (!isset($lines[$key])) { throw new FileDoesNotHaveLineException($file, $line); @@ -136,7 +140,7 @@ private static function calculateHash(string $file, int $line): string $hash = sha1($lines[$key]); - assert(!empty($hash)); + assert($hash !== ''); return $hash; } diff --git a/src/Runner/Baseline/Reader.php b/src/Runner/Baseline/Reader.php index 3aa5cfbc89a..ce3a194ff16 100644 --- a/src/Runner/Baseline/Reader.php +++ b/src/Runner/Baseline/Reader.php @@ -9,9 +9,10 @@ */ namespace PHPUnit\Runner\Baseline; +use const DIRECTORY_SEPARATOR; use function assert; use function dirname; -use function file_exists; +use function is_file; use function realpath; use function sprintf; use function str_replace; @@ -22,18 +23,20 @@ use PHPUnit\Util\Xml\XmlException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Reader +final readonly class Reader { /** - * @psalm-param non-empty-string $baselineFile + * @param non-empty-string $baselineFile * * @throws CannotLoadBaselineException */ public function read(string $baselineFile): Baseline { - if (!file_exists($baselineFile)) { + if (!is_file($baselineFile)) { throw new CannotLoadBaselineException( sprintf( 'Cannot read baseline %s, file does not exist', @@ -47,7 +50,8 @@ public function read(string $baselineFile): Baseline } catch (XmlException $e) { throw new CannotLoadBaselineException( sprintf( - 'Cannot read baseline: %s', + 'Cannot read baseline %s: %s', + $baselineFile, trim($e->getMessage()), ), ); @@ -85,10 +89,9 @@ public function read(string $baselineFile): Baseline $description = $issueElement->textContent; - assert(!empty($file)); assert($line > 0); - assert(!empty($hash)); - assert(!empty($description)); + assert($hash !== ''); + assert($description !== ''); $baseline->add(Issue::from($file, $line, $hash, $description)); } diff --git a/src/Runner/Baseline/RelativePathCalculator.php b/src/Runner/Baseline/RelativePathCalculator.php index 6e29beb90c3..a05e6bbea09 100644 --- a/src/Runner/Baseline/RelativePathCalculator.php +++ b/src/Runner/Baseline/RelativePathCalculator.php @@ -22,6 +22,8 @@ use function trim; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * * @see Copied from https://github.com/phpstan/phpstan-src/blob/1.10.33/src/File/ParentDirectoryRelativePathHelper.php @@ -29,12 +31,12 @@ final readonly class RelativePathCalculator { /** - * @psalm-var non-empty-string $baselineDirectory + * @var non-empty-string */ private string $baselineDirectory; /** - * @psalm-param non-empty-string $baselineDirectory + * @param non-empty-string $baselineDirectory */ public function __construct(string $baselineDirectory) { @@ -42,9 +44,9 @@ public function __construct(string $baselineDirectory) } /** - * @psalm-param non-empty-string $filename + * @param non-empty-string $filename * - * @psalm-return non-empty-string + * @return non-empty-string */ public function calculate(string $filename): string { @@ -56,9 +58,9 @@ public function calculate(string $filename): string } /** - * @psalm-param non-empty-string $filename + * @param non-empty-string $filename * - * @psalm-return list + * @return list */ public function parts(string $filename): array { diff --git a/src/Runner/Baseline/Subscriber/Subscriber.php b/src/Runner/Baseline/Subscriber/Subscriber.php index b3ba386c893..59ca634b1be 100644 --- a/src/Runner/Baseline/Subscriber/Subscriber.php +++ b/src/Runner/Baseline/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\Runner\Baseline; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly Generator $generator; + private Generator $generator; public function __construct(Generator $generator) { diff --git a/src/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.php b/src/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.php index f26ed2ecf31..72e26110609 100644 --- a/src/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.php +++ b/src/Runner/Baseline/Subscriber/TestTriggeredDeprecationSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber +final readonly class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber { /** * @throws FileDoesNotExistException diff --git a/src/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.php b/src/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.php index a531fbcad9c..288d0eff020 100644 --- a/src/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.php +++ b/src/Runner/Baseline/Subscriber/TestTriggeredNoticeSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber +final readonly class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber { /** * @throws FileDoesNotExistException diff --git a/src/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.php b/src/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.php index a7a5d9f117d..f72095ac18d 100644 --- a/src/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.php +++ b/src/Runner/Baseline/Subscriber/TestTriggeredPhpDeprecationSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber +final readonly class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber { /** * @throws FileDoesNotExistException diff --git a/src/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php b/src/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php index 26085fb63d0..9707a46b3fb 100644 --- a/src/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php +++ b/src/Runner/Baseline/Subscriber/TestTriggeredPhpNoticeSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber +final readonly class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber { /** * @throws FileDoesNotExistException diff --git a/src/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.php b/src/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.php index a0e617b4f5f..22af95db419 100644 --- a/src/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.php +++ b/src/Runner/Baseline/Subscriber/TestTriggeredPhpWarningSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber +final readonly class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber { /** * @throws FileDoesNotExistException diff --git a/src/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.php b/src/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.php index 793b7149138..fd5e0db6fc1 100644 --- a/src/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.php +++ b/src/Runner/Baseline/Subscriber/TestTriggeredWarningSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Runner\FileDoesNotExistException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber +final readonly class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber { /** * @throws FileDoesNotExistException diff --git a/src/Runner/Baseline/Writer.php b/src/Runner/Baseline/Writer.php index 28540930068..999b445b7e2 100644 --- a/src/Runner/Baseline/Writer.php +++ b/src/Runner/Baseline/Writer.php @@ -9,18 +9,19 @@ */ namespace PHPUnit\Runner\Baseline; -use function assert; use function dirname; use function file_put_contents; use XMLWriter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Writer +final readonly class Writer { /** - * @psalm-param non-empty-string $baselineFile + * @param non-empty-string $baselineFile */ public function write(string $baselineFile, Baseline $baseline): void { @@ -36,8 +37,6 @@ public function write(string $baselineFile, Baseline $baseline): void $writer->writeAttribute('version', (string) Baseline::VERSION); foreach ($baseline->groupedByFileAndLine() as $file => $lines) { - assert(!empty($file)); - $writer->startElement('file'); $writer->writeAttribute('path', $pathCalculator->calculate($file)); @@ -48,7 +47,7 @@ public function write(string $baselineFile, Baseline $baseline): void foreach ($issues as $issue) { $writer->startElement('issue'); - $writer->writeCData($issue->description()); + $writer->writeCdata($issue->description()); $writer->endElement(); } diff --git a/src/Runner/CodeCoverage.php b/src/Runner/CodeCoverage.php index c726ed1b8ca..19f6dc3889f 100644 --- a/src/Runner/CodeCoverage.php +++ b/src/Runner/CodeCoverage.php @@ -9,14 +9,16 @@ */ namespace PHPUnit\Runner; +use function assert; use function file_put_contents; use function sprintf; +use function sys_get_temp_dir; use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Framework\TestCase; use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry; use PHPUnit\TextUI\Configuration\Configuration; use PHPUnit\TextUI\Output\Printer; +use PHPUnit\Util\Filesystem; use SebastianBergmann\CodeCoverage\Driver\Driver; use SebastianBergmann\CodeCoverage\Driver\Selector; use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; @@ -31,6 +33,9 @@ use SebastianBergmann\CodeCoverage\Report\Text as TextReport; use SebastianBergmann\CodeCoverage\Report\Thresholds; use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; +use SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer; +use SebastianBergmann\CodeCoverage\Test\Target\TargetCollection; +use SebastianBergmann\CodeCoverage\Test\Target\ValidationFailure; use SebastianBergmann\CodeCoverage\Test\TestSize\TestSize; use SebastianBergmann\CodeCoverage\Test\TestStatus\TestStatus; use SebastianBergmann\Comparator\Comparator; @@ -38,21 +43,24 @@ use SebastianBergmann\Timer\Timer; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ final class CodeCoverage { private static ?self $instance = null; private ?\SebastianBergmann\CodeCoverage\CodeCoverage $codeCoverage = null; - private ?Driver $driver = null; - private bool $collecting = false; - private ?TestCase $test = null; - private ?Timer $timer = null; /** - * @psalm-var array> + * @phpstan-ignore property.internalClass */ - private array $linesToBeIgnored = []; + private ?Driver $driver = null; + private bool $collecting = false; + private ?TestCase $test = null; + private ?Timer $timer = null; public static function instance(): self { @@ -78,7 +86,17 @@ public function init(Configuration $configuration, CodeCoverageFilterRegistry $c } if ($configuration->hasCoverageCacheDirectory()) { - $this->codeCoverage()->cacheStaticAnalysis($configuration->coverageCacheDirectory()); + $coverageCacheDirectory = $configuration->coverageCacheDirectory(); + } else { + $candidate = sys_get_temp_dir() . '/phpunit-code-coverage-cache'; + + if (Filesystem::createDirectory($candidate)) { + $coverageCacheDirectory = $candidate; + } + } + + if (isset($coverageCacheDirectory)) { + $this->codeCoverage()->cacheStaticAnalysis($coverageCacheDirectory); } $this->codeCoverage()->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class); @@ -107,21 +125,38 @@ public function init(Configuration $configuration, CodeCoverageFilterRegistry $c if ($codeCoverageFilterRegistry->get()->isEmpty()) { if (!$codeCoverageFilterRegistry->configured()) { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( 'No filter is configured, code coverage will not be processed', ); } else { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( 'Incorrect filter configuration, code coverage will not be processed', ); } $this->deactivate(); } + + if (isset($coverageCacheDirectory) && $configuration->includeUncoveredFiles()) { + EventFacade::emitter()->testRunnerStartedStaticAnalysisForCodeCoverage(); + + /** @phpstan-ignore new.internalClass,method.internalClass */ + $statistics = (new CacheWarmer)->warmCache( + $coverageCacheDirectory, + !$configuration->disableCodeCoverageIgnore(), + $configuration->ignoreDeprecatedCodeUnitsFromCodeCoverage(), + $codeCoverageFilterRegistry->get(), + ); + + EventFacade::emitter()->testRunnerFinishedStaticAnalysisForCodeCoverage( + $statistics['cacheHits'], + $statistics['cacheMisses'], + ); + } } /** - * @psalm-assert-if-true !null $this->instance + * @phpstan-assert-if-true !null $this->instance */ public function isActive(): bool { @@ -133,14 +168,14 @@ public function codeCoverage(): \SebastianBergmann\CodeCoverage\CodeCoverage return $this->codeCoverage; } - public function driver(): Driver + /** + * @return non-empty-string + */ + public function driverNameAndVersion(): string { - return $this->driver; + return $this->driver->nameAndVersion(); } - /** - * @throws NoDataSetFromDataProviderException - */ public function start(TestCase $test): void { if ($this->collecting) { @@ -167,7 +202,7 @@ public function start(TestCase $test): void $this->collecting = true; } - public function stop(bool $append = true, array|false $linesToBeCovered = [], array $linesToBeUsed = []): void + public function stop(bool $append, null|false|TargetCollection $covers = null, ?TargetCollection $uses = null): void { if (!$this->collecting) { return; @@ -183,8 +218,37 @@ public function stop(bool $append = true, array|false $linesToBeCovered = [], ar } } - /* @noinspection UnusedFunctionResultInspection */ - $this->codeCoverage->stop($append, $status, $linesToBeCovered, $linesToBeUsed, $this->linesToBeIgnored); + if ($covers instanceof TargetCollection) { + $result = $this->codeCoverage->validate($covers); + + if ($result->isFailure()) { + assert($result instanceof ValidationFailure); + + EventFacade::emitter()->testTriggeredPhpunitWarning( + $this->test->valueObjectForEvents(), + $result->message(), + ); + + $append = false; + } + } + + if ($uses instanceof TargetCollection) { + $result = $this->codeCoverage->validate($uses); + + if ($result->isFailure()) { + assert($result instanceof ValidationFailure); + + EventFacade::emitter()->testTriggeredPhpunitWarning( + $this->test->valueObjectForEvents(), + $result->message(), + ); + + $append = false; + } + } + + $this->codeCoverage->stop($append, $status, $covers, $uses); $this->test = null; $this->collecting = false; @@ -312,7 +376,9 @@ public function generateReports(Printer $printer, Configuration $configuration): $textReport = $processor->process($this->codeCoverage(), $configuration->colors()); if ($configuration->coverageText() === 'php://stdout') { - $printer->print($textReport); + if (!$configuration->noOutput() && !$configuration->debug()) { + $printer->print($textReport); + } } else { file_put_contents($configuration->coverageText(), $textReport); } @@ -334,22 +400,6 @@ public function generateReports(Printer $printer, Configuration $configuration): } } - /** - * @psalm-param array> $linesToBeIgnored - */ - public function ignoreLines(array $linesToBeIgnored): void - { - $this->linesToBeIgnored = $linesToBeIgnored; - } - - /** - * @psalm-return array> - */ - public function linesToBeIgnored(): array - { - return $this->linesToBeIgnored; - } - private function activate(Filter $filter, bool $pathCoverage): void { try { @@ -364,7 +414,7 @@ private function activate(Filter $filter, bool $pathCoverage): void $filter, ); } catch (CodeCoverageException $e) { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( $e->getMessage(), ); } diff --git a/src/Runner/DeprecationCollector/Collector.php b/src/Runner/DeprecationCollector/Collector.php new file mode 100644 index 00000000000..575bc2dd335 --- /dev/null +++ b/src/Runner/DeprecationCollector/Collector.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\DeprecationCollector; + +use PHPUnit\Event\Facade; +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\TestRunner\IssueFilter; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Collector +{ + private readonly IssueFilter $issueFilter; + + /** + * @var list + */ + private array $deprecations = []; + + /** + * @var list + */ + private array $filteredDeprecations = []; + + public function __construct(Facade $facade, IssueFilter $issueFilter) + { + $facade->registerSubscribers( + new TestPreparedSubscriber($this), + new TestTriggeredDeprecationSubscriber($this), + ); + + $this->issueFilter = $issueFilter; + } + + /** + * @return list + */ + public function deprecations(): array + { + return $this->deprecations; + } + + /** + * @return list + */ + public function filteredDeprecations(): array + { + return $this->filteredDeprecations; + } + + public function testPrepared(): void + { + $this->deprecations = []; + } + + public function testTriggeredDeprecation(DeprecationTriggered $event): void + { + $this->deprecations[] = $event->message(); + + if (!$this->issueFilter->shouldBeProcessed($event)) { + return; + } + + $this->filteredDeprecations[] = $event->message(); + } +} diff --git a/src/Runner/DeprecationCollector/Facade.php b/src/Runner/DeprecationCollector/Facade.php new file mode 100644 index 00000000000..fc2a94e0071 --- /dev/null +++ b/src/Runner/DeprecationCollector/Facade.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\DeprecationCollector; + +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\TestRunner\IssueFilter; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class Facade +{ + private static ?Collector $collector = null; + + public static function init(): void + { + self::collector(); + } + + /** + * @return list + */ + public static function deprecations(): array + { + return self::collector()->deprecations(); + } + + /** + * @return list + */ + public static function filteredDeprecations(): array + { + return self::collector()->filteredDeprecations(); + } + + private static function collector(): Collector + { + if (self::$collector === null) { + self::$collector = new Collector( + EventFacade::instance(), + new IssueFilter( + ConfigurationRegistry::get()->source(), + ), + ); + } + + return self::$collector; + } +} diff --git a/src/Runner/DeprecationCollector/Subscriber/Subscriber.php b/src/Runner/DeprecationCollector/Subscriber/Subscriber.php new file mode 100644 index 00000000000..9c034ae721b --- /dev/null +++ b/src/Runner/DeprecationCollector/Subscriber/Subscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\DeprecationCollector; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract class Subscriber +{ + private readonly Collector $collector; + + public function __construct(Collector $collector) + { + $this->collector = $collector; + } + + protected function collector(): Collector + { + return $this->collector; + } +} diff --git a/src/Runner/DeprecationCollector/Subscriber/TestPreparedSubscriber.php b/src/Runner/DeprecationCollector/Subscriber/TestPreparedSubscriber.php new file mode 100644 index 00000000000..0e78c31a422 --- /dev/null +++ b/src/Runner/DeprecationCollector/Subscriber/TestPreparedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\DeprecationCollector; + +use PHPUnit\Event\Test\Prepared; +use PHPUnit\Event\Test\PreparedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +{ + public function notify(Prepared $event): void + { + $this->collector()->testPrepared(); + } +} diff --git a/src/Runner/DeprecationCollector/Subscriber/TestTriggeredDeprecationSubscriber.php b/src/Runner/DeprecationCollector/Subscriber/TestTriggeredDeprecationSubscriber.php new file mode 100644 index 00000000000..a01f1b61ea2 --- /dev/null +++ b/src/Runner/DeprecationCollector/Subscriber/TestTriggeredDeprecationSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\DeprecationCollector; + +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\DeprecationTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber +{ + public function notify(DeprecationTriggered $event): void + { + $this->collector()->testTriggeredDeprecation($event); + } +} diff --git a/src/Runner/ErrorHandler.php b/src/Runner/ErrorHandler.php index 2b012fad2eb..aff36fcdc57 100644 --- a/src/Runner/ErrorHandler.php +++ b/src/Runner/ErrorHandler.php @@ -9,34 +9,68 @@ */ namespace PHPUnit\Runner; +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use const E_COMPILE_ERROR; +use const E_COMPILE_WARNING; +use const E_CORE_ERROR; +use const E_CORE_WARNING; use const E_DEPRECATED; +use const E_ERROR; use const E_NOTICE; -use const E_STRICT; +use const E_PARSE; +use const E_RECOVERABLE_ERROR; use const E_USER_DEPRECATED; +use const E_USER_ERROR; use const E_USER_NOTICE; use const E_USER_WARNING; use const E_WARNING; +use function array_keys; +use function array_values; +use function debug_backtrace; +use function defined; use function error_reporting; use function restore_error_handler; use function set_error_handler; +use function sprintf; use PHPUnit\Event; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException; +use PHPUnit\Event\Code\TestMethod; use PHPUnit\Runner\Baseline\Baseline; use PHPUnit\Runner\Baseline\Issue; +use PHPUnit\TextUI\Configuration\Registry; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\SourceFilter; use PHPUnit\Util\ExcludeList; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ErrorHandler { - private static ?self $instance = null; - private ?Baseline $baseline = null; - private bool $enabled = false; + private const int UNHANDLEABLE_LEVELS = E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING; + private const int INSUPPRESSIBLE_LEVELS = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR; + private static ?self $instance = null; + private ?Baseline $baseline = null; + private bool $enabled = false; + private ?int $originalErrorReportingLevel = null; + private readonly Source $source; + + /** + * @var ?array{functions: list, methods: list} + */ + private ?array $deprecationTriggers = null; public static function instance(): self { - return self::$instance ?? self::$instance = new self; + return self::$instance ?? self::$instance = new self(Registry::get()->source()); + } + + private function __construct(Source $source) + { + $this->source = $source; } /** @@ -44,12 +78,21 @@ public static function instance(): self */ public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool { - $suppressed = !($errorNumber & error_reporting()); + $suppressed = (error_reporting() & ~self::INSUPPRESSIBLE_LEVELS) === 0; if ($suppressed && (new ExcludeList)->isExcluded($errorFile)) { return false; } + /** + * E_STRICT is deprecated since PHP 8.4. + * + * @see https://github.com/sebastianbergmann/phpunit/issues/5956 + */ + if (defined('E_STRICT') && $errorNumber === 2048) { + $errorNumber = E_NOTICE; + } + $test = Event\Code\TestMethodBuilder::fromCallStack(); $ignoredByBaseline = $this->ignoredByBaseline($errorFile, $errorLine, $errorString); @@ -57,7 +100,6 @@ public function __invoke(int $errorNumber, string $errorString, string $errorFil switch ($errorNumber) { case E_NOTICE: - case E_STRICT: Event\Facade::emitter()->testTriggeredPhpNotice( $test, $errorString, @@ -114,19 +156,24 @@ public function __invoke(int $errorNumber, string $errorString, string $errorFil $suppressed, $ignoredByBaseline, $ignoredByTest, + $this->trigger($test, false), ); break; case E_USER_DEPRECATED: + $deprecationFrame = $this->guessDeprecationFrame(); + Event\Facade::emitter()->testTriggeredDeprecation( $test, $errorString, - $errorFile, - $errorLine, + $deprecationFrame['file'] ?? $errorFile, + $deprecationFrame['line'] ?? $errorLine, $suppressed, $ignoredByBaseline, $ignoredByTest, + $this->trigger($test, true), + $this->stackTrace(), ); break; @@ -140,13 +187,13 @@ public function __invoke(int $errorNumber, string $errorString, string $errorFil $suppressed, ); - break; + throw new ErrorException('E_USER_ERROR was triggered'); default: return false; } - return true; + return false; } public function enable(): void @@ -163,7 +210,10 @@ public function enable(): void return; } - $this->enabled = true; + $this->enabled = true; + $this->originalErrorReportingLevel = error_reporting(); + + error_reporting($this->originalErrorReportingLevel & self::UNHANDLEABLE_LEVELS); } public function disable(): void @@ -174,18 +224,29 @@ public function disable(): void restore_error_handler(); - $this->enabled = false; + error_reporting(error_reporting() | $this->originalErrorReportingLevel); + + $this->enabled = false; + $this->originalErrorReportingLevel = null; } - public function use(Baseline $baseline): void + public function useBaseline(Baseline $baseline): void { $this->baseline = $baseline; } /** - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line - * @psalm-param non-empty-string $description + * @param array{functions: list, methods: list} $deprecationTriggers + */ + public function useDeprecationTriggers(array $deprecationTriggers): void + { + $this->deprecationTriggers = $deprecationTriggers; + } + + /** + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $description */ private function ignoredByBaseline(string $file, int $line, string $description): bool { @@ -195,4 +256,169 @@ private function ignoredByBaseline(string $file, int $line, string $description) return $this->baseline->has(Issue::from($file, $line, null, $description)); } + + private function trigger(TestMethod $test, bool $filterTrigger): IssueTrigger + { + if (!$this->source->notEmpty()) { + return IssueTrigger::unknown(); + } + + $trace = $this->filteredStackTrace($filterTrigger); + + $triggeredInFirstPartyCode = false; + $triggerCalledFromFirstPartyCode = false; + + if (isset($trace[0]['file'])) { + if ($trace[0]['file'] === $test->file()) { + return IssueTrigger::test(); + } + + if (SourceFilter::instance()->includes($trace[0]['file'])) { + $triggeredInFirstPartyCode = true; + } + } + + if (isset($trace[1]['file']) && + ($trace[1]['file'] === $test->file() || + SourceFilter::instance()->includes($trace[1]['file']))) { + $triggerCalledFromFirstPartyCode = true; + } + + if ($triggerCalledFromFirstPartyCode) { + if ($triggeredInFirstPartyCode) { + return IssueTrigger::self(); + } + + return IssueTrigger::direct(); + } + + return IssueTrigger::indirect(); + } + + /** + * @return list + */ + private function filteredStackTrace(bool $filterDeprecationTriggers): array + { + $trace = $this->errorStackTrace(); + + if ($this->deprecationTriggers === null || !$filterDeprecationTriggers) { + return array_values($trace); + } + + foreach (array_keys($trace) as $frame) { + foreach ($this->deprecationTriggers['functions'] as $function) { + if ($this->frameIsFunction($trace[$frame], $function)) { + unset($trace[$frame]); + + continue 2; + } + } + + foreach ($this->deprecationTriggers['methods'] as $method) { + if ($this->frameIsMethod($trace[$frame], $method)) { + unset($trace[$frame]); + + continue 2; + } + } + } + + return array_values($trace); + } + + /** + * @return ?array{file: non-empty-string, line: positive-int} + */ + private function guessDeprecationFrame(): ?array + { + if ($this->deprecationTriggers === null) { + return null; + } + + $trace = $this->errorStackTrace(); + + foreach ($trace as $frame) { + foreach ($this->deprecationTriggers['functions'] as $function) { + if ($this->frameIsFunction($frame, $function)) { + return $frame; + } + } + + foreach ($this->deprecationTriggers['methods'] as $method) { + if ($this->frameIsMethod($frame, $method)) { + return $frame; + } + } + } + + return null; + } + + /** + * @return list + */ + private function errorStackTrace(): array + { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + $i = 0; + + do { + unset($trace[$i]); + } while (self::class === ($trace[++$i]['class'] ?? null)); + + return array_values($trace); + } + + /** + * @param array{class? : class-string, function?: non-empty-string} $frame + * @param non-empty-string $function + */ + private function frameIsFunction(array $frame, string $function): bool + { + return !isset($frame['class']) && isset($frame['function']) && $frame['function'] === $function; + } + + /** + * @param array{class? : class-string, function?: non-empty-string} $frame + * @param array{className: class-string, methodName: non-empty-string} $method + */ + private function frameIsMethod(array $frame, array $method): bool + { + return isset($frame['class']) && + $frame['class'] === $method['className'] && + isset($frame['function']) && + $frame['function'] === $method['methodName']; + } + + /** + * @return non-empty-string + */ + private function stackTrace(): string + { + $buffer = ''; + $excludeList = new ExcludeList(true); + + foreach ($this->errorStackTrace() as $frame) { + /** + * @see https://github.com/sebastianbergmann/phpunit/issues/6043 + */ + if (!isset($frame['file'])) { + continue; + } + + if ($excludeList->isExcluded($frame['file'])) { + continue; + } + + $buffer .= sprintf( + "%s:%s\n", + $frame['file'], + $frame['line'] ?? '?', + ); + } + + return $buffer; + } } diff --git a/src/Runner/Exception/ClassCannotBeFoundException.php b/src/Runner/Exception/ClassCannotBeFoundException.php index e64a35932fe..701cbb5b143 100644 --- a/src/Runner/Exception/ClassCannotBeFoundException.php +++ b/src/Runner/Exception/ClassCannotBeFoundException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ClassCannotBeFoundException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/ClassDoesNotExtendTestCaseException.php b/src/Runner/Exception/ClassDoesNotExtendTestCaseException.php index 36ef74d6276..c9d5474e32c 100644 --- a/src/Runner/Exception/ClassDoesNotExtendTestCaseException.php +++ b/src/Runner/Exception/ClassDoesNotExtendTestCaseException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ClassDoesNotExtendTestCaseException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/ClassIsAbstractException.php b/src/Runner/Exception/ClassIsAbstractException.php index 4264bddde66..bf947589979 100644 --- a/src/Runner/Exception/ClassIsAbstractException.php +++ b/src/Runner/Exception/ClassIsAbstractException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ClassIsAbstractException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/DirectoryCannotBeCreatedException.php b/src/Runner/Exception/DirectoryCannotBeCreatedException.php deleted file mode 100644 index 77820625e65..00000000000 --- a/src/Runner/Exception/DirectoryCannotBeCreatedException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function sprintf; -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class DirectoryCannotBeCreatedException extends RuntimeException implements Exception -{ - public function __construct(string $directory) - { - parent::__construct( - sprintf( - 'Cannot create directory "%s"', - $directory, - ), - ); - } -} diff --git a/src/TextUI/Exception/DirectoryDoesNotExistException.php b/src/Runner/Exception/DirectoryDoesNotExistException.php similarity index 84% rename from src/TextUI/Exception/DirectoryDoesNotExistException.php rename to src/Runner/Exception/DirectoryDoesNotExistException.php index e3b11dea127..626c422567f 100644 --- a/src/TextUI/Exception/DirectoryDoesNotExistException.php +++ b/src/Runner/Exception/DirectoryDoesNotExistException.php @@ -7,12 +7,14 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TextUI; +namespace PHPUnit\Runner; use function sprintf; use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DirectoryDoesNotExistException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/ErrorException.php b/src/Runner/Exception/ErrorException.php new file mode 100644 index 00000000000..954684e9fac --- /dev/null +++ b/src/Runner/Exception/ErrorException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use Error; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ErrorException extends Error implements Exception +{ +} diff --git a/src/Runner/Exception/Exception.php b/src/Runner/Exception/Exception.php index 205f76eed42..ea0cf4244d3 100644 --- a/src/Runner/Exception/Exception.php +++ b/src/Runner/Exception/Exception.php @@ -10,6 +10,8 @@ namespace PHPUnit\Runner; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends \PHPUnit\Exception diff --git a/src/Runner/Exception/FileDoesNotExistException.php b/src/Runner/Exception/FileDoesNotExistException.php index f0211ddfdda..5b84c785d5b 100644 --- a/src/Runner/Exception/FileDoesNotExistException.php +++ b/src/Runner/Exception/FileDoesNotExistException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class FileDoesNotExistException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/InvalidOrderException.php b/src/Runner/Exception/InvalidOrderException.php index d02d179ee10..016ec85e457 100644 --- a/src/Runner/Exception/InvalidOrderException.php +++ b/src/Runner/Exception/InvalidOrderException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidOrderException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/InvalidPhptFileException.php b/src/Runner/Exception/InvalidPhptFileException.php deleted file mode 100644 index de8c4ecbe06..00000000000 --- a/src/Runner/Exception/InvalidPhptFileException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class InvalidPhptFileException extends RuntimeException implements Exception -{ -} diff --git a/src/Runner/Exception/NoIgnoredEventException.php b/src/Runner/Exception/NoIgnoredEventException.php deleted file mode 100644 index 487308f7693..00000000000 --- a/src/Runner/Exception/NoIgnoredEventException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class NoIgnoredEventException extends RuntimeException implements Exception -{ -} diff --git a/src/Runner/Exception/ParameterDoesNotExistException.php b/src/Runner/Exception/ParameterDoesNotExistException.php index 521fbb53236..5d7a096754a 100644 --- a/src/Runner/Exception/ParameterDoesNotExistException.php +++ b/src/Runner/Exception/ParameterDoesNotExistException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ParameterDoesNotExistException extends RuntimeException implements Exception diff --git a/src/Runner/Exception/ReflectionException.php b/src/Runner/Exception/ReflectionException.php deleted file mode 100644 index 3972304842c..00000000000 --- a/src/Runner/Exception/ReflectionException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReflectionException extends RuntimeException implements Exception -{ -} diff --git a/src/Runner/Exception/UnsupportedPhptSectionException.php b/src/Runner/Exception/UnsupportedPhptSectionException.php deleted file mode 100644 index a81d07b0528..00000000000 --- a/src/Runner/Exception/UnsupportedPhptSectionException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use function sprintf; -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class UnsupportedPhptSectionException extends RuntimeException implements Exception -{ - public function __construct(string $section) - { - parent::__construct( - sprintf( - 'PHPUnit does not support PHPT %s sections', - $section, - ), - ); - } -} diff --git a/src/Runner/Extension/ExtensionBootstrapper.php b/src/Runner/Extension/ExtensionBootstrapper.php index e7057652dd9..b9c6c9fa29f 100644 --- a/src/Runner/Extension/ExtensionBootstrapper.php +++ b/src/Runner/Extension/ExtensionBootstrapper.php @@ -9,18 +9,20 @@ */ namespace PHPUnit\Runner\Extension; +use const PHP_EOL; use function assert; use function class_exists; use function class_implements; use function in_array; use function sprintf; -use PHPUnit\Event; use PHPUnit\Event\Facade as EventFacade; use PHPUnit\TextUI\Configuration\Configuration; use ReflectionClass; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ExtensionBootstrapper @@ -35,13 +37,13 @@ public function __construct(Configuration $configuration, Facade $facade) } /** - * @psalm-param class-string $className - * @psalm-param array $parameters + * @param non-empty-string $className + * @param array $parameters */ public function bootstrap(string $className, array $parameters): void { if (!class_exists($className)) { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Cannot bootstrap extension because class %s does not exist', $className, @@ -52,7 +54,7 @@ public function bootstrap(string $className, array $parameters): void } if (!in_array(Extension::class, class_implements($className), true)) { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Cannot bootstrap extension because class %s does not implement interface %s', $className, @@ -74,7 +76,7 @@ public function bootstrap(string $className, array $parameters): void ParameterCollection::fromArray($parameters), ); } catch (Throwable $t) { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Bootstrapping of extension %s failed: %s%s%s', $className, @@ -87,7 +89,7 @@ public function bootstrap(string $className, array $parameters): void return; } - Event\Facade::emitter()->testRunnerBootstrappedExtension( + EventFacade::emitter()->testRunnerBootstrappedExtension( $className, $parameters, ); diff --git a/src/Runner/Extension/Facade.php b/src/Runner/Extension/Facade.php index 62ba4b23427..910f4e5fe13 100644 --- a/src/Runner/Extension/Facade.php +++ b/src/Runner/Extension/Facade.php @@ -24,7 +24,6 @@ final class Facade private bool $replacesProgressOutput = false; private bool $replacesResultOutput = false; private bool $requiresCodeCoverageCollection = false; - private bool $requiresExportOfObjects = false; /** * @throws EventFacadeIsSealedException @@ -91,14 +90,4 @@ public function requiresCodeCoverageCollection(): bool { return $this->requiresCodeCoverageCollection; } - - public function requireExportOfObjects(): void - { - $this->requiresExportOfObjects = true; - } - - public function requiresExportOfObjects(): bool - { - return $this->requiresExportOfObjects; - } } diff --git a/src/Runner/Extension/ParameterCollection.php b/src/Runner/Extension/ParameterCollection.php index 329107e9bdd..fef1c9b1ae6 100644 --- a/src/Runner/Extension/ParameterCollection.php +++ b/src/Runner/Extension/ParameterCollection.php @@ -13,22 +13,28 @@ use PHPUnit\Runner\ParameterDoesNotExistException; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final readonly class ParameterCollection { + /** + * @var array + */ private array $parameters; /** - * @psalm-param array $parameters + * @param array $parameters */ public static function fromArray(array $parameters): self { return new self($parameters); } + /** + * @param array $parameters + */ private function __construct(array $parameters) { $this->parameters = $parameters; diff --git a/src/Runner/Extension/PharLoader.php b/src/Runner/Extension/PharLoader.php index 6e0943c66ff..7f3c4b44914 100644 --- a/src/Runner/Extension/PharLoader.php +++ b/src/Runner/Extension/PharLoader.php @@ -26,14 +26,16 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class PharLoader +final readonly class PharLoader { /** - * @psalm-param non-empty-string $directory + * @param non-empty-string $directory * - * @psalm-return list + * @return list */ public function loadPharExtensionsInDirectory(string $directory): array { @@ -42,7 +44,7 @@ public function loadPharExtensionsInDirectory(string $directory): array foreach ((new FileIteratorFacade)->getFilesAsArray($directory, '.phar') as $file) { if (!$pharExtensionLoaded) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Cannot load extension from %s because the PHAR extension is not available', $file, @@ -53,7 +55,7 @@ public function loadPharExtensionsInDirectory(string $directory): array } if (!is_file('phar://' . $file . '/manifest.xml')) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( '%s is not an extension for PHPUnit', $file, @@ -69,7 +71,7 @@ public function loadPharExtensionsInDirectory(string $directory): array $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); if (!$manifest->isExtensionFor($applicationName)) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( '%s is not an extension for PHPUnit', $file, @@ -80,7 +82,7 @@ public function loadPharExtensionsInDirectory(string $directory): array } if (!$manifest->isExtensionFor($applicationName, $version)) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( '%s is not compatible with PHPUnit %s', $file, @@ -91,7 +93,7 @@ public function loadPharExtensionsInDirectory(string $directory): array continue; } } catch (ManifestException $e) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Cannot load extension from %s: %s', $file, @@ -103,10 +105,9 @@ public function loadPharExtensionsInDirectory(string $directory): array } try { - /** @psalm-suppress UnresolvableInclude */ @require $file; } catch (Throwable $t) { - Event\Facade::emitter()->testRunnerTriggeredWarning( + Event\Facade::emitter()->testRunnerTriggeredPhpunitWarning( sprintf( 'Cannot load extension from %s: %s', $file, diff --git a/src/Runner/Filter/ExcludeGroupFilterIterator.php b/src/Runner/Filter/ExcludeGroupFilterIterator.php index b4f3a45c70c..45296b2d199 100644 --- a/src/Runner/Filter/ExcludeGroupFilterIterator.php +++ b/src/Runner/Filter/ExcludeGroupFilterIterator.php @@ -12,12 +12,18 @@ use function in_array; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ExcludeGroupFilterIterator extends GroupFilterIterator { - protected function doAccept(int $id): bool + /** + * @param non-empty-string $id + * @param list $groupTests + */ + protected function doAccept(string $id, array $groupTests): bool { - return !in_array($id, $this->groupTests, true); + return !in_array($id, $groupTests, true); } } diff --git a/src/Runner/Filter/ExcludeNameFilterIterator.php b/src/Runner/Filter/ExcludeNameFilterIterator.php new file mode 100644 index 00000000000..ff8459312ef --- /dev/null +++ b/src/Runner/Filter/ExcludeNameFilterIterator.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class ExcludeNameFilterIterator extends NameFilterIterator +{ + protected function doAccept(bool $result): bool + { + return !$result; + } +} diff --git a/src/Runner/Filter/Factory.php b/src/Runner/Filter/Factory.php index 89eec09c1bf..0335e25b5bd 100644 --- a/src/Runner/Filter/Factory.php +++ b/src/Runner/Filter/Factory.php @@ -12,64 +12,89 @@ use function assert; use FilterIterator; use Iterator; +use PHPUnit\Framework\Test; use PHPUnit\Framework\TestSuite; -use ReflectionClass; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Factory { /** - * @psalm-var array + * @var list>>, argument: list|non-empty-string}> */ private array $filters = []; /** - * @psalm-param list $testIds + * @param list $testIds */ public function addTestIdFilter(array $testIds): void { $this->filters[] = [ - new ReflectionClass(TestIdFilterIterator::class), $testIds, + 'className' => TestIdFilterIterator::class, + 'argument' => $testIds, + ]; + } + + /** + * @param list $groups + */ + public function addIncludeGroupFilter(array $groups): void + { + $this->filters[] = [ + 'className' => IncludeGroupFilterIterator::class, + 'argument' => $groups, ]; } /** - * @psalm-param list $groups + * @param list $groups */ public function addExcludeGroupFilter(array $groups): void { $this->filters[] = [ - new ReflectionClass(ExcludeGroupFilterIterator::class), $groups, + 'className' => ExcludeGroupFilterIterator::class, + 'argument' => $groups, ]; } /** - * @psalm-param list $groups + * @param non-empty-string $name */ - public function addIncludeGroupFilter(array $groups): void + public function addIncludeNameFilter(string $name): void { $this->filters[] = [ - new ReflectionClass(IncludeGroupFilterIterator::class), $groups, + 'className' => IncludeNameFilterIterator::class, + 'argument' => $name, ]; } /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name */ - public function addNameFilter(string $name): void + public function addExcludeNameFilter(string $name): void { $this->filters[] = [ - new ReflectionClass(NameFilterIterator::class), $name, + 'className' => ExcludeNameFilterIterator::class, + 'argument' => $name, ]; } + /** + * @param Iterator $iterator + * + * @return FilterIterator> + */ public function factory(Iterator $iterator, TestSuite $suite): FilterIterator { foreach ($this->filters as $filter) { - [$class, $arguments] = $filter; - $iterator = $class->newInstance($iterator, $arguments, $suite); + $iterator = new $filter['className']( + $iterator, + $filter['argument'], + $suite, + ); } assert($iterator instanceof FilterIterator); diff --git a/src/Runner/Filter/GroupFilterIterator.php b/src/Runner/Filter/GroupFilterIterator.php index 2c9a29602f5..da45211d752 100644 --- a/src/Runner/Filter/GroupFilterIterator.php +++ b/src/Runner/Filter/GroupFilterIterator.php @@ -9,43 +9,47 @@ */ namespace PHPUnit\Runner\Filter; -use function array_map; +use function array_merge; use function array_push; use function in_array; -use function spl_object_id; use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use RecursiveFilterIterator; use RecursiveIterator; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ abstract class GroupFilterIterator extends RecursiveFilterIterator { /** - * @psalm-var list + * @var list */ - protected array $groupTests = []; + private readonly array $groupTests; /** - * @psalm-param RecursiveIterator $iterator - * @psalm-param list $groups + * @param RecursiveIterator $iterator + * @param list $groups */ public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite) { parent::__construct($iterator); - foreach ($suite->groupDetails() as $group => $tests) { - if (in_array((string) $group, $groups, true)) { - $testHashes = array_map( - 'spl_object_id', - $tests, - ); + $groupTests = []; + + foreach ($suite->groups() as $group => $tests) { + if (in_array($group, $groups, true)) { + $groupTests = array_merge($groupTests, $tests); - array_push($this->groupTests, ...$testHashes); + array_push($groupTests, ...$groupTests); } } + + $this->groupTests = $groupTests; } public function accept(): bool @@ -56,8 +60,16 @@ public function accept(): bool return true; } - return $this->doAccept(spl_object_id($test)); + if ($test instanceof TestCase || $test instanceof PhptTestCase) { + return $this->doAccept($test->valueObjectForEvents()->id(), $this->groupTests); + } + + return true; } - abstract protected function doAccept(int $id): bool; + /** + * @param non-empty-string $id + * @param list $groupTests + */ + abstract protected function doAccept(string $id, array $groupTests): bool; } diff --git a/src/Runner/Filter/IncludeGroupFilterIterator.php b/src/Runner/Filter/IncludeGroupFilterIterator.php index 8a89e8e52c1..afdaefda9fd 100644 --- a/src/Runner/Filter/IncludeGroupFilterIterator.php +++ b/src/Runner/Filter/IncludeGroupFilterIterator.php @@ -12,12 +12,18 @@ use function in_array; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class IncludeGroupFilterIterator extends GroupFilterIterator { - protected function doAccept(int $id): bool + /** + * @param non-empty-string $id + * @param list $groupTests + */ + protected function doAccept(string $id, array $groupTests): bool { - return in_array($id, $this->groupTests, true); + return in_array($id, $groupTests, true); } } diff --git a/src/Runner/Filter/IncludeNameFilterIterator.php b/src/Runner/Filter/IncludeNameFilterIterator.php new file mode 100644 index 00000000000..9bca65eb2ba --- /dev/null +++ b/src/Runner/Filter/IncludeNameFilterIterator.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class IncludeNameFilterIterator extends NameFilterIterator +{ + protected function doAccept(bool $result): bool + { + return $result; + } +} diff --git a/src/Runner/Filter/NameFilterIterator.php b/src/Runner/Filter/NameFilterIterator.php index 8d226638524..47c24b2bbc3 100644 --- a/src/Runner/Filter/NameFilterIterator.php +++ b/src/Runner/Filter/NameFilterIterator.php @@ -10,38 +10,43 @@ namespace PHPUnit\Runner\Filter; use function end; -use function implode; use function preg_match; use function sprintf; use function str_replace; -use Exception; -use PHPUnit\Framework\SelfDescribing; +use function substr; use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use RecursiveFilterIterator; use RecursiveIterator; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class NameFilterIterator extends RecursiveFilterIterator +abstract class NameFilterIterator extends RecursiveFilterIterator { - private ?string $filter = null; - private ?int $filterMin = null; - private ?int $filterMax = null; + /** + * @var non-empty-string + */ + private readonly string $regularExpression; + private readonly ?int $dataSetMinimum; + private readonly ?int $dataSetMaximum; /** - * @psalm-param RecursiveIterator $iterator - * @psalm-param non-empty-string $filter - * - * @throws Exception + * @param RecursiveIterator $iterator + * @param non-empty-string $filter */ public function __construct(RecursiveIterator $iterator, string $filter) { parent::__construct($iterator); - $this->setFilter($filter); + $preparedFilter = $this->prepareFilter($filter); + + $this->regularExpression = $preparedFilter['regularExpression']; + $this->dataSetMinimum = $preparedFilter['dataSetMinimum']; + $this->dataSetMaximum = $preparedFilter['dataSetMaximum']; } public function accept(): bool @@ -52,30 +57,35 @@ public function accept(): bool return true; } - $tmp = $this->describe($test); - - if ($tmp[0] !== '') { - $name = implode('::', $tmp); - } else { - $name = $tmp[1]; + if ($test instanceof PhptTestCase) { + return false; } - $accepted = @preg_match($this->filter, $name, $matches); + $name = $test::class . '::' . $test->nameWithDataSet(); + + $accepted = @preg_match($this->regularExpression, $name, $matches) === 1; - if ($accepted && isset($this->filterMax)) { + if ($accepted && isset($this->dataSetMaximum)) { $set = end($matches); - $accepted = $set >= $this->filterMin && $set <= $this->filterMax; + $accepted = $set >= $this->dataSetMinimum && $set <= $this->dataSetMaximum; } - return (bool) $accepted; + return $this->doAccept($accepted); } + abstract protected function doAccept(bool $result): bool; + /** - * @throws Exception + * @param non-empty-string $filter + * + * @return array{regularExpression: non-empty-string, dataSetMinimum: ?int, dataSetMaximum: ?int} */ - private function setFilter(string $filter): void + private function prepareFilter(string $filter): array { - if (@preg_match($filter, '') === false) { + $dataSetMinimum = null; + $dataSetMaximum = null; + + if (preg_match('/[a-zA-Z0-9]/', substr($filter, 0, 1)) === 1 || @preg_match($filter, '') === false) { // Handles: // * testAssertEqualsSucceeds#4 // * testAssertEqualsSucceeds#4-8 @@ -86,8 +96,8 @@ private function setFilter(string $filter): void $matches[1], ); - $this->filterMin = (int) $matches[2]; - $this->filterMax = (int) $matches[3]; + $dataSetMinimum = (int) $matches[2]; + $dataSetMaximum = (int) $matches[3]; } else { $filter = sprintf( '%s.*with data set #%s$', @@ -118,22 +128,10 @@ private function setFilter(string $filter): void ); } - $this->filter = $filter; - } - - /** - * @psalm-return array{0: string, 1: string} - */ - private function describe(Test $test): array - { - if ($test instanceof TestCase) { - return [$test::class, $test->nameWithDataSet()]; - } - - if ($test instanceof SelfDescribing) { - return ['', $test->toString()]; - } - - return ['', $test::class]; + return [ + 'regularExpression' => $filter, + 'dataSetMinimum' => $dataSetMinimum, + 'dataSetMaximum' => $dataSetMaximum, + ]; } } diff --git a/src/Runner/Filter/TestIdFilterIterator.php b/src/Runner/Filter/TestIdFilterIterator.php index 33fbd351208..1180de4e780 100644 --- a/src/Runner/Filter/TestIdFilterIterator.php +++ b/src/Runner/Filter/TestIdFilterIterator.php @@ -14,23 +14,25 @@ use PHPUnit\Framework\Test; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use RecursiveFilterIterator; use RecursiveIterator; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestIdFilterIterator extends RecursiveFilterIterator { /** - * @psalm-var non-empty-list + * @var non-empty-list */ private readonly array $testIds; /** - * @psalm-param RecursiveIterator $iterator - * @psalm-param non-empty-list $testIds + * @param RecursiveIterator $iterator + * @param non-empty-list $testIds */ public function __construct(RecursiveIterator $iterator, array $testIds) { diff --git a/src/Runner/GarbageCollection/GarbageCollectionHandler.php b/src/Runner/GarbageCollection/GarbageCollectionHandler.php index 354c2af300a..c6cd2dd3b1b 100644 --- a/src/Runner/GarbageCollection/GarbageCollectionHandler.php +++ b/src/Runner/GarbageCollection/GarbageCollectionHandler.php @@ -12,11 +12,11 @@ use function gc_collect_cycles; use function gc_disable; use function gc_enable; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; -use PHPUnit\Event\UnknownSubscriberTypeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class GarbageCollectionHandler @@ -25,10 +25,6 @@ final class GarbageCollectionHandler private readonly int $threshold; private int $tests = 0; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(Facade $facade, int $threshold) { $this->facade = $facade; @@ -72,10 +68,6 @@ public function testFinished(): void } } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function registerSubscribers(): void { $this->facade->registerSubscribers( diff --git a/src/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.php b/src/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.php index 6dabd41da82..4ff8e11357e 100644 --- a/src/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.php +++ b/src/Runner/GarbageCollection/Subscriber/ExecutionFinishedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber as TestRunnerExecutionFinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ExecutionFinishedSubscriber extends Subscriber implements TestRunnerExecutionFinishedSubscriber +final readonly class ExecutionFinishedSubscriber extends Subscriber implements TestRunnerExecutionFinishedSubscriber { /** * @throws \PHPUnit\Framework\InvalidArgumentException diff --git a/src/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.php b/src/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.php index 1bb917692a7..1b99b8b72eb 100644 --- a/src/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.php +++ b/src/Runner/GarbageCollection/Subscriber/ExecutionStartedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\TestRunner\ExecutionStartedSubscriber as TestRunnerExecutionStartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ExecutionStartedSubscriber extends Subscriber implements TestRunnerExecutionStartedSubscriber +final readonly class ExecutionStartedSubscriber extends Subscriber implements TestRunnerExecutionStartedSubscriber { /** * @throws \PHPUnit\Framework\InvalidArgumentException diff --git a/src/Runner/GarbageCollection/Subscriber/Subscriber.php b/src/Runner/GarbageCollection/Subscriber/Subscriber.php index 74d3d70bf69..3c9abce8d70 100644 --- a/src/Runner/GarbageCollection/Subscriber/Subscriber.php +++ b/src/Runner/GarbageCollection/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\Runner\GarbageCollection; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly GarbageCollectionHandler $handler; + private GarbageCollectionHandler $handler; public function __construct(GarbageCollectionHandler $handler) { diff --git a/src/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.php b/src/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.php index 5736b0447c1..4ffae389fbc 100644 --- a/src/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.php +++ b/src/Runner/GarbageCollection/Subscriber/TestFinishedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { /** * @throws \PHPUnit\Framework\InvalidArgumentException diff --git a/src/Runner/HookMethod/HookMethod.php b/src/Runner/HookMethod/HookMethod.php new file mode 100644 index 00000000000..2442f75ecac --- /dev/null +++ b/src/Runner/HookMethod/HookMethod.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class HookMethod +{ + /** + * @var non-empty-string + */ + private string $methodName; + private int $priority; + + /** + * @param non-empty-string $methodName + */ + public function __construct(string $methodName, int $priority) + { + $this->methodName = $methodName; + $this->priority = $priority; + } + + /** + * @return non-empty-string + */ + public function methodName(): string + { + return $this->methodName; + } + + public function priority(): int + { + return $this->priority; + } +} diff --git a/src/Runner/HookMethod/HookMethodCollection.php b/src/Runner/HookMethod/HookMethodCollection.php new file mode 100644 index 00000000000..a3593fd5fd3 --- /dev/null +++ b/src/Runner/HookMethod/HookMethodCollection.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use function array_map; +use function usort; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class HookMethodCollection +{ + private readonly bool $shouldPrepend; + + /** + * @var non-empty-list + */ + private array $hookMethods; + + public static function defaultBeforeClass(): self + { + return new self(new HookMethod('setUpBeforeClass', 0), true); + } + + public static function defaultBefore(): self + { + return new self(new HookMethod('setUp', 0), true); + } + + public static function defaultPreCondition(): self + { + return new self(new HookMethod('assertPreConditions', 0), true); + } + + public static function defaultPostCondition(): self + { + return new self(new HookMethod('assertPostConditions', 0), false); + } + + public static function defaultAfter(): self + { + return new self(new HookMethod('tearDown', 0), false); + } + + public static function defaultAfterClass(): self + { + return new self(new HookMethod('tearDownAfterClass', 0), false); + } + + private function __construct(HookMethod $default, bool $shouldPrepend) + { + $this->hookMethods = [$default]; + $this->shouldPrepend = $shouldPrepend; + } + + public function add(HookMethod $hookMethod): self + { + if ($this->shouldPrepend) { + $this->hookMethods = [$hookMethod, ...$this->hookMethods]; + } else { + $this->hookMethods[] = $hookMethod; + } + + return $this; + } + + /** + * @return list + */ + public function methodNamesSortedByPriority(): array + { + $hookMethods = $this->hookMethods; + + usort($hookMethods, static fn (HookMethod $a, HookMethod $b) => $b->priority() <=> $a->priority()); + + return array_map( + static fn (HookMethod $hookMethod) => $hookMethod->methodName(), + $hookMethods, + ); + } +} diff --git a/src/Runner/IssueFilter.php b/src/Runner/IssueFilter.php new file mode 100644 index 00000000000..445c15b7f79 --- /dev/null +++ b/src/Runner/IssueFilter.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner; + +use PHPUnit\Event\Test\DeprecationTriggered; +use PHPUnit\Event\Test\ErrorTriggered; +use PHPUnit\Event\Test\NoticeTriggered; +use PHPUnit\Event\Test\PhpDeprecationTriggered; +use PHPUnit\Event\Test\PhpNoticeTriggered; +use PHPUnit\Event\Test\PhpWarningTriggered; +use PHPUnit\Event\Test\WarningTriggered; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\SourceFilter; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class IssueFilter +{ + private Source $source; + + public function __construct(Source $source) + { + $this->source = $source; + } + + public function shouldBeProcessed(DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event, bool $onlyTestMethods = false): bool + { + if ($onlyTestMethods && !$event->test()->isTestMethod()) { + return false; + } + + if ($event instanceof DeprecationTriggered || $event instanceof PhpDeprecationTriggered) { + if ($event->ignoredByTest()) { + return false; + } + + if ($this->source->ignoreSelfDeprecations() && + ($event->trigger()->isTest() || $event->trigger()->isSelf())) { + return false; + } + + if ($this->source->ignoreDirectDeprecations() && $event->trigger()->isDirect()) { + return false; + } + + if ($this->source->ignoreIndirectDeprecations() && $event->trigger()->isIndirect()) { + return false; + } + + if (!$this->source->ignoreSuppressionOfDeprecations() && $event->wasSuppressed()) { + return false; + } + } + + if ($event instanceof NoticeTriggered) { + if (!$this->source->ignoreSuppressionOfNotices() && $event->wasSuppressed()) { + return false; + } + + if ($this->source->restrictNotices() && !SourceFilter::instance()->includes($event->file())) { + return false; + } + } + + if ($event instanceof PhpNoticeTriggered) { + if (!$this->source->ignoreSuppressionOfPhpNotices() && $event->wasSuppressed()) { + return false; + } + + if ($this->source->restrictNotices() && !SourceFilter::instance()->includes($event->file())) { + return false; + } + } + + if ($event instanceof WarningTriggered) { + if (!$this->source->ignoreSuppressionOfWarnings() && $event->wasSuppressed()) { + return false; + } + + if ($this->source->restrictWarnings() && !SourceFilter::instance()->includes($event->file())) { + return false; + } + } + + if ($event instanceof PhpWarningTriggered) { + if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { + return false; + } + + if ($this->source->restrictWarnings() && !SourceFilter::instance()->includes($event->file())) { + return false; + } + } + + if ($event instanceof ErrorTriggered) { + if (!$this->source->ignoreSuppressionOfErrors() && $event->wasSuppressed()) { + return false; + } + } + + return true; + } +} diff --git a/src/Runner/Phpt/Exception/InvalidPhptFileException.php b/src/Runner/Phpt/Exception/InvalidPhptFileException.php new file mode 100644 index 00000000000..07a2953ec54 --- /dev/null +++ b/src/Runner/Phpt/Exception/InvalidPhptFileException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Phpt; + +use PHPUnit\Runner\Exception as RunnerException; +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class InvalidPhptFileException extends RuntimeException implements RunnerException +{ +} diff --git a/src/Runner/Exception/PhptExternalFileCannotBeLoadedException.php b/src/Runner/Phpt/Exception/PhptExternalFileCannotBeLoadedException.php similarity index 76% rename from src/Runner/Exception/PhptExternalFileCannotBeLoadedException.php rename to src/Runner/Phpt/Exception/PhptExternalFileCannotBeLoadedException.php index 5cdb1d9f960..bdee6262219 100644 --- a/src/Runner/Exception/PhptExternalFileCannotBeLoadedException.php +++ b/src/Runner/Phpt/Exception/PhptExternalFileCannotBeLoadedException.php @@ -7,15 +7,18 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\Runner; +namespace PHPUnit\Runner\Phpt; use function sprintf; +use PHPUnit\Runner\Exception as RunnerException; use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class PhptExternalFileCannotBeLoadedException extends RuntimeException implements Exception +final class PhptExternalFileCannotBeLoadedException extends RuntimeException implements RunnerException { public function __construct(string $section, string $file) { diff --git a/src/Runner/Phpt/Exception/UnsupportedPhptSectionException.php b/src/Runner/Phpt/Exception/UnsupportedPhptSectionException.php new file mode 100644 index 00000000000..9079f9967cc --- /dev/null +++ b/src/Runner/Phpt/Exception/UnsupportedPhptSectionException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Phpt; + +use function sprintf; +use PHPUnit\Runner\Exception as RunnerException; +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class UnsupportedPhptSectionException extends RuntimeException implements RunnerException +{ + public function __construct(string $section) + { + parent::__construct( + sprintf( + 'PHPUnit does not support PHPT --%s-- sections', + $section, + ), + ); + } +} diff --git a/src/Runner/Phpt/Parser.php b/src/Runner/Phpt/Parser.php new file mode 100644 index 00000000000..72582d733ee --- /dev/null +++ b/src/Runner/Phpt/Parser.php @@ -0,0 +1,213 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Phpt; + +use const DIRECTORY_SEPARATOR; +use function assert; +use function dirname; +use function explode; +use function file; +use function file_get_contents; +use function is_file; +use function is_readable; +use function is_string; +use function preg_match; +use function rtrim; +use function str_contains; +use function trim; +use PHPUnit\Runner\Exception; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @see https://qa.php.net/phpt_details.php + */ +final readonly class Parser +{ + /** + * @param non-empty-string $phptFile + * + * @throws Exception + * + * @return array + */ + public function parse(string $phptFile): array + { + $sections = []; + $section = ''; + + $unsupportedSections = [ + 'CGI', + 'COOKIE', + 'DEFLATE_POST', + 'EXPECTHEADERS', + 'EXTENSIONS', + 'GET', + 'GZIP_POST', + 'HEADERS', + 'PHPDBG', + 'POST', + 'POST_RAW', + 'PUT', + 'REDIRECTTEST', + 'REQUEST', + ]; + + $lineNr = 0; + + foreach (file($phptFile) as $line) { + $lineNr++; + + if (preg_match('/^--([_A-Z]+)--/', $line, $result)) { + $section = $result[1]; + $sections[$section] = ''; + $sections[$section . '_offset'] = $lineNr; + + continue; + } + + if ($section === '') { + throw new InvalidPhptFileException; + } + + $sections[$section] .= $line; + } + + if (isset($sections['FILEEOF'])) { + $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n"); + + unset($sections['FILEEOF']); + } + + $this->parseExternal($phptFile, $sections); + $this->validate($sections); + + foreach ($unsupportedSections as $unsupportedSection) { + if (isset($sections[$unsupportedSection])) { + throw new UnsupportedPhptSectionException($unsupportedSection); + } + } + + return $sections; + } + + /** + * @return array + */ + public function parseEnvSection(string $content): array + { + $env = []; + + foreach (explode("\n", trim($content)) as $e) { + $e = explode('=', trim($e), 2); + + if ($e[0] !== '' && isset($e[1])) { + $env[$e[0]] = $e[1]; + } + } + + return $env; + } + + /** + * @param array|string $content + * @param array|non-empty-string> $ini + * + * @return array|non-empty-string> + */ + public function parseIniSection(array|string $content, array $ini = []): array + { + if (is_string($content)) { + $content = explode("\n", trim($content)); + } + + foreach ($content as $setting) { + if (!str_contains($setting, '=')) { + continue; + } + + $setting = explode('=', $setting, 2); + $name = trim($setting[0]); + $value = trim($setting[1]); + + if ($name === 'extension' || $name === 'zend_extension') { + if (!isset($ini[$name])) { + $ini[$name] = []; + } + + $ini[$name][] = $value; + + continue; + } + + $ini[$name] = $value; + } + + return $ini; + } + + /** + * @param non-empty-string $phptFile + * @param array $sections + * + * @throws Exception + */ + private function parseExternal(string $phptFile, array &$sections): void + { + $allowSections = [ + 'FILE', + 'EXPECT', + 'EXPECTF', + 'EXPECTREGEX', + ]; + + $testDirectory = dirname($phptFile) . DIRECTORY_SEPARATOR; + + foreach ($allowSections as $section) { + if (isset($sections[$section . '_EXTERNAL'])) { + $externalFilename = trim($sections[$section . '_EXTERNAL']); + + if (!is_file($testDirectory . $externalFilename) || + !is_readable($testDirectory . $externalFilename)) { + throw new PhptExternalFileCannotBeLoadedException( + $section, + $testDirectory . $externalFilename, + ); + } + + $contents = file_get_contents($testDirectory . $externalFilename); + + assert($contents !== false && $contents !== ''); + + $sections[$section] = $contents; + } + } + } + + /** + * @param array $sections + * + * @throws InvalidPhptFileException + */ + private function validate(array $sections): void + { + if (!isset($sections['FILE'])) { + throw new InvalidPhptFileException; + } + + if (!isset($sections['EXPECT']) && + !isset($sections['EXPECTF']) && + !isset($sections['EXPECTREGEX'])) { + throw new InvalidPhptFileException; + } + } +} diff --git a/src/Runner/Phpt/Renderer.php b/src/Runner/Phpt/Renderer.php new file mode 100644 index 00000000000..0fe1de925e7 --- /dev/null +++ b/src/Runner/Phpt/Renderer.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Phpt; + +use function assert; +use function defined; +use function dirname; +use function file_put_contents; +use function str_replace; +use function var_export; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; +use SebastianBergmann\Template\InvalidArgumentException; +use SebastianBergmann\Template\Template; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @see https://qa.php.net/phpt_details.php + */ +final readonly class Renderer +{ + /** + * @param non-empty-string $phptFile + * @param non-empty-string $code + * + * @return non-empty-string + */ + public function render(string $phptFile, string $code): string + { + return str_replace( + [ + '__DIR__', + '__FILE__', + ], + [ + "'" . dirname($phptFile) . "'", + "'" . $phptFile . "'", + ], + $code, + ); + } + + /** + * @param non-empty-string $job + * @param array{coverage: non-empty-string, job: non-empty-string} $files + * + * @param-out non-empty-string $job + * + * @throws InvalidArgumentException + */ + public function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory, array $files): void + { + $template = new Template( + __DIR__ . '/templates/phpt.tpl', + ); + + $composerAutoload = '\'\''; + + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true); + } + + $phar = '\'\''; + + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, true); + } + + if ($codeCoverageCacheDirectory === null) { + $codeCoverageCacheDirectory = 'null'; + } else { + $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; + } + + $bootstrap = ''; + + if (ConfigurationRegistry::get()->hasBootstrap()) { + $bootstrap = ConfigurationRegistry::get()->bootstrap(); + } + + $template->setVar( + [ + 'bootstrap' => $bootstrap, + 'composerAutoload' => $composerAutoload, + 'phar' => $phar, + 'job' => $files['job'], + 'coverageFile' => $files['coverage'], + 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', + 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, + ], + ); + + file_put_contents($files['job'], $job); + + $rendered = $template->render(); + + assert($rendered !== ''); + + $job = $rendered; + } +} diff --git a/src/Runner/Phpt/TestCase.php b/src/Runner/Phpt/TestCase.php new file mode 100644 index 00000000000..92e62272020 --- /dev/null +++ b/src/Runner/Phpt/TestCase.php @@ -0,0 +1,682 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Phpt; + +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use const DIRECTORY_SEPARATOR; +use function array_merge; +use function basename; +use function debug_backtrace; +use function dirname; +use function explode; +use function extension_loaded; +use function file_get_contents; +use function is_array; +use function is_file; +use function ltrim; +use function ob_get_clean; +use function ob_start; +use function preg_match; +use function preg_replace; +use function preg_split; +use function realpath; +use function str_contains; +use function str_starts_with; +use function strncasecmp; +use function substr; +use function trim; +use function unlink; +use function unserialize; +use PHPUnit\Event\Code\Phpt; +use PHPUnit\Event\Code\ThrowableBuilder; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Event\NoPreviousThrowableException; +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\ExecutionOrderDependency; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\IncompleteTestError; +use PHPUnit\Framework\PhptAssertionFailedError; +use PHPUnit\Framework\Reorderable; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Framework\Test; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\Runner\Exception; +use PHPUnit\Util\PHP\Job; +use PHPUnit\Util\PHP\JobRunnerRegistry; +use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData; +use SebastianBergmann\CodeCoverage\InvalidArgumentException; +use SebastianBergmann\CodeCoverage\ReflectionException; +use SebastianBergmann\CodeCoverage\Test\TestSize\TestSize; +use SebastianBergmann\CodeCoverage\Test\TestStatus\TestStatus; +use SebastianBergmann\CodeCoverage\TestIdMissingException; +use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use staabm\SideEffectsDetector\SideEffect; +use staabm\SideEffectsDetector\SideEffectsDetector; +use Throwable; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @see https://qa.php.net/phpt_details.php + */ +final readonly class TestCase implements Reorderable, SelfDescribing, Test +{ + /** + * @var non-empty-string + */ + private string $filename; + + /** + * @param non-empty-string $filename + */ + public function __construct(string $filename) + { + $this->filename = $filename; + } + + public function count(): int + { + return 1; + } + + /** + * @throws \PHPUnit\Framework\Exception + * @throws \SebastianBergmann\Template\InvalidArgumentException + * @throws Exception + * @throws InvalidArgumentException + * @throws NoPreviousThrowableException + * @throws ReflectionException + * @throws TestIdMissingException + * @throws UnintentionallyCoveredCodeException + */ + public function run(): void + { + $emitter = EventFacade::emitter(); + $parser = new Parser; + + $emitter->testPreparationStarted( + $this->valueObjectForEvents(), + ); + + try { + $sections = $parser->parse($this->filename); + } catch (Exception $e) { + $emitter->testPrepared($this->valueObjectForEvents()); + $emitter->testErrored($this->valueObjectForEvents(), ThrowableBuilder::from($e)); + $emitter->testFinished($this->valueObjectForEvents(), 0); + + return; + } + + $code = (new Renderer)->render($this->filename, $sections['FILE']); + $xfail = false; + $environmentVariables = []; + $phpSettings = $parser->parseIniSection($this->settings(CodeCoverage::instance()->isActive())); + $input = null; + $arguments = []; + + $emitter->testPrepared($this->valueObjectForEvents()); + + if (isset($sections['INI'])) { + $phpSettings = $parser->parseIniSection($sections['INI'], $phpSettings); + } + + if (isset($sections['ENV'])) { + $environmentVariables = $parser->parseEnvSection($sections['ENV']); + } + + if ($this->shouldTestBeSkipped($sections, $phpSettings)) { + return; + } + + if (isset($sections['XFAIL'])) { + $xfail = trim($sections['XFAIL']); + } + + if (isset($sections['STDIN'])) { + $input = $sections['STDIN']; + } + + if (isset($sections['ARGS'])) { + $arguments = explode(' ', $sections['ARGS']); + } + + if (CodeCoverage::instance()->isActive()) { + $codeCoverageCacheDirectory = null; + + if (CodeCoverage::instance()->codeCoverage()->cachesStaticAnalysis()) { + $codeCoverageCacheDirectory = CodeCoverage::instance()->codeCoverage()->cacheDirectory(); + } + + (new Renderer)->renderForCoverage( + $code, + CodeCoverage::instance()->codeCoverage()->collectsBranchAndPathCoverage(), + $codeCoverageCacheDirectory, + $this->coverageFiles(), + ); + } + + $jobResult = JobRunnerRegistry::run( + new Job( + $code, + $this->stringifyIni($phpSettings), + $environmentVariables, + $arguments, + $input, + true, + ), + ); + + EventFacade::emitter()->testRunnerFinishedChildProcess($jobResult->stdout(), $jobResult->stderr()); + + $output = $jobResult->stdout(); + + if (CodeCoverage::instance()->isActive()) { + $coverage = $this->cleanupForCoverage(); + + CodeCoverage::instance()->codeCoverage()->start($this->filename, TestSize::large()); + + CodeCoverage::instance()->codeCoverage()->append( + $coverage, + $this->filename, + true, + TestStatus::unknown(), + ); + } + + $passed = true; + + try { + $this->assertPhptExpectation($sections, $output); + } catch (AssertionFailedError $e) { + $failure = $e; + + if ($xfail !== false) { + $failure = new IncompleteTestError($xfail, 0, $e); + } elseif ($e instanceof ExpectationFailedException) { + $comparisonFailure = $e->getComparisonFailure(); + + if ($comparisonFailure !== null) { + $diff = $comparisonFailure->getDiff(); + } else { + $diff = $e->getMessage(); + } + + $hint = $this->locationHintFromDiff($diff, $sections); + $trace = array_merge($hint, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)); + $failure = new PhptAssertionFailedError( + $e->getMessage(), + 0, + (string) $trace[0]['file'], + (int) $trace[0]['line'], + $trace, + $comparisonFailure !== null ? $diff : '', + ); + } + + if ($failure instanceof IncompleteTestError) { + $emitter->testMarkedAsIncomplete($this->valueObjectForEvents(), ThrowableBuilder::from($failure)); + } else { + $emitter->testFailed($this->valueObjectForEvents(), ThrowableBuilder::from($failure), null); + } + + $passed = false; + } catch (Throwable $t) { + $emitter->testErrored($this->valueObjectForEvents(), ThrowableBuilder::from($t)); + + $passed = false; + } + + if ($passed) { + $emitter->testPassed($this->valueObjectForEvents()); + } + + $this->runClean($sections, CodeCoverage::instance()->isActive()); + + $emitter->testFinished($this->valueObjectForEvents(), 1); + } + + /** + * Returns the name of the test case. + */ + public function getName(): string + { + return $this->toString(); + } + + /** + * Returns a string representation of the test case. + */ + public function toString(): string + { + return $this->filename; + } + + public function sortId(): string + { + return $this->filename; + } + + /** + * @return list + */ + public function provides(): array + { + return []; + } + + /** + * @return list + */ + public function requires(): array + { + return []; + } + + /** + * @internal This method is not covered by the backward compatibility promise for PHPUnit + */ + public function valueObjectForEvents(): Phpt + { + return new Phpt($this->filename); + } + + /** + * @param array $sections + * + * @throws Exception + * @throws ExpectationFailedException + */ + private function assertPhptExpectation(array $sections, string $output): void + { + $assertions = [ + 'EXPECT' => 'assertEquals', + 'EXPECTF' => 'assertStringMatchesFormat', + 'EXPECTREGEX' => 'assertMatchesRegularExpression', + ]; + + $actual = preg_replace('/\r\n/', "\n", trim($output)); + + foreach ($assertions as $sectionName => $sectionAssertion) { + if (isset($sections[$sectionName])) { + $sectionContent = preg_replace('/\r\n/', "\n", trim($sections[$sectionName])); + $expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; + + /** @phpstan-ignore staticMethod.dynamicName */ + Assert::$sectionAssertion($expected, $actual); + + return; + } + } + + throw new InvalidPhptFileException; + } + + /** + * @param array $sections + * @param array|non-empty-string> $settings + */ + private function shouldTestBeSkipped(array $sections, array $settings): bool + { + if (!isset($sections['SKIPIF'])) { + return false; + } + + $skipIfCode = (new Renderer)->render($this->filename, $sections['SKIPIF']); + + if ($this->shouldRunInSubprocess($sections, $skipIfCode)) { + $jobResult = JobRunnerRegistry::run( + new Job( + $skipIfCode, + $this->stringifyIni($settings), + ), + ); + + $output = $jobResult->stdout(); + + EventFacade::emitter()->testRunnerFinishedChildProcess($output, $jobResult->stderr()); + } else { + $output = $this->runCodeInLocalSandbox($skipIfCode); + } + + if (strncasecmp('skip', ltrim($output), 4) === 0) { + $message = ''; + + if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $skipMatch)) { + $message = substr($skipMatch[1], 2); + } + + EventFacade::emitter()->testSkipped( + $this->valueObjectForEvents(), + $message, + ); + + EventFacade::emitter()->testFinished($this->valueObjectForEvents(), 0); + + return true; + } + + return false; + } + + /** + * @param array $sections + */ + private function shouldRunInSubprocess(array $sections, string $cleanCode): bool + { + if (isset($sections['INI'])) { + // to get per-test INI settings, we need a dedicated subprocess + return true; + } + + $detector = new SideEffectsDetector; + $sideEffects = $detector->getSideEffects($cleanCode); + + if ($sideEffects === []) { + // no side-effects + return false; + } + + foreach ($sideEffects as $sideEffect) { + if ($sideEffect === SideEffect::STANDARD_OUTPUT) { + // stdout is fine, we will catch it using output-buffering + continue; + } + + if ($sideEffect === SideEffect::INPUT_OUTPUT) { + // IO is fine, as it doesn't pollute the main process + continue; + } + + return true; + } + + return false; + } + + private function runCodeInLocalSandbox(string $code): string + { + $code = preg_replace('/^<\?(?:php)?|\?>\s*+$/', '', $code); + $code = preg_replace('/declare\S?\([^)]+\)\S?;/', '', $code); + + // wrap in immediately invoked function to isolate local-side-effects of $code from our own process + $code = '(function() {' . $code . '})();'; + ob_start(); + @eval($code); + + return ob_get_clean(); + } + + /** + * @param array $sections + */ + private function runClean(array $sections, bool $collectCoverage): void + { + if (!isset($sections['CLEAN'])) { + return; + } + + $cleanCode = (new Renderer)->render($this->filename, $sections['CLEAN']); + + if ($this->shouldRunInSubprocess($sections, $cleanCode)) { + $result = JobRunnerRegistry::run( + new Job( + $cleanCode, + $this->settings($collectCoverage), + ), + ); + + EventFacade::emitter()->testRunnerFinishedChildProcess($result->stdout(), $result->stderr()); + + return; + } + + $this->runCodeInLocalSandbox($cleanCode); + } + + /** + * @phpstan-ignore return.internalClass + */ + private function cleanupForCoverage(): RawCodeCoverageData + { + /** + * @phpstan-ignore staticMethod.internalClass + */ + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + $files = $this->coverageFiles(); + + $buffer = false; + + if (is_file($files['coverage'])) { + $buffer = @file_get_contents($files['coverage']); + } + + if ($buffer !== false) { + $coverage = @unserialize($buffer); + + if ($coverage === false) { + /** + * @phpstan-ignore staticMethod.internalClass + */ + $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); + } + } + + foreach ($files as $file) { + @unlink($file); + } + + return $coverage; + } + + /** + * @return array{coverage: non-empty-string, job: non-empty-string} + */ + private function coverageFiles(): array + { + $baseDir = dirname(realpath($this->filename)) . DIRECTORY_SEPARATOR; + $basename = basename($this->filename, 'phpt'); + + return [ + 'coverage' => $baseDir . $basename . 'coverage', + 'job' => $baseDir . $basename . 'php', + ]; + } + + /** + * @param array|non-empty-string> $ini + * + * @return list + */ + private function stringifyIni(array $ini): array + { + $settings = []; + + foreach ($ini as $key => $value) { + if (is_array($value)) { + foreach ($value as $val) { + $settings[] = $key . '=' . $val; + } + + continue; + } + + $settings[] = $key . '=' . $value; + } + + return $settings; + } + + /** + * @param array $sections + * + * @return non-empty-list + */ + private function locationHintFromDiff(string $message, array $sections): array + { + $needle = ''; + $previousLine = ''; + $block = 'message'; + + foreach (preg_split('/\r\n|\r|\n/', $message) as $line) { + $line = trim($line); + + if ($block === 'message' && $line === '--- Expected') { + $block = 'expected'; + } + + if ($block === 'expected' && $line === '@@ @@') { + $block = 'diff'; + } + + if ($block === 'diff') { + if (str_starts_with($line, '+')) { + $needle = $this->cleanDiffLine($previousLine); + + break; + } + + if (str_starts_with($line, '-')) { + $needle = $this->cleanDiffLine($line); + + break; + } + } + + if ($line !== '') { + $previousLine = $line; + } + } + + return $this->locationHint($needle, $sections); + } + + private function cleanDiffLine(string $line): string + { + if (preg_match('/^[\-+]([\'\"]?)(.*)\1$/', $line, $matches)) { + $line = $matches[2]; + } + + return $line; + } + + /** + * @param array $sections + * + * @return non-empty-list + */ + private function locationHint(string $needle, array $sections): array + { + $needle = trim($needle); + + if ($needle === '') { + return [[ + 'file' => realpath($this->filename), + 'line' => 1, + ]]; + } + + $search = [ + // 'FILE', + 'EXPECT', + 'EXPECTF', + 'EXPECTREGEX', + ]; + + foreach ($search as $section) { + if (!isset($sections[$section])) { + continue; + } + + if (isset($sections[$section . '_EXTERNAL'])) { + $externalFile = trim($sections[$section . '_EXTERNAL']); + + return [ + [ + 'file' => realpath(dirname($this->filename) . DIRECTORY_SEPARATOR . $externalFile), + 'line' => 1, + ], + [ + 'file' => realpath($this->filename), + 'line' => ($sections[$section . '_EXTERNAL_offset'] ?? 0) + 1, + ], + ]; + } + + $sectionOffset = $sections[$section . '_offset'] ?? 0; + $offset = $sectionOffset + 1; + + foreach (preg_split('/\r\n|\r|\n/', $sections[$section]) as $line) { + if (str_contains($line, $needle)) { + return [ + [ + 'file' => realpath($this->filename), + 'line' => $offset, + ], + ]; + } + + $offset++; + } + } + + return [ + [ + 'file' => realpath($this->filename), + 'line' => 1, + ], + ]; + } + + /** + * @return list + */ + private function settings(bool $collectCoverage): array + { + $settings = [ + 'allow_url_fopen=1', + 'auto_append_file=', + 'auto_prepend_file=', + 'disable_functions=', + 'display_errors=1', + 'docref_ext=.html', + 'docref_root=', + 'error_append_string=', + 'error_prepend_string=', + 'error_reporting=-1', + 'html_errors=0', + 'log_errors=0', + 'open_basedir=', + 'output_buffering=Off', + 'output_handler=', + 'report_memleaks=0', + 'report_zend_debug=0', + ]; + + if (extension_loaded('pcov')) { + if ($collectCoverage) { + $settings[] = 'pcov.enabled=1'; + } else { + $settings[] = 'pcov.enabled=0'; + } + } + + if (extension_loaded('xdebug')) { + if ($collectCoverage) { + $settings[] = 'xdebug.mode=coverage'; + } + } + + return $settings; + } +} diff --git a/src/Util/PHP/Template/PhptTestCase.tpl b/src/Runner/Phpt/templates/phpt.tpl similarity index 100% rename from src/Util/PHP/Template/PhptTestCase.tpl rename to src/Runner/Phpt/templates/phpt.tpl diff --git a/src/Runner/PhptTestCase.php b/src/Runner/PhptTestCase.php deleted file mode 100644 index e732f000c9c..00000000000 --- a/src/Runner/PhptTestCase.php +++ /dev/null @@ -1,834 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use const DEBUG_BACKTRACE_IGNORE_ARGS; -use const DIRECTORY_SEPARATOR; -use function array_merge; -use function basename; -use function debug_backtrace; -use function defined; -use function dirname; -use function explode; -use function extension_loaded; -use function file; -use function file_get_contents; -use function file_put_contents; -use function is_array; -use function is_file; -use function is_readable; -use function is_string; -use function ltrim; -use function preg_match; -use function preg_replace; -use function preg_split; -use function realpath; -use function rtrim; -use function str_contains; -use function str_replace; -use function str_starts_with; -use function strncasecmp; -use function substr; -use function trim; -use function unlink; -use function unserialize; -use function var_export; -use PHPUnit\Event\Code\Phpt; -use PHPUnit\Event\Code\ThrowableBuilder; -use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Event\NoPreviousThrowableException; -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\ExecutionOrderDependency; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\IncompleteTestError; -use PHPUnit\Framework\PhptAssertionFailedError; -use PHPUnit\Framework\Reorderable; -use PHPUnit\Framework\SelfDescribing; -use PHPUnit\Framework\Test; -use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; -use PHPUnit\Util\PHP\AbstractPhpProcess; -use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData; -use SebastianBergmann\CodeCoverage\InvalidArgumentException; -use SebastianBergmann\CodeCoverage\StaticAnalysisCacheNotConfiguredException; -use SebastianBergmann\CodeCoverage\Test\TestSize\TestSize; -use SebastianBergmann\CodeCoverage\Test\TestStatus\TestStatus; -use SebastianBergmann\CodeCoverage\TestIdMissingException; -use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; -use SebastianBergmann\Template\Template; -use Throwable; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class PhptTestCase implements Reorderable, SelfDescribing, Test -{ - /** - * @psalm-var non-empty-string - */ - private readonly string $filename; - private readonly AbstractPhpProcess $phpUtil; - private string $output = ''; - - /** - * Constructs a test case with the given filename. - * - * @psalm-param non-empty-string $filename - * - * @throws Exception - */ - public function __construct(string $filename, AbstractPhpProcess $phpUtil = null) - { - if (!is_file($filename)) { - throw new FileDoesNotExistException($filename); - } - - $this->filename = $filename; - $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); - } - - /** - * Counts the number of test cases executed by run(TestResult result). - */ - public function count(): int - { - return 1; - } - - /** - * Runs a test and collects its result in a TestResult instance. - * - * @throws \PHPUnit\Framework\Exception - * @throws \SebastianBergmann\CodeCoverage\ReflectionException - * @throws \SebastianBergmann\Template\InvalidArgumentException - * @throws Exception - * @throws InvalidArgumentException - * @throws NoPreviousThrowableException - * @throws StaticAnalysisCacheNotConfiguredException - * @throws TestIdMissingException - * @throws UnintentionallyCoveredCodeException - * - * @noinspection RepetitiveMethodCallsInspection - */ - public function run(): void - { - $emitter = EventFacade::emitter(); - - $emitter->testPreparationStarted( - $this->valueObjectForEvents(), - ); - - try { - $sections = $this->parse(); - } catch (Exception $e) { - $emitter->testPrepared($this->valueObjectForEvents()); - $emitter->testErrored($this->valueObjectForEvents(), ThrowableBuilder::from($e)); - $emitter->testFinished($this->valueObjectForEvents(), 0); - - return; - } - - $code = $this->render($sections['FILE']); - $xfail = false; - $settings = $this->parseIniSection($this->settings(CodeCoverage::instance()->isActive())); - - $emitter->testPrepared($this->valueObjectForEvents()); - - if (isset($sections['INI'])) { - $settings = $this->parseIniSection($sections['INI'], $settings); - } - - if (isset($sections['ENV'])) { - $env = $this->parseEnvSection($sections['ENV']); - $this->phpUtil->setEnv($env); - } - - $this->phpUtil->setUseStderrRedirection(true); - - if ($this->shouldTestBeSkipped($sections, $settings)) { - return; - } - - if (isset($sections['XFAIL'])) { - $xfail = trim($sections['XFAIL']); - } - - if (isset($sections['STDIN'])) { - $this->phpUtil->setStdin($sections['STDIN']); - } - - if (isset($sections['ARGS'])) { - $this->phpUtil->setArgs($sections['ARGS']); - } - - if (CodeCoverage::instance()->isActive()) { - $codeCoverageCacheDirectory = null; - - if (CodeCoverage::instance()->codeCoverage()->cachesStaticAnalysis()) { - $codeCoverageCacheDirectory = CodeCoverage::instance()->codeCoverage()->cacheDirectory(); - } - - $this->renderForCoverage( - $code, - CodeCoverage::instance()->codeCoverage()->collectsBranchAndPathCoverage(), - $codeCoverageCacheDirectory, - ); - } - - $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); - $this->output = $jobResult['stdout'] ?? ''; - - if (CodeCoverage::instance()->isActive() && ($coverage = $this->cleanupForCoverage())) { - CodeCoverage::instance()->codeCoverage()->start($this->filename, TestSize::large()); - - CodeCoverage::instance()->codeCoverage()->append( - $coverage, - $this->filename, - true, - TestStatus::unknown(), - [], - [], - ); - } - - try { - $this->assertPhptExpectation($sections, $this->output); - } catch (AssertionFailedError $e) { - $failure = $e; - - if ($xfail !== false) { - $failure = new IncompleteTestError($xfail, 0, $e); - } elseif ($e instanceof ExpectationFailedException) { - $comparisonFailure = $e->getComparisonFailure(); - - if ($comparisonFailure) { - $diff = $comparisonFailure->getDiff(); - } else { - $diff = $e->getMessage(); - } - - $hint = $this->getLocationHintFromDiff($diff, $sections); - $trace = array_merge($hint, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)); - $failure = new PhptAssertionFailedError( - $e->getMessage(), - 0, - $trace[0]['file'], - $trace[0]['line'], - $trace, - $comparisonFailure ? $diff : '', - ); - } - - if ($failure instanceof IncompleteTestError) { - $emitter->testMarkedAsIncomplete($this->valueObjectForEvents(), ThrowableBuilder::from($failure)); - } else { - $emitter->testFailed($this->valueObjectForEvents(), ThrowableBuilder::from($failure), null); - } - } catch (Throwable $t) { - $emitter->testErrored($this->valueObjectForEvents(), ThrowableBuilder::from($t)); - } - - $this->runClean($sections, CodeCoverage::instance()->isActive()); - - $emitter->testFinished($this->valueObjectForEvents(), 1); - } - - /** - * Returns the name of the test case. - */ - public function getName(): string - { - return $this->toString(); - } - - /** - * Returns a string representation of the test case. - */ - public function toString(): string - { - return $this->filename; - } - - public function usesDataProvider(): bool - { - return false; - } - - public function numberOfAssertionsPerformed(): int - { - return 1; - } - - public function output(): string - { - return $this->output; - } - - public function hasOutput(): bool - { - return !empty($this->output); - } - - public function sortId(): string - { - return $this->filename; - } - - /** - * @psalm-return list - */ - public function provides(): array - { - return []; - } - - /** - * @psalm-return list - */ - public function requires(): array - { - return []; - } - - /** - * @internal This method is not covered by the backward compatibility promise for PHPUnit - */ - public function valueObjectForEvents(): Phpt - { - return new Phpt($this->filename); - } - - /** - * Parse --INI-- section key value pairs and return as array. - */ - private function parseIniSection(array|string $content, array $ini = []): array - { - if (is_string($content)) { - $content = explode("\n", trim($content)); - } - - foreach ($content as $setting) { - if (!str_contains($setting, '=')) { - continue; - } - - $setting = explode('=', $setting, 2); - $name = trim($setting[0]); - $value = trim($setting[1]); - - if ($name === 'extension' || $name === 'zend_extension') { - if (!isset($ini[$name])) { - $ini[$name] = []; - } - - $ini[$name][] = $value; - - continue; - } - - $ini[$name] = $value; - } - - return $ini; - } - - private function parseEnvSection(string $content): array - { - $env = []; - - foreach (explode("\n", trim($content)) as $e) { - $e = explode('=', trim($e), 2); - - if (!empty($e[0]) && isset($e[1])) { - $env[$e[0]] = $e[1]; - } - } - - return $env; - } - - /** - * @throws Exception - * @throws ExpectationFailedException - */ - private function assertPhptExpectation(array $sections, string $output): void - { - $assertions = [ - 'EXPECT' => 'assertEquals', - 'EXPECTF' => 'assertStringMatchesFormat', - 'EXPECTREGEX' => 'assertMatchesRegularExpression', - ]; - - $actual = preg_replace('/\r\n/', "\n", trim($output)); - - foreach ($assertions as $sectionName => $sectionAssertion) { - if (isset($sections[$sectionName])) { - $sectionContent = preg_replace('/\r\n/', "\n", trim($sections[$sectionName])); - $expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; - - Assert::$sectionAssertion($expected, $actual); - - return; - } - } - - throw new InvalidPhptFileException; - } - - private function shouldTestBeSkipped(array $sections, array $settings): bool - { - if (!isset($sections['SKIPIF'])) { - return false; - } - - $skipif = $this->render($sections['SKIPIF']); - $jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings)); - - if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) { - $message = ''; - - if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $skipMatch)) { - $message = substr($skipMatch[1], 2); - } - - EventFacade::emitter()->testSkipped( - $this->valueObjectForEvents(), - $message, - ); - - EventFacade::emitter()->testFinished($this->valueObjectForEvents(), 0); - - return true; - } - - return false; - } - - private function runClean(array $sections, bool $collectCoverage): void - { - $this->phpUtil->setStdin(''); - $this->phpUtil->setArgs(''); - - if (isset($sections['CLEAN'])) { - $cleanCode = $this->render($sections['CLEAN']); - - $this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage)); - } - } - - /** - * @throws Exception - */ - private function parse(): array - { - $sections = []; - $section = ''; - - $unsupportedSections = [ - 'CGI', - 'COOKIE', - 'DEFLATE_POST', - 'EXPECTHEADERS', - 'EXTENSIONS', - 'GET', - 'GZIP_POST', - 'HEADERS', - 'PHPDBG', - 'POST', - 'POST_RAW', - 'PUT', - 'REDIRECTTEST', - 'REQUEST', - ]; - - $lineNr = 0; - - foreach (file($this->filename) as $line) { - $lineNr++; - - if (preg_match('/^--([_A-Z]+)--/', $line, $result)) { - $section = $result[1]; - $sections[$section] = ''; - $sections[$section . '_offset'] = $lineNr; - - continue; - } - - if (empty($section)) { - throw new InvalidPhptFileException; - } - - $sections[$section] .= $line; - } - - if (isset($sections['FILEEOF'])) { - $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n"); - unset($sections['FILEEOF']); - } - - $this->parseExternal($sections); - - if (!$this->validate($sections)) { - throw new InvalidPhptFileException; - } - - foreach ($unsupportedSections as $section) { - if (isset($sections[$section])) { - throw new UnsupportedPhptSectionException($section); - } - } - - return $sections; - } - - /** - * @throws Exception - */ - private function parseExternal(array &$sections): void - { - $allowSections = [ - 'FILE', - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ]; - $testDirectory = dirname($this->filename) . DIRECTORY_SEPARATOR; - - foreach ($allowSections as $section) { - if (isset($sections[$section . '_EXTERNAL'])) { - $externalFilename = trim($sections[$section . '_EXTERNAL']); - - if (!is_file($testDirectory . $externalFilename) || - !is_readable($testDirectory . $externalFilename)) { - throw new PhptExternalFileCannotBeLoadedException( - $section, - $testDirectory . $externalFilename, - ); - } - - $sections[$section] = file_get_contents($testDirectory . $externalFilename); - } - } - } - - private function validate(array $sections): bool - { - $requiredSections = [ - 'FILE', - [ - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ], - ]; - - foreach ($requiredSections as $section) { - if (is_array($section)) { - $foundSection = false; - - foreach ($section as $anySection) { - if (isset($sections[$anySection])) { - $foundSection = true; - - break; - } - } - - if (!$foundSection) { - return false; - } - - continue; - } - - if (!isset($sections[$section])) { - return false; - } - } - - return true; - } - - private function render(string $code): string - { - return str_replace( - [ - '__DIR__', - '__FILE__', - ], - [ - "'" . dirname($this->filename) . "'", - "'" . $this->filename . "'", - ], - $code, - ); - } - - private function getCoverageFiles(): array - { - $baseDir = dirname(realpath($this->filename)) . DIRECTORY_SEPARATOR; - $basename = basename($this->filename, 'phpt'); - - return [ - 'coverage' => $baseDir . $basename . 'coverage', - 'job' => $baseDir . $basename . 'php', - ]; - } - - /** - * @throws \SebastianBergmann\Template\InvalidArgumentException - */ - private function renderForCoverage(string &$job, bool $pathCoverage, ?string $codeCoverageCacheDirectory): void - { - $files = $this->getCoverageFiles(); - - $template = new Template( - __DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl', - ); - - $composerAutoload = '\'\''; - - if (defined('PHPUNIT_COMPOSER_INSTALL')) { - $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true); - } - - $phar = '\'\''; - - if (defined('__PHPUNIT_PHAR__')) { - $phar = var_export(__PHPUNIT_PHAR__, true); - } - - if ($codeCoverageCacheDirectory === null) { - $codeCoverageCacheDirectory = 'null'; - } else { - $codeCoverageCacheDirectory = "'" . $codeCoverageCacheDirectory . "'"; - } - - $bootstrap = ''; - - if (ConfigurationRegistry::get()->hasBootstrap()) { - $bootstrap = ConfigurationRegistry::get()->bootstrap(); - } - - $template->setVar( - [ - 'bootstrap' => $bootstrap, - 'composerAutoload' => $composerAutoload, - 'phar' => $phar, - 'job' => $files['job'], - 'coverageFile' => $files['coverage'], - 'driverMethod' => $pathCoverage ? 'forLineAndPathCoverage' : 'forLineCoverage', - 'codeCoverageCacheDirectory' => $codeCoverageCacheDirectory, - ], - ); - - file_put_contents($files['job'], $job); - - $job = $template->render(); - } - - private function cleanupForCoverage(): RawCodeCoverageData - { - $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - $files = $this->getCoverageFiles(); - - if (is_file($files['coverage'])) { - $buffer = @file_get_contents($files['coverage']); - - if ($buffer !== false) { - $coverage = @unserialize($buffer); - - if ($coverage === false) { - $coverage = RawCodeCoverageData::fromXdebugWithoutPathCoverage([]); - } - } - } - - foreach ($files as $file) { - @unlink($file); - } - - return $coverage; - } - - private function stringifyIni(array $ini): array - { - $settings = []; - - foreach ($ini as $key => $value) { - if (is_array($value)) { - foreach ($value as $val) { - $settings[] = $key . '=' . $val; - } - - continue; - } - - $settings[] = $key . '=' . $value; - } - - return $settings; - } - - private function getLocationHintFromDiff(string $message, array $sections): array - { - $needle = ''; - $previousLine = ''; - $block = 'message'; - - foreach (preg_split('/\r\n|\r|\n/', $message) as $line) { - $line = trim($line); - - if ($block === 'message' && $line === '--- Expected') { - $block = 'expected'; - } - - if ($block === 'expected' && $line === '@@ @@') { - $block = 'diff'; - } - - if ($block === 'diff') { - if (str_starts_with($line, '+')) { - $needle = $this->getCleanDiffLine($previousLine); - - break; - } - - if (str_starts_with($line, '-')) { - $needle = $this->getCleanDiffLine($line); - - break; - } - } - - if (!empty($line)) { - $previousLine = $line; - } - } - - return $this->getLocationHint($needle, $sections); - } - - private function getCleanDiffLine(string $line): string - { - if (preg_match('/^[\-+]([\'\"]?)(.*)\1$/', $line, $matches)) { - $line = $matches[2]; - } - - return $line; - } - - private function getLocationHint(string $needle, array $sections): array - { - $needle = trim($needle); - - if (empty($needle)) { - return [[ - 'file' => realpath($this->filename), - 'line' => 1, - ]]; - } - - $search = [ - // 'FILE', - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ]; - - foreach ($search as $section) { - if (!isset($sections[$section])) { - continue; - } - - if (isset($sections[$section . '_EXTERNAL'])) { - $externalFile = trim($sections[$section . '_EXTERNAL']); - - return [ - [ - 'file' => realpath(dirname($this->filename) . DIRECTORY_SEPARATOR . $externalFile), - 'line' => 1, - ], - [ - 'file' => realpath($this->filename), - 'line' => ($sections[$section . '_EXTERNAL_offset'] ?? 0) + 1, - ], - ]; - } - - $sectionOffset = $sections[$section . '_offset'] ?? 0; - $offset = $sectionOffset + 1; - - foreach (preg_split('/\r\n|\r|\n/', $sections[$section]) as $line) { - if (str_contains($line, $needle)) { - return [ - [ - 'file' => realpath($this->filename), - 'line' => $offset, - ], - ]; - } - - $offset++; - } - } - - return [ - [ - 'file' => realpath($this->filename), - 'line' => 1, - ], - ]; - } - - /** - * @psalm-return list - */ - private function settings(bool $collectCoverage): array - { - $settings = [ - 'allow_url_fopen=1', - 'auto_append_file=', - 'auto_prepend_file=', - 'disable_functions=', - 'display_errors=1', - 'docref_ext=.html', - 'docref_root=', - 'error_append_string=', - 'error_prepend_string=', - 'error_reporting=-1', - 'html_errors=0', - 'log_errors=0', - 'open_basedir=', - 'output_buffering=Off', - 'output_handler=', - 'report_memleaks=0', - 'report_zend_debug=0', - ]; - - if (extension_loaded('pcov')) { - if ($collectCoverage) { - $settings[] = 'pcov.enabled=1'; - } else { - $settings[] = 'pcov.enabled=0'; - } - } - - if (extension_loaded('xdebug')) { - if ($collectCoverage) { - $settings[] = 'xdebug.mode=coverage'; - } else { - $settings[] = 'xdebug.mode=off'; - } - } - - return $settings; - } -} diff --git a/src/Runner/ResultCache/DefaultResultCache.php b/src/Runner/ResultCache/DefaultResultCache.php index bc3e8c4492c..ab53c8f21be 100644 --- a/src/Runner/ResultCache/DefaultResultCache.php +++ b/src/Runner/ResultCache/DefaultResultCache.php @@ -10,6 +10,7 @@ namespace PHPUnit\Runner\ResultCache; use const DIRECTORY_SEPARATOR; +use const LOCK_EX; use function array_keys; use function assert; use function dirname; @@ -21,33 +22,28 @@ use function json_decode; use function json_encode; use PHPUnit\Framework\TestStatus\TestStatus; -use PHPUnit\Runner\DirectoryCannotBeCreatedException; +use PHPUnit\Runner\DirectoryDoesNotExistException; use PHPUnit\Runner\Exception; use PHPUnit\Util\Filesystem; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DefaultResultCache implements ResultCache { - /** - * @var int - */ - private const VERSION = 1; - - /** - * @var string - */ - private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; + private const int VERSION = 1; + private const string DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; private readonly string $cacheFilename; /** - * @psalm-var array + * @var array */ private array $defects = []; /** - * @psalm-var array + * @var array */ private array $times = []; @@ -84,14 +80,31 @@ public function time(string $id): float return $this->times[$id] ?? 0.0; } + public function mergeWith(self $other): void + { + foreach ($other->defects as $id => $defect) { + $this->defects[$id] = $defect; + } + + foreach ($other->times as $id => $time) { + $this->times[$id] = $time; + } + } + public function load(): void { if (!is_file($this->cacheFilename)) { return; } + $contents = file_get_contents($this->cacheFilename); + + if ($contents === false) { + return; + } + $data = json_decode( - file_get_contents($this->cacheFilename), + $contents, true, ); @@ -124,7 +137,7 @@ public function load(): void public function persist(): void { if (!Filesystem::createDirectory(dirname($this->cacheFilename))) { - throw new DirectoryCannotBeCreatedException($this->cacheFilename); + throw new DirectoryDoesNotExistException(dirname($this->cacheFilename)); } $data = [ diff --git a/src/Runner/ResultCache/NullResultCache.php b/src/Runner/ResultCache/NullResultCache.php index 1e93eeb9b70..277e504a346 100644 --- a/src/Runner/ResultCache/NullResultCache.php +++ b/src/Runner/ResultCache/NullResultCache.php @@ -12,9 +12,11 @@ use PHPUnit\Framework\TestStatus\TestStatus; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class NullResultCache implements ResultCache +final readonly class NullResultCache implements ResultCache { public function setStatus(string $id, TestStatus $status): void { diff --git a/src/Runner/ResultCache/ResultCache.php b/src/Runner/ResultCache/ResultCache.php index 95546048203..74efe2516cf 100644 --- a/src/Runner/ResultCache/ResultCache.php +++ b/src/Runner/ResultCache/ResultCache.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\TestStatus\TestStatus; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface ResultCache diff --git a/src/Runner/ResultCache/ResultCacheHandler.php b/src/Runner/ResultCache/ResultCacheHandler.php index f0b054e2098..54621160556 100644 --- a/src/Runner/ResultCache/ResultCacheHandler.php +++ b/src/Runner/ResultCache/ResultCacheHandler.php @@ -11,7 +11,6 @@ use function round; use PHPUnit\Event\Event; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; use PHPUnit\Event\Telemetry\HRTime; use PHPUnit\Event\Test\ConsideredRisky; @@ -21,11 +20,12 @@ use PHPUnit\Event\Test\MarkedIncomplete; use PHPUnit\Event\Test\Prepared; use PHPUnit\Event\Test\Skipped; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\Framework\InvalidArgumentException; use PHPUnit\Framework\TestStatus\TestStatus; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ResultCacheHandler @@ -34,10 +34,6 @@ final class ResultCacheHandler private ?HRTime $time = null; private int $testSuite = 0; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(ResultCache $cache, Facade $facade) { $this->cache = $cache; @@ -134,10 +130,6 @@ private function duration(Event $event): float return round($event->telemetryInfo()->time()->duration($this->time)->asFloat(), 3); } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function registerSubscribers(Facade $facade): void { $facade->registerSubscribers( diff --git a/src/Runner/ResultCache/Subscriber/Subscriber.php b/src/Runner/ResultCache/Subscriber/Subscriber.php index 77abb6f1523..d64dd9f4a22 100644 --- a/src/Runner/ResultCache/Subscriber/Subscriber.php +++ b/src/Runner/ResultCache/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\Runner\ResultCache; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly ResultCacheHandler $handler; + private ResultCacheHandler $handler; public function __construct(ResultCacheHandler $handler) { diff --git a/src/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.php b/src/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.php index 6ca0701a54a..b2d934013ab 100644 --- a/src/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestConsideredRiskySubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ConsideredRiskySubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber +final readonly class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber { public function notify(ConsideredRisky $event): void { diff --git a/src/Runner/ResultCache/Subscriber/TestErroredSubscriber.php b/src/Runner/ResultCache/Subscriber/TestErroredSubscriber.php index 954551bdda3..ff34e0d8f33 100644 --- a/src/Runner/ResultCache/Subscriber/TestErroredSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestErroredSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ErroredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber +final readonly class TestErroredSubscriber extends Subscriber implements ErroredSubscriber { public function notify(Errored $event): void { diff --git a/src/Runner/ResultCache/Subscriber/TestFailedSubscriber.php b/src/Runner/ResultCache/Subscriber/TestFailedSubscriber.php index effdb0c2089..082fa51bd6a 100644 --- a/src/Runner/ResultCache/Subscriber/TestFailedSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestFailedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\FailedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFailedSubscriber extends Subscriber implements FailedSubscriber +final readonly class TestFailedSubscriber extends Subscriber implements FailedSubscriber { public function notify(Failed $event): void { diff --git a/src/Runner/ResultCache/Subscriber/TestFinishedSubscriber.php b/src/Runner/ResultCache/Subscriber/TestFinishedSubscriber.php index ff39f716bb6..65f75fcb6f5 100644 --- a/src/Runner/ResultCache/Subscriber/TestFinishedSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestFinishedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { /** * @throws \PHPUnit\Framework\InvalidArgumentException diff --git a/src/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.php b/src/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.php index 69364ac2da7..d9c65cf8cff 100644 --- a/src/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestMarkedIncompleteSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\MarkedIncompleteSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber +final readonly class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber { public function notify(MarkedIncomplete $event): void { diff --git a/src/Runner/ResultCache/Subscriber/TestPreparedSubscriber.php b/src/Runner/ResultCache/Subscriber/TestPreparedSubscriber.php index eee675b17de..a92b82777f0 100644 --- a/src/Runner/ResultCache/Subscriber/TestPreparedSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestPreparedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PreparedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +final readonly class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber { public function notify(Prepared $event): void { diff --git a/src/Runner/ResultCache/Subscriber/TestSkippedSubscriber.php b/src/Runner/ResultCache/Subscriber/TestSkippedSubscriber.php index 21c748b09b4..0e493bdc25b 100644 --- a/src/Runner/ResultCache/Subscriber/TestSkippedSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestSkippedSubscriber.php @@ -14,9 +14,11 @@ use PHPUnit\Event\Test\SkippedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber +final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber { /** * @throws \PHPUnit\Framework\InvalidArgumentException diff --git a/src/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.php b/src/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.php index c252823b7e5..1ef0cc3fb46 100644 --- a/src/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestSuiteFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber { public function notify(Finished $event): void { diff --git a/src/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.php b/src/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.php index 1c78b0ec904..cddedf511d6 100644 --- a/src/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.php +++ b/src/Runner/ResultCache/Subscriber/TestSuiteStartedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\StartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber +final readonly class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber { public function notify(Started $event): void { diff --git a/src/Runner/TestResult/Collector.php b/src/Runner/TestResult/Collector.php index bd6d540a931..e25dc3a21de 100644 --- a/src/Runner/TestResult/Collector.php +++ b/src/Runner/TestResult/Collector.php @@ -11,12 +11,15 @@ use function array_values; use function assert; +use function count; use function implode; use function str_contains; use PHPUnit\Event\Code\TestMethod; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; +use PHPUnit\Event\Test\AfterLastTestMethodErrored; +use PHPUnit\Event\Test\AfterLastTestMethodFailed; use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\BeforeFirstTestMethodFailed; use PHPUnit\Event\Test\ConsideredRisky; use PHPUnit\Event\Test\DeprecationTriggered; use PHPUnit\Event\Test\Errored; @@ -29,30 +32,31 @@ use PHPUnit\Event\Test\PhpNoticeTriggered; use PHPUnit\Event\Test\PhpunitDeprecationTriggered; use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitNoticeTriggered; use PHPUnit\Event\Test\PhpunitWarningTriggered; use PHPUnit\Event\Test\PhpWarningTriggered; use PHPUnit\Event\Test\Skipped as TestSkipped; use PHPUnit\Event\Test\WarningTriggered; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Event\TestRunner\DeprecationTriggered as TestRunnerDeprecationTriggered; use PHPUnit\Event\TestRunner\ExecutionStarted; +use PHPUnit\Event\TestRunner\NoticeTriggered as TestRunnerNoticeTriggered; use PHPUnit\Event\TestRunner\WarningTriggered as TestRunnerWarningTriggered; use PHPUnit\Event\TestSuite\Finished as TestSuiteFinished; use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; use PHPUnit\Event\TestSuite\Started as TestSuiteStarted; use PHPUnit\Event\TestSuite\TestSuiteForTestClass; use PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider; -use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\TestRunner\IssueFilter; use PHPUnit\TestRunner\TestResult\Issues\Issue; -use PHPUnit\TextUI\Configuration\Source; -use PHPUnit\TextUI\Configuration\SourceFilter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Collector { - private readonly Source $source; + private readonly IssueFilter $issueFilter; private int $numberOfTests = 0; private int $numberOfTestsRun = 0; private int $numberOfAssertions = 0; @@ -60,105 +64,111 @@ final class Collector private bool $currentTestSuiteForTestClassFailed = false; /** - * @psalm-var non-negative-int + * @var non-negative-int */ private int $numberOfIssuesIgnoredByBaseline = 0; /** - * @psalm-var list + * @var list */ private array $testErroredEvents = []; /** - * @psalm-var list + * @var list */ private array $testFailedEvents = []; /** - * @psalm-var list + * @var list */ private array $testMarkedIncompleteEvents = []; /** - * @psalm-var list + * @var list */ private array $testSuiteSkippedEvents = []; /** - * @psalm-var list + * @var list */ private array $testSkippedEvents = []; /** - * @psalm-var array> + * @var array> */ private array $testConsideredRiskyEvents = []; /** - * @psalm-var array> + * @var array> */ private array $testTriggeredPhpunitDeprecationEvents = []; /** - * @psalm-var array> + * @var array> */ private array $testTriggeredPhpunitErrorEvents = []; /** - * @psalm-var array> + * @var array> */ - private array $testTriggeredPhpunitWarningEvents = []; + private array $testTriggeredPhpunitNoticeEvents = []; /** - * @psalm-var list + * @var array> */ - private array $testRunnerTriggeredWarningEvents = []; + private array $testTriggeredPhpunitWarningEvents = []; /** - * @psalm-var list + * @var list */ private array $testRunnerTriggeredDeprecationEvents = []; /** - * @psalm-var array + * @var list + */ + private array $testRunnerTriggeredNoticeEvents = []; + + /** + * @var list + */ + private array $testRunnerTriggeredWarningEvents = []; + + /** + * @var array */ private array $errors = []; /** - * @psalm-var array + * @var array */ private array $deprecations = []; /** - * @psalm-var array + * @var array */ private array $notices = []; /** - * @psalm-var array + * @var array */ private array $warnings = []; /** - * @psalm-var array + * @var array */ private array $phpDeprecations = []; /** - * @psalm-var array + * @var array */ private array $phpNotices = []; /** - * @psalm-var array + * @var array */ private array $phpWarnings = []; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ - public function __construct(Facade $facade, Source $source) + public function __construct(Facade $facade, IssueFilter $issueFilter) { $facade->registerSubscribers( new ExecutionStartedSubscriber($this), @@ -168,6 +178,9 @@ public function __construct(Facade $facade, Source $source) new TestPreparedSubscriber($this), new TestFinishedSubscriber($this), new BeforeTestClassMethodErroredSubscriber($this), + new BeforeTestClassMethodFailedSubscriber($this), + new AfterTestClassMethodErroredSubscriber($this), + new AfterTestClassMethodFailedSubscriber($this), new TestErroredSubscriber($this), new TestFailedSubscriber($this), new TestMarkedIncompleteSubscriber($this), @@ -180,14 +193,16 @@ public function __construct(Facade $facade, Source $source) new TestTriggeredPhpNoticeSubscriber($this), new TestTriggeredPhpunitDeprecationSubscriber($this), new TestTriggeredPhpunitErrorSubscriber($this), + new TestTriggeredPhpunitNoticeSubscriber($this), new TestTriggeredPhpunitWarningSubscriber($this), new TestTriggeredPhpWarningSubscriber($this), new TestTriggeredWarningSubscriber($this), new TestRunnerTriggeredDeprecationSubscriber($this), + new TestRunnerTriggeredNoticeSubscriber($this), new TestRunnerTriggeredWarningSubscriber($this), ); - $this->source = $source; + $this->issueFilter = $issueFilter; } public function result(): TestResult @@ -204,8 +219,10 @@ public function result(): TestResult $this->testMarkedIncompleteEvents, $this->testTriggeredPhpunitDeprecationEvents, $this->testTriggeredPhpunitErrorEvents, + $this->testTriggeredPhpunitNoticeEvents, $this->testTriggeredPhpunitWarningEvents, $this->testRunnerTriggeredDeprecationEvents, + $this->testRunnerTriggeredNoticeEvents, $this->testRunnerTriggeredWarningEvents, array_values($this->errors), array_values($this->deprecations), @@ -245,9 +262,6 @@ public function testSuiteStarted(TestSuiteStarted $event): void $this->currentTestSuiteForTestClassFailed = false; } - /** - * @throws NoDataSetFromDataProviderException - */ public function testSuiteFinished(TestSuiteFinished $event): void { if ($this->currentTestSuiteForTestClassFailed) { @@ -262,6 +276,7 @@ public function testSuiteFinished(TestSuiteFinished $event): void if ($testSuite->isForTestMethodWithDataProvider()) { assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider); + assert(count($testSuite->tests()->asArray()) > 0); $test = $testSuite->tests()->asArray()[0]; @@ -298,6 +313,23 @@ public function beforeTestClassMethodErrored(BeforeFirstTestMethodErrored $event $this->numberOfTestsRun++; } + public function beforeTestClassMethodFailed(BeforeFirstTestMethodFailed $event): void + { + $this->testFailedEvents[] = $event; + + $this->numberOfTestsRun++; + } + + public function afterTestClassMethodErrored(AfterLastTestMethodErrored $event): void + { + $this->testErroredEvents[] = $event; + } + + public function afterTestClassMethodFailed(AfterLastTestMethodFailed $event): void + { + $this->testFailedEvents[] = $event; + } + public function testErrored(Errored $event): void { $this->testErroredEvents[] = $event; @@ -348,7 +380,7 @@ public function testConsideredRisky(ConsideredRisky $event): void public function testTriggeredDeprecation(DeprecationTriggered $event): void { - if ($event->ignoredByTest()) { + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } @@ -358,14 +390,6 @@ public function testTriggeredDeprecation(DeprecationTriggered $event): void return; } - if (!$this->source->ignoreSuppressionOfDeprecations() && $event->wasSuppressed()) { - return; - } - - if ($this->source->restrictDeprecations() && !(new SourceFilter)->includes($this->source, $event->file())) { - return; - } - $id = $this->issueId($event); if (!isset($this->deprecations[$id])) { @@ -374,6 +398,7 @@ public function testTriggeredDeprecation(DeprecationTriggered $event): void $event->line(), $event->message(), $event->test(), + $event->stackTrace(), ); return; @@ -384,7 +409,7 @@ public function testTriggeredDeprecation(DeprecationTriggered $event): void public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): void { - if ($event->ignoredByTest()) { + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } @@ -394,14 +419,6 @@ public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): voi return; } - if (!$this->source->ignoreSuppressionOfPhpDeprecations() && $event->wasSuppressed()) { - return; - } - - if ($this->source->restrictDeprecations() && !(new SourceFilter)->includes($this->source, $event->file())) { - return; - } - $id = $this->issueId($event); if (!isset($this->phpDeprecations[$id])) { @@ -427,9 +444,18 @@ public function testTriggeredPhpunitDeprecation(PhpunitDeprecationTriggered $eve $this->testTriggeredPhpunitDeprecationEvents[$event->test()->id()][] = $event; } + public function testTriggeredPhpunitNotice(PhpunitNoticeTriggered $event): void + { + if (!isset($this->testTriggeredPhpunitNoticeEvents[$event->test()->id()])) { + $this->testTriggeredPhpunitNoticeEvents[$event->test()->id()] = []; + } + + $this->testTriggeredPhpunitNoticeEvents[$event->test()->id()][] = $event; + } + public function testTriggeredError(ErrorTriggered $event): void { - if (!$this->source->ignoreSuppressionOfErrors() && $event->wasSuppressed()) { + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } @@ -451,17 +477,13 @@ public function testTriggeredError(ErrorTriggered $event): void public function testTriggeredNotice(NoticeTriggered $event): void { - if ($event->ignoredByBaseline()) { - $this->numberOfIssuesIgnoredByBaseline++; - + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } - if (!$this->source->ignoreSuppressionOfNotices() && $event->wasSuppressed()) { - return; - } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; - if ($this->source->restrictNotices() && !(new SourceFilter)->includes($this->source, $event->file())) { return; } @@ -483,17 +505,13 @@ public function testTriggeredNotice(NoticeTriggered $event): void public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void { - if ($event->ignoredByBaseline()) { - $this->numberOfIssuesIgnoredByBaseline++; - + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } - if (!$this->source->ignoreSuppressionOfPhpNotices() && $event->wasSuppressed()) { - return; - } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; - if ($this->source->restrictNotices() && !(new SourceFilter)->includes($this->source, $event->file())) { return; } @@ -515,17 +533,13 @@ public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void public function testTriggeredWarning(WarningTriggered $event): void { - if ($event->ignoredByBaseline()) { - $this->numberOfIssuesIgnoredByBaseline++; - + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } - if (!$this->source->ignoreSuppressionOfWarnings() && $event->wasSuppressed()) { - return; - } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; - if ($this->source->restrictWarnings() && !(new SourceFilter)->includes($this->source, $event->file())) { return; } @@ -547,17 +561,13 @@ public function testTriggeredWarning(WarningTriggered $event): void public function testTriggeredPhpWarning(PhpWarningTriggered $event): void { - if ($event->ignoredByBaseline()) { - $this->numberOfIssuesIgnoredByBaseline++; - + if (!$this->issueFilter->shouldBeProcessed($event)) { return; } - if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { - return; - } + if ($event->ignoredByBaseline()) { + $this->numberOfIssuesIgnoredByBaseline++; - if ($this->source->restrictWarnings() && !(new SourceFilter)->includes($this->source, $event->file())) { return; } @@ -600,6 +610,11 @@ public function testRunnerTriggeredDeprecation(TestRunnerDeprecationTriggered $e $this->testRunnerTriggeredDeprecationEvents[] = $event; } + public function testRunnerTriggeredNotice(TestRunnerNoticeTriggered $event): void + { + $this->testRunnerTriggeredNoticeEvents[] = $event; + } + public function testRunnerTriggeredWarning(TestRunnerWarningTriggered $event): void { $this->testRunnerTriggeredWarningEvents[] = $event; @@ -607,53 +622,53 @@ public function testRunnerTriggeredWarning(TestRunnerWarningTriggered $event): v public function hasErroredTests(): bool { - return !empty($this->testErroredEvents); + return $this->testErroredEvents !== []; } public function hasFailedTests(): bool { - return !empty($this->testFailedEvents); + return $this->testFailedEvents !== []; } public function hasRiskyTests(): bool { - return !empty($this->testConsideredRiskyEvents); + return $this->testConsideredRiskyEvents !== []; } public function hasSkippedTests(): bool { - return !empty($this->testSkippedEvents); + return $this->testSkippedEvents !== []; } public function hasIncompleteTests(): bool { - return !empty($this->testMarkedIncompleteEvents); + return $this->testMarkedIncompleteEvents !== []; } public function hasDeprecations(): bool { - return !empty($this->deprecations) || - !empty($this->phpDeprecations) || - !empty($this->testTriggeredPhpunitDeprecationEvents) || - !empty($this->testRunnerTriggeredDeprecationEvents); + return $this->deprecations !== [] || + $this->phpDeprecations !== [] || + $this->testTriggeredPhpunitDeprecationEvents !== [] || + $this->testRunnerTriggeredDeprecationEvents !== []; } public function hasNotices(): bool { - return !empty($this->notices) || - !empty($this->phpNotices); + return $this->notices !== [] || + $this->phpNotices !== []; } public function hasWarnings(): bool { - return !empty($this->warnings) || - !empty($this->phpWarnings) || - !empty($this->testTriggeredPhpunitWarningEvents) || - !empty($this->testRunnerTriggeredWarningEvents); + return $this->warnings !== [] || + $this->phpWarnings !== [] || + $this->testTriggeredPhpunitWarningEvents !== [] || + $this->testRunnerTriggeredWarningEvents !== []; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ private function issueId(DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpWarningTriggered|WarningTriggered $event): string { diff --git a/src/Runner/TestResult/Facade.php b/src/Runner/TestResult/Facade.php index 790e99705d6..4862d686001 100644 --- a/src/Runner/TestResult/Facade.php +++ b/src/Runner/TestResult/Facade.php @@ -9,40 +9,32 @@ */ namespace PHPUnit\TestRunner\TestResult; -use PHPUnit\Event\EventFacadeIsSealedException; +use function str_contains; use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\Runner\DeprecationCollector\Facade as DeprecationCollectorFacade; +use PHPUnit\TestRunner\IssueFilter; +use PHPUnit\TextUI\Configuration\Configuration; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Facade { private static ?Collector $collector = null; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public static function init(): void { self::collector(); } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public static function result(): TestResult { return self::collector()->result(); } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public static function shouldStop(): bool { $configuration = ConfigurationRegistry::get(); @@ -64,7 +56,7 @@ public static function shouldStop(): bool return true; } - if ($configuration->stopOnDeprecation() && $collector->hasDeprecations()) { + if (self::stopOnDeprecation($configuration)) { return true; } @@ -83,10 +75,6 @@ public static function shouldStop(): bool return false; } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private static function collector(): Collector { if (self::$collector === null) { @@ -94,10 +82,31 @@ private static function collector(): Collector self::$collector = new Collector( EventFacade::instance(), - $configuration->source(), + new IssueFilter($configuration->source()), ); } return self::$collector; } + + private static function stopOnDeprecation(Configuration $configuration): bool + { + if (!$configuration->stopOnDeprecation()) { + return false; + } + + $deprecations = DeprecationCollectorFacade::filteredDeprecations(); + + if (!$configuration->hasSpecificDeprecationToStopOn()) { + return $deprecations !== []; + } + + foreach ($deprecations as $deprecation) { + if (str_contains($deprecation, $configuration->specificDeprecationToStopOn())) { + return true; + } + } + + return false; + } } diff --git a/src/Runner/TestResult/Issue.php b/src/Runner/TestResult/Issue.php index 93fe6d0334e..12ade5c3f5d 100644 --- a/src/Runner/TestResult/Issue.php +++ b/src/Runner/TestResult/Issue.php @@ -9,53 +9,63 @@ */ namespace PHPUnit\TestRunner\TestResult\Issues; +use function array_keys; +use function count; use PHPUnit\Event\Code\Test; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Issue { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private readonly string $file; /** - * @psalm-var positive-int + * @var positive-int */ private readonly int $line; /** - * @psalm-var non-empty-string + * @var non-empty-string */ private readonly string $description; /** - * @psalm-var non-empty-array + * @var non-empty-array */ private array $triggeringTests; /** - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line - * @psalm-param non-empty-string $description + * @var ?non-empty-string */ - public static function from(string $file, int $line, string $description, Test $triggeringTest): self + private ?string $stackTrace; + + /** + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $description + */ + public static function from(string $file, int $line, string $description, Test $triggeringTest, ?string $stackTrace = null): self { - return new self($file, $line, $description, $triggeringTest); + return new self($file, $line, $description, $triggeringTest, $stackTrace); } /** - * @psalm-param non-empty-string $file - * @psalm-param positive-int $line - * @psalm-param non-empty-string $description + * @param non-empty-string $file + * @param positive-int $line + * @param non-empty-string $description */ - private function __construct(string $file, int $line, string $description, Test $triggeringTest) + private function __construct(string $file, int $line, string $description, Test $triggeringTest, ?string $stackTrace) { $this->file = $file; $this->line = $line; $this->description = $description; + $this->stackTrace = $stackTrace; $this->triggeringTests = [ $triggeringTest->id() => [ @@ -80,7 +90,7 @@ public function triggeredBy(Test $test): void } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function file(): string { @@ -88,7 +98,7 @@ public function file(): string } /** - * @psalm-return positive-int + * @return positive-int */ public function line(): int { @@ -96,7 +106,7 @@ public function line(): int } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function description(): string { @@ -104,10 +114,32 @@ public function description(): string } /** - * @psalm-return non-empty-array + * @return non-empty-array */ public function triggeringTests(): array { return $this->triggeringTests; } + + /** + * @phpstan-assert-if-true !null $this->stackTrace + */ + public function hasStackTrace(): bool + { + return $this->stackTrace !== null; + } + + /** + * @return ?non-empty-string + */ + public function stackTrace(): ?string + { + return $this->stackTrace; + } + + public function triggeredInTest(): bool + { + return count($this->triggeringTests) === 1 && + $this->file === $this->triggeringTests[array_keys($this->triggeringTests)[0]]['test']->file(); + } } diff --git a/src/Runner/TestResult/PassedTests.php b/src/Runner/TestResult/PassedTests.php index 8462f97cb6a..edd51ed7d4f 100644 --- a/src/Runner/TestResult/PassedTests.php +++ b/src/Runner/TestResult/PassedTests.php @@ -13,12 +13,13 @@ use function assert; use function in_array; use PHPUnit\Event\Code\TestMethod; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Framework\TestSize\Known; use PHPUnit\Framework\TestSize\TestSize; use PHPUnit\Metadata\Api\Groups; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class PassedTests @@ -26,12 +27,12 @@ final class PassedTests private static ?self $instance = null; /** - * @psalm-var list + * @var list */ private array $passedTestClasses = []; /** - * @psalm-var array + * @var array */ private array $passedTestMethods = []; @@ -47,16 +48,13 @@ public static function instance(): self } /** - * @psalm-param class-string $className + * @param class-string $className */ public function testClassPassed(string $className): void { $this->passedTestClasses[] = $className; } - /** - * @throws NoDataSetFromDataProviderException - */ public function testMethodPassed(TestMethod $test, mixed $returnValue): void { $size = (new Groups)->size( @@ -84,7 +82,7 @@ public function import(self $other): void } /** - * @psalm-param class-string $className + * @param class-string $className */ public function hasTestClassPassed(string $className): bool { diff --git a/src/Runner/TestResult/Subscriber/AfterTestClassMethodErroredSubscriber.php b/src/Runner/TestResult/Subscriber/AfterTestClassMethodErroredSubscriber.php new file mode 100644 index 00000000000..eb94433bdeb --- /dev/null +++ b/src/Runner/TestResult/Subscriber/AfterTestClassMethodErroredSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner\TestResult; + +use PHPUnit\Event\Test\AfterLastTestMethodErrored; +use PHPUnit\Event\Test\AfterLastTestMethodErroredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class AfterTestClassMethodErroredSubscriber extends Subscriber implements AfterLastTestMethodErroredSubscriber +{ + public function notify(AfterLastTestMethodErrored $event): void + { + $this->collector()->afterTestClassMethodErrored($event); + } +} diff --git a/src/Runner/TestResult/Subscriber/AfterTestClassMethodFailedSubscriber.php b/src/Runner/TestResult/Subscriber/AfterTestClassMethodFailedSubscriber.php new file mode 100644 index 00000000000..e207ba1d079 --- /dev/null +++ b/src/Runner/TestResult/Subscriber/AfterTestClassMethodFailedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner\TestResult; + +use PHPUnit\Event\Test\AfterLastTestMethodFailed; +use PHPUnit\Event\Test\AfterLastTestMethodFailedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class AfterTestClassMethodFailedSubscriber extends Subscriber implements AfterLastTestMethodFailedSubscriber +{ + public function notify(AfterLastTestMethodFailed $event): void + { + $this->collector()->afterTestClassMethodFailed($event); + } +} diff --git a/src/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php b/src/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php index cfa5e9d5d77..1929125e573 100644 --- a/src/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php +++ b/src/Runner/TestResult/Subscriber/BeforeTestClassMethodErroredSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\BeforeFirstTestMethodErroredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class BeforeTestClassMethodErroredSubscriber extends Subscriber implements BeforeFirstTestMethodErroredSubscriber +final readonly class BeforeTestClassMethodErroredSubscriber extends Subscriber implements BeforeFirstTestMethodErroredSubscriber { public function notify(BeforeFirstTestMethodErrored $event): void { diff --git a/src/Runner/TestResult/Subscriber/BeforeTestClassMethodFailedSubscriber.php b/src/Runner/TestResult/Subscriber/BeforeTestClassMethodFailedSubscriber.php new file mode 100644 index 00000000000..0e69855a0ae --- /dev/null +++ b/src/Runner/TestResult/Subscriber/BeforeTestClassMethodFailedSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner\TestResult; + +use PHPUnit\Event\Test\BeforeFirstTestMethodFailed; +use PHPUnit\Event\Test\BeforeFirstTestMethodFailedSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class BeforeTestClassMethodFailedSubscriber extends Subscriber implements BeforeFirstTestMethodFailedSubscriber +{ + public function notify(BeforeFirstTestMethodFailed $event): void + { + $this->collector()->beforeTestClassMethodFailed($event); + } +} diff --git a/src/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php b/src/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php index 2d1c1a5f0dc..b54ae9e724a 100644 --- a/src/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/ExecutionStartedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestRunner\ExecutionStartedSubscriber as TestRunnerExecutionStartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ExecutionStartedSubscriber extends Subscriber implements TestRunnerExecutionStartedSubscriber +final readonly class ExecutionStartedSubscriber extends Subscriber implements TestRunnerExecutionStartedSubscriber { public function notify(ExecutionStarted $event): void { diff --git a/src/Runner/TestResult/Subscriber/Subscriber.php b/src/Runner/TestResult/Subscriber/Subscriber.php index 7bb28d20aee..36be4941b31 100644 --- a/src/Runner/TestResult/Subscriber/Subscriber.php +++ b/src/Runner/TestResult/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\TestRunner\TestResult; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly Collector $collector; + private Collector $collector; public function __construct(Collector $collector) { diff --git a/src/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.php b/src/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.php index 777ca47a8e6..8584fddcc27 100644 --- a/src/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestConsideredRiskySubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ConsideredRiskySubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber +final readonly class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber { public function notify(ConsideredRisky $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestErroredSubscriber.php b/src/Runner/TestResult/Subscriber/TestErroredSubscriber.php index 132482b76ba..a97c21a689e 100644 --- a/src/Runner/TestResult/Subscriber/TestErroredSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestErroredSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ErroredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber +final readonly class TestErroredSubscriber extends Subscriber implements ErroredSubscriber { public function notify(Errored $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestFailedSubscriber.php b/src/Runner/TestResult/Subscriber/TestFailedSubscriber.php index 7382b0bb457..118b304cdc6 100644 --- a/src/Runner/TestResult/Subscriber/TestFailedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestFailedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\FailedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFailedSubscriber extends Subscriber implements FailedSubscriber +final readonly class TestFailedSubscriber extends Subscriber implements FailedSubscriber { public function notify(Failed $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestFinishedSubscriber.php b/src/Runner/TestResult/Subscriber/TestFinishedSubscriber.php index 44aea9f981e..37fe67d0f47 100644 --- a/src/Runner/TestResult/Subscriber/TestFinishedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { public function notify(Finished $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php b/src/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php index 9c486c5316a..c9d13ab5620 100644 --- a/src/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\MarkedIncompleteSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber +final readonly class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber { public function notify(MarkedIncomplete $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestPreparedSubscriber.php b/src/Runner/TestResult/Subscriber/TestPreparedSubscriber.php index cc91590da8c..6dd05ca75ec 100644 --- a/src/Runner/TestResult/Subscriber/TestPreparedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestPreparedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PreparedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +final readonly class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber { public function notify(Prepared $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.php b/src/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.php index 4dce071c6e6..36b3ea03917 100644 --- a/src/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestRunnerTriggeredDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestRunner\DeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestRunnerTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber +final readonly class TestRunnerTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber { public function notify(DeprecationTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestRunnerTriggeredNoticeSubscriber.php b/src/Runner/TestResult/Subscriber/TestRunnerTriggeredNoticeSubscriber.php new file mode 100644 index 00000000000..8fd9bc4b86e --- /dev/null +++ b/src/Runner/TestResult/Subscriber/TestRunnerTriggeredNoticeSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner\TestResult; + +use PHPUnit\Event\TestRunner\NoticeTriggered; +use PHPUnit\Event\TestRunner\NoticeTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestRunnerTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber +{ + public function notify(NoticeTriggered $event): void + { + $this->collector()->testRunnerTriggeredNotice($event); + } +} diff --git a/src/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.php b/src/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.php index ddc2564dd34..cc01d5db8c1 100644 --- a/src/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestRunnerTriggeredWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestRunner\WarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestRunnerTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber +final readonly class TestRunnerTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber { public function notify(WarningTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestSkippedSubscriber.php b/src/Runner/TestResult/Subscriber/TestSkippedSubscriber.php index 651e12f5c7e..152db85bf77 100644 --- a/src/Runner/TestResult/Subscriber/TestSkippedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestSkippedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\SkippedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber +final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber { public function notify(Skipped $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.php b/src/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.php index 5ad8c1a727f..e5f2acac512 100644 --- a/src/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestSuiteFinishedSubscriber.php @@ -9,18 +9,16 @@ */ namespace PHPUnit\TestRunner\TestResult; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\Event\TestSuite\Finished; use PHPUnit\Event\TestSuite\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber { - /** - * @throws NoDataSetFromDataProviderException - */ public function notify(Finished $event): void { $this->collector()->testSuiteFinished($event); diff --git a/src/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.php b/src/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.php index a16ae17e692..0c7cd7abb8d 100644 --- a/src/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestSuiteSkippedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\SkippedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteSkippedSubscriber extends Subscriber implements SkippedSubscriber +final readonly class TestSuiteSkippedSubscriber extends Subscriber implements SkippedSubscriber { public function notify(Skipped $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.php b/src/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.php index d50b1232209..d3cb3bffdd0 100644 --- a/src/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestSuiteStartedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestSuite\StartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber +final readonly class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber { public function notify(Started $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php index 7692bae372e..81e93eb3472 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\DeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber +final readonly class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber { public function notify(DeprecationTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.php index fc48c2d435f..0aef461dbcb 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredErrorSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ErrorTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredErrorSubscriber extends Subscriber implements ErrorTriggeredSubscriber +final readonly class TestTriggeredErrorSubscriber extends Subscriber implements ErrorTriggeredSubscriber { public function notify(ErrorTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php index dbedc8bd593..67b73c06a01 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredNoticeSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\NoticeTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber +final readonly class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber { public function notify(NoticeTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php index a1bbc804193..5cd17e3f981 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpDeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber +final readonly class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber { public function notify(PhpDeprecationTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php index 16f442c8888..9af0d3206fb 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpNoticeSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpNoticeTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber +final readonly class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber { public function notify(PhpNoticeTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php index 9ff153160e6..18eaf4f17a6 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpWarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber +final readonly class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber { public function notify(PhpWarningTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php index bb3ce03cac9..3475f11aae1 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpunitDeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpunitDeprecationSubscriber extends Subscriber implements PhpunitDeprecationTriggeredSubscriber +final readonly class TestTriggeredPhpunitDeprecationSubscriber extends Subscriber implements PhpunitDeprecationTriggeredSubscriber { public function notify(PhpunitDeprecationTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php index e61bf0b406d..0ceba9caa29 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitErrorSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpunitErrorTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpunitErrorSubscriber extends Subscriber implements PhpunitErrorTriggeredSubscriber +final readonly class TestTriggeredPhpunitErrorSubscriber extends Subscriber implements PhpunitErrorTriggeredSubscriber { public function notify(PhpunitErrorTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitNoticeSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitNoticeSubscriber.php new file mode 100644 index 00000000000..f7ebfd1463e --- /dev/null +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitNoticeSubscriber.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestRunner\TestResult; + +use PHPUnit\Event\Test\PhpunitNoticeTriggered; +use PHPUnit\Event\Test\PhpunitNoticeTriggeredSubscriber; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class TestTriggeredPhpunitNoticeSubscriber extends Subscriber implements PhpunitNoticeTriggeredSubscriber +{ + public function notify(PhpunitNoticeTriggered $event): void + { + $this->collector()->testTriggeredPhpunitNotice($event); + } +} diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php index 89f2488626f..376c4b60e80 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredPhpunitWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpunitWarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpunitWarningSubscriber extends Subscriber implements PhpunitWarningTriggeredSubscriber +final readonly class TestTriggeredPhpunitWarningSubscriber extends Subscriber implements PhpunitWarningTriggeredSubscriber { public function notify(PhpunitWarningTriggered $event): void { diff --git a/src/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.php b/src/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.php index ac34bb270a1..d5fe3ed5cd5 100644 --- a/src/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.php +++ b/src/Runner/TestResult/Subscriber/TestTriggeredWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\WarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber +final readonly class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber { public function notify(WarningTriggered $event): void { diff --git a/src/Runner/TestResult/TestResult.php b/src/Runner/TestResult/TestResult.php index 2c572608b94..0820d21ad87 100644 --- a/src/Runner/TestResult/TestResult.php +++ b/src/Runner/TestResult/TestResult.php @@ -10,21 +10,28 @@ namespace PHPUnit\TestRunner\TestResult; use function count; +use PHPUnit\Event\Test\AfterLastTestMethodErrored; +use PHPUnit\Event\Test\AfterLastTestMethodFailed; use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\BeforeFirstTestMethodFailed; use PHPUnit\Event\Test\ConsideredRisky; use PHPUnit\Event\Test\Errored; use PHPUnit\Event\Test\Failed; use PHPUnit\Event\Test\MarkedIncomplete; use PHPUnit\Event\Test\PhpunitDeprecationTriggered; use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitNoticeTriggered; use PHPUnit\Event\Test\PhpunitWarningTriggered; use PHPUnit\Event\Test\Skipped as TestSkipped; use PHPUnit\Event\TestRunner\DeprecationTriggered as TestRunnerDeprecationTriggered; +use PHPUnit\Event\TestRunner\NoticeTriggered as TestRunnerNoticeTriggered; use PHPUnit\Event\TestRunner\WarningTriggered as TestRunnerWarningTriggered; use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; use PHPUnit\TestRunner\TestResult\Issues\Issue; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class TestResult @@ -34,122 +41,134 @@ private int $numberOfAssertions; /** - * @psalm-var list + * @var list */ private array $testErroredEvents; /** - * @psalm-var list + * @var list */ private array $testFailedEvents; /** - * @psalm-var list + * @var list */ private array $testMarkedIncompleteEvents; /** - * @psalm-var list + * @var list */ private array $testSuiteSkippedEvents; /** - * @psalm-var list + * @var list */ private array $testSkippedEvents; /** - * @psalm-var array> + * @var array> */ private array $testConsideredRiskyEvents; /** - * @psalm-var array> + * @var array> */ private array $testTriggeredPhpunitDeprecationEvents; /** - * @psalm-var array> + * @var array> */ private array $testTriggeredPhpunitErrorEvents; /** - * @psalm-var array> + * @var array> + */ + private array $testTriggeredPhpunitNoticeEvents; + + /** + * @var array> */ private array $testTriggeredPhpunitWarningEvents; /** - * @psalm-var list + * @var list */ private array $testRunnerTriggeredDeprecationEvents; /** - * @psalm-var list + * @var list + */ + private array $testRunnerTriggeredNoticeEvents; + + /** + * @var list */ private array $testRunnerTriggeredWarningEvents; /** - * @psalm-var list + * @var list */ private array $errors; /** - * @psalm-var list + * @var list */ private array $deprecations; /** - * @psalm-var list + * @var list */ private array $notices; /** - * @psalm-var list + * @var list */ private array $warnings; /** - * @psalm-var list + * @var list */ private array $phpDeprecations; /** - * @psalm-var list + * @var list */ private array $phpNotices; /** - * @psalm-var list + * @var list */ private array $phpWarnings; /** - * @psalm-var non-negative-int + * @var non-negative-int */ private int $numberOfIssuesIgnoredByBaseline; /** - * @psalm-param list $testErroredEvents - * @psalm-param list $testFailedEvents - * @psalm-param array> $testConsideredRiskyEvents - * @psalm-param list $testSuiteSkippedEvents - * @psalm-param list $testSkippedEvents - * @psalm-param list $testMarkedIncompleteEvents - * @psalm-param array> $testTriggeredPhpunitDeprecationEvents - * @psalm-param array> $testTriggeredPhpunitErrorEvents - * @psalm-param array> $testTriggeredPhpunitWarningEvents - * @psalm-param list $testRunnerTriggeredDeprecationEvents - * @psalm-param list $testRunnerTriggeredWarningEvents - * @psalm-param list $errors - * @psalm-param list $deprecations - * @psalm-param list $notices - * @psalm-param list $warnings - * @psalm-param list $phpDeprecations - * @psalm-param list $phpNotices - * @psalm-param list $phpWarnings - * @psalm-param non-negative-int $numberOfIssuesIgnoredByBaseline - */ - public function __construct(int $numberOfTests, int $numberOfTestsRun, int $numberOfAssertions, array $testErroredEvents, array $testFailedEvents, array $testConsideredRiskyEvents, array $testSuiteSkippedEvents, array $testSkippedEvents, array $testMarkedIncompleteEvents, array $testTriggeredPhpunitDeprecationEvents, array $testTriggeredPhpunitErrorEvents, array $testTriggeredPhpunitWarningEvents, array $testRunnerTriggeredDeprecationEvents, array $testRunnerTriggeredWarningEvents, array $errors, array $deprecations, array $notices, array $warnings, array $phpDeprecations, array $phpNotices, array $phpWarnings, int $numberOfIssuesIgnoredByBaseline) + * @param list $testErroredEvents + * @param list $testFailedEvents + * @param array> $testConsideredRiskyEvents + * @param list $testSuiteSkippedEvents + * @param list $testSkippedEvents + * @param list $testMarkedIncompleteEvents + * @param array> $testTriggeredPhpunitDeprecationEvents + * @param array> $testTriggeredPhpunitErrorEvents + * @param array> $testTriggeredPhpunitNoticeEvents + * @param array> $testTriggeredPhpunitWarningEvents + * @param list $testRunnerTriggeredDeprecationEvents + * @param list $testRunnerTriggeredNoticeEvents + * @param list $testRunnerTriggeredWarningEvents + * @param list $errors + * @param list $deprecations + * @param list $notices + * @param list $warnings + * @param list $phpDeprecations + * @param list $phpNotices + * @param list $phpWarnings + * @param non-negative-int $numberOfIssuesIgnoredByBaseline + */ + public function __construct(int $numberOfTests, int $numberOfTestsRun, int $numberOfAssertions, array $testErroredEvents, array $testFailedEvents, array $testConsideredRiskyEvents, array $testSuiteSkippedEvents, array $testSkippedEvents, array $testMarkedIncompleteEvents, array $testTriggeredPhpunitDeprecationEvents, array $testTriggeredPhpunitErrorEvents, array $testTriggeredPhpunitNoticeEvents, array $testTriggeredPhpunitWarningEvents, array $testRunnerTriggeredDeprecationEvents, array $testRunnerTriggeredNoticeEvents, array $testRunnerTriggeredWarningEvents, array $errors, array $deprecations, array $notices, array $warnings, array $phpDeprecations, array $phpNotices, array $phpWarnings, int $numberOfIssuesIgnoredByBaseline) { $this->numberOfTests = $numberOfTests; $this->numberOfTestsRun = $numberOfTestsRun; @@ -162,8 +181,10 @@ public function __construct(int $numberOfTests, int $numberOfTestsRun, int $numb $this->testMarkedIncompleteEvents = $testMarkedIncompleteEvents; $this->testTriggeredPhpunitDeprecationEvents = $testTriggeredPhpunitDeprecationEvents; $this->testTriggeredPhpunitErrorEvents = $testTriggeredPhpunitErrorEvents; + $this->testTriggeredPhpunitNoticeEvents = $testTriggeredPhpunitNoticeEvents; $this->testTriggeredPhpunitWarningEvents = $testTriggeredPhpunitWarningEvents; $this->testRunnerTriggeredDeprecationEvents = $testRunnerTriggeredDeprecationEvents; + $this->testRunnerTriggeredNoticeEvents = $testRunnerTriggeredNoticeEvents; $this->testRunnerTriggeredWarningEvents = $testRunnerTriggeredWarningEvents; $this->errors = $errors; $this->deprecations = $deprecations; @@ -186,7 +207,7 @@ public function numberOfAssertions(): int } /** - * @psalm-return list + * @return list */ public function testErroredEvents(): array { @@ -204,7 +225,7 @@ public function hasTestErroredEvents(): bool } /** - * @psalm-return list + * @return list */ public function testFailedEvents(): array { @@ -222,7 +243,7 @@ public function hasTestFailedEvents(): bool } /** - * @psalm-return array> + * @return array> */ public function testConsideredRiskyEvents(): array { @@ -240,7 +261,7 @@ public function hasTestConsideredRiskyEvents(): bool } /** - * @psalm-return list + * @return list */ public function testSuiteSkippedEvents(): array { @@ -258,7 +279,7 @@ public function hasTestSuiteSkippedEvents(): bool } /** - * @psalm-return list + * @return list */ public function testSkippedEvents(): array { @@ -276,7 +297,7 @@ public function hasTestSkippedEvents(): bool } /** - * @psalm-return list + * @return list */ public function testMarkedIncompleteEvents(): array { @@ -294,7 +315,7 @@ public function hasTestMarkedIncompleteEvents(): bool } /** - * @psalm-return array> + * @return array> */ public function testTriggeredPhpunitDeprecationEvents(): array { @@ -312,7 +333,7 @@ public function hasTestTriggeredPhpunitDeprecationEvents(): bool } /** - * @psalm-return array> + * @return array> */ public function testTriggeredPhpunitErrorEvents(): array { @@ -330,7 +351,25 @@ public function hasTestTriggeredPhpunitErrorEvents(): bool } /** - * @psalm-return array> + * @return array> + */ + public function testTriggeredPhpunitNoticeEvents(): array + { + return $this->testTriggeredPhpunitNoticeEvents; + } + + public function numberOfTestsWithTestTriggeredPhpunitNoticeEvents(): int + { + return count($this->testTriggeredPhpunitNoticeEvents); + } + + public function hasTestTriggeredPhpunitNoticeEvents(): bool + { + return $this->numberOfTestsWithTestTriggeredPhpunitNoticeEvents() > 0; + } + + /** + * @return array> */ public function testTriggeredPhpunitWarningEvents(): array { @@ -348,7 +387,7 @@ public function hasTestTriggeredPhpunitWarningEvents(): bool } /** - * @psalm-return list + * @return list */ public function testRunnerTriggeredDeprecationEvents(): array { @@ -366,7 +405,25 @@ public function hasTestRunnerTriggeredDeprecationEvents(): bool } /** - * @psalm-return list + * @return list + */ + public function testRunnerTriggeredNoticeEvents(): array + { + return $this->testRunnerTriggeredNoticeEvents; + } + + public function numberOfTestRunnerTriggeredNoticeEvents(): int + { + return count($this->testRunnerTriggeredNoticeEvents); + } + + public function hasTestRunnerTriggeredNoticeEvents(): bool + { + return $this->numberOfTestRunnerTriggeredNoticeEvents() > 0; + } + + /** + * @return list */ public function testRunnerTriggeredWarningEvents(): array { @@ -407,13 +464,14 @@ public function hasTestsWithIssues(): bool return $this->hasRiskyTests() || $this->hasIncompleteTests() || $this->hasDeprecations() || - !empty($this->errors) || + $this->errors !== [] || $this->hasNotices() || - $this->hasWarnings(); + $this->hasWarnings() || + $this->hasPhpunitNotices(); } /** - * @psalm-return list + * @return list */ public function errors(): array { @@ -421,7 +479,7 @@ public function errors(): array } /** - * @psalm-return list + * @return list */ public function deprecations(): array { @@ -429,7 +487,7 @@ public function deprecations(): array } /** - * @psalm-return list + * @return list */ public function notices(): array { @@ -437,7 +495,7 @@ public function notices(): array } /** - * @psalm-return list + * @return list */ public function warnings(): array { @@ -445,7 +503,7 @@ public function warnings(): array } /** - * @psalm-return list + * @return list */ public function phpDeprecations(): array { @@ -453,7 +511,7 @@ public function phpDeprecations(): array } /** - * @psalm-return list + * @return list */ public function phpNotices(): array { @@ -461,7 +519,7 @@ public function phpNotices(): array } /** - * @psalm-return list + * @return list */ public function phpWarnings(): array { @@ -490,6 +548,28 @@ public function hasDeprecations(): bool return $this->numberOfDeprecations() > 0; } + public function hasPhpOrUserDeprecations(): bool + { + return $this->numberOfPhpOrUserDeprecations() > 0; + } + + public function numberOfPhpOrUserDeprecations(): int + { + return count($this->deprecations) + + count($this->phpDeprecations); + } + + public function hasPhpunitDeprecations(): bool + { + return $this->numberOfPhpunitDeprecations() > 0; + } + + public function numberOfPhpunitDeprecations(): int + { + return count($this->testTriggeredPhpunitDeprecationEvents) + + count($this->testRunnerTriggeredDeprecationEvents); + } + public function numberOfDeprecations(): int { return count($this->deprecations) + @@ -524,17 +604,17 @@ public function numberOfWarnings(): int public function hasIncompleteTests(): bool { - return !empty($this->testMarkedIncompleteEvents); + return $this->testMarkedIncompleteEvents !== []; } public function hasRiskyTests(): bool { - return !empty($this->testConsideredRiskyEvents); + return $this->testConsideredRiskyEvents !== []; } public function hasSkippedTests(): bool { - return !empty($this->testSkippedEvents); + return $this->testSkippedEvents !== []; } public function hasIssuesIgnoredByBaseline(): bool @@ -543,10 +623,21 @@ public function hasIssuesIgnoredByBaseline(): bool } /** - * @psalm-return non-negative-int + * @return non-negative-int */ public function numberOfIssuesIgnoredByBaseline(): int { return $this->numberOfIssuesIgnoredByBaseline; } + + public function hasPhpunitNotices(): bool + { + return $this->numberOfPhpunitNotices() > 0; + } + + public function numberOfPhpunitNotices(): int + { + return $this->numberOfTestsWithTestTriggeredPhpunitNoticeEvents() + + $this->numberOfTestRunnerTriggeredNoticeEvents(); + } } diff --git a/src/Runner/TestSuiteLoader.php b/src/Runner/TestSuiteLoader.php index 84d4b2dbd17..3d01964deaa 100644 --- a/src/Runner/TestSuiteLoader.php +++ b/src/Runner/TestSuiteLoader.php @@ -10,9 +10,7 @@ namespace PHPUnit\Runner; use function array_diff; -use function array_values; use function basename; -use function class_exists; use function get_declared_classes; use function realpath; use function str_ends_with; @@ -23,22 +21,26 @@ use ReflectionClass; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestSuiteLoader { /** - * @psalm-var list + * @var list */ private static array $declaredClasses = []; /** - * @psalm-var array> + * @var array> */ private static array $fileToClassesMap = []; /** * @throws Exception + * + * @return ReflectionClass */ public function load(string $suiteClassFile): ReflectionClass { @@ -77,11 +79,13 @@ public function load(string $suiteClassFile): ReflectionClass throw $e; } - if (!class_exists($suiteClassName)) { - throw new ClassCannotBeFoundException($suiteClassName, $suiteClassFile); + foreach ($loadedClasses as $className) { + if (str_ends_with(strtolower($className), strtolower($suiteClassName))) { + throw new ClassDoesNotExtendTestCaseException($className, $suiteClassFile); + } } - throw new ClassDoesNotExtendTestCaseException($suiteClassName, $suiteClassFile); + throw new ClassCannotBeFoundException($suiteClassName, $suiteClassFile); } private function classNameFromFileName(string $suiteClassFile): string @@ -97,7 +101,7 @@ private function classNameFromFileName(string $suiteClassFile): string } /** - * @psalm-return list + * @return array */ private function loadSuiteClassFile(string $suiteClassFile): array { @@ -105,17 +109,15 @@ private function loadSuiteClassFile(string $suiteClassFile): array return self::$fileToClassesMap[$suiteClassFile]; } - if (empty(self::$declaredClasses)) { + if (self::$declaredClasses === []) { self::$declaredClasses = get_declared_classes(); } require_once $suiteClassFile; - $loadedClasses = array_values( - array_diff( - get_declared_classes(), - self::$declaredClasses, - ), + $loadedClasses = array_diff( + get_declared_classes(), + self::$declaredClasses, ); foreach ($loadedClasses as $loadedClass) { @@ -131,7 +133,7 @@ private function loadSuiteClassFile(string $suiteClassFile): array self::$declaredClasses = get_declared_classes(); - if (empty($loadedClasses)) { + if ($loadedClasses === []) { return self::$declaredClasses; } diff --git a/src/Runner/TestSuiteSorter.php b/src/Runner/TestSuiteSorter.php index 6d1a78e716f..100f15d531a 100644 --- a/src/Runner/TestSuiteSorter.php +++ b/src/Runner/TestSuiteSorter.php @@ -27,41 +27,23 @@ use PHPUnit\Runner\ResultCache\ResultCache; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestSuiteSorter { - /** - * @var int - */ - public const ORDER_DEFAULT = 0; - - /** - * @var int - */ - public const ORDER_RANDOMIZED = 1; + public const int ORDER_DEFAULT = 0; + public const int ORDER_RANDOMIZED = 1; + public const int ORDER_REVERSED = 2; + public const int ORDER_DEFECTS_FIRST = 3; + public const int ORDER_DURATION = 4; + public const int ORDER_SIZE = 5; /** - * @var int + * @var non-empty-array */ - public const ORDER_REVERSED = 2; - - /** - * @var int - */ - public const ORDER_DEFECTS_FIRST = 3; - - /** - * @var int - */ - public const ORDER_DURATION = 4; - - /** - * @var int - */ - public const ORDER_SIZE = 5; - - private const SIZE_SORT_WEIGHT = [ + private const array SIZE_SORT_WEIGHT = [ 'small' => 1, 'medium' => 2, 'large' => 3, @@ -69,18 +51,18 @@ final class TestSuiteSorter ]; /** - * @psalm-var array Associative array of (string => DEFECT_SORT_WEIGHT) elements + * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements */ private array $defectSortOrder = []; private readonly ResultCache $cache; /** - * @psalm-var array A list of normalized names of tests before reordering + * @var array A list of normalized names of tests before reordering */ private array $originalExecutionOrder = []; /** - * @psalm-var array A list of normalized names of tests affected by reordering + * @var array A list of normalized names of tests affected by reordering */ private array $executionOrder = []; @@ -136,11 +118,17 @@ public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDepend } } + /** + * @return array + */ public function getOriginalExecutionOrder(): array { return $this->originalExecutionOrder; } + /** + * @return array + */ public function getExecutionOrder(): array { return $this->executionOrder; @@ -148,7 +136,7 @@ public function getExecutionOrder(): array private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects): void { - if (empty($suite->tests())) { + if ($suite->tests() === []) { return; } @@ -169,6 +157,8 @@ private function sort(TestSuite $suite, int $order, bool $resolveDependencies, i if ($resolveDependencies && !($suite instanceof DataProviderTestSuite)) { $tests = $suite->tests(); + /** @noinspection PhpParamsInspection */ + /** @phpstan-ignore argument.type */ $suite->setTests($this->resolveDependencies($tests)); } } @@ -191,11 +181,21 @@ private function addSuiteToDefectSortOrder(TestSuite $suite): void $this->defectSortOrder[$suite->sortId()] = $max; } + /** + * @param list $tests + * + * @return list + */ private function reverse(array $tests): array { return array_reverse($tests); } + /** + * @param list $tests + * + * @return list + */ private function randomize(array $tests): array { shuffle($tests); @@ -203,31 +203,46 @@ private function randomize(array $tests): array return $tests; } + /** + * @param list $tests + * + * @return list + */ private function sortDefectsFirst(array $tests): array { usort( $tests, - fn ($left, $right) => $this->cmpDefectPriorityAndTime($left, $right), + fn (Test $left, Test $right) => $this->cmpDefectPriorityAndTime($left, $right), ); return $tests; } + /** + * @param list $tests + * + * @return list + */ private function sortByDuration(array $tests): array { usort( $tests, - fn ($left, $right) => $this->cmpDuration($left, $right), + fn (Test $left, Test $right) => $this->cmpDuration($left, $right), ); return $tests; } + /** + * @param list $tests + * + * @return list + */ private function sortBySize(array $tests): array { usort( $tests, - fn ($left, $right) => $this->cmpSize($left, $right), + fn (Test $left, Test $right) => $this->cmpSize($left, $right), ); return $tests; @@ -249,12 +264,12 @@ private function cmpDefectPriorityAndTime(Test $a, Test $b): int $priorityA = $this->defectSortOrder[$a->sortId()] ?? 0; $priorityB = $this->defectSortOrder[$b->sortId()] ?? 0; - if ($priorityB <=> $priorityA) { + if (($priorityB <=> $priorityA) > 0) { // Sort defect weight descending return $priorityB <=> $priorityA; } - if ($priorityA || $priorityB) { + if ($priorityA > 0 || $priorityB > 0) { return $this->cmpDuration($a, $b); } @@ -300,9 +315,9 @@ private function cmpSize(Test $a, Test $b): int * 3. If the test has dependencies but none left to do: mark done, start again from the top * 4. When we reach the end add any leftover tests to the end. These will be marked 'skipped' during execution. * - * @psalm-param array $tests + * @param array $tests * - * @psalm-return array + * @return array */ private function resolveDependencies(array $tests): array { @@ -318,11 +333,14 @@ private function resolveDependencies(array $tests): array } else { $i++; } - } while (!empty($tests) && ($i < count($tests))); + } while ($tests !== [] && ($i < count($tests))); return array_merge($newTestOrder, $tests); } + /** + * @return array + */ private function calculateTestExecutionOrder(Test $suite): array { $tests = []; diff --git a/src/Runner/Version.php b/src/Runner/Version.php index 3f541d87c06..0139dfcf6b1 100644 --- a/src/Runner/Version.php +++ b/src/Runner/Version.php @@ -10,6 +10,7 @@ namespace PHPUnit\Runner; use function array_slice; +use function assert; use function dirname; use function explode; use function implode; @@ -25,7 +26,7 @@ final class Version private static string $version = ''; /** - * Returns the current version of PHPUnit. + * @return non-empty-string */ public static function id(): string { @@ -34,12 +35,15 @@ public static function id(): string } if (self::$version === '') { - self::$version = (new VersionId('11.0', dirname(__DIR__, 2)))->asString(); + self::$version = (new VersionId('12.2', dirname(__DIR__, 2)))->asString(); } return self::$version; } + /** + * @return non-empty-string + */ public static function series(): string { if (str_contains(self::id(), '-')) { @@ -51,6 +55,20 @@ public static function series(): string return implode('.', array_slice(explode('.', $version), 0, 2)); } + /** + * @return positive-int + */ + public static function majorVersionNumber(): int + { + $majorVersion = (int) explode('.', self::series())[0]; + assert($majorVersion > 0); + + return $majorVersion; + } + + /** + * @return non-empty-string + */ public static function getVersionString(): string { return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; diff --git a/src/TextUI/Application.php b/src/TextUI/Application.php index 35368780ede..3b913b7b2f6 100644 --- a/src/TextUI/Application.php +++ b/src/TextUI/Application.php @@ -10,16 +10,27 @@ namespace PHPUnit\TextUI; use const PHP_EOL; +use const PHP_VERSION; +use function assert; +use function class_exists; +use function defined; +use function dirname; +use function explode; +use function function_exists; use function is_file; use function is_readable; +use function method_exists; use function printf; use function realpath; use function sprintf; +use function str_contains; +use function str_starts_with; use function trim; use function unlink; use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Event\UnknownSubscriberTypeException; +use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite; use PHPUnit\Logging\EventLogger; use PHPUnit\Logging\JUnit\JunitXmlLogger; @@ -32,17 +43,21 @@ use PHPUnit\Runner\Baseline\Reader; use PHPUnit\Runner\Baseline\Writer; use PHPUnit\Runner\CodeCoverage; +use PHPUnit\Runner\DeprecationCollector\Facade as DeprecationCollector; +use PHPUnit\Runner\DirectoryDoesNotExistException; use PHPUnit\Runner\ErrorHandler; use PHPUnit\Runner\Extension\ExtensionBootstrapper; use PHPUnit\Runner\Extension\Facade as ExtensionFacade; use PHPUnit\Runner\Extension\PharLoader; use PHPUnit\Runner\GarbageCollection\GarbageCollectionHandler; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use PHPUnit\Runner\ResultCache\DefaultResultCache; use PHPUnit\Runner\ResultCache\NullResultCache; use PHPUnit\Runner\ResultCache\ResultCache; use PHPUnit\Runner\ResultCache\ResultCacheHandler; use PHPUnit\Runner\TestSuiteSorter; use PHPUnit\Runner\Version; +use PHPUnit\TestRunner\IssueFilter; use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade; use PHPUnit\TextUI\CliArguments\Builder; use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; @@ -51,6 +66,7 @@ use PHPUnit\TextUI\Command\AtLeastVersionCommand; use PHPUnit\TextUI\Command\GenerateConfigurationCommand; use PHPUnit\TextUI\Command\ListGroupsCommand; +use PHPUnit\TextUI\Command\ListTestFilesCommand; use PHPUnit\TextUI\Command\ListTestsAsTextCommand; use PHPUnit\TextUI\Command\ListTestsAsXmlCommand; use PHPUnit\TextUI\Command\ListTestSuitesCommand; @@ -71,16 +87,24 @@ use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; use PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration; use PHPUnit\TextUI\XmlConfiguration\Loader; +use PHPUnit\Util\Http\PhpDownloader; use SebastianBergmann\Timer\Timer; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Application +final readonly class Application { + /** + * @param list $argv + */ public function run(array $argv): int { + $this->preload(); + try { EventFacade::emitter()->applicationStarted(); @@ -102,19 +126,13 @@ public function run(array $argv): int $this->loadBootstrapScript($configuration->bootstrap()); } - $this->executeCommandsThatRequireCompleteConfiguration($configuration, $cliConfiguration); - - $testSuite = $this->buildTestSuite($configuration); - - $this->executeCommandsThatRequireCliConfigurationAndTestSuite($cliConfiguration, $testSuite); - $this->executeHelpCommandWhenThereIsNothingElseToDo($configuration, $testSuite); + $this->executeCommandsThatDoNotRequireTheTestSuite($configuration, $cliConfiguration); $pharExtensions = null; $extensionRequiresCodeCoverageCollection = false; $extensionReplacesOutput = false; $extensionReplacesProgressOutput = false; $extensionReplacesResultOutput = false; - $extensionRequiresExportOfObjects = false; if (!$configuration->noExtensions()) { if ($configuration->hasPharExtensionDirectory()) { @@ -128,39 +146,30 @@ public function run(array $argv): int $extensionReplacesOutput = $bootstrappedExtensions['replacesOutput']; $extensionReplacesProgressOutput = $bootstrappedExtensions['replacesProgressOutput']; $extensionReplacesResultOutput = $bootstrappedExtensions['replacesResultOutput']; - $extensionRequiresExportOfObjects = $bootstrappedExtensions['requiresExportOfObjects']; - } - - if ($extensionRequiresExportOfObjects) { - EventFacade::emitter()->exportObjects(); } - CodeCoverage::instance()->init( - $configuration, - CodeCoverageFilterRegistry::instance(), - $extensionRequiresCodeCoverageCollection, - ); - $printer = OutputFacade::init( $configuration, $extensionReplacesProgressOutput, $extensionReplacesResultOutput, ); - if (!$extensionReplacesOutput) { - $this->writeRuntimeInformation($printer, $configuration); - $this->writePharExtensionInformation($printer, $pharExtensions); - $this->writeRandomSeedInformation($printer, $configuration); - - $printer->print(PHP_EOL); + if ($configuration->debug()) { + EventFacade::instance()->registerTracer( + new EventLogger( + 'php://stdout', + $configuration->withTelemetry(), + ), + ); } + TestResultFacade::init(); + DeprecationCollector::init(); + $this->registerLogfileWriters($configuration); $testDoxResultCollector = $this->testDoxResultCollector($configuration); - TestResultFacade::init(); - $resultCache = $this->initializeTestResultCache($configuration); if ($configuration->controlGarbageCollector()) { @@ -174,6 +183,30 @@ public function run(array $argv): int EventFacade::instance()->seal(); + $testSuite = $this->buildTestSuite($configuration); + + $this->executeCommandsThatRequireTheTestSuite($configuration, $cliConfiguration, $testSuite); + + if ($testSuite->isEmpty() && !$configuration->hasCliArguments() && $configuration->testSuite()->isEmpty()) { + $this->execute(new ShowHelpCommand(Result::FAILURE)); + } + + CodeCoverage::instance()->init( + $configuration, + CodeCoverageFilterRegistry::instance(), + $extensionRequiresCodeCoverageCollection, + ); + + if (!$configuration->debug() && !$extensionReplacesOutput) { + $this->writeRuntimeInformation($printer, $configuration); + $this->writePharExtensionInformation($printer, $pharExtensions); + $this->writeRandomSeedInformation($printer, $configuration); + + $printer->print(PHP_EOL); + } + + $this->configureDeprecationTriggers($configuration); + $timer = new Timer; $timer->start(); @@ -195,22 +228,47 @@ public function run(array $argv): int if ($testDoxResult !== null && $configuration->hasLogfileTestdoxHtml()) { - OutputFacade::printerFor($configuration->logfileTestdoxHtml())->print( - (new TestDoxHtmlRenderer)->render($testDoxResult), - ); + try { + OutputFacade::printerFor($configuration->logfileTestdoxHtml())->print( + (new TestDoxHtmlRenderer)->render($testDoxResult), + ); + } catch (DirectoryDoesNotExistException|InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Cannot log test results in TestDox HTML format to "%s": %s', + $configuration->logfileTestdoxHtml(), + $e->getMessage(), + ), + ); + } } if ($testDoxResult !== null && $configuration->hasLogfileTestdoxText()) { - OutputFacade::printerFor($configuration->logfileTestdoxText())->print( - (new TestDoxTextRenderer)->render($testDoxResult), - ); + try { + OutputFacade::printerFor($configuration->logfileTestdoxText())->print( + (new TestDoxTextRenderer)->render($testDoxResult), + ); + } catch (DirectoryDoesNotExistException|InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Cannot log test results in TestDox plain text format to "%s": %s', + $configuration->logfileTestdoxText(), + $e->getMessage(), + ), + ); + } } $result = TestResultFacade::result(); - if (!$extensionReplacesResultOutput) { - OutputFacade::printResult($result, $testDoxResult, $duration); + if (!$extensionReplacesResultOutput && !$configuration->debug()) { + OutputFacade::printResult( + $result, + $testDoxResult, + $duration, + $configuration->hasSpecificDeprecationToStopOn(), + ); } CodeCoverage::instance()->generateReports($printer, $configuration); @@ -230,13 +288,15 @@ public function run(array $argv): int } $shellExitCode = (new ShellExitCodeCalculator)->calculate( - $configuration->failOnDeprecation(), - $configuration->failOnEmptyTestSuite(), - $configuration->failOnIncomplete(), - $configuration->failOnNotice(), - $configuration->failOnRisky(), - $configuration->failOnSkipped(), - $configuration->failOnWarning(), + $configuration->failOnDeprecation() || $configuration->failOnAllIssues(), + $configuration->failOnPhpunitDeprecation() || $configuration->failOnAllIssues(), + $configuration->failOnPhpunitNotice() || $configuration->failOnAllIssues(), + $configuration->failOnEmptyTestSuite() || $configuration->failOnAllIssues(), + $configuration->failOnIncomplete() || $configuration->failOnAllIssues(), + $configuration->failOnNotice() || $configuration->failOnAllIssues(), + $configuration->failOnRisky() || $configuration->failOnAllIssues(), + $configuration->failOnSkipped() || $configuration->failOnAllIssues(), + $configuration->failOnWarning() || $configuration->failOnAllIssues(), $result, ); @@ -250,15 +310,43 @@ public function run(array $argv): int // @codeCoverageIgnoreEnd } - private function execute(Command\Command $command): never + private function execute(Command\Command $command, bool $requiresResultCollectedFromEvents = false): never { + $errored = false; + + if ($requiresResultCollectedFromEvents) { + try { + TestResultFacade::init(); + EventFacade::instance()->seal(); + + $resultCollectedFromEvents = TestResultFacade::result(); + + $errored = $resultCollectedFromEvents->hasTestTriggeredPhpunitErrorEvents(); + } catch (EventFacadeIsSealedException|UnknownSubscriberTypeException) { + } + } + print Version::getVersionString() . PHP_EOL . PHP_EOL; - $result = $command->execute(); + if (!$errored) { + $result = $command->execute(); + + print $result->output(); + + exit($result->shellExitCode()); + } + + assert(isset($resultCollectedFromEvents)); - print $result->output(); + print 'There were errors:' . PHP_EOL; - exit($result->shellExitCode()); + foreach ($resultCollectedFromEvents->testTriggeredPhpunitErrorEvents() as $events) { + foreach ($events as $event) { + print PHP_EOL . trim($event->message()) . PHP_EOL; + } + } + + exit(Result::EXCEPTION); } private function loadBootstrapScript(string $filename): void @@ -303,6 +391,9 @@ private function loadBootstrapScript(string $filename): void EventFacade::emitter()->testRunnerBootstrapFinished($filename); } + /** + * @param list $argv + */ private function buildCliConfiguration(array $argv): CliConfiguration { try { @@ -316,7 +407,7 @@ private function buildCliConfiguration(array $argv): CliConfiguration private function loadXmlConfiguration(false|string $configurationFile): XmlConfiguration { - if (!$configurationFile) { + if ($configurationFile === false) { return DefaultConfiguration::create(); } @@ -337,7 +428,7 @@ private function buildTestSuite(Configuration $configuration): TestSuite } /** - * @psalm-return array{requiresCodeCoverageCollection: bool, replacesOutput: bool, replacesProgressOutput: bool, replacesResultOutput: bool, requiresExportOfObjects: bool} + * @return array{requiresCodeCoverageCollection: bool, replacesOutput: bool, replacesProgressOutput: bool, replacesResultOutput: bool} */ private function bootstrapExtensions(Configuration $configuration): array { @@ -360,7 +451,6 @@ private function bootstrapExtensions(Configuration $configuration): array 'replacesOutput' => $facade->replacesOutput(), 'replacesProgressOutput' => $facade->replacesProgressOutput(), 'replacesResultOutput' => $facade->replacesResultOutput(), - 'requiresExportOfObjects' => $facade->requiresExportOfObjects(), ]; } @@ -371,7 +461,7 @@ private function executeCommandsThatOnlyRequireCliConfiguration(CliConfiguration } if ($cliConfiguration->migrateConfiguration()) { - if (!$configurationFile) { + if ($configurationFile === false) { $this->exitWithErrorMessage('No configuration file found to migrate'); } @@ -387,7 +477,7 @@ private function executeCommandsThatOnlyRequireCliConfiguration(CliConfiguration } if ($cliConfiguration->checkVersion()) { - $this->execute(new VersionCheckCommand); + $this->execute(new VersionCheckCommand(new PhpDownloader, Version::majorVersionNumber(), Version::id())); } if ($cliConfiguration->help()) { @@ -395,41 +485,66 @@ private function executeCommandsThatOnlyRequireCliConfiguration(CliConfiguration } } - private function executeCommandsThatRequireCliConfigurationAndTestSuite(CliConfiguration $cliConfiguration, TestSuite $testSuite): void + private function executeCommandsThatDoNotRequireTheTestSuite(Configuration $configuration, CliConfiguration $cliConfiguration): void + { + if ($cliConfiguration->warmCoverageCache()) { + $this->execute(new WarmCodeCoverageCacheCommand($configuration, CodeCoverageFilterRegistry::instance())); + } + } + + private function executeCommandsThatRequireTheTestSuite(Configuration $configuration, CliConfiguration $cliConfiguration, TestSuite $testSuite): void { + if ($cliConfiguration->listSuites()) { + $this->execute(new ListTestSuitesCommand($testSuite)); + } + if ($cliConfiguration->listGroups()) { - $this->execute(new ListGroupsCommand($testSuite)); + $this->execute( + new ListGroupsCommand( + $this->filteredTests( + $configuration, + $testSuite, + ), + ), + true, + ); } if ($cliConfiguration->listTests()) { - $this->execute(new ListTestsAsTextCommand($testSuite)); + $this->execute( + new ListTestsAsTextCommand( + $this->filteredTests( + $configuration, + $testSuite, + ), + ), + true, + ); } if ($cliConfiguration->hasListTestsXml()) { $this->execute( new ListTestsAsXmlCommand( + $this->filteredTests( + $configuration, + $testSuite, + ), $cliConfiguration->listTestsXml(), - $testSuite, ), + true, ); } - } - - private function executeCommandsThatRequireCompleteConfiguration(Configuration $configuration, CliConfiguration $cliConfiguration): void - { - if ($cliConfiguration->listSuites()) { - $this->execute(new ListTestSuitesCommand($configuration->testSuite())); - } - - if ($cliConfiguration->warmCoverageCache()) { - $this->execute(new WarmCodeCoverageCacheCommand($configuration, CodeCoverageFilterRegistry::instance())); - } - } - private function executeHelpCommandWhenThereIsNothingElseToDo(Configuration $configuration, TestSuite $testSuite): void - { - if ($testSuite->isEmpty() && !$configuration->hasCliArguments() && $configuration->testSuite()->isEmpty()) { - $this->execute(new ShowHelpCommand(Result::FAILURE)); + if ($cliConfiguration->listTestFiles()) { + $this->execute( + new ListTestFilesCommand( + $this->filteredTests( + $configuration, + $testSuite, + ), + ), + true, + ); } } @@ -440,7 +555,7 @@ private function writeRuntimeInformation(Printer $printer, Configuration $config $runtime = 'PHP ' . PHP_VERSION; if (CodeCoverage::instance()->isActive()) { - $runtime .= ' with ' . CodeCoverage::instance()->driver()->nameAndVersion(); + $runtime .= ' with ' . CodeCoverage::instance()->driverNameAndVersion(); } $this->writeMessage($printer, 'Runtime', $runtime); @@ -455,7 +570,7 @@ private function writeRuntimeInformation(Printer $printer, Configuration $config } /** - * @psalm-param ?list $pharExtensions + * @param ?list $pharExtensions */ private function writePharExtensionInformation(Printer $printer, ?array $pharExtensions): void { @@ -494,10 +609,6 @@ private function writeRandomSeedInformation(Printer $printer, Configuration $con } } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function registerLogfileWriters(Configuration $configuration): void { if ($configuration->hasLogEventsText()) { @@ -524,46 +635,59 @@ private function registerLogfileWriters(Configuration $configuration): void true, ), ); - - EventFacade::emitter()->exportObjects(); } if ($configuration->hasLogfileJunit()) { - new JunitXmlLogger( - OutputFacade::printerFor($configuration->logfileJunit()), - EventFacade::instance(), - ); + try { + new JunitXmlLogger( + OutputFacade::printerFor($configuration->logfileJunit()), + EventFacade::instance(), + ); + } catch (DirectoryDoesNotExistException|InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Cannot log test results in JUnit XML format to "%s": %s', + $configuration->logfileJunit(), + $e->getMessage(), + ), + ); + } } if ($configuration->hasLogfileTeamcity()) { - new TeamCityLogger( - DefaultPrinter::from( - $configuration->logfileTeamcity(), - ), - EventFacade::instance(), - ); + try { + new TeamCityLogger( + DefaultPrinter::from( + $configuration->logfileTeamcity(), + ), + EventFacade::instance(), + ); + } catch (DirectoryDoesNotExistException|InvalidSocketException $e) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Cannot log test results in TeamCity format to "%s": %s', + $configuration->logfileTeamcity(), + $e->getMessage(), + ), + ); + } } } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function testDoxResultCollector(Configuration $configuration): ?TestDoxResultCollector { if ($configuration->hasLogfileTestdoxHtml() || $configuration->hasLogfileTestdoxText() || $configuration->outputIsTestDox()) { - return new TestDoxResultCollector(EventFacade::instance()); + return new TestDoxResultCollector( + EventFacade::instance(), + new IssueFilter($configuration->source()), + ); } return null; } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function initializeTestResultCache(Configuration $configuration): ResultCache { if ($configuration->cacheResult()) { @@ -577,10 +701,6 @@ private function initializeTestResultCache(Configuration $configuration): Result return new NullResultCache; } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function configureBaseline(Configuration $configuration): ?BaselineGenerator { if ($configuration->hasGenerateBaseline()) { @@ -591,18 +711,17 @@ private function configureBaseline(Configuration $configuration): ?BaselineGener } if ($configuration->source()->useBaseline()) { - /** @psalm-suppress MissingThrowsDocblock */ $baselineFile = $configuration->source()->baseline(); $baseline = null; try { $baseline = (new Reader)->read($baselineFile); } catch (CannotLoadBaselineException $e) { - EventFacade::emitter()->testRunnerTriggeredWarning($e->getMessage()); + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning($e->getMessage()); } if ($baseline !== null) { - ErrorHandler::instance()->use($baseline); + ErrorHandler::instance()->useBaseline($baseline); } } @@ -616,7 +735,7 @@ private function exitWithCrashMessage(Throwable $t): never { $message = $t->getMessage(); - if (empty(trim($message))) { + if (trim($message) === '') { $message = '(no message)'; } @@ -631,6 +750,10 @@ private function exitWithCrashMessage(Throwable $t): never $first = true; + if ($t->getPrevious() !== null) { + $t = $t->getPrevious(); + } + do { printf( '%s%s: %s:%d%s%s%s%s', @@ -656,4 +779,97 @@ private function exitWithErrorMessage(string $message): never exit(Result::EXCEPTION); } + + /** + * @return list + */ + private function filteredTests(Configuration $configuration, TestSuite $suite): array + { + (new TestSuiteFilterProcessor)->process($configuration, $suite); + + return $suite->collect(); + } + + private function configureDeprecationTriggers(Configuration $configuration): void + { + $deprecationTriggers = [ + 'functions' => [], + 'methods' => [], + ]; + + foreach ($configuration->source()->deprecationTriggers()['functions'] as $function) { + if (!function_exists($function)) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Function %s cannot be configured as a deprecation trigger because it is not declared', + $function, + ), + ); + + continue; + } + + $deprecationTriggers['functions'][] = $function; + } + + foreach ($configuration->source()->deprecationTriggers()['methods'] as $method) { + if (!str_contains($method, '::')) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + '%s cannot be configured as a deprecation trigger because it is not in ClassName::methodName format', + $method, + ), + ); + + continue; + } + + [$className, $methodName] = explode('::', $method); + + if (!class_exists($className) || !method_exists($className, $methodName)) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Method %s::%s cannot be configured as a deprecation trigger because it is not declared', + $className, + $methodName, + ), + ); + + continue; + } + + $deprecationTriggers['methods'][] = [ + 'className' => $className, + 'methodName' => $methodName, + ]; + } + + ErrorHandler::instance()->useDeprecationTriggers($deprecationTriggers); + } + + private function preload(): void + { + if (!defined('PHPUNIT_COMPOSER_INSTALL')) { + return; + } + + $classMapFile = dirname(PHPUNIT_COMPOSER_INSTALL) . '/composer/autoload_classmap.php'; + + if (!is_file($classMapFile)) { + return; + } + + foreach (require $classMapFile as $codeUnitName => $sourceCodeFile) { + if (!str_starts_with($codeUnitName, 'PHPUnit\\') && + !str_starts_with($codeUnitName, 'SebastianBergmann\\')) { + continue; + } + + if (str_contains($sourceCodeFile, '/tests/')) { + continue; + } + + require_once $sourceCodeFile; + } + } } diff --git a/src/TextUI/Command/Command.php b/src/TextUI/Command/Command.php index d340c8303e7..4194551e41a 100644 --- a/src/TextUI/Command/Command.php +++ b/src/TextUI/Command/Command.php @@ -10,6 +10,8 @@ namespace PHPUnit\TextUI\Command; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Command diff --git a/src/TextUI/Command/Commands/AtLeastVersionCommand.php b/src/TextUI/Command/Commands/AtLeastVersionCommand.php index 0d776af09a7..7bace86c8fd 100644 --- a/src/TextUI/Command/Commands/AtLeastVersionCommand.php +++ b/src/TextUI/Command/Commands/AtLeastVersionCommand.php @@ -13,6 +13,8 @@ use PHPUnit\Runner\Version; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class AtLeastVersionCommand implements Command diff --git a/src/TextUI/Command/Commands/GenerateConfigurationCommand.php b/src/TextUI/Command/Commands/GenerateConfigurationCommand.php index 7f450006d5a..cb1a9ac9513 100644 --- a/src/TextUI/Command/Commands/GenerateConfigurationCommand.php +++ b/src/TextUI/Command/Commands/GenerateConfigurationCommand.php @@ -9,21 +9,31 @@ */ namespace PHPUnit\TextUI\Command; +use const PHP_EOL; +use const STDIN; +use function assert; +use function defined; use function fgets; use function file_put_contents; use function getcwd; +use function is_file; +use function sprintf; use function trim; use PHPUnit\Runner\Version; use PHPUnit\TextUI\XmlConfiguration\Generator; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class GenerateConfigurationCommand implements Command +final readonly class GenerateConfigurationCommand implements Command { public function execute(): Result { - print 'Generating phpunit.xml in ' . getcwd() . PHP_EOL . PHP_EOL; + $directory = getcwd(); + + print 'Generating phpunit.xml in ' . $directory . PHP_EOL . PHP_EOL; print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; $bootstrapScript = $this->read(); @@ -56,12 +66,22 @@ public function execute(): Result $cacheDirectory = '.phpunit.cache'; } + if (defined('PHPUNIT_COMPOSER_INSTALL') && + is_file($directory . '/vendor/phpunit/phpunit/phpunit.xsd')) { + $schemaLocation = 'vendor/phpunit/phpunit/phpunit.xsd'; + } else { + $schemaLocation = sprintf( + 'https://schema.phpunit.de/%s/phpunit.xsd', + Version::series(), + ); + } + $generator = new Generator; - file_put_contents( - 'phpunit.xml', + $result = @file_put_contents( + $directory . '/phpunit.xml', $generator->generateDefaultConfiguration( - Version::series(), + $schemaLocation, $bootstrapScript, $testsDirectory, $src, @@ -69,15 +89,34 @@ public function execute(): Result ), ); - /* @noinspection MissingDirectorySeparatorInspection */ - print PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . PHP_EOL; - print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . PHP_EOL; + if ($result !== false) { + return Result::from( + sprintf( + PHP_EOL . 'Generated phpunit.xml in %s.' . PHP_EOL . + 'Make sure to exclude the %s directory from version control.' . PHP_EOL, + $directory, + $cacheDirectory, + ), + ); + } - return Result::from(); + // @codeCoverageIgnoreStart + return Result::from( + sprintf( + PHP_EOL . 'Could not write phpunit.xml in %s.' . PHP_EOL, + $directory, + ), + Result::EXCEPTION, + ); + // @codeCoverageIgnoreEnd } private function read(): string { - return trim(fgets(STDIN)); + $buffer = fgets(STDIN); + + assert($buffer !== false); + + return trim($buffer); } } diff --git a/src/TextUI/Command/Commands/ListGroupsCommand.php b/src/TextUI/Command/Commands/ListGroupsCommand.php index 01489579e67..94eb1355ced 100644 --- a/src/TextUI/Command/Commands/ListGroupsCommand.php +++ b/src/TextUI/Command/Commands/ListGroupsCommand.php @@ -9,72 +9,75 @@ */ namespace PHPUnit\TextUI\Command; -use function sort; +use const PHP_EOL; +use function count; +use function ksort; use function sprintf; use function str_starts_with; -use PHPUnit\Framework\TestSuite; -use PHPUnit\TextUI\Configuration\Registry; +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ListGroupsCommand implements Command { - private TestSuite $suite; - - public function __construct(TestSuite $suite) + /** + * @var list + */ + private array $tests; + + /** + * @param list $tests + */ + public function __construct(array $tests) { - $this->suite = $suite; + $this->tests = $tests; } public function execute(): Result { - $buffer = $this->warnAboutConflictingOptions(); - $buffer .= 'Available test group(s):' . PHP_EOL; + /** @var array $groups */ + $groups = []; + + foreach ($this->tests as $test) { + if ($test instanceof PhptTestCase) { + $_groups = ['default']; + } else { + $_groups = $test->groups(); + } + + foreach ($_groups as $group) { + if (!isset($groups[$group])) { + $groups[$group] = 1; + } else { + $groups[$group]++; + } + } + } - $groups = $this->suite->groups(); - sort($groups); + ksort($groups); - foreach ($groups as $group) { - if (str_starts_with($group, '__phpunit_')) { + $buffer = sprintf( + 'Available test group%s:' . PHP_EOL, + count($groups) > 1 ? 's' : '', + ); + + foreach ($groups as $group => $numberOfTests) { + if (str_starts_with((string) $group, '__phpunit_')) { continue; } $buffer .= sprintf( - ' - %s' . PHP_EOL, - $group, + ' - %s (%d test%s)' . PHP_EOL, + (string) $group, + $numberOfTests, + $numberOfTests > 1 ? 's' : '', ); } return Result::from($buffer); } - - private function warnAboutConflictingOptions(): string - { - $buffer = ''; - - $configuration = Registry::get(); - - if ($configuration->hasFilter()) { - $buffer .= 'The --filter and --list-groups options cannot be combined, --filter is ignored' . PHP_EOL; - } - - if ($configuration->hasGroups()) { - $buffer .= 'The --group and --list-groups options cannot be combined, --group is ignored' . PHP_EOL; - } - - if ($configuration->hasExcludeGroups()) { - $buffer .= 'The --exclude-group and --list-groups options cannot be combined, --exclude-group is ignored' . PHP_EOL; - } - - if ($configuration->includeTestSuite() !== '') { - $buffer .= 'The --testsuite and --list-groups options cannot be combined, --exclude-group is ignored' . PHP_EOL; - } - - if (!empty($buffer)) { - $buffer .= PHP_EOL; - } - - return $buffer; - } } diff --git a/src/TextUI/Command/Commands/ListTestFilesCommand.php b/src/TextUI/Command/Commands/ListTestFilesCommand.php new file mode 100644 index 00000000000..6d7f875f438 --- /dev/null +++ b/src/TextUI/Command/Commands/ListTestFilesCommand.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; + +use const PHP_EOL; +use function array_unique; +use function assert; +use function sprintf; +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; +use ReflectionClass; +use ReflectionException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class ListTestFilesCommand implements Command +{ + /** + * @var list + */ + private array $tests; + + /** + * @param list $tests + */ + public function __construct(array $tests) + { + $this->tests = $tests; + } + + /** + * @throws ReflectionException + */ + public function execute(): Result + { + $buffer = 'Available test files:' . PHP_EOL; + + $results = []; + + foreach ($this->tests as $test) { + if ($test instanceof TestCase) { + $name = (new ReflectionClass($test))->getFileName(); + + assert($name !== false); + + $results[] = $name; + + continue; + } + + $results[] = $test->getName(); + } + + foreach (array_unique($results) as $result) { + $buffer .= sprintf( + ' - %s' . PHP_EOL, + $result, + ); + } + + return Result::from($buffer); + } +} diff --git a/src/TextUI/Command/Commands/ListTestSuitesCommand.php b/src/TextUI/Command/Commands/ListTestSuitesCommand.php index 82ce790722f..33b76139b8c 100644 --- a/src/TextUI/Command/Commands/ListTestSuitesCommand.php +++ b/src/TextUI/Command/Commands/ListTestSuitesCommand.php @@ -9,31 +9,54 @@ */ namespace PHPUnit\TextUI\Command; +use const PHP_EOL; +use function assert; +use function count; +use function ksort; use function sprintf; +use PHPUnit\Framework\TestSuite; use PHPUnit\TextUI\Configuration\Registry; -use PHPUnit\TextUI\Configuration\TestSuiteCollection; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ListTestSuitesCommand implements Command { - private TestSuiteCollection $suites; + private TestSuite $testSuite; - public function __construct(TestSuiteCollection $suites) + public function __construct(TestSuite $testSuite) { - $this->suites = $suites; + $this->testSuite = $testSuite; } public function execute(): Result { + /** @var array $suites */ + $suites = []; + + foreach ($this->testSuite->tests() as $test) { + assert($test instanceof TestSuite); + + $suites[$test->name()] = count($test->collect()); + } + + ksort($suites); + $buffer = $this->warnAboutConflictingOptions(); - $buffer .= 'Available test suite(s):' . PHP_EOL; - foreach ($this->suites as $suite) { + $buffer .= sprintf( + 'Available test suite%s:' . PHP_EOL, + count($suites) > 1 ? 's' : '', + ); + + foreach ($suites as $suite => $numberOfTests) { $buffer .= sprintf( - ' - %s' . PHP_EOL, - $suite->name(), + ' - %s (%d test%s)' . PHP_EOL, + $suite, + $numberOfTests, + $numberOfTests > 1 ? 's' : '', ); } @@ -46,23 +69,27 @@ private function warnAboutConflictingOptions(): string $configuration = Registry::get(); + if ($configuration->hasDefaultTestSuite()) { + $buffer .= 'The defaultTestSuite (XML) and --list-suites (CLI) options cannot be combined, only the default test suite is shown' . PHP_EOL; + } + + if ($configuration->includeTestSuite() !== '' && !$configuration->hasDefaultTestSuite()) { + $buffer .= 'The --testsuite and --list-suites options cannot be combined, --testsuite is ignored' . PHP_EOL; + } + if ($configuration->hasFilter()) { $buffer .= 'The --filter and --list-suites options cannot be combined, --filter is ignored' . PHP_EOL; } if ($configuration->hasGroups()) { - $buffer .= 'The --group and --list-suites options cannot be combined, --group is ignored' . PHP_EOL; + $buffer .= 'The --group (CLI) and (XML) options cannot be combined with --list-suites, --group and are ignored' . PHP_EOL; } if ($configuration->hasExcludeGroups()) { - $buffer .= 'The --exclude-group and --list-suites options cannot be combined, --exclude-group is ignored' . PHP_EOL; - } - - if ($configuration->includeTestSuite() !== '') { - $buffer .= 'The --testsuite and --list-suites options cannot be combined, --exclude-group is ignored' . PHP_EOL; + $buffer .= 'The --exclude-group (CLI) and (XML) options cannot be combined with --list-suites, --exclude-group and are ignored' . PHP_EOL; } - if (!empty($buffer)) { + if ($buffer !== '') { $buffer .= PHP_EOL; } diff --git a/src/TextUI/Command/Commands/ListTestsAsTextCommand.php b/src/TextUI/Command/Commands/ListTestsAsTextCommand.php index dd771b18189..c3d71b5d237 100644 --- a/src/TextUI/Command/Commands/ListTestsAsTextCommand.php +++ b/src/TextUI/Command/Commands/ListTestsAsTextCommand.php @@ -9,43 +9,49 @@ */ namespace PHPUnit\TextUI\Command; +use const PHP_EOL; +use function count; use function sprintf; use function str_replace; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\TextUI\Configuration\Registry; -use RecursiveIteratorIterator; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ListTestsAsTextCommand implements Command { - private TestSuite $suite; + /** + * @var list + */ + private array $tests; - public function __construct(TestSuite $suite) + /** + * @param list $tests + */ + public function __construct(array $tests) { - $this->suite = $suite; + $this->tests = $tests; } public function execute(): Result { - $buffer = $this->warnAboutConflictingOptions(); - - $buffer .= 'Available test(s):' . PHP_EOL; + $buffer = sprintf( + 'Available test%s:' . PHP_EOL, + count($this->tests) > 1 ? 's' : '', + ); - foreach (new RecursiveIteratorIterator($this->suite) as $test) { + foreach ($this->tests as $test) { if ($test instanceof TestCase) { $name = sprintf( '%s::%s', $test::class, str_replace(' with data set ', '', $test->nameWithDataSet()), ); - } elseif ($test instanceof PhptTestCase) { - $name = $test->getName(); } else { - continue; + $name = $test->getName(); } $buffer .= sprintf( @@ -56,29 +62,4 @@ public function execute(): Result return Result::from($buffer); } - - private function warnAboutConflictingOptions(): string - { - $buffer = ''; - - $configuration = Registry::get(); - - if ($configuration->hasFilter()) { - $buffer .= 'The --filter and --list-tests options cannot be combined, --filter is ignored' . PHP_EOL; - } - - if ($configuration->hasGroups()) { - $buffer .= 'The --group and --list-tests options cannot be combined, --group is ignored' . PHP_EOL; - } - - if ($configuration->hasExcludeGroups()) { - $buffer .= 'The --exclude-group and --list-tests options cannot be combined, --exclude-group is ignored' . PHP_EOL; - } - - if (!empty($buffer)) { - $buffer .= PHP_EOL; - } - - return $buffer; - } } diff --git a/src/TextUI/Command/Commands/ListTestsAsXmlCommand.php b/src/TextUI/Command/Commands/ListTestsAsXmlCommand.php index 19638b0f22e..67b17d2addd 100644 --- a/src/TextUI/Command/Commands/ListTestsAsXmlCommand.php +++ b/src/TextUI/Command/Commands/ListTestsAsXmlCommand.php @@ -9,115 +9,126 @@ */ namespace PHPUnit\TextUI\Command; +use const PHP_EOL; use function file_put_contents; -use function implode; +use function ksort; use function sprintf; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\TextUI\Configuration\Registry; -use RecursiveIteratorIterator; +use PHPUnit\Runner\Phpt\TestCase as PhptTestCase; use XMLWriter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ListTestsAsXmlCommand implements Command { + /** + * @var list + */ + private array $tests; private string $filename; - private TestSuite $suite; - public function __construct(string $filename, TestSuite $suite) + /** + * @param list $tests + */ + public function __construct(array $tests, string $filename) { + $this->tests = $tests; $this->filename = $filename; - $this->suite = $suite; } public function execute(): Result { - $buffer = $this->warnAboutConflictingOptions(); $writer = new XMLWriter; $writer->openMemory(); $writer->setIndent(true); $writer->startDocument(); + + $writer->startElement('testSuite'); + $writer->writeAttribute('xmlns', 'https://xml.phpunit.de/testSuite'); + $writer->startElement('tests'); - $currentTestCase = null; + $currentTestClass = null; + $groups = []; - foreach (new RecursiveIteratorIterator($this->suite) as $test) { + foreach ($this->tests as $test) { if ($test instanceof TestCase) { - if ($test::class !== $currentTestCase) { - if ($currentTestCase !== null) { + foreach ($test->groups() as $group) { + if (!isset($groups[$group])) { + $groups[$group] = []; + } + + $groups[$group][] = $test->valueObjectForEvents()->id(); + } + + if ($test::class !== $currentTestClass) { + if ($currentTestClass !== null) { $writer->endElement(); } - $writer->startElement('testCaseClass'); + $writer->startElement('testClass'); $writer->writeAttribute('name', $test::class); + $writer->writeAttribute('file', $test->valueObjectForEvents()->file()); - $currentTestCase = $test::class; + $currentTestClass = $test::class; } - $writer->startElement('testCaseMethod'); + $writer->startElement('testMethod'); $writer->writeAttribute('id', $test->valueObjectForEvents()->id()); - $writer->writeAttribute('name', $test->name()); - $writer->writeAttribute('groups', implode(',', $test->groups())); + $writer->writeAttribute('name', $test->valueObjectForEvents()->methodName()); $writer->endElement(); continue; } - if ($test instanceof PhptTestCase) { - if ($currentTestCase !== null) { - $writer->endElement(); - - $currentTestCase = null; - } - - $writer->startElement('phptFile'); - $writer->writeAttribute('path', $test->getName()); + if ($currentTestClass !== null) { $writer->endElement(); + + $currentTestClass = null; } + + $writer->startElement('phpt'); + $writer->writeAttribute('file', $test->getName()); + $writer->endElement(); } - if ($currentTestCase !== null) { + if ($currentTestClass !== null) { $writer->endElement(); } $writer->endElement(); - file_put_contents($this->filename, $writer->outputMemory()); + ksort($groups); - $buffer .= sprintf( - 'Wrote list of tests that would have been run to %s' . PHP_EOL, - $this->filename, - ); + $writer->startElement('groups'); - return Result::from($buffer); - } - - private function warnAboutConflictingOptions(): string - { - $buffer = ''; - - $configuration = Registry::get(); + foreach ($groups as $groupName => $testIds) { + $writer->startElement('group'); + $writer->writeAttribute('name', (string) $groupName); - if ($configuration->hasFilter()) { - $buffer .= 'The --filter and --list-tests-xml options cannot be combined, --filter is ignored' . PHP_EOL; - } + foreach ($testIds as $testId) { + $writer->startElement('test'); + $writer->writeAttribute('id', $testId); + $writer->endElement(); + } - if ($configuration->hasGroups()) { - $buffer .= 'The --group and --list-tests-xml options cannot be combined, --group is ignored' . PHP_EOL; + $writer->endElement(); } - if ($configuration->hasExcludeGroups()) { - $buffer .= 'The --exclude-group and --list-tests-xml options cannot be combined, --exclude-group is ignored' . PHP_EOL; - } + $writer->endElement(); + $writer->endElement(); - if (!empty($buffer)) { - $buffer .= PHP_EOL; - } + file_put_contents($this->filename, $writer->outputMemory()); - return $buffer; + return Result::from( + sprintf( + 'Wrote list of tests that would have been run to %s' . PHP_EOL, + $this->filename, + ), + ); } } diff --git a/src/TextUI/Command/Commands/MigrateConfigurationCommand.php b/src/TextUI/Command/Commands/MigrateConfigurationCommand.php index 428410fc803..507ff90f346 100644 --- a/src/TextUI/Command/Commands/MigrateConfigurationCommand.php +++ b/src/TextUI/Command/Commands/MigrateConfigurationCommand.php @@ -9,12 +9,16 @@ */ namespace PHPUnit\TextUI\Command; +use const PHP_EOL; use function copy; use function file_put_contents; +use function sprintf; use PHPUnit\TextUI\XmlConfiguration\Migrator; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class MigrateConfigurationCommand implements Command @@ -28,24 +32,33 @@ public function __construct(string $filename) public function execute(): Result { - copy($this->filename, $this->filename . '.bak'); + try { + $migrated = (new Migrator)->migrate($this->filename); - $buffer = 'Created backup: ' . $this->filename . '.bak' . PHP_EOL; - $shellExitCode = Result::SUCCESS; + copy($this->filename, $this->filename . '.bak'); - try { - file_put_contents( - $this->filename, - (new Migrator)->migrate($this->filename), - ); + file_put_contents($this->filename, $migrated); - $buffer .= 'Migrated configuration: ' . $this->filename . PHP_EOL; + return Result::from( + sprintf( + 'Created backup: %s.bak%sMigrated configuration: %s%s', + $this->filename, + PHP_EOL, + $this->filename, + PHP_EOL, + ), + ); } catch (Throwable $t) { - $buffer .= 'Migration failed: ' . $t->getMessage() . PHP_EOL; - - $shellExitCode = Result::FAILURE; + return Result::from( + sprintf( + 'Migration of %s failed:%s%s%s', + $this->filename, + PHP_EOL, + $t->getMessage(), + PHP_EOL, + ), + Result::FAILURE, + ); } - - return Result::from($buffer, $shellExitCode); } } diff --git a/src/TextUI/Command/Commands/ShowHelpCommand.php b/src/TextUI/Command/Commands/ShowHelpCommand.php index c58231f8b17..1fd04811fc7 100644 --- a/src/TextUI/Command/Commands/ShowHelpCommand.php +++ b/src/TextUI/Command/Commands/ShowHelpCommand.php @@ -12,6 +12,8 @@ use PHPUnit\TextUI\Help; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ShowHelpCommand implements Command diff --git a/src/TextUI/Command/Commands/ShowVersionCommand.php b/src/TextUI/Command/Commands/ShowVersionCommand.php index b5ddfc29fbf..4455a3d2359 100644 --- a/src/TextUI/Command/Commands/ShowVersionCommand.php +++ b/src/TextUI/Command/Commands/ShowVersionCommand.php @@ -10,9 +10,11 @@ namespace PHPUnit\TextUI\Command; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ShowVersionCommand implements Command +final readonly class ShowVersionCommand implements Command { public function execute(): Result { diff --git a/src/TextUI/Command/Commands/VersionCheckCommand.php b/src/TextUI/Command/Commands/VersionCheckCommand.php index 9de4c02a9e0..3e076ebeedc 100644 --- a/src/TextUI/Command/Commands/VersionCheckCommand.php +++ b/src/TextUI/Command/Commands/VersionCheckCommand.php @@ -9,35 +9,68 @@ */ namespace PHPUnit\TextUI\Command; -use function file_get_contents; +use const PHP_EOL; +use function assert; use function sprintf; use function version_compare; -use PHPUnit\Runner\Version; +use PHPUnit\Util\Http\Downloader; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @codeCoverageIgnore + * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class VersionCheckCommand implements Command +final readonly class VersionCheckCommand implements Command { + private Downloader $downloader; + private int $majorVersionNumber; + private string $versionId; + + public function __construct(Downloader $downloader, int $majorVersionNumber, string $versionId) + { + $this->downloader = $downloader; + $this->majorVersionNumber = $majorVersionNumber; + $this->versionId = $versionId; + } + public function execute(): Result { - $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); - $isOutdated = version_compare($latestVersion, Version::id(), '>'); + $latestVersion = $this->downloader->download('https://phar.phpunit.de/latest-version-of/phpunit'); + + assert($latestVersion !== false); + + $latestCompatibleVersion = $this->downloader->download('https://phar.phpunit.de/latest-version-of/phpunit-' . $this->majorVersionNumber); + + $notLatest = version_compare($latestVersion, $this->versionId, '>'); + $notLatestCompatible = false; - if ($isOutdated) { + if ($latestCompatibleVersion !== false) { + $notLatestCompatible = version_compare($latestCompatibleVersion, $this->versionId, '>'); + } + + if (!$notLatest && !$notLatestCompatible) { return Result::from( - sprintf( - 'You are not using the latest version of PHPUnit.' . PHP_EOL . - 'The latest version is PHPUnit %s.' . PHP_EOL, - $latestVersion, - ), + 'You are using the latest version of PHPUnit.' . PHP_EOL, + ); + } + + $buffer = 'You are not using the latest version of PHPUnit.' . PHP_EOL; + + if ($notLatestCompatible) { + $buffer .= sprintf( + 'The latest version compatible with PHPUnit %s is PHPUnit %s.' . PHP_EOL, + $this->versionId, + $latestCompatibleVersion, + ); + } + + if ($notLatest) { + $buffer .= sprintf( + 'The latest version is PHPUnit %s.' . PHP_EOL, + $latestVersion, ); } - return Result::from( - 'You are using the latest version of PHPUnit.' . PHP_EOL, - ); + return Result::from($buffer, Result::FAILURE); } } diff --git a/src/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php b/src/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php index abd81f9a9ec..7d1afafe322 100644 --- a/src/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php +++ b/src/TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TextUI\Command; +use const PHP_EOL; use function printf; use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry; use PHPUnit\TextUI\Configuration\Configuration; @@ -18,7 +19,11 @@ use SebastianBergmann\Timer\Timer; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore */ final readonly class WarmCodeCoverageCacheCommand implements Command { @@ -58,7 +63,8 @@ public function execute(): Result print 'Warming cache for static analysis ... '; - (new CacheWarmer)->warmCache( + /** @phpstan-ignore new.internalClass,method.internalClass */ + $statistics = (new CacheWarmer)->warmCache( $this->configuration->coverageCacheDirectory(), !$this->configuration->disableCodeCoverageIgnore(), $this->configuration->ignoreDeprecatedCodeUnitsFromCodeCoverage(), @@ -66,9 +72,17 @@ public function execute(): Result ); printf( - '[%s]%s', + '[%s]%s%s%d file%s processed, %d cache hit%s, %d cache miss%s%s', $timer->stop()->asString(), - \PHP_EOL, + PHP_EOL, + PHP_EOL, + $statistics['cacheHits'] + $statistics['cacheMisses'], + ($statistics['cacheHits'] + $statistics['cacheMisses']) !== 1 ? 's' : '', + $statistics['cacheHits'], + $statistics['cacheHits'] !== 1 ? 's' : '', + $statistics['cacheMisses'], + $statistics['cacheMisses'] !== 1 ? 'es' : '', + PHP_EOL, ); return Result::from(); diff --git a/src/TextUI/Command/Result.php b/src/TextUI/Command/Result.php index 7391c6da2ef..ae4a3e27fe8 100644 --- a/src/TextUI/Command/Result.php +++ b/src/TextUI/Command/Result.php @@ -10,18 +10,20 @@ namespace PHPUnit\TextUI\Command; /** - * @psalm-immutable + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Result +final readonly class Result { - public const SUCCESS = 0; - public const FAILURE = 1; - public const EXCEPTION = 2; - public const CRASH = 255; - private readonly string $output; - private readonly int $shellExitCode; + public const int SUCCESS = 0; + public const int FAILURE = 1; + public const int EXCEPTION = 2; + public const int CRASH = 255; + private string $output; + private int $shellExitCode; public static function from(string $output = '', int $shellExitCode = self::SUCCESS): self { diff --git a/src/TextUI/Configuration/Builder.php b/src/TextUI/Configuration/Builder.php index 48fc057880e..6f9e81a1558 100644 --- a/src/TextUI/Configuration/Builder.php +++ b/src/TextUI/Configuration/Builder.php @@ -21,9 +21,11 @@ * * @codeCoverageIgnore */ -final class Builder +final readonly class Builder { /** + * @param list $argv + * * @throws ConfigurationCannotBeBuiltException */ public function build(array $argv): Configuration @@ -33,7 +35,7 @@ public function build(array $argv): Configuration $configurationFile = (new XmlConfigurationFileFinder)->find($cliConfiguration); $xmlConfiguration = DefaultConfiguration::create(); - if ($configurationFile) { + if ($configurationFile !== false) { $xmlConfiguration = (new Loader)->load($configurationFile); } diff --git a/src/TextUI/Configuration/Cli/Builder.php b/src/TextUI/Configuration/Cli/Builder.php index 7a4bc9d7622..7a42d230762 100644 --- a/src/TextUI/Configuration/Cli/Builder.php +++ b/src/TextUI/Configuration/Cli/Builder.php @@ -9,23 +9,32 @@ */ namespace PHPUnit\TextUI\CliArguments; -use function array_map; +use const DIRECTORY_SEPARATOR; +use function assert; use function basename; use function explode; use function getcwd; use function is_file; use function is_numeric; use function sprintf; +use function strtolower; +use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\Util\Filesystem; use SebastianBergmann\CliParser\Exception as CliParserException; use SebastianBergmann\CliParser\Parser as CliParser; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Builder { - private const LONG_OPTIONS = [ + /** + * @var non-empty-list + */ + private const array LONG_OPTIONS = [ 'atleast-version=', 'bootstrap=', 'cache-result', @@ -43,12 +52,17 @@ final class Builder 'coverage-html=', 'coverage-php=', 'coverage-text==', + 'only-summary-for-coverage-text', + 'show-uncovered-for-coverage-text', 'coverage-xml=', 'path-coverage', 'disallow-test-output', + 'display-all-issues', 'display-incomplete', 'display-skipped', 'display-deprecations', + 'display-phpunit-deprecations', + 'display-phpunit-notices', 'display-errors', 'display-notices', 'display-warnings', @@ -56,6 +70,7 @@ final class Builder 'enforce-time-limit', 'exclude-group=', 'filter=', + 'exclude-filter=', 'generate-baseline=', 'use-baseline=', 'ignore-baseline', @@ -64,12 +79,14 @@ final class Builder 'group=', 'covers=', 'uses=', + 'requires-php-extension=', 'help', 'resolve-dependencies', 'ignore-dependencies', 'include-path=', 'list-groups', 'list-suites', + 'list-test-files', 'list-tests', 'list-tests-xml=', 'log-junit=', @@ -91,7 +108,10 @@ final class Builder 'reverse-list', 'static-backup', 'stderr', + 'fail-on-all-issues', 'fail-on-deprecation', + 'fail-on-phpunit-deprecation', + 'fail-on-phpunit-notice', 'fail-on-empty-test-suite', 'fail-on-incomplete', 'fail-on-notice', @@ -99,7 +119,7 @@ final class Builder 'fail-on-skipped', 'fail-on-warning', 'stop-on-defect', - 'stop-on-deprecation', + 'stop-on-deprecation==', 'stop-on-error', 'stop-on-failure', 'stop-on-incomplete', @@ -112,6 +132,7 @@ final class Builder 'strict-global-state', 'teamcity', 'testdox', + 'testdox-summary', 'testdox-html=', 'testdox-text=', 'test-suffix=', @@ -120,10 +141,21 @@ final class Builder 'log-events-text=', 'log-events-verbose-text=', 'version', + 'debug', + 'with-telemetry', + 'extension=', ]; - private const SHORT_OPTIONS = 'd:c:h'; + + private const string SHORT_OPTIONS = 'd:c:h'; + + /** + * @var array + */ + private array $processed = []; /** + * @param list $parameters + * * @throws Exception */ public function fromParameters(array $parameters): Configuration @@ -168,9 +200,12 @@ public function fromParameters(array $parameters): Configuration $defaultTimeLimit = null; $disableCodeCoverageIgnore = null; $disallowTestOutput = null; + $displayAllIssues = null; $displayIncomplete = null; $displaySkipped = null; $displayDeprecations = null; + $displayPhpunitDeprecations = null; + $displayPhpunitNotices = null; $displayErrors = null; $displayNotices = null; $displayWarnings = null; @@ -178,7 +213,10 @@ public function fromParameters(array $parameters): Configuration $excludeGroups = null; $executionOrder = null; $executionOrderDefects = null; + $failOnAllIssues = null; $failOnDeprecation = null; + $failOnPhpunitDeprecation = null; + $failOnPhpunitNotice = null; $failOnEmptyTestSuite = null; $failOnIncomplete = null; $failOnNotice = null; @@ -187,6 +225,7 @@ public function fromParameters(array $parameters): Configuration $failOnWarning = null; $stopOnDefect = null; $stopOnDeprecation = null; + $specificDeprecationToStopOn = null; $stopOnError = null; $stopOnFailure = null; $stopOnIncomplete = null; @@ -195,6 +234,7 @@ public function fromParameters(array $parameters): Configuration $stopOnSkipped = null; $stopOnWarning = null; $filter = null; + $excludeFilter = null; $generateBaseline = null; $useBaseline = null; $ignoreBaseline = false; @@ -203,12 +243,14 @@ public function fromParameters(array $parameters): Configuration $groups = null; $testsCovering = null; $testsUsing = null; + $testsRequiringPhpExtension = null; $help = false; $includePath = null; $iniSettings = []; $junitLogfile = null; $listGroups = false; $listSuites = false; + $listTestFiles = false; $listTests = false; $listTestsXml = null; $noCoverage = null; @@ -236,11 +278,21 @@ public function fromParameters(array $parameters): Configuration $logEventsVerboseText = null; $printerTeamCity = null; $printerTestDox = null; + $printerTestDoxSummary = null; + $debug = false; + $withTelemetry = false; + $extensions = []; foreach ($options[0] as $option) { + $optionAllowedMultipleTimes = false; + switch ($option[0]) { case '--colors': - $colors = $option[1] ?: \PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO; + $colors = \PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO; + + if ($option[1] !== null) { + $colors = $option[1]; + } break; @@ -314,9 +366,17 @@ public function fromParameters(array $parameters): Configuration $option[1] = 'php://stdout'; } - $coverageText = $option[1]; - $coverageTextShowUncoveredFiles = false; - $coverageTextShowOnlySummary = false; + $coverageText = $option[1]; + + break; + + case '--only-summary-for-coverage-text': + $coverageTextShowOnlySummary = true; + + break; + + case '--show-uncovered-for-coverage-text': + $coverageTextShowUncoveredFiles = true; break; @@ -334,13 +394,19 @@ public function fromParameters(array $parameters): Configuration $tmp = explode('=', $option[1]); if (isset($tmp[0])) { + assert($tmp[0] !== ''); + if (isset($tmp[1])) { + assert($tmp[1] !== ''); + $iniSettings[$tmp[0]] = $tmp[1]; } else { $iniSettings[$tmp[0]] = '1'; } } + $optionAllowedMultipleTimes = true; + break; case 'h': @@ -354,6 +420,11 @@ public function fromParameters(array $parameters): Configuration break; + case '--exclude-filter': + $excludeFilter = $option[1]; + + break; + case '--testsuite': $testSuite = $option[1]; @@ -376,7 +447,7 @@ public function fromParameters(array $parameters): Configuration case '--use-baseline': $useBaseline = $option[1]; - if (!is_file($useBaseline) && basename($useBaseline) === $useBaseline) { + if (basename($useBaseline) === $useBaseline && !is_file($useBaseline)) { $useBaseline = getcwd() . DIRECTORY_SEPARATOR . $useBaseline; } @@ -398,27 +469,68 @@ public function fromParameters(array $parameters): Configuration break; case '--group': - $groups = explode(',', $option[1]); + if ($groups === null) { + $groups = []; + } + + $groups[] = $option[1]; + + $optionAllowedMultipleTimes = true; break; case '--exclude-group': - $excludeGroups = explode(',', $option[1]); + if ($excludeGroups === null) { + $excludeGroups = []; + } + + $excludeGroups[] = $option[1]; + + $optionAllowedMultipleTimes = true; break; case '--covers': - $testsCovering = array_map('strtolower', explode(',', $option[1])); + if ($testsCovering === null) { + $testsCovering = []; + } + + $testsCovering[] = strtolower($option[1]); + + $optionAllowedMultipleTimes = true; break; case '--uses': - $testsUsing = array_map('strtolower', explode(',', $option[1])); + if ($testsUsing === null) { + $testsUsing = []; + } + + $testsUsing[] = strtolower($option[1]); + + $optionAllowedMultipleTimes = true; + + break; + + case '--requires-php-extension': + if ($testsRequiringPhpExtension === null) { + $testsRequiringPhpExtension = []; + } + + $testsRequiringPhpExtension[] = strtolower($option[1]); + + $optionAllowedMultipleTimes = true; break; case '--test-suffix': - $testSuffixes = explode(',', $option[1]); + if ($testSuffixes === null) { + $testSuffixes = []; + } + + $testSuffixes[] = $option[1]; + + $optionAllowedMultipleTimes = true; break; @@ -437,6 +549,11 @@ public function fromParameters(array $parameters): Configuration break; + case '--list-test-files': + $listTestFiles = true; + + break; + case '--list-tests': $listTests = true; @@ -524,11 +641,26 @@ public function fromParameters(array $parameters): Configuration break; + case '--fail-on-all-issues': + $failOnAllIssues = true; + + break; + case '--fail-on-deprecation': $failOnDeprecation = true; break; + case '--fail-on-phpunit-deprecation': + $failOnPhpunitDeprecation = true; + + break; + + case '--fail-on-phpunit-notice': + $failOnPhpunitNotice = true; + + break; + case '--fail-on-empty-test-suite': $failOnEmptyTestSuite = true; @@ -567,6 +699,10 @@ public function fromParameters(array $parameters): Configuration case '--stop-on-deprecation': $stopOnDeprecation = true; + if ($option[1] !== null) { + $specificDeprecationToStopOn = $option[1]; + } + break; case '--stop-on-error': @@ -614,6 +750,11 @@ public function fromParameters(array $parameters): Configuration break; + case '--testdox-summary': + $printerTestDoxSummary = true; + + break; + case '--testdox-html': $testdoxHtmlFile = $option[1]; @@ -704,6 +845,11 @@ public function fromParameters(array $parameters): Configuration break; + case '--display-all-issues': + $displayAllIssues = true; + + break; + case '--display-incomplete': $displayIncomplete = true; @@ -719,6 +865,16 @@ public function fromParameters(array $parameters): Configuration break; + case '--display-phpunit-deprecations': + $displayPhpunitDeprecations = true; + + break; + + case '--display-phpunit-notices': + $displayPhpunitNotices = true; + + break; + case '--display-errors': $displayErrors = true; @@ -761,6 +917,8 @@ public function fromParameters(array $parameters): Configuration $coverageFilter[] = $option[1]; + $optionAllowedMultipleTimes = true; + break; case '--random-order': @@ -789,23 +947,62 @@ public function fromParameters(array $parameters): Configuration break; case '--log-events-text': - $logEventsText = $option[1]; + $logEventsText = Filesystem::resolveStreamOrFile($option[1]); + + if ($logEventsText === false) { + throw new Exception( + sprintf( + 'The path "%s" specified for the --log-events-text option could not be resolved', + $option[1], + ), + ); + } break; case '--log-events-verbose-text': - $logEventsVerboseText = $option[1]; + $logEventsVerboseText = Filesystem::resolveStreamOrFile($option[1]); + + if ($logEventsVerboseText === false) { + throw new Exception( + sprintf( + 'The path "%s" specified for the --log-events-verbose-text option could not be resolved', + $option[1], + ), + ); + } break; + + case '--debug': + $debug = true; + + break; + + case '--with-telemetry': + $withTelemetry = true; + + break; + + case '--extension': + $extensions[] = $option[1]; + + $optionAllowedMultipleTimes = true; + + break; + } + + if (!$optionAllowedMultipleTimes) { + $this->markProcessed($option[0]); } } - if (empty($iniSettings)) { + if ($iniSettings === []) { $iniSettings = null; } - if (empty($coverageFilter)) { - $coverageFilter = null; + if ($extensions === []) { + $extensions = null; } return new Configuration( @@ -839,7 +1036,10 @@ public function fromParameters(array $parameters): Configuration $excludeGroups, $executionOrder, $executionOrderDefects, + $failOnAllIssues, $failOnDeprecation, + $failOnPhpunitDeprecation, + $failOnPhpunitNotice, $failOnEmptyTestSuite, $failOnIncomplete, $failOnNotice, @@ -848,6 +1048,7 @@ public function fromParameters(array $parameters): Configuration $failOnWarning, $stopOnDefect, $stopOnDeprecation, + $specificDeprecationToStopOn, $stopOnError, $stopOnFailure, $stopOnIncomplete, @@ -856,6 +1057,7 @@ public function fromParameters(array $parameters): Configuration $stopOnSkipped, $stopOnWarning, $filter, + $excludeFilter, $generateBaseline, $useBaseline, $ignoreBaseline, @@ -864,12 +1066,14 @@ public function fromParameters(array $parameters): Configuration $groups, $testsCovering, $testsUsing, + $testsRequiringPhpExtension, $help, $includePath, $iniSettings, $junitLogfile, $listGroups, $listSuites, + $listTestFiles, $listTests, $listTestsXml, $noCoverage, @@ -892,9 +1096,12 @@ public function fromParameters(array $parameters): Configuration $testSuite, $excludeTestSuite, $useDefaultConfiguration, + $displayAllIssues, $displayIncomplete, $displaySkipped, $displayDeprecations, + $displayPhpunitDeprecations, + $displayPhpunitNotices, $displayErrors, $displayNotices, $displayWarnings, @@ -904,6 +1111,33 @@ public function fromParameters(array $parameters): Configuration $logEventsVerboseText, $printerTeamCity, $printerTestDox, + $printerTestDoxSummary, + $debug, + $withTelemetry, + $extensions, ); } + + /** + * @param non-empty-string $option + */ + private function markProcessed(string $option): void + { + if (!isset($this->processed[$option])) { + $this->processed[$option] = 1; + + return; + } + + $this->processed[$option]++; + + if ($this->processed[$option] === 2) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Option %s cannot be used more than once', + $option, + ), + ); + } + } } diff --git a/src/TextUI/Configuration/Cli/Configuration.php b/src/TextUI/Configuration/Cli/Configuration.php index 7f4b880fccc..3572a6ef7b3 100644 --- a/src/TextUI/Configuration/Cli/Configuration.php +++ b/src/TextUI/Configuration/Cli/Configuration.php @@ -10,14 +10,16 @@ namespace PHPUnit\TextUI\CliArguments; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Configuration { /** - * @psalm-var list + * @var list */ private array $arguments; private ?string $atLeastVersion; @@ -31,6 +33,10 @@ private ?string $colors; private null|int|string $columns; private ?string $configurationFile; + + /** + * @var ?non-empty-list + */ private ?array $coverageFilter; private ?string $coverageClover; private ?string $coverageCobertura; @@ -47,10 +53,17 @@ private ?bool $disableCodeCoverageIgnore; private ?bool $disallowTestOutput; private ?bool $enforceTimeLimit; + + /** + * @var ?non-empty-list + */ private ?array $excludeGroups; private ?int $executionOrder; private ?int $executionOrderDefects; + private ?bool $failOnAllIssues; private ?bool $failOnDeprecation; + private ?bool $failOnPhpunitDeprecation; + private ?bool $failOnPhpunitNotice; private ?bool $failOnEmptyTestSuite; private ?bool $failOnIncomplete; private ?bool $failOnNotice; @@ -59,6 +72,7 @@ private ?bool $failOnWarning; private ?bool $stopOnDefect; private ?bool $stopOnDeprecation; + private ?string $specificDeprecationToStopOn; private ?bool $stopOnError; private ?bool $stopOnFailure; private ?bool $stopOnIncomplete; @@ -67,20 +81,43 @@ private ?bool $stopOnSkipped; private ?bool $stopOnWarning; private ?string $filter; + private ?string $excludeFilter; private ?string $generateBaseline; private ?string $useBaseline; private bool $ignoreBaseline; private bool $generateConfiguration; private bool $migrateConfiguration; + + /** + * @var ?non-empty-list + */ private ?array $groups; + + /** + * @var ?non-empty-list + */ private ?array $testsCovering; + + /** + * @var ?non-empty-list + */ private ?array $testsUsing; + + /** + * @var ?non-empty-list + */ + private ?array $testsRequiringPhpExtension; private bool $help; private ?string $includePath; + + /** + * @var ?non-empty-array + */ private ?array $iniSettings; private ?string $junitLogfile; private bool $listGroups; private bool $listSuites; + private bool $listTestFiles; private bool $listTests; private ?string $listTestsXml; private ?bool $noCoverage; @@ -101,29 +138,48 @@ private ?string $testdoxHtmlFile; private ?string $testdoxTextFile; private ?bool $testdoxPrinter; + private ?bool $testdoxPrinterSummary; /** - * @psalm-var ?non-empty-list + * @var ?non-empty-list */ private ?array $testSuffixes; private ?string $testSuite; private ?string $excludeTestSuite; private bool $useDefaultConfiguration; + private ?bool $displayDetailsOnAllIssues; private ?bool $displayDetailsOnIncompleteTests; private ?bool $displayDetailsOnSkippedTests; private ?bool $displayDetailsOnTestsThatTriggerDeprecations; + private ?bool $displayDetailsOnPhpunitDeprecations; + private ?bool $displayDetailsOnPhpunitNotices; private ?bool $displayDetailsOnTestsThatTriggerErrors; private ?bool $displayDetailsOnTestsThatTriggerNotices; private ?bool $displayDetailsOnTestsThatTriggerWarnings; private bool $version; private ?string $logEventsText; private ?string $logEventsVerboseText; + private bool $debug; + private bool $withTelemetry; + + /** + * @var ?non-empty-list + */ + private ?array $extensions; /** - * @psalm-param list $arguments - * @psalm-param ?non-empty-list $testSuffixes + * @param list $arguments + * @param ?non-empty-list $excludeGroups + * @param ?non-empty-list $groups + * @param ?non-empty-list $testsCovering + * @param ?non-empty-list $testsUsing + * @param ?non-empty-list $testsRequiringPhpExtension + * @param ?non-empty-array $iniSettings + * @param ?non-empty-list $testSuffixes + * @param ?non-empty-list $coverageFilter + * @param ?non-empty-list $extensions */ - public function __construct(array $arguments, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticProperties, ?bool $beStrictAboutChangesToGlobalState, ?string $bootstrap, ?string $cacheDirectory, ?bool $cacheResult, bool $checkVersion, ?string $colors, null|int|string $columns, ?string $configurationFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, bool $warmCoverageCache, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?bool $failOnDeprecation, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnNotice, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?bool $stopOnDefect, ?bool $stopOnDeprecation, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnNotice, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $filter, ?string $generateBaseline, ?string $useBaseline, bool $ignoreBaseline, bool $generateConfiguration, bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, bool $listGroups, bool $listSuites, bool $listTests, ?string $listTestsXml, ?bool $noCoverage, ?bool $noExtensions, ?bool $noOutput, ?bool $noProgress, ?bool $noResults, ?bool $noLogging, ?bool $processIsolation, ?int $randomOrderSeed, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?string $teamcityLogfile, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?array $testSuffixes, ?string $testSuite, ?string $excludeTestSuite, bool $useDefaultConfiguration, ?bool $displayDetailsOnIncompleteTests, ?bool $displayDetailsOnSkippedTests, ?bool $displayDetailsOnTestsThatTriggerDeprecations, ?bool $displayDetailsOnTestsThatTriggerErrors, ?bool $displayDetailsOnTestsThatTriggerNotices, ?bool $displayDetailsOnTestsThatTriggerWarnings, bool $version, ?array $coverageFilter, ?string $logEventsText, ?string $logEventsVerboseText, ?bool $printerTeamCity, ?bool $printerTestDox) + public function __construct(array $arguments, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticProperties, ?bool $beStrictAboutChangesToGlobalState, ?string $bootstrap, ?string $cacheDirectory, ?bool $cacheResult, bool $checkVersion, ?string $colors, null|int|string $columns, ?string $configurationFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, bool $warmCoverageCache, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?bool $failOnAllIssues, ?bool $failOnDeprecation, ?bool $failOnPhpunitDeprecation, ?bool $failOnPhpunitNotice, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnNotice, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?bool $stopOnDefect, ?bool $stopOnDeprecation, ?string $specificDeprecationToStopOn, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnNotice, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $filter, ?string $excludeFilter, ?string $generateBaseline, ?string $useBaseline, bool $ignoreBaseline, bool $generateConfiguration, bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?array $testsRequiringPhpExtension, bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, bool $listGroups, bool $listSuites, bool $listTestFiles, bool $listTests, ?string $listTestsXml, ?bool $noCoverage, ?bool $noExtensions, ?bool $noOutput, ?bool $noProgress, ?bool $noResults, ?bool $noLogging, ?bool $processIsolation, ?int $randomOrderSeed, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?string $teamcityLogfile, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?array $testSuffixes, ?string $testSuite, ?string $excludeTestSuite, bool $useDefaultConfiguration, ?bool $displayDetailsOnAllIssues, ?bool $displayDetailsOnIncompleteTests, ?bool $displayDetailsOnSkippedTests, ?bool $displayDetailsOnTestsThatTriggerDeprecations, ?bool $displayDetailsOnPhpunitDeprecations, ?bool $displayDetailsOnPhpunitNotices, ?bool $displayDetailsOnTestsThatTriggerErrors, ?bool $displayDetailsOnTestsThatTriggerNotices, ?bool $displayDetailsOnTestsThatTriggerWarnings, bool $version, ?array $coverageFilter, ?string $logEventsText, ?string $logEventsVerboseText, ?bool $printerTeamCity, ?bool $testdoxPrinter, ?bool $testdoxPrinterSummary, bool $debug, bool $withTelemetry, ?array $extensions) { $this->arguments = $arguments; $this->atLeastVersion = $atLeastVersion; @@ -156,7 +212,10 @@ public function __construct(array $arguments, ?string $atLeastVersion, ?bool $ba $this->excludeGroups = $excludeGroups; $this->executionOrder = $executionOrder; $this->executionOrderDefects = $executionOrderDefects; + $this->failOnAllIssues = $failOnAllIssues; $this->failOnDeprecation = $failOnDeprecation; + $this->failOnPhpunitDeprecation = $failOnPhpunitDeprecation; + $this->failOnPhpunitNotice = $failOnPhpunitNotice; $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; $this->failOnIncomplete = $failOnIncomplete; $this->failOnNotice = $failOnNotice; @@ -165,6 +224,7 @@ public function __construct(array $arguments, ?string $atLeastVersion, ?bool $ba $this->failOnWarning = $failOnWarning; $this->stopOnDefect = $stopOnDefect; $this->stopOnDeprecation = $stopOnDeprecation; + $this->specificDeprecationToStopOn = $specificDeprecationToStopOn; $this->stopOnError = $stopOnError; $this->stopOnFailure = $stopOnFailure; $this->stopOnIncomplete = $stopOnIncomplete; @@ -173,6 +233,7 @@ public function __construct(array $arguments, ?string $atLeastVersion, ?bool $ba $this->stopOnSkipped = $stopOnSkipped; $this->stopOnWarning = $stopOnWarning; $this->filter = $filter; + $this->excludeFilter = $excludeFilter; $this->generateBaseline = $generateBaseline; $this->useBaseline = $useBaseline; $this->ignoreBaseline = $ignoreBaseline; @@ -181,12 +242,14 @@ public function __construct(array $arguments, ?string $atLeastVersion, ?bool $ba $this->groups = $groups; $this->testsCovering = $testsCovering; $this->testsUsing = $testsUsing; + $this->testsRequiringPhpExtension = $testsRequiringPhpExtension; $this->help = $help; $this->includePath = $includePath; $this->iniSettings = $iniSettings; $this->junitLogfile = $junitLogfile; $this->listGroups = $listGroups; $this->listSuites = $listSuites; + $this->listTestFiles = $listTestFiles; $this->listTests = $listTests; $this->listTestsXml = $listTestsXml; $this->noCoverage = $noCoverage; @@ -209,9 +272,12 @@ public function __construct(array $arguments, ?string $atLeastVersion, ?bool $ba $this->testSuite = $testSuite; $this->excludeTestSuite = $excludeTestSuite; $this->useDefaultConfiguration = $useDefaultConfiguration; + $this->displayDetailsOnAllIssues = $displayDetailsOnAllIssues; $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnPhpunitDeprecations = $displayDetailsOnPhpunitDeprecations; + $this->displayDetailsOnPhpunitNotices = $displayDetailsOnPhpunitNotices; $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; @@ -219,11 +285,15 @@ public function __construct(array $arguments, ?string $atLeastVersion, ?bool $ba $this->logEventsText = $logEventsText; $this->logEventsVerboseText = $logEventsVerboseText; $this->teamCityPrinter = $printerTeamCity; - $this->testdoxPrinter = $printerTestDox; + $this->testdoxPrinter = $testdoxPrinter; + $this->testdoxPrinterSummary = $testdoxPrinterSummary; + $this->debug = $debug; + $this->withTelemetry = $withTelemetry; + $this->extensions = $extensions; } /** - * @psalm-return list + * @return list */ public function arguments(): array { @@ -231,7 +301,7 @@ public function arguments(): array } /** - * @psalm-assert-if-true !null $this->atLeastVersion + * @phpstan-assert-if-true !null $this->atLeastVersion */ public function hasAtLeastVersion(): bool { @@ -251,7 +321,7 @@ public function atLeastVersion(): string } /** - * @psalm-assert-if-true !null $this->backupGlobals + * @phpstan-assert-if-true !null $this->backupGlobals */ public function hasBackupGlobals(): bool { @@ -271,7 +341,7 @@ public function backupGlobals(): bool } /** - * @psalm-assert-if-true !null $this->backupStaticProperties + * @phpstan-assert-if-true !null $this->backupStaticProperties */ public function hasBackupStaticProperties(): bool { @@ -291,7 +361,7 @@ public function backupStaticProperties(): bool } /** - * @psalm-assert-if-true !null $this->beStrictAboutChangesToGlobalState + * @phpstan-assert-if-true !null $this->beStrictAboutChangesToGlobalState */ public function hasBeStrictAboutChangesToGlobalState(): bool { @@ -311,7 +381,7 @@ public function beStrictAboutChangesToGlobalState(): bool } /** - * @psalm-assert-if-true !null $this->bootstrap + * @phpstan-assert-if-true !null $this->bootstrap */ public function hasBootstrap(): bool { @@ -331,7 +401,7 @@ public function bootstrap(): string } /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @phpstan-assert-if-true !null $this->cacheDirectory */ public function hasCacheDirectory(): bool { @@ -351,7 +421,7 @@ public function cacheDirectory(): string } /** - * @psalm-assert-if-true !null $this->cacheResult + * @phpstan-assert-if-true !null $this->cacheResult */ public function hasCacheResult(): bool { @@ -376,7 +446,7 @@ public function checkVersion(): bool } /** - * @psalm-assert-if-true !null $this->colors + * @phpstan-assert-if-true !null $this->colors */ public function hasColors(): bool { @@ -396,7 +466,7 @@ public function colors(): string } /** - * @psalm-assert-if-true !null $this->columns + * @phpstan-assert-if-true !null $this->columns */ public function hasColumns(): bool { @@ -416,7 +486,7 @@ public function columns(): int|string } /** - * @psalm-assert-if-true !null $this->configurationFile + * @phpstan-assert-if-true !null $this->configurationFile */ public function hasConfigurationFile(): bool { @@ -436,7 +506,7 @@ public function configurationFile(): string } /** - * @psalm-assert-if-true !null $this->coverageFilter + * @phpstan-assert-if-true !null $this->coverageFilter */ public function hasCoverageFilter(): bool { @@ -445,6 +515,8 @@ public function hasCoverageFilter(): bool /** * @throws Exception + * + * @return non-empty-list */ public function coverageFilter(): array { @@ -456,7 +528,7 @@ public function coverageFilter(): array } /** - * @psalm-assert-if-true !null $this->coverageClover + * @phpstan-assert-if-true !null $this->coverageClover */ public function hasCoverageClover(): bool { @@ -476,7 +548,7 @@ public function coverageClover(): string } /** - * @psalm-assert-if-true !null $this->coverageCobertura + * @phpstan-assert-if-true !null $this->coverageCobertura */ public function hasCoverageCobertura(): bool { @@ -496,7 +568,7 @@ public function coverageCobertura(): string } /** - * @psalm-assert-if-true !null $this->coverageCrap4J + * @phpstan-assert-if-true !null $this->coverageCrap4J */ public function hasCoverageCrap4J(): bool { @@ -516,7 +588,7 @@ public function coverageCrap4J(): string } /** - * @psalm-assert-if-true !null $this->coverageHtml + * @phpstan-assert-if-true !null $this->coverageHtml */ public function hasCoverageHtml(): bool { @@ -536,7 +608,7 @@ public function coverageHtml(): string } /** - * @psalm-assert-if-true !null $this->coveragePhp + * @phpstan-assert-if-true !null $this->coveragePhp */ public function hasCoveragePhp(): bool { @@ -556,7 +628,7 @@ public function coveragePhp(): string } /** - * @psalm-assert-if-true !null $this->coverageText + * @phpstan-assert-if-true !null $this->coverageText */ public function hasCoverageText(): bool { @@ -576,7 +648,7 @@ public function coverageText(): string } /** - * @psalm-assert-if-true !null $this->coverageTextShowUncoveredFiles + * @phpstan-assert-if-true !null $this->coverageTextShowUncoveredFiles */ public function hasCoverageTextShowUncoveredFiles(): bool { @@ -596,7 +668,7 @@ public function coverageTextShowUncoveredFiles(): bool } /** - * @psalm-assert-if-true !null $this->coverageTextShowOnlySummary + * @phpstan-assert-if-true !null $this->coverageTextShowOnlySummary */ public function hasCoverageTextShowOnlySummary(): bool { @@ -616,7 +688,7 @@ public function coverageTextShowOnlySummary(): bool } /** - * @psalm-assert-if-true !null $this->coverageXml + * @phpstan-assert-if-true !null $this->coverageXml */ public function hasCoverageXml(): bool { @@ -636,7 +708,7 @@ public function coverageXml(): string } /** - * @psalm-assert-if-true !null $this->pathCoverage + * @phpstan-assert-if-true !null $this->pathCoverage */ public function hasPathCoverage(): bool { @@ -661,7 +733,7 @@ public function warmCoverageCache(): bool } /** - * @psalm-assert-if-true !null $this->defaultTimeLimit + * @phpstan-assert-if-true !null $this->defaultTimeLimit */ public function hasDefaultTimeLimit(): bool { @@ -681,7 +753,7 @@ public function defaultTimeLimit(): int } /** - * @psalm-assert-if-true !null $this->disableCodeCoverageIgnore + * @phpstan-assert-if-true !null $this->disableCodeCoverageIgnore */ public function hasDisableCodeCoverageIgnore(): bool { @@ -701,7 +773,7 @@ public function disableCodeCoverageIgnore(): bool } /** - * @psalm-assert-if-true !null $this->disallowTestOutput + * @phpstan-assert-if-true !null $this->disallowTestOutput */ public function hasDisallowTestOutput(): bool { @@ -721,7 +793,7 @@ public function disallowTestOutput(): bool } /** - * @psalm-assert-if-true !null $this->enforceTimeLimit + * @phpstan-assert-if-true !null $this->enforceTimeLimit */ public function hasEnforceTimeLimit(): bool { @@ -741,7 +813,7 @@ public function enforceTimeLimit(): bool } /** - * @psalm-assert-if-true !null $this->excludeGroups + * @phpstan-assert-if-true !null $this->excludeGroups */ public function hasExcludeGroups(): bool { @@ -750,6 +822,8 @@ public function hasExcludeGroups(): bool /** * @throws Exception + * + * @return non-empty-list */ public function excludeGroups(): array { @@ -761,7 +835,7 @@ public function excludeGroups(): array } /** - * @psalm-assert-if-true !null $this->executionOrder + * @phpstan-assert-if-true !null $this->executionOrder */ public function hasExecutionOrder(): bool { @@ -781,7 +855,7 @@ public function executionOrder(): int } /** - * @psalm-assert-if-true !null $this->executionOrderDefects + * @phpstan-assert-if-true !null $this->executionOrderDefects */ public function hasExecutionOrderDefects(): bool { @@ -801,7 +875,27 @@ public function executionOrderDefects(): int } /** - * @psalm-assert-if-true !null $this->failOnDeprecation + * @phpstan-assert-if-true !null $this->failOnAllIssues + */ + public function hasFailOnAllIssues(): bool + { + return $this->failOnAllIssues !== null; + } + + /** + * @throws Exception + */ + public function failOnAllIssues(): bool + { + if (!$this->hasFailOnAllIssues()) { + throw new Exception; + } + + return $this->failOnAllIssues; + } + + /** + * @phpstan-assert-if-true !null $this->failOnDeprecation */ public function hasFailOnDeprecation(): bool { @@ -821,7 +915,47 @@ public function failOnDeprecation(): bool } /** - * @psalm-assert-if-true !null $this->failOnEmptyTestSuite + * @phpstan-assert-if-true !null $this->failOnPhpunitDeprecation + */ + public function hasFailOnPhpunitDeprecation(): bool + { + return $this->failOnPhpunitDeprecation !== null; + } + + /** + * @throws Exception + */ + public function failOnPhpunitDeprecation(): bool + { + if (!$this->hasFailOnPhpunitDeprecation()) { + throw new Exception; + } + + return $this->failOnPhpunitDeprecation; + } + + /** + * @phpstan-assert-if-true !null $this->failOnPhpunitNotice + */ + public function hasFailOnPhpunitNotice(): bool + { + return $this->failOnPhpunitNotice !== null; + } + + /** + * @throws Exception + */ + public function failOnPhpunitNotice(): bool + { + if (!$this->hasFailOnPhpunitNotice()) { + throw new Exception; + } + + return $this->failOnPhpunitNotice; + } + + /** + * @phpstan-assert-if-true !null $this->failOnEmptyTestSuite */ public function hasFailOnEmptyTestSuite(): bool { @@ -841,7 +975,7 @@ public function failOnEmptyTestSuite(): bool } /** - * @psalm-assert-if-true !null $this->failOnIncomplete + * @phpstan-assert-if-true !null $this->failOnIncomplete */ public function hasFailOnIncomplete(): bool { @@ -861,7 +995,7 @@ public function failOnIncomplete(): bool } /** - * @psalm-assert-if-true !null $this->failOnNotice + * @phpstan-assert-if-true !null $this->failOnNotice */ public function hasFailOnNotice(): bool { @@ -881,7 +1015,7 @@ public function failOnNotice(): bool } /** - * @psalm-assert-if-true !null $this->failOnRisky + * @phpstan-assert-if-true !null $this->failOnRisky */ public function hasFailOnRisky(): bool { @@ -901,7 +1035,7 @@ public function failOnRisky(): bool } /** - * @psalm-assert-if-true !null $this->failOnSkipped + * @phpstan-assert-if-true !null $this->failOnSkipped */ public function hasFailOnSkipped(): bool { @@ -921,7 +1055,7 @@ public function failOnSkipped(): bool } /** - * @psalm-assert-if-true !null $this->failOnWarning + * @phpstan-assert-if-true !null $this->failOnWarning */ public function hasFailOnWarning(): bool { @@ -941,7 +1075,7 @@ public function failOnWarning(): bool } /** - * @psalm-assert-if-true !null $this->stopOnDefect + * @phpstan-assert-if-true !null $this->stopOnDefect */ public function hasStopOnDefect(): bool { @@ -961,7 +1095,7 @@ public function stopOnDefect(): bool } /** - * @psalm-assert-if-true !null $this->stopOnDeprecation + * @phpstan-assert-if-true !null $this->stopOnDeprecation */ public function hasStopOnDeprecation(): bool { @@ -981,7 +1115,27 @@ public function stopOnDeprecation(): bool } /** - * @psalm-assert-if-true !null $this->stopOnError + * @phpstan-assert-if-true !null $this->specificDeprecationToStopOn + */ + public function hasSpecificDeprecationToStopOn(): bool + { + return $this->specificDeprecationToStopOn !== null; + } + + /** + * @throws Exception + */ + public function specificDeprecationToStopOn(): string + { + if (!$this->hasSpecificDeprecationToStopOn()) { + throw new Exception; + } + + return $this->specificDeprecationToStopOn; + } + + /** + * @phpstan-assert-if-true !null $this->stopOnError */ public function hasStopOnError(): bool { @@ -1001,7 +1155,7 @@ public function stopOnError(): bool } /** - * @psalm-assert-if-true !null $this->stopOnFailure + * @phpstan-assert-if-true !null $this->stopOnFailure */ public function hasStopOnFailure(): bool { @@ -1021,7 +1175,7 @@ public function stopOnFailure(): bool } /** - * @psalm-assert-if-true !null $this->stopOnIncomplete + * @phpstan-assert-if-true !null $this->stopOnIncomplete */ public function hasStopOnIncomplete(): bool { @@ -1041,7 +1195,7 @@ public function stopOnIncomplete(): bool } /** - * @psalm-assert-if-true !null $this->stopOnNotice + * @phpstan-assert-if-true !null $this->stopOnNotice */ public function hasStopOnNotice(): bool { @@ -1061,7 +1215,7 @@ public function stopOnNotice(): bool } /** - * @psalm-assert-if-true !null $this->stopOnRisky + * @phpstan-assert-if-true !null $this->stopOnRisky */ public function hasStopOnRisky(): bool { @@ -1081,7 +1235,7 @@ public function stopOnRisky(): bool } /** - * @psalm-assert-if-true !null $this->stopOnSkipped + * @phpstan-assert-if-true !null $this->stopOnSkipped */ public function hasStopOnSkipped(): bool { @@ -1101,7 +1255,7 @@ public function stopOnSkipped(): bool } /** - * @psalm-assert-if-true !null $this->stopOnWarning + * @phpstan-assert-if-true !null $this->stopOnWarning */ public function hasStopOnWarning(): bool { @@ -1121,7 +1275,27 @@ public function stopOnWarning(): bool } /** - * @psalm-assert-if-true !null $this->filter + * @phpstan-assert-if-true !null $this->excludeFilter + */ + public function hasExcludeFilter(): bool + { + return $this->excludeFilter !== null; + } + + /** + * @throws Exception + */ + public function excludeFilter(): string + { + if (!$this->hasExcludeFilter()) { + throw new Exception; + } + + return $this->excludeFilter; + } + + /** + * @phpstan-assert-if-true !null $this->filter */ public function hasFilter(): bool { @@ -1141,7 +1315,7 @@ public function filter(): string } /** - * @psalm-assert-if-true !null $this->generateBaseline + * @phpstan-assert-if-true !null $this->generateBaseline */ public function hasGenerateBaseline(): bool { @@ -1161,7 +1335,7 @@ public function generateBaseline(): string } /** - * @psalm-assert-if-true !null $this->useBaseline + * @phpstan-assert-if-true !null $this->useBaseline */ public function hasUseBaseline(): bool { @@ -1196,7 +1370,7 @@ public function migrateConfiguration(): bool } /** - * @psalm-assert-if-true !null $this->groups + * @phpstan-assert-if-true !null $this->groups */ public function hasGroups(): bool { @@ -1205,6 +1379,8 @@ public function hasGroups(): bool /** * @throws Exception + * + * @return non-empty-list */ public function groups(): array { @@ -1216,7 +1392,7 @@ public function groups(): array } /** - * @psalm-assert-if-true !null $this->testsCovering + * @phpstan-assert-if-true !null $this->testsCovering */ public function hasTestsCovering(): bool { @@ -1225,6 +1401,8 @@ public function hasTestsCovering(): bool /** * @throws Exception + * + * @return non-empty-list */ public function testsCovering(): array { @@ -1236,7 +1414,7 @@ public function testsCovering(): array } /** - * @psalm-assert-if-true !null $this->testsUsing + * @phpstan-assert-if-true !null $this->testsUsing */ public function hasTestsUsing(): bool { @@ -1245,6 +1423,8 @@ public function hasTestsUsing(): bool /** * @throws Exception + * + * @return non-empty-list */ public function testsUsing(): array { @@ -1255,13 +1435,35 @@ public function testsUsing(): array return $this->testsUsing; } + /** + * @phpstan-assert-if-true !null $this->testsRequiringPhpExtension + */ + public function hasTestsRequiringPhpExtension(): bool + { + return $this->testsRequiringPhpExtension !== null; + } + + /** + * @throws Exception + * + * @return non-empty-list + */ + public function testsRequiringPhpExtension(): array + { + if (!$this->hasTestsRequiringPhpExtension()) { + throw new Exception; + } + + return $this->testsRequiringPhpExtension; + } + public function help(): bool { return $this->help; } /** - * @psalm-assert-if-true !null $this->includePath + * @phpstan-assert-if-true !null $this->includePath */ public function hasIncludePath(): bool { @@ -1281,7 +1483,7 @@ public function includePath(): string } /** - * @psalm-assert-if-true !null $this->iniSettings + * @phpstan-assert-if-true !null $this->iniSettings */ public function hasIniSettings(): bool { @@ -1290,6 +1492,8 @@ public function hasIniSettings(): bool /** * @throws Exception + * + * @return non-empty-array */ public function iniSettings(): array { @@ -1301,7 +1505,7 @@ public function iniSettings(): array } /** - * @psalm-assert-if-true !null $this->junitLogfile + * @phpstan-assert-if-true !null $this->junitLogfile */ public function hasJunitLogfile(): bool { @@ -1330,13 +1534,18 @@ public function listSuites(): bool return $this->listSuites; } + public function listTestFiles(): bool + { + return $this->listTestFiles; + } + public function listTests(): bool { return $this->listTests; } /** - * @psalm-assert-if-true !null $this->listTestsXml + * @phpstan-assert-if-true !null $this->listTestsXml */ public function hasListTestsXml(): bool { @@ -1356,7 +1565,7 @@ public function listTestsXml(): string } /** - * @psalm-assert-if-true !null $this->noCoverage + * @phpstan-assert-if-true !null $this->noCoverage */ public function hasNoCoverage(): bool { @@ -1376,7 +1585,7 @@ public function noCoverage(): bool } /** - * @psalm-assert-if-true !null $this->noExtensions + * @phpstan-assert-if-true !null $this->noExtensions */ public function hasNoExtensions(): bool { @@ -1396,7 +1605,7 @@ public function noExtensions(): bool } /** - * @psalm-assert-if-true !null $this->noOutput + * @phpstan-assert-if-true !null $this->noOutput */ public function hasNoOutput(): bool { @@ -1416,7 +1625,7 @@ public function noOutput(): bool } /** - * @psalm-assert-if-true !null $this->noProgress + * @phpstan-assert-if-true !null $this->noProgress */ public function hasNoProgress(): bool { @@ -1436,7 +1645,7 @@ public function noProgress(): bool } /** - * @psalm-assert-if-true !null $this->noResults + * @phpstan-assert-if-true !null $this->noResults */ public function hasNoResults(): bool { @@ -1456,7 +1665,7 @@ public function noResults(): bool } /** - * @psalm-assert-if-true !null $this->noLogging + * @phpstan-assert-if-true !null $this->noLogging */ public function hasNoLogging(): bool { @@ -1476,7 +1685,7 @@ public function noLogging(): bool } /** - * @psalm-assert-if-true !null $this->processIsolation + * @phpstan-assert-if-true !null $this->processIsolation */ public function hasProcessIsolation(): bool { @@ -1496,7 +1705,7 @@ public function processIsolation(): bool } /** - * @psalm-assert-if-true !null $this->randomOrderSeed + * @phpstan-assert-if-true !null $this->randomOrderSeed */ public function hasRandomOrderSeed(): bool { @@ -1516,7 +1725,7 @@ public function randomOrderSeed(): int } /** - * @psalm-assert-if-true !null $this->reportUselessTests + * @phpstan-assert-if-true !null $this->reportUselessTests */ public function hasReportUselessTests(): bool { @@ -1536,7 +1745,7 @@ public function reportUselessTests(): bool } /** - * @psalm-assert-if-true !null $this->resolveDependencies + * @phpstan-assert-if-true !null $this->resolveDependencies */ public function hasResolveDependencies(): bool { @@ -1556,7 +1765,7 @@ public function resolveDependencies(): bool } /** - * @psalm-assert-if-true !null $this->reverseList + * @phpstan-assert-if-true !null $this->reverseList */ public function hasReverseList(): bool { @@ -1576,7 +1785,7 @@ public function reverseList(): bool } /** - * @psalm-assert-if-true !null $this->stderr + * @phpstan-assert-if-true !null $this->stderr */ public function hasStderr(): bool { @@ -1596,7 +1805,7 @@ public function stderr(): bool } /** - * @psalm-assert-if-true !null $this->strictCoverage + * @phpstan-assert-if-true !null $this->strictCoverage */ public function hasStrictCoverage(): bool { @@ -1616,7 +1825,7 @@ public function strictCoverage(): bool } /** - * @psalm-assert-if-true !null $this->teamcityLogfile + * @phpstan-assert-if-true !null $this->teamcityLogfile */ public function hasTeamcityLogfile(): bool { @@ -1636,7 +1845,7 @@ public function teamcityLogfile(): string } /** - * @psalm-assert-if-true !null $this->teamcityPrinter + * @phpstan-assert-if-true !null $this->teamCityPrinter */ public function hasTeamCityPrinter(): bool { @@ -1656,7 +1865,7 @@ public function teamCityPrinter(): bool } /** - * @psalm-assert-if-true !null $this->testdoxHtmlFile + * @phpstan-assert-if-true !null $this->testdoxHtmlFile */ public function hasTestdoxHtmlFile(): bool { @@ -1676,7 +1885,7 @@ public function testdoxHtmlFile(): string } /** - * @psalm-assert-if-true !null $this->testdoxTextFile + * @phpstan-assert-if-true !null $this->testdoxTextFile */ public function hasTestdoxTextFile(): bool { @@ -1696,7 +1905,7 @@ public function testdoxTextFile(): string } /** - * @psalm-assert-if-true !null $this->testdoxPrinter + * @phpstan-assert-if-true !null $this->testdoxPrinter */ public function hasTestDoxPrinter(): bool { @@ -1708,7 +1917,7 @@ public function hasTestDoxPrinter(): bool */ public function testdoxPrinter(): bool { - if (!$this->hasTestdoxPrinter()) { + if (!$this->hasTestDoxPrinter()) { throw new Exception; } @@ -1716,7 +1925,27 @@ public function testdoxPrinter(): bool } /** - * @psalm-assert-if-true !null $this->testSuffixes + * @phpstan-assert-if-true !null $this->testdoxPrinterSummary + */ + public function hasTestDoxPrinterSummary(): bool + { + return $this->testdoxPrinterSummary !== null; + } + + /** + * @throws Exception + */ + public function testdoxPrinterSummary(): bool + { + if (!$this->hasTestDoxPrinterSummary()) { + throw new Exception; + } + + return $this->testdoxPrinterSummary; + } + + /** + * @phpstan-assert-if-true !null $this->testSuffixes */ public function hasTestSuffixes(): bool { @@ -1724,9 +1953,9 @@ public function hasTestSuffixes(): bool } /** - * @psalm-return non-empty-list - * * @throws Exception + * + * @return non-empty-list */ public function testSuffixes(): array { @@ -1738,7 +1967,7 @@ public function testSuffixes(): array } /** - * @psalm-assert-if-true !null $this->testSuite + * @phpstan-assert-if-true !null $this->testSuite */ public function hasTestSuite(): bool { @@ -1758,7 +1987,7 @@ public function testSuite(): string } /** - * @psalm-assert-if-true !null $this->excludedTestSuite + * @phpstan-assert-if-true !null $this->excludeTestSuite */ public function hasExcludedTestSuite(): bool { @@ -1783,7 +2012,27 @@ public function useDefaultConfiguration(): bool } /** - * @psalm-assert-if-true !null $this->displayDetailsOnIncompleteTests + * @phpstan-assert-if-true !null $this->displayDetailsOnAllIssues + */ + public function hasDisplayDetailsOnAllIssues(): bool + { + return $this->displayDetailsOnAllIssues !== null; + } + + /** + * @throws Exception + */ + public function displayDetailsOnAllIssues(): bool + { + if (!$this->hasDisplayDetailsOnAllIssues()) { + throw new Exception; + } + + return $this->displayDetailsOnAllIssues; + } + + /** + * @phpstan-assert-if-true !null $this->displayDetailsOnIncompleteTests */ public function hasDisplayDetailsOnIncompleteTests(): bool { @@ -1803,7 +2052,7 @@ public function displayDetailsOnIncompleteTests(): bool } /** - * @psalm-assert-if-true !null $this->displayDetailsOnSkippedTests + * @phpstan-assert-if-true !null $this->displayDetailsOnSkippedTests */ public function hasDisplayDetailsOnSkippedTests(): bool { @@ -1823,7 +2072,7 @@ public function displayDetailsOnSkippedTests(): bool } /** - * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerDeprecations + * @phpstan-assert-if-true !null $this->displayDetailsOnTestsThatTriggerDeprecations */ public function hasDisplayDetailsOnTestsThatTriggerDeprecations(): bool { @@ -1843,7 +2092,47 @@ public function displayDetailsOnTestsThatTriggerDeprecations(): bool } /** - * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerErrors + * @phpstan-assert-if-true !null $this->displayDetailsOnPhpunitDeprecations + */ + public function hasDisplayDetailsOnPhpunitDeprecations(): bool + { + return $this->displayDetailsOnPhpunitDeprecations !== null; + } + + /** + * @throws Exception + */ + public function displayDetailsOnPhpunitDeprecations(): bool + { + if (!$this->hasDisplayDetailsOnPhpunitDeprecations()) { + throw new Exception; + } + + return $this->displayDetailsOnPhpunitDeprecations; + } + + /** + * @phpstan-assert-if-true !null $this->displayDetailsOnPhpunitNotices + */ + public function hasDisplayDetailsOnPhpunitNotices(): bool + { + return $this->displayDetailsOnPhpunitNotices !== null; + } + + /** + * @throws Exception + */ + public function displayDetailsOnPhpunitNotices(): bool + { + if (!$this->hasDisplayDetailsOnPhpunitNotices()) { + throw new Exception; + } + + return $this->displayDetailsOnPhpunitNotices; + } + + /** + * @phpstan-assert-if-true !null $this->displayDetailsOnTestsThatTriggerErrors */ public function hasDisplayDetailsOnTestsThatTriggerErrors(): bool { @@ -1863,7 +2152,7 @@ public function displayDetailsOnTestsThatTriggerErrors(): bool } /** - * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerNotices + * @phpstan-assert-if-true !null $this->displayDetailsOnTestsThatTriggerNotices */ public function hasDisplayDetailsOnTestsThatTriggerNotices(): bool { @@ -1883,7 +2172,7 @@ public function displayDetailsOnTestsThatTriggerNotices(): bool } /** - * @psalm-assert-if-true !null $this->displayDetailsOnTestsThatTriggerWarnings + * @phpstan-assert-if-true !null $this->displayDetailsOnTestsThatTriggerWarnings */ public function hasDisplayDetailsOnTestsThatTriggerWarnings(): bool { @@ -1908,7 +2197,7 @@ public function version(): bool } /** - * @psalm-assert-if-true !null $this->logEventsText + * @phpstan-assert-if-true !null $this->logEventsText */ public function hasLogEventsText(): bool { @@ -1928,7 +2217,7 @@ public function logEventsText(): string } /** - * @psalm-assert-if-true !null $this->logEventsVerboseText + * @phpstan-assert-if-true !null $this->logEventsVerboseText */ public function hasLogEventsVerboseText(): bool { @@ -1946,4 +2235,36 @@ public function logEventsVerboseText(): string return $this->logEventsVerboseText; } + + public function debug(): bool + { + return $this->debug; + } + + public function withTelemetry(): bool + { + return $this->withTelemetry; + } + + /** + * @phpstan-assert-if-true !null $this->extensions + */ + public function hasExtensions(): bool + { + return $this->extensions !== null; + } + + /** + * @throws Exception + * + * @return non-empty-list + */ + public function extensions(): array + { + if (!$this->hasExtensions()) { + throw new Exception; + } + + return $this->extensions; + } } diff --git a/src/TextUI/Configuration/Cli/Exception.php b/src/TextUI/Configuration/Cli/Exception.php index dd5536eaa50..0d9a5a00e6e 100644 --- a/src/TextUI/Configuration/Cli/Exception.php +++ b/src/TextUI/Configuration/Cli/Exception.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Exception extends RuntimeException implements \PHPUnit\Exception diff --git a/src/TextUI/Configuration/Cli/XmlConfigurationFileFinder.php b/src/TextUI/Configuration/Cli/XmlConfigurationFileFinder.php index 55a89c1e185..5357ef63d0a 100644 --- a/src/TextUI/Configuration/Cli/XmlConfigurationFileFinder.php +++ b/src/TextUI/Configuration/Cli/XmlConfigurationFileFinder.php @@ -15,9 +15,11 @@ use function realpath; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class XmlConfigurationFileFinder +final readonly class XmlConfigurationFileFinder { public function find(Configuration $configuration): false|string { @@ -27,7 +29,7 @@ public function find(Configuration $configuration): false|string if (is_dir($configuration->configurationFile())) { $candidate = $this->configurationFileInDirectory($configuration->configurationFile()); - if ($candidate) { + if ($candidate !== false) { return $candidate; } @@ -38,10 +40,14 @@ public function find(Configuration $configuration): false|string } if ($useDefaultConfiguration) { - $candidate = $this->configurationFileInDirectory(getcwd()); + $directory = getcwd(); + + if ($directory !== false) { + $candidate = $this->configurationFileInDirectory($directory); - if ($candidate) { - return $candidate; + if ($candidate !== false) { + return $candidate; + } } } diff --git a/src/TextUI/Configuration/CodeCoverageFilterRegistry.php b/src/TextUI/Configuration/CodeCoverageFilterRegistry.php index 3ea7bc96bf7..7051d2840d8 100644 --- a/src/TextUI/Configuration/CodeCoverageFilterRegistry.php +++ b/src/TextUI/Configuration/CodeCoverageFilterRegistry.php @@ -17,6 +17,8 @@ * CLI options and XML configuration are static within a single PHPUnit process. * It is therefore okay to use a Singleton registry here. * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CodeCoverageFilterRegistry @@ -34,6 +36,9 @@ public static function instance(): self return self::$instance; } + /** + * @codeCoverageIgnore + */ public function get(): Filter { assert($this->filter !== null); @@ -41,6 +46,9 @@ public function get(): Filter return $this->filter; } + /** + * @codeCoverageIgnore + */ public function init(Configuration $configuration, bool $force = false): void { if (!$configuration->hasCoverageReport() && !$force) { @@ -60,6 +68,9 @@ public function init(Configuration $configuration, bool $force = false): void } } + /** + * @codeCoverageIgnore + */ public function configured(): bool { return $this->configured; diff --git a/src/TextUI/Configuration/Configuration.php b/src/TextUI/Configuration/Configuration.php index 72cea86e9e2..0a8fd444a51 100644 --- a/src/TextUI/Configuration/Configuration.php +++ b/src/TextUI/Configuration/Configuration.php @@ -10,257 +10,316 @@ namespace PHPUnit\TextUI\Configuration; /** - * @psalm-immutable + * @immutable * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ -final class Configuration +final readonly class Configuration { - public const COLOR_NEVER = 'never'; - public const COLOR_AUTO = 'auto'; - public const COLOR_ALWAYS = 'always'; - public const COLOR_DEFAULT = self::COLOR_NEVER; - - /** - * @psalm-var list - */ - private readonly array $cliArguments; - private readonly ?string $configurationFile; - private readonly ?string $bootstrap; - private readonly bool $cacheResult; - private readonly ?string $cacheDirectory; - private readonly ?string $coverageCacheDirectory; - private readonly Source $source; - private readonly bool $pathCoverage; - private readonly ?string $coverageClover; - private readonly ?string $coverageCobertura; - private readonly ?string $coverageCrap4j; - private readonly int $coverageCrap4jThreshold; - private readonly ?string $coverageHtml; - private readonly int $coverageHtmlLowUpperBound; - private readonly int $coverageHtmlHighLowerBound; - private readonly string $coverageHtmlColorSuccessLow; - private readonly string $coverageHtmlColorSuccessMedium; - private readonly string $coverageHtmlColorSuccessHigh; - private readonly string $coverageHtmlColorWarning; - private readonly string $coverageHtmlColorDanger; - private readonly ?string $coverageHtmlCustomCssFile; - private readonly ?string $coveragePhp; - private readonly ?string $coverageText; - private readonly bool $coverageTextShowUncoveredFiles; - private readonly bool $coverageTextShowOnlySummary; - private readonly ?string $coverageXml; - private readonly string $testResultCacheFile; - private readonly bool $ignoreDeprecatedCodeUnitsFromCodeCoverage; - private readonly bool $disableCodeCoverageIgnore; - private readonly bool $failOnDeprecation; - private readonly bool $failOnEmptyTestSuite; - private readonly bool $failOnIncomplete; - private readonly bool $failOnNotice; - private readonly bool $failOnRisky; - private readonly bool $failOnSkipped; - private readonly bool $failOnWarning; - private readonly bool $stopOnDefect; - private readonly bool $stopOnDeprecation; - private readonly bool $stopOnError; - private readonly bool $stopOnFailure; - private readonly bool $stopOnIncomplete; - private readonly bool $stopOnNotice; - private readonly bool $stopOnRisky; - private readonly bool $stopOnSkipped; - private readonly bool $stopOnWarning; - private readonly bool $outputToStandardErrorStream; - private readonly int $columns; - private readonly bool $noExtensions; - - /** - * @psalm-var ?non-empty-string - */ - private readonly ?string $pharExtensionDirectory; - - /** - * @psalm-var list}> - */ - private readonly array $extensionBootstrappers; - private readonly bool $backupGlobals; - private readonly bool $backupStaticProperties; - private readonly bool $beStrictAboutChangesToGlobalState; - private readonly bool $colors; - private readonly bool $processIsolation; - private readonly bool $enforceTimeLimit; - private readonly int $defaultTimeLimit; - private readonly int $timeoutForSmallTests; - private readonly int $timeoutForMediumTests; - private readonly int $timeoutForLargeTests; - private readonly bool $reportUselessTests; - private readonly bool $strictCoverage; - private readonly bool $disallowTestOutput; - private readonly bool $displayDetailsOnIncompleteTests; - private readonly bool $displayDetailsOnSkippedTests; - private readonly bool $displayDetailsOnTestsThatTriggerDeprecations; - private readonly bool $displayDetailsOnTestsThatTriggerErrors; - private readonly bool $displayDetailsOnTestsThatTriggerNotices; - private readonly bool $displayDetailsOnTestsThatTriggerWarnings; - private readonly bool $reverseDefectList; - private readonly bool $requireCoverageMetadata; - private readonly bool $registerMockObjectsFromTestArgumentsRecursively; - private readonly bool $noProgress; - private readonly bool $noResults; - private readonly bool $noOutput; - private readonly int $executionOrder; - private readonly int $executionOrderDefects; - private readonly bool $resolveDependencies; - private readonly ?string $logfileTeamcity; - private readonly ?string $logfileJunit; - private readonly ?string $logfileTestdoxHtml; - private readonly ?string $logfileTestdoxText; - private readonly ?string $logEventsText; - private readonly ?string $logEventsVerboseText; - private readonly ?array $testsCovering; - private readonly ?array $testsUsing; - private readonly bool $teamCityOutput; - private readonly bool $testDoxOutput; - private readonly ?string $filter; - private readonly ?array $groups; - private readonly ?array $excludeGroups; - private readonly int $randomOrderSeed; - private readonly bool $includeUncoveredFiles; - private readonly TestSuiteCollection $testSuite; - private readonly string $includeTestSuite; - private readonly string $excludeTestSuite; - private readonly ?string $defaultTestSuite; - - /** - * @psalm-var non-empty-list - */ - private readonly array $testSuffixes; - private readonly Php $php; - private readonly bool $controlGarbageCollector; - private readonly int $numberOfTestsBeforeGarbageCollection; - private readonly ?string $generateBaseline; - - /** - * @psalm-param list $cliArguments - * @psalm-param ?non-empty-string $pharExtensionDirectory - * @psalm-param non-empty-list $testSuffixes - * @psalm-param list}> $extensionBootstrappers - */ - public function __construct(array $cliArguments, ?string $configurationFile, ?string $bootstrap, bool $cacheResult, ?string $cacheDirectory, ?string $coverageCacheDirectory, Source $source, string $testResultCacheFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4j, int $coverageCrap4jThreshold, ?string $coverageHtml, int $coverageHtmlLowUpperBound, int $coverageHtmlHighLowerBound, string $coverageHtmlColorSuccessLow, string $coverageHtmlColorSuccessMedium, string $coverageHtmlColorSuccessHigh, string $coverageHtmlColorWarning, string $coverageHtmlColorDanger, ?string $coverageHtmlCustomCssFile, ?string $coveragePhp, ?string $coverageText, bool $coverageTextShowUncoveredFiles, bool $coverageTextShowOnlySummary, ?string $coverageXml, bool $pathCoverage, bool $ignoreDeprecatedCodeUnitsFromCodeCoverage, bool $disableCodeCoverageIgnore, bool $failOnDeprecation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, bool $outputToStandardErrorStream, int|string $columns, bool $noExtensions, ?string $pharExtensionDirectory, array $extensionBootstrappers, bool $backupGlobals, bool $backupStaticProperties, bool $beStrictAboutChangesToGlobalState, bool $colors, bool $processIsolation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, bool $reportUselessTests, bool $strictCoverage, bool $disallowTestOutput, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, bool $registerMockObjectsFromTestArgumentsRecursively, bool $noProgress, bool $noResults, bool $noOutput, int $executionOrder, int $executionOrderDefects, bool $resolveDependencies, ?string $logfileTeamcity, ?string $logfileJunit, ?string $logfileTestdoxHtml, ?string $logfileTestdoxText, ?string $logEventsText, ?string $logEventsVerboseText, bool $teamCityOutput, bool $testDoxOutput, ?array $testsCovering, ?array $testsUsing, ?string $filter, ?array $groups, ?array $excludeGroups, int $randomOrderSeed, bool $includeUncoveredFiles, TestSuiteCollection $testSuite, string $includeTestSuite, string $excludeTestSuite, ?string $defaultTestSuite, array $testSuffixes, Php $php, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection, ?string $generateBaseline) - { - $this->cliArguments = $cliArguments; - $this->configurationFile = $configurationFile; - $this->bootstrap = $bootstrap; - $this->cacheResult = $cacheResult; - $this->cacheDirectory = $cacheDirectory; - $this->coverageCacheDirectory = $coverageCacheDirectory; - $this->source = $source; - $this->testResultCacheFile = $testResultCacheFile; - $this->coverageClover = $coverageClover; - $this->coverageCobertura = $coverageCobertura; - $this->coverageCrap4j = $coverageCrap4j; - $this->coverageCrap4jThreshold = $coverageCrap4jThreshold; - $this->coverageHtml = $coverageHtml; - $this->coverageHtmlLowUpperBound = $coverageHtmlLowUpperBound; - $this->coverageHtmlHighLowerBound = $coverageHtmlHighLowerBound; - $this->coverageHtmlColorSuccessLow = $coverageHtmlColorSuccessLow; - $this->coverageHtmlColorSuccessMedium = $coverageHtmlColorSuccessMedium; - $this->coverageHtmlColorSuccessHigh = $coverageHtmlColorSuccessHigh; - $this->coverageHtmlColorWarning = $coverageHtmlColorWarning; - $this->coverageHtmlColorDanger = $coverageHtmlColorDanger; - $this->coverageHtmlCustomCssFile = $coverageHtmlCustomCssFile; - $this->coveragePhp = $coveragePhp; - $this->coverageText = $coverageText; - $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; - $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; - $this->coverageXml = $coverageXml; - $this->pathCoverage = $pathCoverage; - $this->ignoreDeprecatedCodeUnitsFromCodeCoverage = $ignoreDeprecatedCodeUnitsFromCodeCoverage; - $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; - $this->failOnDeprecation = $failOnDeprecation; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnNotice = $failOnNotice; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnDeprecation = $stopOnDeprecation; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnNotice = $stopOnNotice; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->stopOnWarning = $stopOnWarning; - $this->outputToStandardErrorStream = $outputToStandardErrorStream; - $this->columns = $columns; - $this->noExtensions = $noExtensions; - $this->pharExtensionDirectory = $pharExtensionDirectory; - $this->extensionBootstrappers = $extensionBootstrappers; - $this->backupGlobals = $backupGlobals; - $this->backupStaticProperties = $backupStaticProperties; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->colors = $colors; - $this->processIsolation = $processIsolation; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->timeoutForSmallTests = $timeoutForSmallTests; - $this->timeoutForMediumTests = $timeoutForMediumTests; - $this->timeoutForLargeTests = $timeoutForLargeTests; - $this->reportUselessTests = $reportUselessTests; - $this->strictCoverage = $strictCoverage; - $this->disallowTestOutput = $disallowTestOutput; - $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; - $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; - $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; - $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; - $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; - $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; - $this->reverseDefectList = $reverseDefectList; - $this->requireCoverageMetadata = $requireCoverageMetadata; - $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; - $this->noProgress = $noProgress; - $this->noResults = $noResults; - $this->noOutput = $noOutput; - $this->executionOrder = $executionOrder; - $this->executionOrderDefects = $executionOrderDefects; - $this->resolveDependencies = $resolveDependencies; - $this->logfileTeamcity = $logfileTeamcity; - $this->logfileJunit = $logfileJunit; - $this->logfileTestdoxHtml = $logfileTestdoxHtml; - $this->logfileTestdoxText = $logfileTestdoxText; - $this->logEventsText = $logEventsText; - $this->logEventsVerboseText = $logEventsVerboseText; - $this->teamCityOutput = $teamCityOutput; - $this->testDoxOutput = $testDoxOutput; - $this->testsCovering = $testsCovering; - $this->testsUsing = $testsUsing; - $this->filter = $filter; - $this->groups = $groups; - $this->excludeGroups = $excludeGroups; - $this->randomOrderSeed = $randomOrderSeed; - $this->includeUncoveredFiles = $includeUncoveredFiles; - $this->testSuite = $testSuite; - $this->includeTestSuite = $includeTestSuite; - $this->excludeTestSuite = $excludeTestSuite; - $this->defaultTestSuite = $defaultTestSuite; - $this->testSuffixes = $testSuffixes; - $this->php = $php; - $this->controlGarbageCollector = $controlGarbageCollector; - $this->numberOfTestsBeforeGarbageCollection = $numberOfTestsBeforeGarbageCollection; - $this->generateBaseline = $generateBaseline; - } - - /** - * @psalm-assert-if-true !empty $this->cliArguments + public const string COLOR_NEVER = 'never'; + public const string COLOR_AUTO = 'auto'; + public const string COLOR_ALWAYS = 'always'; + public const string COLOR_DEFAULT = self::COLOR_NEVER; + + /** + * @var list + */ + private array $cliArguments; + private ?string $configurationFile; + private ?string $bootstrap; + private bool $cacheResult; + private ?string $cacheDirectory; + private ?string $coverageCacheDirectory; + private Source $source; + private bool $pathCoverage; + private ?string $coverageClover; + private ?string $coverageCobertura; + private ?string $coverageCrap4j; + private int $coverageCrap4jThreshold; + private ?string $coverageHtml; + private int $coverageHtmlLowUpperBound; + private int $coverageHtmlHighLowerBound; + private string $coverageHtmlColorSuccessLow; + private string $coverageHtmlColorSuccessMedium; + private string $coverageHtmlColorSuccessHigh; + private string $coverageHtmlColorWarning; + private string $coverageHtmlColorDanger; + private ?string $coverageHtmlCustomCssFile; + private ?string $coveragePhp; + private ?string $coverageText; + private bool $coverageTextShowUncoveredFiles; + private bool $coverageTextShowOnlySummary; + private ?string $coverageXml; + private string $testResultCacheFile; + private bool $ignoreDeprecatedCodeUnitsFromCodeCoverage; + private bool $disableCodeCoverageIgnore; + private bool $failOnAllIssues; + private bool $failOnDeprecation; + private bool $failOnPhpunitDeprecation; + private bool $failOnPhpunitNotice; + private bool $failOnEmptyTestSuite; + private bool $failOnIncomplete; + private bool $failOnNotice; + private bool $failOnRisky; + private bool $failOnSkipped; + private bool $failOnWarning; + private bool $stopOnDefect; + private bool $stopOnDeprecation; + private ?string $specificDeprecationToStopOn; + private bool $stopOnError; + private bool $stopOnFailure; + private bool $stopOnIncomplete; + private bool $stopOnNotice; + private bool $stopOnRisky; + private bool $stopOnSkipped; + private bool $stopOnWarning; + private bool $outputToStandardErrorStream; + private int $columns; + private bool $noExtensions; + + /** + * @var ?non-empty-string + */ + private ?string $pharExtensionDirectory; + + /** + * @var list}> + */ + private array $extensionBootstrappers; + private bool $backupGlobals; + private bool $backupStaticProperties; + private bool $beStrictAboutChangesToGlobalState; + private bool $colors; + private bool $processIsolation; + private bool $enforceTimeLimit; + private int $defaultTimeLimit; + private int $timeoutForSmallTests; + private int $timeoutForMediumTests; + private int $timeoutForLargeTests; + private bool $reportUselessTests; + private bool $strictCoverage; + private bool $disallowTestOutput; + private bool $displayDetailsOnAllIssues; + private bool $displayDetailsOnIncompleteTests; + private bool $displayDetailsOnSkippedTests; + private bool $displayDetailsOnTestsThatTriggerDeprecations; + private bool $displayDetailsOnPhpunitDeprecations; + private bool $displayDetailsOnPhpunitNotices; + private bool $displayDetailsOnTestsThatTriggerErrors; + private bool $displayDetailsOnTestsThatTriggerNotices; + private bool $displayDetailsOnTestsThatTriggerWarnings; + private bool $reverseDefectList; + private bool $requireCoverageMetadata; + private bool $noProgress; + private bool $noResults; + private bool $noOutput; + private int $executionOrder; + private int $executionOrderDefects; + private bool $resolveDependencies; + private ?string $logfileTeamcity; + private ?string $logfileJunit; + private ?string $logfileTestdoxHtml; + private ?string $logfileTestdoxText; + private ?string $logEventsText; + private ?string $logEventsVerboseText; + + /** + * @var ?non-empty-list + */ + private ?array $testsCovering; + + /** + * @var ?non-empty-list + */ + private ?array $testsUsing; + + /** + * @var ?non-empty-list + */ + private ?array $testsRequiringPhpExtension; + private bool $teamCityOutput; + private bool $testDoxOutput; + private bool $testDoxOutputSummary; + private ?string $filter; + private ?string $excludeFilter; + + /** + * @var list + */ + private array $groups; + + /** + * @var list + */ + private array $excludeGroups; + private int $randomOrderSeed; + private bool $includeUncoveredFiles; + private TestSuiteCollection $testSuite; + private string $includeTestSuite; + private string $excludeTestSuite; + private ?string $defaultTestSuite; + + /** + * @var non-empty-list + */ + private array $testSuffixes; + private Php $php; + private bool $controlGarbageCollector; + private int $numberOfTestsBeforeGarbageCollection; + + /** + * @var null|non-empty-string + */ + private ?string $generateBaseline; + private bool $debug; + private bool $withTelemetry; + + /** + * @var non-negative-int + */ + private int $shortenArraysForExportThreshold; + + /** + * @param list $cliArguments + * @param ?non-empty-string $pharExtensionDirectory + * @param list}> $extensionBootstrappers + * @param ?non-empty-list $testsCovering + * @param ?non-empty-list $testsUsing + * @param ?non-empty-list $testsRequiringPhpExtension + * @param list $groups + * @param list $excludeGroups + * @param non-empty-list $testSuffixes + * @param null|non-empty-string $generateBaseline + * @param non-negative-int $shortenArraysForExportThreshold + */ + public function __construct(array $cliArguments, ?string $configurationFile, ?string $bootstrap, bool $cacheResult, ?string $cacheDirectory, ?string $coverageCacheDirectory, Source $source, string $testResultCacheFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4j, int $coverageCrap4jThreshold, ?string $coverageHtml, int $coverageHtmlLowUpperBound, int $coverageHtmlHighLowerBound, string $coverageHtmlColorSuccessLow, string $coverageHtmlColorSuccessMedium, string $coverageHtmlColorSuccessHigh, string $coverageHtmlColorWarning, string $coverageHtmlColorDanger, ?string $coverageHtmlCustomCssFile, ?string $coveragePhp, ?string $coverageText, bool $coverageTextShowUncoveredFiles, bool $coverageTextShowOnlySummary, ?string $coverageXml, bool $pathCoverage, bool $ignoreDeprecatedCodeUnitsFromCodeCoverage, bool $disableCodeCoverageIgnore, bool $failOnAllIssues, bool $failOnDeprecation, bool $failOnPhpunitDeprecation, bool $failOnPhpunitNotice, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, ?string $specificDeprecationToStopOn, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, bool $outputToStandardErrorStream, int $columns, bool $noExtensions, ?string $pharExtensionDirectory, array $extensionBootstrappers, bool $backupGlobals, bool $backupStaticProperties, bool $beStrictAboutChangesToGlobalState, bool $colors, bool $processIsolation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, bool $reportUselessTests, bool $strictCoverage, bool $disallowTestOutput, bool $displayDetailsOnAllIssues, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnPhpunitDeprecations, bool $displayDetailsOnPhpunitNotices, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, bool $noProgress, bool $noResults, bool $noOutput, int $executionOrder, int $executionOrderDefects, bool $resolveDependencies, ?string $logfileTeamcity, ?string $logfileJunit, ?string $logfileTestdoxHtml, ?string $logfileTestdoxText, ?string $logEventsText, ?string $logEventsVerboseText, bool $teamCityOutput, bool $testDoxOutput, bool $testDoxOutputSummary, ?array $testsCovering, ?array $testsUsing, ?array $testsRequiringPhpExtension, ?string $filter, ?string $excludeFilter, array $groups, array $excludeGroups, int $randomOrderSeed, bool $includeUncoveredFiles, TestSuiteCollection $testSuite, string $includeTestSuite, string $excludeTestSuite, ?string $defaultTestSuite, array $testSuffixes, Php $php, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection, ?string $generateBaseline, bool $debug, bool $withTelemetry, int $shortenArraysForExportThreshold) + { + $this->cliArguments = $cliArguments; + $this->configurationFile = $configurationFile; + $this->bootstrap = $bootstrap; + $this->cacheResult = $cacheResult; + $this->cacheDirectory = $cacheDirectory; + $this->coverageCacheDirectory = $coverageCacheDirectory; + $this->source = $source; + $this->testResultCacheFile = $testResultCacheFile; + $this->coverageClover = $coverageClover; + $this->coverageCobertura = $coverageCobertura; + $this->coverageCrap4j = $coverageCrap4j; + $this->coverageCrap4jThreshold = $coverageCrap4jThreshold; + $this->coverageHtml = $coverageHtml; + $this->coverageHtmlLowUpperBound = $coverageHtmlLowUpperBound; + $this->coverageHtmlHighLowerBound = $coverageHtmlHighLowerBound; + $this->coverageHtmlColorSuccessLow = $coverageHtmlColorSuccessLow; + $this->coverageHtmlColorSuccessMedium = $coverageHtmlColorSuccessMedium; + $this->coverageHtmlColorSuccessHigh = $coverageHtmlColorSuccessHigh; + $this->coverageHtmlColorWarning = $coverageHtmlColorWarning; + $this->coverageHtmlColorDanger = $coverageHtmlColorDanger; + $this->coverageHtmlCustomCssFile = $coverageHtmlCustomCssFile; + $this->coveragePhp = $coveragePhp; + $this->coverageText = $coverageText; + $this->coverageTextShowUncoveredFiles = $coverageTextShowUncoveredFiles; + $this->coverageTextShowOnlySummary = $coverageTextShowOnlySummary; + $this->coverageXml = $coverageXml; + $this->pathCoverage = $pathCoverage; + $this->ignoreDeprecatedCodeUnitsFromCodeCoverage = $ignoreDeprecatedCodeUnitsFromCodeCoverage; + $this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore; + $this->failOnAllIssues = $failOnAllIssues; + $this->failOnDeprecation = $failOnDeprecation; + $this->failOnPhpunitDeprecation = $failOnPhpunitDeprecation; + $this->failOnPhpunitNotice = $failOnPhpunitNotice; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnNotice = $failOnNotice; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnDeprecation = $stopOnDeprecation; + $this->specificDeprecationToStopOn = $specificDeprecationToStopOn; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnNotice = $stopOnNotice; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->outputToStandardErrorStream = $outputToStandardErrorStream; + $this->columns = $columns; + $this->noExtensions = $noExtensions; + $this->pharExtensionDirectory = $pharExtensionDirectory; + $this->extensionBootstrappers = $extensionBootstrappers; + $this->backupGlobals = $backupGlobals; + $this->backupStaticProperties = $backupStaticProperties; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->colors = $colors; + $this->processIsolation = $processIsolation; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->timeoutForSmallTests = $timeoutForSmallTests; + $this->timeoutForMediumTests = $timeoutForMediumTests; + $this->timeoutForLargeTests = $timeoutForLargeTests; + $this->reportUselessTests = $reportUselessTests; + $this->strictCoverage = $strictCoverage; + $this->disallowTestOutput = $disallowTestOutput; + $this->displayDetailsOnAllIssues = $displayDetailsOnAllIssues; + $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; + $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; + $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnPhpunitDeprecations = $displayDetailsOnPhpunitDeprecations; + $this->displayDetailsOnPhpunitNotices = $displayDetailsOnPhpunitNotices; + $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; + $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; + $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; + $this->reverseDefectList = $reverseDefectList; + $this->requireCoverageMetadata = $requireCoverageMetadata; + $this->noProgress = $noProgress; + $this->noResults = $noResults; + $this->noOutput = $noOutput; + $this->executionOrder = $executionOrder; + $this->executionOrderDefects = $executionOrderDefects; + $this->resolveDependencies = $resolveDependencies; + $this->logfileTeamcity = $logfileTeamcity; + $this->logfileJunit = $logfileJunit; + $this->logfileTestdoxHtml = $logfileTestdoxHtml; + $this->logfileTestdoxText = $logfileTestdoxText; + $this->logEventsText = $logEventsText; + $this->logEventsVerboseText = $logEventsVerboseText; + $this->teamCityOutput = $teamCityOutput; + $this->testDoxOutput = $testDoxOutput; + $this->testDoxOutputSummary = $testDoxOutputSummary; + $this->testsCovering = $testsCovering; + $this->testsUsing = $testsUsing; + $this->testsRequiringPhpExtension = $testsRequiringPhpExtension; + $this->filter = $filter; + $this->excludeFilter = $excludeFilter; + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + $this->randomOrderSeed = $randomOrderSeed; + $this->includeUncoveredFiles = $includeUncoveredFiles; + $this->testSuite = $testSuite; + $this->includeTestSuite = $includeTestSuite; + $this->excludeTestSuite = $excludeTestSuite; + $this->defaultTestSuite = $defaultTestSuite; + $this->testSuffixes = $testSuffixes; + $this->php = $php; + $this->controlGarbageCollector = $controlGarbageCollector; + $this->numberOfTestsBeforeGarbageCollection = $numberOfTestsBeforeGarbageCollection; + $this->generateBaseline = $generateBaseline; + $this->debug = $debug; + $this->withTelemetry = $withTelemetry; + $this->shortenArraysForExportThreshold = $shortenArraysForExportThreshold; + } + + /** + * @phpstan-assert-if-true !empty $this->cliArguments */ public function hasCliArguments(): bool { - return !empty($this->cliArguments); + return $this->cliArguments !== []; } /** - * @psalm-return list + * @return list */ public function cliArguments(): array { @@ -268,33 +327,7 @@ public function cliArguments(): array } /** - * @psalm-assert-if-true !empty $this->cliArguments - * - * @deprecated Use hasCliArguments() instead - */ - public function hasCliArgument(): bool - { - return !empty($this->cliArguments); - } - - /** - * @throws NoCliArgumentException - * - * @return non-empty-string - * - * @deprecated Use cliArguments()[0] instead - */ - public function cliArgument(): string - { - if (!$this->hasCliArguments()) { - throw new NoCliArgumentException; - } - - return $this->cliArguments[0]; - } - - /** - * @psalm-assert-if-true !null $this->configurationFile + * @phpstan-assert-if-true !null $this->configurationFile */ public function hasConfigurationFile(): bool { @@ -314,7 +347,7 @@ public function configurationFile(): string } /** - * @psalm-assert-if-true !null $this->bootstrap + * @phpstan-assert-if-true !null $this->bootstrap */ public function hasBootstrap(): bool { @@ -339,7 +372,7 @@ public function cacheResult(): bool } /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @phpstan-assert-if-true !null $this->cacheDirectory */ public function hasCacheDirectory(): bool { @@ -359,7 +392,7 @@ public function cacheDirectory(): string } /** - * @psalm-assert-if-true !null $this->coverageCacheDirectory + * @phpstan-assert-if-true !null $this->coverageCacheDirectory */ public function hasCoverageCacheDirectory(): bool { @@ -383,70 +416,6 @@ public function source(): Source return $this->source; } - /** - * @deprecated Use source()->restrictDeprecations() instead - */ - public function restrictDeprecations(): bool - { - return $this->source()->restrictDeprecations(); - } - - /** - * @deprecated Use source()->restrictNotices() instead - */ - public function restrictNotices(): bool - { - return $this->source()->restrictNotices(); - } - - /** - * @deprecated Use source()->restrictWarnings() instead - */ - public function restrictWarnings(): bool - { - return $this->source()->restrictWarnings(); - } - - /** - * @deprecated Use source()->notEmpty() instead - */ - public function hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport(): bool - { - return $this->source->notEmpty(); - } - - /** - * @deprecated Use source()->includeDirectories() instead - */ - public function coverageIncludeDirectories(): FilterDirectoryCollection - { - return $this->source()->includeDirectories(); - } - - /** - * @deprecated Use source()->includeFiles() instead - */ - public function coverageIncludeFiles(): FileCollection - { - return $this->source()->includeFiles(); - } - - /** - * @deprecated Use source()->excludeDirectories() instead - */ - public function coverageExcludeDirectories(): FilterDirectoryCollection - { - return $this->source()->excludeDirectories(); - } - - /** - * @deprecated Use source()->excludeFiles() instead - */ - public function coverageExcludeFiles(): FileCollection - { - return $this->source()->excludeFiles(); - } - public function testResultCacheFile(): string { return $this->testResultCacheFile; @@ -470,16 +439,16 @@ public function pathCoverage(): bool public function hasCoverageReport(): bool { return $this->hasCoverageClover() || - $this->hasCoverageCobertura() || - $this->hasCoverageCrap4j() || - $this->hasCoverageHtml() || - $this->hasCoveragePhp() || - $this->hasCoverageText() || - $this->hasCoverageXml(); + $this->hasCoverageCobertura() || + $this->hasCoverageCrap4j() || + $this->hasCoverageHtml() || + $this->hasCoveragePhp() || + $this->hasCoverageText() || + $this->hasCoverageXml(); } /** - * @psalm-assert-if-true !null $this->coverageClover + * @phpstan-assert-if-true !null $this->coverageClover */ public function hasCoverageClover(): bool { @@ -499,7 +468,7 @@ public function coverageClover(): string } /** - * @psalm-assert-if-true !null $this->coverageCobertura + * @phpstan-assert-if-true !null $this->coverageCobertura */ public function hasCoverageCobertura(): bool { @@ -519,7 +488,7 @@ public function coverageCobertura(): string } /** - * @psalm-assert-if-true !null $this->coverageCrap4j + * @phpstan-assert-if-true !null $this->coverageCrap4j */ public function hasCoverageCrap4j(): bool { @@ -544,7 +513,7 @@ public function coverageCrap4jThreshold(): int } /** - * @psalm-assert-if-true !null $this->coverageHtml + * @phpstan-assert-if-true !null $this->coverageHtml */ public function hasCoverageHtml(): bool { @@ -599,7 +568,7 @@ public function coverageHtmlColorDanger(): string } /** - * @psalm-assert-if-true !null $this->coverageHtmlCustomCssFile + * @phpstan-assert-if-true !null $this->coverageHtmlCustomCssFile */ public function hasCoverageHtmlCustomCssFile(): bool { @@ -619,7 +588,7 @@ public function coverageHtmlCustomCssFile(): string } /** - * @psalm-assert-if-true !null $this->coveragePhp + * @phpstan-assert-if-true !null $this->coveragePhp */ public function hasCoveragePhp(): bool { @@ -639,7 +608,7 @@ public function coveragePhp(): string } /** - * @psalm-assert-if-true !null $this->coverageText + * @phpstan-assert-if-true !null $this->coverageText */ public function hasCoverageText(): bool { @@ -669,7 +638,7 @@ public function coverageTextShowOnlySummary(): bool } /** - * @psalm-assert-if-true !null $this->coverageXml + * @phpstan-assert-if-true !null $this->coverageXml */ public function hasCoverageXml(): bool { @@ -688,11 +657,26 @@ public function coverageXml(): string return $this->coverageXml; } + public function failOnAllIssues(): bool + { + return $this->failOnAllIssues; + } + public function failOnDeprecation(): bool { return $this->failOnDeprecation; } + public function failOnPhpunitDeprecation(): bool + { + return $this->failOnPhpunitDeprecation; + } + + public function failOnPhpunitNotice(): bool + { + return $this->failOnPhpunitNotice; + } + public function failOnEmptyTestSuite(): bool { return $this->failOnEmptyTestSuite; @@ -733,6 +717,26 @@ public function stopOnDeprecation(): bool return $this->stopOnDeprecation; } + /** + * @phpstan-assert-if-true !null $this->specificDeprecationToStopOn + */ + public function hasSpecificDeprecationToStopOn(): bool + { + return $this->specificDeprecationToStopOn !== null; + } + + /** + * @throws SpecificDeprecationToStopOnNotConfiguredException + */ + public function specificDeprecationToStopOn(): string + { + if (!$this->hasSpecificDeprecationToStopOn()) { + throw new SpecificDeprecationToStopOnNotConfiguredException; + } + + return $this->specificDeprecationToStopOn; + } + public function stopOnError(): bool { return $this->stopOnError; @@ -778,21 +782,13 @@ public function columns(): int return $this->columns; } - /** - * @deprecated Use noExtensions() instead - */ - public function loadPharExtensions(): bool - { - return $this->noExtensions; - } - public function noExtensions(): bool { return $this->noExtensions; } /** - * @psalm-assert-if-true !null $this->pharExtensionDirectory + * @phpstan-assert-if-true !null $this->pharExtensionDirectory */ public function hasPharExtensionDirectory(): bool { @@ -800,9 +796,9 @@ public function hasPharExtensionDirectory(): bool } /** - * @psalm-return non-empty-string - * * @throws NoPharExtensionDirectoryException + * + * @return non-empty-string */ public function pharExtensionDirectory(): string { @@ -814,7 +810,7 @@ public function pharExtensionDirectory(): string } /** - * @psalm-return list}> + * @return list}> */ public function extensionBootstrappers(): array { @@ -886,6 +882,11 @@ public function disallowTestOutput(): bool return $this->disallowTestOutput; } + public function displayDetailsOnAllIssues(): bool + { + return $this->displayDetailsOnAllIssues; + } + public function displayDetailsOnIncompleteTests(): bool { return $this->displayDetailsOnIncompleteTests; @@ -901,6 +902,16 @@ public function displayDetailsOnTestsThatTriggerDeprecations(): bool return $this->displayDetailsOnTestsThatTriggerDeprecations; } + public function displayDetailsOnPhpunitDeprecations(): bool + { + return $this->displayDetailsOnPhpunitDeprecations; + } + + public function displayDetailsOnPhpunitNotices(): bool + { + return $this->displayDetailsOnPhpunitNotices; + } + public function displayDetailsOnTestsThatTriggerErrors(): bool { return $this->displayDetailsOnTestsThatTriggerErrors; @@ -926,11 +937,6 @@ public function requireCoverageMetadata(): bool return $this->requireCoverageMetadata; } - public function registerMockObjectsFromTestArgumentsRecursively(): bool - { - return $this->registerMockObjectsFromTestArgumentsRecursively; - } - public function noProgress(): bool { return $this->noProgress; @@ -962,7 +968,7 @@ public function resolveDependencies(): bool } /** - * @psalm-assert-if-true !null $this->logfileTeamcity + * @phpstan-assert-if-true !null $this->logfileTeamcity */ public function hasLogfileTeamcity(): bool { @@ -982,7 +988,7 @@ public function logfileTeamcity(): string } /** - * @psalm-assert-if-true !null $this->logfileJunit + * @phpstan-assert-if-true !null $this->logfileJunit */ public function hasLogfileJunit(): bool { @@ -1002,7 +1008,7 @@ public function logfileJunit(): string } /** - * @psalm-assert-if-true !null $this->logfileTestdoxHtml + * @phpstan-assert-if-true !null $this->logfileTestdoxHtml */ public function hasLogfileTestdoxHtml(): bool { @@ -1022,7 +1028,7 @@ public function logfileTestdoxHtml(): string } /** - * @psalm-assert-if-true !null $this->logfileTestdoxText + * @phpstan-assert-if-true !null $this->logfileTestdoxText */ public function hasLogfileTestdoxText(): bool { @@ -1042,7 +1048,7 @@ public function logfileTestdoxText(): string } /** - * @psalm-assert-if-true !null $this->logEventsText + * @phpstan-assert-if-true !null $this->logEventsText */ public function hasLogEventsText(): bool { @@ -1062,7 +1068,7 @@ public function logEventsText(): string } /** - * @psalm-assert-if-true !null $this->logEventsVerboseText + * @phpstan-assert-if-true !null $this->logEventsVerboseText */ public function hasLogEventsVerboseText(): bool { @@ -1091,18 +1097,23 @@ public function outputIsTestDox(): bool return $this->testDoxOutput; } + public function testDoxOutputWithSummary(): bool + { + return $this->testDoxOutputSummary; + } + /** - * @psalm-assert-if-true !empty $this->testsCovering + * @phpstan-assert-if-true !empty $this->testsCovering */ public function hasTestsCovering(): bool { - return !empty($this->testsCovering); + return $this->testsCovering !== null; } /** - * @psalm-return list - * * @throws FilterNotConfiguredException + * + * @return list */ public function testsCovering(): array { @@ -1114,17 +1125,17 @@ public function testsCovering(): array } /** - * @psalm-assert-if-true !empty $this->testsUsing + * @phpstan-assert-if-true !empty $this->testsUsing */ public function hasTestsUsing(): bool { - return !empty($this->testsUsing); + return $this->testsUsing !== null; } /** - * @psalm-return list - * * @throws FilterNotConfiguredException + * + * @return list */ public function testsUsing(): array { @@ -1136,7 +1147,29 @@ public function testsUsing(): array } /** - * @psalm-assert-if-true !null $this->filter + * @phpstan-assert-if-true !empty $this->testsRequiringPhpExtension + */ + public function hasTestsRequiringPhpExtension(): bool + { + return $this->testsRequiringPhpExtension !== null; + } + + /** + * @throws FilterNotConfiguredException + * + * @return non-empty-list + */ + public function testsRequiringPhpExtension(): array + { + if (!$this->hasTestsRequiringPhpExtension()) { + throw new FilterNotConfiguredException; + } + + return $this->testsRequiringPhpExtension; + } + + /** + * @phpstan-assert-if-true !null $this->filter */ public function hasFilter(): bool { @@ -1156,15 +1189,37 @@ public function filter(): string } /** - * @psalm-assert-if-true !empty $this->groups + * @phpstan-assert-if-true !null $this->excludeFilter + */ + public function hasExcludeFilter(): bool + { + return $this->excludeFilter !== null; + } + + /** + * @throws FilterNotConfiguredException + */ + public function excludeFilter(): string + { + if (!$this->hasExcludeFilter()) { + throw new FilterNotConfiguredException; + } + + return $this->excludeFilter; + } + + /** + * @phpstan-assert-if-true !empty $this->groups */ public function hasGroups(): bool { - return !empty($this->groups); + return $this->groups !== []; } /** * @throws FilterNotConfiguredException + * + * @return non-empty-list */ public function groups(): array { @@ -1176,15 +1231,17 @@ public function groups(): array } /** - * @psalm-assert-if-true !empty $this->excludeGroups + * @phpstan-assert-if-true !empty $this->excludeGroups */ public function hasExcludeGroups(): bool { - return !empty($this->excludeGroups); + return $this->excludeGroups !== []; } /** * @throws FilterNotConfiguredException + * + * @return non-empty-list */ public function excludeGroups(): array { @@ -1221,7 +1278,7 @@ public function excludeTestSuite(): string } /** - * @psalm-assert-if-true !null $this->defaultTestSuite + * @phpstan-assert-if-true !null $this->defaultTestSuite */ public function hasDefaultTestSuite(): bool { @@ -1241,7 +1298,7 @@ public function defaultTestSuite(): string } /** - * @psalm-return non-empty-list + * @return non-empty-list */ public function testSuffixes(): array { @@ -1264,7 +1321,7 @@ public function numberOfTestsBeforeGarbageCollection(): int } /** - * @psalm-assert-if-true !null $this->generateBaseline + * @phpstan-assert-if-true !null $this->generateBaseline */ public function hasGenerateBaseline(): bool { @@ -1273,6 +1330,8 @@ public function hasGenerateBaseline(): bool /** * @throws NoBaselineException + * + * @return non-empty-string */ public function generateBaseline(): string { @@ -1282,4 +1341,22 @@ public function generateBaseline(): string return $this->generateBaseline; } + + public function debug(): bool + { + return $this->debug; + } + + public function withTelemetry(): bool + { + return $this->withTelemetry; + } + + /** + * @return non-negative-int + */ + public function shortenArraysForExportThreshold(): int + { + return $this->shortenArraysForExportThreshold; + } } diff --git a/src/TextUI/Configuration/Exception/CannotFindSchemaException.php b/src/TextUI/Configuration/Exception/CannotFindSchemaException.php index 3e89f932a6a..6eef052db7a 100644 --- a/src/TextUI/Configuration/Exception/CannotFindSchemaException.php +++ b/src/TextUI/Configuration/Exception/CannotFindSchemaException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CannotFindSchemaException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php b/src/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php index 05243c9a844..83faa0a2d67 100644 --- a/src/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php +++ b/src/TextUI/Configuration/Exception/CodeCoverageReportNotConfiguredException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class CodeCoverageReportNotConfiguredException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.php b/src/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.php index 8aef86433db..e95e09428d0 100644 --- a/src/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.php +++ b/src/TextUI/Configuration/Exception/ConfigurationCannotBeBuiltException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ConfigurationCannotBeBuiltException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/Exception.php b/src/TextUI/Configuration/Exception/Exception.php index 8678983b9f8..dc49125a5f6 100644 --- a/src/TextUI/Configuration/Exception/Exception.php +++ b/src/TextUI/Configuration/Exception/Exception.php @@ -10,6 +10,8 @@ namespace PHPUnit\TextUI\Configuration; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends \PHPUnit\TextUI\Exception diff --git a/src/TextUI/Configuration/Exception/FilterNotConfiguredException.php b/src/TextUI/Configuration/Exception/FilterNotConfiguredException.php index 9cb4a79ab24..5ae4331f6c5 100644 --- a/src/TextUI/Configuration/Exception/FilterNotConfiguredException.php +++ b/src/TextUI/Configuration/Exception/FilterNotConfiguredException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class FilterNotConfiguredException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/IncludePathNotConfiguredException.php b/src/TextUI/Configuration/Exception/IncludePathNotConfiguredException.php deleted file mode 100644 index e4623b24ed0..00000000000 --- a/src/TextUI/Configuration/Exception/IncludePathNotConfiguredException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\Configuration; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class IncludePathNotConfiguredException extends RuntimeException implements Exception -{ -} diff --git a/src/TextUI/Configuration/Exception/LoggingNotConfiguredException.php b/src/TextUI/Configuration/Exception/LoggingNotConfiguredException.php index c10febbb904..63cf9b07010 100644 --- a/src/TextUI/Configuration/Exception/LoggingNotConfiguredException.php +++ b/src/TextUI/Configuration/Exception/LoggingNotConfiguredException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class LoggingNotConfiguredException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoBaselineException.php b/src/TextUI/Configuration/Exception/NoBaselineException.php index eb8cf3ba174..7611dceb17d 100644 --- a/src/TextUI/Configuration/Exception/NoBaselineException.php +++ b/src/TextUI/Configuration/Exception/NoBaselineException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoBaselineException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoBootstrapException.php b/src/TextUI/Configuration/Exception/NoBootstrapException.php index 1abc7e88f04..ff1bddf0ea2 100644 --- a/src/TextUI/Configuration/Exception/NoBootstrapException.php +++ b/src/TextUI/Configuration/Exception/NoBootstrapException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoBootstrapException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoCacheDirectoryException.php b/src/TextUI/Configuration/Exception/NoCacheDirectoryException.php index 36d2499ed2e..215fe21f6f8 100644 --- a/src/TextUI/Configuration/Exception/NoCacheDirectoryException.php +++ b/src/TextUI/Configuration/Exception/NoCacheDirectoryException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoCacheDirectoryException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoCliArgumentException.php b/src/TextUI/Configuration/Exception/NoCliArgumentException.php deleted file mode 100644 index 18c5e16fb98..00000000000 --- a/src/TextUI/Configuration/Exception/NoCliArgumentException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\Configuration; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class NoCliArgumentException extends RuntimeException implements Exception -{ -} diff --git a/src/TextUI/Configuration/Exception/NoConfigurationFileException.php b/src/TextUI/Configuration/Exception/NoConfigurationFileException.php index 1d488d3b09a..f8ceb80ba2e 100644 --- a/src/TextUI/Configuration/Exception/NoConfigurationFileException.php +++ b/src/TextUI/Configuration/Exception/NoConfigurationFileException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoConfigurationFileException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.php b/src/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.php index 54eaabbe4eb..113950b5d3d 100644 --- a/src/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.php +++ b/src/TextUI/Configuration/Exception/NoCoverageCacheDirectoryException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoCoverageCacheDirectoryException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoCustomCssFileException.php b/src/TextUI/Configuration/Exception/NoCustomCssFileException.php index 672b1fc1ead..e524c8db50a 100644 --- a/src/TextUI/Configuration/Exception/NoCustomCssFileException.php +++ b/src/TextUI/Configuration/Exception/NoCustomCssFileException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoCustomCssFileException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoDefaultTestSuiteException.php b/src/TextUI/Configuration/Exception/NoDefaultTestSuiteException.php index 28663ff5db1..96e7a7ada86 100644 --- a/src/TextUI/Configuration/Exception/NoDefaultTestSuiteException.php +++ b/src/TextUI/Configuration/Exception/NoDefaultTestSuiteException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoDefaultTestSuiteException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.php b/src/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.php index 269be3bd6bf..ce573ca79ec 100644 --- a/src/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.php +++ b/src/TextUI/Configuration/Exception/NoPharExtensionDirectoryException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NoPharExtensionDirectoryException extends RuntimeException implements Exception diff --git a/src/TextUI/Configuration/Exception/SpecificDeprecationToStopOnNotConfiguredException.php b/src/TextUI/Configuration/Exception/SpecificDeprecationToStopOnNotConfiguredException.php new file mode 100644 index 00000000000..73074db1306 --- /dev/null +++ b/src/TextUI/Configuration/Exception/SpecificDeprecationToStopOnNotConfiguredException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class SpecificDeprecationToStopOnNotConfiguredException extends RuntimeException implements Exception +{ +} diff --git a/src/TextUI/Configuration/Merger.php b/src/TextUI/Configuration/Merger.php index 9ef87427c82..002964953f5 100644 --- a/src/TextUI/Configuration/Merger.php +++ b/src/TextUI/Configuration/Merger.php @@ -10,6 +10,7 @@ namespace PHPUnit\TextUI\Configuration; use const DIRECTORY_SEPARATOR; +use const PATH_SEPARATOR; use function array_diff; use function assert; use function dirname; @@ -20,6 +21,7 @@ use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Runner\TestSuiteSorter; use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; +use PHPUnit\TextUI\CliArguments\Exception; use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; use PHPUnit\TextUI\XmlConfiguration\LoadedFromFileConfiguration; use PHPUnit\TextUI\XmlConfiguration\SchemaDetector; @@ -30,13 +32,15 @@ use SebastianBergmann\Invoker\Invoker; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Merger +final readonly class Merger { /** - * @throws \PHPUnit\TextUI\CliArguments\Exception * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * @throws Exception * @throws NoCustomCssFileException */ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlConfiguration): Configuration @@ -97,12 +101,30 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $disableCodeCoverageIgnore = $xmlConfiguration->codeCoverage()->disableCodeCoverageIgnore(); } + if ($cliConfiguration->hasFailOnAllIssues()) { + $failOnAllIssues = $cliConfiguration->failOnAllIssues(); + } else { + $failOnAllIssues = $xmlConfiguration->phpunit()->failOnAllIssues(); + } + if ($cliConfiguration->hasFailOnDeprecation()) { $failOnDeprecation = $cliConfiguration->failOnDeprecation(); } else { $failOnDeprecation = $xmlConfiguration->phpunit()->failOnDeprecation(); } + if ($cliConfiguration->hasFailOnPhpunitDeprecation()) { + $failOnPhpunitDeprecation = $cliConfiguration->failOnPhpunitDeprecation(); + } else { + $failOnPhpunitDeprecation = $xmlConfiguration->phpunit()->failOnPhpunitDeprecation(); + } + + if ($cliConfiguration->hasFailOnPhpunitNotice()) { + $failOnPhpunitNotice = $cliConfiguration->failOnPhpunitNotice(); + } else { + $failOnPhpunitNotice = $xmlConfiguration->phpunit()->failOnPhpunitNotice(); + } + if ($cliConfiguration->hasFailOnEmptyTestSuite()) { $failOnEmptyTestSuite = $cliConfiguration->failOnEmptyTestSuite(); } else { @@ -151,6 +173,12 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $stopOnDeprecation = $xmlConfiguration->phpunit()->stopOnDeprecation(); } + $specificDeprecationToStopOn = null; + + if ($cliConfiguration->hasSpecificDeprecationToStopOn()) { + $specificDeprecationToStopOn = $cliConfiguration->specificDeprecationToStopOn(); + } + if ($cliConfiguration->hasStopOnError()) { $stopOnError = $cliConfiguration->stopOnError(); } else { @@ -212,7 +240,7 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC if ($columns < 16) { $columns = 16; - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( 'Less than 16 columns requested, number of columns set to 16', ); } @@ -233,6 +261,15 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $extensionBootstrappers = []; + if ($cliConfiguration->hasExtensions()) { + foreach ($cliConfiguration->extensions() as $extension) { + $extensionBootstrappers[] = [ + 'className' => $extension, + 'parameters' => [], + ]; + } + } + foreach ($xmlConfiguration->extensions() as $extension) { $extensionBootstrappers[] = [ 'className' => $extension->className(), @@ -332,6 +369,14 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $coverageTextShowOnlySummary = $xmlConfiguration->codeCoverage()->text()->showOnlySummary(); } + if ($cliConfiguration->hasCoverageTextShowUncoveredFiles()) { + $coverageTextShowUncoveredFiles = $cliConfiguration->coverageTextShowUncoveredFiles(); + } + + if ($cliConfiguration->hasCoverageTextShowOnlySummary()) { + $coverageTextShowOnlySummary = $cliConfiguration->coverageTextShowOnlySummary(); + } + if ($cliConfiguration->hasCoverageText()) { $coverageText = $cliConfiguration->coverageText(); } elseif ($coverageFromXmlConfiguration && $xmlConfiguration->codeCoverage()->hasText()) { @@ -375,7 +420,7 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC } if ($enforceTimeLimit && !(new Invoker)->canInvokeWithTimeout()) { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( 'The pcntl extension is required for enforcing time limits', ); } @@ -408,6 +453,12 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $disallowTestOutput = $xmlConfiguration->phpunit()->beStrictAboutOutputDuringTests(); } + if ($cliConfiguration->hasDisplayDetailsOnAllIssues()) { + $displayDetailsOnAllIssues = $cliConfiguration->displayDetailsOnAllIssues(); + } else { + $displayDetailsOnAllIssues = $xmlConfiguration->phpunit()->displayDetailsOnAllIssues(); + } + if ($cliConfiguration->hasDisplayDetailsOnIncompleteTests()) { $displayDetailsOnIncompleteTests = $cliConfiguration->displayDetailsOnIncompleteTests(); } else { @@ -426,6 +477,18 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $displayDetailsOnTestsThatTriggerDeprecations = $xmlConfiguration->phpunit()->displayDetailsOnTestsThatTriggerDeprecations(); } + if ($cliConfiguration->hasDisplayDetailsOnPhpunitDeprecations()) { + $displayDetailsOnPhpunitDeprecations = $cliConfiguration->displayDetailsOnPhpunitDeprecations(); + } else { + $displayDetailsOnPhpunitDeprecations = $xmlConfiguration->phpunit()->displayDetailsOnPhpunitDeprecations(); + } + + if ($cliConfiguration->hasDisplayDetailsOnPhpunitNotices()) { + $displayDetailsOnPhpunitNotices = $cliConfiguration->displayDetailsOnPhpunitNotices(); + } else { + $displayDetailsOnPhpunitNotices = $xmlConfiguration->phpunit()->displayDetailsOnPhpunitNotices(); + } + if ($cliConfiguration->hasDisplayDetailsOnTestsThatTriggerErrors()) { $displayDetailsOnTestsThatTriggerErrors = $cliConfiguration->displayDetailsOnTestsThatTriggerErrors(); } else { @@ -450,8 +513,7 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $reverseDefectList = $xmlConfiguration->phpunit()->reverseDefectList(); } - $requireCoverageMetadata = $xmlConfiguration->phpunit()->requireCoverageMetadata(); - $registerMockObjectsFromTestArgumentsRecursively = $xmlConfiguration->phpunit()->registerMockObjectsFromTestArgumentsRecursively(); + $requireCoverageMetadata = $xmlConfiguration->phpunit()->requireCoverageMetadata(); if ($cliConfiguration->hasExecutionOrder()) { $executionOrder = $cliConfiguration->executionOrder(); @@ -546,6 +608,12 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $testDoxOutput = $xmlConfiguration->phpunit()->testdoxPrinter(); } + if ($cliConfiguration->hasTestDoxPrinterSummary() && $cliConfiguration->testdoxPrinterSummary()) { + $testDoxOutputSummary = true; + } else { + $testDoxOutputSummary = $xmlConfiguration->phpunit()->testdoxPrinterSummary(); + } + $noProgress = false; if ($cliConfiguration->hasNoProgress() && $cliConfiguration->noProgress()) { @@ -576,12 +644,24 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $testsUsing = $cliConfiguration->testsUsing(); } + $testsRequiringPhpExtension = null; + + if ($cliConfiguration->hasTestsRequiringPhpExtension()) { + $testsRequiringPhpExtension = $cliConfiguration->testsRequiringPhpExtension(); + } + $filter = null; if ($cliConfiguration->hasFilter()) { $filter = $cliConfiguration->filter(); } + $excludeFilter = null; + + if ($cliConfiguration->hasExcludeFilter()) { + $excludeFilter = $cliConfiguration->excludeFilter(); + } + if ($cliConfiguration->hasGroups()) { $groups = $cliConfiguration->groups(); } else { @@ -604,11 +684,11 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC if ($xmlConfiguration->wasLoadedFromFile() && $xmlConfiguration->hasValidationErrors()) { if ((new SchemaDetector)->detect($xmlConfiguration->filename())->detected()) { - EventFacade::emitter()->testRunnerTriggeredDeprecation( + EventFacade::emitter()->testRunnerTriggeredPhpunitDeprecation( 'Your XML configuration validates against a deprecated schema. Migrate your XML configuration using "--migrate-configuration"!', ); } else { - EventFacade::emitter()->testRunnerTriggeredWarning( + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( "Test results may not be as expected because the XML configuration file did not pass validation:\n" . $xmlConfiguration->validationErrors(), ); @@ -693,6 +773,38 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC assert($useBaseline !== ''); assert($generateBaseline !== ''); + if ($failOnAllIssues) { + $displayDetailsOnAllIssues = true; + } + + if ($failOnDeprecation) { + $displayDetailsOnTestsThatTriggerDeprecations = true; + } + + if ($failOnPhpunitDeprecation) { + $displayDetailsOnPhpunitDeprecations = true; + } + + if ($failOnPhpunitNotice) { + $displayDetailsOnPhpunitNotices = true; + } + + if ($failOnNotice) { + $displayDetailsOnTestsThatTriggerNotices = true; + } + + if ($failOnWarning) { + $displayDetailsOnTestsThatTriggerWarnings = true; + } + + if ($failOnIncomplete) { + $displayDetailsOnIncompleteTests = true; + } + + if ($failOnSkipped) { + $displayDetailsOnSkippedTests = true; + } + return new Configuration( $cliConfiguration->arguments(), $configurationFile, @@ -707,7 +819,6 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $sourceIncludeFiles, $sourceExcludeDirectories, $sourceExcludeFiles, - $xmlConfiguration->source()->restrictDeprecations(), $xmlConfiguration->source()->restrictNotices(), $xmlConfiguration->source()->restrictWarnings(), $xmlConfiguration->source()->ignoreSuppressionOfDeprecations(), @@ -717,6 +828,10 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $xmlConfiguration->source()->ignoreSuppressionOfPhpNotices(), $xmlConfiguration->source()->ignoreSuppressionOfWarnings(), $xmlConfiguration->source()->ignoreSuppressionOfPhpWarnings(), + $xmlConfiguration->source()->deprecationTriggers(), + $xmlConfiguration->source()->ignoreSelfDeprecations(), + $xmlConfiguration->source()->ignoreDirectDeprecations(), + $xmlConfiguration->source()->ignoreIndirectDeprecations(), ), $testResultCacheFile, $coverageClover, @@ -740,7 +855,10 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $pathCoverage, $xmlConfiguration->codeCoverage()->ignoreDeprecatedCodeUnits(), $disableCodeCoverageIgnore, + $failOnAllIssues, $failOnDeprecation, + $failOnPhpunitDeprecation, + $failOnPhpunitNotice, $failOnEmptyTestSuite, $failOnIncomplete, $failOnNotice, @@ -749,6 +867,7 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $failOnWarning, $stopOnDefect, $stopOnDeprecation, + $specificDeprecationToStopOn, $stopOnError, $stopOnFailure, $stopOnIncomplete, @@ -774,15 +893,17 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $reportUselessTests, $strictCoverage, $disallowTestOutput, + $displayDetailsOnAllIssues, $displayDetailsOnIncompleteTests, $displayDetailsOnSkippedTests, $displayDetailsOnTestsThatTriggerDeprecations, + $displayDetailsOnPhpunitDeprecations, + $displayDetailsOnPhpunitNotices, $displayDetailsOnTestsThatTriggerErrors, $displayDetailsOnTestsThatTriggerNotices, $displayDetailsOnTestsThatTriggerWarnings, $reverseDefectList, $requireCoverageMetadata, - $registerMockObjectsFromTestArgumentsRecursively, $noProgress, $noResults, $noOutput, @@ -797,9 +918,12 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $logEventsVerboseText, $teamCityOutput, $testDoxOutput, + $testDoxOutputSummary, $testsCovering, $testsUsing, + $testsRequiringPhpExtension, $filter, + $excludeFilter, $groups, $excludeGroups, $randomOrderSeed, @@ -825,6 +949,9 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC $xmlConfiguration->phpunit()->controlGarbageCollector(), $xmlConfiguration->phpunit()->numberOfTestsBeforeGarbageCollection(), $generateBaseline, + $cliConfiguration->debug(), + $cliConfiguration->withTelemetry(), + $xmlConfiguration->phpunit()->shortenArraysForExportThreshold(), ); } } diff --git a/src/TextUI/Configuration/PhpHandler.php b/src/TextUI/Configuration/PhpHandler.php index 4417c71347a..3aa13160e16 100644 --- a/src/TextUI/Configuration/PhpHandler.php +++ b/src/TextUI/Configuration/PhpHandler.php @@ -20,9 +20,11 @@ use function putenv; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class PhpHandler +final readonly class PhpHandler { public function handle(Php $configuration): void { diff --git a/src/TextUI/Configuration/Registry.php b/src/TextUI/Configuration/Registry.php index e2b1ad3b784..ad8075235d2 100644 --- a/src/TextUI/Configuration/Registry.php +++ b/src/TextUI/Configuration/Registry.php @@ -16,6 +16,7 @@ use function unserialize; use PHPUnit\Event\Facade as EventFacade; use PHPUnit\TextUI\CliArguments\Configuration as CliConfiguration; +use PHPUnit\TextUI\CliArguments\Exception; use PHPUnit\TextUI\XmlConfiguration\Configuration as XmlConfiguration; use PHPUnit\Util\VersionComparisonOperator; @@ -23,6 +24,8 @@ * CLI options and XML configuration are static within a single PHPUnit process. * It is therefore okay to use a Singleton registry here. * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Registry @@ -40,18 +43,26 @@ public static function saveTo(string $path): bool return true; } + // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } /** * This method is used by the "run test(s) in separate process" templates. * * @noinspection PhpUnused + * + * @codeCoverageIgnore */ public static function loadFrom(string $path): void { + $buffer = file_get_contents($path); + + assert($buffer !== false); + self::$instance = unserialize( - file_get_contents($path), + $buffer, [ 'allowed_classes' => [ Configuration::class, @@ -89,8 +100,8 @@ public static function get(): Configuration } /** - * @throws \PHPUnit\TextUI\CliArguments\Exception * @throws \PHPUnit\TextUI\XmlConfiguration\Exception + * @throws Exception * @throws NoCustomCssFileException */ public static function init(CliConfiguration $cliConfiguration, XmlConfiguration $xmlConfiguration): Configuration diff --git a/src/TextUI/Configuration/SourceFilter.php b/src/TextUI/Configuration/SourceFilter.php index 1586a649a13..845a9b3763f 100644 --- a/src/TextUI/Configuration/SourceFilter.php +++ b/src/TextUI/Configuration/SourceFilter.php @@ -10,14 +10,42 @@ namespace PHPUnit\TextUI\Configuration; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class SourceFilter { - public function includes(Source $source, string $path): bool + private static ?self $instance = null; + + /** + * @var array + */ + private readonly array $map; + + public static function instance(): self { - $files = (new SourceMapper)->map($source); + if (self::$instance === null) { + self::$instance = new self( + (new SourceMapper)->map( + Registry::get()->source(), + ), + ); + } + + return self::$instance; + } - return isset($files[$path]); + /** + * @param array $map + */ + public function __construct(array $map) + { + $this->map = $map; + } + + public function includes(string $path): bool + { + return isset($this->map[$path]); } } diff --git a/src/TextUI/Configuration/SourceMapper.php b/src/TextUI/Configuration/SourceMapper.php index 8e42dbf8b1d..c2c5483822f 100644 --- a/src/TextUI/Configuration/SourceMapper.php +++ b/src/TextUI/Configuration/SourceMapper.php @@ -14,17 +14,19 @@ use SplObjectStorage; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class SourceMapper { /** - * @psalm-var SplObjectStorage> + * @var ?SplObjectStorage> */ private static ?SplObjectStorage $files = null; /** - * @psalm-return array + * @return array */ public function map(Source $source): array { @@ -38,8 +40,10 @@ public function map(Source $source): array $files = []; - foreach ($source->includeDirectories() as $directory) { - foreach ((new FileIteratorFacade)->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix()) as $file) { + $directories = $this->aggregateDirectories($source->includeDirectories()); + + foreach ($directories as $path => [$prefixes, $suffixes]) { + foreach ((new FileIteratorFacade)->getFilesAsArray($path, $suffixes, $prefixes) as $file) { $file = realpath($file); if (!$file) { @@ -60,8 +64,10 @@ public function map(Source $source): array $files[$file] = true; } - foreach ($source->excludeDirectories() as $directory) { - foreach ((new FileIteratorFacade)->getFilesAsArray($directory->path(), $directory->suffix(), $directory->prefix()) as $file) { + $directories = $this->aggregateDirectories($source->excludeDirectories()); + + foreach ($directories as $path => [$prefixes, $suffixes]) { + foreach ((new FileIteratorFacade)->getFilesAsArray($path, $suffixes, $prefixes) as $file) { $file = realpath($file); if (!$file) { @@ -94,4 +100,35 @@ public function map(Source $source): array return $files; } + + /** + * @return array,list}> + */ + private function aggregateDirectories(FilterDirectoryCollection $directories): array + { + $aggregated = []; + + foreach ($directories as $directory) { + if (!isset($aggregated[$directory->path()])) { + $aggregated[$directory->path()] = [ + 0 => [], + 1 => [], + ]; + } + + $prefix = $directory->prefix(); + + if ($prefix !== '') { + $aggregated[$directory->path()][0][] = $prefix; + } + + $suffix = $directory->suffix(); + + if ($suffix !== '') { + $aggregated[$directory->path()][1][] = $suffix; + } + } + + return $aggregated; + } } diff --git a/src/TextUI/Configuration/TestSuiteBuilder.php b/src/TextUI/Configuration/TestSuiteBuilder.php index d3fff95db42..53f558f6d73 100644 --- a/src/TextUI/Configuration/TestSuiteBuilder.php +++ b/src/TextUI/Configuration/TestSuiteBuilder.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TextUI\Configuration; +use const PHP_EOL; use function assert; use function count; use function is_dir; @@ -26,9 +27,11 @@ use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteBuilder +final readonly class TestSuiteBuilder { /** * @throws \PHPUnit\Framework\Exception @@ -67,7 +70,7 @@ public function build(Configuration $configuration): TestSuite if (!isset($testSuite)) { $xmlConfigurationFile = $configuration->hasConfigurationFile() ? $configuration->configurationFile() : 'Root Test Suite'; - assert(!empty($xmlConfigurationFile)); + assert($xmlConfigurationFile !== ''); $testSuite = (new TestSuiteMapper)->map( $xmlConfigurationFile, @@ -83,26 +86,31 @@ public function build(Configuration $configuration): TestSuite } /** - * @psalm-param non-empty-string $path - * @psalm-param list $suffixes - * @psalm-param ?TestSuite $suite + * @param non-empty-string $path + * @param list $suffixes * * @throws \PHPUnit\Framework\Exception */ private function testSuiteFromPath(string $path, array $suffixes, ?TestSuite $suite = null): TestSuite { - if (is_dir($path)) { - $files = (new FileIteratorFacade)->getFilesAsArray($path, $suffixes); + if (str_ends_with($path, '.phpt') && is_file($path)) { + if ($suite === null) { + $suite = TestSuite::empty($path); + } - $suite = $suite ?: TestSuite::empty('CLI Arguments'); - $suite->addTestFiles($files); + $suite->addTestFile($path); return $suite; } - if (is_file($path) && str_ends_with($path, '.phpt')) { - $suite = $suite ?: TestSuite::empty($path); - $suite->addTestFile($path); + if (is_dir($path)) { + $files = (new FileIteratorFacade)->getFilesAsArray($path, $suffixes); + + if ($suite === null) { + $suite = TestSuite::empty('CLI Arguments'); + } + + $suite->addTestFiles($files); return $suite; } @@ -115,7 +123,7 @@ private function testSuiteFromPath(string $path, array $suffixes, ?TestSuite $su exit(1); } - if (!$suite) { + if ($suite === null) { return TestSuite::fromClassReflector($testClass); } @@ -125,8 +133,8 @@ private function testSuiteFromPath(string $path, array $suffixes, ?TestSuite $su } /** - * @psalm-param list $paths - * @psalm-param list $suffixes + * @param list $paths + * @param list $suffixes * * @throws \PHPUnit\Framework\Exception */ diff --git a/src/TextUI/Configuration/Value/Constant.php b/src/TextUI/Configuration/Value/Constant.php index e2d43042fdd..0ff240dd03e 100644 --- a/src/TextUI/Configuration/Value/Constant.php +++ b/src/TextUI/Configuration/Value/Constant.php @@ -12,7 +12,7 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Constant { diff --git a/src/TextUI/Configuration/Value/ConstantCollection.php b/src/TextUI/Configuration/Value/ConstantCollection.php index 653b82892ca..3e34d7434ec 100644 --- a/src/TextUI/Configuration/Value/ConstantCollection.php +++ b/src/TextUI/Configuration/Value/ConstantCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class ConstantCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $constants; /** - * @psalm-param list $constants + * @param list $constants */ public static function fromArray(array $constants): self { @@ -41,7 +41,7 @@ private function __construct(Constant ...$constants) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/ConstantCollectionIterator.php b/src/TextUI/Configuration/Value/ConstantCollectionIterator.php index acb4c8b90aa..f385b7faf4f 100644 --- a/src/TextUI/Configuration/Value/ConstantCollectionIterator.php +++ b/src/TextUI/Configuration/Value/ConstantCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class ConstantCollectionIterator implements Countable, Iterator +final class ConstantCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $constants; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(ConstantCollection $constants) $this->constants = $constants->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/Directory.php b/src/TextUI/Configuration/Value/Directory.php index b40b2d7adcb..f44e28b1559 100644 --- a/src/TextUI/Configuration/Value/Directory.php +++ b/src/TextUI/Configuration/Value/Directory.php @@ -12,7 +12,7 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Directory { diff --git a/src/TextUI/Configuration/Value/DirectoryCollection.php b/src/TextUI/Configuration/Value/DirectoryCollection.php index 8ff890ff82b..dc1e840c94c 100644 --- a/src/TextUI/Configuration/Value/DirectoryCollection.php +++ b/src/TextUI/Configuration/Value/DirectoryCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class DirectoryCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $directories; /** - * @psalm-param list $directories + * @param list $directories */ public static function fromArray(array $directories): self { @@ -41,7 +41,7 @@ private function __construct(Directory ...$directories) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/DirectoryCollectionIterator.php b/src/TextUI/Configuration/Value/DirectoryCollectionIterator.php index 4e5655ceb83..73d2cff6344 100644 --- a/src/TextUI/Configuration/Value/DirectoryCollectionIterator.php +++ b/src/TextUI/Configuration/Value/DirectoryCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class DirectoryCollectionIterator implements Countable, Iterator +final class DirectoryCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $directories; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(DirectoryCollection $directories) $this->directories = $directories->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/ExtensionBootstrap.php b/src/TextUI/Configuration/Value/ExtensionBootstrap.php index 959fbbfe6a1..09430c7f3f7 100644 --- a/src/TextUI/Configuration/Value/ExtensionBootstrap.php +++ b/src/TextUI/Configuration/Value/ExtensionBootstrap.php @@ -12,23 +12,23 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class ExtensionBootstrap { /** - * @psalm-var class-string + * @var non-empty-string */ private string $className; /** - * @psalm-var array + * @var array */ private array $parameters; /** - * @psalm-param class-string $className - * @psalm-param array $parameters + * @param non-empty-string $className + * @param array $parameters */ public function __construct(string $className, array $parameters) { @@ -37,7 +37,7 @@ public function __construct(string $className, array $parameters) } /** - * @psalm-return class-string + * @return non-empty-string */ public function className(): string { @@ -45,7 +45,7 @@ public function className(): string } /** - * @psalm-return array + * @return array */ public function parameters(): array { diff --git a/src/TextUI/Configuration/Value/ExtensionBootstrapCollection.php b/src/TextUI/Configuration/Value/ExtensionBootstrapCollection.php index 5a105211c2c..16ca1e07049 100644 --- a/src/TextUI/Configuration/Value/ExtensionBootstrapCollection.php +++ b/src/TextUI/Configuration/Value/ExtensionBootstrapCollection.php @@ -16,17 +16,17 @@ * * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class ExtensionBootstrapCollection implements IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $extensionBootstraps; /** - * @psalm-param list $extensionBootstraps + * @param list $extensionBootstraps */ public static function fromArray(array $extensionBootstraps): self { @@ -39,7 +39,7 @@ private function __construct(ExtensionBootstrap ...$extensionBootstraps) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.php b/src/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.php index 0de1711fc0d..0b5c20ba15c 100644 --- a/src/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.php +++ b/src/TextUI/Configuration/Value/ExtensionBootstrapCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class ExtensionBootstrapCollectionIterator implements Countable, Iterator +final class ExtensionBootstrapCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $extensionBootstraps; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(ExtensionBootstrapCollection $extensionBootstraps) $this->extensionBootstraps = $extensionBootstraps->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/File.php b/src/TextUI/Configuration/Value/File.php index fcaebed9c09..85900f47f6c 100644 --- a/src/TextUI/Configuration/Value/File.php +++ b/src/TextUI/Configuration/Value/File.php @@ -12,17 +12,17 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class File { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $path; /** - * @psalm-param non-empty-string $path + * @param non-empty-string $path */ public function __construct(string $path) { @@ -30,7 +30,7 @@ public function __construct(string $path) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function path(): string { diff --git a/src/TextUI/Configuration/Value/FileCollection.php b/src/TextUI/Configuration/Value/FileCollection.php index 342394c7f00..61522a5eb6d 100644 --- a/src/TextUI/Configuration/Value/FileCollection.php +++ b/src/TextUI/Configuration/Value/FileCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class FileCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $files; /** - * @psalm-param list $files + * @param list $files */ public static function fromArray(array $files): self { @@ -41,7 +41,7 @@ private function __construct(File ...$files) } /** - * @psalm-return list + * @return list */ public function asArray(): array { @@ -55,7 +55,7 @@ public function count(): int public function notEmpty(): bool { - return !empty($this->files); + return $this->files !== []; } public function getIterator(): FileCollectionIterator diff --git a/src/TextUI/Configuration/Value/FileCollectionIterator.php b/src/TextUI/Configuration/Value/FileCollectionIterator.php index 9627fca6692..91ec8e27638 100644 --- a/src/TextUI/Configuration/Value/FileCollectionIterator.php +++ b/src/TextUI/Configuration/Value/FileCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class FileCollectionIterator implements Countable, Iterator +final class FileCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $files; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(FileCollection $files) $this->files = $files->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/FilterDirectory.php b/src/TextUI/Configuration/Value/FilterDirectory.php index fcae361721c..52dcd1b1323 100644 --- a/src/TextUI/Configuration/Value/FilterDirectory.php +++ b/src/TextUI/Configuration/Value/FilterDirectory.php @@ -12,19 +12,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class FilterDirectory { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $path; private string $prefix; private string $suffix; /** - * @psalm-param non-empty-string $path + * @param non-empty-string $path */ public function __construct(string $path, string $prefix, string $suffix) { @@ -34,7 +34,7 @@ public function __construct(string $path, string $prefix, string $suffix) } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function path(): string { diff --git a/src/TextUI/Configuration/Value/FilterDirectoryCollection.php b/src/TextUI/Configuration/Value/FilterDirectoryCollection.php index 232c5d24d5e..147f0618f0d 100644 --- a/src/TextUI/Configuration/Value/FilterDirectoryCollection.php +++ b/src/TextUI/Configuration/Value/FilterDirectoryCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class FilterDirectoryCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $directories; /** - * @psalm-param list $directories + * @param list $directories */ public static function fromArray(array $directories): self { @@ -41,7 +41,7 @@ private function __construct(FilterDirectory ...$directories) } /** - * @psalm-return list + * @return list */ public function asArray(): array { @@ -55,7 +55,7 @@ public function count(): int public function notEmpty(): bool { - return !empty($this->directories); + return $this->directories !== []; } public function getIterator(): FilterDirectoryCollectionIterator diff --git a/src/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.php b/src/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.php index 3d145981550..737c752f465 100644 --- a/src/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.php +++ b/src/TextUI/Configuration/Value/FilterDirectoryCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class FilterDirectoryCollectionIterator implements Countable, Iterator +final class FilterDirectoryCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $directories; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(FilterDirectoryCollection $directories) $this->directories = $directories->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/Group.php b/src/TextUI/Configuration/Value/Group.php index d60a7de29c6..cb0bdc8aa3e 100644 --- a/src/TextUI/Configuration/Value/Group.php +++ b/src/TextUI/Configuration/Value/Group.php @@ -12,7 +12,7 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Group { diff --git a/src/TextUI/Configuration/Value/GroupCollection.php b/src/TextUI/Configuration/Value/GroupCollection.php index 0787f1f9672..8232e1f34fc 100644 --- a/src/TextUI/Configuration/Value/GroupCollection.php +++ b/src/TextUI/Configuration/Value/GroupCollection.php @@ -14,19 +14,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class GroupCollection implements IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $groups; /** - * @psalm-param list $groups + * @param list $groups */ public static function fromArray(array $groups): self { @@ -39,7 +39,7 @@ private function __construct(Group ...$groups) } /** - * @psalm-return list + * @return list */ public function asArray(): array { @@ -47,7 +47,7 @@ public function asArray(): array } /** - * @psalm-return list + * @return list */ public function asArrayOfStrings(): array { @@ -62,7 +62,7 @@ public function asArrayOfStrings(): array public function isEmpty(): bool { - return empty($this->groups); + return $this->groups === []; } public function getIterator(): GroupCollectionIterator diff --git a/src/TextUI/Configuration/Value/GroupCollectionIterator.php b/src/TextUI/Configuration/Value/GroupCollectionIterator.php index 9b4795ac214..774808757cb 100644 --- a/src/TextUI/Configuration/Value/GroupCollectionIterator.php +++ b/src/TextUI/Configuration/Value/GroupCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class GroupCollectionIterator implements Countable, Iterator +final class GroupCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $groups; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(GroupCollection $groups) $this->groups = $groups->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/IniSetting.php b/src/TextUI/Configuration/Value/IniSetting.php index b7539f981ad..b4d11665551 100644 --- a/src/TextUI/Configuration/Value/IniSetting.php +++ b/src/TextUI/Configuration/Value/IniSetting.php @@ -12,7 +12,7 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class IniSetting { diff --git a/src/TextUI/Configuration/Value/IniSettingCollection.php b/src/TextUI/Configuration/Value/IniSettingCollection.php index 4ab61027787..abfd8fd241b 100644 --- a/src/TextUI/Configuration/Value/IniSettingCollection.php +++ b/src/TextUI/Configuration/Value/IniSettingCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class IniSettingCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $iniSettings; /** - * @psalm-param list $iniSettings + * @param list $iniSettings */ public static function fromArray(array $iniSettings): self { @@ -41,7 +41,7 @@ private function __construct(IniSetting ...$iniSettings) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/IniSettingCollectionIterator.php b/src/TextUI/Configuration/Value/IniSettingCollectionIterator.php index 196358c09ec..cb68c3dd1a4 100644 --- a/src/TextUI/Configuration/Value/IniSettingCollectionIterator.php +++ b/src/TextUI/Configuration/Value/IniSettingCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class IniSettingCollectionIterator implements Countable, Iterator +final class IniSettingCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $iniSettings; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(IniSettingCollection $iniSettings) $this->iniSettings = $iniSettings->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/Php.php b/src/TextUI/Configuration/Value/Php.php index 19654ebef5c..0dc4735dca7 100644 --- a/src/TextUI/Configuration/Value/Php.php +++ b/src/TextUI/Configuration/Value/Php.php @@ -12,7 +12,7 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Php { diff --git a/src/TextUI/Configuration/Value/Source.php b/src/TextUI/Configuration/Value/Source.php index a13eccec4ec..b6f61eb07d9 100644 --- a/src/TextUI/Configuration/Value/Source.php +++ b/src/TextUI/Configuration/Value/Source.php @@ -12,12 +12,12 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Source { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private ?string $baseline; private bool $ignoreBaseline; @@ -25,7 +25,6 @@ private FileCollection $includeFiles; private FilterDirectoryCollection $excludeDirectories; private FileCollection $excludeFiles; - private bool $restrictDeprecations; private bool $restrictNotices; private bool $restrictWarnings; private bool $ignoreSuppressionOfDeprecations; @@ -35,11 +34,20 @@ private bool $ignoreSuppressionOfPhpNotices; private bool $ignoreSuppressionOfWarnings; private bool $ignoreSuppressionOfPhpWarnings; + private bool $ignoreSelfDeprecations; + private bool $ignoreDirectDeprecations; + private bool $ignoreIndirectDeprecations; /** - * @psalm-param non-empty-string $baseline + * @var array{functions: list, methods: list} */ - public function __construct(?string $baseline, bool $ignoreBaseline, FilterDirectoryCollection $includeDirectories, FileCollection $includeFiles, FilterDirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $restrictDeprecations, bool $restrictNotices, bool $restrictWarnings, bool $ignoreSuppressionOfDeprecations, bool $ignoreSuppressionOfPhpDeprecations, bool $ignoreSuppressionOfErrors, bool $ignoreSuppressionOfNotices, bool $ignoreSuppressionOfPhpNotices, bool $ignoreSuppressionOfWarnings, bool $ignoreSuppressionOfPhpWarnings) + private array $deprecationTriggers; + + /** + * @param non-empty-string $baseline + * @param array{functions: list, methods: list} $deprecationTriggers + */ + public function __construct(?string $baseline, bool $ignoreBaseline, FilterDirectoryCollection $includeDirectories, FileCollection $includeFiles, FilterDirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $restrictNotices, bool $restrictWarnings, bool $ignoreSuppressionOfDeprecations, bool $ignoreSuppressionOfPhpDeprecations, bool $ignoreSuppressionOfErrors, bool $ignoreSuppressionOfNotices, bool $ignoreSuppressionOfPhpNotices, bool $ignoreSuppressionOfWarnings, bool $ignoreSuppressionOfPhpWarnings, array $deprecationTriggers, bool $ignoreSelfDeprecations, bool $ignoreDirectDeprecations, bool $ignoreIndirectDeprecations) { $this->baseline = $baseline; $this->ignoreBaseline = $ignoreBaseline; @@ -47,7 +55,6 @@ public function __construct(?string $baseline, bool $ignoreBaseline, FilterDirec $this->includeFiles = $includeFiles; $this->excludeDirectories = $excludeDirectories; $this->excludeFiles = $excludeFiles; - $this->restrictDeprecations = $restrictDeprecations; $this->restrictNotices = $restrictNotices; $this->restrictWarnings = $restrictWarnings; $this->ignoreSuppressionOfDeprecations = $ignoreSuppressionOfDeprecations; @@ -57,10 +64,14 @@ public function __construct(?string $baseline, bool $ignoreBaseline, FilterDirec $this->ignoreSuppressionOfPhpNotices = $ignoreSuppressionOfPhpNotices; $this->ignoreSuppressionOfWarnings = $ignoreSuppressionOfWarnings; $this->ignoreSuppressionOfPhpWarnings = $ignoreSuppressionOfPhpWarnings; + $this->deprecationTriggers = $deprecationTriggers; + $this->ignoreSelfDeprecations = $ignoreSelfDeprecations; + $this->ignoreDirectDeprecations = $ignoreDirectDeprecations; + $this->ignoreIndirectDeprecations = $ignoreIndirectDeprecations; } /** - * @psalm-assert-if-true !null $this->baseline + * @phpstan-assert-if-true !null $this->baseline */ public function useBaseline(): bool { @@ -68,7 +79,7 @@ public function useBaseline(): bool } /** - * @psalm-assert-if-true !null $this->baseline + * @phpstan-assert-if-true !null $this->baseline */ public function hasBaseline(): bool { @@ -76,9 +87,9 @@ public function hasBaseline(): bool } /** - * @psalm-return non-empty-string - * * @throws NoBaselineException + * + * @return non-empty-string */ public function baseline(): string { @@ -114,11 +125,6 @@ public function notEmpty(): bool return $this->includeDirectories->notEmpty() || $this->includeFiles->notEmpty(); } - public function restrictDeprecations(): bool - { - return $this->restrictDeprecations; - } - public function restrictNotices(): bool { return $this->restrictNotices; @@ -163,4 +169,27 @@ public function ignoreSuppressionOfPhpWarnings(): bool { return $this->ignoreSuppressionOfPhpWarnings; } + + /** + * @return array{functions: list, methods: list} + */ + public function deprecationTriggers(): array + { + return $this->deprecationTriggers; + } + + public function ignoreSelfDeprecations(): bool + { + return $this->ignoreSelfDeprecations; + } + + public function ignoreDirectDeprecations(): bool + { + return $this->ignoreDirectDeprecations; + } + + public function ignoreIndirectDeprecations(): bool + { + return $this->ignoreIndirectDeprecations; + } } diff --git a/src/TextUI/Configuration/Value/TestDirectory.php b/src/TextUI/Configuration/Value/TestDirectory.php index f4671c3ed12..dfe301a941e 100644 --- a/src/TextUI/Configuration/Value/TestDirectory.php +++ b/src/TextUI/Configuration/Value/TestDirectory.php @@ -14,12 +14,12 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class TestDirectory { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $path; private string $prefix; @@ -28,19 +28,26 @@ private VersionComparisonOperator $phpVersionOperator; /** - * @psalm-param non-empty-string $path + * @var list */ - public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, VersionComparisonOperator $phpVersionOperator) + private array $groups; + + /** + * @param non-empty-string $path + * @param list $groups + */ + public function __construct(string $path, string $prefix, string $suffix, string $phpVersion, VersionComparisonOperator $phpVersionOperator, array $groups) { $this->path = $path; $this->prefix = $prefix; $this->suffix = $suffix; $this->phpVersion = $phpVersion; $this->phpVersionOperator = $phpVersionOperator; + $this->groups = $groups; } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function path(): string { @@ -66,4 +73,12 @@ public function phpVersionOperator(): VersionComparisonOperator { return $this->phpVersionOperator; } + + /** + * @return list + */ + public function groups(): array + { + return $this->groups; + } } diff --git a/src/TextUI/Configuration/Value/TestDirectoryCollection.php b/src/TextUI/Configuration/Value/TestDirectoryCollection.php index 9048370c8be..ba867273ce2 100644 --- a/src/TextUI/Configuration/Value/TestDirectoryCollection.php +++ b/src/TextUI/Configuration/Value/TestDirectoryCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class TestDirectoryCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $directories; /** - * @psalm-param list $directories + * @param list $directories */ public static function fromArray(array $directories): self { @@ -41,7 +41,7 @@ private function __construct(TestDirectory ...$directories) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/TestDirectoryCollectionIterator.php b/src/TextUI/Configuration/Value/TestDirectoryCollectionIterator.php index 157c498307f..fa57410a633 100644 --- a/src/TextUI/Configuration/Value/TestDirectoryCollectionIterator.php +++ b/src/TextUI/Configuration/Value/TestDirectoryCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class TestDirectoryCollectionIterator implements Countable, Iterator +final class TestDirectoryCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $directories; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(TestDirectoryCollection $directories) $this->directories = $directories->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/TestFile.php b/src/TextUI/Configuration/Value/TestFile.php index edc757b4356..e658ff88437 100644 --- a/src/TextUI/Configuration/Value/TestFile.php +++ b/src/TextUI/Configuration/Value/TestFile.php @@ -14,21 +14,37 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class TestFile { + /** + * @var non-empty-string + */ private string $path; private string $phpVersion; private VersionComparisonOperator $phpVersionOperator; - public function __construct(string $path, string $phpVersion, VersionComparisonOperator $phpVersionOperator) + /** + * @var list + */ + private array $groups; + + /** + * @param non-empty-string $path + * @param list $groups + */ + public function __construct(string $path, string $phpVersion, VersionComparisonOperator $phpVersionOperator, array $groups) { $this->path = $path; $this->phpVersion = $phpVersion; $this->phpVersionOperator = $phpVersionOperator; + $this->groups = $groups; } + /** + * @return non-empty-string + */ public function path(): string { return $this->path; @@ -43,4 +59,12 @@ public function phpVersionOperator(): VersionComparisonOperator { return $this->phpVersionOperator; } + + /** + * @return list + */ + public function groups(): array + { + return $this->groups; + } } diff --git a/src/TextUI/Configuration/Value/TestFileCollection.php b/src/TextUI/Configuration/Value/TestFileCollection.php index fcb30a3c60a..6d1ae27993c 100644 --- a/src/TextUI/Configuration/Value/TestFileCollection.php +++ b/src/TextUI/Configuration/Value/TestFileCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class TestFileCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $files; /** - * @psalm-param list $files + * @param list $files */ public static function fromArray(array $files): self { @@ -41,7 +41,7 @@ private function __construct(TestFile ...$files) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/TestFileCollectionIterator.php b/src/TextUI/Configuration/Value/TestFileCollectionIterator.php index 4efdfdd36f4..ed328e9ec36 100644 --- a/src/TextUI/Configuration/Value/TestFileCollectionIterator.php +++ b/src/TextUI/Configuration/Value/TestFileCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class TestFileCollectionIterator implements Countable, Iterator +final class TestFileCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $files; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(TestFileCollection $files) $this->files = $files->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/TestSuite.php b/src/TextUI/Configuration/Value/TestSuite.php index 17a158c2592..fdba72e0bad 100644 --- a/src/TextUI/Configuration/Value/TestSuite.php +++ b/src/TextUI/Configuration/Value/TestSuite.php @@ -12,12 +12,12 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class TestSuite { /** - * @psalm-var non-empty-string + * @var non-empty-string */ private string $name; private TestDirectoryCollection $directories; @@ -25,7 +25,7 @@ private FileCollection $exclude; /** - * @psalm-param non-empty-string $name + * @param non-empty-string $name */ public function __construct(string $name, TestDirectoryCollection $directories, TestFileCollection $files, FileCollection $exclude) { @@ -36,7 +36,7 @@ public function __construct(string $name, TestDirectoryCollection $directories, } /** - * @psalm-return non-empty-string + * @return non-empty-string */ public function name(): string { diff --git a/src/TextUI/Configuration/Value/TestSuiteCollection.php b/src/TextUI/Configuration/Value/TestSuiteCollection.php index ab4a1b329d6..26c9a645709 100644 --- a/src/TextUI/Configuration/Value/TestSuiteCollection.php +++ b/src/TextUI/Configuration/Value/TestSuiteCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class TestSuiteCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $testSuites; /** - * @psalm-param list $testSuites + * @param list $testSuites */ public static function fromArray(array $testSuites): self { @@ -41,7 +41,7 @@ private function __construct(TestSuite ...$testSuites) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/TestSuiteCollectionIterator.php b/src/TextUI/Configuration/Value/TestSuiteCollectionIterator.php index 89d35d9bb2c..d0b0768a48d 100644 --- a/src/TextUI/Configuration/Value/TestSuiteCollectionIterator.php +++ b/src/TextUI/Configuration/Value/TestSuiteCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class TestSuiteCollectionIterator implements Countable, Iterator +final class TestSuiteCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $testSuites; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(TestSuiteCollection $testSuites) $this->testSuites = $testSuites->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Value/Variable.php b/src/TextUI/Configuration/Value/Variable.php index 97d931074c4..cc0425c8c6d 100644 --- a/src/TextUI/Configuration/Value/Variable.php +++ b/src/TextUI/Configuration/Value/Variable.php @@ -12,7 +12,7 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Variable { diff --git a/src/TextUI/Configuration/Value/VariableCollection.php b/src/TextUI/Configuration/Value/VariableCollection.php index 3b7b9176d65..77fb4cff965 100644 --- a/src/TextUI/Configuration/Value/VariableCollection.php +++ b/src/TextUI/Configuration/Value/VariableCollection.php @@ -16,19 +16,19 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable * * @template-implements IteratorAggregate */ final readonly class VariableCollection implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $variables; /** - * @psalm-param list $variables + * @param list $variables */ public static function fromArray(array $variables): self { @@ -41,7 +41,7 @@ private function __construct(Variable ...$variables) } /** - * @psalm-return list + * @return list */ public function asArray(): array { diff --git a/src/TextUI/Configuration/Value/VariableCollectionIterator.php b/src/TextUI/Configuration/Value/VariableCollectionIterator.php index e9d6a355614..2e32194c17c 100644 --- a/src/TextUI/Configuration/Value/VariableCollectionIterator.php +++ b/src/TextUI/Configuration/Value/VariableCollectionIterator.php @@ -10,8 +10,6 @@ namespace PHPUnit\TextUI\Configuration; use function count; -use function iterator_count; -use Countable; use Iterator; /** @@ -19,10 +17,10 @@ * * @template-implements Iterator */ -final class VariableCollectionIterator implements Countable, Iterator +final class VariableCollectionIterator implements Iterator { /** - * @psalm-var list + * @var list */ private readonly array $variables; private int $position = 0; @@ -32,11 +30,6 @@ public function __construct(VariableCollection $variables) $this->variables = $variables->asArray(); } - public function count(): int - { - return iterator_count($this); - } - public function rewind(): void { $this->position = 0; diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php b/src/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php index 47b31daa0a3..c3975a8be52 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php @@ -19,9 +19,11 @@ use PHPUnit\TextUI\XmlConfiguration\Exception; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class CodeCoverage { @@ -73,7 +75,7 @@ public function disableCodeCoverageIgnore(): bool } /** - * @psalm-assert-if-true !null $this->clover + * @phpstan-assert-if-true !null $this->clover */ public function hasClover(): bool { @@ -95,7 +97,7 @@ public function clover(): Clover } /** - * @psalm-assert-if-true !null $this->cobertura + * @phpstan-assert-if-true !null $this->cobertura */ public function hasCobertura(): bool { @@ -117,7 +119,7 @@ public function cobertura(): Cobertura } /** - * @psalm-assert-if-true !null $this->crap4j + * @phpstan-assert-if-true !null $this->crap4j */ public function hasCrap4j(): bool { @@ -139,7 +141,7 @@ public function crap4j(): Crap4j } /** - * @psalm-assert-if-true !null $this->html + * @phpstan-assert-if-true !null $this->html */ public function hasHtml(): bool { @@ -161,7 +163,7 @@ public function html(): Html } /** - * @psalm-assert-if-true !null $this->php + * @phpstan-assert-if-true !null $this->php */ public function hasPhp(): bool { @@ -183,7 +185,7 @@ public function php(): Php } /** - * @psalm-assert-if-true !null $this->text + * @phpstan-assert-if-true !null $this->text */ public function hasText(): bool { @@ -205,7 +207,7 @@ public function text(): Text } /** - * @psalm-assert-if-true !null $this->xml + * @phpstan-assert-if-true !null $this->xml */ public function hasXml(): bool { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.php index dd5ca28d7d1..cdaf122e9c8 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Clover.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Clover { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.php index 19cb5dcef5e..015dba39407 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Cobertura.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Cobertura { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.php index 47ab2a81589..24aa66ddf98 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Crap4j.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Crap4j { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php index 91605b8fa81..dde8880f129 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Html.php @@ -13,9 +13,11 @@ use PHPUnit\TextUI\Configuration\NoCustomCssFileException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Html { @@ -83,7 +85,7 @@ public function colorDanger(): string } /** - * @psalm-assert-if-true !null $this->customCssFile + * @phpstan-assert-if-true !null $this->customCssFile */ public function hasCustomCssFile(): bool { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Php.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Php.php index 91c18333917..ae022e7a018 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Php.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Php.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Php { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Text.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Text.php index 4c4a3b8504a..cf04d9101d5 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Text.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Text.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Text { diff --git a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.php b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.php index 581f0efcd9c..62f99a061a7 100644 --- a/src/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.php +++ b/src/TextUI/Configuration/Xml/CodeCoverage/Report/Xml.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\Directory; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Xml { diff --git a/src/TextUI/Configuration/Xml/Configuration.php b/src/TextUI/Configuration/Xml/Configuration.php index c78d58f5e03..378022b0be1 100644 --- a/src/TextUI/Configuration/Xml/Configuration.php +++ b/src/TextUI/Configuration/Xml/Configuration.php @@ -17,20 +17,22 @@ use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -abstract class Configuration +abstract readonly class Configuration { - private readonly ExtensionBootstrapCollection $extensions; - private readonly Source $source; - private readonly CodeCoverage $codeCoverage; - private readonly Groups $groups; - private readonly Logging $logging; - private readonly Php $php; - private readonly PHPUnit $phpunit; - private readonly TestSuiteCollection $testSuite; + private ExtensionBootstrapCollection $extensions; + private Source $source; + private CodeCoverage $codeCoverage; + private Groups $groups; + private Logging $logging; + private Php $php; + private PHPUnit $phpunit; + private TestSuiteCollection $testSuite; public function __construct(ExtensionBootstrapCollection $extensions, Source $source, CodeCoverage $codeCoverage, Groups $groups, Logging $logging, Php $php, PHPUnit $phpunit, TestSuiteCollection $testSuite) { @@ -85,7 +87,7 @@ public function testSuite(): TestSuiteCollection } /** - * @psalm-assert-if-true DefaultConfiguration $this + * @phpstan-assert-if-true DefaultConfiguration $this */ public function isDefault(): bool { @@ -93,7 +95,7 @@ public function isDefault(): bool } /** - * @psalm-assert-if-true LoadedFromFileConfiguration $this + * @phpstan-assert-if-true LoadedFromFileConfiguration $this */ public function wasLoadedFromFile(): bool { diff --git a/src/TextUI/Configuration/Xml/DefaultConfiguration.php b/src/TextUI/Configuration/Xml/DefaultConfiguration.php index 3f44fbbeac9..1c900cf5cb5 100644 --- a/src/TextUI/Configuration/Xml/DefaultConfiguration.php +++ b/src/TextUI/Configuration/Xml/DefaultConfiguration.php @@ -25,11 +25,13 @@ use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class DefaultConfiguration extends Configuration +final readonly class DefaultConfiguration extends Configuration { public static function create(): self { @@ -51,6 +53,12 @@ public static function create(): self false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ), new CodeCoverage( @@ -103,6 +111,9 @@ public static function create(): self false, false, false, + false, + false, + false, null, false, false, @@ -121,6 +132,9 @@ public static function create(): self false, false, false, + false, + false, + false, null, false, false, @@ -141,6 +155,7 @@ public static function create(): self false, false, 100, + 10, ), TestSuiteCollection::fromArray([]), ); diff --git a/src/TextUI/Configuration/Xml/Exception.php b/src/TextUI/Configuration/Xml/Exception.php index 162b37e8814..60c3c9acc71 100644 --- a/src/TextUI/Configuration/Xml/Exception.php +++ b/src/TextUI/Configuration/Xml/Exception.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Exception extends RuntimeException implements \PHPUnit\Exception diff --git a/src/TextUI/Configuration/Xml/Generator.php b/src/TextUI/Configuration/Xml/Generator.php index 39e7095feed..4fc4ca2e69b 100644 --- a/src/TextUI/Configuration/Xml/Generator.php +++ b/src/TextUI/Configuration/Xml/Generator.php @@ -12,23 +12,27 @@ use function str_replace; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Generator +final readonly class Generator { /** * @var string */ - private const TEMPLATE = <<<'EOT' + private const string TEMPLATE = <<<'EOT' @@ -37,7 +41,7 @@ final class Generator - + {src_directory} @@ -46,18 +50,18 @@ final class Generator EOT; - public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory): string + public function generateDefaultConfiguration(string $schemaLocation, string $bootstrapScript, string $testsDirectory, string $srcDirectory, string $cacheDirectory): string { return str_replace( [ - '{phpunit_version}', + '{schema_location}', '{bootstrap_script}', '{tests_directory}', '{src_directory}', '{cache_directory}', ], [ - $phpunitVersion, + $schemaLocation, $bootstrapScript, $testsDirectory, $srcDirectory, diff --git a/src/TextUI/Configuration/Xml/Groups.php b/src/TextUI/Configuration/Xml/Groups.php index b51d7504b7f..1a7cc6b453f 100644 --- a/src/TextUI/Configuration/Xml/Groups.php +++ b/src/TextUI/Configuration/Xml/Groups.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\GroupCollection; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Groups { diff --git a/src/TextUI/Configuration/Xml/LoadedFromFileConfiguration.php b/src/TextUI/Configuration/Xml/LoadedFromFileConfiguration.php index cf7274d636b..e69d137fe2c 100644 --- a/src/TextUI/Configuration/Xml/LoadedFromFileConfiguration.php +++ b/src/TextUI/Configuration/Xml/LoadedFromFileConfiguration.php @@ -17,15 +17,23 @@ use PHPUnit\TextUI\XmlConfiguration\Logging\Logging; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class LoadedFromFileConfiguration extends Configuration +final readonly class LoadedFromFileConfiguration extends Configuration { - private readonly string $filename; - private readonly ValidationResult $validationResult; + /** + * @var non-empty-string + */ + private string $filename; + private ValidationResult $validationResult; + /** + * @param non-empty-string $filename + */ public function __construct(string $filename, ValidationResult $validationResult, ExtensionBootstrapCollection $extensions, Source $source, CodeCoverage $codeCoverage, Groups $groups, Logging $logging, Php $php, PHPUnit $phpunit, TestSuiteCollection $testSuite) { $this->filename = $filename; @@ -43,6 +51,9 @@ public function __construct(string $filename, ValidationResult $validationResult ); } + /** + * @return non-empty-string + */ public function filename(): string { return $this->filename; diff --git a/src/TextUI/Configuration/Xml/Loader.php b/src/TextUI/Configuration/Xml/Loader.php index 1871cc00cdb..9211e0be2fa 100644 --- a/src/TextUI/Configuration/Xml/Loader.php +++ b/src/TextUI/Configuration/Xml/Loader.php @@ -26,9 +26,12 @@ use function trim; use DOMDocument; use DOMElement; +use DOMNode; +use DOMNodeList; use DOMXPath; use PHPUnit\Runner\TestSuiteSorter; use PHPUnit\Runner\Version; +use PHPUnit\TextUI\Configuration\Configuration; use PHPUnit\TextUI\Configuration\Constant; use PHPUnit\TextUI\Configuration\ConstantCollection; use PHPUnit\TextUI\Configuration\Directory; @@ -73,9 +76,11 @@ use SebastianBergmann\CodeCoverage\Report\Thresholds; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Loader +final readonly class Loader { /** * @throws Exception @@ -106,6 +111,8 @@ public function load(string $filename): LoadedFromFileConfiguration $configurationFileRealpath = realpath($filename); + assert($configurationFileRealpath !== false && $configurationFileRealpath !== ''); + return new LoadedFromFileConfiguration( $configurationFileRealpath, (new Validator)->validate($document, $xsdFilename), @@ -125,12 +132,12 @@ private function logging(string $filename, DOMXPath $xpath): Logging $junit = null; $element = $this->element($xpath, 'logging/junit'); - if ($element) { + if ($element !== null) { $junit = new Junit( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -139,12 +146,12 @@ private function logging(string $filename, DOMXPath $xpath): Logging $teamCity = null; $element = $this->element($xpath, 'logging/teamcity'); - if ($element) { + if ($element !== null) { $teamCity = new TeamCity( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -153,12 +160,12 @@ private function logging(string $filename, DOMXPath $xpath): Logging $testDoxHtml = null; $element = $this->element($xpath, 'logging/testdoxHtml'); - if ($element) { + if ($element !== null) { $testDoxHtml = new TestDoxHtml( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -167,12 +174,12 @@ private function logging(string $filename, DOMXPath $xpath): Logging $testDoxText = null; $element = $this->element($xpath, 'logging/testdoxText'); - if ($element) { + if ($element !== null) { $testDoxText = new TestDoxText( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -190,19 +197,31 @@ private function extensions(DOMXPath $xpath): ExtensionBootstrapCollection { $extensionBootstrappers = []; - foreach ($xpath->query('extensions/bootstrap') as $bootstrap) { + $bootstrapNodes = $xpath->query('extensions/bootstrap'); + + assert($bootstrapNodes instanceof DOMNodeList); + + foreach ($bootstrapNodes as $bootstrap) { assert($bootstrap instanceof DOMElement); $parameters = []; - foreach ($xpath->query('parameter', $bootstrap) as $parameter) { + $parameterNodes = $xpath->query('parameter', $bootstrap); + + assert($parameterNodes instanceof DOMNodeList); + + foreach ($parameterNodes as $parameter) { assert($parameter instanceof DOMElement); $parameters[$parameter->getAttribute('name')] = $parameter->getAttribute('value'); } + $className = $bootstrap->getAttribute('class'); + + assert($className !== ''); + $extensionBootstrappers[] = new ExtensionBootstrap( - $bootstrap->getAttribute('class'), + $className, $parameters, ); } @@ -211,7 +230,7 @@ private function extensions(DOMXPath $xpath): ExtensionBootstrapCollection } /** - * @psalm-return non-empty-string + * @return non-empty-string */ private function toAbsolutePath(string $filename, string $path): string { @@ -230,7 +249,7 @@ private function toAbsolutePath(string $filename, string $path): string // - C:/windows // - c:/windows if (defined('PHP_WINDOWS_VERSION_BUILD') && - !empty($path) && + $path !== '' && ($path[0] === '\\' || (strlen($path) >= 3 && preg_match('#^[A-Z]:[/\\\]#i', substr($path, 0, 3))))) { return $path; } @@ -245,7 +264,6 @@ private function toAbsolutePath(string $filename, string $path): string private function source(string $filename, DOMXPath $xpath): Source { $baseline = null; - $restrictDeprecations = false; $restrictNotices = false; $restrictWarnings = false; $ignoreSuppressionOfDeprecations = false; @@ -255,26 +273,56 @@ private function source(string $filename, DOMXPath $xpath): Source $ignoreSuppressionOfPhpNotices = false; $ignoreSuppressionOfWarnings = false; $ignoreSuppressionOfPhpWarnings = false; + $ignoreSelfDeprecations = false; + $ignoreDirectDeprecations = false; + $ignoreIndirectDeprecations = false; $element = $this->element($xpath, 'source'); - if ($element) { - $baseline = $this->getStringAttribute($element, 'baseline'); + if ($element !== null) { + $baseline = $this->parseStringAttribute($element, 'baseline'); if ($baseline !== null) { $baseline = $this->toAbsolutePath($filename, $baseline); } - $restrictDeprecations = $this->getBooleanAttribute($element, 'restrictDeprecations', false); - $restrictNotices = $this->getBooleanAttribute($element, 'restrictNotices', false); - $restrictWarnings = $this->getBooleanAttribute($element, 'restrictWarnings', false); - $ignoreSuppressionOfDeprecations = $this->getBooleanAttribute($element, 'ignoreSuppressionOfDeprecations', false); - $ignoreSuppressionOfPhpDeprecations = $this->getBooleanAttribute($element, 'ignoreSuppressionOfPhpDeprecations', false); - $ignoreSuppressionOfErrors = $this->getBooleanAttribute($element, 'ignoreSuppressionOfErrors', false); - $ignoreSuppressionOfNotices = $this->getBooleanAttribute($element, 'ignoreSuppressionOfNotices', false); - $ignoreSuppressionOfPhpNotices = $this->getBooleanAttribute($element, 'ignoreSuppressionOfPhpNotices', false); - $ignoreSuppressionOfWarnings = $this->getBooleanAttribute($element, 'ignoreSuppressionOfWarnings', false); - $ignoreSuppressionOfPhpWarnings = $this->getBooleanAttribute($element, 'ignoreSuppressionOfPhpWarnings', false); + $restrictNotices = $this->parseBooleanAttribute($element, 'restrictNotices', false); + $restrictWarnings = $this->parseBooleanAttribute($element, 'restrictWarnings', false); + $ignoreSuppressionOfDeprecations = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfDeprecations', false); + $ignoreSuppressionOfPhpDeprecations = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfPhpDeprecations', false); + $ignoreSuppressionOfErrors = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfErrors', false); + $ignoreSuppressionOfNotices = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfNotices', false); + $ignoreSuppressionOfPhpNotices = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfPhpNotices', false); + $ignoreSuppressionOfWarnings = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfWarnings', false); + $ignoreSuppressionOfPhpWarnings = $this->parseBooleanAttribute($element, 'ignoreSuppressionOfPhpWarnings', false); + $ignoreSelfDeprecations = $this->parseBooleanAttribute($element, 'ignoreSelfDeprecations', false); + $ignoreDirectDeprecations = $this->parseBooleanAttribute($element, 'ignoreDirectDeprecations', false); + $ignoreIndirectDeprecations = $this->parseBooleanAttribute($element, 'ignoreIndirectDeprecations', false); + } + + $deprecationTriggers = [ + 'functions' => [], + 'methods' => [], + ]; + + $functionNodes = $xpath->query('source/deprecationTrigger/function'); + + assert($functionNodes instanceof DOMNodeList); + + foreach ($functionNodes as $functionNode) { + assert($functionNode instanceof DOMElement); + + $deprecationTriggers['functions'][] = $functionNode->textContent; + } + + $methodNodes = $xpath->query('source/deprecationTrigger/method'); + + assert($methodNodes instanceof DOMNodeList); + + foreach ($methodNodes as $methodNode) { + assert($methodNode instanceof DOMElement); + + $deprecationTriggers['methods'][] = $methodNode->textContent; } return new Source( @@ -284,7 +332,6 @@ private function source(string $filename, DOMXPath $xpath): Source $this->readFilterFiles($filename, $xpath, 'source/include/file'), $this->readFilterDirectories($filename, $xpath, 'source/exclude/directory'), $this->readFilterFiles($filename, $xpath, 'source/exclude/file'), - $restrictDeprecations, $restrictNotices, $restrictWarnings, $ignoreSuppressionOfDeprecations, @@ -294,6 +341,10 @@ private function source(string $filename, DOMXPath $xpath): Source $ignoreSuppressionOfPhpNotices, $ignoreSuppressionOfWarnings, $ignoreSuppressionOfPhpWarnings, + $deprecationTriggers, + $ignoreSelfDeprecations, + $ignoreDirectDeprecations, + $ignoreIndirectDeprecations, ); } @@ -306,26 +357,26 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage $element = $this->element($xpath, 'coverage'); - if ($element) { - $pathCoverage = $this->getBooleanAttribute( + if ($element !== null) { + $pathCoverage = $this->parseBooleanAttribute( $element, 'pathCoverage', false, ); - $includeUncoveredFiles = $this->getBooleanAttribute( + $includeUncoveredFiles = $this->parseBooleanAttribute( $element, 'includeUncoveredFiles', true, ); - $ignoreDeprecatedCodeUnits = $this->getBooleanAttribute( + $ignoreDeprecatedCodeUnits = $this->parseBooleanAttribute( $element, 'ignoreDeprecatedCodeUnits', false, ); - $disableCodeCoverageIgnore = $this->getBooleanAttribute( + $disableCodeCoverageIgnore = $this->parseBooleanAttribute( $element, 'disableCodeCoverageIgnore', false, @@ -335,12 +386,12 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage $clover = null; $element = $this->element($xpath, 'coverage/report/clover'); - if ($element) { + if ($element !== null) { $clover = new Clover( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -349,12 +400,12 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage $cobertura = null; $element = $this->element($xpath, 'coverage/report/cobertura'); - if ($element) { + if ($element !== null) { $cobertura = new Cobertura( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -363,22 +414,22 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage $crap4j = null; $element = $this->element($xpath, 'coverage/report/crap4j'); - if ($element) { + if ($element !== null) { $crap4j = new Crap4j( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), - $this->getIntegerAttribute($element, 'threshold', 30), + $this->parseIntegerAttribute($element, 'threshold', 30), ); } $html = null; $element = $this->element($xpath, 'coverage/report/html'); - if ($element) { + if ($element !== null) { $defaultColors = Colors::default(); $defaultThresholds = Thresholds::default(); @@ -386,29 +437,29 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage new Directory( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputDirectory'), + (string) $this->parseStringAttribute($element, 'outputDirectory'), ), ), - $this->getIntegerAttribute($element, 'lowUpperBound', $defaultThresholds->lowUpperBound()), - $this->getIntegerAttribute($element, 'highLowerBound', $defaultThresholds->highLowerBound()), - $this->getStringAttributeWithDefault($element, 'colorSuccessLow', $defaultColors->successLow()), - $this->getStringAttributeWithDefault($element, 'colorSuccessMedium', $defaultColors->successMedium()), - $this->getStringAttributeWithDefault($element, 'colorSuccessHigh', $defaultColors->successHigh()), - $this->getStringAttributeWithDefault($element, 'colorWarning', $defaultColors->warning()), - $this->getStringAttributeWithDefault($element, 'colorDanger', $defaultColors->danger()), - $this->getStringAttribute($element, 'customCssFile'), + $this->parseIntegerAttribute($element, 'lowUpperBound', $defaultThresholds->lowUpperBound()), + $this->parseIntegerAttribute($element, 'highLowerBound', $defaultThresholds->highLowerBound()), + $this->parseStringAttributeWithDefault($element, 'colorSuccessLow', $defaultColors->successLow()), + $this->parseStringAttributeWithDefault($element, 'colorSuccessMedium', $defaultColors->successMedium()), + $this->parseStringAttributeWithDefault($element, 'colorSuccessHigh', $defaultColors->successHigh()), + $this->parseStringAttributeWithDefault($element, 'colorWarning', $defaultColors->warning()), + $this->parseStringAttributeWithDefault($element, 'colorDanger', $defaultColors->danger()), + $this->parseStringAttribute($element, 'customCssFile'), ); } $php = null; $element = $this->element($xpath, 'coverage/report/php'); - if ($element) { + if ($element !== null) { $php = new CodeCoveragePhp( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), ); @@ -417,28 +468,28 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage $text = null; $element = $this->element($xpath, 'coverage/report/text'); - if ($element) { + if ($element !== null) { $text = new CodeCoverageText( new File( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputFile'), + (string) $this->parseStringAttribute($element, 'outputFile'), ), ), - $this->getBooleanAttribute($element, 'showUncoveredFiles', false), - $this->getBooleanAttribute($element, 'showOnlySummary', false), + $this->parseBooleanAttribute($element, 'showUncoveredFiles', false), + $this->parseBooleanAttribute($element, 'showOnlySummary', false), ); } $xml = null; $element = $this->element($xpath, 'coverage/report/xml'); - if ($element) { + if ($element !== null) { $xml = new CodeCoverageXml( new Directory( $this->toAbsolutePath( $filename, - (string) $this->getStringAttribute($element, 'outputDirectory'), + (string) $this->parseStringAttribute($element, 'outputDirectory'), ), ), ); @@ -459,7 +510,7 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage ); } - private function getBoolean(string $value, bool|string $default): bool|string + private function booleanFromString(string $value, bool $default): bool { if (strtolower($value) === 'false') { return false; @@ -472,16 +523,33 @@ private function getBoolean(string $value, bool|string $default): bool|string return $default; } + private function valueFromString(string $value): bool|string + { + if (strtolower($value) === 'false') { + return false; + } + + if (strtolower($value) === 'true') { + return true; + } + + return $value; + } + private function readFilterDirectories(string $filename, DOMXPath $xpath, string $query): FilterDirectoryCollection { $directories = []; - foreach ($xpath->query($query) as $directoryNode) { + $directoryNodes = $xpath->query($query); + + assert($directoryNodes instanceof DOMNodeList); + + foreach ($directoryNodes as $directoryNode) { assert($directoryNode instanceof DOMElement); $directoryPath = $directoryNode->textContent; - if (!$directoryPath) { + if ($directoryPath === '') { continue; } @@ -499,10 +567,16 @@ private function readFilterFiles(string $filename, DOMXPath $xpath, string $quer { $files = []; - foreach ($xpath->query($query) as $file) { - $filePath = $file->textContent; + $fileNodes = $xpath->query($query); + + assert($fileNodes instanceof DOMNodeList); - if ($filePath) { + foreach ($fileNodes as $fileNode) { + assert($fileNode instanceof DOMNode); + + $filePath = $fileNode->textContent; + + if ($filePath !== '') { $files[] = new File($this->toAbsolutePath($filename, $filePath)); } } @@ -515,12 +589,24 @@ private function groups(DOMXPath $xpath): Groups $include = []; $exclude = []; - foreach ($xpath->query('groups/include/group') as $group) { - $include[] = new Group($group->textContent); + $groupNodes = $xpath->query('groups/include/group'); + + assert($groupNodes instanceof DOMNodeList); + + foreach ($groupNodes as $groupNode) { + assert($groupNode instanceof DOMNode); + + $include[] = new Group($groupNode->textContent); } - foreach ($xpath->query('groups/exclude/group') as $group) { - $exclude[] = new Group($group->textContent); + $groupNodes = $xpath->query('groups/exclude/group'); + + assert($groupNodes instanceof DOMNodeList); + + foreach ($groupNodes as $groupNode) { + assert($groupNode instanceof DOMNode); + + $exclude[] = new Group($groupNode->textContent); } return new Groups( @@ -529,31 +615,31 @@ private function groups(DOMXPath $xpath): Groups ); } - private function getBooleanAttribute(DOMElement $element, string $attribute, bool $default): bool + private function parseBooleanAttribute(DOMElement $element, string $attribute, bool $default): bool { if (!$element->hasAttribute($attribute)) { return $default; } - return (bool) $this->getBoolean( + return $this->booleanFromString( $element->getAttribute($attribute), false, ); } - private function getIntegerAttribute(DOMElement $element, string $attribute, int $default): int + private function parseIntegerAttribute(DOMElement $element, string $attribute, int $default): int { if (!$element->hasAttribute($attribute)) { return $default; } - return $this->getInteger( + return $this->parseInteger( $element->getAttribute($attribute), $default, ); } - private function getStringAttribute(DOMElement $element, string $attribute): ?string + private function parseStringAttribute(DOMElement $element, string $attribute): ?string { if (!$element->hasAttribute($attribute)) { return null; @@ -562,7 +648,7 @@ private function getStringAttribute(DOMElement $element, string $attribute): ?st return $element->getAttribute($attribute); } - private function getStringAttributeWithDefault(DOMElement $element, string $attribute, string $default): string + private function parseStringAttributeWithDefault(DOMElement $element, string $attribute, string $default): string { if (!$element->hasAttribute($attribute)) { return $default; @@ -571,7 +657,7 @@ private function getStringAttributeWithDefault(DOMElement $element, string $attr return $element->getAttribute($attribute); } - private function getInteger(string $value, int $default): int + private function parseInteger(string $value, int $default): int { if (is_numeric($value)) { return (int) $value; @@ -584,17 +670,27 @@ private function php(string $filename, DOMXPath $xpath): Php { $includePaths = []; - foreach ($xpath->query('php/includePath') as $includePath) { + $includePathNodes = $xpath->query('php/includePath'); + + assert($includePathNodes instanceof DOMNodeList); + + foreach ($includePathNodes as $includePath) { + assert($includePath instanceof DOMNode); + $path = $includePath->textContent; - if ($path) { + if ($path !== '') { $includePaths[] = new Directory($this->toAbsolutePath($filename, $path)); } } $iniSettings = []; - foreach ($xpath->query('php/ini') as $ini) { + $iniNodes = $xpath->query('php/ini'); + + assert($iniNodes instanceof DOMNodeList); + + foreach ($iniNodes as $ini) { assert($ini instanceof DOMElement); $iniSettings[] = new IniSetting( @@ -605,14 +701,18 @@ private function php(string $filename, DOMXPath $xpath): Php $constants = []; - foreach ($xpath->query('php/const') as $const) { - assert($const instanceof DOMElement); + $constNodes = $xpath->query('php/const'); - $value = $const->getAttribute('value'); + assert($constNodes instanceof DOMNodeList); + + foreach ($constNodes as $constNode) { + assert($constNode instanceof DOMElement); + + $value = $constNode->getAttribute('value'); $constants[] = new Constant( - $const->getAttribute('name'), - $this->getBoolean($value, $value), + $constNode->getAttribute('name'), + $this->valueFromString($value), ); } @@ -628,7 +728,11 @@ private function php(string $filename, DOMXPath $xpath): Php ]; foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { - foreach ($xpath->query('php/' . $array) as $var) { + $varNodes = $xpath->query('php/' . $array); + + assert($varNodes instanceof DOMNodeList); + + foreach ($varNodes as $var) { assert($var instanceof DOMElement); $name = $var->getAttribute('name'); @@ -637,15 +741,15 @@ private function php(string $filename, DOMXPath $xpath): Php $verbatim = false; if ($var->hasAttribute('force')) { - $force = (bool) $this->getBoolean($var->getAttribute('force'), false); + $force = $this->booleanFromString($var->getAttribute('force'), false); } if ($var->hasAttribute('verbatim')) { - $verbatim = $this->getBoolean($var->getAttribute('verbatim'), false); + $verbatim = $this->booleanFromString($var->getAttribute('verbatim'), false); } if (!$verbatim) { - $value = $this->getBoolean($value, $value); + $value = $this->valueFromString($value); } $variables[$array][] = new Variable($name, $value, $force); @@ -671,7 +775,7 @@ private function phpunit(string $filename, DOMDocument $document): PHPUnit { $executionOrder = TestSuiteSorter::ORDER_DEFAULT; $defectsFirst = false; - $resolveDependencies = $this->getBooleanAttribute($document->documentElement, 'resolveDependencies', true); + $resolveDependencies = $this->parseBooleanAttribute($document->documentElement, 'resolveDependencies', true); if ($document->documentElement->hasAttribute('executionOrder')) { foreach (explode(',', $document->documentElement->getAttribute('executionOrder')) as $order) { @@ -721,19 +825,19 @@ private function phpunit(string $filename, DOMDocument $document): PHPUnit } } - $cacheDirectory = $this->getStringAttribute($document->documentElement, 'cacheDirectory'); + $cacheDirectory = $this->parseStringAttribute($document->documentElement, 'cacheDirectory'); if ($cacheDirectory !== null) { $cacheDirectory = $this->toAbsolutePath($filename, $cacheDirectory); } - $bootstrap = $this->getStringAttribute($document->documentElement, 'bootstrap'); + $bootstrap = $this->parseStringAttribute($document->documentElement, 'bootstrap'); if ($bootstrap !== null) { $bootstrap = $this->toAbsolutePath($filename, $bootstrap); } - $extensionsDirectory = $this->getStringAttribute($document->documentElement, 'extensionsDirectory'); + $extensionsDirectory = $this->parseStringAttribute($document->documentElement, 'extensionsDirectory'); if ($extensionsDirectory !== null) { $extensionsDirectory = $this->toAbsolutePath($filename, $extensionsDirectory); @@ -742,94 +846,107 @@ private function phpunit(string $filename, DOMDocument $document): PHPUnit $backupStaticProperties = false; if ($document->documentElement->hasAttribute('backupStaticProperties')) { - $backupStaticProperties = $this->getBooleanAttribute($document->documentElement, 'backupStaticProperties', false); + $backupStaticProperties = $this->parseBooleanAttribute($document->documentElement, 'backupStaticProperties', false); } $requireCoverageMetadata = false; if ($document->documentElement->hasAttribute('requireCoverageMetadata')) { - $requireCoverageMetadata = $this->getBooleanAttribute($document->documentElement, 'requireCoverageMetadata', false); + $requireCoverageMetadata = $this->parseBooleanAttribute($document->documentElement, 'requireCoverageMetadata', false); } $beStrictAboutCoverageMetadata = false; if ($document->documentElement->hasAttribute('beStrictAboutCoverageMetadata')) { - $beStrictAboutCoverageMetadata = $this->getBooleanAttribute($document->documentElement, 'beStrictAboutCoverageMetadata', false); + $beStrictAboutCoverageMetadata = $this->parseBooleanAttribute($document->documentElement, 'beStrictAboutCoverageMetadata', false); + } + + $shortenArraysForExportThreshold = $this->parseIntegerAttribute($document->documentElement, 'shortenArraysForExportThreshold', 10); + + if ($shortenArraysForExportThreshold < 0) { + $shortenArraysForExportThreshold = 0; } return new PHPUnit( $cacheDirectory, - $this->getBooleanAttribute($document->documentElement, 'cacheResult', true), - $this->getColumns($document), - $this->getColors($document), - $this->getBooleanAttribute($document->documentElement, 'stderr', false), - $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnIncompleteTests', false), - $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnSkippedTests', false), - $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerDeprecations', false), - $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerErrors', false), - $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerNotices', false), - $this->getBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerWarnings', false), - $this->getBooleanAttribute($document->documentElement, 'reverseDefectList', false), + $this->parseBooleanAttribute($document->documentElement, 'cacheResult', true), + $this->parseColumns($document), + $this->parseColors($document), + $this->parseBooleanAttribute($document->documentElement, 'stderr', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnAllIssues', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnIncompleteTests', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnSkippedTests', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerDeprecations', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnPhpunitDeprecations', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnPhpunitNotices', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerErrors', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerNotices', false), + $this->parseBooleanAttribute($document->documentElement, 'displayDetailsOnTestsThatTriggerWarnings', false), + $this->parseBooleanAttribute($document->documentElement, 'reverseDefectList', false), $requireCoverageMetadata, $bootstrap, - $this->getBooleanAttribute($document->documentElement, 'processIsolation', false), - $this->getBooleanAttribute($document->documentElement, 'failOnDeprecation', false), - $this->getBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', false), - $this->getBooleanAttribute($document->documentElement, 'failOnIncomplete', false), - $this->getBooleanAttribute($document->documentElement, 'failOnNotice', false), - $this->getBooleanAttribute($document->documentElement, 'failOnRisky', false), - $this->getBooleanAttribute($document->documentElement, 'failOnSkipped', false), - $this->getBooleanAttribute($document->documentElement, 'failOnWarning', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnDefect', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnDeprecation', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnError', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnFailure', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnIncomplete', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnNotice', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnRisky', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnSkipped', false), - $this->getBooleanAttribute($document->documentElement, 'stopOnWarning', false), + $this->parseBooleanAttribute($document->documentElement, 'processIsolation', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnAllIssues', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnDeprecation', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnPhpunitDeprecation', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnPhpunitNotice', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnEmptyTestSuite', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnIncomplete', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnNotice', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnRisky', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnSkipped', false), + $this->parseBooleanAttribute($document->documentElement, 'failOnWarning', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnDefect', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnDeprecation', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnError', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnFailure', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnIncomplete', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnNotice', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnRisky', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnSkipped', false), + $this->parseBooleanAttribute($document->documentElement, 'stopOnWarning', false), $extensionsDirectory, - $this->getBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', false), - $this->getBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', false), - $this->getBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', true), + $this->parseBooleanAttribute($document->documentElement, 'beStrictAboutChangesToGlobalState', false), + $this->parseBooleanAttribute($document->documentElement, 'beStrictAboutOutputDuringTests', false), + $this->parseBooleanAttribute($document->documentElement, 'beStrictAboutTestsThatDoNotTestAnything', true), $beStrictAboutCoverageMetadata, - $this->getBooleanAttribute($document->documentElement, 'enforceTimeLimit', false), - $this->getIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), - $this->getIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), - $this->getIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), - $this->getIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), - $this->getStringAttribute($document->documentElement, 'defaultTestSuite'), + $this->parseBooleanAttribute($document->documentElement, 'enforceTimeLimit', false), + $this->parseIntegerAttribute($document->documentElement, 'defaultTimeLimit', 1), + $this->parseIntegerAttribute($document->documentElement, 'timeoutForSmallTests', 1), + $this->parseIntegerAttribute($document->documentElement, 'timeoutForMediumTests', 10), + $this->parseIntegerAttribute($document->documentElement, 'timeoutForLargeTests', 60), + $this->parseStringAttribute($document->documentElement, 'defaultTestSuite'), $executionOrder, $resolveDependencies, $defectsFirst, - $this->getBooleanAttribute($document->documentElement, 'backupGlobals', false), + $this->parseBooleanAttribute($document->documentElement, 'backupGlobals', false), $backupStaticProperties, - $this->getBooleanAttribute($document->documentElement, 'registerMockObjectsFromTestArgumentsRecursively', false), - $this->getBooleanAttribute($document->documentElement, 'testdox', false), - $this->getBooleanAttribute($document->documentElement, 'controlGarbageCollector', false), - $this->getIntegerAttribute($document->documentElement, 'numberOfTestsBeforeGarbageCollection', 100), + $this->parseBooleanAttribute($document->documentElement, 'testdox', false), + $this->parseBooleanAttribute($document->documentElement, 'testdoxSummary', false), + $this->parseBooleanAttribute($document->documentElement, 'controlGarbageCollector', false), + $this->parseIntegerAttribute($document->documentElement, 'numberOfTestsBeforeGarbageCollection', 100), + $shortenArraysForExportThreshold, ); } - private function getColors(DOMDocument $document): string + private function parseColors(DOMDocument $document): string { - $colors = \PHPUnit\TextUI\Configuration\Configuration::COLOR_DEFAULT; + $colors = Configuration::COLOR_DEFAULT; if ($document->documentElement->hasAttribute('colors')) { /* only allow boolean for compatibility with previous versions 'always' only allowed from command line */ - if ($this->getBoolean($document->documentElement->getAttribute('colors'), false)) { - $colors = \PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO; + if ($this->booleanFromString($document->documentElement->getAttribute('colors'), false)) { + $colors = Configuration::COLOR_AUTO; } else { - $colors = \PHPUnit\TextUI\Configuration\Configuration::COLOR_NEVER; + $colors = Configuration::COLOR_NEVER; } } return $colors; } - private function getColumns(DOMDocument $document): int|string + private function parseColumns(DOMDocument $document): int|string { $columns = 80; @@ -837,7 +954,7 @@ private function getColumns(DOMDocument $document): int|string $columns = $document->documentElement->getAttribute('columns'); if ($columns !== 'max') { - $columns = $this->getInteger($columns, 80); + $columns = $this->parseInteger($columns, 80); } } @@ -848,13 +965,13 @@ private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollecti { $testSuites = []; - foreach ($this->getTestSuiteElements($xpath) as $element) { + foreach ($this->parseTestSuiteElements($xpath) as $element) { $exclude = []; foreach ($element->getElementsByTagName('exclude') as $excludeNode) { $excludeFile = $excludeNode->textContent; - if ($excludeFile) { + if ($excludeFile !== '') { $exclude[] = new File($this->toAbsolutePath($filename, $excludeFile)); } } @@ -866,7 +983,7 @@ private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollecti $directory = $directoryNode->textContent; - if (empty($directory)) { + if ($directory === '') { continue; } @@ -894,12 +1011,27 @@ private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollecti $phpVersionOperator = new VersionComparisonOperator($directoryNode->getAttribute('phpVersionOperator')); } + $groups = []; + + if ($directoryNode->hasAttribute('groups')) { + foreach (explode(',', $directoryNode->getAttribute('groups')) as $group) { + $group = trim($group); + + if ($group === '') { + continue; + } + + $groups[] = $group; + } + } + $directories[] = new TestDirectory( $this->toAbsolutePath($filename, $directory), $prefix, $suffix, $phpVersion, $phpVersionOperator, + $groups, ); } @@ -910,7 +1042,7 @@ private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollecti $file = $fileNode->textContent; - if (empty($file)) { + if ($file === '') { continue; } @@ -926,16 +1058,31 @@ private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollecti $phpVersionOperator = new VersionComparisonOperator($fileNode->getAttribute('phpVersionOperator')); } + $groups = []; + + if ($fileNode->hasAttribute('groups')) { + foreach (explode(',', $fileNode->getAttribute('groups')) as $group) { + $group = trim($group); + + if ($group === '') { + continue; + } + + $groups[] = $group; + } + } + $files[] = new TestFile( $this->toAbsolutePath($filename, $file), $phpVersion, $phpVersionOperator, + $groups, ); } $name = $element->getAttribute('name'); - assert(!empty($name)); + assert($name !== ''); $testSuites[] = new TestSuiteConfiguration( $name, @@ -949,16 +1096,20 @@ private function testSuite(string $filename, DOMXPath $xpath): TestSuiteCollecti } /** - * @psalm-return list + * @return list */ - private function getTestSuiteElements(DOMXPath $xpath): array + private function parseTestSuiteElements(DOMXPath $xpath): array { $elements = []; $testSuiteNodes = $xpath->query('testsuites/testsuite'); + assert($testSuiteNodes instanceof DOMNodeList); + if ($testSuiteNodes->length === 0) { $testSuiteNodes = $xpath->query('testsuite'); + + assert($testSuiteNodes instanceof DOMNodeList); } if ($testSuiteNodes->length === 1) { @@ -982,6 +1133,8 @@ private function element(DOMXPath $xpath, string $element): ?DOMElement { $nodes = $xpath->query($element); + assert($nodes instanceof DOMNodeList); + if ($nodes->length === 1) { $node = $nodes->item(0); diff --git a/src/TextUI/Configuration/Xml/Logging/Junit.php b/src/TextUI/Configuration/Xml/Logging/Junit.php index 36b24109987..cf9878dfd25 100644 --- a/src/TextUI/Configuration/Xml/Logging/Junit.php +++ b/src/TextUI/Configuration/Xml/Logging/Junit.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Junit { diff --git a/src/TextUI/Configuration/Xml/Logging/Logging.php b/src/TextUI/Configuration/Xml/Logging/Logging.php index b2dc9af42bf..a37dea55cc7 100644 --- a/src/TextUI/Configuration/Xml/Logging/Logging.php +++ b/src/TextUI/Configuration/Xml/Logging/Logging.php @@ -14,9 +14,11 @@ use PHPUnit\TextUI\XmlConfiguration\Logging\TestDox\Text as TestDoxText; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Logging { diff --git a/src/TextUI/Configuration/Xml/Logging/TeamCity.php b/src/TextUI/Configuration/Xml/Logging/TeamCity.php index 8c5607cf9a7..daf1ceccfd7 100644 --- a/src/TextUI/Configuration/Xml/Logging/TeamCity.php +++ b/src/TextUI/Configuration/Xml/Logging/TeamCity.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class TeamCity { diff --git a/src/TextUI/Configuration/Xml/Logging/TestDox/Html.php b/src/TextUI/Configuration/Xml/Logging/TestDox/Html.php index 8d11a672365..60e9d4da342 100644 --- a/src/TextUI/Configuration/Xml/Logging/TestDox/Html.php +++ b/src/TextUI/Configuration/Xml/Logging/TestDox/Html.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Html { diff --git a/src/TextUI/Configuration/Xml/Logging/TestDox/Text.php b/src/TextUI/Configuration/Xml/Logging/TestDox/Text.php index 1950c13e7cd..ed436c0a84b 100644 --- a/src/TextUI/Configuration/Xml/Logging/TestDox/Text.php +++ b/src/TextUI/Configuration/Xml/Logging/TestDox/Text.php @@ -12,9 +12,11 @@ use PHPUnit\TextUI\Configuration\File; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class Text { diff --git a/src/TextUI/Configuration/Xml/Migration/MigrationBuilder.php b/src/TextUI/Configuration/Xml/Migration/MigrationBuilder.php index 47729221733..6235c55f3fe 100644 --- a/src/TextUI/Configuration/Xml/Migration/MigrationBuilder.php +++ b/src/TextUI/Configuration/Xml/Migration/MigrationBuilder.php @@ -9,16 +9,19 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; -use function array_key_exists; -use function sprintf; use function version_compare; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MigrationBuilder +final readonly class MigrationBuilder { - private const AVAILABLE_MIGRATIONS = [ + /** + * @var non-empty-array> + */ + private const array AVAILABLE_MIGRATIONS = [ '8.5' => [ RemoveLogTypes::class, ], @@ -63,22 +66,34 @@ final class MigrationBuilder '10.0' => [ MoveCoverageDirectoriesToSource::class, ], + + '10.4' => [ + RemoveBeStrictAboutTodoAnnotatedTestsAttribute::class, + ], + + '10.5' => [ + RemoveRegisterMockObjectsFromTestArgumentsRecursivelyAttribute::class, + ], + + '11.0' => [ + ReplaceRestrictDeprecationsWithIgnoreDeprecations::class, + ], + + '11.1' => [ + RemoveCacheResultFileAttribute::class, + RemoveCoverageElementCacheDirectoryAttribute::class, + ], + + '11.2' => [ + RemoveBeStrictAboutTodoAnnotatedTestsAttribute::class, + ], ]; /** - * @throws MigrationBuilderException + * @return non-empty-list */ public function build(string $fromVersion): array { - if (!array_key_exists($fromVersion, self::AVAILABLE_MIGRATIONS)) { - throw new MigrationBuilderException( - sprintf( - 'Migration from schema version %s is not supported', - $fromVersion, - ), - ); - } - $stack = [new UpdateSchemaLocation]; foreach (self::AVAILABLE_MIGRATIONS as $version => $migrations) { diff --git a/src/TextUI/Configuration/Xml/Migration/MigrationBuilderException.php b/src/TextUI/Configuration/Xml/Migration/MigrationBuilderException.php deleted file mode 100644 index 3d3c767af50..00000000000 --- a/src/TextUI/Configuration/Xml/Migration/MigrationBuilderException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI\XmlConfiguration; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class MigrationBuilderException extends RuntimeException implements \PHPUnit\Exception -{ -} diff --git a/src/TextUI/Configuration/Xml/Migration/MigrationException.php b/src/TextUI/Configuration/Xml/Migration/MigrationException.php index f92b2db3035..bb35aca6817 100644 --- a/src/TextUI/Configuration/Xml/Migration/MigrationException.php +++ b/src/TextUI/Configuration/Xml/Migration/MigrationException.php @@ -9,11 +9,14 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use PHPUnit\Exception; use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MigrationException extends RuntimeException implements \PHPUnit\Exception +final class MigrationException extends RuntimeException implements Exception { } diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.php b/src/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.php index 697bbe082cb..81a0e322abc 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/ConvertLogTypes.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ConvertLogTypes implements Migration +final readonly class ConvertLogTypes implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.php b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.php index ccefbfbc2b3..0dfee46fda2 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCloverToReport.php @@ -12,9 +12,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoverageCloverToReport extends LogToReportMigration +final readonly class CoverageCloverToReport extends LogToReportMigration { protected function forType(): string { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.php b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.php index afbaaec181a..f0aac5c7c9b 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageCrap4jToReport.php @@ -12,9 +12,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoverageCrap4jToReport extends LogToReportMigration +final readonly class CoverageCrap4jToReport extends LogToReportMigration { protected function forType(): string { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.php b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.php index 7e12095b4ca..f6b7982d98b 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageHtmlToReport.php @@ -12,9 +12,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoverageHtmlToReport extends LogToReportMigration +final readonly class CoverageHtmlToReport extends LogToReportMigration { protected function forType(): string { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.php b/src/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.php index bfa10030b1b..7e362708b3e 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/CoveragePhpToReport.php @@ -12,9 +12,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoveragePhpToReport extends LogToReportMigration +final readonly class CoveragePhpToReport extends LogToReportMigration { protected function forType(): string { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.php b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.php index 063d8df0c43..d463cef8d45 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageTextToReport.php @@ -12,9 +12,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoverageTextToReport extends LogToReportMigration +final readonly class CoverageTextToReport extends LogToReportMigration { protected function forType(): string { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.php b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.php index 480d7777e3d..3db89974215 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/CoverageXmlToReport.php @@ -12,9 +12,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class CoverageXmlToReport extends LogToReportMigration +final readonly class CoverageXmlToReport extends LogToReportMigration { protected function forType(): string { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php index 4ac7331c9ef..87624cc6dec 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCacheDirectoryAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class IntroduceCacheDirectoryAttribute implements Migration +final readonly class IntroduceCacheDirectoryAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.php b/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.php index de52857ee49..9334c1f43f4 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/IntroduceCoverageElement.php @@ -12,9 +12,11 @@ use DOMDocument; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class IntroduceCoverageElement implements Migration +final readonly class IntroduceCoverageElement implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.php b/src/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.php index c07de0ec7f5..08815cbcb31 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/LogToReportMigration.php @@ -9,15 +9,18 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use function assert; use function sprintf; use DOMDocument; use DOMElement; use DOMXPath; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class LogToReportMigration implements Migration +abstract readonly class LogToReportMigration implements Migration { /** * @throws MigrationException @@ -48,6 +51,9 @@ public function migrate(DOMDocument $document): void $logNode->parentNode->removeChild($logNode); } + /** + * @param list $attributes + */ protected function migrateAttributes(DOMElement $src, DOMElement $dest, array $attributes): void { foreach ($attributes as $attr) { @@ -66,9 +72,18 @@ abstract protected function toReportFormat(DOMElement $logNode): DOMElement; private function findLogNode(DOMDocument $document): ?DOMElement { - $logNode = (new DOMXPath($document))->query( - sprintf('//logging/log[@type="%s"]', $this->forType()), - )->item(0); + $xpath = new DOMXPath($document); + + $logNode = $xpath->query( + sprintf( + '//logging/log[@type="%s"]', + $this->forType(), + ), + ); + + assert($logNode !== false); + + $logNode = $logNode->item(0); if (!$logNode instanceof DOMElement) { return null; diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/Migration.php b/src/TextUI/Configuration/Xml/Migration/Migrations/Migration.php index fa4092a9fe7..05359a2d082 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/Migration.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/Migration.php @@ -12,6 +12,8 @@ use DOMDocument; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface Migration diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php index a7aab5e511c..685381698cb 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MoveAttributesFromFilterWhitelistToCoverage implements Migration +final readonly class MoveAttributesFromFilterWhitelistToCoverage implements Migration { /** * @throws MigrationException @@ -24,7 +26,7 @@ public function migrate(DOMDocument $document): void { $whitelist = $document->getElementsByTagName('whitelist')->item(0); - if (!$whitelist) { + if ($whitelist === null) { return; } diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.php b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.php index 1e0fb4c924d..f0e47b90fe4 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveAttributesFromRootToCoverage.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MoveAttributesFromRootToCoverage implements Migration +final readonly class MoveAttributesFromRootToCoverage implements Migration { /** * @throws MigrationException diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.php b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.php index 3c32d2a3c31..4dd37ea5158 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveCoverageDirectoriesToSource.php @@ -15,9 +15,11 @@ use DOMXPath; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MoveCoverageDirectoriesToSource implements Migration +final readonly class MoveCoverageDirectoriesToSource implements Migration { /** * @throws MigrationException @@ -46,9 +48,21 @@ public function migrate(DOMDocument $document): void $xpath = new DOMXPath($document); foreach (['include', 'exclude'] as $element) { - foreach (SnapshotNodeList::fromNodeList($xpath->query('//coverage/' . $element)) as $node) { + $nodes = $xpath->query('//coverage/' . $element); + + assert($nodes !== false); + + foreach (SnapshotNodeList::fromNodeList($nodes) as $node) { $source->appendChild($node); } } + + if ($coverage->childElementCount !== 0) { + return; + } + + assert($coverage->parentNode !== null); + + $coverage->parentNode->removeChild($coverage); } } diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.php b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.php index 40132156f71..09641bb13b7 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistExcludesToCoverage.php @@ -15,9 +15,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MoveWhitelistExcludesToCoverage implements Migration +final readonly class MoveWhitelistExcludesToCoverage implements Migration { /** * @throws MigrationException diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.php b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.php index 2b1b23edc0e..9990124963d 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/MoveWhitelistIncludesToCoverage.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class MoveWhitelistIncludesToCoverage implements Migration +final readonly class MoveWhitelistIncludesToCoverage implements Migration { /** * @throws MigrationException diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.php index c88ff348bc6..cdb90775cd9 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute implements Migration +final readonly class RemoveBeStrictAboutResourceUsageDuringSmallTestsAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.php index 84b92ef8467..1858e67f42b 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveBeStrictAboutTodoAnnotatedTestsAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveBeStrictAboutTodoAnnotatedTestsAttribute implements Migration +final readonly class RemoveBeStrictAboutTodoAnnotatedTestsAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php index 6a9aae9ee08..a5c51c4ebc3 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheResultFileAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveCacheResultFileAttribute implements Migration +final readonly class RemoveCacheResultFileAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php index faf16f2e5b8..69bf38a21f3 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCacheTokensAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveCacheTokensAttribute implements Migration +final readonly class RemoveCacheTokensAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php index 267ff0e8e6c..a908aee381f 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveConversionToExceptionsAttributes.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveConversionToExceptionsAttributes implements Migration +final readonly class RemoveConversionToExceptionsAttributes implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.php index f34518e3422..c26d2071c72 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementCacheDirectoryAttribute.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveCoverageElementCacheDirectoryAttribute implements Migration +final readonly class RemoveCoverageElementCacheDirectoryAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.php index 7a55eacf632..34768625101 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveCoverageElementProcessUncoveredFilesAttribute.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveCoverageElementProcessUncoveredFilesAttribute implements Migration +final readonly class RemoveCoverageElementProcessUncoveredFilesAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.php index 8f1a6d5471c..a831e205109 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveEmptyFilter.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveEmptyFilter implements Migration +final readonly class RemoveEmptyFilter implements Migration { /** * @throws MigrationException diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php index 3095c03bb25..bf2889965de 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveListeners.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveListeners implements Migration +final readonly class RemoveListeners implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.php index 7419204eee0..46ee55e925b 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLogTypes.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveLogTypes implements Migration +final readonly class RemoveLogTypes implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php index cf59ce71b4a..ccccb819931 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveLoggingElements.php @@ -9,14 +9,17 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use function assert; use DOMDocument; use DOMElement; use DOMXPath; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveLoggingElements implements Migration +final readonly class RemoveLoggingElements implements Migration { public function migrate(DOMDocument $document): void { @@ -26,7 +29,11 @@ public function migrate(DOMDocument $document): void private function removeTestDoxElement(DOMDocument $document): void { - $node = (new DOMXPath($document))->query('logging/testdoxXml')->item(0); + $nodes = (new DOMXPath($document))->query('logging/testdoxXml'); + + assert($nodes !== false); + + $node = $nodes->item(0); if (!$node instanceof DOMElement || $node->parentNode === null) { return; @@ -37,7 +44,11 @@ private function removeTestDoxElement(DOMDocument $document): void private function removeTextElement(DOMDocument $document): void { - $node = (new DOMXPath($document))->query('logging/text')->item(0); + $nodes = (new DOMXPath($document))->query('logging/text'); + + assert($nodes !== false); + + $node = $nodes->item(0); if (!$node instanceof DOMElement || $node->parentNode === null) { return; diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php index 5efaefadb37..897cccd81bd 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveNoInteractionAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveNoInteractionAttribute implements Migration +final readonly class RemoveNoInteractionAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.php index 5281cc3acc7..84f4bcf1add 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemovePrinterAttributes.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemovePrinterAttributes implements Migration +final readonly class RemovePrinterAttributes implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveRegisterMockObjectsFromTestArgumentsRecursivelyAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveRegisterMockObjectsFromTestArgumentsRecursivelyAttribute.php new file mode 100644 index 00000000000..e2ff30514f9 --- /dev/null +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveRegisterMockObjectsFromTestArgumentsRecursivelyAttribute.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class RemoveRegisterMockObjectsFromTestArgumentsRecursivelyAttribute implements Migration +{ + public function migrate(DOMDocument $document): void + { + $root = $document->documentElement; + + assert($root instanceof DOMElement); + + if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) { + $root->removeAttribute('registerMockObjectsFromTestArgumentsRecursively'); + } + } +} diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php index 50906f156df..ea5a69217be 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestDoxGroupsElement.php @@ -13,9 +13,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveTestDoxGroupsElement implements Migration +final readonly class RemoveTestDoxGroupsElement implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.php index 4b15dc73ac2..284dda2ef88 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveTestSuiteLoaderAttributes.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveTestSuiteLoaderAttributes implements Migration +final readonly class RemoveTestSuiteLoaderAttributes implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php index 6091cd5af40..d4aa66087bb 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RemoveVerboseAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RemoveVerboseAttribute implements Migration +final readonly class RemoveVerboseAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php index 3fe871d9cd6..c2de95ce38c 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBackupStaticAttributesAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RenameBackupStaticAttributesAttribute implements Migration +final readonly class RenameBackupStaticAttributesAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.php index eba48730e51..dda890b66cb 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RenameBeStrictAboutCoversAnnotationAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RenameBeStrictAboutCoversAnnotationAttribute implements Migration +final readonly class RenameBeStrictAboutCoversAnnotationAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php b/src/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php index b1b41f5d2a0..707aff8a368 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/RenameForceCoversAnnotationAttribute.php @@ -14,9 +14,11 @@ use DOMElement; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class RenameForceCoversAnnotationAttribute implements Migration +final readonly class RenameForceCoversAnnotationAttribute implements Migration { public function migrate(DOMDocument $document): void { diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/ReplaceRestrictDeprecationsWithIgnoreDeprecations.php b/src/TextUI/Configuration/Xml/Migration/Migrations/ReplaceRestrictDeprecationsWithIgnoreDeprecations.php new file mode 100644 index 00000000000..12cb1e7f087 --- /dev/null +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/ReplaceRestrictDeprecationsWithIgnoreDeprecations.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\XmlConfiguration; + +use function assert; +use DOMDocument; +use DOMElement; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class ReplaceRestrictDeprecationsWithIgnoreDeprecations implements Migration +{ + /** + * @throws MigrationException + */ + public function migrate(DOMDocument $document): void + { + $source = $document->getElementsByTagName('source')->item(0); + + if ($source === null) { + return; + } + + assert($source instanceof DOMElement); + + if (!$source->hasAttribute('restrictDeprecations')) { + return; + } + + $restrictDeprecations = $source->getAttribute('restrictDeprecations') === 'true'; + + $source->removeAttribute('restrictDeprecations'); + + if (!$restrictDeprecations || + $source->hasAttribute('ignoreIndirectDeprecations')) { + return; + } + + $source->setAttribute('ignoreIndirectDeprecations', 'true'); + } +} diff --git a/src/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.php b/src/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.php index d8f65d6a199..35c8abe81d2 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrations/UpdateSchemaLocation.php @@ -10,24 +10,36 @@ namespace PHPUnit\TextUI\XmlConfiguration; use function assert; +use function str_contains; use DOMDocument; use DOMElement; use PHPUnit\Runner\Version; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class UpdateSchemaLocation implements Migration +final readonly class UpdateSchemaLocation implements Migration { + private const string NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema-instance'; + private const string LOCAL_NAME_SCHEMA_LOCATION = 'noNamespaceSchemaLocation'; + public function migrate(DOMDocument $document): void { $root = $document->documentElement; assert($root instanceof DOMElement); + $existingSchemaLocation = $root->getAttributeNodeNS(self::NAMESPACE_URI, self::LOCAL_NAME_SCHEMA_LOCATION)->value; + + if (str_contains($existingSchemaLocation, '://') === false) { // If the current schema location is a relative path, don't update it + return; + } + $root->setAttributeNS( - 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:noNamespaceSchemaLocation', + self::NAMESPACE_URI, + 'xsi:' . self::LOCAL_NAME_SCHEMA_LOCATION, 'https://schema.phpunit.de/' . Version::series() . '/phpunit.xsd', ); } diff --git a/src/TextUI/Configuration/Xml/Migration/Migrator.php b/src/TextUI/Configuration/Xml/Migration/Migrator.php index 2755a4f597e..4649dec28e4 100644 --- a/src/TextUI/Configuration/Xml/Migration/Migrator.php +++ b/src/TextUI/Configuration/Xml/Migration/Migrator.php @@ -9,18 +9,20 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; -use function sprintf; +use function assert; +use PHPUnit\Runner\Version; use PHPUnit\Util\Xml\Loader as XmlLoader; use PHPUnit\Util\Xml\XmlException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Migrator +final readonly class Migrator { /** * @throws Exception - * @throws MigrationBuilderException * @throws MigrationException * @throws XmlException */ @@ -29,12 +31,11 @@ public function migrate(string $filename): string $origin = (new SchemaDetector)->detect($filename); if (!$origin->detected()) { - throw new Exception( - sprintf( - '"%s" is not a valid PHPUnit XML configuration file that can be migrated', - $filename, - ), - ); + throw new Exception('The file does not validate against any known schema'); + } + + if ($origin->version() === Version::series()) { + throw new Exception('The file does not need to be migrated'); } $configurationDocument = (new XmlLoader)->loadFile($filename); @@ -46,6 +47,10 @@ public function migrate(string $filename): string $configurationDocument->formatOutput = true; $configurationDocument->preserveWhiteSpace = false; - return $configurationDocument->saveXML(); + $xml = $configurationDocument->saveXML(); + + assert($xml !== false); + + return $xml; } } diff --git a/src/TextUI/Configuration/Xml/Migration/SnapshotNodeList.php b/src/TextUI/Configuration/Xml/Migration/SnapshotNodeList.php index a760c3f2da4..491c24edace 100644 --- a/src/TextUI/Configuration/Xml/Migration/SnapshotNodeList.php +++ b/src/TextUI/Configuration/Xml/Migration/SnapshotNodeList.php @@ -17,6 +17,8 @@ use IteratorAggregate; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * * @template-implements IteratorAggregate @@ -24,10 +26,13 @@ final class SnapshotNodeList implements Countable, IteratorAggregate { /** - * @psalm-var list + * @var list */ private array $nodes = []; + /** + * @param DOMNodeList $list + */ public static function fromNodeList(DOMNodeList $list): self { $snapshot = new self; @@ -44,6 +49,9 @@ public function count(): int return count($this->nodes); } + /** + * @return ArrayIterator + */ public function getIterator(): ArrayIterator { return new ArrayIterator($this->nodes); diff --git a/src/TextUI/Configuration/Xml/PHPUnit.php b/src/TextUI/Configuration/Xml/PHPUnit.php index 0626b44f058..904b9dec0ce 100644 --- a/src/TextUI/Configuration/Xml/PHPUnit.php +++ b/src/TextUI/Configuration/Xml/PHPUnit.php @@ -10,9 +10,11 @@ namespace PHPUnit\TextUI\XmlConfiguration; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class PHPUnit { @@ -21,9 +23,12 @@ private int|string $columns; private string $colors; private bool $stderr; + private bool $displayDetailsOnAllIssues; private bool $displayDetailsOnIncompleteTests; private bool $displayDetailsOnSkippedTests; private bool $displayDetailsOnTestsThatTriggerDeprecations; + private bool $displayDetailsOnPhpunitDeprecations; + private bool $displayDetailsOnPhpunitNotices; private bool $displayDetailsOnTestsThatTriggerErrors; private bool $displayDetailsOnTestsThatTriggerNotices; private bool $displayDetailsOnTestsThatTriggerWarnings; @@ -31,7 +36,10 @@ private bool $requireCoverageMetadata; private ?string $bootstrap; private bool $processIsolation; + private bool $failOnAllIssues; private bool $failOnDeprecation; + private bool $failOnPhpunitDeprecation; + private bool $failOnPhpunitNotice; private bool $failOnEmptyTestSuite; private bool $failOnIncomplete; private bool $failOnNotice; @@ -49,7 +57,7 @@ private bool $stopOnWarning; /** - * @psalm-var ?non-empty-string + * @var ?non-empty-string */ private ?string $extensionsDirectory; private bool $beStrictAboutChangesToGlobalState; @@ -67,71 +75,84 @@ private bool $defectsFirst; private bool $backupGlobals; private bool $backupStaticProperties; - private bool $registerMockObjectsFromTestArgumentsRecursively; private bool $testdoxPrinter; + private bool $testdoxPrinterSummary; private bool $controlGarbageCollector; private int $numberOfTestsBeforeGarbageCollection; /** - * @psalm-param ?non-empty-string $extensionsDirectory + * @var non-negative-int */ - public function __construct(?string $cacheDirectory, bool $cacheResult, int|string $columns, string $colors, bool $stderr, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, ?string $bootstrap, bool $processIsolation, bool $failOnDeprecation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, ?string $extensionsDirectory, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutCoverageMetadata, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticProperties, bool $registerMockObjectsFromTestArgumentsRecursively, bool $testdoxPrinter, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection) - { - $this->cacheDirectory = $cacheDirectory; - $this->cacheResult = $cacheResult; - $this->columns = $columns; - $this->colors = $colors; - $this->stderr = $stderr; - $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; - $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; - $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; - $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; - $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; - $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; - $this->reverseDefectList = $reverseDefectList; - $this->requireCoverageMetadata = $requireCoverageMetadata; - $this->bootstrap = $bootstrap; - $this->processIsolation = $processIsolation; - $this->failOnDeprecation = $failOnDeprecation; - $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; - $this->failOnIncomplete = $failOnIncomplete; - $this->failOnNotice = $failOnNotice; - $this->failOnRisky = $failOnRisky; - $this->failOnSkipped = $failOnSkipped; - $this->failOnWarning = $failOnWarning; - $this->stopOnDefect = $stopOnDefect; - $this->stopOnDeprecation = $stopOnDeprecation; - $this->stopOnError = $stopOnError; - $this->stopOnFailure = $stopOnFailure; - $this->stopOnIncomplete = $stopOnIncomplete; - $this->stopOnNotice = $stopOnNotice; - $this->stopOnRisky = $stopOnRisky; - $this->stopOnSkipped = $stopOnSkipped; - $this->stopOnWarning = $stopOnWarning; - $this->extensionsDirectory = $extensionsDirectory; - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; - $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; - $this->beStrictAboutCoverageMetadata = $beStrictAboutCoverageMetadata; - $this->enforceTimeLimit = $enforceTimeLimit; - $this->defaultTimeLimit = $defaultTimeLimit; - $this->timeoutForSmallTests = $timeoutForSmallTests; - $this->timeoutForMediumTests = $timeoutForMediumTests; - $this->timeoutForLargeTests = $timeoutForLargeTests; - $this->defaultTestSuite = $defaultTestSuite; - $this->executionOrder = $executionOrder; - $this->resolveDependencies = $resolveDependencies; - $this->defectsFirst = $defectsFirst; - $this->backupGlobals = $backupGlobals; - $this->backupStaticProperties = $backupStaticProperties; - $this->registerMockObjectsFromTestArgumentsRecursively = $registerMockObjectsFromTestArgumentsRecursively; - $this->testdoxPrinter = $testdoxPrinter; - $this->controlGarbageCollector = $controlGarbageCollector; - $this->numberOfTestsBeforeGarbageCollection = $numberOfTestsBeforeGarbageCollection; + private int $shortenArraysForExportThreshold; + + /** + * @param ?non-empty-string $extensionsDirectory + * @param non-negative-int $shortenArraysForExportThreshold + */ + public function __construct(?string $cacheDirectory, bool $cacheResult, int|string $columns, string $colors, bool $stderr, bool $displayDetailsOnAllIssues, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnPhpunitDeprecations, bool $displayDetailsOnPhpunitNotices, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, ?string $bootstrap, bool $processIsolation, bool $failOnAllIssues, bool $failOnDeprecation, bool $failOnPhpunitDeprecation, bool $failOnPhpunitNotice, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, ?string $extensionsDirectory, bool $beStrictAboutChangesToGlobalState, bool $beStrictAboutOutputDuringTests, bool $beStrictAboutTestsThatDoNotTestAnything, bool $beStrictAboutCoverageMetadata, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, ?string $defaultTestSuite, int $executionOrder, bool $resolveDependencies, bool $defectsFirst, bool $backupGlobals, bool $backupStaticProperties, bool $testdoxPrinter, bool $testdoxPrinterSummary, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection, int $shortenArraysForExportThreshold) + { + $this->cacheDirectory = $cacheDirectory; + $this->cacheResult = $cacheResult; + $this->columns = $columns; + $this->colors = $colors; + $this->stderr = $stderr; + $this->displayDetailsOnAllIssues = $displayDetailsOnAllIssues; + $this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests; + $this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests; + $this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations; + $this->displayDetailsOnPhpunitDeprecations = $displayDetailsOnPhpunitDeprecations; + $this->displayDetailsOnPhpunitNotices = $displayDetailsOnPhpunitNotices; + $this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors; + $this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices; + $this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings; + $this->reverseDefectList = $reverseDefectList; + $this->requireCoverageMetadata = $requireCoverageMetadata; + $this->bootstrap = $bootstrap; + $this->processIsolation = $processIsolation; + $this->failOnAllIssues = $failOnAllIssues; + $this->failOnDeprecation = $failOnDeprecation; + $this->failOnPhpunitDeprecation = $failOnPhpunitDeprecation; + $this->failOnPhpunitNotice = $failOnPhpunitNotice; + $this->failOnEmptyTestSuite = $failOnEmptyTestSuite; + $this->failOnIncomplete = $failOnIncomplete; + $this->failOnNotice = $failOnNotice; + $this->failOnRisky = $failOnRisky; + $this->failOnSkipped = $failOnSkipped; + $this->failOnWarning = $failOnWarning; + $this->stopOnDefect = $stopOnDefect; + $this->stopOnDeprecation = $stopOnDeprecation; + $this->stopOnError = $stopOnError; + $this->stopOnFailure = $stopOnFailure; + $this->stopOnIncomplete = $stopOnIncomplete; + $this->stopOnNotice = $stopOnNotice; + $this->stopOnRisky = $stopOnRisky; + $this->stopOnSkipped = $stopOnSkipped; + $this->stopOnWarning = $stopOnWarning; + $this->extensionsDirectory = $extensionsDirectory; + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + $this->beStrictAboutOutputDuringTests = $beStrictAboutOutputDuringTests; + $this->beStrictAboutTestsThatDoNotTestAnything = $beStrictAboutTestsThatDoNotTestAnything; + $this->beStrictAboutCoverageMetadata = $beStrictAboutCoverageMetadata; + $this->enforceTimeLimit = $enforceTimeLimit; + $this->defaultTimeLimit = $defaultTimeLimit; + $this->timeoutForSmallTests = $timeoutForSmallTests; + $this->timeoutForMediumTests = $timeoutForMediumTests; + $this->timeoutForLargeTests = $timeoutForLargeTests; + $this->defaultTestSuite = $defaultTestSuite; + $this->executionOrder = $executionOrder; + $this->resolveDependencies = $resolveDependencies; + $this->defectsFirst = $defectsFirst; + $this->backupGlobals = $backupGlobals; + $this->backupStaticProperties = $backupStaticProperties; + $this->testdoxPrinter = $testdoxPrinter; + $this->testdoxPrinterSummary = $testdoxPrinterSummary; + $this->controlGarbageCollector = $controlGarbageCollector; + $this->numberOfTestsBeforeGarbageCollection = $numberOfTestsBeforeGarbageCollection; + $this->shortenArraysForExportThreshold = $shortenArraysForExportThreshold; } /** - * @psalm-assert-if-true !null $this->cacheDirectory + * @phpstan-assert-if-true !null $this->cacheDirectory */ public function hasCacheDirectory(): bool { @@ -170,6 +191,11 @@ public function stderr(): bool return $this->stderr; } + public function displayDetailsOnAllIssues(): bool + { + return $this->displayDetailsOnAllIssues; + } + public function displayDetailsOnIncompleteTests(): bool { return $this->displayDetailsOnIncompleteTests; @@ -185,6 +211,16 @@ public function displayDetailsOnTestsThatTriggerDeprecations(): bool return $this->displayDetailsOnTestsThatTriggerDeprecations; } + public function displayDetailsOnPhpunitDeprecations(): bool + { + return $this->displayDetailsOnPhpunitDeprecations; + } + + public function displayDetailsOnPhpunitNotices(): bool + { + return $this->displayDetailsOnPhpunitNotices; + } + public function displayDetailsOnTestsThatTriggerErrors(): bool { return $this->displayDetailsOnTestsThatTriggerErrors; @@ -211,7 +247,7 @@ public function requireCoverageMetadata(): bool } /** - * @psalm-assert-if-true !null $this->bootstrap + * @phpstan-assert-if-true !null $this->bootstrap */ public function hasBootstrap(): bool { @@ -235,11 +271,26 @@ public function processIsolation(): bool return $this->processIsolation; } + public function failOnAllIssues(): bool + { + return $this->failOnAllIssues; + } + public function failOnDeprecation(): bool { return $this->failOnDeprecation; } + public function failOnPhpunitDeprecation(): bool + { + return $this->failOnPhpunitDeprecation; + } + + public function failOnPhpunitNotice(): bool + { + return $this->failOnPhpunitNotice; + } + public function failOnEmptyTestSuite(): bool { return $this->failOnEmptyTestSuite; @@ -316,7 +367,7 @@ public function stopOnWarning(): bool } /** - * @psalm-assert-if-true !null $this->extensionsDirectory + * @phpstan-assert-if-true !null $this->extensionsDirectory */ public function hasExtensionsDirectory(): bool { @@ -324,9 +375,9 @@ public function hasExtensionsDirectory(): bool } /** - * @psalm-return non-empty-string - * * @throws Exception + * + * @return non-empty-string */ public function extensionsDirectory(): string { @@ -383,7 +434,7 @@ public function timeoutForLargeTests(): int } /** - * @psalm-assert-if-true !null $this->defaultTestSuite + * @phpstan-assert-if-true !null $this->defaultTestSuite */ public function hasDefaultTestSuite(): bool { @@ -427,14 +478,14 @@ public function backupStaticProperties(): bool return $this->backupStaticProperties; } - public function registerMockObjectsFromTestArgumentsRecursively(): bool + public function testdoxPrinter(): bool { - return $this->registerMockObjectsFromTestArgumentsRecursively; + return $this->testdoxPrinter; } - public function testdoxPrinter(): bool + public function testdoxPrinterSummary(): bool { - return $this->testdoxPrinter; + return $this->testdoxPrinterSummary; } public function controlGarbageCollector(): bool @@ -446,4 +497,12 @@ public function numberOfTestsBeforeGarbageCollection(): int { return $this->numberOfTestsBeforeGarbageCollection; } + + /** + * @return non-negative-int + */ + public function shortenArraysForExportThreshold(): int + { + return $this->shortenArraysForExportThreshold; + } } diff --git a/src/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.php b/src/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.php index 82124a2c9be..5bd282c8c3b 100644 --- a/src/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.php +++ b/src/TextUI/Configuration/Xml/SchemaDetector/FailedSchemaDetectionResult.php @@ -10,10 +10,12 @@ namespace PHPUnit\TextUI\XmlConfiguration; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class FailedSchemaDetectionResult extends SchemaDetectionResult +final readonly class FailedSchemaDetectionResult extends SchemaDetectionResult { } diff --git a/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php b/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php index e92426a1dd5..aa855b0cb06 100644 --- a/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php +++ b/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetectionResult.php @@ -12,12 +12,17 @@ use PHPUnit\Util\Xml\XmlException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -abstract class SchemaDetectionResult +abstract readonly class SchemaDetectionResult { + /** + * @phpstan-assert-if-true SuccessfulSchemaDetectionResult $this + */ public function detected(): bool { return false; diff --git a/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.php b/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.php index 8d39f860477..5f55f5f2016 100644 --- a/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.php +++ b/src/TextUI/Configuration/Xml/SchemaDetector/SchemaDetector.php @@ -13,9 +13,11 @@ use PHPUnit\Util\Xml\XmlException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class SchemaDetector +final readonly class SchemaDetector { /** * @throws XmlException @@ -24,7 +26,9 @@ public function detect(string $filename): SchemaDetectionResult { $document = (new Loader)->loadFile($filename); - foreach (['10.0', '9.5', '9.2', '8.5'] as $candidate) { + $schemaFinder = new SchemaFinder; + + foreach ($schemaFinder->available() as $candidate) { $schema = (new SchemaFinder)->find($candidate); if (!(new Validator)->validate($document, $schema)->hasValidationErrors()) { diff --git a/src/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php b/src/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php index 07b0b03aff0..18658f87f24 100644 --- a/src/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php +++ b/src/TextUI/Configuration/Xml/SchemaDetector/SuccessfulSchemaDetectionResult.php @@ -10,14 +10,22 @@ namespace PHPUnit\TextUI\XmlConfiguration; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ -final class SuccessfulSchemaDetectionResult extends SchemaDetectionResult +final readonly class SuccessfulSchemaDetectionResult extends SchemaDetectionResult { - private readonly string $version; + /** + * @var non-empty-string + */ + private string $version; + /** + * @param non-empty-string $version + */ public function __construct(string $version) { $this->version = $version; @@ -28,6 +36,9 @@ public function detected(): bool return true; } + /** + * @return non-empty-string + */ public function version(): string { return $this->version; diff --git a/src/TextUI/Configuration/Xml/SchemaFinder.php b/src/TextUI/Configuration/Xml/SchemaFinder.php index 8e46a489ba1..39d25cfadf5 100644 --- a/src/TextUI/Configuration/Xml/SchemaFinder.php +++ b/src/TextUI/Configuration/Xml/SchemaFinder.php @@ -9,16 +9,45 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use function assert; use function defined; use function is_file; +use function rsort; use function sprintf; +use DirectoryIterator; use PHPUnit\Runner\Version; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class SchemaFinder +final readonly class SchemaFinder { + /** + * @return non-empty-list + */ + public function available(): array + { + $result = [Version::series()]; + + foreach ((new DirectoryIterator($this->path() . 'schema')) as $file) { + if ($file->isDot()) { + continue; + } + + $version = $file->getBasename('.xsd'); + + assert($version !== ''); + + $result[] = $version; + } + + rsort($result); + + return $result; + } + /** * @throws CannotFindSchemaException */ diff --git a/src/TextUI/Configuration/Xml/TestSuiteMapper.php b/src/TextUI/Configuration/Xml/TestSuiteMapper.php index 530ae10bcbd..9bbc933bdca 100644 --- a/src/TextUI/Configuration/Xml/TestSuiteMapper.php +++ b/src/TextUI/Configuration/Xml/TestSuiteMapper.php @@ -10,14 +10,14 @@ namespace PHPUnit\TextUI\XmlConfiguration; use const PHP_VERSION; -use function array_merge; -use function array_unique; use function explode; use function in_array; use function is_dir; use function is_file; +use function sprintf; use function str_contains; use function version_compare; +use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Framework\Exception as FrameworkException; use PHPUnit\Framework\TestSuite as TestSuiteObject; use PHPUnit\TextUI\Configuration\TestSuiteCollection; @@ -27,42 +27,47 @@ use SebastianBergmann\FileIterator\Facade; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteMapper +final readonly class TestSuiteMapper { /** - * @psalm-param non-empty-string $xmlConfigurationFile, + * @param non-empty-string $xmlConfigurationFile, * * @throws RuntimeException * @throws TestDirectoryNotFoundException * @throws TestFileNotFoundException */ - public function map(string $xmlConfigurationFile, TestSuiteCollection $configuration, string $filter, string $excludedTestSuites): TestSuiteObject + public function map(string $xmlConfigurationFile, TestSuiteCollection $configuredTestSuites, string $namesOfIncludedTestSuites, string $namesOfExcludedTestSuites): TestSuiteObject { try { - $filterAsArray = $filter ? explode(',', $filter) : []; - $excludedFilterAsArray = $excludedTestSuites ? explode(',', $excludedTestSuites) : []; - $result = TestSuiteObject::empty($xmlConfigurationFile); + $namesOfIncludedTestSuitesAsArray = $namesOfIncludedTestSuites !== '' ? explode(',', $namesOfIncludedTestSuites) : []; + $excludedTestSuitesAsArray = $namesOfExcludedTestSuites !== '' ? explode(',', $namesOfExcludedTestSuites) : []; + $result = TestSuiteObject::empty($xmlConfigurationFile); + $processed = []; - foreach ($configuration as $testSuiteConfiguration) { - if (!empty($filterAsArray) && !in_array($testSuiteConfiguration->name(), $filterAsArray, true)) { + foreach ($configuredTestSuites as $configuredTestSuite) { + if ($namesOfIncludedTestSuitesAsArray !== [] && !in_array($configuredTestSuite->name(), $namesOfIncludedTestSuitesAsArray, true)) { continue; } - if (!empty($excludedFilterAsArray) && in_array($testSuiteConfiguration->name(), $excludedFilterAsArray, true)) { + if ($excludedTestSuitesAsArray !== [] && in_array($configuredTestSuite->name(), $excludedTestSuitesAsArray, true)) { continue; } - $exclude = []; + $testSuiteName = $configuredTestSuite->name(); + $exclude = []; - foreach ($testSuiteConfiguration->exclude()->asArray() as $file) { + foreach ($configuredTestSuite->exclude()->asArray() as $file) { $exclude[] = $file->path(); } - $files = []; + $testSuite = TestSuiteObject::empty($configuredTestSuite->name()); + $empty = true; - foreach ($testSuiteConfiguration->directories() as $directory) { + foreach ($configuredTestSuite->directories() as $directory) { if (!str_contains($directory->path(), '*') && !is_dir($directory->path())) { throw new TestDirectoryNotFoundException($directory->path()); } @@ -71,18 +76,37 @@ public function map(string $xmlConfigurationFile, TestSuiteCollection $configura continue; } - $files = array_merge( - $files, - (new Facade)->getFilesAsArray( - $directory->path(), - $directory->suffix(), - $directory->prefix(), - $exclude, - ), + $files = (new Facade)->getFilesAsArray( + $directory->path(), + $directory->suffix(), + $directory->prefix(), + $exclude, ); + + $groups = $directory->groups(); + + foreach ($files as $file) { + if (isset($processed[$file])) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Cannot add file %s to test suite "%s" as it was already added to test suite "%s"', + $file, + $testSuiteName, + $processed[$file], + ), + ); + + continue; + } + + $processed[$file] = $testSuiteName; + $empty = false; + + $testSuite->addTestFile($file, $groups); + } } - foreach ($testSuiteConfiguration->files() as $file) { + foreach ($configuredTestSuite->files() as $file) { if (!is_file($file->path())) { throw new TestFileNotFoundException($file->path()); } @@ -91,14 +115,26 @@ public function map(string $xmlConfigurationFile, TestSuiteCollection $configura continue; } - $files[] = $file->path(); - } + if (isset($processed[$file->path()])) { + EventFacade::emitter()->testRunnerTriggeredPhpunitWarning( + sprintf( + 'Cannot add file %s to test suite "%s" as it was already added to test suite "%s"', + $file->path(), + $testSuiteName, + $processed[$file->path()], + ), + ); + + continue; + } - if (!empty($files)) { - $testSuite = TestSuiteObject::empty($testSuiteConfiguration->name()); + $processed[$file->path()] = $testSuiteName; + $empty = false; - $testSuite->addTestFiles(array_unique($files)); + $testSuite->addTestFile($file->path(), $file->groups()); + } + if (!$empty) { $result->addTest($testSuite); } } diff --git a/src/TextUI/Configuration/Xml/Validator/ValidationResult.php b/src/TextUI/Configuration/Xml/Validator/ValidationResult.php index 212725e6f3f..95fe473d686 100644 --- a/src/TextUI/Configuration/Xml/Validator/ValidationResult.php +++ b/src/TextUI/Configuration/Xml/Validator/ValidationResult.php @@ -9,23 +9,27 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use const PHP_EOL; use function sprintf; use function trim; +use LibXMLError; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class ValidationResult { /** - * @psalm-var array> + * @var array> */ private array $validationErrors; /** - * @psalm-param array $errors + * @param array $errors */ public static function fromArray(array $errors): self { @@ -42,6 +46,9 @@ public static function fromArray(array $errors): self return new self($validationErrors); } + /** + * @param array> $validationErrors + */ private function __construct(array $validationErrors) { $this->validationErrors = $validationErrors; @@ -49,7 +56,7 @@ private function __construct(array $validationErrors) public function hasValidationErrors(): bool { - return !empty($this->validationErrors); + return $this->validationErrors !== []; } public function asString(): string diff --git a/src/TextUI/Configuration/Xml/Validator/Validator.php b/src/TextUI/Configuration/Xml/Validator/Validator.php index b93f961aac9..cc3a93dd29d 100644 --- a/src/TextUI/Configuration/Xml/Validator/Validator.php +++ b/src/TextUI/Configuration/Xml/Validator/Validator.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use function assert; use function file_get_contents; use function libxml_clear_errors; use function libxml_get_errors; @@ -16,15 +17,21 @@ use DOMDocument; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Validator +final readonly class Validator { public function validate(DOMDocument $document, string $xsdFilename): ValidationResult { + $buffer = file_get_contents($xsdFilename); + + assert($buffer !== false); + $originalErrorHandling = libxml_use_internal_errors(true); - $document->schemaValidateSource(file_get_contents($xsdFilename)); + $document->schemaValidateSource($buffer); $errors = libxml_get_errors(); libxml_clear_errors(); diff --git a/src/TextUI/Exception/CannotOpenSocketException.php b/src/TextUI/Exception/CannotOpenSocketException.php new file mode 100644 index 00000000000..519d1378568 --- /dev/null +++ b/src/TextUI/Exception/CannotOpenSocketException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use function sprintf; +use RuntimeException; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class CannotOpenSocketException extends RuntimeException implements Exception +{ + public function __construct(string $hostname, int $port) + { + parent::__construct( + sprintf( + 'Cannot open socket %s:%d', + $hostname, + $port, + ), + ); + } +} diff --git a/src/TextUI/Exception/Exception.php b/src/TextUI/Exception/Exception.php index ee2ae4ffa90..6b370ca0760 100644 --- a/src/TextUI/Exception/Exception.php +++ b/src/TextUI/Exception/Exception.php @@ -12,6 +12,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends Throwable diff --git a/src/TextUI/Exception/ExtensionsNotConfiguredException.php b/src/TextUI/Exception/ExtensionsNotConfiguredException.php deleted file mode 100644 index c192e7535a2..00000000000 --- a/src/TextUI/Exception/ExtensionsNotConfiguredException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ExtensionsNotConfiguredException extends RuntimeException implements Exception -{ -} diff --git a/src/TextUI/Exception/InvalidSocketException.php b/src/TextUI/Exception/InvalidSocketException.php index 6c9e9216695..441afd2a1ed 100644 --- a/src/TextUI/Exception/InvalidSocketException.php +++ b/src/TextUI/Exception/InvalidSocketException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidSocketException extends RuntimeException implements Exception diff --git a/src/TextUI/Exception/ReflectionException.php b/src/TextUI/Exception/ReflectionException.php deleted file mode 100644 index 5e041444443..00000000000 --- a/src/TextUI/Exception/ReflectionException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI; - -use RuntimeException; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class ReflectionException extends RuntimeException implements Exception -{ -} diff --git a/src/TextUI/Exception/RuntimeException.php b/src/TextUI/Exception/RuntimeException.php index 619023190db..875a0487c8c 100644 --- a/src/TextUI/Exception/RuntimeException.php +++ b/src/TextUI/Exception/RuntimeException.php @@ -10,6 +10,8 @@ namespace PHPUnit\TextUI; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class RuntimeException extends \RuntimeException implements Exception diff --git a/src/TextUI/Exception/TestDirectoryNotFoundException.php b/src/TextUI/Exception/TestDirectoryNotFoundException.php index da15966c6ba..9b35390cdab 100644 --- a/src/TextUI/Exception/TestDirectoryNotFoundException.php +++ b/src/TextUI/Exception/TestDirectoryNotFoundException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestDirectoryNotFoundException extends RuntimeException implements Exception diff --git a/src/TextUI/Exception/TestFileNotFoundException.php b/src/TextUI/Exception/TestFileNotFoundException.php index 247690d06b8..46c9df80671 100644 --- a/src/TextUI/Exception/TestFileNotFoundException.php +++ b/src/TextUI/Exception/TestFileNotFoundException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestFileNotFoundException extends RuntimeException implements Exception diff --git a/src/TextUI/Help.php b/src/TextUI/Help.php index 5ae9c10402c..ae1c514692c 100644 --- a/src/TextUI/Help.php +++ b/src/TextUI/Help.php @@ -11,6 +11,7 @@ use const PHP_EOL; use function count; +use function defined; use function explode; use function max; use function preg_replace_callback; @@ -22,148 +23,16 @@ use SebastianBergmann\Environment\Console; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Help { - private const LEFT_MARGIN = ' '; - private const HELP_TEXT = [ - 'Usage' => [ - ['text' => 'phpunit [options] ...'], - ], - - 'Configuration' => [ - ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], - ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], - ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], - ['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'], - ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], - ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], - ['arg' => '--cache-directory ', 'desc' => 'Specify cache directory'], - ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], - ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format'], - ['arg' => '--generate-baseline ', 'desc' => 'Generate baseline for issues'], - ['arg' => '--use-baseline ', 'desc' => 'Use baseline to ignore issues'], - ['arg' => '--ignore-baseline', 'desc' => 'Do not use baseline to ignore issues'], - ], - - 'Selection' => [ - ['arg' => '--list-suites', 'desc' => 'List available test suites'], - ['arg' => '--testsuite ', 'desc' => 'Only run tests from the specified test suite(s)'], - ['arg' => '--exclude-testsuite ', 'desc' => 'Exclude tests from the specified test suite(s)'], - ['arg' => '--list-groups', 'desc' => 'List available test groups'], - ['arg' => '--group ', 'desc' => 'Only run tests from the specified group(s)'], - ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], - ['arg' => '--covers ', 'desc' => 'Only run tests that intend to cover '], - ['arg' => '--uses ', 'desc' => 'Only run tests that intend to use '], - ['arg' => '--list-tests', 'desc' => 'List available tests'], - ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], - ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], - ['arg' => '--test-suffix ', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt'], - ], - - 'Execution' => [ - ['arg' => '--process-isolation', 'desc' => 'Run each test in a separate PHP process'], - ['arg' => '--globals-backup', 'desc' => 'Backup and restore $GLOBALS for each test'], - ['arg' => '--static-backup', 'desc' => 'Backup and restore static properties for each test'], - ['spacer' => ''], - - ['arg' => '--strict-coverage', 'desc' => 'Be strict about code coverage metadata'], - ['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'], - ['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'], - ['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'], - ['arg' => '--default-time-limit ', 'desc' => 'Timeout in seconds for tests that have no declared size'], - ['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'], - ['spacer' => ''], - - ['arg' => '--stop-on-defect', 'desc' => 'Stop after first error, failure, warning, or risky test'], - ['arg' => '--stop-on-error', 'desc' => 'Stop after first error'], - ['arg' => '--stop-on-failure', 'desc' => 'Stop after first failure'], - ['arg' => '--stop-on-warning', 'desc' => 'Stop after first warning'], - ['arg' => '--stop-on-risky', 'desc' => 'Stop after first risky test'], - ['arg' => '--stop-on-deprecation', 'desc' => 'Stop after first test that triggered a deprecation'], - ['arg' => '--stop-on-notice', 'desc' => 'Stop after first test that triggered a notice'], - ['arg' => '--stop-on-skipped', 'desc' => 'Stop after first skipped test'], - ['arg' => '--stop-on-incomplete', 'desc' => 'Stop after first incomplete test'], - ['spacer' => ''], - - ['arg' => '--fail-on-warning', 'desc' => 'Signal failure using shell exit code when a warning was triggered'], - ['arg' => '--fail-on-risky', 'desc' => 'Signal failure using shell exit code when a test was considered risky'], - ['arg' => '--fail-on-deprecation', 'desc' => 'Signal failure using shell exit code when a deprecation was triggered'], - ['arg' => '--fail-on-notice', 'desc' => 'Signal failure using shell exit code when a notice was triggered'], - ['arg' => '--fail-on-skipped', 'desc' => 'Signal failure using shell exit code when a test was skipped'], - ['arg' => '--fail-on-incomplete', 'desc' => 'Signal failure using shell exit code when a test was marked incomplete'], - ['spacer' => ''], - - ['arg' => '--cache-result', 'desc' => 'Write test results to cache file'], - ['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file'], - ['spacer' => ''], - - ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size'], - ['arg' => '--random-order-seed ', 'desc' => 'Use the specified random seed when running tests in random order'], - ], - - 'Reporting' => [ - ['arg' => '--colors ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], - ['arg' => '--columns ', 'desc' => 'Number of columns to use for progress output'], - ['arg' => '--columns max', 'desc' => 'Use maximum number of columns for progress output'], - ['arg' => '--stderr', 'desc' => 'Write to STDERR instead of STDOUT'], - ['spacer' => ''], - - ['arg' => '--no-progress', 'desc' => 'Disable output of test execution progress'], - ['arg' => '--no-results', 'desc' => 'Disable output of test results'], - ['arg' => '--no-output', 'desc' => 'Disable all output'], - ['spacer' => ''], - - ['arg' => '--display-incomplete', 'desc' => 'Display details for incomplete tests'], - ['arg' => '--display-skipped', 'desc' => 'Display details for skipped tests'], - ['arg' => '--display-deprecations', 'desc' => 'Display details for deprecations triggered by tests'], - ['arg' => '--display-errors', 'desc' => 'Display details for errors triggered by tests'], - ['arg' => '--display-notices', 'desc' => 'Display details for notices triggered by tests'], - ['arg' => '--display-warnings', 'desc' => 'Display details for warnings triggered by tests'], - ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], - ['spacer' => ''], - - ['arg' => '--teamcity', 'desc' => 'Replace default progress and result output with TeamCity format'], - ['arg' => '--testdox', 'desc' => 'Replace default result output with TestDox format'], - ], - - 'Logging' => [ - ['arg' => '--log-junit ', 'desc' => 'Write test results in JUnit XML format to file'], - ['arg' => '--log-teamcity ', 'desc' => 'Write test results in TeamCity format to file'], - ['arg' => '--testdox-html ', 'desc' => 'Write test results in TestDox format (HTML) to file'], - ['arg' => '--testdox-text ', 'desc' => 'Write test results in TestDox format (plain text) to file'], - ['arg' => '--log-events-text ', 'desc' => 'Stream events as plain text to file'], - ['arg' => '--log-events-verbose-text ', 'desc' => 'Stream events as plain text with extended information to file'], - ['arg' => '--no-logging', 'desc' => 'Ignore logging configured in the XML configuration file'], - ], - - 'Code Coverage' => [ - ['arg' => '--coverage-clover ', 'desc' => 'Write code coverage report in Clover XML format to file'], - ['arg' => '--coverage-cobertura ', 'desc' => 'Write code coverage report in Cobertura XML format to file'], - ['arg' => '--coverage-crap4j ', 'desc' => 'Write code coverage report in Crap4J XML format to file'], - ['arg' => '--coverage-html ', 'desc' => 'Write code coverage report in HTML format to directory'], - ['arg' => '--coverage-php ', 'desc' => 'Write serialized code coverage data to file'], - ['arg' => '--coverage-text=', 'desc' => 'Write code coverage report in text format to file [default: standard output]'], - ['arg' => '--coverage-xml ', 'desc' => 'Write code coverage report in XML format to directory'], - ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], - ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage reporting'], - ['arg' => '--path-coverage', 'desc' => 'Report path coverage in addition to line coverage'], - ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable metadata for ignoring code coverage'], - ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage reporting configured in the XML configuration file'], - ], - - 'Miscellaneous' => [ - ['arg' => '-h|--help', 'desc' => 'Prints this usage information'], - ['arg' => '--version', 'desc' => 'Prints the version and exits'], - ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than and exits'], - ['arg' => '--check-version', 'desc' => 'Check whether PHPUnit is the latest version and exits'], - ], - - ]; + private const string LEFT_MARGIN = ' '; private int $lengthOfLongestOptionName = 0; private readonly int $columnsAvailableForDescription; - private ?bool $hasColor; + private bool $hasColor; public function __construct(?int $width = null, ?bool $withColor = null) { @@ -177,10 +46,10 @@ public function __construct(?int $width = null, ?bool $withColor = null) $this->hasColor = $withColor; } - foreach (self::HELP_TEXT as $options) { + foreach ($this->elements() as $options) { foreach ($options as $option) { if (isset($option['arg'])) { - $this->lengthOfLongestOptionName = max($this->lengthOfLongestOptionName, isset($option['arg']) ? strlen($option['arg']) : 0); + $this->lengthOfLongestOptionName = max($this->lengthOfLongestOptionName, strlen($option['arg'])); } } } @@ -201,7 +70,7 @@ private function writeWithoutColor(): string { $buffer = ''; - foreach (self::HELP_TEXT as $section => $options) { + foreach ($this->elements() as $section => $options) { $buffer .= "{$section}:" . PHP_EOL; if ($section !== 'Usage') { @@ -234,7 +103,7 @@ private function writeWithColor(): string { $buffer = ''; - foreach (self::HELP_TEXT as $section => $options) { + foreach ($this->elements() as $section => $options) { $buffer .= Color::colorize('fg-yellow', "{$section}:") . PHP_EOL; if ($section !== 'Usage') { @@ -254,7 +123,7 @@ private function writeWithColor(): string $arg = Color::colorize('fg-green', str_pad($option['arg'], $this->lengthOfLongestOptionName)); $arg = preg_replace_callback( '/(<[^>]+>)/', - static fn ($matches) => Color::colorize('fg-cyan', $matches[0]), + static fn (array $matches) => Color::colorize('fg-cyan', $matches[0]), $arg, ); @@ -273,4 +142,172 @@ private function writeWithColor(): string return $buffer; } + + /** + * @return array> + */ + private function elements(): array + { + $elements = [ + 'Usage' => [ + ['text' => 'phpunit [options] ...'], + ], + + 'Configuration' => [ + ['arg' => '--bootstrap ', 'desc' => 'A PHP script that is included before the tests run'], + ['arg' => '-c|--configuration ', 'desc' => 'Read configuration from XML file'], + ['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'], + ['arg' => '--extension ', 'desc' => 'Register test runner extension with bootstrap '], + ['arg' => '--no-extensions', 'desc' => 'Do not register test runner extensions'], + ['arg' => '--include-path ', 'desc' => 'Prepend PHP\'s include_path with given path(s)'], + ['arg' => '-d ', 'desc' => 'Sets a php.ini value'], + ['arg' => '--cache-directory ', 'desc' => 'Specify cache directory'], + ['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'], + ['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format'], + ['arg' => '--generate-baseline ', 'desc' => 'Generate baseline for issues'], + ['arg' => '--use-baseline ', 'desc' => 'Use baseline to ignore issues'], + ['arg' => '--ignore-baseline', 'desc' => 'Do not use baseline to ignore issues'], + ], + + 'Selection' => [ + ['arg' => '--list-suites', 'desc' => 'List available test suites'], + ['arg' => '--testsuite ', 'desc' => 'Only run tests from the specified test suite(s)'], + ['arg' => '--exclude-testsuite ', 'desc' => 'Exclude tests from the specified test suite(s)'], + ['arg' => '--list-groups', 'desc' => 'List available test groups'], + ['arg' => '--group ', 'desc' => 'Only run tests from the specified group(s)'], + ['arg' => '--exclude-group ', 'desc' => 'Exclude tests from the specified group(s)'], + ['arg' => '--covers ', 'desc' => 'Only run tests that intend to cover '], + ['arg' => '--uses ', 'desc' => 'Only run tests that intend to use '], + ['arg' => '--requires-php-extension ', 'desc' => 'Only run tests that require PHP extension '], + ['arg' => '--list-test-files', 'desc' => 'List available test files'], + ['arg' => '--list-tests', 'desc' => 'List available tests'], + ['arg' => '--list-tests-xml ', 'desc' => 'List available tests in XML format'], + ['arg' => '--filter ', 'desc' => 'Filter which tests to run'], + ['arg' => '--exclude-filter ', 'desc' => 'Exclude tests for the specified filter pattern'], + ['arg' => '--test-suffix ', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt'], + ], + + 'Execution' => [ + ['arg' => '--process-isolation', 'desc' => 'Run each test in a separate PHP process'], + ['arg' => '--globals-backup', 'desc' => 'Backup and restore $GLOBALS for each test'], + ['arg' => '--static-backup', 'desc' => 'Backup and restore static properties for each test'], + ['spacer' => ''], + + ['arg' => '--strict-coverage', 'desc' => 'Be strict about code coverage metadata'], + ['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'], + ['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'], + ['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'], + ['arg' => '--default-time-limit ', 'desc' => 'Timeout in seconds for tests that have no declared size'], + ['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'], + ['spacer' => ''], + + ['arg' => '--stop-on-defect', 'desc' => 'Stop after first error, failure, warning, or risky test'], + ['arg' => '--stop-on-error', 'desc' => 'Stop after first error'], + ['arg' => '--stop-on-failure', 'desc' => 'Stop after first failure'], + ['arg' => '--stop-on-warning', 'desc' => 'Stop after first warning'], + ['arg' => '--stop-on-risky', 'desc' => 'Stop after first risky test'], + ['arg' => '--stop-on-deprecation', 'desc' => 'Stop after first test that triggered a deprecation'], + ['arg' => '--stop-on-notice', 'desc' => 'Stop after first test that triggered a notice'], + ['arg' => '--stop-on-skipped', 'desc' => 'Stop after first skipped test'], + ['arg' => '--stop-on-incomplete', 'desc' => 'Stop after first incomplete test'], + ['spacer' => ''], + + ['arg' => '--fail-on-empty-test-suite', 'desc' => 'Signal failure using shell exit code when no tests were run'], + ['arg' => '--fail-on-warning', 'desc' => 'Signal failure using shell exit code when a warning was triggered'], + ['arg' => '--fail-on-risky', 'desc' => 'Signal failure using shell exit code when a test was considered risky'], + ['arg' => '--fail-on-deprecation', 'desc' => 'Signal failure using shell exit code when a deprecation was triggered'], + ['arg' => '--fail-on-phpunit-deprecation', 'desc' => 'Signal failure using shell exit code when a PHPUnit deprecation was triggered'], + ['arg' => '--fail-on-phpunit-notice', 'desc' => 'Signal failure using shell exit code when a PHPUnit notice was triggered'], + ['arg' => '--fail-on-notice', 'desc' => 'Signal failure using shell exit code when a notice was triggered'], + ['arg' => '--fail-on-skipped', 'desc' => 'Signal failure using shell exit code when a test was skipped'], + ['arg' => '--fail-on-incomplete', 'desc' => 'Signal failure using shell exit code when a test was marked incomplete'], + ['arg' => '--fail-on-all-issues', 'desc' => 'Signal failure using shell exit code when an issue is triggered'], + ['spacer' => ''], + + ['arg' => '--cache-result', 'desc' => 'Write test results to cache file'], + ['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file'], + ['spacer' => ''], + + ['arg' => '--order-by ', 'desc' => 'Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size'], + ['arg' => '--random-order-seed ', 'desc' => 'Use the specified random seed when running tests in random order'], + ], + + 'Reporting' => [ + ['arg' => '--colors ', 'desc' => 'Use colors in output ("never", "auto" or "always")'], + ['arg' => '--columns ', 'desc' => 'Number of columns to use for progress output'], + ['arg' => '--columns max', 'desc' => 'Use maximum number of columns for progress output'], + ['arg' => '--stderr', 'desc' => 'Write to STDERR instead of STDOUT'], + ['spacer' => ''], + + ['arg' => '--no-progress', 'desc' => 'Disable output of test execution progress'], + ['arg' => '--no-results', 'desc' => 'Disable output of test results'], + ['arg' => '--no-output', 'desc' => 'Disable all output'], + ['spacer' => ''], + + ['arg' => '--display-incomplete', 'desc' => 'Display details for incomplete tests'], + ['arg' => '--display-skipped', 'desc' => 'Display details for skipped tests'], + ['arg' => '--display-deprecations', 'desc' => 'Display details for deprecations triggered by tests'], + ['arg' => '--display-phpunit-deprecations', 'desc' => 'Display details for PHPUnit deprecations'], + ['arg' => '--display-phpunit-notices', 'desc' => 'Display details for PHPUnit notices'], + ['arg' => '--display-errors', 'desc' => 'Display details for errors triggered by tests'], + ['arg' => '--display-notices', 'desc' => 'Display details for notices triggered by tests'], + ['arg' => '--display-warnings', 'desc' => 'Display details for warnings triggered by tests'], + ['arg' => '--display-all-issues', 'desc' => 'Display details for all issues that are triggered'], + ['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'], + ['spacer' => ''], + + ['arg' => '--teamcity', 'desc' => 'Replace default progress and result output with TeamCity format'], + ['arg' => '--testdox', 'desc' => 'Replace default result output with TestDox format'], + ['arg' => '--testdox-summary', 'desc' => 'Repeat TestDox output for tests with errors, failures, or issues'], + ['spacer' => ''], + + ['arg' => '--debug', 'desc' => 'Replace default progress and result output with debugging information'], + ['arg' => '--with-telemetry', 'desc' => 'Include telemetry information in debugging information output'], + ], + + 'Logging' => [ + ['arg' => '--log-junit ', 'desc' => 'Write test results in JUnit XML format to file'], + ['arg' => '--log-teamcity ', 'desc' => 'Write test results in TeamCity format to file'], + ['arg' => '--testdox-html ', 'desc' => 'Write test results in TestDox format (HTML) to file'], + ['arg' => '--testdox-text ', 'desc' => 'Write test results in TestDox format (plain text) to file'], + ['arg' => '--log-events-text ', 'desc' => 'Stream events as plain text to file'], + ['arg' => '--log-events-verbose-text ', 'desc' => 'Stream events as plain text with extended information to file'], + ['arg' => '--no-logging', 'desc' => 'Ignore logging configured in the XML configuration file'], + ], + + 'Code Coverage' => [ + ['arg' => '--coverage-clover ', 'desc' => 'Write code coverage report in Clover XML format to file'], + ['arg' => '--coverage-cobertura ', 'desc' => 'Write code coverage report in Cobertura XML format to file'], + ['arg' => '--coverage-crap4j ', 'desc' => 'Write code coverage report in Crap4J XML format to file'], + ['arg' => '--coverage-html ', 'desc' => 'Write code coverage report in HTML format to directory'], + ['arg' => '--coverage-php ', 'desc' => 'Write serialized code coverage data to file'], + ['arg' => '--coverage-text=', 'desc' => 'Write code coverage report in text format to file [default: standard output]'], + ['arg' => '--only-summary-for-coverage-text', 'desc' => 'Option for code coverage report in text format: only show summary'], + ['arg' => '--show-uncovered-for-coverage-text', 'desc' => 'Option for code coverage report in text format: show uncovered files'], + ['arg' => '--coverage-xml ', 'desc' => 'Write code coverage report in XML format to directory'], + ['arg' => '--warm-coverage-cache', 'desc' => 'Warm static analysis cache'], + ['arg' => '--coverage-filter ', 'desc' => 'Include in code coverage reporting'], + ['arg' => '--path-coverage', 'desc' => 'Report path coverage in addition to line coverage'], + ['arg' => '--disable-coverage-ignore', 'desc' => 'Disable metadata for ignoring code coverage'], + ['arg' => '--no-coverage', 'desc' => 'Ignore code coverage reporting configured in the XML configuration file'], + ], + ]; + + if (defined('__PHPUNIT_PHAR__')) { + $elements['PHAR'] = [ + ['arg' => '--manifest', 'desc' => 'Print Software Bill of Materials (SBOM) in plain-text format'], + ['arg' => '--sbom', 'desc' => 'Print Software Bill of Materials (SBOM) in CycloneDX XML format'], + ['arg' => '--composer-lock', 'desc' => 'Print composer.lock file used to build the PHAR'], + ]; + } + + $elements['Miscellaneous'] = [ + ['arg' => '-h|--help', 'desc' => 'Prints this usage information'], + ['arg' => '--version', 'desc' => 'Prints the version and exits'], + ['arg' => '--atleast-version ', 'desc' => 'Checks that version is greater than and exits'], + ['arg' => '--check-version', 'desc' => 'Checks whether PHPUnit is the latest version and exits'], + ]; + + return $elements; + } } diff --git a/src/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.php b/src/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.php index 0300147725b..4e5565bb240 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.php +++ b/src/TextUI/Output/Default/ProgressPrinter/ProgressPrinter.php @@ -14,7 +14,6 @@ use function str_contains; use function str_repeat; use function strlen; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; use PHPUnit\Event\Test\DeprecationTriggered; use PHPUnit\Event\Test\Errored; @@ -25,7 +24,6 @@ use PHPUnit\Event\Test\PhpWarningTriggered; use PHPUnit\Event\Test\WarningTriggered; use PHPUnit\Event\TestRunner\ExecutionStarted; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\Framework\TestStatus\TestStatus; use PHPUnit\TextUI\Configuration\Source; use PHPUnit\TextUI\Configuration\SourceFilter; @@ -33,6 +31,8 @@ use PHPUnit\Util\Color; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ProgressPrinter @@ -49,10 +49,6 @@ final class ProgressPrinter private ?TestStatus $status = null; private bool $prepared = false; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(Printer $printer, Facade $facade, bool $colors, int $numberOfColumns, Source $source) { $this->printer = $printer; @@ -104,7 +100,7 @@ public function testTriggeredNotice(NoticeTriggered $event): void } if ($this->source->restrictNotices() && - !(new SourceFilter)->includes($this->source, $event->file())) { + !SourceFilter::instance()->includes($event->file())) { return; } @@ -122,7 +118,7 @@ public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void } if ($this->source->restrictNotices() && - !(new SourceFilter)->includes($this->source, $event->file())) { + !SourceFilter::instance()->includes($event->file())) { return; } @@ -139,8 +135,16 @@ public function testTriggeredDeprecation(DeprecationTriggered $event): void return; } - if ($this->source->restrictDeprecations() && - !(new SourceFilter)->includes($this->source, $event->file())) { + if ($this->source->ignoreSelfDeprecations() && + ($event->trigger()->isTest() || $event->trigger()->isSelf())) { + return; + } + + if ($this->source->ignoreDirectDeprecations() && $event->trigger()->isDirect()) { + return; + } + + if ($this->source->ignoreIndirectDeprecations() && $event->trigger()->isIndirect()) { return; } @@ -157,8 +161,16 @@ public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): voi return; } - if ($this->source->restrictDeprecations() && - !(new SourceFilter)->includes($this->source, $event->file())) { + if ($this->source->ignoreSelfDeprecations() && + ($event->trigger()->isTest() || $event->trigger()->isSelf())) { + return; + } + + if ($this->source->ignoreDirectDeprecations() && $event->trigger()->isDirect()) { + return; + } + + if ($this->source->ignoreIndirectDeprecations() && $event->trigger()->isIndirect()) { return; } @@ -186,7 +198,7 @@ public function testTriggeredWarning(WarningTriggered $event): void } if ($this->source->restrictWarnings() && - !(new SourceFilter)->includes($this->source, $event->file())) { + !SourceFilter::instance()->includes($event->file())) { return; } @@ -204,7 +216,7 @@ public function testTriggeredPhpWarning(PhpWarningTriggered $event): void } if ($this->source->restrictWarnings() && - !(new SourceFilter)->includes($this->source, $event->file())) { + !SourceFilter::instance()->includes($event->file())) { return; } @@ -278,10 +290,6 @@ public function testFinished(): void $this->prepared = false; } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private function registerSubscribers(Facade $facade): void { $facade->registerSubscribers( diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.php index 742aa95c5ab..2984cdda8b7 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/BeforeTestClassMethodErroredSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\BeforeFirstTestMethodErroredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class BeforeTestClassMethodErroredSubscriber extends Subscriber implements BeforeFirstTestMethodErroredSubscriber +final readonly class BeforeTestClassMethodErroredSubscriber extends Subscriber implements BeforeFirstTestMethodErroredSubscriber { public function notify(BeforeFirstTestMethodErrored $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php index 46aa73d2feb..32515ee343c 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/Subscriber.php @@ -10,11 +10,13 @@ namespace PHPUnit\TextUI\Output\Default\ProgressPrinter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -abstract class Subscriber +abstract readonly class Subscriber { - private readonly ProgressPrinter $printer; + private ProgressPrinter $printer; public function __construct(ProgressPrinter $printer) { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.php index a18303bc1ab..e5b57c6d3d6 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestConsideredRiskySubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ConsideredRiskySubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber +final readonly class TestConsideredRiskySubscriber extends Subscriber implements ConsideredRiskySubscriber { public function notify(ConsideredRisky $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.php index d9fbacd7296..33340755261 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestErroredSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ErroredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber +final readonly class TestErroredSubscriber extends Subscriber implements ErroredSubscriber { public function notify(Errored $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.php index 48ed642c4a7..9109d1b312b 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFailedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\FailedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFailedSubscriber extends Subscriber implements FailedSubscriber +final readonly class TestFailedSubscriber extends Subscriber implements FailedSubscriber { public function notify(Failed $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.php index 6322c128bad..e4b4ca5ee87 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestFinishedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\FinishedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber +final readonly class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber { public function notify(Finished $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.php index aa3bdb92041..8b445088064 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestMarkedIncompleteSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\MarkedIncompleteSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber +final readonly class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber { public function notify(MarkedIncomplete $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.php index 0924178e76d..d99f2fa4190 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestPreparedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PreparedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber +final readonly class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber { public function notify(Prepared $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php index 6735462c29f..78e104f6d46 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestRunnerExecutionStartedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\TestRunner\ExecutionStartedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestRunnerExecutionStartedSubscriber extends Subscriber implements ExecutionStartedSubscriber +final readonly class TestRunnerExecutionStartedSubscriber extends Subscriber implements ExecutionStartedSubscriber { public function notify(ExecutionStarted $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php index c7b29fe2d3c..a2f4e25569b 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\SkippedSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber +final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber { public function notify(Skipped $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php index f734cf55ddf..16a4ccf96ec 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\DeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber +final readonly class TestTriggeredDeprecationSubscriber extends Subscriber implements DeprecationTriggeredSubscriber { public function notify(DeprecationTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.php index 9c4cb0b0495..1f89911b45e 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredErrorSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\ErrorTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredErrorSubscriber extends Subscriber implements ErrorTriggeredSubscriber +final readonly class TestTriggeredErrorSubscriber extends Subscriber implements ErrorTriggeredSubscriber { public function notify(ErrorTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.php index 410fa531c9c..0639f027272 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredNoticeSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\NoticeTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber +final readonly class TestTriggeredNoticeSubscriber extends Subscriber implements NoticeTriggeredSubscriber { public function notify(NoticeTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php index 97de0032ad0..550250c2100 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpDeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber +final readonly class TestTriggeredPhpDeprecationSubscriber extends Subscriber implements PhpDeprecationTriggeredSubscriber { public function notify(PhpDeprecationTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.php index 861e2b8cb06..299b898c076 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpNoticeSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpNoticeTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber +final readonly class TestTriggeredPhpNoticeSubscriber extends Subscriber implements PhpNoticeTriggeredSubscriber { public function notify(PhpNoticeTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.php index 87bec210129..a4ff81c2276 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpWarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber +final readonly class TestTriggeredPhpWarningSubscriber extends Subscriber implements PhpWarningTriggeredSubscriber { public function notify(PhpWarningTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php index 8bceec7aeac..62311a0123a 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitDeprecationSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpunitDeprecationTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpunitDeprecationSubscriber extends Subscriber implements PhpunitDeprecationTriggeredSubscriber +final readonly class TestTriggeredPhpunitDeprecationSubscriber extends Subscriber implements PhpunitDeprecationTriggeredSubscriber { public function notify(PhpunitDeprecationTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php index 69a9458512e..4e3e3d21cef 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredPhpunitWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\PhpunitWarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredPhpunitWarningSubscriber extends Subscriber implements PhpunitWarningTriggeredSubscriber +final readonly class TestTriggeredPhpunitWarningSubscriber extends Subscriber implements PhpunitWarningTriggeredSubscriber { public function notify(PhpunitWarningTriggered $event): void { diff --git a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php index c80a3f5a501..620458422dd 100644 --- a/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php +++ b/src/TextUI/Output/Default/ProgressPrinter/Subscriber/TestTriggeredWarningSubscriber.php @@ -13,9 +13,11 @@ use PHPUnit\Event\Test\WarningTriggeredSubscriber; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber +final readonly class TestTriggeredWarningSubscriber extends Subscriber implements WarningTriggeredSubscriber { public function notify(WarningTriggered $event): void { diff --git a/src/TextUI/Output/Default/ResultPrinter.php b/src/TextUI/Output/Default/ResultPrinter.php index 8383bd917cc..555382db45f 100644 --- a/src/TextUI/Output/Default/ResultPrinter.php +++ b/src/TextUI/Output/Default/ResultPrinter.php @@ -26,6 +26,7 @@ use function trim; use PHPUnit\Event\Code\Test; use PHPUnit\Event\Code\TestMethod; +use PHPUnit\Event\Test\AfterLastTestMethodErrored; use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; use PHPUnit\Event\Test\ConsideredRisky; use PHPUnit\Event\Test\DeprecationTriggered; @@ -35,26 +36,29 @@ use PHPUnit\Event\Test\PhpNoticeTriggered; use PHPUnit\Event\Test\PhpunitDeprecationTriggered; use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitNoticeTriggered; use PHPUnit\Event\Test\PhpunitWarningTriggered; use PHPUnit\Event\Test\PhpWarningTriggered; use PHPUnit\Event\Test\WarningTriggered; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; use PHPUnit\TestRunner\TestResult\Issues\Issue; use PHPUnit\TestRunner\TestResult\TestResult; use PHPUnit\TextUI\Output\Printer; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ResultPrinter { private readonly Printer $printer; + private readonly bool $displayPhpunitDeprecations; private readonly bool $displayPhpunitErrors; + private readonly bool $displayPhpunitNotices; private readonly bool $displayPhpunitWarnings; private readonly bool $displayTestsWithErrors; private readonly bool $displayTestsWithFailedAssertions; private readonly bool $displayRiskyTests; - private readonly bool $displayPhpunitDeprecations; private readonly bool $displayDetailsOnIncompleteTests; private readonly bool $displayDetailsOnSkippedTests; private readonly bool $displayDetailsOnTestsThatTriggerDeprecations; @@ -64,12 +68,13 @@ final class ResultPrinter private readonly bool $displayDefectsInReverseOrder; private bool $listPrinted = false; - public function __construct(Printer $printer, bool $displayPhpunitErrors, bool $displayPhpunitWarnings, bool $displayPhpunitDeprecations, bool $displayTestsWithErrors, bool $displayTestsWithFailedAssertions, bool $displayRiskyTests, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $displayDefectsInReverseOrder) + public function __construct(Printer $printer, bool $displayPhpunitDeprecations, bool $displayPhpunitErrors, bool $displayPhpunitNotices, bool $displayPhpunitWarnings, bool $displayTestsWithErrors, bool $displayTestsWithFailedAssertions, bool $displayRiskyTests, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $displayDefectsInReverseOrder) { $this->printer = $printer; + $this->displayPhpunitDeprecations = $displayPhpunitDeprecations; $this->displayPhpunitErrors = $displayPhpunitErrors; + $this->displayPhpunitNotices = $displayPhpunitNotices; $this->displayPhpunitWarnings = $displayPhpunitWarnings; - $this->displayPhpunitDeprecations = $displayPhpunitDeprecations; $this->displayTestsWithErrors = $displayTestsWithErrors; $this->displayTestsWithFailedAssertions = $displayTestsWithFailedAssertions; $this->displayRiskyTests = $displayRiskyTests; @@ -82,7 +87,7 @@ public function __construct(Printer $printer, bool $displayPhpunitErrors, bool $ $this->displayDefectsInReverseOrder = $displayDefectsInReverseOrder; } - public function print(TestResult $result): void + public function print(TestResult $result, bool $stackTraceForDeprecations = false): void { if ($this->displayPhpunitErrors) { $this->printPhpunitErrors($result); @@ -96,6 +101,10 @@ public function print(TestResult $result): void $this->printTestRunnerDeprecations($result); } + if ($this->displayPhpunitNotices) { + $this->printTestRunnerNotices($result); + } + if ($this->displayTestsWithErrors) { $this->printTestsWithErrors($result); } @@ -116,6 +125,10 @@ public function print(TestResult $result): void $this->printRiskyTests($result); } + if ($this->displayPhpunitNotices) { + $this->printDetailsOnTestsThatTriggeredPhpunitNotices($result); + } + if ($this->displayDetailsOnIncompleteTests) { $this->printIncompleteTests($result); } @@ -141,15 +154,10 @@ public function print(TestResult $result): void if ($this->displayDetailsOnTestsThatTriggerDeprecations) { $this->printIssueList('PHP deprecation', $result->phpDeprecations()); - $this->printIssueList('deprecation', $result->deprecations()); + $this->printIssueList('deprecation', $result->deprecations(), $stackTraceForDeprecations); } } - public function flush(): void - { - $this->printer->flush(); - } - private function printPhpunitErrors(TestResult $result): void { if (!$result->hasTestTriggeredPhpunitErrorEvents()) { @@ -179,6 +187,49 @@ private function printDetailsOnTestsThatTriggeredPhpunitDeprecations(TestResult $this->printList($elements['elements']); } + private function printDetailsOnTestsThatTriggeredPhpunitNotices(TestResult $result): void + { + if (!$result->hasTestTriggeredPhpunitNoticeEvents()) { + return; + } + + $elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitNoticeEvents()); + + $this->printListHeaderWithNumberOfTestsAndNumberOfIssues( + $elements['numberOfTestsWithIssues'], + $elements['numberOfIssues'], + 'PHPUnit notice', + ); + + $this->printList($elements['elements']); + } + + private function printTestRunnerNotices(TestResult $result): void + { + if (!$result->hasTestRunnerTriggeredNoticeEvents()) { + return; + } + + $elements = []; + $messages = []; + + foreach ($result->testRunnerTriggeredNoticeEvents() as $event) { + if (isset($messages[$event->message()])) { + continue; + } + + $elements[] = [ + 'title' => $event->message(), + 'body' => '', + ]; + + $messages[$event->message()] = true; + } + + $this->printListHeaderWithNumber(count($elements), 'PHPUnit test runner notice'); + $this->printList($elements); + } + private function printTestRunnerWarnings(TestResult $result): void { if (!$result->hasTestRunnerTriggeredWarningEvents()) { @@ -186,12 +237,19 @@ private function printTestRunnerWarnings(TestResult $result): void } $elements = []; + $messages = []; foreach ($result->testRunnerTriggeredWarningEvents() as $event) { + if (isset($messages[$event->message()])) { + continue; + } + $elements[] = [ 'title' => $event->message(), 'body' => '', ]; + + $messages[$event->message()] = true; } $this->printListHeaderWithNumber(count($elements), 'PHPUnit test runner warning'); @@ -243,7 +301,7 @@ private function printTestsWithErrors(TestResult $result): void $elements = []; foreach ($result->testErroredEvents() as $event) { - if ($event instanceof BeforeFirstTestMethodErrored) { + if ($event instanceof AfterLastTestMethodErrored || $event instanceof BeforeFirstTestMethodErrored) { $title = $event->testClassName(); } else { $title = $this->name($event->test()); @@ -354,12 +412,12 @@ private function printSkippedTests(TestResult $result): void } /** - * @psalm-param non-empty-string $type - * @psalm-param list $issues + * @param non-empty-string $type + * @param list $issues */ - private function printIssueList(string $type, array $issues): void + private function printIssueList(string $type, array $issues, bool $stackTrace = false): void { - if (empty($issues)) { + if ($issues === []) { return; } @@ -393,24 +451,32 @@ private function printIssueList(string $type, array $issues): void $issue->line(), ); - $body = trim($issue->description()) . PHP_EOL . PHP_EOL . 'Triggered by:'; + $body = trim($issue->description()) . PHP_EOL . PHP_EOL; - $triggeringTests = $issue->triggeringTests(); + if ($stackTrace && $issue->hasStackTrace()) { + $body .= trim($issue->stackTrace()) . PHP_EOL . PHP_EOL; + } - ksort($triggeringTests); + if (!$issue->triggeredInTest()) { + $body .= 'Triggered by:'; - foreach ($triggeringTests as $triggeringTest) { - $body .= PHP_EOL . PHP_EOL . '* ' . $triggeringTest['test']->id(); + $triggeringTests = $issue->triggeringTests(); - if ($triggeringTest['count'] > 1) { - $body .= sprintf( - ' (%d times)', - $triggeringTest['count'], - ); - } + ksort($triggeringTests); - if ($triggeringTest['test']->isTestMethod()) { - $body .= PHP_EOL . ' ' . $triggeringTest['test']->file() . ':' . $triggeringTest['test']->line(); + foreach ($triggeringTests as $triggeringTest) { + $body .= PHP_EOL . PHP_EOL . '* ' . $triggeringTest['test']->id(); + + if ($triggeringTest['count'] > 1) { + $body .= sprintf( + ' (%d times)', + $triggeringTest['count'], + ); + } + + if ($triggeringTest['test']->isTestMethod()) { + $body .= PHP_EOL . ' ' . $triggeringTest['test']->file() . ':' . $triggeringTest['test']->line(); + } } } @@ -459,7 +525,7 @@ private function printListHeader(string $header): void } /** - * @psalm-param list $elements + * @param list $elements */ private function printList(array $elements): void { @@ -487,7 +553,7 @@ private function printListElement(int $number, string $title, string $body): voi $number, $title, $body, - !empty($body) ? "\n" : '', + $body !== '' ? "\n" : '', ), ); } @@ -502,29 +568,30 @@ private function printIssueListElement(int $number, string $title, string $body) $number, $title, $body, - !empty($body) ? "\n" : '', + $body !== '' ? "\n" : '', ), ); } - /** - * @throws NoDataSetFromDataProviderException - */ private function name(Test $test): string { if ($test->isTestMethod()) { assert($test instanceof TestMethod); - return $test->nameWithClass(); + if (!$test->testData()->hasDataFromDataProvider()) { + return $test->nameWithClass(); + } + + return $test->className() . '::' . $test->methodName() . $test->testData()->dataFromDataProvider()->dataAsStringForResultOutput(); } return $test->name(); } /** - * @psalm-param array> $events + * @param array> $events * - * @psalm-return array{numberOfTestsWithIssues: int, numberOfIssues: int, elements: list} + * @return array{numberOfTestsWithIssues: int, numberOfIssues: int, elements: list} */ private function mapTestsWithIssuesEventsToElements(array $events): array { @@ -552,7 +619,7 @@ private function mapTestsWithIssuesEventsToElements(array $events): array $issues++; } - if (!empty($testLocation)) { + if ($testLocation !== '') { $body .= $testLocation; } @@ -586,7 +653,7 @@ private function testLocation(Test $test): string ); } - private function reasonMessage(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single): string + private function reasonMessage(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitNoticeTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single): string { $message = trim($reason->message()); @@ -606,7 +673,7 @@ private function reasonMessage(ConsideredRisky|DeprecationTriggered|ErrorTrigger return $buffer; } - private function reasonLocation(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single): string + private function reasonLocation(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitNoticeTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single): string { if (!$reason instanceof DeprecationTriggered && !$reason instanceof PhpDeprecationTriggered && diff --git a/src/TextUI/Output/Default/UnexpectedOutputPrinter.php b/src/TextUI/Output/Default/UnexpectedOutputPrinter.php index af669906c18..7185e794d59 100644 --- a/src/TextUI/Output/Default/UnexpectedOutputPrinter.php +++ b/src/TextUI/Output/Default/UnexpectedOutputPrinter.php @@ -9,21 +9,15 @@ */ namespace PHPUnit\TextUI\Output\Default; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade; use PHPUnit\Event\Test\PrintedUnexpectedOutput; use PHPUnit\Event\Test\PrintedUnexpectedOutputSubscriber; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\TextUI\Output\Printer; final readonly class UnexpectedOutputPrinter implements PrintedUnexpectedOutputSubscriber { private Printer $printer; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public function __construct(Printer $printer, Facade $facade) { $this->printer = $printer; diff --git a/src/TextUI/Output/Facade.php b/src/TextUI/Output/Facade.php index 23f15236822..2a50017b0b7 100644 --- a/src/TextUI/Output/Facade.php +++ b/src/TextUI/Output/Facade.php @@ -9,15 +9,15 @@ */ namespace PHPUnit\TextUI\Output; +use const PHP_EOL; use function assert; -use PHPUnit\Event\EventFacadeIsSealedException; use PHPUnit\Event\Facade as EventFacade; -use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\Logging\TeamCity\TeamCityLogger; use PHPUnit\Logging\TestDox\TestResultCollection; +use PHPUnit\Runner\DirectoryDoesNotExistException; use PHPUnit\TestRunner\TestResult\TestResult; +use PHPUnit\TextUI\CannotOpenSocketException; use PHPUnit\TextUI\Configuration\Configuration; -use PHPUnit\TextUI\DirectoryDoesNotExistException; use PHPUnit\TextUI\InvalidSocketException; use PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter as DefaultProgressPrinter; use PHPUnit\TextUI\Output\Default\ResultPrinter as DefaultResultPrinter; @@ -27,6 +27,8 @@ use SebastianBergmann\Timer\ResourceUsageFormatter; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Facade @@ -37,16 +39,16 @@ final class Facade private static ?SummaryPrinter $summaryPrinter = null; private static bool $defaultProgressPrinter = false; - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ public static function init(Configuration $configuration, bool $extensionReplacesProgressOutput, bool $extensionReplacesResultOutput): Printer { self::createPrinter($configuration); assert(self::$printer !== null); + if ($configuration->debug()) { + return self::$printer; + } + self::createUnexpectedOutputPrinter(); if (!$extensionReplacesProgressOutput) { @@ -69,9 +71,9 @@ public static function init(Configuration $configuration, bool $extensionReplace } /** - * @psalm-param ?array $testDoxResult + * @param ?array $testDoxResult */ - public static function printResult(TestResult $result, ?array $testDoxResult, Duration $duration): void + public static function printResult(TestResult $result, ?array $testDoxResult, Duration $duration, bool $stackTraceForDeprecations): void { assert(self::$printer !== null); @@ -84,11 +86,11 @@ public static function printResult(TestResult $result, ?array $testDoxResult, Du } if (self::$testDoxResultPrinter !== null && $testDoxResult !== null) { - self::$testDoxResultPrinter->print($testDoxResult); + self::$testDoxResultPrinter->print($result, $testDoxResult); } if (self::$defaultResultPrinter !== null) { - self::$defaultResultPrinter->print($result); + self::$defaultResultPrinter->print($result, $stackTraceForDeprecations); } if (self::$summaryPrinter !== null) { @@ -97,6 +99,7 @@ public static function printResult(TestResult $result, ?array $testDoxResult, Du } /** + * @throws CannotOpenSocketException * @throws DirectoryDoesNotExistException * @throws InvalidSocketException */ @@ -117,6 +120,10 @@ private static function createPrinter(Configuration $configuration): void { $printerNeeded = false; + if ($configuration->debug()) { + $printerNeeded = true; + } + if ($configuration->outputIsTeamCity()) { $printerNeeded = true; } @@ -191,19 +198,20 @@ private static function createResultPrinter(Configuration $configuration): void if ($configuration->outputIsTestDox()) { self::$defaultResultPrinter = new DefaultResultPrinter( self::$printer, + $configuration->displayDetailsOnPhpunitDeprecations() || $configuration->displayDetailsOnAllIssues(), true, + $configuration->displayDetailsOnPhpunitNotices() || $configuration->displayDetailsOnAllIssues(), true, - true, - false, - false, - false, - false, - false, - false, false, false, + true, false, false, + $configuration->displayDetailsOnTestsThatTriggerDeprecations() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerErrors() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerNotices() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerWarnings() || $configuration->displayDetailsOnAllIssues(), + $configuration->reverseDefectList(), ); } @@ -211,6 +219,8 @@ private static function createResultPrinter(Configuration $configuration): void self::$testDoxResultPrinter = new TestDoxResultPrinter( self::$printer, $configuration->colors(), + $configuration->columns(), + $configuration->testDoxOutputWithSummary(), ); } @@ -224,18 +234,19 @@ private static function createResultPrinter(Configuration $configuration): void self::$defaultResultPrinter = new DefaultResultPrinter( self::$printer, + $configuration->displayDetailsOnPhpunitDeprecations() || $configuration->displayDetailsOnAllIssues(), true, + $configuration->displayDetailsOnPhpunitNotices() || $configuration->displayDetailsOnAllIssues(), true, true, true, true, - true, - $configuration->displayDetailsOnIncompleteTests(), - $configuration->displayDetailsOnSkippedTests(), - $configuration->displayDetailsOnTestsThatTriggerDeprecations(), - $configuration->displayDetailsOnTestsThatTriggerErrors(), - $configuration->displayDetailsOnTestsThatTriggerNotices(), - $configuration->displayDetailsOnTestsThatTriggerWarnings(), + $configuration->displayDetailsOnIncompleteTests() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnSkippedTests() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerDeprecations() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerErrors() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerNotices() || $configuration->displayDetailsOnAllIssues(), + $configuration->displayDetailsOnTestsThatTriggerWarnings() || $configuration->displayDetailsOnAllIssues(), $configuration->reverseDefectList(), ); } @@ -255,10 +266,6 @@ private static function createSummaryPrinter(Configuration $configuration): void ); } - /** - * @throws EventFacadeIsSealedException - * @throws UnknownSubscriberTypeException - */ private static function createUnexpectedOutputPrinter(): void { assert(self::$printer !== null); diff --git a/src/TextUI/Output/Printer/DefaultPrinter.php b/src/TextUI/Output/Printer/DefaultPrinter.php index 4f6809c4aaa..382f4815403 100644 --- a/src/TextUI/Output/Printer/DefaultPrinter.php +++ b/src/TextUI/Output/Printer/DefaultPrinter.php @@ -17,26 +17,29 @@ use function fopen; use function fsockopen; use function fwrite; -use function sprintf; use function str_replace; use function str_starts_with; -use PHPUnit\TextUI\DirectoryDoesNotExistException; +use PHPUnit\Runner\DirectoryDoesNotExistException; +use PHPUnit\TextUI\CannotOpenSocketException; use PHPUnit\TextUI\InvalidSocketException; use PHPUnit\Util\Filesystem; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class DefaultPrinter implements Printer { /** - * @psalm-var closed-resource|resource + * @var closed-resource|resource */ private $stream; private readonly bool $isPhpStream; private bool $isOpen; /** + * @throws CannotOpenSocketException * @throws DirectoryDoesNotExistException * @throws InvalidSocketException */ @@ -46,6 +49,7 @@ public static function from(string $out): self } /** + * @throws CannotOpenSocketException * @throws DirectoryDoesNotExistException * @throws InvalidSocketException */ @@ -55,6 +59,7 @@ public static function standardOutput(): self } /** + * @throws CannotOpenSocketException * @throws DirectoryDoesNotExistException * @throws InvalidSocketException */ @@ -64,36 +69,42 @@ public static function standardError(): self } /** + * @throws CannotOpenSocketException * @throws DirectoryDoesNotExistException * @throws InvalidSocketException */ private function __construct(string $out) { + $this->isPhpStream = str_starts_with($out, 'php://'); + if (str_starts_with($out, 'socket://')) { $tmp = explode(':', str_replace('socket://', '', $out)); if (count($tmp) !== 2) { - throw new InvalidSocketException( - sprintf( - '"%s" does not match "socket://hostname:port" format', - $out, - ), - ); + throw new InvalidSocketException($out); + } + + $stream = @fsockopen($tmp[0], (int) $tmp[1]); + + if ($stream === false) { + throw new CannotOpenSocketException($tmp[0], (int) $tmp[1]); } - $this->stream = fsockopen($tmp[0], (int) $tmp[1]); + $this->stream = $stream; $this->isOpen = true; return; } - $this->isPhpStream = str_starts_with($out, 'php://'); - if (!$this->isPhpStream && !Filesystem::createDirectory(dirname($out))) { throw new DirectoryDoesNotExistException(dirname($out)); } - $this->stream = fopen($out, 'wb'); + $stream = fopen($out, 'wb'); + + assert($stream !== false); + + $this->stream = $stream; $this->isOpen = true; } diff --git a/src/TextUI/Output/Printer/NullPrinter.php b/src/TextUI/Output/Printer/NullPrinter.php index e4a50101138..5e6b7ddf225 100644 --- a/src/TextUI/Output/Printer/NullPrinter.php +++ b/src/TextUI/Output/Printer/NullPrinter.php @@ -10,9 +10,11 @@ namespace PHPUnit\TextUI\Output; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class NullPrinter implements Printer +final readonly class NullPrinter implements Printer { public function print(string $buffer): void { diff --git a/src/TextUI/Output/Printer/Printer.php b/src/TextUI/Output/Printer/Printer.php index bc2e8bb5348..c9b0fb97069 100644 --- a/src/TextUI/Output/Printer/Printer.php +++ b/src/TextUI/Output/Printer/Printer.php @@ -10,6 +10,8 @@ namespace PHPUnit\TextUI\Output; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Printer diff --git a/src/TextUI/Output/SummaryPrinter.php b/src/TextUI/Output/SummaryPrinter.php index 7168815c63f..49e930e4bd9 100644 --- a/src/TextUI/Output/SummaryPrinter.php +++ b/src/TextUI/Output/SummaryPrinter.php @@ -9,11 +9,14 @@ */ namespace PHPUnit\TextUI\Output; +use const PHP_EOL; use function sprintf; use PHPUnit\TestRunner\TestResult\TestResult; use PHPUnit\Util\Color; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class SummaryPrinter @@ -110,7 +113,9 @@ public function print(TestResult $result): void $this->printCountString($result->numberOfErrors(), 'Errors', $color); $this->printCountString($result->numberOfTestFailedEvents(), 'Failures', $color); $this->printCountString($result->numberOfWarnings(), 'Warnings', $color); - $this->printCountString($result->numberOfDeprecations(), 'Deprecations', $color); + $this->printCountString($result->numberOfPhpOrUserDeprecations(), 'Deprecations', $color); + $this->printCountString($result->numberOfPhpunitDeprecations(), 'PHPUnit Deprecations', $color); + $this->printCountString($result->numberOfPhpunitNotices(), 'PHPUnit Notices', $color); $this->printCountString($result->numberOfNotices(), 'Notices', $color); $this->printCountString($result->numberOfTestSuiteSkippedEvents() + $result->numberOfTestSkippedEvents(), 'Skipped', $color); $this->printCountString($result->numberOfTestMarkedIncompleteEvents(), 'Incomplete', $color); diff --git a/src/TextUI/Output/TestDox/ResultPrinter.php b/src/TextUI/Output/TestDox/ResultPrinter.php index 305c0f22da4..bfd7878e00b 100644 --- a/src/TextUI/Output/TestDox/ResultPrinter.php +++ b/src/TextUI/Output/TestDox/ResultPrinter.php @@ -17,39 +17,113 @@ use function preg_match; use function preg_split; use function rtrim; +use function sprintf; use function str_starts_with; use function trim; use PHPUnit\Event\Code\Throwable; -use PHPUnit\Event\TestData\NoDataSetFromDataProviderException; +use PHPUnit\Event\Test\AfterLastTestMethodErrored; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; use PHPUnit\Framework\TestStatus\TestStatus; use PHPUnit\Logging\TestDox\TestResult as TestDoxTestResult; use PHPUnit\Logging\TestDox\TestResultCollection; +use PHPUnit\TestRunner\TestResult\TestResult; use PHPUnit\TextUI\Output\Printer; use PHPUnit\Util\Color; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final readonly class ResultPrinter { private Printer $printer; private bool $colors; + private int $columns; + private bool $printSummary; - public function __construct(Printer $printer, bool $colors) + public function __construct(Printer $printer, bool $colors, int $columns, bool $printSummary) { - $this->printer = $printer; - $this->colors = $colors; + $this->printer = $printer; + $this->colors = $colors; + $this->columns = $columns; + $this->printSummary = $printSummary; } /** - * @psalm-param array $tests + * @param array $tests */ - public function print(array $tests): void + public function print(TestResult $result, array $tests): void + { + $this->doPrint($tests, false); + + if ($this->printSummary) { + $this->printer->print('Summary of tests with errors, failures, or issues:' . PHP_EOL . PHP_EOL); + + $this->doPrint($tests, true); + } + + $beforeFirstTestMethodErrored = []; + $afterLastTestMethodErrored = []; + + foreach ($result->testErroredEvents() as $error) { + if ($error instanceof BeforeFirstTestMethodErrored) { + $beforeFirstTestMethodErrored[$error->calledMethod()->className() . '::' . $error->calledMethod()->methodName()] = $error; + } + + if ($error instanceof AfterLastTestMethodErrored) { + $afterLastTestMethodErrored[$error->calledMethod()->className() . '::' . $error->calledMethod()->methodName()] = $error; + } + } + + $this->printBeforeClassOrAfterClassErrors( + 'before-first-test', + $beforeFirstTestMethodErrored, + ); + + $this->printBeforeClassOrAfterClassErrors( + 'after-last-test', + $afterLastTestMethodErrored, + ); + } + + /** + * @param array $tests + */ + private function doPrint(array $tests, bool $onlySummary): void { foreach ($tests as $prettifiedClassName => $_tests) { + $print = true; + + if ($onlySummary) { + $found = false; + + foreach ($_tests as $test) { + if ($test->status()->isSuccess()) { + continue; + } + + $found = true; + + break; + } + + if (!$found) { + $print = false; + } + } + + if (!$print) { + continue; + } + $this->printPrettifiedClassName($prettifiedClassName); foreach ($_tests as $test) { + if ($onlySummary && $test->status()->isSuccess()) { + continue; + } + $this->printTestResult($test); } @@ -57,14 +131,6 @@ public function print(array $tests): void } } - public function flush(): void - { - $this->printer->flush(); - } - - /** - * @psalm-param string $prettifiedClassName - */ private function printPrettifiedClassName(string $prettifiedClassName): void { $buffer = $prettifiedClassName; @@ -76,18 +142,12 @@ private function printPrettifiedClassName(string $prettifiedClassName): void $this->printer->print($buffer . PHP_EOL); } - /** - * @throws NoDataSetFromDataProviderException - */ private function printTestResult(TestDoxTestResult $test): void { $this->printTestResultHeader($test); $this->printTestResultBody($test); } - /** - * @throws NoDataSetFromDataProviderException - */ private function printTestResultHeader(TestDoxTestResult $test): void { $buffer = ' ' . $this->symbolFor($test->status()) . ' '; @@ -157,14 +217,14 @@ private function printThrowable(TestDoxTestResult $test): void $stackTrace = $this->formatStackTrace($throwable->stackTrace()); $diff = ''; - if (!empty($message) && $this->colors) { + if ($message !== '' && $this->colors) { ['message' => $message, 'diff' => $diff] = $this->colorizeMessageAndDiff( $message, $this->messageColorFor($test->status()), ); } - if (!empty($message)) { + if ($message !== '') { $this->printer->print( $this->prefixLines( $this->prefixFor('message', $test->status()), @@ -175,7 +235,7 @@ private function printThrowable(TestDoxTestResult $test): void $this->printer->print(PHP_EOL); } - if (!empty($diff)) { + if ($diff !== '') { $this->printer->print( $this->prefixLines( $this->prefixFor('diff', $test->status()), @@ -186,8 +246,8 @@ private function printThrowable(TestDoxTestResult $test): void $this->printer->print(PHP_EOL); } - if (!empty($stackTrace)) { - if (!empty($message) || !empty($diff)) { + if ($stackTrace !== '') { + if ($message !== '' || $diff !== '') { $prefix = $this->prefixFor('default', $test->status()); } else { $prefix = $this->prefixFor('trace', $test->status()); @@ -200,11 +260,16 @@ private function printThrowable(TestDoxTestResult $test): void } /** - * @psalm-return array{message: string, diff: string} + * @return array{message: string, diff: string} */ private function colorizeMessageAndDiff(string $buffer, string $style): array { - $lines = $buffer ? array_map('\rtrim', explode(PHP_EOL, $buffer)) : []; + $lines = []; + + if ($buffer !== '') { + $lines = array_map('\rtrim', explode(PHP_EOL, $buffer)); + } + $message = []; $diff = []; $insideDiff = false; @@ -232,8 +297,9 @@ private function colorizeMessageAndDiff(string $buffer, string $style): array $message = implode(PHP_EOL, $message); $diff = implode(PHP_EOL, $diff); - if (!empty($message)) { - $message = Color::colorizeTextBox($style, $message); + if ($message !== '') { + // Testdox output has a left-margin of 5; keep right-margin to prevent terminal scrolling + $message = Color::colorizeTextBox($style, $message, $this->columns - 7); } return [ @@ -252,7 +318,7 @@ private function formatStackTrace(string $stackTrace): string $previousPath = ''; foreach (explode(PHP_EOL, $stackTrace) as $line) { - if (preg_match('/^(.*):(\d+)$/', $line, $matches)) { + if (preg_match('/^(.*):(\d+)$/', $line, $matches) > 0) { $lines[] = Color::colorizePath($matches[1], $previousPath) . Color::dim(':') . Color::colorize('fg-blue', $matches[2]) . "\n"; $previousPath = $matches[1]; @@ -268,17 +334,23 @@ private function formatStackTrace(string $stackTrace): string private function prefixLines(string $prefix, string $message): string { + $lines = preg_split('/\r\n|\r|\n/', $message); + + if ($lines === false) { + $lines = []; + } + return implode( PHP_EOL, array_map( - static fn (string $line) => ' ' . $prefix . ($line ? ' ' . $line : ''), - preg_split('/\r\n|\r|\n/', $message), + static fn (string $line) => ' ' . $prefix . ($line !== '' ? ' ' . $line : ''), + $lines, ), ); } /** - * @psalm-param 'default'|'start'|'message'|'diff'|'trace'|'last' $type + * @param 'default'|'diff'|'last'|'message'|'start'|'trace' $type */ private function prefixFor(string $type, TestStatus $status): string { @@ -317,7 +389,7 @@ private function colorFor(TestStatus $status): string return 'fg-cyan'; } - if ($status->isRisky() || $status->isIncomplete() || $status->isWarning()) { + if ($status->isIncomplete() || $status->isDeprecation() || $status->isNotice() || $status->isRisky() || $status->isWarning()) { return 'fg-yellow'; } @@ -342,7 +414,7 @@ private function messageColorFor(TestStatus $status): string return 'fg-cyan'; } - if ($status->isRisky() || $status->isIncomplete() || $status->isWarning()) { + if ($status->isIncomplete() || $status->isDeprecation() || $status->isNotice() || $status->isRisky() || $status->isWarning()) { return 'fg-yellow'; } @@ -363,18 +435,49 @@ private function symbolFor(TestStatus $status): string return '↩'; } - if ($status->isRisky()) { - return '☢'; + if ($status->isDeprecation() || $status->isNotice() || $status->isRisky() || $status->isWarning()) { + return '⚠'; } if ($status->isIncomplete()) { return '∅'; } - if ($status->isWarning()) { - return '⚠'; + return '?'; + } + + /** + * @param 'after-last-test'|'before-first-test' $type + * @param array $errors + */ + private function printBeforeClassOrAfterClassErrors(string $type, array $errors): void + { + if ($errors === []) { + return; } - return '?'; + $this->printer->print( + sprintf( + 'These %s methods errored:' . PHP_EOL . PHP_EOL, + $type, + ), + ); + + $index = 0; + + foreach ($errors as $method => $error) { + $this->printer->print( + sprintf( + '%d) %s' . PHP_EOL, + ++$index, + $method, + ), + ); + + $this->printer->print(trim($error->throwable()->description()) . PHP_EOL . PHP_EOL); + $this->printer->print($this->formatStackTrace($error->throwable()->stackTrace()) . PHP_EOL); + } + + $this->printer->print(PHP_EOL); } } diff --git a/src/TextUI/ShellExitCodeCalculator.php b/src/TextUI/ShellExitCodeCalculator.php index 38fe77e7322..40ab63576bd 100644 --- a/src/TextUI/ShellExitCodeCalculator.php +++ b/src/TextUI/ShellExitCodeCalculator.php @@ -12,15 +12,17 @@ use PHPUnit\TestRunner\TestResult\TestResult; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ShellExitCodeCalculator +final readonly class ShellExitCodeCalculator { - private const SUCCESS_EXIT = 0; - private const FAILURE_EXIT = 1; - private const EXCEPTION_EXIT = 2; + private const int SUCCESS_EXIT = 0; + private const int FAILURE_EXIT = 1; + private const int EXCEPTION_EXIT = 2; - public function calculate(bool $failOnDeprecation, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, TestResult $result): int + public function calculate(bool $failOnDeprecation, bool $failOnPhpunitDeprecation, bool $failOnPhpunitNotice, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, TestResult $result): int { $returnCode = self::FAILURE_EXIT; @@ -33,7 +35,15 @@ public function calculate(bool $failOnDeprecation, bool $failOnEmptyTestSuite, b } if ($result->wasSuccessfulIgnoringPhpunitWarnings()) { - if ($failOnDeprecation && $result->hasDeprecations()) { + if ($failOnDeprecation && $result->hasPhpOrUserDeprecations()) { + $returnCode = self::FAILURE_EXIT; + } + + if ($failOnPhpunitDeprecation && $result->hasPhpunitDeprecations()) { + $returnCode = self::FAILURE_EXIT; + } + + if ($failOnPhpunitNotice && $result->hasPhpunitNotices()) { $returnCode = self::FAILURE_EXIT; } diff --git a/src/TextUI/TestRunner.php b/src/TextUI/TestRunner.php index 97460e2cb80..2363ca290b9 100644 --- a/src/TextUI/TestRunner.php +++ b/src/TextUI/TestRunner.php @@ -12,13 +12,14 @@ use function mt_srand; use PHPUnit\Event; use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\Filter\Factory; use PHPUnit\Runner\ResultCache\ResultCache; use PHPUnit\Runner\TestSuiteSorter; use PHPUnit\TextUI\Configuration\Configuration; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class TestRunner @@ -54,7 +55,7 @@ public function run(Configuration $configuration, ResultCache $resultCache, Test ); } - (new TestSuiteFilterProcessor(new Factory))->process($configuration, $suite); + (new TestSuiteFilterProcessor)->process($configuration, $suite); Event\Facade::emitter()->testRunnerExecutionStarted( Event\TestSuite\TestSuiteBuilder::from($suite), diff --git a/src/TextUI/TestSuiteFilterProcessor.php b/src/TextUI/TestSuiteFilterProcessor.php index 18d8a9fd3f4..c0e4beb1936 100644 --- a/src/TextUI/TestSuiteFilterProcessor.php +++ b/src/TextUI/TestSuiteFilterProcessor.php @@ -17,45 +17,44 @@ use PHPUnit\TextUI\Configuration\FilterNotConfiguredException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class TestSuiteFilterProcessor +final readonly class TestSuiteFilterProcessor { - private Factory $filterFactory; - - public function __construct(Factory $factory = new Factory) - { - $this->filterFactory = $factory; - } - /** * @throws Event\RuntimeException * @throws FilterNotConfiguredException */ public function process(Configuration $configuration, TestSuite $suite): void { + $factory = new Factory; + if (!$configuration->hasFilter() && !$configuration->hasGroups() && !$configuration->hasExcludeGroups() && + !$configuration->hasExcludeFilter() && !$configuration->hasTestsCovering() && - !$configuration->hasTestsUsing()) { + !$configuration->hasTestsUsing() && + !$configuration->hasTestsRequiringPhpExtension()) { return; } if ($configuration->hasExcludeGroups()) { - $this->filterFactory->addExcludeGroupFilter( + $factory->addExcludeGroupFilter( $configuration->excludeGroups(), ); } if ($configuration->hasGroups()) { - $this->filterFactory->addIncludeGroupFilter( + $factory->addIncludeGroupFilter( $configuration->groups(), ); } if ($configuration->hasTestsCovering()) { - $this->filterFactory->addIncludeGroupFilter( + $factory->addIncludeGroupFilter( array_map( static fn (string $name): string => '__phpunit_covers_' . $name, $configuration->testsCovering(), @@ -64,7 +63,7 @@ public function process(Configuration $configuration, TestSuite $suite): void } if ($configuration->hasTestsUsing()) { - $this->filterFactory->addIncludeGroupFilter( + $factory->addIncludeGroupFilter( array_map( static fn (string $name): string => '__phpunit_uses_' . $name, $configuration->testsUsing(), @@ -72,13 +71,28 @@ public function process(Configuration $configuration, TestSuite $suite): void ); } + if ($configuration->hasTestsRequiringPhpExtension()) { + $factory->addIncludeGroupFilter( + array_map( + static fn (string $name): string => '__phpunit_requires_php_extension' . $name, + $configuration->testsRequiringPhpExtension(), + ), + ); + } + + if ($configuration->hasExcludeFilter()) { + $factory->addExcludeNameFilter( + $configuration->excludeFilter(), + ); + } + if ($configuration->hasFilter()) { - $this->filterFactory->addNameFilter( + $factory->addIncludeNameFilter( $configuration->filter(), ); } - $suite->injectFilter($this->filterFactory); + $suite->injectFilter($factory); Event\Facade::emitter()->testSuiteFiltered( Event\TestSuite\TestSuiteBuilder::from($suite), diff --git a/src/Util/Cloner.php b/src/Util/Cloner.php deleted file mode 100644 index 0102b51b861..00000000000 --- a/src/Util/Cloner.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use Throwable; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -final class Cloner -{ - /** - * @psalm-template OriginalType - * - * @psalm-param OriginalType $original - * - * @psalm-return OriginalType - */ - public static function clone(object $original): object - { - try { - return clone $original; - } catch (Throwable) { - return $original; - } - } -} diff --git a/src/Util/Color.php b/src/Util/Color.php index 0f6bee49235..c225e37b73e 100644 --- a/src/Util/Color.php +++ b/src/Util/Color.php @@ -12,6 +12,7 @@ use const DIRECTORY_SEPARATOR; use const PHP_EOL; use function array_map; +use function array_walk; use function count; use function explode; use function implode; @@ -26,22 +27,24 @@ use function trim; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Color { /** - * @psalm-var array + * @var non-empty-array */ - private const WHITESPACE_MAP = [ + private const array WHITESPACE_MAP = [ ' ' => '·', "\t" => '⇥', ]; /** - * @psalm-var array + * @var non-empty-array */ - private const WHITESPACE_EOL_MAP = [ + private const array WHITESPACE_EOL_MAP = [ ' ' => '·', "\t" => '⇥', "\n" => '↵', @@ -49,9 +52,9 @@ final class Color ]; /** - * @psalm-var array + * @var non-empty-array */ - private static array $ansiCodes = [ + private const array ANSI_CODES = [ 'reset' => '0', 'bold' => '1', 'dim' => '2', @@ -87,30 +90,33 @@ public static function colorize(string $color, string $buffer): string $styles = []; foreach ($codes as $code) { - if (isset(self::$ansiCodes[$code])) { - $styles[] = self::$ansiCodes[$code] ?? ''; + if (isset(self::ANSI_CODES[$code])) { + $styles[] = self::ANSI_CODES[$code]; } } - if (empty($styles)) { + if ($styles === []) { return $buffer; } return self::optimizeColor(sprintf("\x1b[%sm", implode(';', $styles)) . $buffer . "\x1b[0m"); } - public static function colorizeTextBox(string $color, string $buffer): string + public static function colorizeTextBox(string $color, string $buffer, ?int $columns = null): string { - $lines = preg_split('/\r\n|\r|\n/', $buffer); - $padding = max(array_map('\strlen', $lines)); - - $styledLines = []; + $lines = preg_split('/\r\n|\r|\n/', $buffer); + $maxBoxWidth = max(array_map('\strlen', $lines)); - foreach ($lines as $line) { - $styledLines[] = self::colorize($color, str_pad($line, $padding)); + if ($columns !== null) { + $maxBoxWidth = min($maxBoxWidth, $columns); } - return implode(PHP_EOL, $styledLines); + array_walk($lines, static function (string &$line) use ($color, $maxBoxWidth): void + { + $line = self::colorize($color, str_pad($line, $maxBoxWidth)); + }); + + return implode(PHP_EOL, $lines); } public static function colorizePath(string $path, ?string $previousPath = null, bool $colorizeFilename = false): string @@ -132,7 +138,7 @@ public static function colorizePath(string $path, ?string $previousPath = null, $last = count($path) - 1; $path[$last] = preg_replace_callback( '/([\-_.]+|phpt$)/', - static fn ($matches) => self::dim($matches[0]), + static fn (array $matches) => self::dim($matches[0]), $path[$last], ); } @@ -155,7 +161,7 @@ public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = return preg_replace_callback( '/\s+/', - static fn ($matches) => self::dim(strtr($matches[0], $replaceMap)), + static fn (array $matches) => self::dim(strtr($matches[0], $replaceMap)), $buffer, ); } diff --git a/src/Util/Exception/Exception.php b/src/Util/Exception/Exception.php index a66f00b0a23..58f42db77f3 100644 --- a/src/Util/Exception/Exception.php +++ b/src/Util/Exception/Exception.php @@ -12,6 +12,8 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends Throwable diff --git a/src/Util/Exception/InvalidDirectoryException.php b/src/Util/Exception/InvalidDirectoryException.php index bdfa84b5438..623af2ded5f 100644 --- a/src/Util/Exception/InvalidDirectoryException.php +++ b/src/Util/Exception/InvalidDirectoryException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidDirectoryException extends RuntimeException implements Exception diff --git a/src/Util/Exception/InvalidJsonException.php b/src/Util/Exception/InvalidJsonException.php index 3d30ce4b881..224f7115c59 100644 --- a/src/Util/Exception/InvalidJsonException.php +++ b/src/Util/Exception/InvalidJsonException.php @@ -12,6 +12,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidJsonException extends RuntimeException implements Exception diff --git a/src/Util/Exception/InvalidVersionOperatorException.php b/src/Util/Exception/InvalidVersionOperatorException.php index 49b50e4be59..bc2fe9a0ed5 100644 --- a/src/Util/Exception/InvalidVersionOperatorException.php +++ b/src/Util/Exception/InvalidVersionOperatorException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidVersionOperatorException extends RuntimeException implements Exception diff --git a/src/Util/Exception/PhpProcessException.php b/src/Util/Exception/PhpProcessException.php index 0a6b6ee7652..05069ef05ec 100644 --- a/src/Util/Exception/PhpProcessException.php +++ b/src/Util/Exception/PhpProcessException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class PhpProcessException extends RuntimeException implements Exception diff --git a/src/Util/Exception/XmlException.php b/src/Util/Exception/XmlException.php index f2dc45d3787..127e1ecaffe 100644 --- a/src/Util/Exception/XmlException.php +++ b/src/Util/Exception/XmlException.php @@ -13,6 +13,8 @@ use RuntimeException; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class XmlException extends RuntimeException implements Exception diff --git a/src/Util/ExcludeList.php b/src/Util/ExcludeList.php index f3752278449..7ec8d4273f2 100644 --- a/src/Util/ExcludeList.php +++ b/src/Util/ExcludeList.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\Util; +use const PHP_OS_FAMILY; +use function assert; use function class_exists; use function defined; use function dirname; @@ -25,8 +27,6 @@ use ReflectionClass; use SebastianBergmann\CliParser\Parser as CliParser; use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeUnit\CodeUnit; -use SebastianBergmann\CodeUnitReverseLookup\Wizard; use SebastianBergmann\Comparator\Comparator; use SebastianBergmann\Complexity\Calculator; use SebastianBergmann\Diff\Diff; @@ -37,11 +37,13 @@ use SebastianBergmann\Invoker\Invoker; use SebastianBergmann\LinesOfCode\Counter; use SebastianBergmann\ObjectEnumerator\Enumerator; +use SebastianBergmann\ObjectReflector\ObjectReflector; use SebastianBergmann\RecursionContext\Context; use SebastianBergmann\Template\Template; use SebastianBergmann\Timer\Timer; use SebastianBergmann\Type\TypeName; use SebastianBergmann\Version; +use staabm\SideEffectsDetector\SideEffectsDetector; use TheSeer\Tokenizer\Tokenizer; /** @@ -50,9 +52,9 @@ final class ExcludeList { /** - * @psalm-var array + * @var non-empty-array */ - private const EXCLUDED_CLASS_NAMES = [ + private const array EXCLUDED_CLASS_NAMES = [ // composer ClassLoader::class => 1, @@ -89,12 +91,6 @@ final class ExcludeList // sebastian/cli-parser CliParser::class => 1, - // sebastian/code-unit - CodeUnit::class => 1, - - // sebastian/code-unit-reverse-lookup - Wizard::class => 1, - // sebastian/comparator Comparator::class => 1, @@ -119,6 +115,9 @@ final class ExcludeList // sebastian/object-enumerator Enumerator::class => 1, + // sebastian/object-reflector + ObjectReflector::class => 1, + // sebastian/recursion-context Context::class => 1, @@ -128,19 +127,22 @@ final class ExcludeList // sebastian/version Version::class => 1, + // staabm/side-effects-detector + SideEffectsDetector::class => 1, + // theseer/tokenizer Tokenizer::class => 1, ]; /** - * @psalm-var list + * @var list */ private static array $directories = []; private static bool $initialized = false; private readonly bool $enabled; /** - * @psalm-param non-empty-string $directory + * @param non-empty-string $directory * * @throws InvalidDirectoryException */ @@ -150,7 +152,11 @@ public static function addDirectory(string $directory): void throw new InvalidDirectoryException($directory); } - self::$directories[] = realpath($directory); + $directory = realpath($directory); + + assert($directory !== false); + + self::$directories[] = $directory; } public function __construct(?bool $enabled = null) @@ -163,7 +169,7 @@ public function __construct(?bool $enabled = null) } /** - * @psalm-return list + * @return list */ public function getExcludedDirectories(): array { @@ -209,11 +215,16 @@ private static function initialize(): void self::$directories[] = $directory; } - // Hide process isolation workaround on Windows. + /** + * Hide process isolation workaround on Windows: + * tempnam() prefix is limited to first 3 characters. + * + * @see https://php.net/manual/en/function.tempnam.php + */ if (PHP_OS_FAMILY === 'Windows') { - // tempnam() prefix is limited to first 3 chars. - // @see https://php.net/manual/en/function.tempnam.php + // @codeCoverageIgnoreStart self::$directories[] = sys_get_temp_dir() . '\\PHP'; + // @codeCoverageIgnoreEnd } self::$initialized = true; diff --git a/src/Util/Exporter.php b/src/Util/Exporter.php index 4cbe4141ff8..182499e9da0 100644 --- a/src/Util/Exporter.php +++ b/src/Util/Exporter.php @@ -9,39 +9,44 @@ */ namespace PHPUnit\Util; -use function is_array; -use function is_scalar; +use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; +use SebastianBergmann\Exporter\Exporter as OriginalExporter; /** - * @internal This class is not covered by the backward compatibility promise for PHPUnit + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final class Exporter { - public static function export(mixed $value, bool $exportObjects = false): string + private static ?OriginalExporter $exporter = null; + + public static function export(mixed $value): string { - if (self::isScalarOrArrayOfScalars($value) || $exportObjects) { - return (new \SebastianBergmann\Exporter\Exporter)->export($value); - } + return self::exporter()->export($value); + } - return '{enable export of objects to see this value}'; + /** + * @param array $data + */ + public static function shortenedRecursiveExport(array $data): string + { + return self::exporter()->shortenedRecursiveExport($data); } - private static function isScalarOrArrayOfScalars(mixed $value): bool + public static function shortenedExport(mixed $value): string { - if (is_scalar($value)) { - return true; - } + return self::exporter()->shortenedExport($value); + } - if (!is_array($value)) { - return false; + private static function exporter(): OriginalExporter + { + if (self::$exporter !== null) { + return self::$exporter; } - foreach ($value as $_value) { - if (!self::isScalarOrArrayOfScalars($_value)) { - return false; - } - } + self::$exporter = new OriginalExporter( + ConfigurationRegistry::get()->shortenArraysForExportThreshold(), + ); - return true; + return self::$exporter; } } diff --git a/src/Util/Filesystem.php b/src/Util/Filesystem.php index 9789fe29a74..3da54042379 100644 --- a/src/Util/Filesystem.php +++ b/src/Util/Filesystem.php @@ -9,16 +9,43 @@ */ namespace PHPUnit\Util; +use const DIRECTORY_SEPARATOR; +use function basename; +use function dirname; use function is_dir; use function mkdir; +use function realpath; +use function str_starts_with; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Filesystem +final readonly class Filesystem { public static function createDirectory(string $directory): bool { return !(!is_dir($directory) && !@mkdir($directory, 0o777, true) && !is_dir($directory)); } + + /** + * @param non-empty-string $path + * + * @return false|non-empty-string + */ + public static function resolveStreamOrFile(string $path): false|string + { + if (str_starts_with($path, 'php://') || str_starts_with($path, 'socket://')) { + return $path; + } + + $directory = dirname($path); + + if (is_dir($directory)) { + return realpath($directory) . DIRECTORY_SEPARATOR . basename($path); + } + + return false; + } } diff --git a/src/Util/Filter.php b/src/Util/Filter.php index 512d22623c2..122e5c0655f 100644 --- a/src/Util/Filter.php +++ b/src/Util/Filter.php @@ -12,6 +12,7 @@ use function array_unshift; use function defined; use function in_array; +use function is_array; use function is_file; use function realpath; use function sprintf; @@ -21,48 +22,57 @@ use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Filter +final readonly class Filter { /** * @throws Exception */ - public static function getFilteredStacktrace(Throwable $t): string + public static function stackTraceFromThrowableAsString(Throwable $t, bool $unwrap = true): string { - $filteredStacktrace = ''; - if ($t instanceof PhptAssertionFailedError) { - $eTrace = $t->syntheticTrace(); - $eFile = $t->syntheticFile(); - $eLine = $t->syntheticLine(); + $stackTrace = $t->syntheticTrace(); + $file = $t->syntheticFile(); + $line = $t->syntheticLine(); } elseif ($t instanceof Exception) { - $eTrace = $t->getSerializableTrace(); - $eFile = $t->getFile(); - $eLine = $t->getLine(); + $stackTrace = $t->getSerializableTrace(); + $file = $t->getFile(); + $line = $t->getLine(); } else { - if ($t->getPrevious()) { + if ($unwrap && $t->getPrevious() !== null) { $t = $t->getPrevious(); } - $eTrace = $t->getTrace(); - $eFile = $t->getFile(); - $eLine = $t->getLine(); + $stackTrace = $t->getTrace(); + $file = $t->getFile(); + $line = $t->getLine(); } - if (!self::frameExists($eTrace, $eFile, $eLine)) { + if (!self::frameExists($stackTrace, $file, $line)) { array_unshift( - $eTrace, - ['file' => $eFile, 'line' => $eLine], + $stackTrace, + ['file' => $file, 'line' => $line], ); } + return self::stackTraceAsString($stackTrace); + } + + /** + * @param list $frames + */ + private static function stackTraceAsString(array $frames): string + { + $buffer = ''; $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : false; $excludeList = new ExcludeList; - foreach ($eTrace as $frame) { + foreach ($frames as $frame) { if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { - $filteredStacktrace .= sprintf( + $buffer .= sprintf( "%s:%s\n", $frame['file'], $frame['line'] ?? '?', @@ -70,9 +80,12 @@ public static function getFilteredStacktrace(Throwable $t): string } } - return $filteredStacktrace; + return $buffer; } + /** + * @param array{file?: non-empty-string} $frame + */ private static function shouldPrintFrame(array $frame, false|string $prefix, ExcludeList $excludeList): bool { if (!isset($frame['file'])) { @@ -86,22 +99,29 @@ private static function shouldPrintFrame(array $frame, false|string $prefix, Exc if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) { $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); } else { + // @codeCoverageIgnoreStart $script = ''; + // @codeCoverageIgnoreEnd } - return is_file($file) && + return $fileIsNotPrefixed && + $file !== $script && self::fileIsExcluded($file, $excludeList) && - $fileIsNotPrefixed && - $file !== $script; + is_file($file); } private static function fileIsExcluded(string $file, ExcludeList $excludeList): bool { - return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || + return (!isset($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || + !is_array($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || + $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] === [] || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) && !$excludeList->isExcluded($file); } + /** + * @param list $trace + */ private static function frameExists(array $trace, string $file, int $line): bool { foreach ($trace as $frame) { diff --git a/src/Util/GlobalState.php b/src/Util/GlobalState.php index a364bca641c..717ad4dc472 100644 --- a/src/Util/GlobalState.php +++ b/src/Util/GlobalState.php @@ -11,9 +11,9 @@ use const PHP_MAJOR_VERSION; use const PHP_MINOR_VERSION; -use function array_keys; use function array_reverse; use function array_shift; +use function assert; use function defined; use function get_defined_constants; use function get_included_files; @@ -32,14 +32,16 @@ use Closure; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class GlobalState +final readonly class GlobalState { /** - * @psalm-var list + * @var non-empty-list */ - private const SUPER_GLOBAL_ARRAYS = [ + private const array SUPER_GLOBAL_ARRAYS = [ '_ENV', '_POST', '_GET', @@ -50,9 +52,9 @@ final class GlobalState ]; /** - * @psalm-var array> + * @var non-empty-array> */ - private const DEPRECATED_INI_SETTINGS = [ + private const array DEPRECATED_INI_SETTINGS = [ '7.3' => [ 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, @@ -131,7 +133,7 @@ public static function getIncludedFilesAsString(): string } /** - * @psalm-param list $files + * @param list $files * * @throws Exception */ @@ -142,7 +144,9 @@ public static function processIncludedFilesAsString(array $files): string $result = ''; if (defined('__PHPUNIT_PHAR__')) { + // @codeCoverageIgnoreStart $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; + // @codeCoverageIgnoreEnd } // Do not process bootstrap script @@ -150,11 +154,15 @@ public static function processIncludedFilesAsString(array $files): string // If bootstrap script was a Composer bin proxy, skip the second entry as well if (str_ends_with(strtr($files[0], '\\', '/'), '/phpunit/phpunit/phpunit')) { + // @codeCoverageIgnoreStart array_shift($files); + // @codeCoverageIgnoreEnd } foreach (array_reverse($files) as $file) { - if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && + if (isset($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && + is_array($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && + $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] !== [] && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) { continue; } @@ -164,7 +172,7 @@ public static function processIncludedFilesAsString(array $files): string } // Skip virtual file system protocols - if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { + if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file) > 0) { continue; } @@ -180,7 +188,11 @@ public static function getIniSettingsAsString(): string { $result = ''; - foreach (ini_get_all(null, false) as $key => $value) { + $iniSettings = ini_get_all(null, false); + + assert($iniSettings !== false); + + foreach ($iniSettings as $key => $value) { if (self::isIniSettingDeprecated($key)) { continue; } @@ -220,8 +232,8 @@ public static function getGlobalsAsString(): string foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) { if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { - foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) { - if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + if ($value instanceof Closure) { continue; } @@ -238,12 +250,12 @@ public static function getGlobalsAsString(): string $excludeList = self::SUPER_GLOBAL_ARRAYS; $excludeList[] = 'GLOBALS'; - foreach (array_keys($GLOBALS) as $key) { - if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, true)) { + foreach ($GLOBALS as $key => $value) { + if (!$value instanceof Closure && !in_array($key, $excludeList, true)) { $result .= sprintf( '$GLOBALS[\'%s\'] = %s;' . "\n", $key, - self::exportVariable($GLOBALS[$key]), + self::exportVariable($value), ); } } @@ -261,6 +273,9 @@ private static function exportVariable(mixed $variable): string return 'unserialize(' . var_export(serialize($variable), true) . ')'; } + /** + * @param array $array + */ private static function arrayOnlyContainsScalars(array $array): bool { $result = true; diff --git a/src/Util/Http/Downloader.php b/src/Util/Http/Downloader.php new file mode 100644 index 00000000000..a9af2e38fbb --- /dev/null +++ b/src/Util/Http/Downloader.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Http; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +interface Downloader +{ + /** + * @param non-empty-string $url + */ + public function download(string $url): false|string; +} diff --git a/src/Util/Http/PhpDownloader.php b/src/Util/Http/PhpDownloader.php new file mode 100644 index 00000000000..5969c042609 --- /dev/null +++ b/src/Util/Http/PhpDownloader.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Http; + +use function file_get_contents; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + * + * @codeCoverageIgnore + */ +final class PhpDownloader implements Downloader +{ + /** + * @param non-empty-string $url + */ + public function download(string $url): false|string + { + return file_get_contents($url); + } +} diff --git a/src/Util/Json.php b/src/Util/Json.php index cd52c7cd2df..cbe959accd1 100644 --- a/src/Util/Json.php +++ b/src/Util/Json.php @@ -9,21 +9,25 @@ */ namespace PHPUnit\Util; +use const JSON_ERROR_NONE; use const JSON_PRETTY_PRINT; use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -use function count; -use function is_array; +use const SORT_STRING; +use function assert; use function is_object; +use function is_scalar; use function json_decode; use function json_encode; use function json_last_error; use function ksort; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Json +final readonly class Json { /** * @throws InvalidJsonException @@ -32,26 +36,29 @@ public static function prettify(string $json): string { $decodedJson = json_decode($json, false); - if (json_last_error()) { + if (json_last_error() !== JSON_ERROR_NONE) { throw new InvalidJsonException; } - return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $result = json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + assert($result !== false); + + return $result; } /** - * To allow comparison of JSON strings, first process them into a consistent - * format so that they can be compared as strings. + * Element 0 is true and element 1 is null when JSON decoding did not work. + * * Element 0 is false and element 1 has the decoded value when JSON decoding did work. + * * This is used to avoid ambiguity with JSON strings consisting entirely of 'null' or 'false'. * - * @return array ($error, $canonicalized_json) The $error parameter is used - * to indicate an error decoding the json. This is used to avoid ambiguity - * with JSON strings consisting entirely of 'null' or 'false'. + * @return array{0: false, 1: mixed}|array{0: true, 1: null} */ public static function canonicalize(string $json): array { $decodedJson = json_decode($json); - if (json_last_error()) { + if (json_last_error() !== JSON_ERROR_NONE) { return [true, null]; } @@ -70,24 +77,30 @@ public static function canonicalize(string $json): array */ private static function recursiveSort(mixed &$json): void { - if (!is_array($json)) { - // If the object is not empty, change it to an associative array - // so we can sort the keys (and we will still re-encode it - // correctly, since PHP encodes associative arrays as JSON objects.) - // But EMPTY objects MUST remain empty objects. (Otherwise we will - // re-encode it as a JSON array rather than a JSON object.) - // See #2919. - if (is_object($json) && count((array) $json) > 0) { - $json = (array) $json; - } else { - return; - } + if ($json === null || $json === [] || is_scalar($json)) { + return; } - ksort($json); + $isObject = is_object($json); + + if ($isObject) { + // Objects need to be sorted during canonicalization to ensure + // correct comparsion since JSON objects are unordered. It must be + // kept as an object so that the value correctly stays as a JSON + // object instead of potentially being converted to an array. This + // approach ensures that numeric string JSON keys are preserved and + // don't risk being flattened due to PHP's array semantics. + // See #2919, #4584, #4674 + $json = (array) $json; + ksort($json, SORT_STRING); + } foreach ($json as &$value) { self::recursiveSort($value); } + + if ($isObject) { + $json = (object) $json; + } } } diff --git a/src/Util/PHP/AbstractPhpProcess.php b/src/Util/PHP/AbstractPhpProcess.php deleted file mode 100644 index 1a7b5a9d7a3..00000000000 --- a/src/Util/PHP/AbstractPhpProcess.php +++ /dev/null @@ -1,313 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use const PHP_SAPI; -use function array_keys; -use function array_merge; -use function assert; -use function escapeshellarg; -use function file_exists; -use function file_get_contents; -use function ini_get_all; -use function restore_error_handler; -use function set_error_handler; -use function trim; -use function unlink; -use function unserialize; -use ErrorException; -use PHPUnit\Event\Code\TestMethodBuilder; -use PHPUnit\Event\Code\ThrowableBuilder; -use PHPUnit\Event\Facade; -use PHPUnit\Event\NoPreviousThrowableException; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Runner\CodeCoverage; -use PHPUnit\TestRunner\TestResult\PassedTests; -use SebastianBergmann\Environment\Runtime; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -abstract class AbstractPhpProcess -{ - protected bool $stderrRedirection = false; - protected string $stdin = ''; - protected string $arguments = ''; - - /** - * @psalm-var array - */ - protected array $env = []; - - public static function factory(): self - { - if (PHP_OS_FAMILY === 'Windows') { - return new WindowsPhpProcess; - } - - return new DefaultPhpProcess; - } - - /** - * Defines if should use STDERR redirection or not. - * - * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. - */ - public function setUseStderrRedirection(bool $stderrRedirection): void - { - $this->stderrRedirection = $stderrRedirection; - } - - /** - * Returns TRUE if uses STDERR redirection or FALSE if not. - */ - public function useStderrRedirection(): bool - { - return $this->stderrRedirection; - } - - /** - * Sets the input string to be sent via STDIN. - */ - public function setStdin(string $stdin): void - { - $this->stdin = $stdin; - } - - /** - * Returns the input string to be sent via STDIN. - */ - public function getStdin(): string - { - return $this->stdin; - } - - /** - * Sets the string of arguments to pass to the php job. - */ - public function setArgs(string $arguments): void - { - $this->arguments = $arguments; - } - - /** - * Returns the string of arguments to pass to the php job. - */ - public function getArgs(): string - { - return $this->arguments; - } - - /** - * Sets the array of environment variables to start the child process with. - * - * @psalm-param array $env - */ - public function setEnv(array $env): void - { - $this->env = $env; - } - - /** - * Returns the array of environment variables to start the child process with. - */ - public function getEnv(): array - { - return $this->env; - } - - /** - * Runs a single test in a separate PHP process. - * - * @throws \PHPUnit\Runner\Exception - * @throws Exception - * @throws NoPreviousThrowableException - */ - public function runTestJob(string $job, Test $test, string $processResultFile): void - { - $_result = $this->runJob($job); - - $processResult = ''; - - if (file_exists($processResultFile)) { - $processResult = file_get_contents($processResultFile); - - @unlink($processResultFile); - } - - $this->processChildResult( - $test, - $processResult, - $_result['stderr'], - ); - } - - /** - * Returns the command based into the configurations. - */ - public function getCommand(array $settings, string $file = null): string - { - $runtime = new Runtime; - - $command = $runtime->getBinary(); - - if ($runtime->hasPCOV()) { - $settings = array_merge( - $settings, - $runtime->getCurrentSettings( - array_keys(ini_get_all('pcov')), - ), - ); - } elseif ($runtime->hasXdebug()) { - $settings = array_merge( - $settings, - $runtime->getCurrentSettings( - array_keys(ini_get_all('xdebug')), - ), - ); - } - - $command .= $this->settingsToParameters($settings); - - if (PHP_SAPI === 'phpdbg') { - $command .= ' -qrr'; - - if (!$file) { - $command .= 's='; - } - } - - if ($file) { - $command .= ' ' . escapeshellarg($file); - } - - if ($this->arguments) { - if (!$file) { - $command .= ' --'; - } - $command .= ' ' . $this->arguments; - } - - if ($this->stderrRedirection) { - $command .= ' 2>&1'; - } - - return $command; - } - - /** - * Runs a single job (PHP code) using a separate PHP process. - */ - abstract public function runJob(string $job, array $settings = []): array; - - protected function settingsToParameters(array $settings): string - { - $buffer = ''; - - foreach ($settings as $setting) { - $buffer .= ' -d ' . escapeshellarg($setting); - } - - return $buffer; - } - - /** - * @throws \PHPUnit\Runner\Exception - * @throws Exception - * @throws NoPreviousThrowableException - */ - private function processChildResult(Test $test, string $stdout, string $stderr): void - { - if (!empty($stderr)) { - $exception = new Exception(trim($stderr)); - - assert($test instanceof TestCase); - - Facade::emitter()->testErrored( - TestMethodBuilder::fromTestCase($test), - ThrowableBuilder::from($exception), - ); - - return; - } - - set_error_handler( - /** - * @throws ErrorException - */ - static function (int $errno, string $errstr, string $errfile, int $errline): never - { - throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); - }, - ); - - try { - $childResult = unserialize($stdout); - - restore_error_handler(); - - if ($childResult === false) { - $exception = new AssertionFailedError('Test was run in child process and ended unexpectedly'); - - assert($test instanceof TestCase); - - Facade::emitter()->testErrored( - TestMethodBuilder::fromTestCase($test), - ThrowableBuilder::from($exception), - ); - - Facade::emitter()->testFinished( - TestMethodBuilder::fromTestCase($test), - 0, - ); - } - } catch (ErrorException $e) { - restore_error_handler(); - - $childResult = false; - - $exception = new Exception(trim($stdout), 0, $e); - - assert($test instanceof TestCase); - - Facade::emitter()->testErrored( - TestMethodBuilder::fromTestCase($test), - ThrowableBuilder::from($exception), - ); - } - - if ($childResult !== false) { - if (!empty($childResult['output'])) { - $output = $childResult['output']; - } - - Facade::instance()->forward($childResult['events']); - PassedTests::instance()->import($childResult['passedTests']); - - assert($test instanceof TestCase); - - $test->setResult($childResult['testResult']); - $test->addToAssertionCount($childResult['numAssertions']); - - if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) { - CodeCoverage::instance()->codeCoverage()->merge( - $childResult['codeCoverage'], - ); - } - } - - if (!empty($output)) { - print $output; - } - } -} diff --git a/src/Util/PHP/DefaultJobRunner.php b/src/Util/PHP/DefaultJobRunner.php new file mode 100644 index 00000000000..caeea30a7da --- /dev/null +++ b/src/Util/PHP/DefaultJobRunner.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use const PHP_BINARY; +use const PHP_SAPI; +use function array_keys; +use function array_merge; +use function array_values; +use function assert; +use function fclose; +use function file_put_contents; +use function function_exists; +use function fwrite; +use function ini_get_all; +use function is_array; +use function is_resource; +use function proc_close; +use function proc_open; +use function stream_get_contents; +use function sys_get_temp_dir; +use function tempnam; +use function trim; +use function unlink; +use function xdebug_is_debugger_active; +use PHPUnit\Event\Facade; +use PHPUnit\Runner\CodeCoverage; +use SebastianBergmann\Environment\Runtime; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class DefaultJobRunner extends JobRunner +{ + /** + * @throws PhpProcessException + */ + public function run(Job $job): Result + { + $temporaryFile = null; + + if ($job->hasInput()) { + $temporaryFile = tempnam(sys_get_temp_dir(), 'phpunit_'); + + if ($temporaryFile === false || + file_put_contents($temporaryFile, $job->code()) === false) { + // @codeCoverageIgnoreStart + throw new PhpProcessException( + 'Unable to write temporary file', + ); + // @codeCoverageIgnoreEnd + } + + $job = new Job( + $job->input(), + $job->phpSettings(), + $job->environmentVariables(), + $job->arguments(), + null, + $job->redirectErrors(), + ); + } + + assert($temporaryFile !== ''); + + return $this->runProcess($job, $temporaryFile); + } + + /** + * @param ?non-empty-string $temporaryFile + * + * @throws PhpProcessException + */ + private function runProcess(Job $job, ?string $temporaryFile): Result + { + $environmentVariables = null; + + if ($job->hasEnvironmentVariables()) { + /** @phpstan-ignore nullCoalesce.variable */ + $environmentVariables = $_SERVER ?? []; + + unset($environmentVariables['argv'], $environmentVariables['argc']); + + $environmentVariables = array_merge($environmentVariables, $job->environmentVariables()); + + foreach ($environmentVariables as $key => $value) { + if (is_array($value)) { + unset($environmentVariables[$key]); + } + } + + unset($key, $value); + } + + $pipeSpec = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + + if ($job->redirectErrors()) { + $pipeSpec[2] = ['redirect', 1]; + } + + $process = proc_open( + $this->buildCommand($job, $temporaryFile), + $pipeSpec, + $pipes, + null, + $environmentVariables, + ); + + if (!is_resource($process)) { + // @codeCoverageIgnoreStart + throw new PhpProcessException( + 'Unable to spawn worker process', + ); + // @codeCoverageIgnoreEnd + } + + Facade::emitter()->testRunnerStartedChildProcess(); + + fwrite($pipes[0], $job->code()); + fclose($pipes[0]); + + $stdout = ''; + $stderr = ''; + + if (isset($pipes[1])) { + $stdout = stream_get_contents($pipes[1]); + + fclose($pipes[1]); + } + + if (isset($pipes[2])) { + $stderr = stream_get_contents($pipes[2]); + + fclose($pipes[2]); + } + + proc_close($process); + + if ($temporaryFile !== null) { + unlink($temporaryFile); + } + + assert($stdout !== false); + assert($stderr !== false); + + return new Result($stdout, $stderr); + } + + /** + * @return non-empty-list + */ + private function buildCommand(Job $job, ?string $file): array + { + $runtime = new Runtime; + $command = [PHP_BINARY]; + $phpSettings = $job->phpSettings(); + + if ($runtime->hasPCOV()) { + $pcovSettings = ini_get_all('pcov'); + + assert($pcovSettings !== false); + + $phpSettings = array_merge( + $phpSettings, + $runtime->getCurrentSettings( + array_keys($pcovSettings), + ), + ); + } elseif ($runtime->hasXdebug()) { + assert(function_exists('xdebug_is_debugger_active')); + + $xdebugSettings = ini_get_all('xdebug'); + + assert($xdebugSettings !== false); + + $phpSettings = array_merge( + $phpSettings, + $runtime->getCurrentSettings( + array_keys($xdebugSettings), + ), + ); + + if (!CodeCoverage::instance()->isActive() && + xdebug_is_debugger_active() === false) { + $phpSettings['xdebug.mode'] = 'xdebug.mode=off'; + } + } + + $command = array_merge($command, $this->settingsToParameters(array_values($phpSettings))); + + if (PHP_SAPI === 'phpdbg') { + $command[] = '-qrr'; + + if ($file === null) { + $command[] = 's='; + } + } + + if ($file !== null) { + $command[] = '-f'; + $command[] = $file; + } + + if ($job->hasArguments()) { + if ($file === null) { + $command[] = '--'; + } + + foreach ($job->arguments() as $argument) { + $command[] = trim($argument); + } + } + + return $command; + } + + /** + * @param list $settings + * + * @return list + */ + private function settingsToParameters(array $settings): array + { + $buffer = []; + + foreach ($settings as $setting) { + $buffer[] = '-d'; + $buffer[] = $setting; + } + + return $buffer; + } +} diff --git a/src/Util/PHP/DefaultPhpProcess.php b/src/Util/PHP/DefaultPhpProcess.php deleted file mode 100644 index 658c0ce7fba..00000000000 --- a/src/Util/PHP/DefaultPhpProcess.php +++ /dev/null @@ -1,174 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use function array_merge; -use function fclose; -use function file_put_contents; -use function fwrite; -use function is_array; -use function is_resource; -use function proc_close; -use function proc_open; -use function rewind; -use function stream_get_contents; -use function sys_get_temp_dir; -use function tempnam; -use function unlink; -use PHPUnit\Framework\Exception; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ -class DefaultPhpProcess extends AbstractPhpProcess -{ - private ?string $tempFile = null; - - /** - * Runs a single job (PHP code) using a separate PHP process. - * - * @psalm-return array{stdout: string, stderr: string} - * - * @throws Exception - * @throws PhpProcessException - */ - public function runJob(string $job, array $settings = []): array - { - if ($this->stdin || $this->useTemporaryFile()) { - if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'phpunit_')) || - file_put_contents($this->tempFile, $job) === false) { - throw new PhpProcessException( - 'Unable to write temporary file', - ); - } - - $job = $this->stdin; - } - - return $this->runProcess($job, $settings); - } - - /** - * Returns an array of file handles to be used in place of pipes. - */ - protected function getHandles(): array - { - return []; - } - - /** - * Handles creating the child process and returning the STDOUT and STDERR. - * - * @psalm-return array{stdout: string, stderr: string} - * - * @throws Exception - * @throws PhpProcessException - */ - protected function runProcess(string $job, array $settings): array - { - $handles = $this->getHandles(); - - $env = null; - - if ($this->env) { - $env = $_SERVER ?? []; - unset($env['argv'], $env['argc']); - $env = array_merge($env, $this->env); - - foreach ($env as $envKey => $envVar) { - if (is_array($envVar)) { - unset($env[$envKey]); - } - } - } - - $pipeSpec = [ - 0 => $handles[0] ?? ['pipe', 'r'], - 1 => $handles[1] ?? ['pipe', 'w'], - 2 => $handles[2] ?? ['pipe', 'w'], - ]; - - $process = proc_open( - $this->getCommand($settings, $this->tempFile), - $pipeSpec, - $pipes, - null, - $env, - ); - - if (!is_resource($process)) { - throw new PhpProcessException( - 'Unable to spawn worker process', - ); - } - - if ($job) { - $this->process($pipes[0], $job); - } - - fclose($pipes[0]); - - $stderr = $stdout = ''; - - if (isset($pipes[1])) { - $stdout = stream_get_contents($pipes[1]); - - fclose($pipes[1]); - } - - if (isset($pipes[2])) { - $stderr = stream_get_contents($pipes[2]); - - fclose($pipes[2]); - } - - if (isset($handles[1])) { - rewind($handles[1]); - - $stdout = stream_get_contents($handles[1]); - - fclose($handles[1]); - } - - if (isset($handles[2])) { - rewind($handles[2]); - - $stderr = stream_get_contents($handles[2]); - - fclose($handles[2]); - } - - proc_close($process); - - $this->cleanup(); - - return ['stdout' => $stdout, 'stderr' => $stderr]; - } - - /** - * @param resource $pipe - */ - protected function process($pipe, string $job): void - { - fwrite($pipe, $job); - } - - protected function cleanup(): void - { - if ($this->tempFile) { - unlink($this->tempFile); - } - } - - protected function useTemporaryFile(): bool - { - return false; - } -} diff --git a/src/Util/PHP/Job.php b/src/Util/PHP/Job.php new file mode 100644 index 00000000000..5405590ac22 --- /dev/null +++ b/src/Util/PHP/Job.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class Job +{ + /** + * @var non-empty-string + */ + private string $code; + + /** + * @var list + */ + private array $phpSettings; + + /** + * @var array + */ + private array $environmentVariables; + + /** + * @var list + */ + private array $arguments; + + /** + * @var ?non-empty-string + */ + private ?string $input; + private bool $redirectErrors; + + /** + * @param non-empty-string $code + * @param list $phpSettings + * @param array $environmentVariables + * @param list $arguments + * @param ?non-empty-string $input + */ + public function __construct(string $code, array $phpSettings = [], array $environmentVariables = [], array $arguments = [], ?string $input = null, bool $redirectErrors = false) + { + $this->code = $code; + $this->phpSettings = $phpSettings; + $this->environmentVariables = $environmentVariables; + $this->arguments = $arguments; + $this->input = $input; + $this->redirectErrors = $redirectErrors; + } + + /** + * @return non-empty-string + */ + public function code(): string + { + return $this->code; + } + + /** + * @return list + */ + public function phpSettings(): array + { + return $this->phpSettings; + } + + /** + * @phpstan-assert-if-true !empty $this->environmentVariables + */ + public function hasEnvironmentVariables(): bool + { + return $this->environmentVariables !== []; + } + + /** + * @return array + */ + public function environmentVariables(): array + { + return $this->environmentVariables; + } + + /** + * @phpstan-assert-if-true !empty $this->arguments + */ + public function hasArguments(): bool + { + return $this->arguments !== []; + } + + /** + * @return list + */ + public function arguments(): array + { + return $this->arguments; + } + + /** + * @phpstan-assert-if-true !empty $this->input + */ + public function hasInput(): bool + { + return $this->input !== null; + } + + /** + * @throws PhpProcessException + * + * @return non-empty-string + */ + public function input(): string + { + if ($this->input === null) { + throw new PhpProcessException('No input specified'); + } + + return $this->input; + } + + public function redirectErrors(): bool + { + return $this->redirectErrors; + } +} diff --git a/src/Util/PHP/JobRunner.php b/src/Util/PHP/JobRunner.php new file mode 100644 index 00000000000..56d9e577f48 --- /dev/null +++ b/src/Util/PHP/JobRunner.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use function assert; +use function file_get_contents; +use function is_file; +use function unlink; +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Framework\ChildProcessResultProcessor; +use PHPUnit\Framework\Test; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +abstract readonly class JobRunner +{ + private ChildProcessResultProcessor $processor; + + public function __construct(ChildProcessResultProcessor $processor) + { + $this->processor = $processor; + } + + /** + * @param non-empty-string $processResultFile + */ + final public function runTestJob(Job $job, string $processResultFile, Test $test): void + { + $result = $this->run($job); + + $processResult = ''; + + if (is_file($processResultFile)) { + $processResult = file_get_contents($processResultFile); + + assert($processResult !== false); + + @unlink($processResultFile); + } + + $this->processor->process( + $test, + $processResult, + $result->stderr(), + ); + + EventFacade::emitter()->testRunnerFinishedChildProcess($result->stdout(), $result->stderr()); + } + + abstract public function run(Job $job): Result; +} diff --git a/src/Util/PHP/JobRunnerRegistry.php b/src/Util/PHP/JobRunnerRegistry.php new file mode 100644 index 00000000000..94e22a4e454 --- /dev/null +++ b/src/Util/PHP/JobRunnerRegistry.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use PHPUnit\Event\Facade; +use PHPUnit\Framework\ChildProcessResultProcessor; +use PHPUnit\Framework\Test; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\TestRunner\TestResult\PassedTests; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class JobRunnerRegistry +{ + private static ?JobRunner $runner = null; + + public static function run(Job $job): Result + { + return self::runner()->run($job); + } + + /** + * @param non-empty-string $processResultFile + */ + public static function runTestJob(Job $job, string $processResultFile, Test $test): void + { + self::runner()->runTestJob($job, $processResultFile, $test); + } + + public static function set(JobRunner $runner): void + { + self::$runner = $runner; + } + + private static function runner(): JobRunner + { + if (self::$runner === null) { + self::$runner = new DefaultJobRunner( + new ChildProcessResultProcessor( + Facade::instance(), + Facade::emitter(), + PassedTests::instance(), + CodeCoverage::instance(), + ), + ); + } + + return self::$runner; + } +} diff --git a/src/Util/PHP/Result.php b/src/Util/PHP/Result.php new file mode 100644 index 00000000000..ed05822601b --- /dev/null +++ b/src/Util/PHP/Result.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +/** + * @immutable + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final readonly class Result +{ + private string $stdout; + private string $stderr; + + public function __construct(string $stdout, string $stderr) + { + $this->stdout = $stdout; + $this->stderr = $stderr; + } + + public function stdout(): string + { + return $this->stdout; + } + + public function stderr(): string + { + return $this->stderr; + } +} diff --git a/src/Util/PHP/Template/TestCaseClass.tpl b/src/Util/PHP/Template/TestCaseClass.tpl deleted file mode 100644 index f6d06695e0a..00000000000 --- a/src/Util/PHP/Template/TestCaseClass.tpl +++ /dev/null @@ -1,117 +0,0 @@ -initForIsolation( - PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( - {offsetSeconds}, - {offsetNanoseconds} - ), - {exportObjects}, - ); - - require_once '{filename}'; - - if ({collectCodeCoverageInformation}) { - CodeCoverage::instance()->init(ConfigurationRegistry::get(), CodeCoverageFilterRegistry::instance(), true); - CodeCoverage::instance()->ignoreLines({linesToBeIgnored}); - } - - $test = new {className}('{name}'); - - $test->setData('{dataName}', unserialize('{data}')); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - - $test->run(); - - $output = ''; - - if (!$test->expectsOutput()) { - $output = $test->output(); - } - - ini_set('xdebug.scream', '0'); - - // Not every STDOUT target stream is rewindable - @rewind(STDOUT); - - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); - } - } - - file_put_contents( - '{processResultFile}', - serialize( - [ - 'testResult' => $test->result(), - 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, - 'numAssertions' => $test->numberOfAssertionsPerformed(), - 'output' => $output, - 'events' => $dispatcher->flush(), - 'passedTests' => PassedTests::instance() - ] - ) - ); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -ConfigurationRegistry::loadFrom('{serializedConfiguration}'); -(new PhpHandler)->handle(ConfigurationRegistry::get()->php()); - -if ('{bootstrap}' !== '') { - require_once '{bootstrap}'; -} - -__phpunit_run_isolated_test(); diff --git a/src/Util/PHP/Template/TestCaseMethod.tpl b/src/Util/PHP/Template/TestCaseMethod.tpl deleted file mode 100644 index b9eaef7362b..00000000000 --- a/src/Util/PHP/Template/TestCaseMethod.tpl +++ /dev/null @@ -1,117 +0,0 @@ -initForIsolation( - PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( - {offsetSeconds}, - {offsetNanoseconds} - ), - {exportObjects}, - ); - - require_once '{filename}'; - - if ({collectCodeCoverageInformation}) { - CodeCoverage::instance()->init(ConfigurationRegistry::get(), CodeCoverageFilterRegistry::instance(), true); - CodeCoverage::instance()->ignoreLines({linesToBeIgnored}); - } - - $test = new {className}('{methodName}'); - - $test->setData('{dataName}', unserialize('{data}')); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(true); - - ob_end_clean(); - - $test->run(); - - $output = ''; - - if (!$test->expectsOutput()) { - $output = $test->output(); - } - - ini_set('xdebug.scream', '0'); - - // Not every STDOUT target stream is rewindable - @rewind(STDOUT); - - if ($stdout = @stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); - } - } - - file_put_contents( - '{processResultFile}', - serialize( - [ - 'testResult' => $test->result(), - 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, - 'numAssertions' => $test->numberOfAssertionsPerformed(), - 'output' => $output, - 'events' => $dispatcher->flush(), - 'passedTests' => PassedTests::instance() - ] - ) - ); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -ConfigurationRegistry::loadFrom('{serializedConfiguration}'); -(new PhpHandler)->handle(ConfigurationRegistry::get()->php()); - -if ('{bootstrap}' !== '') { - require_once '{bootstrap}'; -} - -__phpunit_run_isolated_test(); diff --git a/src/Util/PHP/WindowsPhpProcess.php b/src/Util/PHP/WindowsPhpProcess.php deleted file mode 100644 index 74524996d36..00000000000 --- a/src/Util/PHP/WindowsPhpProcess.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use function tmpfile; -use PHPUnit\Framework\Exception; - -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - * - * @see https://bugs.php.net/bug.php?id=51800 - */ -final class WindowsPhpProcess extends DefaultPhpProcess -{ - /** - * @throws Exception - * @throws PhpProcessException - */ - protected function getHandles(): array - { - if (false === $stdout_handle = tmpfile()) { - throw new PhpProcessException( - 'A temporary file could not be created; verify that your TEMP environment variable is writable', - ); - } - - return [ - 1 => $stdout_handle, - ]; - } - - protected function useTemporaryFile(): bool - { - return true; - } -} diff --git a/src/Util/Reflection.php b/src/Util/Reflection.php index 421fac47fbf..b61c19b9b46 100644 --- a/src/Util/Reflection.php +++ b/src/Util/Reflection.php @@ -12,6 +12,7 @@ use function array_keys; use function array_merge; use function array_reverse; +use function assert; use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -19,15 +20,17 @@ use ReflectionMethod; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Reflection +final readonly class Reflection { /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName + * @param class-string $className + * @param non-empty-string $methodName * - * @psalm-return array{file: non-empty-string, line: non-negative-int} + * @return array{file: non-empty-string, line: non-negative-int} */ public static function sourceLocationFor(string $className, string $methodName): array { @@ -41,6 +44,9 @@ public static function sourceLocationFor(string $className, string $methodName): $line = 0; } + assert($file !== false && $file !== ''); + assert($line !== false && $line >= 0); + return [ 'file' => $file, 'line' => $line, @@ -48,23 +54,29 @@ public static function sourceLocationFor(string $className, string $methodName): } /** - * @psalm-return list + * @param ReflectionClass $class + * + * @return list */ - public static function publicMethodsInTestClass(ReflectionClass $class): array + public static function publicMethodsDeclaredDirectlyInTestClass(ReflectionClass $class): array { return self::filterAndSortMethods($class, ReflectionMethod::IS_PUBLIC, true); } /** - * @psalm-return list + * @param ReflectionClass $class + * + * @return list */ - public static function methodsInTestClass(ReflectionClass $class): array + public static function methodsDeclaredDirectlyInTestClass(ReflectionClass $class): array { return self::filterAndSortMethods($class, null, false); } /** - * @psalm-return list + * @param ReflectionClass $class + * + * @return list */ private static function filterAndSortMethods(ReflectionClass $class, ?int $filter, bool $sortHighestToLowest): array { diff --git a/src/Util/Test.php b/src/Util/Test.php index 56333f95c39..bcc2f371bbc 100644 --- a/src/Util/Test.php +++ b/src/Util/Test.php @@ -9,15 +9,36 @@ */ namespace PHPUnit\Util; +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use const DEBUG_BACKTRACE_PROVIDE_OBJECT; +use function debug_backtrace; use function str_starts_with; +use PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException; +use PHPUnit\Framework\TestCase; use PHPUnit\Metadata\Parser\Registry; use ReflectionMethod; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Test +final readonly class Test { + /** + * @throws NoTestCaseObjectOnCallStackException + */ + public static function currentTestCase(): TestCase + { + foreach (debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { + if (isset($frame['object']) && $frame['object'] instanceof TestCase) { + return $frame['object']; + } + } + + throw new NoTestCaseObjectOnCallStackException; + } + public static function isTestMethod(ReflectionMethod $method): bool { if (!$method->isPublic()) { diff --git a/src/Util/ThrowableToStringMapper.php b/src/Util/ThrowableToStringMapper.php index a8f3eb861ac..0fcf3695b83 100644 --- a/src/Util/ThrowableToStringMapper.php +++ b/src/Util/ThrowableToStringMapper.php @@ -13,19 +13,26 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\PhptAssertionFailedError; use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Runner\ErrorException; use Throwable; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class ThrowableToStringMapper +final readonly class ThrowableToStringMapper { public static function map(Throwable $t): string { + if ($t instanceof ErrorException) { + return $t->getMessage(); + } + if ($t instanceof SelfDescribing) { $buffer = $t->toString(); - if ($t instanceof ExpectationFailedException && $t->getComparisonFailure()) { + if ($t instanceof ExpectationFailedException && $t->getComparisonFailure() !== null) { $buffer .= $t->getComparisonFailure()->getDiff(); } @@ -33,7 +40,7 @@ public static function map(Throwable $t): string $buffer .= $t->diff(); } - if (!empty($buffer)) { + if ($buffer !== '') { $buffer = trim($buffer) . "\n"; } diff --git a/src/Util/VersionComparisonOperator.php b/src/Util/VersionComparisonOperator.php index a9e85513595..9dcba3c32a3 100644 --- a/src/Util/VersionComparisonOperator.php +++ b/src/Util/VersionComparisonOperator.php @@ -14,17 +14,17 @@ /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * - * @psalm-immutable + * @immutable */ final readonly class VersionComparisonOperator { /** - * @psalm-var '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + * @var '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' */ private string $operator; /** - * @psalm-param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator + * @param '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' $operator * * @throws InvalidVersionOperatorException */ @@ -36,7 +36,7 @@ public function __construct(string $operator) } /** - * @psalm-return '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' + * @return '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' */ public function asString(): string { @@ -44,7 +44,7 @@ public function asString(): string } /** - * @psalm-param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator + * @param '!='|'<'|'<='|'<>'|'='|'=='|'>'|'>='|'eq'|'ge'|'gt'|'le'|'lt'|'ne' $operator * * @throws InvalidVersionOperatorException */ diff --git a/src/Util/Xml/Loader.php b/src/Util/Xml/Loader.php index 7df5fd5b60e..1289c253fc6 100644 --- a/src/Util/Xml/Loader.php +++ b/src/Util/Xml/Loader.php @@ -9,20 +9,20 @@ */ namespace PHPUnit\Util\Xml; -use function chdir; -use function dirname; use function error_reporting; use function file_get_contents; -use function getcwd; use function libxml_get_errors; use function libxml_use_internal_errors; use function sprintf; +use function trim; use DOMDocument; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Loader +final readonly class Loader { /** * @throws XmlException @@ -43,25 +43,25 @@ public function loadFile(string $filename): DOMDocument ); } - return $this->load($contents, $filename); + if (trim($contents) === '') { + throw new XmlException( + sprintf( + 'Could not parse XML from empty file "%s"', + $filename, + ), + ); + } + + return $this->load($contents); } /** * @throws XmlException */ - public function load(string $actual, ?string $filename = null): DOMDocument + public function load(string $actual): DOMDocument { if ($actual === '') { - if ($filename === null) { - throw new XmlException('Could not parse XML from empty string'); - } - - throw new XmlException( - sprintf( - 'Could not parse XML from empty file "%s"', - $filename, - ), - ); + throw new XmlException('Could not parse XML from empty string'); } $document = new DOMDocument; @@ -70,23 +70,7 @@ public function load(string $actual, ?string $filename = null): DOMDocument $internal = libxml_use_internal_errors(true); $message = ''; $reporting = error_reporting(0); - - // Required for XInclude - if ($filename !== null) { - // Required for XInclude on Windows - if (PHP_OS_FAMILY === 'Windows') { - $cwd = getcwd(); - @chdir(dirname($filename)); - } - - $document->documentURI = $filename; - } - - $loaded = $document->loadXML($actual); - - if ($filename !== null) { - $document->xinclude(); - } + $loaded = $document->loadXML($actual); foreach (libxml_get_errors() as $error) { $message .= "\n" . $error->message; @@ -95,21 +79,7 @@ public function load(string $actual, ?string $filename = null): DOMDocument libxml_use_internal_errors($internal); error_reporting($reporting); - if (isset($cwd)) { - @chdir($cwd); - } - - if ($loaded === false || $message !== '') { - if ($filename !== null) { - throw new XmlException( - sprintf( - 'Could not load "%s"%s', - $filename, - $message !== '' ? ":\n" . $message : '', - ), - ); - } - + if ($loaded === false) { if ($message === '') { $message = 'Could not load XML for unknown reason'; } diff --git a/src/Util/Xml/Xml.php b/src/Util/Xml/Xml.php index 5e96faa9233..5e30bb43b70 100644 --- a/src/Util/Xml/Xml.php +++ b/src/Util/Xml/Xml.php @@ -17,9 +17,11 @@ use function strlen; /** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class Xml +final readonly class Xml { /** * Escapes a string for the use in XML documents. diff --git a/tests/README.md b/tests/README.md index db6b9a37dcc..1f2f51c6c78 100644 --- a/tests/README.md +++ b/tests/README.md @@ -7,7 +7,6 @@ This is the top-level directory structure of the `tests` directory: * `tests/unit` holds tests that are "regular" PHPUnit tests (implemented using `PHPUnit\Framework\TestCase`) * `tests/end-to-end` holds tests in the [PHPT](https://qa.php.net/phpt_details.php) format * `tests/end-to-end/phar` holds PHAR-specific tests that are not part of the regular `end-to-end` tests -* `tests/static-analysis` holds test fixture that is used for static analysis of PHPUnit's API using Psalm * `tests/_files` holds test fixture that is used by tests in `tests/unit` and/or `tests/end-to-end` ## Running the Test Suite @@ -16,4 +15,3 @@ This is the top-level directory structure of the `tests` directory: * `./phpunit --testsuite unit` will run all tests from `tests/unit` * `./phpunit --testsuite end-to-end` will run all tests from `tests/end-to-end` (except the PHAR-specific tests) * `ant phar-snapshot run-phar-specific-tests` will build a PHAR and run the PHAR-specific tests -* `./tools/psalm --config=.psalm/static-analysis.xml` will run the static analysis of PHPUnit's API using Psalm diff --git a/tests/_files/3194.php b/tests/_files/3194.php index 7c012b48589..5240a977503 100644 --- a/tests/_files/3194.php +++ b/tests/_files/3194.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; trait T3194 @@ -29,9 +30,7 @@ public function doSomething(): bool } } -/** - * @covers \PHPUnit\TestFixture\C3194 - */ +#[CoversClass(C3194::class)] final class Test3194 extends TestCase { public function testOne(): void diff --git a/tests/_files/3530.wsdl b/tests/_files/3530.wsdl deleted file mode 100644 index b94a1e0a061..00000000000 --- a/tests/_files/3530.wsdl +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/_files/AbstractVariousIterableDataProviderTest.php b/tests/_files/AbstractVariousIterableDataProviderTest.php index 4c614a0daa2..d2d390e8d43 100644 --- a/tests/_files/AbstractVariousIterableDataProviderTest.php +++ b/tests/_files/AbstractVariousIterableDataProviderTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; + abstract class AbstractVariousIterableDataProviderTest { public static function asArrayProviderInParent() @@ -44,20 +46,16 @@ abstract public static function asIteratorProvider(); abstract public static function asTraversableProvider(); - /** - * @dataProvider asArrayProvider - * @dataProvider asIteratorProvider - * @dataProvider asTraversableProvider - */ + #[DataProvider('asArrayProvider')] + #[DataProvider('asIteratorProvider')] + #[DataProvider('asTraversableProvider')] public function testAbstract(): void { } - /** - * @dataProvider asArrayProviderInParent - * @dataProvider asIteratorProviderInParent - * @dataProvider asTraversableProviderInParent - */ + #[DataProvider('asArrayProviderInParent')] + #[DataProvider('asIteratorProviderInParent')] + #[DataProvider('asTraversableProviderInParent')] public function testInParent(): void { } diff --git a/tests/_files/AlternativeSuffixTest.test.php b/tests/_files/AlternativeSuffixTest.test.php index 0acfa0839b3..6abb44ce1c6 100644 --- a/tests/_files/AlternativeSuffixTest.test.php +++ b/tests/_files/AlternativeSuffixTest.test.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class AlternativeSuffixTest extends TestCase +final class AlternativeSuffixTest extends TestCase { public function testOne(): void { diff --git a/tests/_files/AssertionExample.php b/tests/_files/AssertionExample.php index 56ae6d47cc0..c9c322a39d8 100644 --- a/tests/_files/AssertionExample.php +++ b/tests/_files/AssertionExample.php @@ -11,7 +11,7 @@ use function assert; -class AssertionExample +final class AssertionExample { public function doSomething(): void { diff --git a/tests/_files/AssertionExampleTest.php b/tests/_files/AssertionExampleTest.php index 3e0fd35ac35..625ab79ab4f 100644 --- a/tests/_files/AssertionExampleTest.php +++ b/tests/_files/AssertionExampleTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class AssertionExampleTest extends TestCase +final class AssertionExampleTest extends TestCase { public function testOne(): void { diff --git a/tests/_files/Author.php b/tests/_files/Author.php index 235178048af..f2403b1aa9d 100644 --- a/tests/_files/Author.php +++ b/tests/_files/Author.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class Author +final class Author { // the order of properties is important for testing the cycle! public $books = []; diff --git a/tests/_files/BankAccount.php b/tests/_files/BankAccount.php index 6a161e2dbae..7921321e6b4 100644 --- a/tests/_files/BankAccount.php +++ b/tests/_files/BankAccount.php @@ -9,14 +9,14 @@ */ namespace PHPUnit\TestFixture; -class BankAccount +final class BankAccount { /** * The bank account's balance. * * @var float */ - protected $balance = 0; + private $balance = 0; /** * Returns the bank account's balance. @@ -63,7 +63,7 @@ public function withdrawMoney($balance) * * @throws BankAccountException */ - protected function setBalance($balance): void + private function setBalance($balance): void { if ($balance >= 0) { $this->balance = $balance; diff --git a/tests/_files/BankAccountException.php b/tests/_files/BankAccountException.php index c51dc2f108f..316c61a2510 100644 --- a/tests/_files/BankAccountException.php +++ b/tests/_files/BankAccountException.php @@ -11,6 +11,6 @@ use RuntimeException; -class BankAccountException extends RuntimeException +final class BankAccountException extends RuntimeException { } diff --git a/tests/_files/BankAccountTest.php b/tests/_files/BankAccountTest.php index e98ef756fd6..aec77f7961f 100644 --- a/tests/_files/BankAccountTest.php +++ b/tests/_files/BankAccountTest.php @@ -9,24 +9,16 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; -class BankAccountTest extends TestCase +#[CoversFunction(BankAccount::class)] +final class BankAccountTest extends TestCase { - protected $ba; - - protected function setUp(): void - { - $this->ba = new BankAccount; - } - - /** - * @covers BankAccount::getBalance - * - * @group balanceIsInitiallyZero - * @group specification - * @group 1234 - */ + #[Group('balanceIsInitiallyZero')] + #[Group('specification')] + #[Group('1234')] public function testBalanceIsInitiallyZero(): void { /* @Given a fresh bank account */ @@ -39,18 +31,16 @@ public function testBalanceIsInitiallyZero(): void $this->assertEquals(0, $balance); } - /** - * @covers BankAccount::withdrawMoney - * - * @group balanceCannotBecomeNegative - * @group specification - */ + #[Group('balanceCannotBecomeNegative')] + #[Group('specification')] public function testBalanceCannotBecomeNegative(): void { + $ba = new BankAccount; + try { - $this->ba->withdrawMoney(1); + $ba->withdrawMoney(1); } catch (BankAccountException) { - $this->assertEquals(0, $this->ba->getBalance()); + $this->assertEquals(0, $ba->getBalance()); return; } @@ -58,39 +48,20 @@ public function testBalanceCannotBecomeNegative(): void $this->fail(); } - /** - * @covers BankAccount::depositMoney - * - * @group balanceCannotBecomeNegative - * @group specification - */ + #[Group('balanceCannotBecomeNegative')] + #[Group('specification')] public function testBalanceCannotBecomeNegative2(): void { + $ba = new BankAccount; + try { - $this->ba->depositMoney(-1); + $ba->depositMoney(-1); } catch (BankAccountException) { - $this->assertEquals(0, $this->ba->getBalance()); + $this->assertEquals(0, $ba->getBalance()); return; } $this->fail(); } - - /* - * @covers BankAccount::getBalance - * @covers BankAccount::depositMoney - * @covers BankAccount::withdrawMoney - * @group balanceCannotBecomeNegative - */ - /* - public function testDepositingAndWithdrawingMoneyWorks() - { - $this->assertEquals(0, $this->ba->getBalance()); - $this->ba->depositMoney(1); - $this->assertEquals(1, $this->ba->getBalance()); - $this->ba->withdrawMoney(1); - $this->assertEquals(0, $this->ba->getBalance()); - } - */ } diff --git a/tests/_files/BeforeClassAndAfterClassTest.php b/tests/_files/BeforeClassAndAfterClassTest.php deleted file mode 100644 index 71405301d7f..00000000000 --- a/tests/_files/BeforeClassAndAfterClassTest.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class BeforeClassAndAfterClassTest extends TestCase -{ - public static $beforeClassWasRun = 0; - public static $afterClassWasRun = 0; - - public static function resetProperties(): void - { - self::$beforeClassWasRun = 0; - self::$afterClassWasRun = 0; - } - - /** - * @beforeClass - */ - public static function initialClassSetup(): void - { - self::$beforeClassWasRun++; - } - - /** - * @afterClass - */ - public static function finalClassTeardown(): void - { - self::$afterClassWasRun++; - } - - public function test1(): void - { - } - - public function test2(): void - { - } -} diff --git a/tests/_files/BeforeClassWithOnlyDataProviderTest.php b/tests/_files/BeforeClassWithOnlyDataProviderTest.php deleted file mode 100644 index f17851afc88..00000000000 --- a/tests/_files/BeforeClassWithOnlyDataProviderTest.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -class BeforeClassWithOnlyDataProviderTest extends \PHPUnit\Framework\TestCase -{ - public static $setUpBeforeClassWasCalled; - public static $beforeClassWasCalled; - - public static function resetProperties(): void - { - self::$setUpBeforeClassWasCalled = false; - self::$beforeClassWasCalled = false; - } - - /** - * @beforeClass - */ - public static function someAnnotatedSetupMethod(): void - { - self::$beforeClassWasCalled = true; - } - - public static function setUpBeforeClass(): void - { - self::$setUpBeforeClassWasCalled = true; - } - - public function dummyProvider() - { - return [[1]]; - } - - /** - * @dataProvider dummyProvider - * delete annotation to fail test case - */ - public function testDummy(): void - { - $this->assertFalse(false); - } -} diff --git a/tests/_files/Book.php b/tests/_files/Book.php index a1d779640da..57d8a382ded 100644 --- a/tests/_files/Book.php +++ b/tests/_files/Book.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class Book +final class Book { // the order of properties is important for testing the cycle! public $author; diff --git a/tests/_files/BooleanConstraint.php b/tests/_files/BooleanConstraint.php deleted file mode 100644 index 27053d773d0..00000000000 --- a/tests/_files/BooleanConstraint.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\Constraint\Constraint; - -final class BooleanConstraint extends Constraint -{ - /** - * @var bool - */ - private $matches; - - public static function fromBool(bool $matches): self - { - $instance = new self; - - $instance->matches = $matches; - - return $instance; - } - - public function matches(mixed $other): bool - { - return $this->matches; - } - - public function toString(bool $exportObjects = false): string - { - if ($this->matches) { - return 'true'; - } - - return 'false'; - } -} diff --git a/tests/_files/ClassWithNonPublicAttributes.php b/tests/_files/ClassWithNonPublicAttributes.php index 66b5591d7dd..eeb36548e12 100644 --- a/tests/_files/ClassWithNonPublicAttributes.php +++ b/tests/_files/ClassWithNonPublicAttributes.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class ClassWithNonPublicAttributes extends ParentClassWithProtectedAttributes +final class ClassWithNonPublicAttributes extends ParentClassWithProtectedAttributes { public static $publicStaticAttribute = 'foo'; protected static $protectedStaticAttribute = 'bar'; diff --git a/tests/_files/ClassWithScalarTypeDeclarations.php b/tests/_files/ClassWithScalarTypeDeclarations.php index d18c85606ad..d334cb5d6fc 100644 --- a/tests/_files/ClassWithScalarTypeDeclarations.php +++ b/tests/_files/ClassWithScalarTypeDeclarations.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class ClassWithScalarTypeDeclarations +final class ClassWithScalarTypeDeclarations { public function foo(string $string, int $int): void { diff --git a/tests/_files/ClassWithToString.php b/tests/_files/ClassWithToString.php index b34dfe005df..4195b190583 100644 --- a/tests/_files/ClassWithToString.php +++ b/tests/_files/ClassWithToString.php @@ -11,7 +11,7 @@ use Stringable; -class ClassWithToString implements Stringable +final class ClassWithToString implements Stringable { public function __toString(): string { diff --git a/tests/_files/ConcreteTest.my.php b/tests/_files/ConcreteTest.my.php index df33f32c53c..87c0b7e4de1 100644 --- a/tests/_files/ConcreteTest.my.php +++ b/tests/_files/ConcreteTest.my.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class ConcreteTest extends AbstractTest +final class ConcreteTest extends AbstractTest { public function testTwo(): void { diff --git a/tests/_files/ConcreteTest.php b/tests/_files/ConcreteTest.php index 8ee63f5ca73..fa939c2af25 100644 --- a/tests/_files/ConcreteTest.php +++ b/tests/_files/ConcreteTest.php @@ -9,7 +9,7 @@ */ use PHPUnit\TestFixture\AbstractTest; -class ConcreteTest extends AbstractTest +final class ConcreteTest extends AbstractTest { public function testTwo(): void { diff --git a/tests/_files/CountConstraint.php b/tests/_files/CountConstraint.php deleted file mode 100644 index de2e54d6be5..00000000000 --- a/tests/_files/CountConstraint.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use function sprintf; -use PHPUnit\Framework\Constraint\Constraint; - -final class CountConstraint extends Constraint -{ - /** - * @var int - */ - private $count; - - public static function fromCount(int $count): self - { - $instance = new self; - - $instance->count = $count; - - return $instance; - } - - public function matches(mixed $other): bool - { - return true; - } - - public function toString(bool $exportObjects = false): string - { - return sprintf( - 'is accepted by %s', - self::class, - ); - } - - public function count(): int - { - return $this->count; - } -} diff --git a/tests/_files/CoverageClassNothingTest.php b/tests/_files/CoverageClassNothingTest.php deleted file mode 100644 index 8bf8d16a015..00000000000 --- a/tests/_files/CoverageClassNothingTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @coversNothing - */ -class CoverageClassNothingTest extends TestCase -{ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageClassTest.php b/tests/_files/CoverageClassTest.php deleted file mode 100644 index 7a3f1b7ad20..00000000000 --- a/tests/_files/CoverageClassTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageClassTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass - * - * @uses \PHPUnit\TestFixture\CoveredClass - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageClassWithoutAnnotationsTest.php b/tests/_files/CoverageClassWithoutAnnotationsTest.php deleted file mode 100644 index 93e42229cf8..00000000000 --- a/tests/_files/CoverageClassWithoutAnnotationsTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageClassWithoutAnnotationsTest extends TestCase -{ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageCoversOverridesCoversNothingTest.php b/tests/_files/CoverageCoversOverridesCoversNothingTest.php deleted file mode 100644 index 7f486c6c236..00000000000 --- a/tests/_files/CoverageCoversOverridesCoversNothingTest.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @coversNothing - */ -class CoverageCoversOverridesCoversNothingTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass::publicMethod - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageFunctionParenthesesTest.php b/tests/_files/CoverageFunctionParenthesesTest.php deleted file mode 100644 index 97e8e21f570..00000000000 --- a/tests/_files/CoverageFunctionParenthesesTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageFunctionParenthesesTest extends TestCase -{ - /** - * @covers ::globalFunction() - * - * @uses ::globalFunction() - */ - public function testSomething(): void - { - globalFunction(); - } -} diff --git a/tests/_files/CoverageFunctionParenthesesWhitespaceTest.php b/tests/_files/CoverageFunctionParenthesesWhitespaceTest.php deleted file mode 100644 index f8f5503309c..00000000000 --- a/tests/_files/CoverageFunctionParenthesesWhitespaceTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageFunctionParenthesesWhitespaceTest extends TestCase -{ - /** - * @covers ::globalFunction ( ) - * - * @uses ::globalFunction ( ) - */ - public function testSomething(): void - { - globalFunction(); - } -} diff --git a/tests/_files/CoverageFunctionTest.php b/tests/_files/CoverageFunctionTest.php index 3663230826d..05ad044baa2 100644 --- a/tests/_files/CoverageFunctionTest.php +++ b/tests/_files/CoverageFunctionTest.php @@ -9,15 +9,14 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\UsesFunction; use PHPUnit\Framework\TestCase; -class CoverageFunctionTest extends TestCase +#[CoversFunction('globalFunction')] +#[UsesFunction('globalFunction')] +final class CoverageFunctionTest extends TestCase { - /** - * @covers ::globalFunction - * - * @uses ::globalFunction - */ public function testSomething(): void { globalFunction(); diff --git a/tests/_files/CoverageMethodNothingCoversMethod.php b/tests/_files/CoverageMethodNothingCoversMethod.php deleted file mode 100644 index 9f968788954..00000000000 --- a/tests/_files/CoverageMethodNothingCoversMethod.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageMethodNothingCoversMethod extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass::publicMethod - * - * @coversNothing - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageMethodNothingTest.php b/tests/_files/CoverageMethodNothingTest.php deleted file mode 100644 index 56cae669bca..00000000000 --- a/tests/_files/CoverageMethodNothingTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageMethodNothingTest extends TestCase -{ - /** - * @coversNothing - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageMethodOneLineAnnotationTest.php b/tests/_files/CoverageMethodOneLineAnnotationTest.php deleted file mode 100644 index 8b6e7dc675f..00000000000 --- a/tests/_files/CoverageMethodOneLineAnnotationTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageMethodOneLineAnnotationTest extends TestCase -{ - /** @covers \PHPUnit\TestFixture\CoveredClass::publicMethod */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageMethodParenthesesTest.php b/tests/_files/CoverageMethodParenthesesTest.php deleted file mode 100644 index d86fad3dd6d..00000000000 --- a/tests/_files/CoverageMethodParenthesesTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageMethodParenthesesTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass::publicMethod() - * - * @uses \PHPUnit\TestFixture\CoveredClass::publicMethod() - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageMethodParenthesesWhitespaceTest.php b/tests/_files/CoverageMethodParenthesesWhitespaceTest.php deleted file mode 100644 index 1303687a6b6..00000000000 --- a/tests/_files/CoverageMethodParenthesesWhitespaceTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageMethodParenthesesWhitespaceTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass::publicMethod ( ) - * - * @uses \PHPUnit\TestFixture\CoveredClass::publicMethod ( ) - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/CoverageMethodTest.php b/tests/_files/CoverageMethodTest.php index b9302a88a0d..52c0d169776 100644 --- a/tests/_files/CoverageMethodTest.php +++ b/tests/_files/CoverageMethodTest.php @@ -9,15 +9,14 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\UsesMethod; use PHPUnit\Framework\TestCase; -class CoverageMethodTest extends TestCase +#[CoversMethod(CoveredClass::class, 'publicMethod')] +#[UsesMethod(CoveredClass::class, 'publicMethod')] +final class CoverageMethodTest extends TestCase { - /** - * @covers \PHPUnit\TestFixture\CoveredClass::publicMethod - * - * @uses \PHPUnit\TestFixture\CoveredClass::publicMethod - */ public function testSomething(): void { $o = new CoveredClass; diff --git a/tests/_files/CoverageNamespacedFunctionTest.php b/tests/_files/CoverageNamespacedFunctionTest.php deleted file mode 100644 index 9dde5d5794b..00000000000 --- a/tests/_files/CoverageNamespacedFunctionTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class CoverageNamespacedFunctionTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\func() - * - * @uses \PHPUnit\TestFixture\func() - */ - public function testFunc(): void - { - func(); - } -} diff --git a/tests/_files/CoverageNoneTest.php b/tests/_files/CoverageNoneTest.php index 43f1dc76068..bbebec59a86 100644 --- a/tests/_files/CoverageNoneTest.php +++ b/tests/_files/CoverageNoneTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class CoverageNoneTest extends TestCase +final class CoverageNoneTest extends TestCase { public function testSomething(): void { diff --git a/tests/_files/CoverageTraitTest.php b/tests/_files/CoverageTraitTest.php new file mode 100644 index 00000000000..37e26a38135 --- /dev/null +++ b/tests/_files/CoverageTraitTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\CoversTrait; +use PHPUnit\Framework\Attributes\UsesTrait; +use PHPUnit\Framework\TestCase; + +#[CoversTrait(CoveredTrait::class)] +#[UsesTrait(CoveredTrait::class)] +final class CoverageTraitTest extends TestCase +{ + public function testSomething(): void + { + } +} diff --git a/tests/_files/CoveredClass.php b/tests/_files/CoveredClass.php index ccd30c00f6a..331a28e401a 100644 --- a/tests/_files/CoveredClass.php +++ b/tests/_files/CoveredClass.php @@ -9,24 +9,7 @@ */ namespace PHPUnit\TestFixture; -class CoveredParentClass -{ - public function publicMethod(): void - { - $this->protectedMethod(); - } - - protected function protectedMethod(): void - { - $this->privateMethod(); - } - - private function privateMethod(): void - { - } -} - -class CoveredClass extends CoveredParentClass +final class CoveredClass extends CoveredParentClass { public function publicMethod(): void { diff --git a/tests/_files/CoveredClassUsingCoveredTrait.php b/tests/_files/CoveredClassUsingCoveredTrait.php new file mode 100644 index 00000000000..151f5b47d2e --- /dev/null +++ b/tests/_files/CoveredClassUsingCoveredTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +final class CoveredClassUsingCoveredTrait +{ + use CoveredTrait; + + public function publicMethod(): void + { + } + + protected function protectedMethod(): void + { + } + + private function privateMethod(): void + { + } +} diff --git a/tests/_files/CoveredClassUsingCoveredTraitTest.php b/tests/_files/CoveredClassUsingCoveredTraitTest.php new file mode 100644 index 00000000000..2ea583bfc75 --- /dev/null +++ b/tests/_files/CoveredClassUsingCoveredTraitTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(CoveredClassUsingCoveredTrait::class)] +#[UsesClass(CoveredClassUsingCoveredTrait::class)] +final class CoveredClassUsingCoveredTraitTest extends TestCase +{ + public function testSomething(): void + { + } +} diff --git a/tests/_files/CoveredParentClass.php b/tests/_files/CoveredParentClass.php new file mode 100644 index 00000000000..fce85ca2903 --- /dev/null +++ b/tests/_files/CoveredParentClass.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +class CoveredParentClass +{ + public function publicMethod(): void + { + $this->protectedMethod(); + } + + protected function protectedMethod(): void + { + $this->privateMethod(); + } + + private function privateMethod(): void + { + } +} diff --git a/tests/_files/CoveredTrait.php b/tests/_files/CoveredTrait.php new file mode 100644 index 00000000000..9b9a37d454c --- /dev/null +++ b/tests/_files/CoveredTrait.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +trait CoveredTrait +{ + public function m(int $x, int $y): int + { + return $x + $y; + } +} diff --git a/tests/_files/CoversClassOnClassTest.php b/tests/_files/CoversClassOnClassTest.php new file mode 100644 index 00000000000..62d63175d9f --- /dev/null +++ b/tests/_files/CoversClassOnClassTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(CoveredClass::class)] +#[UsesClass(CoveredClass::class)] +final class CoversClassOnClassTest extends TestCase +{ + public function testSomething(): void + { + $o = new CoveredClass; + + $o->publicMethod(); + } +} diff --git a/tests/_files/CoversNothingOnClassTest.php b/tests/_files/CoversNothingOnClassTest.php new file mode 100644 index 00000000000..9b361f38ada --- /dev/null +++ b/tests/_files/CoversNothingOnClassTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\TestCase; + +#[CoversNothing] +final class CoversNothingOnClassTest extends TestCase +{ + public function testSomething(): void + { + $o = new CoveredClass; + + $o->publicMethod(); + } +} diff --git a/tests/_files/CoversNothingOnMethodTest.php b/tests/_files/CoversNothingOnMethodTest.php new file mode 100644 index 00000000000..c5b178274ad --- /dev/null +++ b/tests/_files/CoversNothingOnMethodTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\TestCase; + +final class CoversNothingOnMethodTest extends TestCase +{ + #[CoversNothing] + public function testSomething(): void + { + $o = new CoveredClass; + + $o->publicMethod(); + } +} diff --git a/tests/_files/CwdRestoreTest.php b/tests/_files/CwdRestoreTest.php new file mode 100644 index 00000000000..d8f42809ba6 --- /dev/null +++ b/tests/_files/CwdRestoreTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use function chdir; +use function sys_get_temp_dir; +use PHPUnit\Framework\TestCase; + +final class CwdRestoreTest extends TestCase +{ + public function testChangesCwd(): void + { + chdir(sys_get_temp_dir()); + + $this->assertTrue(true); + } +} diff --git a/tests/_files/DataProviderDependencyResultTest.php b/tests/_files/DataProviderDependencyResultTest.php new file mode 100644 index 00000000000..752778b10dc --- /dev/null +++ b/tests/_files/DataProviderDependencyResultTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\TestCase; + +final class DataProviderDependencyResultTest extends TestCase +{ + public static function providerMethod(): array + { + return [ + [0, 2], + [1, 1], + ['b' => 2, 'a' => 0], + ]; + } + + #[DataProvider('providerMethod')] + #[Depends('testDependency')] + public function testAdd($a, $b, $c): void + { + $this->assertSame(2, $c); + $this->assertSame($c, $a + $b); + } + + public function testDependency(): int + { + $a = 2; + $this->assertSame(2, $a); + + return $a; + } +} diff --git a/tests/_files/DataProviderDependencyTest.php b/tests/_files/DataProviderDependencyTest.php index 8c501171b10..9b14f5d97c7 100644 --- a/tests/_files/DataProviderDependencyTest.php +++ b/tests/_files/DataProviderDependencyTest.php @@ -9,31 +9,27 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\TestCase; -class DataProviderDependencyTest extends TestCase +final class DataProviderDependencyTest extends TestCase { - public function testReference(): void + public static function provider(): array { - $this->markTestSkipped('This test should be skipped.'); - $this->assertTrue(true); + self::markTestSkipped('Any test with this data provider should be skipped.'); } - /** - * @see https://github.com/sebastianbergmann/phpunit/issues/1896 - * - * @depends testReference - * - * @dataProvider provider - */ - public function testDependency($param): void + public function testReference(): void { + $this->markTestSkipped('This test should be skipped.'); } - public function provider() + #[Depends('testReference')] + #[DataProvider('provider')] + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/1896')] + public function testDependency($param): void { - $this->markTestSkipped('Any test with this data provider should be skipped.'); - - return []; } } diff --git a/tests/_files/DataProviderExecutionOrderTest.php b/tests/_files/DataProviderExecutionOrderTest.php new file mode 100644 index 00000000000..17f6d69ee97 --- /dev/null +++ b/tests/_files/DataProviderExecutionOrderTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class DataProviderExecutionOrderTest extends TestCase +{ + public static function dataProviderAdditions(): array + { + return [ + '1+2=3' => [1, 2, 3], + '2+1=3' => [2, 1, 3], + '1+1=3' => [1, 1, 3], + ]; + } + + public function testFirstTestThatAlwaysWorks(): void + { + $this->assertTrue(true); + } + + #[DataProvider('dataProviderAdditions')] + public function testAddNumbersWithDataProvider(int $a, int $b, int $sum): void + { + $this->assertSame($sum, $a + $b); + } + + public function testTestInTheMiddleThatAlwaysWorks(): void + { + $this->assertTrue(true); + } + + #[DataProvider('dataProviderAdditions')] + public function testAddMoreNumbersWithDataProvider(int $a, int $b, int $sum): void + { + $this->assertSame($sum, $a + $b); + } +} diff --git a/tests/_files/DataProviderFilterTest.php b/tests/_files/DataProviderFilterTest.php index bc3e6840905..1c05d2ecae1 100644 --- a/tests/_files/DataProviderFilterTest.php +++ b/tests/_files/DataProviderFilterTest.php @@ -9,11 +9,12 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -class DataProviderFilterTest extends TestCase +final class DataProviderFilterTest extends TestCase { - public static function truthProvider() + public static function truthProvider(): array { return [ [true], @@ -23,7 +24,7 @@ public static function truthProvider() ]; } - public static function falseProvider() + public static function falseProvider(): array { return [ 'false test' => [false], @@ -33,17 +34,13 @@ public static function falseProvider() ]; } - /** - * @dataProvider truthProvider - */ + #[DataProvider('truthProvider')] public function testTrue($truth): void { $this->assertTrue($truth); } - /** - * @dataProvider falseProvider - */ + #[DataProvider('falseProvider')] public function testFalse($false): void { $this->assertFalse($false); diff --git a/tests/_files/DataProviderIncompleteTest.php b/tests/_files/DataProviderIncompleteTest.php index 992c006baf0..82df3b64708 100644 --- a/tests/_files/DataProviderIncompleteTest.php +++ b/tests/_files/DataProviderIncompleteTest.php @@ -9,11 +9,12 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -class DataProviderIncompleteTest extends TestCase +final class DataProviderIncompleteTest extends TestCase { - public static function providerMethod() + public static function providerMethod(): array { return [ [0, 0, 0], @@ -21,29 +22,20 @@ public static function providerMethod() ]; } - /** - * @dataProvider incompleteTestProviderMethod - */ + public static function incompleteTestProviderMethod(): array + { + self::markTestIncomplete('incomplete'); + } + + #[DataProvider('incompleteTestProviderMethod')] public function testIncomplete($a, $b, $c): void { $this->assertTrue(true); } - /** - * @dataProvider providerMethod - */ + #[DataProvider('providerMethod')] public function testAdd($a, $b, $c): void { $this->assertEquals($c, $a + $b); } - - public function incompleteTestProviderMethod() - { - $this->markTestIncomplete('incomplete'); - - return [ - [0, 0, 0], - [0, 1, 1], - ]; - } } diff --git a/tests/_files/DataProviderIssue2833/FirstTest.php b/tests/_files/DataProviderIssue2833/FirstTest.php index 08aba21102f..165411222e2 100644 --- a/tests/_files/DataProviderIssue2833/FirstTest.php +++ b/tests/_files/DataProviderIssue2833/FirstTest.php @@ -9,20 +9,19 @@ */ namespace PHPUnit\TestFixture\DataProviderIssue2833; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class FirstTest extends TestCase { - public static function provide() + public static function provide(): array { SecondTest::DUMMY; return [[true]]; } - /** - * @dataProvider provide - */ + #[DataProvider('provide')] public function testFirst($x): void { $this->assertTrue(true); diff --git a/tests/_files/DataProviderIssue2859/phpunit.xml b/tests/_files/DataProviderIssue2859/phpunit.xml deleted file mode 100644 index 1e47e5123be..00000000000 --- a/tests/_files/DataProviderIssue2859/phpunit.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - ./tests/ - ./tests/*/ - - - diff --git a/tests/_files/DataProviderIssue2859/tests/another/TestWithDataProviderTest.php b/tests/_files/DataProviderIssue2859/tests/another/TestWithDataProviderTest.php deleted file mode 100644 index 384945bc1bc..00000000000 --- a/tests/_files/DataProviderIssue2859/tests/another/TestWithDataProviderTest.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\DataProviderIssue2859; - -use PHPUnit\Framework\TestCase; - -class TestWithDataProviderTest extends TestCase -{ - public static function provide() - { - return [[true]]; - } - - /** - * @dataProvider provide - */ - public function testFirst($x): void - { - $this->assertTrue(true); - } -} diff --git a/tests/_files/DataProviderNamedArgumentsTest.php b/tests/_files/DataProviderNamedArgumentsTest.php new file mode 100644 index 00000000000..facc8dded37 --- /dev/null +++ b/tests/_files/DataProviderNamedArgumentsTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class DataProviderNamedArgumentsTest extends TestCase +{ + public static function providerMethod() + { + return [ + ['a' => 1, 'b' => 2, 'c' => 3], + ['c' => 3, 'a' => 2, 'b' => 1], + ]; + } + + #[DataProvider('providerMethod')] + public function testAdd($a, $b, $c): void + { + $this->assertEquals($c, $a + $b); + } +} diff --git a/tests/_files/DataProviderRequiresPhpUnitTest.php b/tests/_files/DataProviderRequiresPhpUnitTest.php new file mode 100644 index 00000000000..03a32bd2a4f --- /dev/null +++ b/tests/_files/DataProviderRequiresPhpUnitTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use Exception; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\TestCase; + +final class DataProviderRequiresPhpUnitTest extends TestCase +{ + public static function providerThatThrows(): array + { + throw new Exception('Should have been skipped.'); + } + + public static function validProvider(): array + { + return [[true], [true]]; + } + + public function invalidProvider(): array + { + return [[true], [true]]; + } + + #[RequiresPhpunit('< 10')] + #[DataProvider('invalidProvider')] + public function testWithInvalidDataProvider(bool $param): void + { + $this->assertTrue($param); + } + + #[RequiresPhpunit('>= 10')] + #[DataProvider('validProvider')] + public function testWithValidDataProvider(bool $param): void + { + $this->assertTrue($param); + } + + #[RequiresPhpunit('< 10')] + #[DataProvider('providerThatThrows')] + public function testWithDataProviderThatThrows(): void + { + } + + #[RequiresPhpunit('< 10')] + #[DataProviderExternal(self::class, 'providerThatThrows')] + public function testWithDataProviderExternalThatThrows(): void + { + } +} diff --git a/tests/_files/DataProviderSkippedTest.php b/tests/_files/DataProviderSkippedTest.php index 9eabf9ceb5b..939b100290d 100644 --- a/tests/_files/DataProviderSkippedTest.php +++ b/tests/_files/DataProviderSkippedTest.php @@ -9,9 +9,10 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -class DataProviderSkippedTest extends TestCase +final class DataProviderSkippedTest extends TestCase { public static function providerMethod() { @@ -21,29 +22,20 @@ public static function providerMethod() ]; } - /** - * @dataProvider skippedTestProviderMethod - */ + public static function skippedTestProviderMethod(): array + { + self::markTestSkipped('skipped'); + } + + #[DataProvider('skippedTestProviderMethod')] public function testSkipped($a, $b, $c): void { $this->assertTrue(true); } - /** - * @dataProvider providerMethod - */ + #[DataProvider('providerMethod')] public function testAdd($a, $b, $c): void { $this->assertEquals($c, $a + $b); } - - public function skippedTestProviderMethod() - { - $this->markTestSkipped('skipped'); - - return [ - [0, 0, 0], - [0, 1, 1], - ]; - } } diff --git a/tests/_files/DataProviderTest.php b/tests/_files/DataProviderTest.php index 71c71974613..7fd152cc0e4 100644 --- a/tests/_files/DataProviderTest.php +++ b/tests/_files/DataProviderTest.php @@ -9,11 +9,12 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -class DataProviderTest extends TestCase +final class DataProviderTest extends TestCase { - public static function providerMethod() + public static function providerMethod(): array { return [ [0, 0, 0], @@ -23,9 +24,7 @@ public static function providerMethod() ]; } - /** - * @dataProvider providerMethod - */ + #[DataProvider('providerMethod')] public function testAdd($a, $b, $c): void { $this->assertEquals($c, $a + $b); diff --git a/tests/_files/DataProviderWithStringKeysTest.php b/tests/_files/DataProviderWithStringKeysTest.php new file mode 100644 index 00000000000..4328f243296 --- /dev/null +++ b/tests/_files/DataProviderWithStringKeysTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class DataProviderWithStringKeysTest extends TestCase +{ + public static function providerMethod(): array + { + return [ + '0 + 0 = 0' => [0, 0, 0], + '0 + 1 = 1' => [0, 1, 1], + '1 + 1 = 3' => [1, 1, 3], + '1 + 0 = 1' => [1, 0, 1], + ]; + } + + #[DataProvider('providerMethod')] + public function testAdd($a, $b, $c): void + { + $this->assertEquals($c, $a + $b); + } +} diff --git a/tests/_files/DataproviderExecutionOrderTest.php b/tests/_files/DataproviderExecutionOrderTest.php deleted file mode 100644 index 4a6a01d8f0c..00000000000 --- a/tests/_files/DataproviderExecutionOrderTest.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class DataproviderExecutionOrderTest extends TestCase -{ - public function testFirstTestThatAlwaysWorks(): void - { - $this->assertTrue(true); - } - - /** - * @dataProvider dataproviderAdditions - */ - public function testAddNumbersWithADataprovider(int $a, int $b, int $sum): void - { - $this->assertSame($sum, $a + $b); - } - - public function testTestInTheMiddleThatAlwaysWorks(): void - { - $this->assertTrue(true); - } - - /** - * @dataProvider dataproviderAdditions - */ - public function testAddMoreNumbersWithADataprovider(int $a, int $b, int $sum): void - { - $this->assertSame($sum, $a + $b); - } - - public function dataproviderAdditions() - { - return [ - '1+2=3' => [1, 2, 3], - '2+1=3' => [2, 1, 3], - '1+1=3' => [1, 1, 3], - ]; - } -} diff --git a/tests/_files/DependencyInputTest.php b/tests/_files/DependencyInputTest.php index 236302b357a..9e27f85d2cd 100644 --- a/tests/_files/DependencyInputTest.php +++ b/tests/_files/DependencyInputTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class DependencyInputTest extends TestCase +final class DependencyInputTest extends TestCase { public function testDependencyInputAsParameter(string $dependencyInput): void { diff --git a/tests/_files/DependencyOnClassTest.php b/tests/_files/DependencyOnClassTest.php index 9bbee43bbd6..763f9ce1f31 100644 --- a/tests/_files/DependencyOnClassTest.php +++ b/tests/_files/DependencyOnClassTest.php @@ -9,29 +9,18 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DependsOnClass; use PHPUnit\Framework\TestCase; -class DependencyOnClassTest extends TestCase +final class DependencyOnClassTest extends TestCase { - /** - * Guard support for using annotations to depend on a whole successful TestSuite. - * - * @depends PHPUnit\TestFixture\DependencySuccessTest::class - * - * @see https://github.com/sebastianbergmann/phpunit/issues/3519 - */ + #[DependsOnClass(DependencySuccessTest::class)] public function testThatDependsOnASuccessfulClass(): void { $this->assertTrue(true); } - /** - * Guard support for using annotations to depend on a whole failing TestSuite. - * - * @depends PHPUnit\TestFixture\DependencyFailureTest::class - * - * @see https://github.com/sebastianbergmann/phpunit/issues/3519 - */ + #[DependsOnClass(DependencyFailureTest::class)] public function testThatDependsOnAFailingClass(): void { $this->assertTrue(true); diff --git a/tests/_files/DoNoAssertionTestCase.php b/tests/_files/DoNoAssertionTestCase.php index 282793f8bc9..0e6747cf4d7 100644 --- a/tests/_files/DoNoAssertionTestCase.php +++ b/tests/_files/DoNoAssertionTestCase.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class DoNoAssertionTestCase extends TestCase +final class DoNoAssertionTestCase extends TestCase { public function testNothing(): void { diff --git a/tests/_files/DoesNotPerformAssertionsButPerformingAssertionsTest.php b/tests/_files/DoesNotPerformAssertionsButPerformingAssertionsTest.php index 59d332fe972..226d7423cf4 100644 --- a/tests/_files/DoesNotPerformAssertionsButPerformingAssertionsTest.php +++ b/tests/_files/DoesNotPerformAssertionsButPerformingAssertionsTest.php @@ -9,13 +9,12 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; -class DoesNotPerformAssertionsButPerformingAssertionsTest extends TestCase +final class DoesNotPerformAssertionsButPerformingAssertionsTest extends TestCase { - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testFalseAndTrueAreStillFine(): void { $this->assertFalse(false); diff --git a/tests/_files/DummyBarTest.php b/tests/_files/DummyBarTest.php index d6aa560e3c9..a06b782ff21 100644 --- a/tests/_files/DummyBarTest.php +++ b/tests/_files/DummyBarTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class DummyBarTest extends TestCase +final class DummyBarTest extends TestCase { public function testBarEqualsBar(): void { diff --git a/tests/_files/DummyException.php b/tests/_files/DummyException.php index b8fd8191bbe..fce0c0565c4 100644 --- a/tests/_files/DummyException.php +++ b/tests/_files/DummyException.php @@ -11,6 +11,6 @@ use Exception; -class DummyException extends Exception +final class DummyException extends Exception { } diff --git a/tests/_files/DummyFooTest.php b/tests/_files/DummyFooTest.php index 10e7e4a8bb0..7ef011680ee 100644 --- a/tests/_files/DummyFooTest.php +++ b/tests/_files/DummyFooTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class DummyFooTest extends TestCase +final class DummyFooTest extends TestCase { public function testFooEqualsFoo(): void { diff --git a/tests/_files/DuplicateKeyDataProviderTest.php b/tests/_files/DuplicateKeyDataProviderTest.php index 7fa882ebc24..60568a9c732 100644 --- a/tests/_files/DuplicateKeyDataProviderTest.php +++ b/tests/_files/DuplicateKeyDataProviderTest.php @@ -9,20 +9,20 @@ */ namespace PHPUnit\TestFixture; +use Generator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class DuplicateKeyDataProviderTest extends TestCase { - public static function dataProvider() + public static function dataProvider(): Generator { yield 'foo' => ['foo']; yield 'foo' => ['bar']; } - /** - * @dataProvider dataProvider - */ + #[DataProvider('dataProvider')] public function test($arg): void { } diff --git a/tests/_files/DuplicateKeyDataProvidersTest.php b/tests/_files/DuplicateKeyDataProvidersTest.php new file mode 100644 index 00000000000..a44c5578449 --- /dev/null +++ b/tests/_files/DuplicateKeyDataProvidersTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class DuplicateKeyDataProvidersTest extends TestCase +{ + public static function dataProvider1(): iterable + { + return [ + 'bar' => [1], + ]; + } + + public static function dataProvider2(): iterable + { + return [ + 'bar' => [2], + ]; + } + + #[DataProvider('dataProvider1')] + #[DataProvider('dataProvider2')] + public function test($value): void + { + $this->assertSame(2, $value); + } +} diff --git a/tests/_files/EmptyDataProviderTest.php b/tests/_files/EmptyDataProviderTest.php index bb4af0a6249..b7654d38fe6 100644 --- a/tests/_files/EmptyDataProviderTest.php +++ b/tests/_files/EmptyDataProviderTest.php @@ -9,18 +9,17 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -class EmptyDataProviderTest extends TestCase +final class EmptyDataProviderTest extends TestCase { - public static function providerMethod() + public static function providerMethod(): array { return []; } - /** - * @dataProvider providerMethod - */ + #[DataProvider('providerMethod')] public function testCase(): void { } diff --git a/tests/_files/EnumerationEquals/Example.php b/tests/_files/EnumerationEquals/Example.php new file mode 100644 index 00000000000..0667f81a8c4 --- /dev/null +++ b/tests/_files/EnumerationEquals/Example.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\EnumerationEquals; + +enum Example +{ + case Foo; + case Bar; +} diff --git a/tests/_files/EnumerationEquals/ExampleInt.php b/tests/_files/EnumerationEquals/ExampleInt.php new file mode 100644 index 00000000000..c5468c4b2b8 --- /dev/null +++ b/tests/_files/EnumerationEquals/ExampleInt.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\EnumerationEquals; + +enum ExampleInt: int +{ + case Foo = 0; + case Bar = 1; +} diff --git a/tests/_files/EnumerationEquals/ExampleString.php b/tests/_files/EnumerationEquals/ExampleString.php new file mode 100644 index 00000000000..e40241ccd33 --- /dev/null +++ b/tests/_files/EnumerationEquals/ExampleString.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\EnumerationEquals; + +enum ExampleString: string +{ + case Foo = 'foo'; + case Bar = 'bar'; +} diff --git a/tests/_files/ExceptionInMockDestructor.php b/tests/_files/ExceptionInMockDestructor.php new file mode 100644 index 00000000000..6be9ff15e1a --- /dev/null +++ b/tests/_files/ExceptionInMockDestructor.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use Exception; + +class ExceptionInMockDestructor +{ + public function __destruct() + { + throw new Exception('Some exception.'); + } +} diff --git a/tests/_files/ExceptionInMockDestructorTest.php b/tests/_files/ExceptionInMockDestructorTest.php new file mode 100644 index 00000000000..10ca362150c --- /dev/null +++ b/tests/_files/ExceptionInMockDestructorTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\TestCase; + +final class ExceptionInMockDestructorTest extends TestCase +{ + public function testOne(): void + { + $mock = $this->createMock(ExceptionInMockDestructor::class); + + $this->assertInstanceOf(ExceptionInMockDestructor::class, $mock); + } +} diff --git a/tests/_files/ExceptionStackTest.php b/tests/_files/ExceptionStackTest.php index 07218d575a2..fef5e2df644 100644 --- a/tests/_files/ExceptionStackTest.php +++ b/tests/_files/ExceptionStackTest.php @@ -15,7 +15,7 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -class ExceptionStackTest extends TestCase +final class ExceptionStackTest extends TestCase { public function testPrintingChildException(): void { diff --git a/tests/_files/FailureTest.php b/tests/_files/FailureTest.php index 828da0a953f..deb2b4f4e98 100644 --- a/tests/_files/FailureTest.php +++ b/tests/_files/FailureTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\TestCase; use stdClass; -class FailureTest extends TestCase +final class FailureTest extends TestCase { public function testAssertArrayEqualsArray(): void { diff --git a/tests/_files/Foo.php b/tests/_files/Foo.php index 61fc861dce6..f10ca723838 100644 --- a/tests/_files/Foo.php +++ b/tests/_files/Foo.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class Foo +final class Foo { public function doSomething(Bar $bar) { diff --git a/tests/_files/Go ogle-Sea.rch.wsdl b/tests/_files/Go ogle-Sea.rch.wsdl deleted file mode 100644 index e448501dd1b..00000000000 --- a/tests/_files/Go ogle-Sea.rch.wsdl +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/_files/GoogleSearch.wsdl b/tests/_files/GoogleSearch.wsdl deleted file mode 100644 index e448501dd1b..00000000000 --- a/tests/_files/GoogleSearch.wsdl +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/_files/IncompleteTest.php b/tests/_files/IncompleteTest.php index 72d1eb8bf45..3668c8fd121 100644 --- a/tests/_files/IncompleteTest.php +++ b/tests/_files/IncompleteTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class IncompleteTest extends TestCase +final class IncompleteTest extends TestCase { public function testIncomplete(): void { diff --git a/tests/_files/InheritedTestCase.php b/tests/_files/InheritedTestCase.php index 3afc714074a..5c379378e0e 100644 --- a/tests/_files/InheritedTestCase.php +++ b/tests/_files/InheritedTestCase.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class InheritedTestCase extends OneTestCase +final class InheritedTestCase extends OneTestCase { public function test2(): void { diff --git a/tests/_files/IniTest.php b/tests/_files/IniTest.php index c97e6ae4342..2400d0571c8 100644 --- a/tests/_files/IniTest.php +++ b/tests/_files/IniTest.php @@ -10,13 +10,12 @@ namespace PHPUnit\TestFixture; use function ini_get; +use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\TestCase; -class IniTest extends TestCase +final class IniTest extends TestCase { - /** - * @preserveGlobalState enabled - */ + #[PreserveGlobalState(true)] public function testIni(): void { $this->assertEquals('application/x-test', ini_get('default_mimetype')); diff --git a/tests/_files/InterfaceAsTargetWithAttributeTest.php b/tests/_files/InterfaceAsTargetWithAttributeTest.php new file mode 100644 index 00000000000..98d4eb45b7e --- /dev/null +++ b/tests/_files/InterfaceAsTargetWithAttributeTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; +use Throwable; + +#[CoversClass(Throwable::class)] +#[UsesClass(Throwable::class)] +final class InterfaceAsTargetWithAttributeTest extends TestCase +{ + public function testOne(): void + { + } +} diff --git a/tests/_files/InterfaceTargetTest.php b/tests/_files/InterfaceTargetTest.php deleted file mode 100644 index 070c2ed4da8..00000000000 --- a/tests/_files/InterfaceTargetTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @covers \Throwable - */ -final class InterfaceTargetTest extends TestCase -{ - public function testOne(): void - { - } -} diff --git a/tests/_files/InvalidClassTargetWithAnnotationTest.php b/tests/_files/InvalidClassTargetWithAnnotationTest.php deleted file mode 100644 index 65d4c5d1092..00000000000 --- a/tests/_files/InvalidClassTargetWithAnnotationTest.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @covers InvalidClass - * - * @uses InvalidClass - */ -final class InvalidClassTargetWithAnnotationTest extends TestCase -{ - public function testOne(): void - { - } -} diff --git a/tests/_files/IsolationTest.php b/tests/_files/IsolationTest.php index 113f5443729..3a0a76cf640 100644 --- a/tests/_files/IsolationTest.php +++ b/tests/_files/IsolationTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class IsolationTest extends TestCase +final class IsolationTest extends TestCase { public function testIsInIsolationReturnsFalse(): void { diff --git a/tests/_files/Metadata/Annotation/src/Example.php b/tests/_files/Metadata/Annotation/src/Example.php deleted file mode 100644 index 3e69570715d..00000000000 --- a/tests/_files/Metadata/Annotation/src/Example.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -final class Example -{ - public function method(): void - { - } -} - -function f(): void -{ -} diff --git a/tests/_files/Metadata/Annotation/tests/BackupGlobalsTest.php b/tests/_files/Metadata/Annotation/tests/BackupGlobalsTest.php deleted file mode 100644 index 0551f00b7fa..00000000000 --- a/tests/_files/Metadata/Annotation/tests/BackupGlobalsTest.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @backupGlobals enabled - * - * @excludeGlobalVariableFromBackup foo - */ -final class BackupGlobalsTest extends TestCase -{ - /** - * @backupGlobals disabled - * - * @excludeGlobalVariableFromBackup bar - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/BackupStaticPropertiesTest.php b/tests/_files/Metadata/Annotation/tests/BackupStaticPropertiesTest.php deleted file mode 100644 index 82e19cd5423..00000000000 --- a/tests/_files/Metadata/Annotation/tests/BackupStaticPropertiesTest.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @backupStaticProperties enabled - * - * @excludeStaticPropertyFromBackup className propertyName - */ -final class BackupStaticPropertiesTest extends TestCase -{ - /** - * @backupStaticAttributes disabled - * - * @excludeStaticPropertyFromBackup anotherClassName propertyName - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/CoversTest.php b/tests/_files/Metadata/Annotation/tests/CoversTest.php deleted file mode 100644 index 39b5a2a6a54..00000000000 --- a/tests/_files/Metadata/Annotation/tests/CoversTest.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @covers ::\PHPUnit\TestFixture\Metadata\Annotation\f() - * @covers \PHPUnit\TestFixture\Metadata\Annotation\Example - * - * @coversNothing - * - * @coversDefaultClass \PHPUnit\TestFixture\Metadata\Annotation\Example - */ -final class CoversTest extends TestCase -{ - /** - * @coversNothing - */ - public function testOne(): void - { - } - - /** - * @covers Foo::bar - */ - public function testTwo(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/DependencyTest.php b/tests/_files/Metadata/Annotation/tests/DependencyTest.php deleted file mode 100644 index ff3694c6e34..00000000000 --- a/tests/_files/Metadata/Annotation/tests/DependencyTest.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -final class DependencyTest extends TestCase -{ - /** - * @depends PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::testOne - * @depends !clone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::testOne - * @depends clone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::testOne - * @depends !shallowClone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::testOne - * @depends shallowClone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::testOne - */ - public function testOne(): void - { - } - - /** - * @depends testOne - * @depends !clone testOne - * @depends clone testOne - * @depends !shallowClone testOne - * @depends shallowClone testOne - */ - public function testTwo(): void - { - } - - /** - * @depends PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::class - * @depends !clone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::class - * @depends clone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::class - * @depends !shallowClone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::class - * @depends shallowClone PHPUnit\TestFixture\Metadata\Annotation\AnotherTest::class - */ - public function testThree(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/DoesNotPerformAssertionsTest.php b/tests/_files/Metadata/Annotation/tests/DoesNotPerformAssertionsTest.php deleted file mode 100644 index e0cbfabd3f3..00000000000 --- a/tests/_files/Metadata/Annotation/tests/DoesNotPerformAssertionsTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @doesNotPerformAssertions - */ -final class DoesNotPerformAssertionsTest extends TestCase -{ - /** - * @doesNotPerformAssertions - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/GroupTest.php b/tests/_files/Metadata/Annotation/tests/GroupTest.php deleted file mode 100644 index 3a74dff2eeb..00000000000 --- a/tests/_files/Metadata/Annotation/tests/GroupTest.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @group group - * - * @ticket ticket - */ -final class GroupTest extends TestCase -{ - /** - * @group another-group - * - * @ticket another-ticket - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/LargeTest.php b/tests/_files/Metadata/Annotation/tests/LargeTest.php deleted file mode 100644 index 84971e403bc..00000000000 --- a/tests/_files/Metadata/Annotation/tests/LargeTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @large - */ -final class LargeTest extends TestCase -{ - /** - * @large - */ - public function testWithLargeAnnotation(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/MediumTest.php b/tests/_files/Metadata/Annotation/tests/MediumTest.php deleted file mode 100644 index 2f36e205556..00000000000 --- a/tests/_files/Metadata/Annotation/tests/MediumTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @medium - */ -final class MediumTest extends TestCase -{ - /** - * @medium - */ - public function testWithMediumAnnotation(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/PreserveGlobalStateTest.php b/tests/_files/Metadata/Annotation/tests/PreserveGlobalStateTest.php deleted file mode 100644 index 7db316df7bb..00000000000 --- a/tests/_files/Metadata/Annotation/tests/PreserveGlobalStateTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @preserveGlobalState enabled - */ -final class PreserveGlobalStateTest extends TestCase -{ - /** - * @preserveGlobalState disabled - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/ProcessIsolationTest.php b/tests/_files/Metadata/Annotation/tests/ProcessIsolationTest.php deleted file mode 100644 index 7d7d2918d3f..00000000000 --- a/tests/_files/Metadata/Annotation/tests/ProcessIsolationTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @runClassInSeparateProcess - * - * @runTestsInSeparateProcesses - */ -final class ProcessIsolationTest extends TestCase -{ - /** - * @runInSeparateProcess - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresFunctionTest.php b/tests/_files/Metadata/Annotation/tests/RequiresFunctionTest.php deleted file mode 100644 index cc8306297bd..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresFunctionTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires function f - * @requires function SomeClass::someMethod - */ -final class RequiresFunctionTest extends TestCase -{ - /** - * @requires function g - * @requires function SomeOtherClass::someOtherMethod - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresOperatingSystemFamilyTest.php b/tests/_files/Metadata/Annotation/tests/RequiresOperatingSystemFamilyTest.php deleted file mode 100644 index 72f7215cd7c..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresOperatingSystemFamilyTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires OSFAMILY Linux - */ -final class RequiresOperatingSystemFamilyTest extends TestCase -{ - /** - * @requires OSFAMILY Linux - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresOperatingSystemTest.php b/tests/_files/Metadata/Annotation/tests/RequiresOperatingSystemTest.php deleted file mode 100644 index 230d5aec1b1..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresOperatingSystemTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires OS Linux - */ -final class RequiresOperatingSystemTest extends TestCase -{ - /** - * @requires OS Linux - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresPhp2Test.php b/tests/_files/Metadata/Annotation/tests/RequiresPhp2Test.php deleted file mode 100644 index ce88127780c..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresPhp2Test.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires PHP ^8.0 - */ -final class RequiresPhp2Test extends TestCase -{ - /** - * @requires PHP ^8.0 - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresPhpExtensionTest.php b/tests/_files/Metadata/Annotation/tests/RequiresPhpExtensionTest.php deleted file mode 100644 index bf4e20a183b..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresPhpExtensionTest.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires extension bar - * @requires extension foo >= 1.0.0 - */ -final class RequiresPhpExtensionTest extends TestCase -{ - /** - * @requires extension foo - * @requires extension bar >= 1.0.0 - */ - public function testOne(): void - { - } - - /** - * @requires extension baz < 2.0.0 - */ - public function testTwo(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresPhpTest.php b/tests/_files/Metadata/Annotation/tests/RequiresPhpTest.php deleted file mode 100644 index da54ec38248..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresPhpTest.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires PHP 8.0.0 - */ -final class RequiresPhpTest extends TestCase -{ - /** - * @requires PHP < 9.0.0 - */ - public function testOne(): void - { - } - - /** @requires PHP < 9.0.0 */ - public function testTwo(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresPhpunit2Test.php b/tests/_files/Metadata/Annotation/tests/RequiresPhpunit2Test.php deleted file mode 100644 index b74e2a949e4..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresPhpunit2Test.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires PHPUnit ^10.0 - */ -final class RequiresPhpunit2Test extends TestCase -{ - /** - * @requires PHPUnit ^11.0 - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresPhpunitTest.php b/tests/_files/Metadata/Annotation/tests/RequiresPhpunitTest.php deleted file mode 100644 index 7643f545ec3..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresPhpunitTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires PHPUnit 10.0.0 - */ -final class RequiresPhpunitTest extends TestCase -{ - /** - * @requires PHPUnit < 11.0.0 - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/RequiresSettingTest.php b/tests/_files/Metadata/Annotation/tests/RequiresSettingTest.php deleted file mode 100644 index ff668af6d02..00000000000 --- a/tests/_files/Metadata/Annotation/tests/RequiresSettingTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @requires setting foo bar - */ -final class RequiresSettingTest extends TestCase -{ - /** - * @requires setting bar baz - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/SmallTest.php b/tests/_files/Metadata/Annotation/tests/SmallTest.php deleted file mode 100644 index 59752bfff62..00000000000 --- a/tests/_files/Metadata/Annotation/tests/SmallTest.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @small - */ -final class SmallTest extends TestCase -{ - /** - * @beforeClass - */ - public function beforeTests(): void - { - } - - /** - * @before - */ - public function beforeTest(): void - { - } - - /** - * @preCondition - */ - public function preCondition(): void - { - } - - /** - * @test - */ - public function one(): void - { - } - - /** - * @dataProvider provider - */ - public function testWithDataProvider(): void - { - } - - /** - * @dataProvider \PHPUnit\TestFixture\Metadata\Annotation\SmallTest::provider - */ - public function testWithDataProviderExternal(): void - { - } - - /** - * @postCondition - */ - public function postCondition(): void - { - } - - /** - * @after - */ - public function afterTest(): void - { - } - - /** - * @afterClass - */ - public function afterTests(): void - { - } - - /** - * @small - */ - public function testWithSmallAnnotation(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/TestDoxTest.php b/tests/_files/Metadata/Annotation/tests/TestDoxTest.php deleted file mode 100644 index b50aa2fe3b8..00000000000 --- a/tests/_files/Metadata/Annotation/tests/TestDoxTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @testdox text - */ -final class TestDoxTest extends TestCase -{ - /** - * @testdox text - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Annotation/tests/TestWithTest.php b/tests/_files/Metadata/Annotation/tests/TestWithTest.php deleted file mode 100644 index 7b53d3e2e35..00000000000 --- a/tests/_files/Metadata/Annotation/tests/TestWithTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -final class TestWithTest extends TestCase -{ - /** - * @testWith [1, 2, 3] - */ - public function testOne(): void - { - $this->assertTrue(true); - } -} diff --git a/tests/_files/Metadata/Annotation/tests/UsesTest.php b/tests/_files/Metadata/Annotation/tests/UsesTest.php deleted file mode 100644 index c575edcc8ea..00000000000 --- a/tests/_files/Metadata/Annotation/tests/UsesTest.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Metadata\Annotation; - -use PHPUnit\Framework\TestCase; - -/** - * @uses ::\PHPUnit\TestFixture\Metadata\Annotation\f() - * @uses \PHPUnit\TestFixture\Metadata\Annotation\Example - * - * @usesDefaultClass \PHPUnit\TestFixture\Metadata\Annotation\Example - */ -final class UsesTest extends TestCase -{ - /** - * @uses Foo::bar - */ - public function testOne(): void - { - } -} diff --git a/tests/_files/Metadata/Attribute/src/ExampleTrait.php b/tests/_files/Metadata/Attribute/src/ExampleTrait.php new file mode 100644 index 00000000000..8b8d743a1a3 --- /dev/null +++ b/tests/_files/Metadata/Attribute/src/ExampleTrait.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +trait ExampleTrait +{ +} diff --git a/tests/_files/Metadata/Attribute/tests/CoversNothingTest.php b/tests/_files/Metadata/Attribute/tests/CoversNothingTest.php new file mode 100644 index 00000000000..bd5b02a1e97 --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/CoversNothingTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\TestCase; + +#[CoversNothing] +final class CoversNothingTest extends TestCase +{ + #[CoversNothing] + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/CoversTest.php b/tests/_files/Metadata/Attribute/tests/CoversTest.php index 6213267ec96..c8eed04d646 100644 --- a/tests/_files/Metadata/Attribute/tests/CoversTest.php +++ b/tests/_files/Metadata/Attribute/tests/CoversTest.php @@ -10,16 +10,23 @@ namespace PHPUnit\TestFixture\Metadata\Attribute; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\CoversClassesThatImplementInterface; use PHPUnit\Framework\Attributes\CoversFunction; -use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\CoversNamespace; +use PHPUnit\Framework\Attributes\CoversTrait; use PHPUnit\Framework\TestCase; +#[CoversNamespace('PHPUnit\TestFixture\Metadata\Attribute')] #[CoversClass(Example::class)] +#[CoversClassesThatExtendClass(Example::class)] +#[CoversClassesThatImplementInterface(Example::class)] +#[CoversTrait(ExampleTrait::class)] +#[CoversMethod(Example::class, 'method')] #[CoversFunction('f')] -#[CoversNothing] final class CoversTest extends TestCase { - #[CoversNothing] public function testOne(): void { } diff --git a/tests/_files/Metadata/Attribute/tests/DisableReturnValueGenerationForTestDoublesTest.php b/tests/_files/Metadata/Attribute/tests/DisableReturnValueGenerationForTestDoublesTest.php new file mode 100644 index 00000000000..6a8537248b3 --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/DisableReturnValueGenerationForTestDoublesTest.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Framework\TestCase; + +#[DisableReturnValueGenerationForTestDoubles] +final class DisableReturnValueGenerationForTestDoublesTest extends TestCase +{ + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/DuplicateSmallAttributeTest.php b/tests/_files/Metadata/Attribute/tests/DuplicateSmallAttributeTest.php new file mode 100644 index 00000000000..218c4bd8fdf --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/DuplicateSmallAttributeTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[Small] +#[Small] +final class DuplicateSmallAttributeTest extends TestCase +{ + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/DuplicateTestAttributeTest.php b/tests/_files/Metadata/Attribute/tests/DuplicateTestAttributeTest.php new file mode 100644 index 00000000000..b5214201fed --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/DuplicateTestAttributeTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; + +final class DuplicateTestAttributeTest extends TestCase +{ + #[Test] + #[Test] + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/PhpunitAttributeThatDoesNotExistTest.php b/tests/_files/Metadata/Attribute/tests/PhpunitAttributeThatDoesNotExistTest.php new file mode 100644 index 00000000000..067a9df2f2e --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/PhpunitAttributeThatDoesNotExistTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\PhpunitAttributeThatDoesNotExist; +use PHPUnit\Framework\TestCase; + +#[PhpunitAttributeThatDoesNotExist] +final class PhpunitAttributeThatDoesNotExistTest extends TestCase +{ + #[PhpunitAttributeThatDoesNotExist] + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/RequiresEnvironmentVariableTest.php b/tests/_files/Metadata/Attribute/tests/RequiresEnvironmentVariableTest.php new file mode 100644 index 00000000000..96fefede969 --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/RequiresEnvironmentVariableTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable; +use PHPUnit\Framework\TestCase; + +#[RequiresEnvironmentVariable('foo', 'bar')] +final class RequiresEnvironmentVariableTest extends TestCase +{ + #[RequiresEnvironmentVariable('foo')] + #[RequiresEnvironmentVariable('bar', 'baz')] + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/RequiresPhpunitExtensionTest.php b/tests/_files/Metadata/Attribute/tests/RequiresPhpunitExtensionTest.php new file mode 100644 index 00000000000..360b01996b3 --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/RequiresPhpunitExtensionTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\RequiresPhpunitExtension; +use PHPUnit\Framework\TestCase; + +#[RequiresPhpunitExtension(SomeExtension::class)] +final class RequiresPhpunitExtensionTest extends TestCase +{ + #[RequiresPhpunitExtension(SomeExtension::class)] + #[RequiresPhpunitExtension(SomeOtherExtension::class)] + public function testOne(): void + { + } +} diff --git a/tests/_files/Metadata/Attribute/tests/TestWithTest.php b/tests/_files/Metadata/Attribute/tests/TestWithTest.php index 1dc18374534..a91a62f9fec 100644 --- a/tests/_files/Metadata/Attribute/tests/TestWithTest.php +++ b/tests/_files/Metadata/Attribute/tests/TestWithTest.php @@ -21,9 +21,21 @@ public function testOne(): void $this->assertTrue(true); } + #[TestWith([1, 2, 3], 'Name1')] + public function testOneWithName(): void + { + $this->assertTrue(true); + } + #[TestWithJson('[1, 2, 3]')] public function testTwo(): void { $this->assertTrue(true); } + + #[TestWithJson('[1, 2, 3]', 'Name2')] + public function testTwoWithName(): void + { + $this->assertTrue(true); + } } diff --git a/tests/_files/Metadata/Attribute/tests/UsesTest.php b/tests/_files/Metadata/Attribute/tests/UsesTest.php index ce733fce306..0333fdb525d 100644 --- a/tests/_files/Metadata/Attribute/tests/UsesTest.php +++ b/tests/_files/Metadata/Attribute/tests/UsesTest.php @@ -10,11 +10,24 @@ namespace PHPUnit\TestFixture\Metadata\Attribute; use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\Attributes\UsesClassesThatExtendClass; +use PHPUnit\Framework\Attributes\UsesClassesThatImplementInterface; use PHPUnit\Framework\Attributes\UsesFunction; +use PHPUnit\Framework\Attributes\UsesMethod; +use PHPUnit\Framework\Attributes\UsesNamespace; +use PHPUnit\Framework\Attributes\UsesTrait; use PHPUnit\Framework\TestCase; +#[UsesNamespace('PHPUnit\TestFixture\Metadata\Attribute')] #[UsesClass(Example::class)] +#[UsesClassesThatExtendClass(Example::class)] +#[UsesClassesThatImplementInterface(Example::class)] +#[UsesTrait(ExampleTrait::class)] +#[UsesMethod(Example::class, 'method')] #[UsesFunction('f')] final class UsesTest extends TestCase { + public function testOne(): void + { + } } diff --git a/tests/_files/Metadata/Attribute/tests/WithEnvironmentVariableTest.php b/tests/_files/Metadata/Attribute/tests/WithEnvironmentVariableTest.php new file mode 100644 index 00000000000..1624ff1419d --- /dev/null +++ b/tests/_files/Metadata/Attribute/tests/WithEnvironmentVariableTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Metadata\Attribute; + +use PHPUnit\Framework\Attributes\WithEnvironmentVariable; +use PHPUnit\Framework\TestCase; + +#[WithEnvironmentVariable('foo', 'bar')] +final class WithEnvironmentVariableTest extends TestCase +{ + #[WithEnvironmentVariable('foo')] + #[WithEnvironmentVariable('bar', 'baz')] + public function testOne(): void + { + } +} diff --git a/tests/_files/MoreThanOneCoversDefaultClassAnnotationTest.php b/tests/_files/MoreThanOneCoversDefaultClassAnnotationTest.php deleted file mode 100644 index 44365046327..00000000000 --- a/tests/_files/MoreThanOneCoversDefaultClassAnnotationTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @coversDefaultClass Foo - * @coversDefaultClass Bar - */ -final class MoreThanOneCoversDefaultClassAnnotationTest extends TestCase -{ - public function testOne(): void - { - } -} diff --git a/tests/_files/MoreThanOneUsesDefaultClassAnnotationTest.php b/tests/_files/MoreThanOneUsesDefaultClassAnnotationTest.php deleted file mode 100644 index 134cc5e86f2..00000000000 --- a/tests/_files/MoreThanOneUsesDefaultClassAnnotationTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @usesDefaultClass Foo - * @usesDefaultClass Bar - */ -final class MoreThanOneUsesDefaultClassAnnotationTest extends TestCase -{ - public function testOne(): void - { - } -} diff --git a/tests/end-to-end/execution-order/_files/MultiDependencyTest.php b/tests/_files/MultiDependencyTest.php similarity index 81% rename from tests/end-to-end/execution-order/_files/MultiDependencyTest.php rename to tests/_files/MultiDependencyTest.php index 7965e849e38..c6b7e207592 100644 --- a/tests/end-to-end/execution-order/_files/MultiDependencyTest.php +++ b/tests/_files/MultiDependencyTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\TestCase; class MultiDependencyTest extends TestCase @@ -27,19 +29,15 @@ public function testTwo() return 'bar'; } - /** - * @depends testOne - * @depends testTwo - */ + #[Depends('testOne')] + #[Depends('testTwo')] public function testThree($a, $b): void { $this->assertEquals('foo', $a); $this->assertEquals('bar', $b); } - /** - * @depends PHPUnit\TestFixture\MultiDependencyTest::testThree - */ + #[DependsExternal(self::class, 'testThree')] public function testFour(): void { $this->assertTrue(true); diff --git a/tests/_files/MultipleDataProviderTest.php b/tests/_files/MultipleDataProviderTest.php index 518ca1f2308..9add02a3156 100644 --- a/tests/_files/MultipleDataProviderTest.php +++ b/tests/_files/MultipleDataProviderTest.php @@ -9,12 +9,16 @@ */ namespace PHPUnit\TestFixture; +use ArrayIterator; use ArrayObject; +use Generator; +use Iterator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -class MultipleDataProviderTest extends TestCase +final class MultipleDataProviderTest extends TestCase { - public static function providerA() + public static function providerA(): array { return [ ['ok', null, null], @@ -23,7 +27,7 @@ public static function providerA() ]; } - public static function providerB() + public static function providerB(): array { return [ [null, 'ok', null], @@ -32,7 +36,7 @@ public static function providerB() ]; } - public static function providerC() + public static function providerC(): array { return [ [null, null, 'ok'], @@ -41,7 +45,7 @@ public static function providerC() ]; } - public static function providerD() + public static function providerD(): Generator { yield ['ok', null, null]; @@ -50,7 +54,7 @@ public static function providerD() yield ['ok', null, null]; } - public static function providerE() + public static function providerE(): Generator { yield [null, 'ok', null]; @@ -59,7 +63,7 @@ public static function providerE() yield [null, 'ok', null]; } - public static function providerF() + public static function providerF(): ArrayIterator|Iterator { $object = new ArrayObject( [ @@ -72,20 +76,16 @@ public static function providerF() return $object->getIterator(); } - /** - * @dataProvider providerA - * @dataProvider providerB - * @dataProvider providerC - */ + #[DataProvider('providerA')] + #[DataProvider('providerB')] + #[DataProvider('providerC')] public function testOne(): void { } - /** - * @dataProvider providerD - * @dataProvider providerE - * @dataProvider providerF - */ + #[DataProvider('providerD')] + #[DataProvider('providerE')] + #[DataProvider('providerF')] public function testTwo(): void { } diff --git a/tests/_files/NamedConstraint.php b/tests/_files/NamedConstraint.php deleted file mode 100644 index 685da03e7ce..00000000000 --- a/tests/_files/NamedConstraint.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\Constraint\Constraint; - -final class NamedConstraint extends Constraint -{ - /** - * @var int - */ - private $name; - - public static function fromName(string $name): self - { - $instance = new self; - - $instance->name = $name; - - return $instance; - } - - public function matches(mixed $other): bool - { - return true; - } - - public function toString(bool $exportObjects = false): string - { - return $this->name; - } -} diff --git a/tests/_files/NamespaceCoverageClassTest.php b/tests/_files/NamespaceCoverageClassTest.php deleted file mode 100644 index a92d2fdb3a9..00000000000 --- a/tests/_files/NamespaceCoverageClassTest.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -class NamespaceCoverageClassTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass - * - * @uses \PHPUnit\TestFixture\CoveredClass - */ - public function testSomething(): void - { - $o = new CoveredClass; - $o->publicMethod(); - } -} diff --git a/tests/_files/NamespaceCoverageCoversClassPublicTest.php b/tests/_files/NamespaceCoverageCoversClassPublicTest.php deleted file mode 100644 index 29d03cf4478..00000000000 --- a/tests/_files/NamespaceCoverageCoversClassPublicTest.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @coversDefaultClass \PHPUnit\TestFixture\CoveredClass - * - * @usesDefaultClass \PHPUnit\TestFixture\CoveredClass - */ -class NamespaceCoverageCoversClassPublicTest extends TestCase -{ - /** - * @covers ::publicMethod - * - * @uses ::publicMethod - */ - public function testSomething(): void - { - $o = new CoveredClass; - $o->publicMethod(); - } -} diff --git a/tests/_files/NamespaceCoverageCoversClassTest.php b/tests/_files/NamespaceCoverageCoversClassTest.php deleted file mode 100644 index 5ef33b3e43f..00000000000 --- a/tests/_files/NamespaceCoverageCoversClassTest.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -/** - * @coversDefaultClass \PHPUnit\TestFixture\CoveredClass - * - * @usesDefaultClass \PHPUnit\TestFixture\CoveredClass - */ -class NamespaceCoverageCoversClassTest extends TestCase -{ - /** - * @covers ::privateMethod - * @covers ::protectedMethod - * @covers ::publicMethod - * @covers \PHPUnit\TestFixture\CoveredParentClass::privateMethod - * @covers \PHPUnit\TestFixture\CoveredParentClass::protectedMethod - * @covers \PHPUnit\TestFixture\CoveredParentClass::publicMethod - * - * @uses ::privateMethod - * @uses ::protectedMethod - * @uses ::publicMethod - * @uses \PHPUnit\TestFixture\CoveredParentClass::privateMethod - * @uses \PHPUnit\TestFixture\CoveredParentClass::protectedMethod - * @uses \PHPUnit\TestFixture\CoveredParentClass::publicMethod - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/NamespaceCoverageMethodTest.php b/tests/_files/NamespaceCoverageMethodTest.php deleted file mode 100644 index 5b1255adbc1..00000000000 --- a/tests/_files/NamespaceCoverageMethodTest.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use CoveredClass; -use PHPUnit\Framework\TestCase; - -class NamespaceCoverageMethodTest extends TestCase -{ - /** - * @covers \PHPUnit\TestFixture\CoveredClass::publicMethod - * - * @uses \PHPUnit\TestFixture\CoveredClass::publicMethod - */ - public function testSomething(): void - { - $o = new CoveredClass; - - $o->publicMethod(); - } -} diff --git a/tests/_files/NoArgTestCaseTest.php b/tests/_files/NoArgTestCaseTest.php index 277376b582c..95d914f4b4d 100644 --- a/tests/_files/NoArgTestCaseTest.php +++ b/tests/_files/NoArgTestCaseTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class NoArgTestCaseTest extends TestCase +final class NoArgTestCaseTest extends TestCase { public function testNothing(): void { diff --git a/tests/_files/NoCoverageAttributesTest.php b/tests/_files/NoCoverageAttributesTest.php new file mode 100644 index 00000000000..422f56e7e80 --- /dev/null +++ b/tests/_files/NoCoverageAttributesTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\TestCase; + +final class NoCoverageAttributesTest extends TestCase +{ + public function testSomething(): void + { + $o = new CoveredClass; + + $o->publicMethod(); + } +} diff --git a/tests/_files/NoTestCase.php b/tests/_files/NoTestCase.php new file mode 100644 index 00000000000..83498f1dcaa --- /dev/null +++ b/tests/_files/NoTestCase.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +final class NoTestCase +{ +} diff --git a/tests/_files/NoTestCases.php b/tests/_files/NoTestCases.php index c434e83ff55..d885f95d373 100644 --- a/tests/_files/NoTestCases.php +++ b/tests/_files/NoTestCases.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class NoTestCases extends TestCase +final class NoTestCases extends TestCase { public function noTestCase(): void { diff --git a/tests/_files/NotPublicTestCase.php b/tests/_files/NotPublicTestCase.php index d24ba8f93f8..651118f9263 100644 --- a/tests/_files/NotPublicTestCase.php +++ b/tests/_files/NotPublicTestCase.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class NotPublicTestCase extends TestCase +final class NotPublicTestCase extends TestCase { public function testPublic(): void { diff --git a/tests/_files/NothingTest.php b/tests/_files/NothingTest.php index 0e0619de279..3c37b9e3572 100644 --- a/tests/_files/NothingTest.php +++ b/tests/_files/NothingTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class NothingTest extends TestCase +final class NothingTest extends TestCase { public function testNothing(): void { diff --git a/tests/_files/NumericGroupAnnotationTest.php b/tests/_files/NumericGroupAnnotationTest.php deleted file mode 100644 index eb485ff6934..00000000000 --- a/tests/_files/NumericGroupAnnotationTest.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -/** - * @ticket t123456 - */ -class NumericGroupAnnotationTest extends \PHPUnit\Framework\TestCase -{ - /** - * @testdox Empty test for @ticket numeric annotation values - * - * @ticket 3502 - * - * @see https://github.com/sebastianbergmann/phpunit/issues/3502 - */ - public function testTicketAnnotationSupportsNumericValue(): void - { - $this->assertTrue(true); - } - - /** - * @testdox Empty test for @group numeric annotation values - * - * @group 3502 - * - * @see https://github.com/sebastianbergmann/phpunit/issues/3502 - */ - public function testGroupAnnotationSupportsNumericValue(): void - { - $this->assertTrue(true); - } - - public function testDummyTestThatShouldNotRun(): void - { - $this->doesNotPerformAssertions(); - } -} diff --git a/tests/_files/OutputTestCase.php b/tests/_files/OutputTestCase.php index e7e0b07e4ed..e58f8084cd1 100644 --- a/tests/_files/OutputTestCase.php +++ b/tests/_files/OutputTestCase.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class OutputTestCase extends TestCase +final class OutputTestCase extends TestCase { public function testExpectOutputStringFooActualFoo(): void { diff --git a/tests/_files/RecordingSubscriber.php b/tests/_files/RecordingSubscriber.php index 8e8705fe5c1..dbb348297db 100644 --- a/tests/_files/RecordingSubscriber.php +++ b/tests/_files/RecordingSubscriber.php @@ -1,6 +1,4 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use Exception; -use PHPUnit\Framework\TestCase; - -/** - * @requires extension nonExistingExtension - */ -class RequirementsClassBeforeClassHookTest extends TestCase -{ - public static function setUpBeforeClass(): void - { - throw new Exception(__METHOD__ . ' should not be called because of class requirements.'); - } -} diff --git a/tests/_files/RequirementsClassDocBlockTest.php b/tests/_files/RequirementsClassDocBlockTest.php deleted file mode 100644 index 78f082025c2..00000000000 --- a/tests/_files/RequirementsClassDocBlockTest.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -/** - * @requires PHP 5.3 - * @requires PHPUnit 4.0 - * @requires OS Linux - * @requires function testFuncClass - * @requires extension testExtClass - */ -class RequirementsClassDocBlockTest -{ - /** - * @requires PHP 5.4 - * @requires PHPUnit 3.7 - * @requires OS WINNT - * @requires function testFuncMethod - * @requires extension testExtMethod - */ - public function testMethod(): void - { - } -} diff --git a/tests/_files/RequirementsEnvironmentVariableTest.php b/tests/_files/RequirementsEnvironmentVariableTest.php new file mode 100644 index 00000000000..dac8f0249ce --- /dev/null +++ b/tests/_files/RequirementsEnvironmentVariableTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable; +use PHPUnit\Framework\TestCase; + +final class RequirementsEnvironmentVariableTest extends TestCase +{ + #[RequiresEnvironmentVariable('FOO', 'bar')] + #[RequiresEnvironmentVariable('BAR')] + #[RequiresEnvironmentVariable('BAZ')] + public function testRequiresEnvironmentVariable(): void + { + } +} diff --git a/tests/_files/RequirementsTest.php b/tests/_files/RequirementsTest.php index 623120b0f28..219fb20312f 100644 --- a/tests/_files/RequirementsTest.php +++ b/tests/_files/RequirementsTest.php @@ -9,484 +9,365 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\RequiresFunction; +use PHPUnit\Framework\Attributes\RequiresMethod; +use PHPUnit\Framework\Attributes\RequiresOperatingSystem; +use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; +use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RequiresPhpunitExtension; +use PHPUnit\Framework\Attributes\RequiresSetting; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\TestCase; +use ReflectionMethod; -class RequirementsTest extends TestCase +final class RequirementsTest extends TestCase { public function testOne(): void { } - /** - * @requires PHPUnit 1.0 - */ + #[RequiresPhpunit('1.0')] public function testTwo(): void { } - /** - * @requires PHP 2.0 - */ + #[RequiresPhp('2.0')] public function testThree(): void { } - /** - * @requires PHPUnit 2.0 - * @requires PHP 1.0 - */ + #[RequiresPhpunit('2.0')] + #[RequiresPhp('1.0')] public function testFour(): void { } - /** - * @requires PHP 5.4.0RC6 - */ + #[RequiresPhp('5.4.0RC6')] public function testFive(): void { } - /** - * @requires PHP 5.4.0-alpha1 - */ + #[RequiresPhp('5.4.0-alpha1')] public function testSix(): void { } - /** - * @requires PHP 5.4.0beta2 - */ + #[RequiresPhp('5.4.0beta2')] public function testSeven(): void { } - /** - * @requires PHP 5.4-dev - */ + #[RequiresPhp('5.4-dev')] public function testEight(): void { } - /** - * @requires function testFunc - */ + #[RequiresFunction('testFunc')] public function testNine(): void { } - /** - * @requires function testFunc2 - * - * @see https://github.com/sebastianbergmann/phpunit/issues/3459 - */ + #[RequiresFunction('testFunc2')] + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/3459')] public function testRequiresFunctionWithDigit(): void { } - /** - * @requires extension testExt - */ + #[RequiresPhpExtension('testExt')] public function testTen(): void { } - /** - * @requires OS SunOS - * @requires OSFAMILY Solaris - */ + #[RequiresOperatingSystem('SunOS')] + #[RequiresOperatingSystemFamily('Solaris')] public function testEleven(): void { } - /** - * @requires PHP 99-dev - * @requires PHPUnit 99-dev - * @requires OS DOESNOTEXIST - * @requires OSFAMILY DOESNOTEXIST - * @requires function testFuncOne - * @requires function testFunc2 - * @requires function DoesNotExist::doesNotExist - * @requires extension testExtOne - * @requires extension testExt2 - * @requires extension testExtThree 2.0 - * @requires setting not_a_setting Off - */ + #[RequiresPhp('99-dev')] + #[RequiresPhpunit('99-dev')] + #[RequiresOperatingSystem('DOESNOTEXIST')] + #[RequiresOperatingSystemFamily('DOESNOTEXIST')] + #[RequiresFunction('testFuncOne')] + #[RequiresFunction('testFunc2')] + #[RequiresMethod('DoesNotExist', 'doesNotExist')] + #[RequiresPhpExtension('testExtOne')] + #[RequiresPhpExtension('testExt2')] + #[RequiresPhpExtension('testExtThree', '2.0')] + #[RequiresSetting('not_a_setting', 'Off')] public function testAllPossibleRequirements(): void { } - /** - * @requires function array_merge - */ + #[RequiresFunction('array_merge')] public function testExistingFunction(): void { } - /** - * @requires function ReflectionMethod::setAccessible - */ + #[RequiresMethod(ReflectionMethod::class, 'setAccessible')] public function testExistingMethod(): void { } - /** - * @requires extension spl - */ + #[RequiresPhpExtension('spl')] public function testExistingExtension(): void { } - /** - * @requires OS .* - */ + #[RequiresOperatingSystem('.*')] public function testExistingOs(): void { } - /** - * @requires PHPUnit 1111111 - */ + #[RequiresPhpunit('1111111')] public function testAlwaysSkip(): void { } - /** - * @requires PHP 9999999 - */ + #[RequiresPhp('9999999')] public function testAlwaysSkip2(): void { } - /** - * @requires OS DOESNOTEXIST - */ + #[RequiresOperatingSystem('DOESNOTEXIST')] public function testAlwaysSkip3(): void { } - /** - * @requires OSFAMILY DOESNOTEXIST - */ + #[RequiresOperatingSystemFamily('DOESNOTEXIST')] public function testAlwaysSkip4(): void { } - /** - * @requires extension spl - * @requires OS .* - */ + #[RequiresPhpExtension('spl')] + #[RequiresOperatingSystem('.*')] public function testSpace(): void { } - /** - * @requires extension testExt 1.8.0 - */ + #[RequiresPhpExtension('testExt', '1.8.0')] public function testSpecificExtensionVersion(): void { } - /** - * @testdox PHP version operator less than - * - * @requires PHP < 5.4 - */ + #[TestDox('PHP version operator less than')] + #[RequiresPhp('< 5.4')] public function testPHPVersionOperatorLessThan(): void { } - /** - * @testdox PHP version operator less than or equals - * - * @requires PHP <= 5.4 - */ + #[TestDox('PHP version operator less than or equals')] + #[RequiresPhp('<= 5.4')] public function testPHPVersionOperatorLessThanEquals(): void { } - /** - * @testdox PHP version operator greater than - * - * @requires PHP > 99 - */ + #[TestDox('PHP version operator greater than')] + #[RequiresPhp('> 99')] public function testPHPVersionOperatorGreaterThan(): void { } - /** - * @testdox PHP version operator greater than or equals - * - * @requires PHP >= 99 - */ + #[TestDox('PHP version operator greater than or equals')] + #[RequiresPhp('>= 99')] public function testPHPVersionOperatorGreaterThanEquals(): void { } - /** - * @testdox PHP version operator equals - * - * @requires PHP = 5.4 - */ + #[TestDox('PHP version operator equals')] + #[RequiresPhp('= 5.4')] public function testPHPVersionOperatorEquals(): void { } - /** - * @testdox PHP version operator double equals - * - * @requires PHP == 5.4 - */ + #[TestDox('PHP version operator double equals')] + #[RequiresPhp('== 5.4')] public function testPHPVersionOperatorDoubleEquals(): void { } - /** - * @testdox PHP version operator bang equals - * - * @requires PHP != 99 - */ + #[TestDox('PHP version operator bang equals')] + #[RequiresPhp('!= 99')] public function testPHPVersionOperatorBangEquals(): void { } - /** - * @testdox PHP version operator not equals - * - * @requires PHP <> 99 - */ + #[TestDox('PHP version operator not equals')] + #[RequiresPhp('<> 99')] public function testPHPVersionOperatorNotEquals(): void { } - /** - * @testdox PHP version operator no space - * - * @requires PHP >=99 - */ + #[TestDox('PHP version operator no space')] + #[RequiresPhp('>=99')] public function testPHPVersionOperatorNoSpace(): void { } - /** - * @testdox PHPUnit version operator less than - * - * @requires PHPUnit < 1.0 - */ + #[TestDox('PHPUnit version operator less than')] + #[RequiresPhpunit('< 1.0')] public function testPHPUnitVersionOperatorLessThan(): void { } - /** - * @testdox PHPUnit version operator less than equals - * - * @requires PHPUnit <= 1.0 - */ + #[TestDox('PHPUnit version operator less than equals')] + #[RequiresPhpunit('<= 1.0')] public function testPHPUnitVersionOperatorLessThanEquals(): void { } - /** - * @testdox PHPUnit version operator greater than - * - * @requires PHPUnit > 99 - */ + #[TestDox('PHPUnit version operator greater than')] + #[RequiresPhpunit('> 99')] public function testPHPUnitVersionOperatorGreaterThan(): void { } - /** - * @testdox PHPUnit version operator greater than or equals - * - * @requires PHPUnit >= 99 - */ + #[TestDox('PHPUnit version operator greater than or equals')] + #[RequiresPhpunit('>= 99')] public function testPHPUnitVersionOperatorGreaterThanEquals(): void { } - /** - * @testdox PHPUnit version operator equals - * - * @requires PHPUnit = 1.0 - */ + #[TestDox('PHPUnit version operator equals')] + #[RequiresPhpunit('= 1.0')] public function testPHPUnitVersionOperatorEquals(): void { } - /** - * @testdox PHPUnit version operator double equals - * - * @requires PHPUnit == 1.0 - */ + #[TestDox('PHPUnit version operator double equals')] + #[RequiresPhpunit('== 1.0')] public function testPHPUnitVersionOperatorDoubleEquals(): void { } - /** - * @testdox PHPUnit version operator bang equals - * - * @requires PHPUnit != 99 - */ + #[TestDox('PHPUnit version operator bang equals')] + #[RequiresPhpunit('!= 99')] public function testPHPUnitVersionOperatorBangEquals(): void { } - /** - * @testdox PHPUnit version operator not equals - * - * @requires PHPUnit <> 99 - */ + #[TestDox('PHPUnit version operator not equals')] + #[RequiresPhpunit('<> 99')] public function testPHPUnitVersionOperatorNotEquals(): void { } - /** - * @testdox PHPUnit version operator no space - * - * @requires PHPUnit >=99 - */ + #[TestDox('PHPUnit version operator no space')] + #[RequiresPhpunit('>=99')] public function testPHPUnitVersionOperatorNoSpace(): void { } - /** - * @requires extension testExtOne < 1.0 - */ + #[RequiresPhpExtension('testExtOne', '< 1.0')] public function testExtensionVersionOperatorLessThan(): void { } - /** - * @requires extension testExtOne <= 1.0 - */ + #[RequiresPhpExtension('testExtOne', '<= 1.0')] public function testExtensionVersionOperatorLessThanEquals(): void { } - /** - * @requires extension testExtOne > 99 - */ + #[RequiresPhpExtension('testExtOne', '> 99')] public function testExtensionVersionOperatorGreaterThan(): void { } - /** - * @requires extension testExtOne >= 99 - */ + #[RequiresPhpExtension('testExtOne', '>= 99')] public function testExtensionVersionOperatorGreaterThanEquals(): void { } - /** - * @requires extension testExtOne = 1.0 - */ + #[RequiresPhpExtension('testExtOne', '= 1.0')] public function testExtensionVersionOperatorEquals(): void { } - /** - * @requires extension testExtOne == 1.0 - */ + #[RequiresPhpExtension('testExtOne', '== 1.0')] public function testExtensionVersionOperatorDoubleEquals(): void { } - /** - * @requires extension testExtOne != 99 - */ + #[RequiresPhpExtension('testExtOne', '!= 99')] public function testExtensionVersionOperatorBangEquals(): void { } - /** - * @requires extension testExtOne <> 99 - */ + #[RequiresPhpExtension('testExtOne', '<> 99')] public function testExtensionVersionOperatorNotEquals(): void { } - /** - * @requires extension testExtOne >=99 - */ + #[RequiresPhpExtension('testExtOne', '>= 99')] public function testExtensionVersionOperatorNoSpace(): void { } - /** - * @requires PHP ~1.0 - * @requires PHPUnit ~2.0 - */ + #[RequiresPhp('~1.0')] + #[RequiresPhpunit('~2.0')] public function testVersionConstraintTildeMajor(): void { } - /** - * @requires PHP ^1.0 - * @requires PHPUnit ^2.0 - */ + #[RequiresPhp('^1.0')] + #[RequiresPhpunit('^2.0')] public function testVersionConstraintCaretMajor(): void { } - /** - * @requires PHP ~3.4.7 - * @requires PHPUnit ~4.7.1 - */ + #[RequiresPhp('~3.4.7')] + #[RequiresPhpunit('~4.7.1')] public function testVersionConstraintTildeMinor(): void { } - /** - * @requires PHP ^7.0.17 - * @requires PHPUnit ^4.7.1 - */ + #[RequiresPhp('^7.0.17')] + #[RequiresPhpunit('^4.7.1')] public function testVersionConstraintCaretMinor(): void { } - /** - * @requires PHP ^5.6 || ^7.0 - * @requires PHPUnit ^5.0 || ^6.0 - */ + #[RequiresPhp('^5.6 || ^7.0')] + #[RequiresPhpunit('^5.0 || ^6.0')] public function testVersionConstraintCaretOr(): void { } - /** - * @requires PHP ~5.6.22 || ~7.0.17 - * @requires PHPUnit ^5.0.5 || ^6.0.6 - */ + #[RequiresPhp('~5.6.22 || ~7.0.17')] + #[RequiresPhpunit('~5.0.5 || ~6.0.6')] public function testVersionConstraintTildeOr(): void { } - /** - * @requires PHP ~5.6.22 || ^7.0 - * @requires PHPUnit ~5.6.22 || ^7.0 - */ + #[RequiresPhp('~5.6.22 || ^7.0')] + #[RequiresPhpunit('~5.6.22 || ^7.0')] public function testVersionConstraintTildeOrCaret(): void { } - /** - * @requires PHP ^5.6 || ~7.0.17 - * @requires PHPUnit ^5.6 || ~7.0.17 - */ + #[RequiresPhp('^5.6 || ~7.0.17')] + #[RequiresPhpunit('^5.6 || ~7.0.17')] public function testVersionConstraintCaretOrTilde(): void { } - /** - * @requires PHP ~5.6.22 || ~7.0.17 - * @requires PHPUnit ~5.6.22 || ~7.0.17 - */ + #[RequiresPhp('~5.6.22 || ~7.0.17')] + #[RequiresPhpunit('~5.6.22 || ~7.0.17')] public function testVersionConstraintRegexpIgnoresWhitespace(): void { } - /** - * @requires setting display_errors On - */ + #[RequiresSetting('display_errors', 'On')] public function testSettingDisplayErrorsOn(): void { } + + #[RequiresPhpunitExtension(SomeExtension::class)] + #[RequiresPhpunitExtension(SomeOtherExtension::class)] + public function testPHPUnitExtensionRequired(): void + { + } } diff --git a/tests/_files/RouterTest.php b/tests/_files/RouterTest.php index e8cc9fc517a..3caffc07ca8 100644 --- a/tests/_files/RouterTest.php +++ b/tests/_files/RouterTest.php @@ -10,21 +10,13 @@ namespace PHPUnit\TestFixture; use FooBarHandler; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; final class RouterTest extends TestCase { - /** - * @dataProvider routesProvider - * - * @testdox Routes $url to $handler - */ - public function testRoutesRequest(string $url, string $handler): void - { - $this->assertTrue(true); - } - - public function routesProvider() + public static function routesProvider(): array { return [ '/foo/bar' => [ @@ -34,4 +26,11 @@ public function routesProvider() ], ]; } + + #[DataProvider('routesProvider')] + #[TestDox('Routes $url to $handler')] + public function testRoutesRequest(string $url, string $handler): void + { + $this->assertTrue(true); + } } diff --git a/tests/_files/SampleArrayAccess.php b/tests/_files/SampleArrayAccess.php index 7b7987b6230..39dccb3f7e1 100644 --- a/tests/_files/SampleArrayAccess.php +++ b/tests/_files/SampleArrayAccess.php @@ -11,7 +11,7 @@ use ArrayAccess; -class SampleArrayAccess implements ArrayAccess +final class SampleArrayAccess implements ArrayAccess { private array $container; diff --git a/tests/_files/SampleClass.php b/tests/_files/SampleClass.php index 6ea8a93ba76..2c3592190ff 100644 --- a/tests/_files/SampleClass.php +++ b/tests/_files/SampleClass.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class SampleClass +final class SampleClass { public $a; public $b; diff --git a/tests/_files/SeparateProcessesTest.php b/tests/_files/SeparateProcessesTest.php index db62995ddb8..910a1b6a147 100644 --- a/tests/_files/SeparateProcessesTest.php +++ b/tests/_files/SeparateProcessesTest.php @@ -9,12 +9,11 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\TestCase; -/** - * @runTestsInSeparateProcesses - */ -class SeparateProcessesTest extends TestCase +#[RunTestsInSeparateProcesses] +final class SeparateProcessesTest extends TestCase { public function testFoo(): void { diff --git a/tests/_files/Singleton.php b/tests/_files/Singleton.php deleted file mode 100644 index 35b6601e79b..00000000000 --- a/tests/_files/Singleton.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -class Singleton -{ - private static $uniqueInstance; - - public static function getInstance() - { - if (self::$uniqueInstance === null) { - self::$uniqueInstance = new self; - } - - return self::$uniqueInstance; - } - - protected function __construct() - { - } - - private function __clone() - { - } -} diff --git a/tests/_files/SmallGroupAnnotationsTest.php b/tests/_files/SmallGroupAnnotationsTest.php index 002244be9b7..ac32ff9b314 100644 --- a/tests/_files/SmallGroupAnnotationsTest.php +++ b/tests/_files/SmallGroupAnnotationsTest.php @@ -9,26 +9,22 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; -/** - * @covers \PHPUnit\TestFixture\CoveredClass - * - * @uses \PHPUnit\TestFixture\CoveredClass - * - * @group the-group - * - * @ticket the-ticket - * - * @small - */ +#[CoversClass(CoveredClass::class)] +#[UsesClass(CoveredClass::class)] +#[Group('the-group')] +#[Ticket('the-ticket')] +#[Small] final class SmallGroupAnnotationsTest extends TestCase { - /** - * @group another-group - * - * @ticket another-ticket - */ + #[Group('another-group')] + #[Ticket('another-ticket')] public function testOne(): void { } diff --git a/tests/_files/StopOnErrorTestSuite.php b/tests/_files/StopOnErrorTestSuite.php index 1cfb8473e92..f40e0535b4a 100644 --- a/tests/_files/StopOnErrorTestSuite.php +++ b/tests/_files/StopOnErrorTestSuite.php @@ -12,7 +12,7 @@ use Error; use PHPUnit\Framework\TestCase; -class StopOnErrorTestSuite extends TestCase +final class StopOnErrorTestSuite extends TestCase { public function testIncomplete(): void { diff --git a/tests/_files/StopsOnWarningTest.php b/tests/_files/StopsOnWarningTest.php index 7d66d28265b..e4d6a24077f 100644 --- a/tests/_files/StopsOnWarningTest.php +++ b/tests/_files/StopsOnWarningTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class StopsOnWarningTest extends TestCase +final class StopsOnWarningTest extends TestCase { public function testOne(): void { diff --git a/tests/_files/Struct.php b/tests/_files/Struct.php index b0f45158b48..aa831b919d3 100644 --- a/tests/_files/Struct.php +++ b/tests/_files/Struct.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class Struct +final class Struct { public $var; diff --git a/tests/_files/TemplateMethodsTest.php b/tests/_files/TemplateMethodsTest.php index feda0d8220a..5a55140bcb8 100644 --- a/tests/_files/TemplateMethodsTest.php +++ b/tests/_files/TemplateMethodsTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\TestCase; use Throwable; -class TemplateMethodsTest extends TestCase +final class TemplateMethodsTest extends TestCase { public static function setUpBeforeClass(): void { diff --git a/tests/_files/TestCaseTest.php b/tests/_files/TestCaseTest.php index 7ab963c3ada..adcb7bb84a5 100644 --- a/tests/_files/TestCaseTest.php +++ b/tests/_files/TestCaseTest.php @@ -23,18 +23,11 @@ public function two(): void { } - /** - * @test - */ public function three(): void { } - public function four(): void - { - } - - private function five(): void + private function four(): void { } } diff --git a/tests/_files/TestDoxAttributeOnTestClassTest.php b/tests/_files/TestDoxAttributeOnTestClassTest.php new file mode 100644 index 00000000000..c80a893998f --- /dev/null +++ b/tests/_files/TestDoxAttributeOnTestClassTest.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestDox; + +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[TestDox('Custom Title')] +final class TestDoxAttributeOnTestClassTest extends TestCase +{ +} diff --git a/tests/_files/TestGeneratorMaker.php b/tests/_files/TestGeneratorMaker.php index f6b89818a37..1fbf8a4a361 100644 --- a/tests/_files/TestGeneratorMaker.php +++ b/tests/_files/TestGeneratorMaker.php @@ -9,7 +9,7 @@ */ namespace PHPUnit\TestFixture; -class TestGeneratorMaker +final class TestGeneratorMaker { public function create($array = []) { diff --git a/tests/_files/TestWithAttributeDataProviderTest.php b/tests/_files/TestWithAttributeDataProviderTest.php new file mode 100644 index 00000000000..64c7fd4c0cf --- /dev/null +++ b/tests/_files/TestWithAttributeDataProviderTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; + +final class TestWithAttributeDataProviderTest extends TestCase +{ + #[TestWith(['a', 'b'], 'foo')] + #[TestWith(['c', 'd'], 'bar')] + #[TestWith(['e', 'f'])] + #[TestWith(['g', 'h'])] + public function testWithAttribute($one, $two): void + { + } + + #[TestWith(['a', 'b'], 'foo')] + #[TestWith(['c', 'd'], 'foo')] + public function testWithDuplicateName($one, $two): void + { + } +} diff --git a/tests/_files/TestWithDifferentSizes.php b/tests/_files/TestWithDifferentSizes.php deleted file mode 100644 index 31ff76389ab..00000000000 --- a/tests/_files/TestWithDifferentSizes.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Framework\TestCase; - -final class TestWithDifferentSizes extends TestCase -{ - public static function provider(): array - { - return [ - [false], - [true], - ]; - } - - public function testWithSizeUnknown(): void - { - $this->assertTrue(true); - } - - /** - * @large - */ - public function testWithSizeLarge(): void - { - $this->assertTrue(true); - } - - /** - * @depends testDataProviderWithSizeMedium - * - * @medium - */ - public function testWithSizeMedium(): void - { - $this->assertTrue(true); - } - - /** - * @depends testWithSizeLarge - * - * @small - */ - public function testWithSizeSmall(): void - { - $this->assertTrue(true); - } - - /** - * @dataProvider provider - * - * @small - */ - public function testDataProviderWithSizeSmall(bool $value): void - { - $this->assertTrue(true); - } - - /** - * @dataProvider provider - * - * @medium - */ - public function testDataProviderWithSizeMedium(bool $value): void - { - $this->assertTrue(true); - } -} diff --git a/tests/_files/TestWithHookMethodsPrioritizedTest.php b/tests/_files/TestWithHookMethodsPrioritizedTest.php new file mode 100644 index 00000000000..96b0afe9c24 --- /dev/null +++ b/tests/_files/TestWithHookMethodsPrioritizedTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\AfterClass; +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\Attributes\BeforeClass; +use PHPUnit\Framework\Attributes\PostCondition; +use PHPUnit\Framework\Attributes\PreCondition; +use PHPUnit\Framework\TestCase; + +final class TestWithHookMethodsPrioritizedTest extends TestCase +{ + #[BeforeClass(priority: 1)] + public static function beforeFirstTest(): void + { + } + + #[AfterClass(priority: 6)] + public static function afterLastTest(): void + { + } + + #[Before(priority: 2)] + protected function beforeEachTest(): void + { + } + + #[PreCondition(priority: 3)] + protected function preConditions(): void + { + } + + #[PostCondition(priority: 4)] + protected function postConditions(): void + { + } + + #[After(priority: 5)] + protected function afterEachTest(): void + { + } +} diff --git a/tests/_files/TestWithHookMethodsTest.php b/tests/_files/TestWithHookMethodsTest.php index becbb85bebc..d7f15a8548a 100644 --- a/tests/_files/TestWithHookMethodsTest.php +++ b/tests/_files/TestWithHookMethodsTest.php @@ -29,20 +29,6 @@ public static function afterLastTestWithAttribute(): void { } - /** - * @beforeClass - */ - public static function beforeFirstTestWithAnnotation(): void - { - } - - /** - * @afterClass - */ - public static function afterLastTestWithAnnotation(): void - { - } - #[Before] protected function beforeEachTestWithAttribute(): void { @@ -62,32 +48,4 @@ protected function preConditionsWithAttribute(): void protected function postConditionsWithAttribute(): void { } - - /** - * @before - */ - protected function beforeEachTestWithAnnotation(): void - { - } - - /** - * @after - */ - protected function afterEachTestWithAnnotation(): void - { - } - - /** - * @preCondition - */ - protected function preConditionsWithAnnotation(): void - { - } - - /** - * @postCondition - */ - protected function postConditionsWithAnnotation(): void - { - } } diff --git a/tests/_files/TestableCliTestDoxPrinter.php b/tests/_files/TestableCliTestDoxPrinter.php deleted file mode 100644 index cdc3a4fbb0b..00000000000 --- a/tests/_files/TestableCliTestDoxPrinter.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use PHPUnit\Logging\TestDox\CliTestDoxPrinter; - -class TestableCliTestDoxPrinter extends CliTestDoxPrinter -{ - private $buffer; - - public function write(string $text): void - { - $this->buffer .= $text; - } - - public function getBuffer(): string - { - return $this->buffer; - } -} diff --git a/tests/_files/ThrowExceptionTestCase.php b/tests/_files/ThrowExceptionTestCase.php index 49b6716f4dc..0835a0b31e4 100644 --- a/tests/_files/ThrowExceptionTestCase.php +++ b/tests/_files/ThrowExceptionTestCase.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\TestCase; use RuntimeException; -class ThrowExceptionTestCase extends TestCase +final class ThrowExceptionTestCase extends TestCase { public function test(): void { diff --git a/tests/_files/ThrowNoExceptionTestCase.php b/tests/_files/ThrowNoExceptionTestCase.php index 953702d59a4..beb5605ba9d 100644 --- a/tests/_files/ThrowNoExceptionTestCase.php +++ b/tests/_files/ThrowNoExceptionTestCase.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; -class ThrowNoExceptionTestCase extends TestCase +final class ThrowNoExceptionTestCase extends TestCase { public function test(): void { diff --git a/tests/_files/VariousDocblockDefinedDataProvider.php b/tests/_files/VariousDocblockDefinedDataProvider.php deleted file mode 100644 index 1a4d4d16337..00000000000 --- a/tests/_files/VariousDocblockDefinedDataProvider.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -final class VariousDocblockDefinedDataProvider -{ - /** - * @anotherAnnotation - */ - public function anotherAnnotation(): void - { - } - - /** - * @testWith [1] - */ - public function testWith1(): void - { - } - - /** - * @testWith [1, 2] - * [3, 4] - */ - public function testWith1234(): void - { - } - - /** - * @testWith ["ab"] - * [true] - * [null] - */ - public function testWithABTrueNull(): void - { - } - - /** - * @testWith [1] - * [2] - * - * @annotation - */ - public function testWith12AndAnotherAnnotation(): void - { - } - - /** - * @testWith [1] - * [2] - * blah blah - */ - public function testWith12AndBlahBlah(): void - { - } - - /** - * @testWith ["\"", "\""] - */ - public function testWithEscapedString(): void - { - } - - /** - * @testWith [s] - */ - public function testWithMalformedValue(): void - { - } - - /** - * @testWith ["valid"] - * [invalid] - */ - public function testWithWellFormedAndMalformedValue(): void - { - } -} diff --git a/tests/_files/VariousIterableDataProviderTest.php b/tests/_files/VariousIterableDataProviderTest.php index 3173efd8678..348146d14bf 100644 --- a/tests/_files/VariousIterableDataProviderTest.php +++ b/tests/_files/VariousIterableDataProviderTest.php @@ -9,9 +9,12 @@ */ namespace PHPUnit\TestFixture; -class VariousIterableDataProviderTest extends AbstractVariousIterableDataProviderTest +use Generator; +use PHPUnit\Framework\Attributes\DataProvider; + +final class VariousIterableDataProviderTest extends AbstractVariousIterableDataProviderTest { - public static function asArrayStaticProvider() + public static function asArrayStaticProvider(): array { return [ ['A'], @@ -20,7 +23,7 @@ public static function asArrayStaticProvider() ]; } - public static function asIteratorStaticProvider() + public static function asIteratorStaticProvider(): Generator { yield ['D']; @@ -29,7 +32,7 @@ public static function asIteratorStaticProvider() yield ['F']; } - public static function asTraversableStaticProvider() + public static function asTraversableStaticProvider(): WrapperIteratorAggregate { return new WrapperIteratorAggregate([ ['G'], @@ -38,7 +41,7 @@ public static function asTraversableStaticProvider() ]); } - public static function asArrayProvider() + public static function asArrayProvider(): array { return [ ['S'], @@ -47,7 +50,7 @@ public static function asArrayProvider() ]; } - public static function asIteratorProvider() + public static function asIteratorProvider(): Generator { yield ['V']; @@ -56,7 +59,7 @@ public static function asIteratorProvider() yield ['X']; } - public static function asTraversableProvider() + public static function asTraversableProvider(): WrapperIteratorAggregate { return new WrapperIteratorAggregate([ ['Y'], @@ -65,29 +68,23 @@ public static function asTraversableProvider() ]); } - /** - * @dataProvider asArrayStaticProvider - * @dataProvider asIteratorStaticProvider - * @dataProvider asTraversableStaticProvider - */ + #[DataProvider('asArrayStaticProvider')] + #[DataProvider('asIteratorStaticProvider')] + #[DataProvider('asTraversableStaticProvider')] public function testStatic(): void { } - /** - * @dataProvider asArrayProvider - * @dataProvider asIteratorProvider - * @dataProvider asTraversableProvider - */ + #[DataProvider('asArrayProvider')] + #[DataProvider('asIteratorProvider')] + #[DataProvider('asTraversableProvider')] public function testNonStatic(): void { } - /** - * @dataProvider asArrayProviderInParent - * @dataProvider asIteratorProviderInParent - * @dataProvider asTraversableProviderInParent - */ + #[DataProvider('asArrayProviderInParent')] + #[DataProvider('asIteratorProviderInParent')] + #[DataProvider('asTraversableProviderInParent')] public function testFromParent(): void { } diff --git a/tests/_files/WrapperIteratorAggregate.php b/tests/_files/WrapperIteratorAggregate.php index 70840c777b3..9bb3666bbd3 100644 --- a/tests/_files/WrapperIteratorAggregate.php +++ b/tests/_files/WrapperIteratorAggregate.php @@ -12,7 +12,7 @@ use Generator; use IteratorAggregate; -class WrapperIteratorAggregate implements IteratorAggregate +final class WrapperIteratorAggregate implements IteratorAggregate { private iterable $baseCollection; diff --git a/tests/_files/XmlConfigurationMigration/input-9.5.xml b/tests/_files/XmlConfigurationMigration/input-9.5.xml index 9f30ea7517f..4b65160d283 100644 --- a/tests/_files/XmlConfigurationMigration/input-9.5.xml +++ b/tests/_files/XmlConfigurationMigration/input-9.5.xml @@ -15,6 +15,7 @@ printerFile="foo" printerClass="bar" backupStaticAttributes="true" + registerMockObjectsFromTestArgumentsRecursively="true" noInteraction="http://github.com/true" verbose="true"> diff --git a/tests/_files/XmlConfigurationMigration/input-issue-5859.xml b/tests/_files/XmlConfigurationMigration/input-issue-5859.xml new file mode 100644 index 00000000000..ee91318f785 --- /dev/null +++ b/tests/_files/XmlConfigurationMigration/input-issue-5859.xml @@ -0,0 +1,5 @@ + + + + diff --git a/tests/_files/XmlConfigurationMigration/input-issue-6087.xml b/tests/_files/XmlConfigurationMigration/input-issue-6087.xml new file mode 100644 index 00000000000..8ba9d4b4814 --- /dev/null +++ b/tests/_files/XmlConfigurationMigration/input-issue-6087.xml @@ -0,0 +1,11 @@ + + + + + tests + + + diff --git a/tests/_files/XmlConfigurationMigration/input-relative-schema-path.xml b/tests/_files/XmlConfigurationMigration/input-relative-schema-path.xml new file mode 100644 index 00000000000..2bd593509cf --- /dev/null +++ b/tests/_files/XmlConfigurationMigration/input-relative-schema-path.xml @@ -0,0 +1,5 @@ + + + + diff --git a/tests/_files/XmlConfigurationMigration/output-9.2.xml b/tests/_files/XmlConfigurationMigration/output-9.2.xml index 8c337f01fe3..807e038552c 100644 --- a/tests/_files/XmlConfigurationMigration/output-9.2.xml +++ b/tests/_files/XmlConfigurationMigration/output-9.2.xml @@ -1,6 +1,6 @@ - - diff --git a/tests/_files/XmlConfigurationMigration/output-issue-5859.xml b/tests/_files/XmlConfigurationMigration/output-issue-5859.xml new file mode 100644 index 00000000000..5bfbaa21dc7 --- /dev/null +++ b/tests/_files/XmlConfigurationMigration/output-issue-5859.xml @@ -0,0 +1,5 @@ + + + + diff --git a/tests/_files/XmlConfigurationMigration/output-issue-6087.xml b/tests/_files/XmlConfigurationMigration/output-issue-6087.xml new file mode 100644 index 00000000000..83716bae3fd --- /dev/null +++ b/tests/_files/XmlConfigurationMigration/output-issue-6087.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/_files/XmlConfigurationMigration/output-relative-schema-path.xml b/tests/_files/XmlConfigurationMigration/output-relative-schema-path.xml new file mode 100644 index 00000000000..31fa3f9c8a6 --- /dev/null +++ b/tests/_files/XmlConfigurationMigration/output-relative-schema-path.xml @@ -0,0 +1,5 @@ + + + + diff --git a/tests/_files/bar.txt b/tests/_files/bar.txt new file mode 100644 index 00000000000..351f9261cd1 --- /dev/null +++ b/tests/_files/bar.txt @@ -0,0 +1 @@ +Voulez-vous un café? diff --git a/tests/_files/configuration.suites.xml b/tests/_files/configuration.suites.xml deleted file mode 100644 index eb9ec3d76e1..00000000000 --- a/tests/_files/configuration.suites.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/_files/configuration.xml b/tests/_files/configuration.xml index cbe148b2c74..f29010b7b0e 100644 --- a/tests/_files/configuration.xml +++ b/tests/_files/configuration.xml @@ -31,6 +31,7 @@ executionOrder="default" controlGarbageCollector="true" numberOfTestsBeforeGarbageCollection="1000" + shortenArraysForExportThreshold="10" > diff --git a/tests/_files/configuration_codecoverage.xml b/tests/_files/configuration_codecoverage.xml index d0e16b78a77..7b3713a17d4 100644 --- a/tests/_files/configuration_codecoverage.xml +++ b/tests/_files/configuration_codecoverage.xml @@ -1,7 +1,7 @@ - + /path/to/files /path/to/file @@ -14,6 +14,11 @@ /path/to/files /path/to/file + + + PHPUnit\TestFixture\DeprecationTrigger\trigger_deprecation + PHPUnit\TestFixture\DeprecationTrigger\DeprecationTrigger::triggerDeprecation + - + diff --git a/tests/_files/configuration_testsuites.xml b/tests/_files/configuration_testsuites.xml index de918896298..b7ce961b927 100644 --- a/tests/_files/configuration_testsuites.xml +++ b/tests/_files/configuration_testsuites.xml @@ -3,12 +3,12 @@ xsi:noNamespaceSchemaLocation="../../phpunit.xsd"> - tests/first + tests/first - tests/second - tests/file.php + tests/second + tests/file.php tests/second/_files diff --git a/tests/_files/delete_directory.php b/tests/_files/delete_directory.php new file mode 100644 index 00000000000..f8776bbb8a8 --- /dev/null +++ b/tests/_files/delete_directory.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +function delete_directory(string $directory): void +{ + if (\is_file($directory)) { + @\unlink($directory); + + return; + } + + if (!\is_dir($directory)) { + return; + } + + foreach (\glob(\rtrim($directory, '/') . '/*') as $path) { + delete_directory($path); + } + + @\rmdir($directory); +} diff --git a/tests/_files/dependencies/DependencyFailureTest.php b/tests/_files/dependencies/DependencyFailureTest.php index 4751eb50e12..06a86e6c2d1 100644 --- a/tests/_files/dependencies/DependencyFailureTest.php +++ b/tests/_files/dependencies/DependencyFailureTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DependsUsingShallowClone; use PHPUnit\Framework\TestCase; class DependencyFailureTest extends TestCase @@ -18,47 +20,32 @@ public function testOne(): void $this->assertTrue(false); } - /** - * @depends testOne - */ + #[Depends('testOne')] public function testTwo(): void { $this->assertTrue(true); } - /** - * @depends !clone testTwo - */ + #[Depends('testTwo')] public function testThree(): void { $this->assertTrue(true); } - /** - * @depends clone testOne - */ + #[DependsUsingShallowClone('testOne')] public function testFour(): void { $this->assertTrue(true); } - /** - * This test has been added to check the printed warnings for the user - * when a dependency simply doesn't exist. - * - * @depends doesNotExist - * - * @see https://github.com/sebastianbergmann/phpunit/issues/3517 - */ - public function testHandlesDependsAnnotationForNonexistentTests(): void + #[Depends('doesNotExist')] + public function testHandlesDependencyOnTestMethodThatDoesNotExist(): void { $this->assertTrue(true); } - /** - * @depends - */ - public function testHandlesDependsAnnotationWithNoMethodSpecified(): void + #[Depends('')] + public function testHandlesDependencyOnTestMethodWithEmptyName(): void { $this->assertTrue(true); } diff --git a/tests/_files/dependencies/DependencySuccessTest.php b/tests/_files/dependencies/DependencySuccessTest.php index 9f89606c1a7..cc9cd119924 100644 --- a/tests/_files/dependencies/DependencySuccessTest.php +++ b/tests/_files/dependencies/DependencySuccessTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\TestCase; class DependencySuccessTest extends TestCase @@ -18,17 +20,13 @@ public function testOne(): void $this->assertTrue(true); } - /** - * @depends testOne - */ + #[Depends('testOne')] public function testTwo(): void { $this->assertTrue(true); } - /** - * @depends PHPUnit\TestFixture\DependencySuccessTest::testTwo - */ + #[DependsExternal(self::class, 'testTwo')] public function testThree(): void { $this->assertTrue(true); diff --git a/tests/_files/deprecation-trigger/DeprecationTrigger.php b/tests/_files/deprecation-trigger/DeprecationTrigger.php new file mode 100644 index 00000000000..d66323d11c6 --- /dev/null +++ b/tests/_files/deprecation-trigger/DeprecationTrigger.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\DeprecationTrigger; + +final class DeprecationTrigger +{ + public function triggerDeprecation(): void + { + } +} diff --git a/tests/_files/deprecation-trigger/trigger_deprecation.php b/tests/_files/deprecation-trigger/trigger_deprecation.php new file mode 100644 index 00000000000..0979dc3976f --- /dev/null +++ b/tests/_files/deprecation-trigger/trigger_deprecation.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\DeprecationTrigger; + +function trigger_deprecation(): void +{ +} diff --git a/tests/_files/failure.phpt b/tests/_files/failure.phpt new file mode 100644 index 00000000000..2d76d87f7ab --- /dev/null +++ b/tests/_files/failure.phpt @@ -0,0 +1,7 @@ +--TEST-- +failure +--FILE-- + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface AnInterfaceForIssue5593 +{ + public function doSomething(): AnotherInterfaceForIssue5593; +} diff --git a/tests/_files/mock-object/AnotherInterfaceForIssue5593.php b/tests/_files/mock-object/AnotherInterfaceForIssue5593.php new file mode 100644 index 00000000000..81f695a029f --- /dev/null +++ b/tests/_files/mock-object/AnotherInterfaceForIssue5593.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface AnotherInterfaceForIssue5593 +{ + public function doSomethingElse(): static; +} diff --git a/tests/_files/mock-object/ExtendableClass.php b/tests/_files/mock-object/ExtendableClass.php index 9a3150b74e1..dbe29c1294e 100644 --- a/tests/_files/mock-object/ExtendableClass.php +++ b/tests/_files/mock-object/ExtendableClass.php @@ -11,6 +11,17 @@ class ExtendableClass { + public bool $constructorCalled = false; + + public function __construct() + { + $this->constructorCalled = true; + } + + public function __destruct() + { + } + public function doSomething(): bool { return $this->doSomethingElse(); @@ -20,4 +31,12 @@ public function doSomethingElse(): bool { return false; } + + final public function finalMethod(): void + { + } + + private function privateMethod(): void + { + } } diff --git a/tests/_files/mock-object/ExtendableClassCallingMethodInConstructor.php b/tests/_files/mock-object/ExtendableClassCallingMethodInConstructor.php new file mode 100644 index 00000000000..dc380ddd5f9 --- /dev/null +++ b/tests/_files/mock-object/ExtendableClassCallingMethodInConstructor.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +class ExtendableClassCallingMethodInConstructor +{ + public function __construct() + { + $this->reset(); + } + + public function reset(): void + { + } + + public function second(): void + { + $this->reset(); + } +} diff --git a/tests/_files/mock-object/ExtendableClassCallingMethodInDestructor.php b/tests/_files/mock-object/ExtendableClassCallingMethodInDestructor.php new file mode 100644 index 00000000000..8141c83345b --- /dev/null +++ b/tests/_files/mock-object/ExtendableClassCallingMethodInDestructor.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +class ExtendableClassCallingMethodInDestructor +{ + public function __destruct() + { + $this->doSomethingElse(); + } + + public function doSomething(): static + { + return $this; + } + + public function doSomethingElse(): void + { + } +} diff --git a/tests/_files/mock-object/ExtendableClassWithConstructorArguments.php b/tests/_files/mock-object/ExtendableClassWithConstructorArguments.php new file mode 100644 index 00000000000..996151caa75 --- /dev/null +++ b/tests/_files/mock-object/ExtendableClassWithConstructorArguments.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +class ExtendableClassWithConstructorArguments +{ + private string $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public function value(): string + { + return $this->value; + } +} diff --git a/tests/_files/mock-object/ExtendableClassWithPropertyWithGetHook.php b/tests/_files/mock-object/ExtendableClassWithPropertyWithGetHook.php new file mode 100644 index 00000000000..dc5051f8d07 --- /dev/null +++ b/tests/_files/mock-object/ExtendableClassWithPropertyWithGetHook.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +class ExtendableClassWithPropertyWithGetHook +{ + public string $property { + get { + return 'value'; + } + } +} diff --git a/tests/_files/mock-object/ExtendableClassWithPropertyWithSetHook.php b/tests/_files/mock-object/ExtendableClassWithPropertyWithSetHook.php new file mode 100644 index 00000000000..dd72d204a03 --- /dev/null +++ b/tests/_files/mock-object/ExtendableClassWithPropertyWithSetHook.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +class ExtendableClassWithPropertyWithSetHook +{ + public string $property { + set (string $value) { + $this->property = $value; + } + } +} diff --git a/tests/_files/mock-object/ExtendableReadonlyClass.php b/tests/_files/mock-object/ExtendableReadonlyClass.php new file mode 100644 index 00000000000..1a1673ba3ee --- /dev/null +++ b/tests/_files/mock-object/ExtendableReadonlyClass.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +readonly class ExtendableReadonlyClass +{ + public function __construct(private mixed $value) + { + } + + public function value(): mixed + { + return $this->value; + } +} diff --git a/tests/_files/mock-object/ExtendableReadonlyClassWithCloneMethod.php b/tests/_files/mock-object/ExtendableReadonlyClassWithCloneMethod.php new file mode 100644 index 00000000000..5f8d9701c03 --- /dev/null +++ b/tests/_files/mock-object/ExtendableReadonlyClassWithCloneMethod.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +use Exception; + +readonly class ExtendableReadonlyClassWithCloneMethod +{ + /** + * @throws Exception + */ + public function __clone(): void + { + throw new Exception(__METHOD__); + } + + public function doSomething(): bool + { + return true; + } +} diff --git a/tests/_files/mock-object/InterfaceWithMethodThatHasDefaultParameterValues.php b/tests/_files/mock-object/InterfaceWithMethodThatHasDefaultParameterValues.php new file mode 100644 index 00000000000..7b10933e685 --- /dev/null +++ b/tests/_files/mock-object/InterfaceWithMethodThatHasDefaultParameterValues.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface InterfaceWithMethodThatHasDefaultParameterValues +{ + public function doSomething(int $a, int $b = 1): int; +} diff --git a/tests/_files/mock-object/InterfaceWithMethodThatReturnsSelf.php b/tests/_files/mock-object/InterfaceWithMethodThatReturnsSelf.php new file mode 100644 index 00000000000..794feed342c --- /dev/null +++ b/tests/_files/mock-object/InterfaceWithMethodThatReturnsSelf.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface InterfaceWithMethodThatReturnsSelf +{ + public function doSomething(): self; +} diff --git a/tests/_files/mock-object/InterfaceWithMethodThatReturnsStatic.php b/tests/_files/mock-object/InterfaceWithMethodThatReturnsStatic.php new file mode 100644 index 00000000000..fd806494803 --- /dev/null +++ b/tests/_files/mock-object/InterfaceWithMethodThatReturnsStatic.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface InterfaceWithMethodThatReturnsStatic +{ + public function doSomething(): static; +} diff --git a/tests/_files/mock-object/InterfaceWithPropertyWithGetHook.php b/tests/_files/mock-object/InterfaceWithPropertyWithGetHook.php new file mode 100644 index 00000000000..33578e78da7 --- /dev/null +++ b/tests/_files/mock-object/InterfaceWithPropertyWithGetHook.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface InterfaceWithPropertyWithGetHook +{ + public string $property { get; } +} diff --git a/tests/_files/mock-object/InterfaceWithPropertyWithSetHook.php b/tests/_files/mock-object/InterfaceWithPropertyWithSetHook.php new file mode 100644 index 00000000000..c455fea1fbe --- /dev/null +++ b/tests/_files/mock-object/InterfaceWithPropertyWithSetHook.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface InterfaceWithPropertyWithSetHook +{ + public string $property { set; } +} diff --git a/tests/_files/mock-object/Issue6174.php b/tests/_files/mock-object/Issue6174.php new file mode 100644 index 00000000000..dd3d9c32484 --- /dev/null +++ b/tests/_files/mock-object/Issue6174.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +interface Issue6174 +{ + public function methodNullDefault(?string $param, ?string $nullDefault = null): string; + + public function methodStringDefault(?string $param, ?string $stringDefault = 'something'): string; +} diff --git a/tests/_files/mock-object/MethodWIthVariadicVariables.php b/tests/_files/mock-object/MethodWIthVariadicVariables.php new file mode 100644 index 00000000000..759a08ca766 --- /dev/null +++ b/tests/_files/mock-object/MethodWIthVariadicVariables.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\MockObject; + +class MethodWIthVariadicVariables +{ + public function testVariadic(string $foo, mixed ...$arguments): array + { + return [$foo, ...$arguments]; + } +} diff --git a/tests/_files/mock-object/ReadonlyClass.php b/tests/_files/mock-object/ReadonlyClass.php deleted file mode 100644 index e968645c547..00000000000 --- a/tests/_files/mock-object/ReadonlyClass.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\MockObject; - -readonly class ReadonlyClass -{ - public function __construct(private mixed $value) - { - } - - public function value(): mixed - { - return $this->value; - } -} diff --git a/tests/_files/mock-object/TraitWithConcreteMethod.php b/tests/_files/mock-object/TraitWithConcreteMethod.php deleted file mode 100644 index bacaba177f6..00000000000 --- a/tests/_files/mock-object/TraitWithConcreteMethod.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\MockObject; - -trait TraitWithConcreteMethod -{ - public function doSomething(): bool - { - return true; - } -} diff --git a/tests/_files/phpt/invalid/no-expectation-code.phpt b/tests/_files/phpt/invalid/no-expectation-code.phpt new file mode 100644 index 00000000000..d463ae53e15 --- /dev/null +++ b/tests/_files/phpt/invalid/no-expectation-code.phpt @@ -0,0 +1,4 @@ +--TEST-- +Test +--FILE-- +=') && \in_array('coverage', xdebug_info('mode'), true)) { +if (version_compare(phpversion('xdebug'), '3.1', '>=') && in_array('coverage', xdebug_info('mode'), true)) { return; } -$mode = \getenv('XDEBUG_MODE'); +$mode = getenv('XDEBUG_MODE'); if ($mode === false || $mode === '') { - $mode = \ini_get('xdebug.mode'); + $mode = ini_get('xdebug.mode'); } if ($mode === false || - !\in_array('coverage', \explode(',', $mode), true)) { + !in_array('coverage', explode(',', $mode), true)) { print 'skip: XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set'; } diff --git a/tests/_files/source-filter/b/e/PrefixExampleSuffix.php b/tests/_files/source-filter/b/e/PrefixExampleSuffix.php new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/_files/success.phpt b/tests/_files/success.phpt new file mode 100644 index 00000000000..d47ea41521b --- /dev/null +++ b/tests/_files/success.phpt @@ -0,0 +1,7 @@ +--TEST-- +success +--FILE-- + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\DeprecatedAnnotationsTestFixture; + +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\TestCase; + +final class BeforeTestMethodWithAttributeTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } + + #[Before] + protected function beforeMethod(): void + { + } +} diff --git a/tests/end-to-end/_files/BeforeTestMethodWithPrioritizedAttributeTest.php b/tests/end-to-end/_files/BeforeTestMethodWithPrioritizedAttributeTest.php new file mode 100644 index 00000000000..053ee663bcf --- /dev/null +++ b/tests/end-to-end/_files/BeforeTestMethodWithPrioritizedAttributeTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\DeprecatedAnnotationsTestFixture; + +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\TestCase; + +final class BeforeTestMethodWithPrioritizedAttributeTest extends TestCase +{ + protected function setUp(): void + { + } + + public function testOne(): void + { + $this->assertTrue(true); + } + + #[Before(priority: 1)] + protected function beforeMethodWithHighPriority(): void + { + } + + #[Before(priority: -1)] + protected function beforeMethodWithLowPriority(): void + { + } +} diff --git a/tests/end-to-end/_files/Extension.php b/tests/end-to-end/_files/Extension.php deleted file mode 100644 index ab1355977b9..00000000000 --- a/tests/end-to-end/_files/Extension.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Test; - -use const PHP_EOL; -use function count; -use function func_get_args; -use PHPUnit\Runner\AfterIncompleteTestHook; -use PHPUnit\Runner\AfterLastTestHook; -use PHPUnit\Runner\AfterRiskyTestHook; -use PHPUnit\Runner\AfterSkippedTestHook; -use PHPUnit\Runner\AfterSuccessfulTestHook; -use PHPUnit\Runner\AfterTestErrorHook; -use PHPUnit\Runner\AfterTestFailureHook; -use PHPUnit\Runner\AfterTestHook; -use PHPUnit\Runner\AfterTestWarningHook; -use PHPUnit\Runner\BeforeFirstTestHook; -use PHPUnit\Runner\BeforeTestHook; - -final class Extension implements AfterIncompleteTestHook, AfterLastTestHook, AfterRiskyTestHook, AfterSkippedTestHook, AfterSuccessfulTestHook, AfterTestErrorHook, AfterTestFailureHook, AfterTestHook, AfterTestWarningHook, BeforeFirstTestHook, BeforeTestHook -{ - private $amountOfInjectedArguments = 0; - - public function __construct() - { - $this->amountOfInjectedArguments = count(func_get_args()); - } - - public function tellAmountOfInjectedArguments(): void - { - print __METHOD__ . ': ' . $this->amountOfInjectedArguments . PHP_EOL; - } - - public function executeBeforeFirstTest(): void - { - $this->tellAmountOfInjectedArguments(); - print __METHOD__ . PHP_EOL; - } - - public function executeBeforeTest(string $test): void - { - print __METHOD__ . ': ' . $test . PHP_EOL; - } - - public function executeAfterTest(string $test, float $time): void - { - print __METHOD__ . ': ' . $test . PHP_EOL; - } - - public function executeAfterSuccessfulTest(string $test, float $time): void - { - print __METHOD__ . ': ' . $test . PHP_EOL; - } - - public function executeAfterIncompleteTest(string $test, string $message, float $time): void - { - print __METHOD__ . ': ' . $test . ': ' . $message . PHP_EOL; - } - - public function executeAfterRiskyTest(string $test, string $message, float $time): void - { - print __METHOD__ . ': ' . $test . ': ' . $message . PHP_EOL; - } - - public function executeAfterSkippedTest(string $test, string $message, float $time): void - { - print __METHOD__ . ': ' . $test . ': ' . $message . PHP_EOL; - } - - public function executeAfterTestError(string $test, string $message, float $time): void - { - print __METHOD__ . ': ' . $test . ': ' . $message . PHP_EOL; - } - - public function executeAfterTestFailure(string $test, string $message, float $time): void - { - print __METHOD__ . ': ' . $test . ': ' . $message . PHP_EOL; - } - - public function executeAfterTestWarning(string $test, string $message, float $time): void - { - print __METHOD__ . ': ' . $test . ': ' . $message . PHP_EOL; - } - - public function executeAfterLastTest(): void - { - print __METHOD__ . PHP_EOL; - } -} diff --git a/tests/end-to-end/_files/HookMethodsOrderTest.php b/tests/end-to-end/_files/HookMethodsOrderTest.php new file mode 100644 index 00000000000..882556c0d6c --- /dev/null +++ b/tests/end-to-end/_files/HookMethodsOrderTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\Before; + +final class HookMethodsOrderTest extends HookMethodsOrderTestCase +{ + protected function setUp(): void + { + } + + protected function tearDown(): void + { + } + + public function testOne(): void + { + $this->assertTrue(true); + } + + #[Before] + protected function beforeSecond(): void + { + } + + #[Before] + protected function beforeFirst(): void + { + } + + #[Before(priority: 1)] + protected function beforeWithPriority(): void + { + } + + #[After] + protected function afterFirst(): void + { + } + + #[After] + protected function afterSecond(): void + { + } + + #[After(priority: 1)] + protected function afterWithPriority(): void + { + } +} diff --git a/tests/end-to-end/_files/HookMethodsOrderTestCase.php b/tests/end-to-end/_files/HookMethodsOrderTestCase.php new file mode 100644 index 00000000000..39f7554c8bd --- /dev/null +++ b/tests/end-to-end/_files/HookMethodsOrderTestCase.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\Before; +use PHPUnit\Framework\TestCase; + +abstract class HookMethodsOrderTestCase extends TestCase +{ + #[Before] + protected function beforeInParent(): void + { + } + + #[Before(priority: 1)] + protected function beforeWithPriorityInParent(): void + { + } + + #[After] + protected function afterInParent(): void + { + } + + #[After(priority: 1)] + protected function afterWithPriorityInParent(): void + { + } +} diff --git a/tests/end-to-end/_files/TraitTargetedWithCoversClassTest.php b/tests/end-to-end/_files/TraitTargetedWithCoversClassTest.php new file mode 100644 index 00000000000..ec3f79aeff2 --- /dev/null +++ b/tests/end-to-end/_files/TraitTargetedWithCoversClassTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\DeprecatedAnnotationsTestFixture; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\CoveredTrait; + +#[CoversClass(CoveredTrait::class)] +final class TraitTargetedWithCoversClassTest extends TestCase +{ + public function testSomething(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/TraitTargetedWithCoversTraitTest.php b/tests/end-to-end/_files/TraitTargetedWithCoversTraitTest.php new file mode 100644 index 00000000000..7e8159c9725 --- /dev/null +++ b/tests/end-to-end/_files/TraitTargetedWithCoversTraitTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\DeprecatedAnnotationsTestFixture; + +use PHPUnit\Framework\Attributes\CoversTrait; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\CoveredTrait; + +#[CoversTrait(CoveredTrait::class)] +final class TraitTargetedWithCoversTraitTest extends TestCase +{ + public function testSomething(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/TraitTargetedWithUsesClassTest.php b/tests/end-to-end/_files/TraitTargetedWithUsesClassTest.php new file mode 100644 index 00000000000..b06f015557a --- /dev/null +++ b/tests/end-to-end/_files/TraitTargetedWithUsesClassTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\DeprecatedAnnotationsTestFixture; + +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\CoveredTrait; + +#[UsesClass(CoveredTrait::class)] +final class TraitTargetedWithUsesClassTest extends TestCase +{ + public function testSomething(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/TraitTargetedWithUsesTraitTest.php b/tests/end-to-end/_files/TraitTargetedWithUsesTraitTest.php new file mode 100644 index 00000000000..699ccc46d72 --- /dev/null +++ b/tests/end-to-end/_files/TraitTargetedWithUsesTraitTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\DeprecatedAnnotationsTestFixture; + +use PHPUnit\Framework\Attributes\UsesTrait; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\CoveredTrait; + +#[UsesTrait(CoveredTrait::class)] +final class TraitTargetedWithUsesTraitTest extends TestCase +{ + public function testSomething(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/phpunit.xml b/tests/end-to-end/_files/attribute-based-filtering/phpunit.xml new file mode 100644 index 00000000000..0025e227829 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/phpunit.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/end-to-end/_files/attribute-based-filtering/src/Foo.php b/tests/end-to-end/_files/attribute-based-filtering/src/Foo.php new file mode 100644 index 00000000000..25481906cf8 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/src/Foo.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +final class Foo +{ + public function bar(): bool + { + return true; + } +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/src/autoload.php b/tests/end-to-end/_files/attribute-based-filtering/src/autoload.php new file mode 100644 index 00000000000..fcbf8ff4050 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/src/autoload.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/f.php'; + +require __DIR__ . '/Foo.php'; diff --git a/tests/end-to-end/_files/attribute-based-filtering/src/f.php b/tests/end-to-end/_files/attribute-based-filtering/src/f.php new file mode 100644 index 00000000000..0c98490d30e --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/src/f.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +function f(): bool +{ + return true; +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/tests/CoversClassTest.php b/tests/end-to-end/_files/attribute-based-filtering/tests/CoversClassTest.php new file mode 100644 index 00000000000..6a5ecf417b5 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/tests/CoversClassTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Foo::class)] +final class CoversClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/tests/CoversFunctionTest.php b/tests/end-to-end/_files/attribute-based-filtering/tests/CoversFunctionTest.php new file mode 100644 index 00000000000..db81b6e2853 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/tests/CoversFunctionTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\TestCase; + +#[CoversFunction('PHPUnit\TestFixture\AttributeBasedFiltering\f')] +final class CoversFunctionTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/tests/RequiresPhpExtensionTest.php b/tests/end-to-end/_files/attribute-based-filtering/tests/RequiresPhpExtensionTest.php new file mode 100644 index 00000000000..baec8800d1f --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/tests/RequiresPhpExtensionTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +use PHPUnit\Framework\Attributes\RequiresPhpExtension; +use PHPUnit\Framework\TestCase; + +#[RequiresPhpExtension('standard')] +final class RequiresPhpExtensionTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/tests/UsesFunctionTest.php b/tests/end-to-end/_files/attribute-based-filtering/tests/UsesFunctionTest.php new file mode 100644 index 00000000000..aad32abfea7 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/tests/UsesFunctionTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +use PHPUnit\Framework\Attributes\UsesFunction; +use PHPUnit\Framework\TestCase; + +#[UsesFunction('PHPUnit\TestFixture\AttributeBasedFiltering\f')] +final class UsesFunctionTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/attribute-based-filtering/tests/UsesTest.php b/tests/end-to-end/_files/attribute-based-filtering/tests/UsesTest.php new file mode 100644 index 00000000000..cffe7cf28e0 --- /dev/null +++ b/tests/end-to-end/_files/attribute-based-filtering/tests/UsesTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\AttributeBasedFiltering; + +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[UsesClass(Foo::class)] +final class UsesTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/phpunit.xml b/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/phpunit.xml deleted file mode 100644 index 7e6630c6765..00000000000 --- a/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/phpunit.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - tests - - - diff --git a/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/tests/Test.php b/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/tests/Test.php deleted file mode 100644 index d9ed86d8a59..00000000000 --- a/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/tests/Test.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Baseline; - -use function trigger_error; -use PHPUnit\Framework\TestCase; - -final class Test extends TestCase -{ - public function testOne(): void - { - trigger_error('deprecation', E_USER_DEPRECATED); - - $this->assertTrue(true); - } -} diff --git a/tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/.gitignore b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/.gitignore similarity index 100% rename from tests/end-to-end/_files/baseline/generate-baseline-no-baseline-configured/.gitignore rename to tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/.gitignore diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/phpunit.xml b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/phpunit.xml new file mode 100644 index 00000000000..303ce33dcfc --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/phpunit.xml @@ -0,0 +1,24 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/src/Source.php b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/src/Source.php new file mode 100644 index 00000000000..edb4cc6d5ca --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/src/Source.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use const E_USER_DEPRECATED; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use function trigger_error; +use Serializable; + +final class Source +{ + public function triggerDeprecation(): void + { + $this->deprecation(); + } + + public function triggerNotice(): void + { + $this->notice(); + } + + public function triggerWarning(): void + { + $this->warning(); + } + + public function triggerPhpDeprecation(): void + { + $this->phpDeprecation(); + } + + public function triggerPhpNoticeAndWarning(): void + { + $this->phpNoticeAndWarning(); + } + + private function deprecation(): void + { + @trigger_error('deprecation', E_USER_DEPRECATED); + } + + private function notice(): void + { + @trigger_error('notice', E_USER_NOTICE); + } + + private function warning(): void + { + @trigger_error('warning', E_USER_WARNING); + } + + private function phpDeprecation(): void + { + @$o = new class implements Serializable + { + public function serialize(): void + { + } + + public function unserialize(string $data): void + { + } + }; + } + + private function phpNoticeAndWarning(): void + { + $o = new class + { + public static $a = 'b'; + }; + + @$o->a; + } +} diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/tests/Test.php b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/tests/Test.php new file mode 100644 index 00000000000..4f578129f7c --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed-with-ignored-suppression/tests/Test.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testDeprecation(): void + { + (new Source)->triggerDeprecation(); + + $this->assertTrue(true); + } + + public function testNotice(): void + { + (new Source)->triggerNotice(); + + $this->assertTrue(true); + } + + public function testWarning(): void + { + (new Source)->triggerWarning(); + + $this->assertTrue(true); + } + + public function testPhpDeprecation(): void + { + (new Source)->triggerPhpDeprecation(); + + $this->assertTrue(true); + } + + public function testPhpNoticeAndWarning(): void + { + (new Source)->triggerPhpNoticeAndWarning(); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed/.gitignore b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/.gitignore new file mode 100644 index 00000000000..8d20d1464c8 --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/.gitignore @@ -0,0 +1 @@ +baseline.xml diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed/phpunit.xml b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/phpunit.xml new file mode 100644 index 00000000000..fd8b4b4f0bb --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/phpunit.xml @@ -0,0 +1,18 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed/src/Source.php b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/src/Source.php new file mode 100644 index 00000000000..edb4cc6d5ca --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/src/Source.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use const E_USER_DEPRECATED; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use function trigger_error; +use Serializable; + +final class Source +{ + public function triggerDeprecation(): void + { + $this->deprecation(); + } + + public function triggerNotice(): void + { + $this->notice(); + } + + public function triggerWarning(): void + { + $this->warning(); + } + + public function triggerPhpDeprecation(): void + { + $this->phpDeprecation(); + } + + public function triggerPhpNoticeAndWarning(): void + { + $this->phpNoticeAndWarning(); + } + + private function deprecation(): void + { + @trigger_error('deprecation', E_USER_DEPRECATED); + } + + private function notice(): void + { + @trigger_error('notice', E_USER_NOTICE); + } + + private function warning(): void + { + @trigger_error('warning', E_USER_WARNING); + } + + private function phpDeprecation(): void + { + @$o = new class implements Serializable + { + public function serialize(): void + { + } + + public function unserialize(string $data): void + { + } + }; + } + + private function phpNoticeAndWarning(): void + { + $o = new class + { + public static $a = 'b'; + }; + + @$o->a; + } +} diff --git a/tests/end-to-end/_files/baseline/generate-baseline-suppressed/tests/Test.php b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/tests/Test.php new file mode 100644 index 00000000000..4f578129f7c --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline-suppressed/tests/Test.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testDeprecation(): void + { + (new Source)->triggerDeprecation(); + + $this->assertTrue(true); + } + + public function testNotice(): void + { + (new Source)->triggerNotice(); + + $this->assertTrue(true); + } + + public function testWarning(): void + { + (new Source)->triggerWarning(); + + $this->assertTrue(true); + } + + public function testPhpDeprecation(): void + { + (new Source)->triggerPhpDeprecation(); + + $this->assertTrue(true); + } + + public function testPhpNoticeAndWarning(): void + { + (new Source)->triggerPhpNoticeAndWarning(); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/baseline/generate-baseline/phpunit.xml b/tests/end-to-end/_files/baseline/generate-baseline/phpunit.xml index bf0ee15c129..fd8b4b4f0bb 100644 --- a/tests/end-to-end/_files/baseline/generate-baseline/phpunit.xml +++ b/tests/end-to-end/_files/baseline/generate-baseline/phpunit.xml @@ -2,6 +2,7 @@ @@ -10,5 +11,8 @@ + + src + diff --git a/tests/end-to-end/_files/baseline/generate-baseline/src/Source.php b/tests/end-to-end/_files/baseline/generate-baseline/src/Source.php new file mode 100644 index 00000000000..a4a9779a0dc --- /dev/null +++ b/tests/end-to-end/_files/baseline/generate-baseline/src/Source.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use const E_USER_DEPRECATED; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use function trigger_error; +use Serializable; + +final class Source +{ + public function triggerDeprecation(): void + { + $this->deprecation(); + } + + public function triggerNotice(): void + { + $this->notice(); + } + + public function triggerWarning(): void + { + $this->warning(); + } + + public function triggerPhpDeprecation(): void + { + $this->phpDeprecation(); + } + + public function triggerPhpNoticeAndWarning(): void + { + $this->phpNoticeAndWarning(); + } + + private function deprecation(): void + { + trigger_error('deprecation', E_USER_DEPRECATED); + } + + private function notice(): void + { + trigger_error('notice', E_USER_NOTICE); + } + + private function warning(): void + { + trigger_error('warning', E_USER_WARNING); + } + + private function phpDeprecation(): void + { + $o = new class implements Serializable + { + public function serialize(): void + { + } + + public function unserialize(string $data): void + { + } + }; + } + + private function phpNoticeAndWarning(): void + { + $o = new class + { + public static $a = 'b'; + }; + + $o->a; + } +} diff --git a/tests/end-to-end/_files/baseline/generate-baseline/tests/Test.php b/tests/end-to-end/_files/baseline/generate-baseline/tests/Test.php index d9ed86d8a59..4f578129f7c 100644 --- a/tests/end-to-end/_files/baseline/generate-baseline/tests/Test.php +++ b/tests/end-to-end/_files/baseline/generate-baseline/tests/Test.php @@ -9,14 +9,41 @@ */ namespace PHPUnit\TestFixture\Baseline; -use function trigger_error; use PHPUnit\Framework\TestCase; final class Test extends TestCase { - public function testOne(): void + public function testDeprecation(): void { - trigger_error('deprecation', E_USER_DEPRECATED); + (new Source)->triggerDeprecation(); + + $this->assertTrue(true); + } + + public function testNotice(): void + { + (new Source)->triggerNotice(); + + $this->assertTrue(true); + } + + public function testWarning(): void + { + (new Source)->triggerWarning(); + + $this->assertTrue(true); + } + + public function testPhpDeprecation(): void + { + (new Source)->triggerPhpDeprecation(); + + $this->assertTrue(true); + } + + public function testPhpNoticeAndWarning(): void + { + (new Source)->triggerPhpNoticeAndWarning(); $this->assertTrue(true); } diff --git a/tests/end-to-end/_files/baseline/invalid-baseline/tests/Test.php b/tests/end-to-end/_files/baseline/invalid-baseline/tests/Test.php index d9ed86d8a59..8104eb56178 100644 --- a/tests/end-to-end/_files/baseline/invalid-baseline/tests/Test.php +++ b/tests/end-to-end/_files/baseline/invalid-baseline/tests/Test.php @@ -9,15 +9,12 @@ */ namespace PHPUnit\TestFixture\Baseline; -use function trigger_error; use PHPUnit\Framework\TestCase; final class Test extends TestCase { public function testOne(): void { - trigger_error('deprecation', E_USER_DEPRECATED); - $this->assertTrue(true); } } diff --git a/tests/end-to-end/_files/baseline/unsupported-baseline/tests/Test.php b/tests/end-to-end/_files/baseline/unsupported-baseline/tests/Test.php index d9ed86d8a59..8104eb56178 100644 --- a/tests/end-to-end/_files/baseline/unsupported-baseline/tests/Test.php +++ b/tests/end-to-end/_files/baseline/unsupported-baseline/tests/Test.php @@ -9,15 +9,12 @@ */ namespace PHPUnit\TestFixture\Baseline; -use function trigger_error; use PHPUnit\Framework\TestCase; final class Test extends TestCase { public function testOne(): void { - trigger_error('deprecation', E_USER_DEPRECATED); - $this->assertTrue(true); } } diff --git a/tests/end-to-end/_files/baseline/use-baseline/baseline.xml b/tests/end-to-end/_files/baseline/use-baseline/baseline.xml index 54457526106..47be2a96f2a 100644 --- a/tests/end-to-end/_files/baseline/use-baseline/baseline.xml +++ b/tests/end-to-end/_files/baseline/use-baseline/baseline.xml @@ -1,8 +1,21 @@ - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/end-to-end/_files/baseline/use-baseline/phpunit.xml b/tests/end-to-end/_files/baseline/use-baseline/phpunit.xml index bf0ee15c129..fd8b4b4f0bb 100644 --- a/tests/end-to-end/_files/baseline/use-baseline/phpunit.xml +++ b/tests/end-to-end/_files/baseline/use-baseline/phpunit.xml @@ -2,6 +2,7 @@ @@ -10,5 +11,8 @@ + + src + diff --git a/tests/end-to-end/_files/baseline/use-baseline/src/Source.php b/tests/end-to-end/_files/baseline/use-baseline/src/Source.php new file mode 100644 index 00000000000..a4a9779a0dc --- /dev/null +++ b/tests/end-to-end/_files/baseline/use-baseline/src/Source.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use const E_USER_DEPRECATED; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use function trigger_error; +use Serializable; + +final class Source +{ + public function triggerDeprecation(): void + { + $this->deprecation(); + } + + public function triggerNotice(): void + { + $this->notice(); + } + + public function triggerWarning(): void + { + $this->warning(); + } + + public function triggerPhpDeprecation(): void + { + $this->phpDeprecation(); + } + + public function triggerPhpNoticeAndWarning(): void + { + $this->phpNoticeAndWarning(); + } + + private function deprecation(): void + { + trigger_error('deprecation', E_USER_DEPRECATED); + } + + private function notice(): void + { + trigger_error('notice', E_USER_NOTICE); + } + + private function warning(): void + { + trigger_error('warning', E_USER_WARNING); + } + + private function phpDeprecation(): void + { + $o = new class implements Serializable + { + public function serialize(): void + { + } + + public function unserialize(string $data): void + { + } + }; + } + + private function phpNoticeAndWarning(): void + { + $o = new class + { + public static $a = 'b'; + }; + + $o->a; + } +} diff --git a/tests/end-to-end/_files/baseline/use-baseline/tests/SourceTest.php b/tests/end-to-end/_files/baseline/use-baseline/tests/SourceTest.php new file mode 100644 index 00000000000..fc4126239f9 --- /dev/null +++ b/tests/end-to-end/_files/baseline/use-baseline/tests/SourceTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Baseline; + +use PHPUnit\Framework\TestCase; + +final class SourceTest extends TestCase +{ + public function testDeprecation(): void + { + (new Source)->triggerDeprecation(); + + $this->assertTrue(true); + } + + public function testNotice(): void + { + (new Source)->triggerNotice(); + + $this->assertTrue(true); + } + + public function testWarning(): void + { + (new Source)->triggerWarning(); + + $this->assertTrue(true); + } + + public function testPhpDeprecation(): void + { + (new Source)->triggerPhpDeprecation(); + + $this->assertTrue(true); + } + + public function testPhpNoticeAndWarning(): void + { + (new Source)->triggerPhpNoticeAndWarning(); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/baseline/use-baseline/tests/Test.php b/tests/end-to-end/_files/baseline/use-baseline/tests/Test.php deleted file mode 100644 index d9ed86d8a59..00000000000 --- a/tests/end-to-end/_files/baseline/use-baseline/tests/Test.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Baseline; - -use function trigger_error; -use PHPUnit\Framework\TestCase; - -final class Test extends TestCase -{ - public function testOne(): void - { - trigger_error('deprecation', E_USER_DEPRECATED); - - $this->assertTrue(true); - } -} diff --git a/tests/end-to-end/_files/basic/SuccessTest.php b/tests/end-to-end/_files/basic/SuccessTest.php new file mode 100644 index 00000000000..5c3cbdd64d9 --- /dev/null +++ b/tests/end-to-end/_files/basic/SuccessTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Basic; + +use PHPUnit\Framework\TestCase; + +final class SuccessTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/basic/unit/StatusTest.php b/tests/end-to-end/_files/basic/unit/StatusTest.php index cff05f85421..ad3f7a85c0e 100644 --- a/tests/end-to-end/_files/basic/unit/StatusTest.php +++ b/tests/end-to-end/_files/basic/unit/StatusTest.php @@ -9,18 +9,17 @@ */ namespace PHPUnit\TestFixture\Basic; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\AnInterface; use RuntimeException; -/** - * @covers Foo - * - * @uses Bar - * - * @testdox Test result status with and without message - */ +#[CoversClass('Foo')] +#[UsesClass('Bar')] +#[TestDox('Test result status with and without message')] class StatusTest extends TestCase { public function testSuccess(): void diff --git a/tests/end-to-end/_files/filter-error-handler/filter-disabled.xml b/tests/end-to-end/_files/filter-error-handler/filter-disabled.xml index 25b8af58083..b5079e29e68 100644 --- a/tests/end-to-end/_files/filter-error-handler/filter-disabled.xml +++ b/tests/end-to-end/_files/filter-error-handler/filter-disabled.xml @@ -8,7 +8,7 @@ - + src diff --git a/tests/end-to-end/_files/filter-error-handler/filter-enabled.xml b/tests/end-to-end/_files/filter-error-handler/filter-enabled.xml index ec547bf58c6..7424e4393fb 100644 --- a/tests/end-to-end/_files/filter-error-handler/filter-enabled.xml +++ b/tests/end-to-end/_files/filter-error-handler/filter-enabled.xml @@ -8,7 +8,7 @@ - + src diff --git a/tests/end-to-end/_files/groups/tests/FooTest.php b/tests/end-to-end/_files/groups/tests/FooTest.php index 818255ee197..53850b69e97 100644 --- a/tests/end-to-end/_files/groups/tests/FooTest.php +++ b/tests/end-to-end/_files/groups/tests/FooTest.php @@ -9,21 +9,18 @@ */ namespace PHPUnit\TestFixture\Groups; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; final class FooTest extends TestCase { - /** - * @group one - */ + #[Group('one')] public function testOne(): void { $this->assertTrue(true); } - /** - * @group two - */ + #[Group('two')] public function testTwo(): void { $this->assertTrue(true); diff --git a/tests/end-to-end/_files/listing-tests-and-groups/ExampleTest.php b/tests/end-to-end/_files/listing-tests-and-groups/ExampleTest.php new file mode 100644 index 00000000000..d4f783d722f --- /dev/null +++ b/tests/end-to-end/_files/listing-tests-and-groups/ExampleTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\ListingTestsAndGroups; + +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; + +final class ExampleTest extends TestCase +{ + #[Group('one')] + public function testOne(): void + { + $this->assertTrue(true); + } + + #[Group('two')] + public function testTwo(): void + { + $this->assertTrue(true); + } + + #[Group('3')] + public function testThree(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/listing-tests-and-groups/example.phpt b/tests/end-to-end/_files/listing-tests-and-groups/example.phpt new file mode 100644 index 00000000000..412a451c6b7 --- /dev/null +++ b/tests/end-to-end/_files/listing-tests-and-groups/example.phpt @@ -0,0 +1,4 @@ +--TEST-- +This is an example PHPT test, this file is never executed! +--FILE-- +--EXPECT-- diff --git a/tests/end-to-end/_files/log-events-text/Test.php b/tests/end-to-end/_files/log-events-text/Test.php index 84afa2313a7..8c21fd3a13f 100644 --- a/tests/end-to-end/_files/log-events-text/Test.php +++ b/tests/end-to-end/_files/log-events-text/Test.php @@ -9,13 +9,45 @@ */ namespace PHPUnit\TestFixture\LogEventsText; +use function fopen; use PHPUnit\Framework\TestCase; use stdClass; final class Test extends TestCase { - public function testOne(): void + public function testExportNull(): void + { + $this->assertNull(null); + } + + public function testExportBool(): void + { + $this->assertTrue(true); + } + + public function testExportInt(): void + { + $this->assertSame(1, 1); + } + + public function testExportStr(): void + { + $this->assertSame('hello, world!', 'hello, world!'); + } + + public function testExportArray(): void + { + $arr = [1, 'foo' => 2]; + $this->assertSame($arr, $arr); + } + + public function testExportObject(): void { $this->assertSame(new stdClass, new stdClass); } + + public function testExportResource(): void + { + $this->assertSame(fopen('php://memory', 'rw+'), fopen('php://memory', 'rw+')); + } } diff --git a/tests/end-to-end/_files/multiple-testsuites/default-test-suite.xml b/tests/end-to-end/_files/multiple-testsuites/default-test-suite.xml new file mode 100644 index 00000000000..1fdf8a59230 --- /dev/null +++ b/tests/end-to-end/_files/multiple-testsuites/default-test-suite.xml @@ -0,0 +1,14 @@ + + + + + tests/unit + + + + tests/end-to-end + + + diff --git a/tests/end-to-end/_files/multiple-testsuites/exclude-group.xml b/tests/end-to-end/_files/multiple-testsuites/exclude-group.xml new file mode 100644 index 00000000000..ce0872c84e2 --- /dev/null +++ b/tests/end-to-end/_files/multiple-testsuites/exclude-group.xml @@ -0,0 +1,19 @@ + + + + + tests/unit + + + + tests/end-to-end + + + + + + default + + + diff --git a/tests/end-to-end/_files/multiple-testsuites/include-group.xml b/tests/end-to-end/_files/multiple-testsuites/include-group.xml new file mode 100644 index 00000000000..d96391fc5a5 --- /dev/null +++ b/tests/end-to-end/_files/multiple-testsuites/include-group.xml @@ -0,0 +1,19 @@ + + + + + tests/unit + + + + tests/end-to-end + + + + + + default + + + diff --git a/tests/end-to-end/_files/multiple-testsuites/phpunit.xml b/tests/end-to-end/_files/multiple-testsuites/phpunit.xml index 644cbe71e49..790b18bdfce 100644 --- a/tests/end-to-end/_files/multiple-testsuites/phpunit.xml +++ b/tests/end-to-end/_files/multiple-testsuites/phpunit.xml @@ -7,7 +7,7 @@ - tests/unit + tests/end-to-end diff --git a/tests/end-to-end/_files/no-log-cc-override/NoLogNoCcTest.php b/tests/end-to-end/_files/no-log-cc-override/NoLogNoCcTest.php index 295cf9eb076..6cb00740fb7 100644 --- a/tests/end-to-end/_files/no-log-cc-override/NoLogNoCcTest.php +++ b/tests/end-to-end/_files/no-log-cc-override/NoLogNoCcTest.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -/** - * @covers PHPUnit\TestFixture\NoLogNoCc - */ +#[CoversClass(NoLogNoCc::class)] final class NoLogNoCcTest extends TestCase { public function testSuccess(): void diff --git a/tests/end-to-end/_files/output-cli-help-color.txt b/tests/end-to-end/_files/output-cli-help-color.txt index a757c83be09..03e35cbe31f 100644 --- a/tests/end-to-end/_files/output-cli-help-color.txt +++ b/tests/end-to-end/_files/output-cli-help-color.txt @@ -3,166 +3,201 @@ Configuration: - --bootstrap   A PHP script that is included before the - tests run - -c|--configuration   Read configuration from XML file - --no-configuration  Ignore default configuration file - (phpunit.xml) - --no-extensions  Do not load PHPUnit extensions - --include-path   Prepend PHP's include_path with given - path(s) - -d   Sets a php.ini value - --cache-directory   Specify cache directory - --generate-configuration  Generate configuration file with suggested - settings - --migrate-configuration  Migrate configuration file to current format - --generate-baseline   Generate baseline for issues - --use-baseline   Use baseline to ignore issues - --ignore-baseline  Do not use baseline to ignore issues + --bootstrap   A PHP script that is included before the + tests run + -c|--configuration   Read configuration from XML file + --no-configuration  Ignore default configuration file + (phpunit.xml) + --extension   Register test runner extension with + bootstrap + --no-extensions  Do not register test runner extensions + --include-path   Prepend PHP's include_path with given + path(s) + -d   Sets a php.ini value + --cache-directory   Specify cache directory + --generate-configuration  Generate configuration file with suggested + settings + --migrate-configuration  Migrate configuration file to current + format + --generate-baseline   Generate baseline for issues + --use-baseline   Use baseline to ignore issues + --ignore-baseline  Do not use baseline to ignore issues Selection: - --list-suites  List available test suites - --testsuite   Only run tests from the specified test - suite(s) - --exclude-testsuite   Exclude tests from the specified test - suite(s) - --list-groups  List available test groups - --group   Only run tests from the specified group(s) - --exclude-group   Exclude tests from the specified group(s) - --covers   Only run tests that intend to cover - --uses   Only run tests that intend to use - --list-tests  List available tests - --list-tests-xml   List available tests in XML format - --filter   Filter which tests to run - --test-suffix   Only search for test in files with specified - suffix(es). Default: Test.php,.phpt + --list-suites  List available test suites + --testsuite   Only run tests from the specified test + suite(s) + --exclude-testsuite   Exclude tests from the specified test + suite(s) + --list-groups  List available test groups + --group   Only run tests from the specified group(s) + --exclude-group   Exclude tests from the specified group(s) + --covers   Only run tests that intend to cover + --uses   Only run tests that intend to use + --requires-php-extension   Only run tests that require PHP extension + + --list-test-files  List available test files + --list-tests  List available tests + --list-tests-xml   List available tests in XML format + --filter   Filter which tests to run + --exclude-filter   Exclude tests for the specified filter + pattern + --test-suffix   Only search for test in files with + specified suffix(es). Default: + Test.php,.phpt Execution: - --process-isolation  Run each test in a separate PHP process - --globals-backup  Backup and restore $GLOBALS for each test - --static-backup  Backup and restore static properties for - each test - - --strict-coverage  Be strict about code coverage metadata - --strict-global-state  Be strict about changes to global state - --disallow-test-output  Be strict about output during tests - --enforce-time-limit  Enforce time limit based on test size - --default-time-limit   Timeout in seconds for tests that have no - declared size - --dont-report-useless-tests  Do not report tests that do not test - anything - - --stop-on-defect  Stop after first error, failure, warning, or - risky test - --stop-on-error  Stop after first error - --stop-on-failure  Stop after first failure - --stop-on-warning  Stop after first warning - --stop-on-risky  Stop after first risky test - --stop-on-deprecation  Stop after first test that triggered a - deprecation - --stop-on-notice  Stop after first test that triggered a - notice - --stop-on-skipped  Stop after first skipped test - --stop-on-incomplete  Stop after first incomplete test - - --fail-on-warning  Signal failure using shell exit code when a - warning was triggered - --fail-on-risky  Signal failure using shell exit code when a - test was considered risky - --fail-on-deprecation  Signal failure using shell exit code when a - deprecation was triggered - --fail-on-notice  Signal failure using shell exit code when a - notice was triggered - --fail-on-skipped  Signal failure using shell exit code when a - test was skipped - --fail-on-incomplete  Signal failure using shell exit code when a - test was marked incomplete - - --cache-result  Write test results to cache file - --do-not-cache-result  Do not write test results to cache file - - --order-by   Run tests in order: - default|defects|depends|duration|no-depends|random|reverse|size - --random-order-seed   Use the specified random seed when running - tests in random order + --process-isolation  Run each test in a separate PHP process + --globals-backup  Backup and restore $GLOBALS for each test + --static-backup  Backup and restore static properties for + each test + + --strict-coverage  Be strict about code coverage metadata + --strict-global-state  Be strict about changes to global state + --disallow-test-output  Be strict about output during tests + --enforce-time-limit  Enforce time limit based on test size + --default-time-limit   Timeout in seconds for tests that have no + declared size + --dont-report-useless-tests  Do not report tests that do not test + anything + + --stop-on-defect  Stop after first error, failure, warning, + or risky test + --stop-on-error  Stop after first error + --stop-on-failure  Stop after first failure + --stop-on-warning  Stop after first warning + --stop-on-risky  Stop after first risky test + --stop-on-deprecation  Stop after first test that triggered a + deprecation + --stop-on-notice  Stop after first test that triggered a + notice + --stop-on-skipped  Stop after first skipped test + --stop-on-incomplete  Stop after first incomplete test + + --fail-on-empty-test-suite  Signal failure using shell exit code when + no tests were run + --fail-on-warning  Signal failure using shell exit code when + a warning was triggered + --fail-on-risky  Signal failure using shell exit code when + a test was considered risky + --fail-on-deprecation  Signal failure using shell exit code when + a deprecation was triggered + --fail-on-phpunit-deprecation  Signal failure using shell exit code when + a PHPUnit deprecation was triggered + --fail-on-phpunit-notice  Signal failure using shell exit code when + a PHPUnit notice was triggered + --fail-on-notice  Signal failure using shell exit code when + a notice was triggered + --fail-on-skipped  Signal failure using shell exit code when + a test was skipped + --fail-on-incomplete  Signal failure using shell exit code when + a test was marked incomplete + --fail-on-all-issues  Signal failure using shell exit code when + an issue is triggered + + --cache-result  Write test results to cache file + --do-not-cache-result  Do not write test results to cache file + + --order-by   Run tests in order: + default|defects|depends|duration|no-depends|random|reverse|size + --random-order-seed   Use the specified random seed when running + tests in random order Reporting: - --colors   Use colors in output ("never", "auto" or - "always") - --columns   Number of columns to use for progress output - --columns max  Use maximum number of columns for progress - output - --stderr  Write to STDERR instead of STDOUT - - --no-progress  Disable output of test execution progress - --no-results  Disable output of test results - --no-output  Disable all output - - --display-incomplete  Display details for incomplete tests - --display-skipped  Display details for skipped tests - --display-deprecations  Display details for deprecations triggered - by tests - --display-errors  Display details for errors triggered by - tests - --display-notices  Display details for notices triggered by - tests - --display-warnings  Display details for warnings triggered by - tests - --reverse-list  Print defects in reverse order - - --teamcity  Replace default progress and result output - with TeamCity format - --testdox  Replace default result output with TestDox - format + --colors   Use colors in output ("never", "auto" or + "always") + --columns   Number of columns to use for progress + output + --columns max  Use maximum number of columns for progress + output + --stderr  Write to STDERR instead of STDOUT + + --no-progress  Disable output of test execution progress + --no-results  Disable output of test results + --no-output  Disable all output + + --display-incomplete  Display details for incomplete tests + --display-skipped  Display details for skipped tests + --display-deprecations  Display details for deprecations triggered + by tests + --display-phpunit-deprecations  Display details for PHPUnit deprecations + --display-phpunit-notices  Display details for PHPUnit notices + --display-errors  Display details for errors triggered by + tests + --display-notices  Display details for notices triggered by + tests + --display-warnings  Display details for warnings triggered by + tests + --display-all-issues  Display details for all issues that are + triggered + --reverse-list  Print defects in reverse order + + --teamcity  Replace default progress and result output + with TeamCity format + --testdox  Replace default result output with TestDox + format + --testdox-summary  Repeat TestDox output for tests with + errors, failures, or issues + + --debug  Replace default progress and result output + with debugging information + --with-telemetry  Include telemetry information in debugging + information output Logging: - --log-junit   Write test results in JUnit XML format to - file - --log-teamcity   Write test results in TeamCity format to - file - --testdox-html   Write test results in TestDox format (HTML) - to file - --testdox-text   Write test results in TestDox format (plain - text) to file - --log-events-text   Stream events as plain text to file - --log-events-verbose-text  Stream events as plain text with extended - information to file - --no-logging  Ignore logging configured in the XML - configuration file + --log-junit   Write test results in JUnit XML format to + file + --log-teamcity   Write test results in TeamCity format to + file + --testdox-html   Write test results in TestDox format + (HTML) to file + --testdox-text   Write test results in TestDox format + (plain text) to file + --log-events-text   Stream events as plain text to file + --log-events-verbose-text   Stream events as plain text with extended + information to file + --no-logging  Ignore logging configured in the XML + configuration file Code Coverage: - --coverage-clover   Write code coverage report in Clover XML - format to file - --coverage-cobertura   Write code coverage report in Cobertura XML - format to file - --coverage-crap4j   Write code coverage report in Crap4J XML - format to file - --coverage-html   Write code coverage report in HTML format to - directory - --coverage-php   Write serialized code coverage data to file - --coverage-text=  Write code coverage report in text format to - file [default: standard output] - --coverage-xml   Write code coverage report in XML format to - directory - --warm-coverage-cache  Warm static analysis cache - --coverage-filter   Include in code coverage reporting - --path-coverage  Report path coverage in addition to line - coverage - --disable-coverage-ignore  Disable metadata for ignoring code coverage - --no-coverage  Ignore code coverage reporting configured in - the XML configuration file + --coverage-clover   Write code coverage report in Clover XML + format to file + --coverage-cobertura   Write code coverage report in Cobertura + XML format to file + --coverage-crap4j   Write code coverage report in Crap4J XML + format to file + --coverage-html   Write code coverage report in HTML format + to directory + --coverage-php   Write serialized code coverage data to + file + --coverage-text=  Write code coverage report in text format + to file [default: standard output] + --only-summary-for-coverage-text  Option for code coverage report in text + format: only show summary + --show-uncovered-for-coverage-text Option for code coverage report in text + format: show uncovered files + --coverage-xml   Write code coverage report in XML format + to directory + --warm-coverage-cache  Warm static analysis cache + --coverage-filter   Include in code coverage reporting + --path-coverage  Report path coverage in addition to line + coverage + --disable-coverage-ignore  Disable metadata for ignoring code + coverage + --no-coverage  Ignore code coverage reporting configured + in the XML configuration file Miscellaneous: - -h|--help  Prints this usage information - --version  Prints the version and exits - --atleast-version   Checks that version is greater than - and exits - --check-version  Check whether PHPUnit is the latest version - and exits + -h|--help  Prints this usage information + --version  Prints the version and exits + --atleast-version   Checks that version is greater than + and exits + --check-version  Checks whether PHPUnit is the latest + version and exits diff --git a/tests/end-to-end/_files/output-cli-usage.txt b/tests/end-to-end/_files/output-cli-usage.txt index f9305887d95..fe090a7bebe 100644 --- a/tests/end-to-end/_files/output-cli-usage.txt +++ b/tests/end-to-end/_files/output-cli-usage.txt @@ -5,120 +5,137 @@ Usage: Configuration: - --bootstrap A PHP script that is included before the tests run - -c|--configuration Read configuration from XML file - --no-configuration Ignore default configuration file (phpunit.xml) - --no-extensions Do not load PHPUnit extensions - --include-path Prepend PHP's include_path with given path(s) - -d Sets a php.ini value - --cache-directory Specify cache directory - --generate-configuration Generate configuration file with suggested settings - --migrate-configuration Migrate configuration file to current format - --generate-baseline Generate baseline for issues - --use-baseline Use baseline to ignore issues - --ignore-baseline Do not use baseline to ignore issues + --bootstrap A PHP script that is included before the tests run + -c|--configuration Read configuration from XML file + --no-configuration Ignore default configuration file (phpunit.xml) + --extension Register test runner extension with bootstrap + --no-extensions Do not register test runner extensions + --include-path Prepend PHP's include_path with given path(s) + -d Sets a php.ini value + --cache-directory Specify cache directory + --generate-configuration Generate configuration file with suggested settings + --migrate-configuration Migrate configuration file to current format + --generate-baseline Generate baseline for issues + --use-baseline Use baseline to ignore issues + --ignore-baseline Do not use baseline to ignore issues Selection: - --list-suites List available test suites - --testsuite Only run tests from the specified test suite(s) - --exclude-testsuite Exclude tests from the specified test suite(s) - --list-groups List available test groups - --group Only run tests from the specified group(s) - --exclude-group Exclude tests from the specified group(s) - --covers Only run tests that intend to cover - --uses Only run tests that intend to use - --list-tests List available tests - --list-tests-xml List available tests in XML format - --filter Filter which tests to run - --test-suffix Only search for test in files with specified suffix(es). Default: Test.php,.phpt + --list-suites List available test suites + --testsuite Only run tests from the specified test suite(s) + --exclude-testsuite Exclude tests from the specified test suite(s) + --list-groups List available test groups + --group Only run tests from the specified group(s) + --exclude-group Exclude tests from the specified group(s) + --covers Only run tests that intend to cover + --uses Only run tests that intend to use + --requires-php-extension Only run tests that require PHP extension + --list-test-files List available test files + --list-tests List available tests + --list-tests-xml List available tests in XML format + --filter Filter which tests to run + --exclude-filter Exclude tests for the specified filter pattern + --test-suffix Only search for test in files with specified suffix(es). Default: Test.php,.phpt Execution: - --process-isolation Run each test in a separate PHP process - --globals-backup Backup and restore $GLOBALS for each test - --static-backup Backup and restore static properties for each test - - --strict-coverage Be strict about code coverage metadata - --strict-global-state Be strict about changes to global state - --disallow-test-output Be strict about output during tests - --enforce-time-limit Enforce time limit based on test size - --default-time-limit Timeout in seconds for tests that have no declared size - --dont-report-useless-tests Do not report tests that do not test anything - - --stop-on-defect Stop after first error, failure, warning, or risky test - --stop-on-error Stop after first error - --stop-on-failure Stop after first failure - --stop-on-warning Stop after first warning - --stop-on-risky Stop after first risky test - --stop-on-deprecation Stop after first test that triggered a deprecation - --stop-on-notice Stop after first test that triggered a notice - --stop-on-skipped Stop after first skipped test - --stop-on-incomplete Stop after first incomplete test - - --fail-on-warning Signal failure using shell exit code when a warning was triggered - --fail-on-risky Signal failure using shell exit code when a test was considered risky - --fail-on-deprecation Signal failure using shell exit code when a deprecation was triggered - --fail-on-notice Signal failure using shell exit code when a notice was triggered - --fail-on-skipped Signal failure using shell exit code when a test was skipped - --fail-on-incomplete Signal failure using shell exit code when a test was marked incomplete - - --cache-result Write test results to cache file - --do-not-cache-result Do not write test results to cache file - - --order-by Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size - --random-order-seed Use the specified random seed when running tests in random order + --process-isolation Run each test in a separate PHP process + --globals-backup Backup and restore $GLOBALS for each test + --static-backup Backup and restore static properties for each test + + --strict-coverage Be strict about code coverage metadata + --strict-global-state Be strict about changes to global state + --disallow-test-output Be strict about output during tests + --enforce-time-limit Enforce time limit based on test size + --default-time-limit Timeout in seconds for tests that have no declared size + --dont-report-useless-tests Do not report tests that do not test anything + + --stop-on-defect Stop after first error, failure, warning, or risky test + --stop-on-error Stop after first error + --stop-on-failure Stop after first failure + --stop-on-warning Stop after first warning + --stop-on-risky Stop after first risky test + --stop-on-deprecation Stop after first test that triggered a deprecation + --stop-on-notice Stop after first test that triggered a notice + --stop-on-skipped Stop after first skipped test + --stop-on-incomplete Stop after first incomplete test + + --fail-on-empty-test-suite Signal failure using shell exit code when no tests were run + --fail-on-warning Signal failure using shell exit code when a warning was triggered + --fail-on-risky Signal failure using shell exit code when a test was considered risky + --fail-on-deprecation Signal failure using shell exit code when a deprecation was triggered + --fail-on-phpunit-deprecation Signal failure using shell exit code when a PHPUnit deprecation was triggered + --fail-on-phpunit-notice Signal failure using shell exit code when a PHPUnit notice was triggered + --fail-on-notice Signal failure using shell exit code when a notice was triggered + --fail-on-skipped Signal failure using shell exit code when a test was skipped + --fail-on-incomplete Signal failure using shell exit code when a test was marked incomplete + --fail-on-all-issues Signal failure using shell exit code when an issue is triggered + + --cache-result Write test results to cache file + --do-not-cache-result Do not write test results to cache file + + --order-by Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size + --random-order-seed Use the specified random seed when running tests in random order Reporting: - --colors Use colors in output ("never", "auto" or "always") - --columns Number of columns to use for progress output - --columns max Use maximum number of columns for progress output - --stderr Write to STDERR instead of STDOUT - - --no-progress Disable output of test execution progress - --no-results Disable output of test results - --no-output Disable all output - - --display-incomplete Display details for incomplete tests - --display-skipped Display details for skipped tests - --display-deprecations Display details for deprecations triggered by tests - --display-errors Display details for errors triggered by tests - --display-notices Display details for notices triggered by tests - --display-warnings Display details for warnings triggered by tests - --reverse-list Print defects in reverse order - - --teamcity Replace default progress and result output with TeamCity format - --testdox Replace default result output with TestDox format + --colors Use colors in output ("never", "auto" or "always") + --columns Number of columns to use for progress output + --columns max Use maximum number of columns for progress output + --stderr Write to STDERR instead of STDOUT + + --no-progress Disable output of test execution progress + --no-results Disable output of test results + --no-output Disable all output + + --display-incomplete Display details for incomplete tests + --display-skipped Display details for skipped tests + --display-deprecations Display details for deprecations triggered by tests + --display-phpunit-deprecations Display details for PHPUnit deprecations + --display-phpunit-notices Display details for PHPUnit notices + --display-errors Display details for errors triggered by tests + --display-notices Display details for notices triggered by tests + --display-warnings Display details for warnings triggered by tests + --display-all-issues Display details for all issues that are triggered + --reverse-list Print defects in reverse order + + --teamcity Replace default progress and result output with TeamCity format + --testdox Replace default result output with TestDox format + --testdox-summary Repeat TestDox output for tests with errors, failures, or issues + + --debug Replace default progress and result output with debugging information + --with-telemetry Include telemetry information in debugging information output Logging: - --log-junit Write test results in JUnit XML format to file - --log-teamcity Write test results in TeamCity format to file - --testdox-html Write test results in TestDox format (HTML) to file - --testdox-text Write test results in TestDox format (plain text) to file - --log-events-text Stream events as plain text to file - --log-events-verbose-text Stream events as plain text with extended information to file - --no-logging Ignore logging configured in the XML configuration file + --log-junit Write test results in JUnit XML format to file + --log-teamcity Write test results in TeamCity format to file + --testdox-html Write test results in TestDox format (HTML) to file + --testdox-text Write test results in TestDox format (plain text) to file + --log-events-text Stream events as plain text to file + --log-events-verbose-text Stream events as plain text with extended information to file + --no-logging Ignore logging configured in the XML configuration file Code Coverage: - --coverage-clover Write code coverage report in Clover XML format to file - --coverage-cobertura Write code coverage report in Cobertura XML format to file - --coverage-crap4j Write code coverage report in Crap4J XML format to file - --coverage-html Write code coverage report in HTML format to directory - --coverage-php Write serialized code coverage data to file - --coverage-text= Write code coverage report in text format to file [default: standard output] - --coverage-xml Write code coverage report in XML format to directory - --warm-coverage-cache Warm static analysis cache - --coverage-filter Include in code coverage reporting - --path-coverage Report path coverage in addition to line coverage - --disable-coverage-ignore Disable metadata for ignoring code coverage - --no-coverage Ignore code coverage reporting configured in the XML configuration file + --coverage-clover Write code coverage report in Clover XML format to file + --coverage-cobertura Write code coverage report in Cobertura XML format to file + --coverage-crap4j Write code coverage report in Crap4J XML format to file + --coverage-html Write code coverage report in HTML format to directory + --coverage-php Write serialized code coverage data to file + --coverage-text= Write code coverage report in text format to file [default: standard output] + --only-summary-for-coverage-text Option for code coverage report in text format: only show summary + --show-uncovered-for-coverage-text Option for code coverage report in text format: show uncovered files + --coverage-xml Write code coverage report in XML format to directory + --warm-coverage-cache Warm static analysis cache + --coverage-filter Include in code coverage reporting + --path-coverage Report path coverage in addition to line coverage + --disable-coverage-ignore Disable metadata for ignoring code coverage + --no-coverage Ignore code coverage reporting configured in the XML configuration file Miscellaneous: - -h|--help Prints this usage information - --version Prints the version and exits - --atleast-version Checks that version is greater than and exits - --check-version Check whether PHPUnit is the latest version and exits + -h|--help Prints this usage information + --version Prints the version and exits + --atleast-version Checks that version is greater than and exits + --check-version Checks whether PHPUnit is the latest version and exits diff --git a/tests/end-to-end/_files/overlapping-testsuite-configuration/phpunit.xml b/tests/end-to-end/_files/overlapping-testsuite-configuration/phpunit.xml new file mode 100644 index 00000000000..205407cfc12 --- /dev/null +++ b/tests/end-to-end/_files/overlapping-testsuite-configuration/phpunit.xml @@ -0,0 +1,14 @@ + + + + + tests + + + + tests + + + diff --git a/tests/end-to-end/_files/overlapping-testsuite-configuration/tests/ExampleTest.php b/tests/end-to-end/_files/overlapping-testsuite-configuration/tests/ExampleTest.php new file mode 100644 index 00000000000..20f1b56624e --- /dev/null +++ b/tests/end-to-end/_files/overlapping-testsuite-configuration/tests/ExampleTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\OverlappingTestSuiteConfiguration; + +use PHPUnit\Framework\TestCase; + +final class ExampleTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/phar-extension/phpunit.xml b/tests/end-to-end/_files/phar-extension/phpunit.xml index b8f869079ba..6fe1bb5596c 100644 --- a/tests/end-to-end/_files/phar-extension/phpunit.xml +++ b/tests/end-to-end/_files/phar-extension/phpunit.xml @@ -1,6 +1,6 @@ diff --git a/tests/end-to-end/_files/phar-extension/tools/phpunit.d/phpunit-test-extension-1.0.0.phar b/tests/end-to-end/_files/phar-extension/tools/phpunit.d/phpunit-test-extension-1.0.0.phar index 9656236231e..04c3ec94203 100644 Binary files a/tests/end-to-end/_files/phar-extension/tools/phpunit.d/phpunit-test-extension-1.0.0.phar and b/tests/end-to-end/_files/phar-extension/tools/phpunit.d/phpunit-test-extension-1.0.0.phar differ diff --git a/tests/end-to-end/_files/phpt-clean-process-polluting.phpt b/tests/end-to-end/_files/phpt-clean-process-polluting.phpt new file mode 100644 index 00000000000..59e6b94d245 --- /dev/null +++ b/tests/end-to-end/_files/phpt-clean-process-polluting.phpt @@ -0,0 +1,10 @@ +--TEST-- +PHPT test with a CLEAN section which pollutes process scope +--FILE-- + diff --git a/tests/end-to-end/_files/phpt-ini-subprocess.phpt b/tests/end-to-end/_files/phpt-ini-subprocess.phpt new file mode 100644 index 00000000000..e27d5824544 --- /dev/null +++ b/tests/end-to-end/_files/phpt-ini-subprocess.phpt @@ -0,0 +1,14 @@ +--TEST-- +PHPT uses a subprocess when --INI-- is present, even if --SKIPIF-- has no side-effect +--INI-- +error_reporting=-1 +--SKIPIF-- + +--EXPECT-- diff --git a/tests/end-to-end/_files/phpt-skipif-exit-subprocess.phpt b/tests/end-to-end/_files/phpt-skipif-exit-subprocess.phpt new file mode 100644 index 00000000000..c482f593222 --- /dev/null +++ b/tests/end-to-end/_files/phpt-skipif-exit-subprocess.phpt @@ -0,0 +1,9 @@ +--TEST-- +PHPT skip condition which exits the subprocess (side-effect) +--FILE-- + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PhpTSkipIfRequiredFile; + +class SomeClass +{ +} diff --git a/tests/end-to-end/_files/requires_environment_variable/SomeTest.php b/tests/end-to-end/_files/requires_environment_variable/SomeTest.php new file mode 100644 index 00000000000..a58c7ecd828 --- /dev/null +++ b/tests/end-to-end/_files/requires_environment_variable/SomeTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\requires_environment_variable; + +use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + #[RequiresEnvironmentVariable('FOO', 'bar')] + public function testShouldNotRunFOOHasWrongValue(): void + { + $this->fail(); + } + + #[RequiresEnvironmentVariable('BAR')] + public function testShouldNotRunBARIsEmpty(): void + { + $this->fail(); + } + + #[RequiresEnvironmentVariable('BAZ')] + public function testShouldNotRunBAZDoesNotExist(): void + { + $this->fail(); + } + + #[RequiresEnvironmentVariable('FOO')] + public function testOneShouldRun(): void + { + $this->assertTrue(true); + } + + #[RequiresEnvironmentVariable('FOO', '1')] + public function testTwoShouldRun(): void + { + $this->assertTrue(true); + } + + #[RequiresEnvironmentVariable('BAR', '')] + public function testThreeShouldRun(): void + { + $this->assertTrue(true); + } + + #[RunInSeparateProcess] + #[RequiresEnvironmentVariable('FOO', '1')] + public function testMustRunInSeparateProcess(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/requires_environment_variable/phpunit.xml b/tests/end-to-end/_files/requires_environment_variable/phpunit.xml new file mode 100644 index 00000000000..f5b185305f0 --- /dev/null +++ b/tests/end-to-end/_files/requires_environment_variable/phpunit.xml @@ -0,0 +1,16 @@ + + + + + SomeTest.php + + + + + + + + diff --git a/tests/end-to-end/_files/size-groups/SizeGroupsTest.php b/tests/end-to-end/_files/size-groups/SizeGroupsTest.php new file mode 100644 index 00000000000..5b70535f7c7 --- /dev/null +++ b/tests/end-to-end/_files/size-groups/SizeGroupsTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SizeGroups; + +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; + +#[Group('small')] +#[Group('medium')] +#[Group('large')] +final class SizeGroupsTest extends TestCase +{ + #[Group('small')] + #[Group('medium')] + #[Group('large')] + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/stop-on-fail-on/DeprecationTest.php b/tests/end-to-end/_files/stop-on-fail-on/DeprecationTest.php index 796bfa5325e..c43da60dc89 100644 --- a/tests/end-to-end/_files/stop-on-fail-on/DeprecationTest.php +++ b/tests/end-to-end/_files/stop-on-fail-on/DeprecationTest.php @@ -9,7 +9,9 @@ */ namespace PHPUnit\TestFixture\TestRunnerStopping; +use const E_USER_DEPRECATED; use function trigger_error; +use PHPUnit\Event\Facade as EventFacade; use PHPUnit\Framework\TestCase; final class DeprecationTest extends TestCase @@ -25,4 +27,14 @@ public function testTwo(): void { $this->assertTrue(true); } + + public function testThree(): void + { + $this->assertTrue(true); + + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + $this->valueObjectForEvents(), + 'message', + ); + } } diff --git a/tests/end-to-end/_files/stop-on-fail-on/NoticeTest.php b/tests/end-to-end/_files/stop-on-fail-on/NoticeTest.php index 0ab4a4a26ac..aa86486f51e 100644 --- a/tests/end-to-end/_files/stop-on-fail-on/NoticeTest.php +++ b/tests/end-to-end/_files/stop-on-fail-on/NoticeTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\TestRunnerStopping; +use const E_USER_NOTICE; use function trigger_error; use PHPUnit\Framework\TestCase; diff --git a/tests/end-to-end/_files/stop-on-fail-on/SkippedBeforeClassTest.php b/tests/end-to-end/_files/stop-on-fail-on/SkippedBeforeClassTest.php new file mode 100644 index 00000000000..d3b0ae50906 --- /dev/null +++ b/tests/end-to-end/_files/stop-on-fail-on/SkippedBeforeClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestRunnerStopping; + +use PHPUnit\Framework\TestCase; + +final class SkippedBeforeClassTest extends TestCase +{ + public static function setUpBeforeClass(): void + { + self::markTestSkipped('message'); + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/stop-on-fail-on/SpecificDeprecationTest.php b/tests/end-to-end/_files/stop-on-fail-on/SpecificDeprecationTest.php new file mode 100644 index 00000000000..c0190ca4a65 --- /dev/null +++ b/tests/end-to-end/_files/stop-on-fail-on/SpecificDeprecationTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestRunnerStopping; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class SpecificDeprecationTest extends TestCase +{ + public function testOne(): void + { + trigger_error('...foo...', E_USER_DEPRECATED); + + $this->assertTrue(true); + } + + public function testTwo(): void + { + trigger_error('...bar...', E_USER_DEPRECATED); + + $this->assertTrue(true); + } + + public function testThree(): void + { + trigger_error('...baz...', E_USER_DEPRECATED); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/_files/stop-on-fail-on/WarningTest.php b/tests/end-to-end/_files/stop-on-fail-on/WarningTest.php index 444eb346043..4b77ac2cbe2 100644 --- a/tests/end-to-end/_files/stop-on-fail-on/WarningTest.php +++ b/tests/end-to-end/_files/stop-on-fail-on/WarningTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\TestRunnerStopping; +use const E_USER_WARNING; use function trigger_error; use PHPUnit\Framework\TestCase; diff --git a/tests/end-to-end/_files/transform-exception-hook-method/phpunit.xml b/tests/end-to-end/_files/transform-exception-hook-method/phpunit.xml index 817f3d331d5..8bf3399168e 100644 --- a/tests/end-to-end/_files/transform-exception-hook-method/phpunit.xml +++ b/tests/end-to-end/_files/transform-exception-hook-method/phpunit.xml @@ -8,7 +8,7 @@ - + src diff --git a/tests/end-to-end/_files/with_environment_variable/WithEnvironmentVariableHookTest.php b/tests/end-to-end/_files/with_environment_variable/WithEnvironmentVariableHookTest.php new file mode 100644 index 00000000000..58d09c2e6a5 --- /dev/null +++ b/tests/end-to-end/_files/with_environment_variable/WithEnvironmentVariableHookTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\requires_environment_variable; + +use function getenv; +use PHPUnit\Framework\Attributes\WithEnvironmentVariable; +use PHPUnit\Framework\TestCase; + +#[WithEnvironmentVariable('FOO', 'foo')] +final class WithEnvironmentVariableHookTest extends TestCase +{ + public static function setUpBeforeClass(): void + { + self::assertEnvironmentVariablesHaveDefaultValues(); + } + + public static function tearDownAfterClass(): void + { + self::assertEnvironmentVariablesHaveDefaultValues(); + } + + protected function setUp(): void + { + $this->assertEnvironmentVariablesHaveCustomValues(); + } + + protected function tearDown(): void + { + $this->assertEnvironmentVariablesHaveCustomValues(); + } + + #[WithEnvironmentVariable('BAR', 'bar')] + public function testFOOShouldHaveValueErasedFromMethodAttribute(): void + { + $this->assertEnvironmentVariablesHaveCustomValues(); + } + + private function assertEnvironmentVariablesHaveCustomValues(): void + { + $this->assertSame('foo', $_ENV['FOO']); + $this->assertSame('foo', getenv('FOO')); + + $this->assertSame('bar', $_ENV['BAR']); + $this->assertSame('bar', getenv('BAR')); + } + + private static function assertEnvironmentVariablesHaveDefaultValues(): void + { + self::assertSame('1', $_ENV['FOO']); + self::assertSame('1', getenv('FOO')); + + self::assertSame('2', $_ENV['BAR']); + self::assertSame('2', getenv('BAR')); + } +} diff --git a/tests/end-to-end/_files/with_environment_variable/WithEnvironmentVariableTest.php b/tests/end-to-end/_files/with_environment_variable/WithEnvironmentVariableTest.php new file mode 100644 index 00000000000..7938ba64e7e --- /dev/null +++ b/tests/end-to-end/_files/with_environment_variable/WithEnvironmentVariableTest.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\requires_environment_variable; + +use function getenv; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\Attributes\WithEnvironmentVariable; +use PHPUnit\Framework\TestCase; + +#[WithEnvironmentVariable('FOO', 'foo')] +final class WithEnvironmentVariableTest extends TestCase +{ + public function testFOOShouldHaveValueFromClassAttribute1(): void + { + $this->assertSame('foo', $_ENV['FOO']); + $this->assertSame('foo', getenv('FOO')); + } + + #[WithEnvironmentVariable('FOO', 'bar')] + public function testFOOShouldHaveValueOverriddenFromMethodAttribute(): void + { + $this->assertSame('bar', $_ENV['FOO']); + $this->assertSame('bar', getenv('FOO')); + } + + #[Depends('testFOOShouldHaveValueOverriddenFromMethodAttribute')] + public function testFOOShouldHaveValueFromClassAttribute2(): void + { + $this->testFOOShouldHaveValueFromClassAttribute1(); + } + + #[WithEnvironmentVariable('FOO')] + public function testFOOShouldHaveValueErasedFromMethodAttribute(): void + { + $this->assertFalse(isset($_ENV['FOO'])); + $this->assertFalse(getenv('FOO')); + } + + #[Depends('testFOOShouldHaveValueErasedFromMethodAttribute')] + public function testFOOShouldHaveValueFromClassAttribute3(): void + { + $this->testFOOShouldHaveValueFromClassAttribute1(); + } + + #[WithEnvironmentVariable('BAR', 'bar')] + public function testBARShouldHaveValueFromMethodAttribute(): void + { + $this->assertSame('bar', $_ENV['BAR']); + $this->assertSame('bar', getenv('BAR')); + } + + #[Depends('testBARShouldHaveValueFromMethodAttribute')] + public function testBARShouldHaveBeenRestoredToDefaultValue1(): void + { + $this->assertSame('2', $_ENV['BAR']); + $this->assertSame('2', getenv('BAR')); + } + + #[WithEnvironmentVariable('BAR')] + public function testBARShouldHaveValueErasedFromMethodAttribute(): void + { + $this->assertFalse(isset($_ENV['BAR'])); + $this->assertFalse(getenv('BAR')); + } + + #[Depends('testBARShouldHaveValueErasedFromMethodAttribute')] + public function testBARShouldHaveBeenRestoredToDefaultValue2(): void + { + $this->testBARShouldHaveBeenRestoredToDefaultValue1(); + } + + #[WithEnvironmentVariable('BAZ', 'baz')] + public function testBAZShouldHaveValueFromMethodAttribute(): void + { + $this->assertSame('baz', $_ENV['BAZ']); + $this->assertSame('baz', getenv('BAZ')); + } + + #[Depends('testBAZShouldHaveValueFromMethodAttribute')] + public function testBAZShouldHaveBeenErased(): void + { + $this->assertFalse(isset($_ENV['BAZ'])); + $this->assertFalse(getenv('BAZ')); + } + + #[RunInSeparateProcess] + #[WithEnvironmentVariable('BAR', 'bar')] + public function testRunInSeparateProcess(): void + { + $this->assertSame('foo', $_ENV['FOO']); + $this->assertSame('foo', getenv('FOO')); + + $this->assertSame('bar', $_ENV['BAR']); + $this->assertSame('bar', getenv('BAR')); + } + + #[WithEnvironmentVariable('BAZ', '1')] + #[WithEnvironmentVariable('BAZ', '2')] + #[WithEnvironmentVariable('BAZ', '3')] + public function testMultipleAttributesKeepTheLastValue(): void + { + $this->assertSame('3', $_ENV['BAZ']); + $this->assertSame('3', getenv('BAZ')); + } + + #[WithEnvironmentVariable('BAZ', '1')] + #[RequiresEnvironmentVariable('BAZ', '1')] + public function testUsingAlongWithRequiresEnvironmentVariableAttribute(): void + { + $this->assertSame('1', $_ENV['BAZ']); + $this->assertSame('1', getenv('BAZ')); + } +} diff --git a/tests/end-to-end/_files/with_environment_variable/phpunit.xml b/tests/end-to-end/_files/with_environment_variable/phpunit.xml new file mode 100644 index 00000000000..aeed5d5c254 --- /dev/null +++ b/tests/end-to-end/_files/with_environment_variable/phpunit.xml @@ -0,0 +1,17 @@ + + + + + WithEnvironmentVariableTest.php + WithEnvironmentVariableHookTest.php + + + + + + + + diff --git a/tests/end-to-end/baseline/baseline-does-not-exist.phpt b/tests/end-to-end/baseline/baseline-does-not-exist.phpt index 913cec63d9a..355b61c4025 100644 --- a/tests/end-to-end/baseline/baseline-does-not-exist.phpt +++ b/tests/end-to-end/baseline/baseline-does-not-exist.phpt @@ -17,7 +17,7 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s -D 1 / 1 (100%) +. 1 / 1 (100%) Time: %s, Memory: %s @@ -26,4 +26,4 @@ There was 1 PHPUnit test runner warning: 1) Cannot read baseline %sdoes-not-exist.xml, file does not exist WARNINGS! -Tests: 1, Assertions: 1, Warnings: 1, Deprecations: 1. +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/baseline/baseline-invalid-xml.phpt b/tests/end-to-end/baseline/baseline-invalid-xml.phpt index d0a2e1013b5..4600a47f375 100644 --- a/tests/end-to-end/baseline/baseline-invalid-xml.phpt +++ b/tests/end-to-end/baseline/baseline-invalid-xml.phpt @@ -15,15 +15,13 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s -D 1 / 1 (100%) +. 1 / 1 (100%) Time: %s, Memory: %s There was 1 PHPUnit test runner warning: -1) Cannot read baseline: Could not load "%sbaseline.xml": - -%snd%sag%s +1) Cannot read baseline %sbaseline.xml: %s WARNINGS! -Tests: 1, Assertions: 1, Warnings: 1, Deprecations: 1. +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/baseline/generate-baseline-suppressed-with-ignored-suppression.phpt b/tests/end-to-end/baseline/generate-baseline-suppressed-with-ignored-suppression.phpt new file mode 100644 index 00000000000..3d7d271c60c --- /dev/null +++ b/tests/end-to-end/baseline/generate-baseline-suppressed-with-ignored-suppression.phpt @@ -0,0 +1,56 @@ +--TEST-- +phpunit --configuration ../_files/baseline/generate-baseline-suppressed-with-ignored-suppression/phpunit.xml --generate-baseline +--FILE-- +run($_SERVER['argv']); + +print file_get_contents($baseline); + +@unlink($baseline); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +DNWDW 5 / 5 (100%) + +Time: %s, Memory: %s + +OK, but there were issues! +Tests: 5, Assertions: 5, Warnings: 2, Deprecations: 2, Notices: 2. + +Baseline written to %sbaseline.xml. + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/end-to-end/baseline/generate-baseline-suppressed.phpt b/tests/end-to-end/baseline/generate-baseline-suppressed.phpt new file mode 100644 index 00000000000..183581c4266 --- /dev/null +++ b/tests/end-to-end/baseline/generate-baseline-suppressed.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --configuration ../_files/baseline/generate-baseline-suppressed/phpunit.xml --generate-baseline +--FILE-- +run($_SERVER['argv']); + +print file_get_contents($baseline); + +@unlink($baseline); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +..... 5 / 5 (100%) + +Time: %s, Memory: %s + +OK (5 tests, 5 assertions) + +Baseline written to %sbaseline.xml. + + diff --git a/tests/end-to-end/baseline/generate-baseline.phpt b/tests/end-to-end/baseline/generate-baseline.phpt index 70b6db3e955..aa5938ec4dd 100644 --- a/tests/end-to-end/baseline/generate-baseline.phpt +++ b/tests/end-to-end/baseline/generate-baseline.phpt @@ -8,7 +8,6 @@ $baseline = realpath($baseline); @unlink($baseline); $_SERVER['argv'][] = '--do-not-cache-result'; -$_SERVER['argv'][] = '--display-deprecations'; $_SERVER['argv'][] = '--generate-baseline'; $_SERVER['argv'][] = $baseline; $_SERVER['argv'][] = '--configuration'; @@ -26,29 +25,32 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s -D 1 / 1 (100%) +DNWDW 5 / 5 (100%) Time: %s, Memory: %s -1 test triggered 1 deprecation: - -1) %sTest.php:%d -deprecation - -Triggered by: - -* PHPUnit\TestFixture\Baseline\Test::testOne - %sTest.php:%d - OK, but there were issues! -Tests: 1, Assertions: 1, Deprecations: 1. +Tests: 5, Assertions: 5, Warnings: 2, Deprecations: 2, Notices: 2. Baseline written to %sbaseline.xml. - - + + + + + + + + + + + + + + + diff --git a/tests/end-to-end/baseline/ignore-baseline-testdox.phpt b/tests/end-to-end/baseline/ignore-baseline-testdox.phpt new file mode 100644 index 00000000000..3b501678ca4 --- /dev/null +++ b/tests/end-to-end/baseline/ignore-baseline-testdox.phpt @@ -0,0 +1,105 @@ +--TEST-- +phpunit --configuration ../_files/baseline/use-baseline/phpunit.xml --ignore-baseline --tes +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +DNWDW 5 / 5 (100%) + +Time: %s, Memory: %s + +Source (PHPUnit\TestFixture\Baseline\Source) + ⚠ Deprecation + ⚠ Notice + ⚠ Warning + ⚠ Php deprecation + ⚠ Php notice and warning + +1 test triggered 1 PHP warning: + +1) %sSource.php:81 +Undefined property: class@anonymous::$a + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testPhpNoticeAndWarning + %sSourceTest.php:44 + +-- + +1 test triggered 1 warning: + +1) %sSource.php:57 +warning + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testWarning + %sSourceTest.php:30 + +-- + +1 test triggered 1 PHP notice: + +1) %sSource.php:81 +Accessing static property class@anonymous::$a as non static + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testPhpNoticeAndWarning + %sSourceTest.php:44 + +-- + +1 test triggered 1 notice: + +1) %sSource.php:52 +notice + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testNotice + %sSourceTest.php:23 + +-- + +1 test triggered 1 PHP deprecation: + +1) %sSource.php:62 +Serializable@anonymous implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testPhpDeprecation + %sSourceTest.php:37 + +-- + +1 test triggered 1 deprecation: + +1) %sSource.php:47 +deprecation + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testDeprecation + %sSourceTest.php:16 + +OK, but there were issues! +Tests: 5, Assertions: 5, Warnings: 2, Deprecations: 2, Notices: 2. diff --git a/tests/end-to-end/baseline/ignore-baseline.phpt b/tests/end-to-end/baseline/ignore-baseline.phpt index a56345af0d3..d3e665662f0 100644 --- a/tests/end-to-end/baseline/ignore-baseline.phpt +++ b/tests/end-to-end/baseline/ignore-baseline.phpt @@ -7,6 +7,8 @@ $_SERVER['argv'][] = '--configuration'; $_SERVER['argv'][] = __DIR__ . '/../_files/baseline/use-baseline/phpunit.xml'; $_SERVER['argv'][] = '--ignore-baseline'; $_SERVER['argv'][] = '--display-deprecations'; +$_SERVER['argv'][] = '--display-notices'; +$_SERVER['argv'][] = '--display-warnings'; require_once __DIR__ . '/../../bootstrap.php'; @@ -17,19 +19,79 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s -D 1 / 1 (100%) +DNWDW 5 / 5 (100%) Time: %s, Memory: %s +1 test triggered 1 PHP warning: + +1) %sSource.php:81 +Undefined property: class@anonymous::$a + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testPhpNoticeAndWarning + %sSourceTest.php:44 + +-- + +1 test triggered 1 warning: + +1) %sSource.php:57 +warning + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testWarning + %sSourceTest.php:30 + +-- + +1 test triggered 1 PHP notice: + +1) %sSource.php:81 +Accessing static property class@anonymous::$a as non static + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testPhpNoticeAndWarning + %sSourceTest.php:44 + +-- + +1 test triggered 1 notice: + +1) %sSource.php:52 +notice + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testNotice + %sSourceTest.php:23 + +-- + +1 test triggered 1 PHP deprecation: + +1) %sSource.php:62 +Serializable@anonymous implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) + +Triggered by: + +* PHPUnit\TestFixture\Baseline\SourceTest::testPhpDeprecation + %sSourceTest.php:37 + +-- + 1 test triggered 1 deprecation: -1) %sTest.php:%d +1) %sSource.php:47 deprecation Triggered by: -* PHPUnit\TestFixture\Baseline\Test::testOne - %sTest.php:%d +* PHPUnit\TestFixture\Baseline\SourceTest::testDeprecation + %sSourceTest.php:16 OK, but there were issues! -Tests: 1, Assertions: 1, Deprecations: 1. +Tests: 5, Assertions: 5, Warnings: 2, Deprecations: 2, Notices: 2. diff --git a/tests/end-to-end/baseline/unsupported-baseline.phpt b/tests/end-to-end/baseline/unsupported-baseline.phpt index 75c59c28227..275fe901b08 100644 --- a/tests/end-to-end/baseline/unsupported-baseline.phpt +++ b/tests/end-to-end/baseline/unsupported-baseline.phpt @@ -15,7 +15,7 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s -D 1 / 1 (100%) +. 1 / 1 (100%) Time: %s, Memory: %s @@ -24,4 +24,4 @@ There was 1 PHPUnit test runner warning: 1) Cannot read baseline %sbaseline.xml, version 0 is not supported WARNINGS! -Tests: 1, Assertions: 1, Warnings: 1, Deprecations: 1. +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/baseline/use-baseline-testdox.phpt b/tests/end-to-end/baseline/use-baseline-testdox.phpt new file mode 100644 index 00000000000..3bd0e04d640 --- /dev/null +++ b/tests/end-to-end/baseline/use-baseline-testdox.phpt @@ -0,0 +1,32 @@ +--TEST-- +phpunit --configuration ../_files/baseline/use-baseline/phpunit.xml --testdox +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +..... 5 / 5 (100%) + +Time: %s, Memory: %s + +Source (PHPUnit\TestFixture\Baseline\Source) + ✔ Deprecation + ✔ Notice + ✔ Warning + ✔ Php deprecation + ✔ Php notice and warning + +OK (5 tests, 5 assertions) + +6 issues were ignored by baseline. diff --git a/tests/end-to-end/baseline/use-baseline.phpt b/tests/end-to-end/baseline/use-baseline.phpt index 5b38af2d187..6b2b333ed2f 100644 --- a/tests/end-to-end/baseline/use-baseline.phpt +++ b/tests/end-to-end/baseline/use-baseline.phpt @@ -15,10 +15,10 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s -. 1 / 1 (100%) +..... 5 / 5 (100%) Time: %s, Memory: %s -OK (1 test, 1 assertion) +OK (5 tests, 5 assertions) -1 issue was ignored by baseline. +6 issues were ignored by baseline. diff --git a/tests/end-to-end/cli/exclude-filter/match.phpt b/tests/end-to-end/cli/exclude-filter/match.phpt new file mode 100644 index 00000000000..c78875601bf --- /dev/null +++ b/tests/end-to-end/cli/exclude-filter/match.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --exclude-filter testThree ../../_files/groups/tests/FooTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (2 tests) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) +Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) +Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) +Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testTwo) +Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testTwo) +Test Passed (PHPUnit\TestFixture\Groups\FooTest::testTwo) +Test Finished (PHPUnit\TestFixture\Groups\FooTest::testTwo) +Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/fail-on/do-not-fail-on-empty-test-suite-by-default.phpt b/tests/end-to-end/cli/fail-on/do-not-fail-on-empty-test-suite-by-default.phpt new file mode 100644 index 00000000000..2a0dfae0479 --- /dev/null +++ b/tests/end-to-end/cli/fail-on/do-not-fail-on-empty-test-suite-by-default.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test Runner exits with shell exit code indicating success by default when no tests were run +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (0 tests) +Test Runner Execution Started (0 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/fail-on/fail-on-deprecation-output.phpt b/tests/end-to-end/cli/fail-on/fail-on-deprecation-output.phpt new file mode 100644 index 00000000000..811237e3e32 --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-deprecation-output.phpt @@ -0,0 +1,38 @@ +--TEST-- +Details for deprecations are displayed when --fail-on-deprecation is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +D.D 3 / 3 (100%) + +Time: %s, Memory: %s + +1 test triggered 1 PHPUnit deprecation: + +1) PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree +message + +%sDeprecationTest.php:%d + +-- + +1 test triggered 1 deprecation: + +1) %sDeprecationTest.php:%d +message + +OK, but there were issues! +Tests: 3, Assertions: 3, Deprecations: 1, PHPUnit Deprecations: 1. diff --git a/tests/end-to-end/cli/fail-on/fail-on-deprecation.phpt b/tests/end-to-end/cli/fail-on/fail-on-deprecation.phpt index 662bf15df36..6f182f63c96 100644 --- a/tests/end-to-end/cli/fail-on/fail-on-deprecation.phpt +++ b/tests/end-to-end/cli/fail-on/fail-on-deprecation.phpt @@ -1,51 +1,42 @@ --TEST-- Test Runner exits with shell exit code indicating failure when all tests are successful but at least one test triggered a deprecation ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (2 tests) -Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 2 tests) +Test Runner Execution Started (3 tests) +Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 3 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) -Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne, unknown if issue was triggered in first-party code or third-party code) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) -Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Triggered PHPUnit Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +message +Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 3 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 1) diff --git a/tests/end-to-end/cli/fail-on/fail-on-empty-test-suite.phpt b/tests/end-to-end/cli/fail-on/fail-on-empty-test-suite.phpt new file mode 100644 index 00000000000..e900ce18e87 --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-empty-test-suite.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test Runner exits with shell exit code indicating failure when no tests were run and --fail-on-empty-test-suite option is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (0 tests) +Test Runner Execution Started (0 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 1) diff --git a/tests/end-to-end/cli/fail-on/fail-on-incomplete-output.phpt b/tests/end-to-end/cli/fail-on/fail-on-incomplete-output.phpt new file mode 100644 index 00000000000..e22c73e8ea1 --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-incomplete-output.phpt @@ -0,0 +1,30 @@ +--TEST-- +Details for incomplete tests are displayed when --fail-on-incomplete is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +I. 2 / 2 (100%) + +Time: %s, Memory: %s + +There was 1 incomplete test: + +1) PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testOne +message + +%sIncompleteTest.php:%d + +OK, but there were issues! +Tests: 2, Assertions: 1, Incomplete: 1. diff --git a/tests/end-to-end/cli/fail-on/fail-on-incomplete.phpt b/tests/end-to-end/cli/fail-on/fail-on-incomplete.phpt index a0656539375..a80fb40a1d4 100644 --- a/tests/end-to-end/cli/fail-on/fail-on-incomplete.phpt +++ b/tests/end-to-end/cli/fail-on/fail-on-incomplete.phpt @@ -1,34 +1,21 @@ --TEST-- Test Runner exits with shell exit code indicating failure when all tests are successful but at least one test was marked incomplete ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -40,7 +27,6 @@ message Test Finished (PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testOne) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testTwo) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testTwo) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testTwo) Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest, 2 tests) diff --git a/tests/end-to-end/cli/fail-on/fail-on-notice-output.phpt b/tests/end-to-end/cli/fail-on/fail-on-notice-output.phpt new file mode 100644 index 00000000000..1662950e39f --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-notice-output.phpt @@ -0,0 +1,28 @@ +--TEST-- +Details for notices are displayed when --fail-on-notice is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +N. 2 / 2 (100%) + +Time: %s, Memory: %s + +1 test triggered 1 notice: + +1) %sNoticeTest.php:%d +message + +OK, but there were issues! +Tests: 2, Assertions: 2, Notices: 1. diff --git a/tests/end-to-end/cli/fail-on/fail-on-notice.phpt b/tests/end-to-end/cli/fail-on/fail-on-notice.phpt index ceadf6b4a9a..f9c1de85831 100644 --- a/tests/end-to-end/cli/fail-on/fail-on-notice.phpt +++ b/tests/end-to-end/cli/fail-on/fail-on-notice.phpt @@ -1,48 +1,33 @@ --TEST-- Test Runner exits with shell exit code indicating failure when all tests are successful but at least one test triggered a notice ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) -Test Triggered Notice (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) +Test Triggered Notice (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testTwo) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testTwo) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testTwo) Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest, 2 tests) diff --git a/tests/end-to-end/cli/fail-on/fail-on-phpunit-deprecation.phpt b/tests/end-to-end/cli/fail-on/fail-on-phpunit-deprecation.phpt new file mode 100644 index 00000000000..e7bbf6b3e0a --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-phpunit-deprecation.phpt @@ -0,0 +1,42 @@ +--TEST-- +Test Runner exits with shell exit code indicating failure when all tests are successful but at least one test triggered a PHPUnit deprecation +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (3 tests) +Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 3 tests) +Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) +Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne, unknown if issue was triggered in first-party code or third-party code) in %s:%d +message +Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) +Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) +Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) +Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) +Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo) +Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Triggered PHPUnit Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +message +Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree) +Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 3 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 1) diff --git a/tests/end-to-end/cli/fail-on/fail-on-risky.phpt b/tests/end-to-end/cli/fail-on/fail-on-risky.phpt index 9f4bca906f4..d7323df117a 100644 --- a/tests/end-to-end/cli/fail-on/fail-on-risky.phpt +++ b/tests/end-to-end/cli/fail-on/fail-on-risky.phpt @@ -1,34 +1,21 @@ --TEST-- Test Runner exits with shell exit code indicating failure when all tests are successful but at least one test was considered risky ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -41,7 +28,6 @@ This test did not perform any assertions Test Finished (PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testOne) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testTwo) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testTwo) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testTwo) Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\RiskyTest, 2 tests) diff --git a/tests/end-to-end/cli/fail-on/fail-on-skipped-output.phpt b/tests/end-to-end/cli/fail-on/fail-on-skipped-output.phpt new file mode 100644 index 00000000000..4e5155df1f8 --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-skipped-output.phpt @@ -0,0 +1,28 @@ +--TEST-- +Details for skipped tests are displayed when --fail-on-skipped is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +S. 2 / 2 (100%) + +Time: %s, Memory: %s + +There was 1 skipped test: + +1) PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testOne +message + +OK, but some tests were skipped! +Tests: 2, Assertions: 1, Skipped: 1. diff --git a/tests/end-to-end/cli/fail-on/fail-on-skipped.phpt b/tests/end-to-end/cli/fail-on/fail-on-skipped.phpt index 093ee6c4922..8978243ea19 100644 --- a/tests/end-to-end/cli/fail-on/fail-on-skipped.phpt +++ b/tests/end-to-end/cli/fail-on/fail-on-skipped.phpt @@ -1,34 +1,21 @@ --TEST-- Test Runner exits with shell exit code indicating failure when all tests are successful but at least one test was skipped ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -40,7 +27,6 @@ message Test Finished (PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testOne) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testTwo) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testTwo) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testTwo) Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\SkippedTest, 2 tests) diff --git a/tests/end-to-end/cli/fail-on/fail-on-warning-output.phpt b/tests/end-to-end/cli/fail-on/fail-on-warning-output.phpt new file mode 100644 index 00000000000..8513c5efc6d --- /dev/null +++ b/tests/end-to-end/cli/fail-on/fail-on-warning-output.phpt @@ -0,0 +1,28 @@ +--TEST-- +Details for warnings are displayed when --fail-on-warning is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +W. 2 / 2 (100%) + +Time: %s, Memory: %s + +1 test triggered 1 warning: + +1) %sWarningTest.php:%d +message + +OK, but there were issues! +Tests: 2, Assertions: 2, Warnings: 1. diff --git a/tests/end-to-end/cli/fail-on/fail-on-warning.phpt b/tests/end-to-end/cli/fail-on/fail-on-warning.phpt index 3fa279b7289..0d3c6783723 100644 --- a/tests/end-to-end/cli/fail-on/fail-on-warning.phpt +++ b/tests/end-to-end/cli/fail-on/fail-on-warning.phpt @@ -1,48 +1,33 @@ --TEST-- Test Runner exits with shell exit code indicating failure when all tests are successful but at least one warning was triggered ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) -Test Triggered Warning (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) +Test Triggered Warning (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testTwo) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testTwo) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testTwo) Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\WarningTest, 2 tests) diff --git a/tests/end-to-end/logging/failure-reverse-list.phpt b/tests/end-to-end/cli/failure-reverse-list.phpt similarity index 98% rename from tests/end-to-end/logging/failure-reverse-list.phpt rename to tests/end-to-end/cli/failure-reverse-list.phpt index 1291823f012..15898e27cd9 100644 --- a/tests/end-to-end/logging/failure-reverse-list.phpt +++ b/tests/end-to-end/cli/failure-reverse-list.phpt @@ -140,4 +140,4 @@ Failed asserting that two arrays are equal. %s:%d FAILURES! -Tests: 13, Assertions: 14, Failures: 13. +Tests: 13, Assertions: 15, Failures: 13. diff --git a/tests/end-to-end/cli/filter/filter-class-match-argument.phpt b/tests/end-to-end/cli/filter/filter-class-match-argument.phpt index 4ca435d14b1..0f8f07a1750 100644 --- a/tests/end-to-end/cli/filter/filter-class-match-argument.phpt +++ b/tests/end-to-end/cli/filter/filter-class-match-argument.phpt @@ -1,19 +1,10 @@ --TEST-- phpunit --filter FooTest tests/FooTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (3 tests) @@ -37,17 +24,14 @@ Test Runner Execution Started (3 tests) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 3 tests) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testTwo) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testTwo) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testTwo) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testThree) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 3 tests) diff --git a/tests/end-to-end/cli/filter/filter-class-match-configuration.phpt b/tests/end-to-end/cli/filter/filter-class-match-configuration.phpt index 7eb643a4533..17e98448355 100644 --- a/tests/end-to-end/cli/filter/filter-class-match-configuration.phpt +++ b/tests/end-to-end/cli/filter/filter-class-match-configuration.phpt @@ -1,60 +1,44 @@ --TEST-- phpunit --filter FooTest ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (3 tests) Test Runner Execution Started (3 tests) -Test Suite Started (%s/tests/end-to-end/_files/groups/phpunit.xml, 3 tests) +Test Suite Started (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 3 tests) Test Suite Started (default, 3 tests) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 3 tests) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testTwo) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testTwo) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testTwo) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testThree) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 3 tests) Test Suite Finished (default, 3 tests) -Test Suite Finished (%s/tests/end-to-end/_files/groups/phpunit.xml, 3 tests) +Test Suite Finished (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 3 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/filter/filter-class-nomatch-argument.phpt b/tests/end-to-end/cli/filter/filter-class-nomatch-argument.phpt index 9acb98208de..be71939651b 100644 --- a/tests/end-to-end/cli/filter/filter-class-nomatch-argument.phpt +++ b/tests/end-to-end/cli/filter/filter-class-nomatch-argument.phpt @@ -1,19 +1,10 @@ --TEST-- phpunit --filter BarTest tests/FooTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (0 tests) diff --git a/tests/end-to-end/cli/filter/filter-class-nomatch-configuration.phpt b/tests/end-to-end/cli/filter/filter-class-nomatch-configuration.phpt index dfe4fc19c25..23a7967a205 100644 --- a/tests/end-to-end/cli/filter/filter-class-nomatch-configuration.phpt +++ b/tests/end-to-end/cli/filter/filter-class-nomatch-configuration.phpt @@ -1,35 +1,22 @@ --TEST-- phpunit --filter BarTest ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (0 tests) diff --git a/tests/end-to-end/cli/filter/filter-method-match-argument.phpt b/tests/end-to-end/cli/filter/filter-method-match-argument.phpt index 21d8e5f9671..8fc0f1b4a58 100644 --- a/tests/end-to-end/cli/filter/filter-method-match-argument.phpt +++ b/tests/end-to-end/cli/filter/filter-method-match-argument.phpt @@ -1,19 +1,10 @@ --TEST-- phpunit --filter testOne tests/FooTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (1 test) @@ -37,7 +24,6 @@ Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 1 test) diff --git a/tests/end-to-end/cli/filter/filter-method-match-configuration.phpt b/tests/end-to-end/cli/filter/filter-method-match-configuration.phpt index 792ec1f4009..f74ff0d5d68 100644 --- a/tests/end-to-end/cli/filter/filter-method-match-configuration.phpt +++ b/tests/end-to-end/cli/filter/filter-method-match-configuration.phpt @@ -1,50 +1,36 @@ --TEST-- phpunit --filter testOne ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (1 test) Test Runner Execution Started (1 test) -Test Suite Started (%s/tests/end-to-end/_files/groups/phpunit.xml, 1 test) +Test Suite Started (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 1 test) Test Suite Started (default, 1 test) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Suite Finished (default, 1 test) -Test Suite Finished (%s/tests/end-to-end/_files/groups/phpunit.xml, 1 test) +Test Suite Finished (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 1 test) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/filter/filter-method-nomatch-argument.phpt b/tests/end-to-end/cli/filter/filter-method-nomatch-argument.phpt index c8b21dc5111..cd052a99bd3 100644 --- a/tests/end-to-end/cli/filter/filter-method-nomatch-argument.phpt +++ b/tests/end-to-end/cli/filter/filter-method-nomatch-argument.phpt @@ -1,19 +1,10 @@ --TEST-- phpunit --filter testFoo tests/FooTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (0 tests) diff --git a/tests/end-to-end/cli/filter/filter-method-nomatch-configuration.phpt b/tests/end-to-end/cli/filter/filter-method-nomatch-configuration.phpt index aecbd9b4165..f1b8168a91f 100644 --- a/tests/end-to-end/cli/filter/filter-method-nomatch-configuration.phpt +++ b/tests/end-to-end/cli/filter/filter-method-nomatch-configuration.phpt @@ -1,35 +1,22 @@ --TEST-- phpunit --filter testFoo ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (0 tests) diff --git a/tests/end-to-end/cli/group/covers-class.phpt b/tests/end-to-end/cli/group/covers-class.phpt new file mode 100644 index 00000000000..a573705ec4a --- /dev/null +++ b/tests/end-to-end/cli/group/covers-class.phpt @@ -0,0 +1,34 @@ +--TEST-- +phpunit --covers PHPUnit\TestFixture\AttributeBasedFiltering\Foo +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (5 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (CLI Arguments, 1 test) +Test Suite Started (PHPUnit\TestFixture\AttributeBasedFiltering\CoversClassTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\AttributeBasedFiltering\CoversClassTest::testOne) +Test Prepared (PHPUnit\TestFixture\AttributeBasedFiltering\CoversClassTest::testOne) +Test Passed (PHPUnit\TestFixture\AttributeBasedFiltering\CoversClassTest::testOne) +Test Finished (PHPUnit\TestFixture\AttributeBasedFiltering\CoversClassTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\AttributeBasedFiltering\CoversClassTest, 1 test) +Test Suite Finished (CLI Arguments, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/group/covers-function.phpt b/tests/end-to-end/cli/group/covers-function.phpt new file mode 100644 index 00000000000..44b894c98b2 --- /dev/null +++ b/tests/end-to-end/cli/group/covers-function.phpt @@ -0,0 +1,34 @@ +--TEST-- +phpunit --covers PHPUnit\TestFixture\AttributeBasedFiltering\f +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (5 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (CLI Arguments, 1 test) +Test Suite Started (PHPUnit\TestFixture\AttributeBasedFiltering\CoversFunctionTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\AttributeBasedFiltering\CoversFunctionTest::testOne) +Test Prepared (PHPUnit\TestFixture\AttributeBasedFiltering\CoversFunctionTest::testOne) +Test Passed (PHPUnit\TestFixture\AttributeBasedFiltering\CoversFunctionTest::testOne) +Test Finished (PHPUnit\TestFixture\AttributeBasedFiltering\CoversFunctionTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\AttributeBasedFiltering\CoversFunctionTest, 1 test) +Test Suite Finished (CLI Arguments, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/group/exclude-group-argument.phpt b/tests/end-to-end/cli/group/exclude-group-argument.phpt index 35340b4830e..81e0027bed9 100644 --- a/tests/end-to-end/cli/group/exclude-group-argument.phpt +++ b/tests/end-to-end/cli/group/exclude-group-argument.phpt @@ -1,35 +1,24 @@ --TEST-- phpunit --exclude-group one,two tests/FooTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (1 test) @@ -37,7 +26,6 @@ Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testThree) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 1 test) diff --git a/tests/end-to-end/cli/group/exclude-group-configuration.phpt b/tests/end-to-end/cli/group/exclude-group-configuration.phpt index 559801e0c5a..cd3cb8f911b 100644 --- a/tests/end-to-end/cli/group/exclude-group-configuration.phpt +++ b/tests/end-to-end/cli/group/exclude-group-configuration.phpt @@ -1,50 +1,38 @@ --TEST-- phpunit --exclude-group one,two ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (1 test) Test Runner Execution Started (1 test) -Test Suite Started (%s/tests/end-to-end/_files/groups/phpunit.xml, 1 test) +Test Suite Started (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 1 test) Test Suite Started (default, 1 test) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testThree) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testThree) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Suite Finished (default, 1 test) -Test Suite Finished (%s/tests/end-to-end/_files/groups/phpunit.xml, 1 test) +Test Suite Finished (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 1 test) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/group/group-argument.phpt b/tests/end-to-end/cli/group/group-argument.phpt index 90a1be21b31..8977194b861 100644 --- a/tests/end-to-end/cli/group/group-argument.phpt +++ b/tests/end-to-end/cli/group/group-argument.phpt @@ -1,19 +1,10 @@ --TEST-- phpunit --group one tests/FooTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (1 test) @@ -37,7 +24,6 @@ Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 1 test) diff --git a/tests/end-to-end/cli/group/group-configuration.phpt b/tests/end-to-end/cli/group/group-configuration.phpt index 4a14c907fe5..f2dfaf744fc 100644 --- a/tests/end-to-end/cli/group/group-configuration.phpt +++ b/tests/end-to-end/cli/group/group-configuration.phpt @@ -1,50 +1,36 @@ --TEST-- phpunit --group one ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Suite Filtered (1 test) Test Runner Execution Started (1 test) -Test Suite Started (%s/tests/end-to-end/_files/groups/phpunit.xml, 1 test) +Test Suite Started (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 1 test) Test Suite Started (default, 1 test) Test Suite Started (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Prepared (PHPUnit\TestFixture\Groups\FooTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Finished (PHPUnit\TestFixture\Groups\FooTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Groups\FooTest, 1 test) Test Suite Finished (default, 1 test) -Test Suite Finished (%s/tests/end-to-end/_files/groups/phpunit.xml, 1 test) +Test Suite Finished (%s%etests%eend-to-end%e_files%egroups%ephpunit.xml, 1 test) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/group/list-groups-argument.phpt b/tests/end-to-end/cli/group/list-groups-argument.phpt deleted file mode 100644 index 45b8e349cfd..00000000000 --- a/tests/end-to-end/cli/group/list-groups-argument.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -phpunit --list-groups tests/FooTest.php ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Available test group(s): - - default - - one - - two diff --git a/tests/end-to-end/cli/group/list-groups-configuration.phpt b/tests/end-to-end/cli/group/list-groups-configuration.phpt deleted file mode 100644 index 8bd18080f1c..00000000000 --- a/tests/end-to-end/cli/group/list-groups-configuration.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -phpunit --list-groups ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Available test group(s): - - default - - one - - two diff --git a/tests/end-to-end/cli/group/requires-php-extension.phpt b/tests/end-to-end/cli/group/requires-php-extension.phpt new file mode 100644 index 00000000000..4d2e66f33c2 --- /dev/null +++ b/tests/end-to-end/cli/group/requires-php-extension.phpt @@ -0,0 +1,34 @@ +--TEST-- +phpunit --requires-php-extension standard +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (5 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (CLI Arguments, 1 test) +Test Suite Started (PHPUnit\TestFixture\AttributeBasedFiltering\RequiresPhpExtensionTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\AttributeBasedFiltering\RequiresPhpExtensionTest::testOne) +Test Prepared (PHPUnit\TestFixture\AttributeBasedFiltering\RequiresPhpExtensionTest::testOne) +Test Passed (PHPUnit\TestFixture\AttributeBasedFiltering\RequiresPhpExtensionTest::testOne) +Test Finished (PHPUnit\TestFixture\AttributeBasedFiltering\RequiresPhpExtensionTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\AttributeBasedFiltering\RequiresPhpExtensionTest, 1 test) +Test Suite Finished (CLI Arguments, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/group/size-groups.phpt b/tests/end-to-end/cli/group/size-groups.phpt new file mode 100644 index 00000000000..731c4a25cc6 --- /dev/null +++ b/tests/end-to-end/cli/group/size-groups.phpt @@ -0,0 +1,37 @@ +--TEST-- +phpunit --group small,medium,large ../../_files/size-groups/SizeGroupsTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +There were 6 PHPUnit test runner warnings: + +1) Group name "small" is not allowed for class PHPUnit\TestFixture\SizeGroups\SizeGroupsTest + +2) Group name "medium" is not allowed for class PHPUnit\TestFixture\SizeGroups\SizeGroupsTest + +3) Group name "large" is not allowed for class PHPUnit\TestFixture\SizeGroups\SizeGroupsTest + +4) Group name "small" is not allowed for method PHPUnit\TestFixture\SizeGroups\SizeGroupsTest::testOne + +5) Group name "medium" is not allowed for method PHPUnit\TestFixture\SizeGroups\SizeGroupsTest::testOne + +6) Group name "large" is not allowed for method PHPUnit\TestFixture\SizeGroups\SizeGroupsTest::testOne + +No tests executed! diff --git a/tests/end-to-end/cli/group/uses-class.phpt b/tests/end-to-end/cli/group/uses-class.phpt new file mode 100644 index 00000000000..a2631cdf3b0 --- /dev/null +++ b/tests/end-to-end/cli/group/uses-class.phpt @@ -0,0 +1,34 @@ +--TEST-- +phpunit --uses PHPUnit\TestFixture\AttributeBasedFiltering\Foo +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (5 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (CLI Arguments, 1 test) +Test Suite Started (PHPUnit\TestFixture\AttributeBasedFiltering\UsesTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\AttributeBasedFiltering\UsesTest::testOne) +Test Prepared (PHPUnit\TestFixture\AttributeBasedFiltering\UsesTest::testOne) +Test Passed (PHPUnit\TestFixture\AttributeBasedFiltering\UsesTest::testOne) +Test Finished (PHPUnit\TestFixture\AttributeBasedFiltering\UsesTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\AttributeBasedFiltering\UsesTest, 1 test) +Test Suite Finished (CLI Arguments, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/group/uses-function.phpt b/tests/end-to-end/cli/group/uses-function.phpt new file mode 100644 index 00000000000..2b71c3956bb --- /dev/null +++ b/tests/end-to-end/cli/group/uses-function.phpt @@ -0,0 +1,34 @@ +--TEST-- +phpunit --covers PHPUnit\TestFixture\AttributeBasedFiltering\f +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (5 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (CLI Arguments, 1 test) +Test Suite Started (PHPUnit\TestFixture\AttributeBasedFiltering\UsesFunctionTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\AttributeBasedFiltering\UsesFunctionTest::testOne) +Test Prepared (PHPUnit\TestFixture\AttributeBasedFiltering\UsesFunctionTest::testOne) +Test Passed (PHPUnit\TestFixture\AttributeBasedFiltering\UsesFunctionTest::testOne) +Test Finished (PHPUnit\TestFixture\AttributeBasedFiltering\UsesFunctionTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\AttributeBasedFiltering\UsesFunctionTest, 1 test) +Test Suite Finished (CLI Arguments, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/list-suites.phpt b/tests/end-to-end/cli/list-suites.phpt deleted file mode 100644 index fbad1738bb7..00000000000 --- a/tests/end-to-end/cli/list-suites.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -phpunit --list-suites --configuration=__DIR__.'/../_files/configuration.suites.xml' ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Available test suite(s): - - Suite One - - Suite Two diff --git a/tests/end-to-end/cli/list-suites/happy-path.phpt b/tests/end-to-end/cli/list-suites/happy-path.phpt new file mode 100644 index 00000000000..82c006799d8 --- /dev/null +++ b/tests/end-to-end/cli/list-suites/happy-path.phpt @@ -0,0 +1,18 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/phpunit.xml --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test suites: + - end-to-end (1 test) + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-default-test-suite.phpt b/tests/end-to-end/cli/list-suites/warning-default-test-suite.phpt new file mode 100644 index 00000000000..bdeb919c87c --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-default-test-suite.phpt @@ -0,0 +1,19 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/default-test-suite.xml --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The defaultTestSuite (XML) and --list-suites (CLI) options cannot be combined, only the default test suite is shown + +Available test suite: + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-exclude-groups-cli.phpt b/tests/end-to-end/cli/list-suites/warning-exclude-groups-cli.phpt new file mode 100644 index 00000000000..c1f2dc76849 --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-exclude-groups-cli.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/phpunit.xml --exclude-group default --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The --exclude-group (CLI) and (XML) options cannot be combined with --list-suites, --exclude-group and are ignored + +Available test suites: + - end-to-end (1 test) + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-exclude-groups-xml.phpt b/tests/end-to-end/cli/list-suites/warning-exclude-groups-xml.phpt new file mode 100644 index 00000000000..afcd824bab9 --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-exclude-groups-xml.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/exclude-group.xml --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The --exclude-group (CLI) and (XML) options cannot be combined with --list-suites, --exclude-group and are ignored + +Available test suites: + - end-to-end (1 test) + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-filter.phpt b/tests/end-to-end/cli/list-suites/warning-filter.phpt new file mode 100644 index 00000000000..15f993a0c65 --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-filter.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/phpunit.xml --filter FooTest --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The --filter and --list-suites options cannot be combined, --filter is ignored + +Available test suites: + - end-to-end (1 test) + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-include-groups-cli.phpt b/tests/end-to-end/cli/list-suites/warning-include-groups-cli.phpt new file mode 100644 index 00000000000..5d9b556c0e3 --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-include-groups-cli.phpt @@ -0,0 +1,22 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/phpunit.xml --group default --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The --group (CLI) and (XML) options cannot be combined with --list-suites, --group and are ignored + +Available test suites: + - end-to-end (1 test) + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-include-groups-xml.phpt b/tests/end-to-end/cli/list-suites/warning-include-groups-xml.phpt new file mode 100644 index 00000000000..e22262cfd85 --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-include-groups-xml.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/include-group.xml --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The --group (CLI) and (XML) options cannot be combined with --list-suites, --group and are ignored + +Available test suites: + - end-to-end (1 test) + - unit (1 test) diff --git a/tests/end-to-end/cli/list-suites/warning-testsuite.phpt b/tests/end-to-end/cli/list-suites/warning-testsuite.phpt new file mode 100644 index 00000000000..6c39e1ed63c --- /dev/null +++ b/tests/end-to-end/cli/list-suites/warning-testsuite.phpt @@ -0,0 +1,21 @@ +--TEST-- +phpunit --configuration ../_files/multiple-testsuites/phpunit.xml --testsuite unit --list-suites +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The --testsuite and --list-suites options cannot be combined, --testsuite is ignored + +Available test suite: + - unit (1 test) diff --git a/tests/end-to-end/cli/list-test-files/list-test-files-exclude-group.phpt b/tests/end-to-end/cli/list-test-files/list-test-files-exclude-group.phpt new file mode 100644 index 00000000000..f6a2a36e873 --- /dev/null +++ b/tests/end-to-end/cli/list-test-files/list-test-files-exclude-group.phpt @@ -0,0 +1,18 @@ +--TEST-- +phpunit --list-test-files --exclude-group one ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test files: + - %slisting-tests-and-groups%sExampleTest.php + - %slisting-tests-and-groups%sexample.phpt diff --git a/tests/end-to-end/cli/list-test-files/list-test-files-group.phpt b/tests/end-to-end/cli/list-test-files/list-test-files-group.phpt new file mode 100644 index 00000000000..851eff96586 --- /dev/null +++ b/tests/end-to-end/cli/list-test-files/list-test-files-group.phpt @@ -0,0 +1,17 @@ +--TEST-- +phpunit --list-test-files --group one ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test files: + - %slisting-tests-and-groups%sExampleTest.php diff --git a/tests/end-to-end/cli/list-test-files/list-test-files.phpt b/tests/end-to-end/cli/list-test-files/list-test-files.phpt new file mode 100644 index 00000000000..7ef216de7bf --- /dev/null +++ b/tests/end-to-end/cli/list-test-files/list-test-files.phpt @@ -0,0 +1,16 @@ +--TEST-- +phpunit --list-test-files ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test files: + - %slisting-tests-and-groups%sExampleTest.php + - %slisting-tests-and-groups%sexample.phpt diff --git a/tests/end-to-end/cli/list-tests/list-tests-dataprovider-filter.phpt b/tests/end-to-end/cli/list-tests/list-tests-dataprovider-filter.phpt deleted file mode 100644 index be00d0536aa..00000000000 --- a/tests/end-to-end/cli/list-tests/list-tests-dataprovider-filter.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -phpunit --list-tests --filter testAdd#0 ../../../_files/DataProviderTest.php ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -The --filter and --list-tests options cannot be combined, --filter is ignored - -Available test(s): - - PHPUnit\TestFixture\DataProviderTest::testAdd#0 - - PHPUnit\TestFixture\DataProviderTest::testAdd#1 - - PHPUnit\TestFixture\DataProviderTest::testAdd#2 - - PHPUnit\TestFixture\DataProviderTest::testAdd#3 diff --git a/tests/end-to-end/cli/list-tests/list-tests-dataprovider.phpt b/tests/end-to-end/cli/list-tests/list-tests-dataprovider.phpt deleted file mode 100644 index bf7f340fefa..00000000000 --- a/tests/end-to-end/cli/list-tests/list-tests-dataprovider.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -phpunit --list-tests ../../../_files/DataProviderTest.php ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Available test(s): - - PHPUnit\TestFixture\DataProviderTest::testAdd#0 - - PHPUnit\TestFixture\DataProviderTest::testAdd#1 - - PHPUnit\TestFixture\DataProviderTest::testAdd#2 - - PHPUnit\TestFixture\DataProviderTest::testAdd#3 diff --git a/tests/end-to-end/cli/list-tests/list-tests-xml-dataprovider.phpt b/tests/end-to-end/cli/list-tests/list-tests-xml-dataprovider.phpt deleted file mode 100644 index 2b55f38d923..00000000000 --- a/tests/end-to-end/cli/list-tests/list-tests-xml-dataprovider.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -phpunit --list-tests-xml ../../../_files/DataProviderTest.php ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - - - - - - - - - -%A diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-groups-exclude-filter.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-exclude-filter.phpt new file mode 100644 index 00000000000..89827356532 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-exclude-filter.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --exclude-filter testOne --list-groups ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test groups: + - 3 (1 test) + - two (1 test) diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-groups-exclude-group.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-exclude-group.phpt new file mode 100644 index 00000000000..ee4abaffdbc --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-exclude-group.phpt @@ -0,0 +1,21 @@ +--TEST-- +phpunit --exclude-group one --list-groups ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test groups: + - 3 (1 test) + - default (1 test) + - two (1 test) diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-groups-include-filter.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-include-filter.phpt new file mode 100644 index 00000000000..89827356532 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-include-filter.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --exclude-filter testOne --list-groups ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test groups: + - 3 (1 test) + - two (1 test) diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-groups-include-group.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-include-group.phpt new file mode 100644 index 00000000000..483cf202b35 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-groups-include-group.phpt @@ -0,0 +1,19 @@ +--TEST-- +phpunit --group one --list-groups ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test group: + - one (1 test) diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-groups.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-groups.phpt new file mode 100644 index 00000000000..449f5d365e0 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-groups.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --list-groups ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test groups: + - 3 (1 test) + - default (1 test) + - one (1 test) + - two (1 test) diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-exclude-filter.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-exclude-filter.phpt new file mode 100644 index 00000000000..80fcfb60c8e --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-exclude-filter.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --exclude-filter testOne --list-tests ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available tests: + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testTwo + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testThree diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-exclude-group.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-exclude-group.phpt new file mode 100644 index 00000000000..0d0eccd8447 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-exclude-group.phpt @@ -0,0 +1,21 @@ +--TEST-- +phpunit --exclude-group one --list-tests ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available tests: + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testTwo + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testThree + - %sexample.phpt diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-include-filter.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-include-filter.phpt new file mode 100644 index 00000000000..463921a4a28 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-include-filter.phpt @@ -0,0 +1,19 @@ +--TEST-- +phpunit --filter testOne --list-tests ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test: + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testOne diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-include-group.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-include-group.phpt new file mode 100644 index 00000000000..6e32349f377 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-include-group.phpt @@ -0,0 +1,19 @@ +--TEST-- +phpunit --group one --list-tests ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test: + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testOne diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-exclude-filter.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-exclude-filter.phpt new file mode 100644 index 00000000000..00546a2c6ff --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-exclude-filter.phpt @@ -0,0 +1,35 @@ +--TEST-- +phpunit --exclude-filter testOne --list-tests-xml php://stdout ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + + + + + + + + + + + + + + + + + +%A diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-exclude-group.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-exclude-group.phpt new file mode 100644 index 00000000000..9b6dec2cd48 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-exclude-group.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --exclude-group one --list-tests-xml php://stdout ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + + + + + + + + + + + + + + + + + + +%A diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-include-filter.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-include-filter.phpt new file mode 100644 index 00000000000..cfe0bdea686 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-include-filter.phpt @@ -0,0 +1,31 @@ +--TEST-- +phpunit --filter testOne --list-tests-xml php://stdout ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + + + + + + + + + + + + + +%A diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-include-group.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-include-group.phpt new file mode 100644 index 00000000000..57ca8004d31 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml-include-group.phpt @@ -0,0 +1,31 @@ +--TEST-- +phpunit --group one --list-tests-xml php://stdout ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + + + + + + + + + + + + + +%A diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml.phpt new file mode 100644 index 00000000000..2be58670f44 --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests-xml.phpt @@ -0,0 +1,39 @@ +--TEST-- +phpunit --no-output --list-tests-xml php://stdout ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + + + + + + + + + + + + + + + + + + + + + + +%A diff --git a/tests/end-to-end/cli/listing-tests-and-groups/list-tests.phpt b/tests/end-to-end/cli/listing-tests-and-groups/list-tests.phpt new file mode 100644 index 00000000000..e443b7331bd --- /dev/null +++ b/tests/end-to-end/cli/listing-tests-and-groups/list-tests.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit --list-tests ../../_files/listing-tests-and-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available tests: + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testOne + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testTwo + - PHPUnit\TestFixture\ListingTestsAndGroups\ExampleTest::testThree + - %sexample.phpt diff --git a/tests/end-to-end/cli/log-events-text-invalid-argument.phpt b/tests/end-to-end/cli/log-events-text-invalid-argument.phpt new file mode 100644 index 00000000000..f3c3c5bd28b --- /dev/null +++ b/tests/end-to-end/cli/log-events-text-invalid-argument.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test fails with invalid path +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The path "%s" specified for the --log-events-text option could not be resolved diff --git a/tests/end-to-end/cli/log-events-text.phpt b/tests/end-to-end/cli/log-events-text.phpt index 8b888e5a366..d76ef45f3bc 100644 --- a/tests/end-to-end/cli/log-events-text.phpt +++ b/tests/end-to-end/cli/log-events-text.phpt @@ -1,10 +1,5 @@ --TEST-- phpunit --no-output --log-events-text logfile.txt ---SKIPIF-- -run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +The path "%s" specified for the --log-events-verbose-text option could not be resolved diff --git a/tests/end-to-end/cli/log-events-verbose-text.phpt b/tests/end-to-end/cli/log-events-verbose-text.phpt index 427627ee888..f9653cbd6fd 100644 --- a/tests/end-to-end/cli/log-events-verbose-text.phpt +++ b/tests/end-to-end/cli/log-events-verbose-text.phpt @@ -1,10 +1,5 @@ --TEST-- phpunit --no-output --log-events-verbose-text logfile.txt ---SKIPIF-- - - + diff --git a/tests/end-to-end/logging/no-output.phpt b/tests/end-to-end/cli/no-output.phpt similarity index 100% rename from tests/end-to-end/logging/no-output.phpt rename to tests/end-to-end/cli/no-output.phpt diff --git a/tests/end-to-end/cli/overlapping-testsuite-configuration.phpt b/tests/end-to-end/cli/overlapping-testsuite-configuration.phpt new file mode 100644 index 00000000000..3d4d0f808e5 --- /dev/null +++ b/tests/end-to-end/cli/overlapping-testsuite-configuration.phpt @@ -0,0 +1,27 @@ +--TEST-- +A test file must not be in more than one test suite configured in the XML configuration file +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %soverlapping-testsuite-configuration%sphpunit.xml + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot add file %sExampleTest.php to test suite "two" as it was already added to test suite "one" + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/cli/stop-on/stop-on-defect-for-error.phpt b/tests/end-to-end/cli/stop-on/stop-on-defect-for-error.phpt index 90a7aebd117..1d0b8e9091e 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-defect-for-error.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-defect-for-error.phpt @@ -1,34 +1,21 @@ --TEST-- Stopping test execution after first error works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/cli/stop-on/stop-on-defect-for-failure.phpt b/tests/end-to-end/cli/stop-on/stop-on-defect-for-failure.phpt index a01ed26b7d4..b2f6a9be461 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-defect-for-failure.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-defect-for-failure.phpt @@ -1,41 +1,27 @@ --TEST-- Stopping test execution after first failure works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\FailureTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) -Assertion Failed (Constraint: is true, Value: false) Test Failed (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) diff --git a/tests/end-to-end/cli/stop-on/stop-on-defect-for-risky.phpt b/tests/end-to-end/cli/stop-on/stop-on-defect-for-risky.phpt index 01409a03268..5b38eb7275b 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-defect-for-risky.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-defect-for-risky.phpt @@ -1,34 +1,21 @@ --TEST-- Stopping test execution after first risky test works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/cli/stop-on/stop-on-defect-for-warning.phpt b/tests/end-to-end/cli/stop-on/stop-on-defect-for-warning.phpt index f335790e562..c3cae13763e 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-defect-for-warning.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-defect-for-warning.phpt @@ -1,43 +1,29 @@ --TEST-- Stopping test execution after first warning works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) -Test Triggered Warning (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) +Test Triggered Warning (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Runner Execution Aborted diff --git a/tests/end-to-end/cli/stop-on/stop-on-deprecation.phpt b/tests/end-to-end/cli/stop-on/stop-on-deprecation.phpt index 7dd51324a58..4ac3cfad9f7 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-deprecation.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-deprecation.phpt @@ -1,47 +1,33 @@ --TEST-- Stopping test execution after first deprecation works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (2 tests) -Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 2 tests) +Test Runner Execution Started (3 tests) +Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 3 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) -Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne, unknown if issue was triggered in first-party code or third-party code) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne) Test Runner Execution Aborted -Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 2 tests) +Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest, 3 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/stop-on/stop-on-error.phpt b/tests/end-to-end/cli/stop-on/stop-on-error.phpt index 4882ad277db..d7dd7834be2 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-error.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-error.phpt @@ -1,34 +1,21 @@ --TEST-- Stopping test execution after first error works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/cli/stop-on/stop-on-failure.phpt b/tests/end-to-end/cli/stop-on/stop-on-failure.phpt index ccc4773ac51..a07f58bd38d 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-failure.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-failure.phpt @@ -1,41 +1,27 @@ --TEST-- Stopping test execution after first failure works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\FailureTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) -Assertion Failed (Constraint: is true, Value: false) Test Failed (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne) diff --git a/tests/end-to-end/cli/stop-on/stop-on-incomplete.phpt b/tests/end-to-end/cli/stop-on/stop-on-incomplete.phpt index 8b938b46ec3..4b23c90d60a 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-incomplete.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-incomplete.phpt @@ -1,34 +1,21 @@ --TEST-- Stopping test execution after first incomplete test works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/cli/stop-on/stop-on-notice.phpt b/tests/end-to-end/cli/stop-on/stop-on-notice.phpt index a3fdcdb6e68..01d62b731b3 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-notice.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-notice.phpt @@ -1,43 +1,29 @@ --TEST-- Stopping test execution after first notice works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) -Test Triggered Notice (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) +Test Triggered Notice (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne) Test Runner Execution Aborted diff --git a/tests/end-to-end/cli/stop-on/stop-on-risky.phpt b/tests/end-to-end/cli/stop-on/stop-on-risky.phpt index 4abc09ff253..7c81b8df921 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-risky.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-risky.phpt @@ -1,34 +1,21 @@ --TEST-- Stopping test execution after first risky test works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/cli/stop-on/stop-on-skipped.phpt b/tests/end-to-end/cli/stop-on/stop-on-skipped.phpt index 95b05484369..37d60f01063 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-skipped.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-skipped.phpt @@ -1,34 +1,21 @@ --TEST-- Stopping test execution after first skipped test works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/cli/stop-on/stop-on-specific-deprecation.phpt b/tests/end-to-end/cli/stop-on/stop-on-specific-deprecation.phpt new file mode 100644 index 00000000000..79843c28b8d --- /dev/null +++ b/tests/end-to-end/cli/stop-on/stop-on-specific-deprecation.phpt @@ -0,0 +1,39 @@ +--TEST-- +Stopping test execution after first deprecation where its message contains a given string +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (3 tests) +Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest, 3 tests) +Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testOne) +Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testOne, unknown if issue was triggered in first-party code or third-party code) in %s:%d +...foo... +Test Passed (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testOne) +Test Finished (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testTwo) +Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testTwo) +Test Triggered Deprecation (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testTwo, unknown if issue was triggered in first-party code or third-party code) in %s:%d +...bar... +Test Passed (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testTwo) +Test Finished (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest::testTwo) +Test Runner Execution Aborted +Test Suite Finished (PHPUnit\TestFixture\TestRunnerStopping\SpecificDeprecationTest, 3 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/cli/stop-on/stop-on-warning.phpt b/tests/end-to-end/cli/stop-on/stop-on-warning.phpt index bef2d56fefb..0f0e72bcb74 100644 --- a/tests/end-to-end/cli/stop-on/stop-on-warning.phpt +++ b/tests/end-to-end/cli/stop-on/stop-on-warning.phpt @@ -1,43 +1,29 @@ --TEST-- Stopping test execution after first warning works ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Prepared (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) -Test Triggered Warning (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) +Test Triggered Warning (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Finished (PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne) Test Runner Execution Aborted diff --git a/tests/end-to-end/data-provider/dependency-result.phpt b/tests/end-to-end/data-provider/dependency-result.phpt new file mode 100644 index 00000000000..c3e5d9e30f4 --- /dev/null +++ b/tests/end-to-end/data-provider/dependency-result.phpt @@ -0,0 +1,26 @@ +--TEST-- +phpunit ../../_files/DataProviderDependencyResultTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +...E 4 / 4 (100%) + +Time: %s, Memory: %s + +There was 1 error: + +1) PHPUnit\TestFixture\DataProviderDependencyResultTest::testAdd with data set #2 (2, 0) +Error: Cannot use positional argument after named argument during unpacking + +ERRORS! +Tests: 4, Assertions: 5, Errors: 1. diff --git a/tests/end-to-end/data-provider/log-junit-isolation.phpt b/tests/end-to-end/data-provider/log-junit-isolation.phpt new file mode 100644 index 00000000000..3156f6d6cf8 --- /dev/null +++ b/tests/end-to-end/data-provider/log-junit-isolation.phpt @@ -0,0 +1,38 @@ +--TEST-- +phpunit --process-isolation --log-junit php://stdout ../../_files/DataProviderTest.php +--FILE-- +run($_SERVER['argv']); + +print file_get_contents($logfile); + +unlink($logfile); +--EXPECTF-- + + + + + + + + PHPUnit\TestFixture\DataProviderTest::testAdd with data set #2%A +Failed asserting that 2 matches expected 3. +%A +%s:%i + + + + + diff --git a/tests/end-to-end/data-provider/log-junit.phpt b/tests/end-to-end/data-provider/log-junit.phpt new file mode 100644 index 00000000000..f38cfb07ad7 --- /dev/null +++ b/tests/end-to-end/data-provider/log-junit.phpt @@ -0,0 +1,66 @@ +--TEST-- +phpunit --log-junit php://stdout ../../_files/DataProviderTest.php +--FILE-- +run($_SERVER['argv']); + +print file_get_contents($logfile); + +unlink($logfile); +--EXPECTF-- + + + + + + + + + PHPUnit\TestFixture\DataProviderTest::testAdd with data set #2%A +Failed asserting that 2 matches expected 3. +%A +%sDataProviderTest.php:%d + + + + + + + + + + PHPUnit\TestFixture\DataProviderWithStringKeysTest::testAdd with data set "1 + 1 = 3"%A +Failed asserting that 2 matches expected 3. +%A +%sDataProviderWithStringKeysTest.php:%d + + + + + + + failure.phptFailed asserting that two strings are equal.%A +--- Expected ++++ Actual +@@ @@ +-'success' ++'failure' +%A +%s:%d + + + diff --git a/tests/end-to-end/data-provider/requires-phpunit.phpt b/tests/end-to-end/data-provider/requires-phpunit.phpt new file mode 100644 index 00000000000..29aa5d7295a --- /dev/null +++ b/tests/end-to-end/data-provider/requires-phpunit.phpt @@ -0,0 +1,34 @@ +--TEST-- +phpunit ../../_files/DataProviderRequiresPhpUnitTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +S..SS 5 / 5 (100%) + +Time: %s, Memory: %s + +There were 3 skipped tests: + +1) PHPUnit\TestFixture\DataProviderRequiresPhpUnitTest::testWithInvalidDataProvider +PHPUnit < 10 is required. + +2) PHPUnit\TestFixture\DataProviderRequiresPhpUnitTest::testWithDataProviderThatThrows +PHPUnit < 10 is required. + +3) PHPUnit\TestFixture\DataProviderRequiresPhpUnitTest::testWithDataProviderExternalThatThrows +PHPUnit < 10 is required. + +OK, but some tests were skipped! +Tests: 5, Assertions: 2, Skipped: 3. + diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/phpunit.xml b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/phpunit.xml new file mode 100644 index 00000000000..ee9592538c3 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/phpunit.xml @@ -0,0 +1,21 @@ + + + + + tests + + + + + + src + + + + PHPUnit\TestFixture\SelfDirectIndirect\trigger_deprecation + + + diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/src/FirstPartyClass.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/src/FirstPartyClass.php new file mode 100644 index 00000000000..aca0b8b8d05 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/src/FirstPartyClass.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; + +final class FirstPartyClass +{ + public function method(): true + { + (new ThirdPartyClass)->method(); + + @trigger_error('deprecation in first-party code', E_USER_DEPRECATED); + + return true; + } +} diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/tests/FirstPartyClassTest.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/tests/FirstPartyClassTest.php new file mode 100644 index 00000000000..1abafab73ab --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/tests/FirstPartyClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class FirstPartyClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue((new FirstPartyClass)->method()); + } + + public function testTwo(): void + { + $this->assertTrue((new ThirdPartyClass)->anotherMethod()); + } +} diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/vendor/ThirdPartyClass.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/vendor/ThirdPartyClass.php new file mode 100644 index 00000000000..b91884d7835 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/vendor/ThirdPartyClass.php @@ -0,0 +1,15 @@ +method(); + } +} diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/vendor/autoload.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/vendor/autoload.php new file mode 100644 index 00000000000..c5ef0a2856c --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-function/vendor/autoload.php @@ -0,0 +1,4 @@ + + + + + tests + + + + + + src + + + + PHPUnit\TestFixture\SelfDirectIndirect\DeprecationTrigger::triggerDeprecation + + + diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/src/FirstPartyClass.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/src/FirstPartyClass.php new file mode 100644 index 00000000000..aca0b8b8d05 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/src/FirstPartyClass.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; + +final class FirstPartyClass +{ + public function method(): true + { + (new ThirdPartyClass)->method(); + + @trigger_error('deprecation in first-party code', E_USER_DEPRECATED); + + return true; + } +} diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/tests/FirstPartyClassTest.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/tests/FirstPartyClassTest.php new file mode 100644 index 00000000000..1abafab73ab --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/tests/FirstPartyClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class FirstPartyClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue((new FirstPartyClass)->method()); + } + + public function testTwo(): void + { + $this->assertTrue((new ThirdPartyClass)->anotherMethod()); + } +} diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/vendor/DeprecationTrigger.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/vendor/DeprecationTrigger.php new file mode 100644 index 00000000000..b54156ad14b --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/vendor/DeprecationTrigger.php @@ -0,0 +1,10 @@ +method(); + } +} diff --git a/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/vendor/autoload.php b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/vendor/autoload.php new file mode 100644 index 00000000000..fe3e4ed83dd --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/deprecation-trigger-method/vendor/autoload.php @@ -0,0 +1,4 @@ + + + + + tests + + + + + + PHPUnit\TestFixture\DeprecationTrigger\Test::triggerDeprecation + PHPUnit\TestFixture\DeprecationTrigger\triggerDeprecation + + + diff --git a/tests/end-to-end/deprecation-trigger/_files/details-process-isolation/tests/Test.php b/tests/end-to-end/deprecation-trigger/_files/details-process-isolation/tests/Test.php new file mode 100644 index 00000000000..39137a32d4b --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/details-process-isolation/tests/Test.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\DeprecationTrigger; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public static function triggerDeprecation(): void + { + trigger_error('deprecation triggered by method', E_USER_DEPRECATED); + } + + public function testDeprecation(): void + { + self::triggerDeprecation(); + triggerDeprecation(); + + $this->assertTrue(true); + } +} + +function triggerDeprecation(): void +{ + trigger_error('deprecation triggered by function', E_USER_DEPRECATED); +} diff --git a/tests/end-to-end/deprecation-trigger/_files/details/phpunit.xml b/tests/end-to-end/deprecation-trigger/_files/details/phpunit.xml new file mode 100644 index 00000000000..d4c4d73e6cd --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/details/phpunit.xml @@ -0,0 +1,18 @@ + + + + + tests + + + + + + PHPUnit\TestFixture\DeprecationTrigger\Test::triggerDeprecation + PHPUnit\TestFixture\DeprecationTrigger\triggerDeprecation + + + diff --git a/tests/end-to-end/deprecation-trigger/_files/details/tests/Test.php b/tests/end-to-end/deprecation-trigger/_files/details/tests/Test.php new file mode 100644 index 00000000000..39137a32d4b --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/_files/details/tests/Test.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\DeprecationTrigger; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public static function triggerDeprecation(): void + { + trigger_error('deprecation triggered by method', E_USER_DEPRECATED); + } + + public function testDeprecation(): void + { + self::triggerDeprecation(); + triggerDeprecation(); + + $this->assertTrue(true); + } +} + +function triggerDeprecation(): void +{ + trigger_error('deprecation triggered by function', E_USER_DEPRECATED); +} diff --git a/tests/end-to-end/deprecation-trigger/deprecation-trigger-function.phpt b/tests/end-to-end/deprecation-trigger/deprecation-trigger-function.phpt new file mode 100644 index 00000000000..1848ed1b102 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/deprecation-trigger-function.phpt @@ -0,0 +1,46 @@ +--TEST-- +The right events are emitted in the right order for a test that runs code which triggers E_USER_DEPRECATED +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Bootstrap Finished (%sautoload.php) +Event Facade Sealed +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (%sphpunit.xml, 2 tests) +Test Suite Started (default, 2 tests) +Test Suite Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by first-party code calling into third-party code, suppressed using operator) in %s:%d +deprecation in third-party code +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by first-party code calling into first-party code, suppressed using operator) in %s:%d +deprecation in first-party code +Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo, issue triggered by first-party code calling into third-party code, suppressed using operator) in %s:%d +deprecation in third-party code +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo, issue triggered by third-party code, suppressed using operator) in %s:%d +deprecation in first-party code +Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Suite Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 2 tests) +Test Suite Finished (default, 2 tests) +Test Suite Finished (%sphpunit.xml, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/deprecation-trigger/deprecation-trigger-method.phpt b/tests/end-to-end/deprecation-trigger/deprecation-trigger-method.phpt new file mode 100644 index 00000000000..4bd800f9be4 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/deprecation-trigger-method.phpt @@ -0,0 +1,46 @@ +--TEST-- +The right events are emitted in the right order for a test that runs code which triggers E_USER_DEPRECATED +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Bootstrap Finished (%sautoload.php) +Event Facade Sealed +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (%sphpunit.xml, 2 tests) +Test Suite Started (default, 2 tests) +Test Suite Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by first-party code calling into third-party code, suppressed using operator) in %s:%d +deprecation in third-party code +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by first-party code calling into first-party code, suppressed using operator) in %s:%d +deprecation in first-party code +Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo, issue triggered by first-party code calling into third-party code, suppressed using operator) in %s:%d +deprecation in third-party code +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo, issue triggered by third-party code, suppressed using operator) in %s:%d +deprecation in first-party code +Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Suite Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 2 tests) +Test Suite Finished (default, 2 tests) +Test Suite Finished (%sphpunit.xml, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/deprecation-trigger/details-process-isolation.phpt b/tests/end-to-end/deprecation-trigger/details-process-isolation.phpt new file mode 100644 index 00000000000..24f80fdf7c1 --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/details-process-isolation.phpt @@ -0,0 +1,32 @@ +--TEST-- +Configured deprecation triggers are filtered when displaying deprecation details in process isolation +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +D 1 / 1 (100%) + +Time: %s, Memory: %s + +1 test triggered 2 deprecations: + +1) %sTest.php:25 +deprecation triggered by method + +2) %sTest.php:26 +deprecation triggered by function + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 2. diff --git a/tests/end-to-end/deprecation-trigger/details.phpt b/tests/end-to-end/deprecation-trigger/details.phpt new file mode 100644 index 00000000000..0d7662ff3ac --- /dev/null +++ b/tests/end-to-end/deprecation-trigger/details.phpt @@ -0,0 +1,32 @@ +--TEST-- +Configured deprecation triggers are filtered when displaying deprecation details +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +D 1 / 1 (100%) + +Time: %s, Memory: %s + +1 test triggered 2 deprecations: + +1) %sTest.php:25 +deprecation triggered by method + +2) %sTest.php:26 +deprecation triggered by function + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 2. diff --git a/tests/end-to-end/event/_files/AssertionFailureInSetUpBeforeClassTest.php b/tests/end-to-end/event/_files/AssertionFailureInSetUpBeforeClassTest.php new file mode 100644 index 00000000000..ce6c0e75bdd --- /dev/null +++ b/tests/end-to-end/event/_files/AssertionFailureInSetUpBeforeClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\TestCase; + +final class AssertionFailureInSetUpBeforeClassTest extends TestCase +{ + public static function setUpBeforeClass(): void + { + self::assertTrue(false); + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/AssertionFailureInTearDownAfterClassTest.php b/tests/end-to-end/event/_files/AssertionFailureInTearDownAfterClassTest.php new file mode 100644 index 00000000000..84f81e79f0c --- /dev/null +++ b/tests/end-to-end/event/_files/AssertionFailureInTearDownAfterClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\TestCase; + +final class AssertionFailureInTearDownAfterClassTest extends TestCase +{ + public static function tearDownAfterClass(): void + { + self::assertTrue(false); + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/DeprecatedFeatureTest.php b/tests/end-to-end/event/_files/DeprecatedFeatureTest.php index 6f8369829a3..8cdf64d4f7f 100644 --- a/tests/end-to-end/event/_files/DeprecatedFeatureTest.php +++ b/tests/end-to-end/event/_files/DeprecatedFeatureTest.php @@ -10,6 +10,7 @@ namespace PHPUnit\TestFixture\Event; use const E_USER_DEPRECATED; +use function error_get_last; use function trigger_error; use PHPUnit\Framework\TestCase; @@ -22,4 +23,11 @@ public function testDeprecatedFeature(): void $this->assertTrue(true); } + + public function testDeprecatedSuppressedErrorGetLast(): void + { + $this->assertNull(error_get_last()); + @trigger_error('message', E_USER_DEPRECATED); + $this->assertIsArray(error_get_last()); + } } diff --git a/tests/end-to-end/event/_files/DeprecatedPhpFeatureTest.php b/tests/end-to-end/event/_files/DeprecatedPhpFeatureTest.php index fbb1f8b8542..aba4afe4c0c 100644 --- a/tests/end-to-end/event/_files/DeprecatedPhpFeatureTest.php +++ b/tests/end-to-end/event/_files/DeprecatedPhpFeatureTest.php @@ -9,14 +9,15 @@ */ namespace PHPUnit\TestFixture\Event; +use function strlen; use PHPUnit\Framework\TestCase; final class DeprecatedPhpFeatureTest extends TestCase { public function testDeprecatedPhpFeature(): void { - @$this->foo = 'bar'; - $this->bar = 'foo'; + strlen(null); + @strlen(null); $this->assertTrue(true); } diff --git a/tests/end-to-end/event/_files/EmptyDataProviderTest.php b/tests/end-to-end/event/_files/EmptyDataProviderTest.php index a9ae46223b0..8f4d04f9af5 100644 --- a/tests/end-to-end/event/_files/EmptyDataProviderTest.php +++ b/tests/end-to-end/event/_files/EmptyDataProviderTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Event; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class EmptyDataProviderTest extends TestCase @@ -18,9 +19,7 @@ public static function providerMethod(): array return []; } - /** - * @dataProvider providerMethod - */ + #[DataProvider('providerMethod')] public function testCase(): void { } diff --git a/tests/end-to-end/event/_files/ExceptionInTearDownAfterClassTest.php b/tests/end-to-end/event/_files/ExceptionInTearDownAfterClassTest.php new file mode 100644 index 00000000000..22094ddd65d --- /dev/null +++ b/tests/end-to-end/event/_files/ExceptionInTearDownAfterClassTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use Exception; +use PHPUnit\Framework\TestCase; + +final class ExceptionInTearDownAfterClassTest extends TestCase +{ + public static function tearDownAfterClass(): void + { + throw new Exception; + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/ExceptionInTearDownTest.php b/tests/end-to-end/event/_files/ExceptionInTearDownTest.php new file mode 100644 index 00000000000..5b7e35311f9 --- /dev/null +++ b/tests/end-to-end/event/_files/ExceptionInTearDownTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use Exception; +use PHPUnit\Framework\TestCase; + +final class ExceptionInTearDownTest extends TestCase +{ + protected function tearDown(): void + { + throw new Exception; + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/ExpectedAndUnexpectedAssertionsTest.php b/tests/end-to-end/event/_files/ExpectedAndUnexpectedAssertionsTest.php new file mode 100644 index 00000000000..06118eb390f --- /dev/null +++ b/tests/end-to-end/event/_files/ExpectedAndUnexpectedAssertionsTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\TestCase; + +final class ExpectedAndUnexpectedAssertionsTest extends TestCase +{ + #[DoesNotPerformAssertions] + public function testOne(): void + { + } + + public function testTwo(): void + { + $this->expectNotToPerformAssertions(); + } + + #[DoesNotPerformAssertions] + public function testThree(): void + { + $this->assertTrue(true); + } + + public function testFour(): void + { + $this->expectNotToPerformAssertions(); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/IgnoreDeprecationsTest.php b/tests/end-to-end/event/_files/IgnoreDeprecationsTest.php index a44c5c82d62..a165a42fbd6 100644 --- a/tests/end-to-end/event/_files/IgnoreDeprecationsTest.php +++ b/tests/end-to-end/event/_files/IgnoreDeprecationsTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture\Event; +use const E_USER_DEPRECATED; +use function error_get_last; use function trigger_error; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; @@ -29,4 +31,19 @@ public function testTwo(): void $this->assertTrue(true); } + + #[IgnoreDeprecations] + public function testOneErrorGetLast(): void + { + $this->assertNull(error_get_last()); + trigger_error('message', E_USER_DEPRECATED); + $this->assertIsArray(error_get_last()); + } + + public function testTwoErrorGetLast(): void + { + $this->assertNull(error_get_last()); + trigger_error('message', E_USER_DEPRECATED); + $this->assertIsArray(error_get_last()); + } } diff --git a/tests/end-to-end/event/_files/IgnoredDeprecatedPhpunitFeatureTest.php b/tests/end-to-end/event/_files/IgnoredDeprecatedPhpunitFeatureTest.php new file mode 100644 index 00000000000..24c49e6577e --- /dev/null +++ b/tests/end-to-end/event/_files/IgnoredDeprecatedPhpunitFeatureTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Event\Facade as EventFacade; +use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; +use PHPUnit\Framework\TestCase; + +final class IgnoredDeprecatedPhpunitFeatureTest extends TestCase +{ + #[IgnorePhpunitDeprecations] + public function testDeprecatedPhpunitFeature(): void + { + EventFacade::emitter()->testTriggeredPhpunitDeprecation( + $this->valueObjectForEvents(), + 'message', + ); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/InvalidDataProviderTest.php b/tests/end-to-end/event/_files/InvalidDataProviderTest.php index 3950135124b..60cff8a16b0 100644 --- a/tests/end-to-end/event/_files/InvalidDataProviderTest.php +++ b/tests/end-to-end/event/_files/InvalidDataProviderTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Event; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class InvalidDataProviderTest extends TestCase @@ -18,9 +19,7 @@ public static function provider(): array return [0]; } - /** - * @dataProvider provider - */ + #[DataProvider('provider')] public function testOne(): void { $this->assertTrue(true); diff --git a/tests/end-to-end/event/_files/InvalidDataProviderWithOneTestPassingTest.php b/tests/end-to-end/event/_files/InvalidDataProviderWithOneTestPassingTest.php index 66b7c62f8b8..e5b0e343bb8 100644 --- a/tests/end-to-end/event/_files/InvalidDataProviderWithOneTestPassingTest.php +++ b/tests/end-to-end/event/_files/InvalidDataProviderWithOneTestPassingTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Event; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class InvalidDataProviderWithOneTestPassingTest extends TestCase @@ -18,9 +19,7 @@ public static function provider(): array return [0]; } - /** - * @dataProvider provider - */ + #[DataProvider('provider')] public function testOne(): void { $this->assertTrue(true); diff --git a/tests/end-to-end/event/_files/InvalidDependencyTest.php b/tests/end-to-end/event/_files/InvalidDependencyTest.php index e9fddfa26b5..ded12c8c6a6 100644 --- a/tests/end-to-end/event/_files/InvalidDependencyTest.php +++ b/tests/end-to-end/event/_files/InvalidDependencyTest.php @@ -9,21 +9,19 @@ */ namespace PHPUnit\TestFixture\Event; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DependsOnClass; use PHPUnit\Framework\TestCase; final class InvalidDependencyTest extends TestCase { - /** - * @depends doesNotExist - */ + #[Depends('doesNotExist')] public function testOne(): void { $this->assertTrue(true); } - /** - * @depends DoesNotExist::class - */ + #[DependsOnClass('DoesNotExist')] public function testTwo(): void { $this->assertTrue(true); diff --git a/tests/end-to-end/event/_files/InvalidParameterNameDataProviderTest.php b/tests/end-to-end/event/_files/InvalidParameterNameDataProviderTest.php new file mode 100644 index 00000000000..5e9d3717d43 --- /dev/null +++ b/tests/end-to-end/event/_files/InvalidParameterNameDataProviderTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class InvalidParameterNameDataProviderTest extends TestCase +{ + public static function values(): array + { + return [ + ['value1' => true, 'value2' => true], + ['value3' => true, 'value4' => true], + ]; + } + + #[DataProvider('values')] + public function testSuccess(bool $value1, bool $value2): void + { + $this->assertTrue($value1); + $this->assertTrue($value2); + } +} diff --git a/tests/end-to-end/event/_files/MissingDependencyTest.php b/tests/end-to-end/event/_files/MissingDependencyTest.php index a215a709843..6d3a39bf0df 100644 --- a/tests/end-to-end/event/_files/MissingDependencyTest.php +++ b/tests/end-to-end/event/_files/MissingDependencyTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Event; +use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\TestCase; final class MissingDependencyTest extends TestCase @@ -18,9 +19,7 @@ public function testOne(): void $this->assertTrue(false); } - /** - * @depends testOne - */ + #[Depends('testOne')] public function testTwo(): void { $this->assertTrue(true); diff --git a/tests/end-to-end/event/_files/PhpunitNoticeTest.php b/tests/end-to-end/event/_files/PhpunitNoticeTest.php new file mode 100644 index 00000000000..20ab335486b --- /dev/null +++ b/tests/end-to-end/event/_files/PhpunitNoticeTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Event\Facade; +use PHPUnit\Framework\TestCase; + +final class PhpunitNoticeTest extends TestCase +{ + public function testOne(): void + { + Facade::emitter()->testTriggeredPhpunitNotice( + $this->valueObjectForEvents(), + 'message', + ); + + Facade::emitter()->testRunnerTriggeredPhpunitNotice('message'); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/SeparateProcessesTest.php b/tests/end-to-end/event/_files/SeparateProcessesTest.php index d0cc38e8b50..549af0818b0 100644 --- a/tests/end-to-end/event/_files/SeparateProcessesTest.php +++ b/tests/end-to-end/event/_files/SeparateProcessesTest.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture\Event; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\TestCase; -/** - * @runTestsInSeparateProcesses - */ +#[RunTestsInSeparateProcesses] final class SeparateProcessesTest extends TestCase { public function testOne(): array diff --git a/tests/end-to-end/event/_files/SuppressedUserNoticeTest.php b/tests/end-to-end/event/_files/SuppressedUserNoticeTest.php index 245ce980a4b..d280987cd2b 100644 --- a/tests/end-to-end/event/_files/SuppressedUserNoticeTest.php +++ b/tests/end-to-end/event/_files/SuppressedUserNoticeTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture\Event; +use const E_USER_NOTICE; +use function error_get_last; use function trigger_error; use PHPUnit\Framework\TestCase; @@ -20,4 +22,11 @@ public function testSuppressedUserNotice(): void @trigger_error('message', E_USER_NOTICE); } + + public function testSuppressedUserNoticeErrorGetLast(): void + { + $this->assertNull(error_get_last()); + @trigger_error('message', E_USER_NOTICE); + $this->assertIsArray(error_get_last()); + } } diff --git a/tests/end-to-end/event/_files/SuppressedUserWarningTest.php b/tests/end-to-end/event/_files/SuppressedUserWarningTest.php index 426746e03c2..d6c362f9cab 100644 --- a/tests/end-to-end/event/_files/SuppressedUserWarningTest.php +++ b/tests/end-to-end/event/_files/SuppressedUserWarningTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture\Event; +use const E_USER_WARNING; +use function error_get_last; use function trigger_error; use PHPUnit\Framework\TestCase; @@ -20,4 +22,11 @@ public function testSuppressedUserWarning(): void @trigger_error('message', E_USER_WARNING); } + + public function testSuppressedUserWarningErrorGetLast(): void + { + $this->assertNull(error_get_last()); + @trigger_error('message', E_USER_WARNING); + $this->assertIsArray(error_get_last()); + } } diff --git a/tests/end-to-end/event/_files/UnsatisfiedRequirementBeforeClassMethodTest.php b/tests/end-to-end/event/_files/UnsatisfiedRequirementBeforeClassMethodTest.php new file mode 100644 index 00000000000..f6f87ac9666 --- /dev/null +++ b/tests/end-to-end/event/_files/UnsatisfiedRequirementBeforeClassMethodTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\TestCase; + +final class UnsatisfiedRequirementBeforeClassMethodTest extends TestCase +{ + #[RequiresPhp('100')] + public static function setUpBeforeClass(): void + { + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/UnsatisfiedRequirementClassTest.php b/tests/end-to-end/event/_files/UnsatisfiedRequirementClassTest.php new file mode 100644 index 00000000000..2453b19b5ec --- /dev/null +++ b/tests/end-to-end/event/_files/UnsatisfiedRequirementClassTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\TestCase; + +#[RequiresPhp('100')] +final class UnsatisfiedRequirementClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/UnsatisfiedRequirementMethodTest.php b/tests/end-to-end/event/_files/UnsatisfiedRequirementMethodTest.php new file mode 100644 index 00000000000..dfe230aad1a --- /dev/null +++ b/tests/end-to-end/event/_files/UnsatisfiedRequirementMethodTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\TestCase; + +final class UnsatisfiedRequirementMethodTest extends TestCase +{ + #[RequiresPhp('100')] + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/UnsatisfiedRequirementTest.php b/tests/end-to-end/event/_files/UnsatisfiedRequirementTest.php deleted file mode 100644 index bcc24322dc7..00000000000 --- a/tests/end-to-end/event/_files/UnsatisfiedRequirementTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Event; - -use PHPUnit\Framework\TestCase; - -final class UnsatisfiedRequirementTest extends TestCase -{ - /** - * @requires PHP 100 - */ - public function testOne(): void - { - $this->assertTrue(true); - } -} diff --git a/tests/end-to-end/event/_files/UserErrorTest.php b/tests/end-to-end/event/_files/UserErrorTest.php index 998a6dc7471..62ea7d170f8 100644 --- a/tests/end-to-end/event/_files/UserErrorTest.php +++ b/tests/end-to-end/event/_files/UserErrorTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Event; +use const E_USER_ERROR; use function trigger_error; use PHPUnit\Framework\TestCase; @@ -20,4 +21,10 @@ public function testUserError(): void trigger_error('message', E_USER_ERROR); } + + public function testUserErrorMustAbortExecution(): void + { + trigger_error('message', E_USER_ERROR); + $this->assertTrue(false); + } } diff --git a/tests/end-to-end/event/_files/UserNoticeTest.php b/tests/end-to-end/event/_files/UserNoticeTest.php index 86fed62b7db..17046c78f5c 100644 --- a/tests/end-to-end/event/_files/UserNoticeTest.php +++ b/tests/end-to-end/event/_files/UserNoticeTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture\Event; +use const E_USER_NOTICE; +use function error_get_last; use function trigger_error; use PHPUnit\Framework\TestCase; @@ -20,4 +22,11 @@ public function testUserNotice(): void trigger_error('message', E_USER_NOTICE); } + + public function testUserNoticeErrorGetLast(): void + { + $this->assertNull(error_get_last()); + trigger_error('message', E_USER_NOTICE); + $this->assertIsArray(error_get_last()); + } } diff --git a/tests/end-to-end/event/_files/UserWarningTest.php b/tests/end-to-end/event/_files/UserWarningTest.php index 4e75bcbef80..21b232ee50a 100644 --- a/tests/end-to-end/event/_files/UserWarningTest.php +++ b/tests/end-to-end/event/_files/UserWarningTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture\Event; +use const E_USER_WARNING; +use function error_get_last; use function trigger_error; use PHPUnit\Framework\TestCase; @@ -20,4 +22,11 @@ public function testUserWarning(): void trigger_error('message', E_USER_WARNING); } + + public function testUserWarningErrorGetLast(): void + { + $this->assertNull(error_get_last()); + trigger_error('message', E_USER_WARNING); + $this->assertIsArray(error_get_last()); + } } diff --git a/tests/end-to-end/event/_files/XdebugIsDisabled.php b/tests/end-to-end/event/_files/XdebugIsDisabled.php new file mode 100644 index 00000000000..e2cb32d2e92 --- /dev/null +++ b/tests/end-to-end/event/_files/XdebugIsDisabled.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use function extension_loaded; +use function ini_get; +use PHPUnit\Framework\TestCase; + +final class XdebugIsDisabled extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(extension_loaded('xdebug')); + $this->assertSame('', (string) ini_get('xdebug.mode')); + } +} diff --git a/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/bootstrap.php b/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/bootstrap.php new file mode 100644 index 00000000000..fc1f5dc3f29 --- /dev/null +++ b/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/bootstrap.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten; + +use function set_error_handler; + +set_error_handler( + static function (int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool + { + return true; + }, +); diff --git a/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/phpunit.xml b/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/phpunit.xml new file mode 100644 index 00000000000..0e0f270cea9 --- /dev/null +++ b/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/phpunit.xml @@ -0,0 +1,11 @@ + + + + + tests + + + diff --git a/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/tests/ExampleTest.php b/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/tests/ExampleTest.php new file mode 100644 index 00000000000..fa5b3fc3d34 --- /dev/null +++ b/tests/end-to-end/event/_files/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit/tests/ExampleTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class ExampleTest extends TestCase +{ + public function testOne(): void + { + trigger_error('message', E_USER_DEPRECATED); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/event/_files/CustomFailureException.php b/tests/end-to-end/event/_files/custom-failure-interface/CustomFailureException.php similarity index 100% rename from tests/end-to-end/event/_files/CustomFailureException.php rename to tests/end-to-end/event/_files/custom-failure-interface/CustomFailureException.php diff --git a/tests/end-to-end/event/_files/CustomFailureInterface.php b/tests/end-to-end/event/_files/custom-failure-interface/CustomFailureInterface.php similarity index 100% rename from tests/end-to-end/event/_files/CustomFailureInterface.php rename to tests/end-to-end/event/_files/custom-failure-interface/CustomFailureInterface.php diff --git a/tests/end-to-end/event/_files/CustomFailureInterfaceTest.php b/tests/end-to-end/event/_files/custom-failure-interface/CustomFailureInterfaceTest.php similarity index 100% rename from tests/end-to-end/event/_files/CustomFailureInterfaceTest.php rename to tests/end-to-end/event/_files/custom-failure-interface/CustomFailureInterfaceTest.php diff --git a/tests/end-to-end/event/_files/custom-failure-interface/bootstrap.php b/tests/end-to-end/event/_files/custom-failure-interface/bootstrap.php new file mode 100644 index 00000000000..560c65452b7 --- /dev/null +++ b/tests/end-to-end/event/_files/custom-failure-interface/bootstrap.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/CustomFailureInterface.php'; + +require __DIR__ . '/CustomFailureException.php'; diff --git a/tests/end-to-end/event/_files/error-handler-can-be-disabled/src/Foo.php b/tests/end-to-end/event/_files/error-handler-can-be-disabled/src/Foo.php index b47fe9c50e0..d503df1b221 100644 --- a/tests/end-to-end/event/_files/error-handler-can-be-disabled/src/Foo.php +++ b/tests/end-to-end/event/_files/error-handler-can-be-disabled/src/Foo.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled; +use const E_USER_WARNING; use function error_get_last; use function fopen; use function trigger_error; diff --git a/tests/end-to-end/event/_files/error-handler-can-be-disabled/tests/FooTest.php b/tests/end-to-end/event/_files/error-handler-can-be-disabled/tests/FooTest.php index 655c225a100..79222867423 100644 --- a/tests/end-to-end/event/_files/error-handler-can-be-disabled/tests/FooTest.php +++ b/tests/end-to-end/event/_files/error-handler-can-be-disabled/tests/FooTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled; +use function restore_error_handler; +use function set_error_handler; use function sys_get_temp_dir; use function tempnam; use Exception; @@ -33,4 +35,26 @@ public function testMethodB(): void { $this->assertSame('Triggering', (new Foo)->methodB()['message']); } + + public function testErrorHandlerSet(): void + { + $this->assertIsCallable($this->getErrorHandler()); + } + + #[WithoutErrorHandler] + public function testErrorHandlerIsNotSet(): void + { + $this->assertNull($this->getErrorHandler()); + } + + /** + * @return null|callable + */ + private function getErrorHandler() + { + $res = set_error_handler(static fn () => false); + restore_error_handler(); + + return $res; + } } diff --git a/tests/end-to-end/event/_files/invalid-coverage-metadata/phpunit.xml b/tests/end-to-end/event/_files/invalid-coverage-metadata/phpunit.xml index 4697f0bb634..c05484a928c 100644 --- a/tests/end-to-end/event/_files/invalid-coverage-metadata/phpunit.xml +++ b/tests/end-to-end/event/_files/invalid-coverage-metadata/phpunit.xml @@ -1,6 +1,7 @@ + xsi:noNamespaceSchemaLocation="../../../../../phpunit.xsd" + cacheDirectory=".phpunit.cache"> tests diff --git a/tests/end-to-end/event/assert-failure.phpt b/tests/end-to-end/event/assert-failure.phpt index 485c19d3ac3..16c437ca968 100644 --- a/tests/end-to-end/event/assert-failure.phpt +++ b/tests/end-to-end/event/assert-failure.phpt @@ -5,37 +5,21 @@ The right events are emitted in the right order for a test that fails because of if (ini_get('zend.assertions') != 1) { print 'skip: zend.assertions=1 is required' . PHP_EOL; } - -if (ini_get('assert.exception') != 1) { - print 'skip: assert.exception=1 is required' . PHP_EOL; -} - -if (DIRECTORY_SEPARATOR === '\\') { - print "skip: this test does not work on Windows / GitHub Actions\n"; -} --FILE-- run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/assertion-failure-in-after-class-method.phpt b/tests/end-to-end/event/assertion-failure-in-after-class-method.phpt new file mode 100644 index 00000000000..535f4ff9dda --- /dev/null +++ b/tests/end-to-end/event/assertion-failure-in-after-class-method.phpt @@ -0,0 +1,34 @@ +--TEST-- +The right events are emitted in the right order for a test that fails because of an assertion failure in a "after last test" method +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::testOne) +Test Passed (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::testOne) +After Last Test Method Called (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::tearDownAfterClass) +After Last Test Method Failed (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::tearDownAfterClass) +Failed asserting that false is true. +After Last Test Method Finished: +- PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest::tearDownAfterClass +Test Suite Finished (PHPUnit\TestFixture\Event\AssertionFailureInTearDownAfterClassTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 1) diff --git a/tests/end-to-end/event/assertion-failure-in-after-test-method.phpt b/tests/end-to-end/event/assertion-failure-in-after-test-method.phpt index d16ad5eab6d..a192920dece 100644 --- a/tests/end-to-end/event/assertion-failure-in-after-test-method.phpt +++ b/tests/end-to-end/event/assertion-failure-in-after-test-method.phpt @@ -1,43 +1,29 @@ --TEST-- The right events are emitted in the right order for a test that fails because of an assertion failure in a "after test" method ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) -Test Passed (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::testOne) -Assertion Failed (Constraint: is true, Value: false) After Test Method Called (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::afterTest) +After Test Method Failed (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::afterTest) +Failed asserting that false is true. After Test Method Finished: - PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::afterTest Test Failed (PHPUnit\TestFixture\Event\AssertionFailureInTearDownTest::testOne) diff --git a/tests/end-to-end/event/assertion-failure-in-before-class-method.phpt b/tests/end-to-end/event/assertion-failure-in-before-class-method.phpt new file mode 100644 index 00000000000..7e47bb8cf35 --- /dev/null +++ b/tests/end-to-end/event/assertion-failure-in-before-class-method.phpt @@ -0,0 +1,30 @@ +--TEST-- +The right events are emitted in the right order for a test that fails because of an assertion failure in a "before first test" method +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\AssertionFailureInSetUpBeforeClassTest, 1 test) +Before First Test Method Called (PHPUnit\TestFixture\Event\AssertionFailureInSetUpBeforeClassTest::setUpBeforeClass) +Before First Test Method Failed (PHPUnit\TestFixture\Event\AssertionFailureInSetUpBeforeClassTest::setUpBeforeClass) +Failed asserting that false is true. +Before First Test Method Finished: +- PHPUnit\TestFixture\Event\AssertionFailureInSetUpBeforeClassTest::setUpBeforeClass +Test Suite Finished (PHPUnit\TestFixture\Event\AssertionFailureInSetUpBeforeClassTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 1) diff --git a/tests/end-to-end/event/assertion-failure-in-before-test-method.phpt b/tests/end-to-end/event/assertion-failure-in-before-test-method.phpt index 2494feef219..9d20fe9c771 100644 --- a/tests/end-to-end/event/assertion-failure-in-before-test-method.phpt +++ b/tests/end-to-end/event/assertion-failure-in-before-test-method.phpt @@ -1,43 +1,32 @@ --TEST-- The right events are emitted in the right order for a test that fails because of an assertion failure in a "before test" method ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::testOne) -Assertion Failed (Constraint: is true, Value: false) Before Test Method Called (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::beforeTest) +Before Test Method Failed (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::beforeTest) +Failed asserting that false is true. Before Test Method Finished: - PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::beforeTest Test Preparation Failed (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::testOne) +Failed asserting that false is true. Test Failed (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::testOne) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\Event\AssertionFailureInSetUpTest::testOne) diff --git a/tests/end-to-end/event/assertion-failure-in-postcondition-method.phpt b/tests/end-to-end/event/assertion-failure-in-postcondition-method.phpt index 1867510110d..faf026b1c90 100644 --- a/tests/end-to-end/event/assertion-failure-in-postcondition-method.phpt +++ b/tests/end-to-end/event/assertion-failure-in-postcondition-method.phpt @@ -1,42 +1,29 @@ --TEST-- -The right events are emitted in the right order for a test that fails because of an assertion failure in a postcondition method ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) -Assertion Failed (Constraint: is true, Value: false) Post Condition Method Called (PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest::postCondition) +Post Condition Method Failed (PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest::postCondition) +Failed asserting that false is true. Post Condition Method Finished: - PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest::postCondition Test Failed (PHPUnit\TestFixture\Event\AssertionFailureInPostConditionTest::testOne) diff --git a/tests/end-to-end/event/assertion-failure-in-precondition-method.phpt b/tests/end-to-end/event/assertion-failure-in-precondition-method.phpt index 0ba39ce0a9f..d02904f0d26 100644 --- a/tests/end-to-end/event/assertion-failure-in-precondition-method.phpt +++ b/tests/end-to-end/event/assertion-failure-in-precondition-method.phpt @@ -1,43 +1,32 @@ --TEST-- -The right events are emitted in the right order for a test that fails because of an assertion failure in a precondition method ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::testOne) -Assertion Failed (Constraint: is true, Value: false) Pre Condition Method Called (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::preCondition) +Pre Condition Method Failed (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::preCondition) +Failed asserting that false is true. Pre Condition Method Finished: - PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::preCondition Test Preparation Failed (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::testOne) +Failed asserting that false is true. Test Failed (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::testOne) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\Event\AssertionFailureInPreConditionTest::testOne) diff --git a/tests/end-to-end/event/assertion-failure-in-test-method.phpt b/tests/end-to-end/event/assertion-failure-in-test-method.phpt index a3c79c75a9b..e74e57fde9e 100644 --- a/tests/end-to-end/event/assertion-failure-in-test-method.phpt +++ b/tests/end-to-end/event/assertion-failure-in-test-method.phpt @@ -1,40 +1,26 @@ --TEST-- The right events are emitted in the right order for a test that fails because of an assertion failure in the test method ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\FailureTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\FailureTest::testFailure) Test Prepared (PHPUnit\TestFixture\Event\FailureTest::testFailure) -Assertion Failed (Constraint: is true, Value: false) Test Failed (PHPUnit\TestFixture\Event\FailureTest::testFailure) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\Event\FailureTest::testFailure) diff --git a/tests/end-to-end/event/custom-comparator.phpt b/tests/end-to-end/event/custom-comparator.phpt index 55dc0652151..3665207d1ba 100644 --- a/tests/end-to-end/event/custom-comparator.phpt +++ b/tests/end-to-end/event/custom-comparator.phpt @@ -1,33 +1,23 @@ --TEST-- The right events are emitted in the right order for a successful test that uses assertEquals() with a custom comparator ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) +Bootstrap Finished (%sCustomComparator.php) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) @@ -35,7 +25,6 @@ Test Suite Started (PHPUnit\TestFixture\Event\CustomComparatorTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\CustomComparatorTest::testWithCustomComparator) Test Prepared (PHPUnit\TestFixture\Event\CustomComparatorTest::testWithCustomComparator) Comparator Registered (PHPUnit\TestFixture\Event\CustomComparator) -Assertion Succeeded (Constraint: is equal to true, Value: false) Test Passed (PHPUnit\TestFixture\Event\CustomComparatorTest::testWithCustomComparator) Test Finished (PHPUnit\TestFixture\Event\CustomComparatorTest::testWithCustomComparator) Test Suite Finished (PHPUnit\TestFixture\Event\CustomComparatorTest, 1 test) diff --git a/tests/end-to-end/event/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit.phpt b/tests/end-to-end/event/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit.phpt new file mode 100644 index 00000000000..f48d3e909bf --- /dev/null +++ b/tests/end-to-end/event/custom-error-handler-registered-in-bootstrap-is-not-overwritten-by-phpunit.phpt @@ -0,0 +1,35 @@ +--TEST-- +A custom error handler registered in the test suite's bootstrap script using set_error_handler() is not overwritten by PHPUnit by default +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Bootstrap Finished (%sbootstrap.php) +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%sphpunit.xml, 1 test) +Test Suite Started (default, 1 test) +Test Suite Started (PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten\ExampleTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten\ExampleTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten\ExampleTest::testOne) +Test Passed (PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten\ExampleTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten\ExampleTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\Event\ErrorHandlerIsNotOverwritten\ExampleTest, 1 test) +Test Suite Finished (default, 1 test) +Test Suite Finished (%sphpunit.xml, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/data-provider-duplicate-key.phpt b/tests/end-to-end/event/data-provider-duplicate-key.phpt index 7b2c2ec93c2..0bc07706d78 100644 --- a/tests/end-to-end/event/data-provider-duplicate-key.phpt +++ b/tests/end-to-end/event/data-provider-duplicate-key.phpt @@ -1,31 +1,19 @@ --TEST-- -The right events are emitted in the right order for a test that uses a data provider that provides duplicate keys ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\DataProviderDuplicateKeyTest::provider for test method PHPUnit\TestFixture\Event\DataProviderDuplicateKeyTest::testSomething) Data Provider Method Finished for PHPUnit\TestFixture\Event\DataProviderDuplicateKeyTest::testSomething: - PHPUnit\TestFixture\Event\DataProviderDuplicateKeyTest::provider @@ -34,7 +22,6 @@ The data provider specified for PHPUnit\TestFixture\Event\DataProviderDuplicateK The key "key" has already been defined by a previous data provider Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\DataProviderDuplicateKeyTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/data-provider-empty.phpt b/tests/end-to-end/event/data-provider-empty.phpt index 4ccdb309343..83c6f4c54af 100644 --- a/tests/end-to-end/event/data-provider-empty.phpt +++ b/tests/end-to-end/event/data-provider-empty.phpt @@ -1,31 +1,19 @@ --TEST-- -The right events are emitted in the right order for a test that uses a data provider that returns an empty array ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\EmptyDataProviderTest::providerMethod for test method PHPUnit\TestFixture\Event\EmptyDataProviderTest::testCase) Data Provider Method Finished for PHPUnit\TestFixture\Event\EmptyDataProviderTest::testCase: - PHPUnit\TestFixture\Event\EmptyDataProviderTest::providerMethod @@ -34,7 +22,6 @@ The data provider specified for PHPUnit\TestFixture\Event\EmptyDataProviderTest: Empty data set provided by data provider Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\EmptyDataProviderTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/data-provider-exception.phpt b/tests/end-to-end/event/data-provider-exception.phpt index 084fb476973..fc4f1dd5203 100644 --- a/tests/end-to-end/event/data-provider-exception.phpt +++ b/tests/end-to-end/event/data-provider-exception.phpt @@ -1,31 +1,19 @@ --TEST-- The right events are emitted in the right order for a test that uses a data provider that raises an exception ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\ExceptionInDataProviderTest::provider for test method PHPUnit\TestFixture\Event\ExceptionInDataProviderTest::testOne) Data Provider Method Finished for PHPUnit\TestFixture\Event\ExceptionInDataProviderTest::testOne: - PHPUnit\TestFixture\Event\ExceptionInDataProviderTest::provider @@ -34,7 +22,6 @@ The data provider specified for PHPUnit\TestFixture\Event\ExceptionInDataProvide message Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\ExceptionInDataProviderTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/data-provider-expects-argument.phpt b/tests/end-to-end/event/data-provider-expects-argument.phpt index 210b37c5afc..3616bc7ec40 100644 --- a/tests/end-to-end/event/data-provider-expects-argument.phpt +++ b/tests/end-to-end/event/data-provider-expects-argument.phpt @@ -1,31 +1,19 @@ --TEST-- -The right events are emitted in the right order for a test that uses a data provider that is not public ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\ArgumentDataProviderTest::values for test method PHPUnit\TestFixture\Event\ArgumentDataProviderTest::testSuccess) Data Provider Method Finished for PHPUnit\TestFixture\Event\ArgumentDataProviderTest::testSuccess: - PHPUnit\TestFixture\Event\ArgumentDataProviderTest::values @@ -34,7 +22,6 @@ The data provider specified for PHPUnit\TestFixture\Event\ArgumentDataProviderTe Data Provider method PHPUnit\TestFixture\Event\ArgumentDataProviderTest::values() expects an argument Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\ArgumentDataProviderTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/data-provider-external.phpt b/tests/end-to-end/event/data-provider-external.phpt index 3562051b1b1..03ff9791292 100644 --- a/tests/end-to-end/event/data-provider-external.phpt +++ b/tests/end-to-end/event/data-provider-external.phpt @@ -1,36 +1,26 @@ --TEST-- -The right events are emitted in the right order for a successful test that uses an external data provider ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Bootstrap Finished (%sDataProvider.php) +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\DataProvider::values for test method PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess) Data Provider Method Finished for PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess: - PHPUnit\TestFixture\Event\DataProvider::values Test Suite Loaded (2 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -38,12 +28,10 @@ Test Suite Started (PHPUnit\TestFixture\Event\DataProviderExternalTest, 2 tests) Test Suite Started (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#0) Test Prepared (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#0) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#0) Test Finished (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#0) Test Preparation Started (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#1) Test Prepared (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#1) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#1) Test Finished (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess#1) Test Suite Finished (PHPUnit\TestFixture\Event\DataProviderExternalTest::testSuccess, 2 tests) diff --git a/tests/end-to-end/event/data-provider-invalid-argument-name.phpt b/tests/end-to-end/event/data-provider-invalid-argument-name.phpt new file mode 100644 index 00000000000..d5b8b946f5b --- /dev/null +++ b/tests/end-to-end/event/data-provider-invalid-argument-name.phpt @@ -0,0 +1,39 @@ +--TEST-- +The right events are emitted in the right order for a test that uses a data provider that provides data with invalid argument names +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Data Provider Method Called (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::values for test method PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess) +Data Provider Method Finished for PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess: +- PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::values +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest, 2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#0) +Test Prepared (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#0) +Test Passed (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#0) +Test Finished (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#0) +Test Preparation Started (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#1) +Test Prepared (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#1) +Test Errored (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#1) +Unknown named parameter $value3 +Test Finished (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess#1) +Test Suite Finished (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest::testSuccess, 2 tests) +Test Suite Finished (PHPUnit\TestFixture\Event\InvalidParameterNameDataProviderTest, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/data-provider-not-public.phpt b/tests/end-to-end/event/data-provider-not-public.phpt index 5f725336660..eaa601f8dc9 100644 --- a/tests/end-to-end/event/data-provider-not-public.phpt +++ b/tests/end-to-end/event/data-provider-not-public.phpt @@ -1,31 +1,19 @@ --TEST-- The right events are emitted in the right order for a test that uses a data provider that is not public ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\PrivateDataProviderTest::values for test method PHPUnit\TestFixture\Event\PrivateDataProviderTest::testSuccess) Data Provider Method Finished for PHPUnit\TestFixture\Event\PrivateDataProviderTest::testSuccess: - PHPUnit\TestFixture\Event\PrivateDataProviderTest::values @@ -34,7 +22,6 @@ The data provider specified for PHPUnit\TestFixture\Event\PrivateDataProviderTes Data Provider method PHPUnit\TestFixture\Event\PrivateDataProviderTest::values() is not public Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\PrivateDataProviderTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/data-provider-not-static.phpt b/tests/end-to-end/event/data-provider-not-static.phpt index 96095e448df..88916b9b939 100644 --- a/tests/end-to-end/event/data-provider-not-static.phpt +++ b/tests/end-to-end/event/data-provider-not-static.phpt @@ -1,31 +1,19 @@ --TEST-- The right events are emitted in the right order for a test that uses a data provider that is not static ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\DynamicDataProviderTest::values for test method PHPUnit\TestFixture\Event\DynamicDataProviderTest::testSuccess) Data Provider Method Finished for PHPUnit\TestFixture\Event\DynamicDataProviderTest::testSuccess: - PHPUnit\TestFixture\Event\DynamicDataProviderTest::values @@ -34,7 +22,6 @@ The data provider specified for PHPUnit\TestFixture\Event\DynamicDataProviderTes Data Provider method PHPUnit\TestFixture\Event\DynamicDataProviderTest::values() is not static Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\DynamicDataProviderTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/data-provider.phpt b/tests/end-to-end/event/data-provider.phpt index 246631abd13..f50444ec77a 100644 --- a/tests/end-to-end/event/data-provider.phpt +++ b/tests/end-to-end/event/data-provider.phpt @@ -1,36 +1,23 @@ --TEST-- The right events are emitted in the right order for a successful test that uses a data provider ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\DataProviderTest::values for test method PHPUnit\TestFixture\Event\DataProviderTest::testSuccess) Data Provider Method Finished for PHPUnit\TestFixture\Event\DataProviderTest::testSuccess: - PHPUnit\TestFixture\Event\DataProviderTest::values Test Suite Loaded (2 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -38,12 +25,10 @@ Test Suite Started (PHPUnit\TestFixture\Event\DataProviderTest, 2 tests) Test Suite Started (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#0) Test Prepared (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#0) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#0) Test Finished (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#0) Test Preparation Started (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#1) Test Prepared (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#1) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#1) Test Finished (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess#1) Test Suite Finished (PHPUnit\TestFixture\Event\DataProviderTest::testSuccess, 2 tests) diff --git a/tests/end-to-end/event/deprecations-can-be-ignored-using-attribute.phpt b/tests/end-to-end/event/deprecations-can-be-ignored-using-attribute.phpt index 6f7643a073e..b0dc9e7b433 100644 --- a/tests/end-to-end/event/deprecations-can-be-ignored-using-attribute.phpt +++ b/tests/end-to-end/event/deprecations-can-be-ignored-using-attribute.phpt @@ -1,52 +1,49 @@ --TEST-- https://github.com/sebastianbergmann/phpunit/issues/5532 ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (4 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (2 tests) -Test Suite Started (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest, 2 tests) +Test Runner Execution Started (4 tests) +Test Suite Started (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest, 4 tests) Test Preparation Started (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOne) -Test Triggered Test-Ignored Deprecation (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOne, unknown if issue was triggered in first-party code or third-party code, ignored by test) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOne) Test Finished (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOne) Test Preparation Started (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo) Test Prepared (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo) -Test Triggered Deprecation (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo, unknown if issue was triggered in first-party code or third-party code) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo) Test Finished (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo) -Test Suite Finished (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOneErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOneErrorGetLast) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOneErrorGetLast, unknown if issue was triggered in first-party code or third-party code, ignored by test) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOneErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testOneErrorGetLast) +Test Preparation Started (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwoErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwoErrorGetLast) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwoErrorGetLast, unknown if issue was triggered in first-party code or third-party code) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwoErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwoErrorGetLast) +Test Suite Finished (PHPUnit\TestFixture\Event\IgnoreDeprecationsTest, 4 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/duplicated-cli-options.phpt b/tests/end-to-end/event/duplicated-cli-options.phpt new file mode 100644 index 00000000000..02a599c5764 --- /dev/null +++ b/tests/end-to-end/event/duplicated-cli-options.phpt @@ -0,0 +1,31 @@ +--TEST-- +The right events are emitted in the right order when duplicated CLI options are used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Triggered Warning (Option --filter cannot be used more than once) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (0 tests) +Test Runner Execution Started (0 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 1) diff --git a/tests/end-to-end/event/error-handler-can-be-disabled.phpt b/tests/end-to-end/event/error-handler-can-be-disabled.phpt index 42e5d71f124..f3609a41a2b 100644 --- a/tests/end-to-end/event/error-handler-can-be-disabled.phpt +++ b/tests/end-to-end/event/error-handler-can-be-disabled.phpt @@ -1,18 +1,9 @@ --TEST-- The right events are emitted in the right order when PHPUnit's error handler is disabled ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Bootstrap Finished (%s/src/Foo.php) -Test Suite Loaded (2 tests) +Bootstrap Finished (%s%esrc/Foo.php) Event Facade Sealed +Test Suite Loaded (4 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (2 tests) -Test Suite Started (%s/phpunit.xml, 2 tests) -Test Suite Started (default, 2 tests) -Test Suite Started (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest, 2 tests) +Test Runner Execution Started (4 tests) +Test Suite Started (%s%ephpunit.xml, 4 tests) +Test Suite Started (default, 4 tests) +Test Suite Started (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest, 4 tests) Test Preparation Started (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodA) Test Prepared (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodA) -Assertion Succeeded (Constraint: exception of type "Exception", Value: {enable export of objects to see this value}) -Assertion Succeeded (Constraint: exception message contains 'Failed to open stream', Value: 'fopen(%s/missing/directory): Failed to open stream: No such file or directory') Test Passed (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodA) Test Finished (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodA) Test Preparation Started (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodB) Test Prepared (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodB) -Assertion Succeeded (Constraint: is identical to 'Triggering', Value: 'Triggering') Test Passed (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodB) Test Finished (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testMethodB) -Test Suite Finished (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest, 2 tests) -Test Suite Finished (default, 2 tests) -Test Suite Finished (%s/phpunit.xml, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerSet) +Test Prepared (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerSet) +Test Passed (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerSet) +Test Finished (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerSet) +Test Preparation Started (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerIsNotSet) +Test Prepared (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerIsNotSet) +Test Passed (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerIsNotSet) +Test Finished (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest::testErrorHandlerIsNotSet) +Test Suite Finished (PHPUnit\TestFixture\Event\ErrorHandlerCanBeDisabled\FooTest, 4 tests) +Test Suite Finished (default, 4 tests) +Test Suite Finished (%s%ephpunit.xml, 4 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/error.phpt b/tests/end-to-end/event/error.phpt index 19fc8e9bb8e..76a965154af 100644 --- a/tests/end-to-end/event/error.phpt +++ b/tests/end-to-end/event/error.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for an errored test ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/exception-in-after-class-method.phpt b/tests/end-to-end/event/exception-in-after-class-method.phpt new file mode 100644 index 00000000000..668310abad9 --- /dev/null +++ b/tests/end-to-end/event/exception-in-after-class-method.phpt @@ -0,0 +1,33 @@ +--TEST-- +The right events are emitted in the right order for when an exception is raised in tearDownAfterClass() +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::testOne) +Test Passed (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::testOne) +After Last Test Method Called (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::tearDownAfterClass) +After Last Test Method Errored (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::tearDownAfterClass) +After Last Test Method Finished: +- PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest::tearDownAfterClass +Test Suite Finished (PHPUnit\TestFixture\Event\ExceptionInTearDownAfterClassTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/exception-in-after-test-method.phpt b/tests/end-to-end/event/exception-in-after-test-method.phpt new file mode 100644 index 00000000000..4a38944c3d9 --- /dev/null +++ b/tests/end-to-end/event/exception-in-after-test-method.phpt @@ -0,0 +1,33 @@ +--TEST-- +The right events are emitted in the right order for when an exception is raised in tearDown() +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\ExceptionInTearDownTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\ExceptionInTearDownTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\ExceptionInTearDownTest::testOne) +After Test Method Called (PHPUnit\TestFixture\Event\ExceptionInTearDownTest::tearDown) +After Test Method Errored (PHPUnit\TestFixture\Event\ExceptionInTearDownTest::tearDown) +After Test Method Finished: +- PHPUnit\TestFixture\Event\ExceptionInTearDownTest::tearDown +Test Errored (PHPUnit\TestFixture\Event\ExceptionInTearDownTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\ExceptionInTearDownTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\Event\ExceptionInTearDownTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/exception-in-before-class-method.phpt b/tests/end-to-end/event/exception-in-before-class-method.phpt new file mode 100644 index 00000000000..72278600bbb --- /dev/null +++ b/tests/end-to-end/event/exception-in-before-class-method.phpt @@ -0,0 +1,29 @@ +--TEST-- +The right events are emitted in the right order for when an exception is raised in setUpBeforeClass() +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest, 1 test) +Before First Test Method Called (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest::setUpBeforeClass) +Before First Test Method Errored (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest::setUpBeforeClass) +Before First Test Method Finished: +- PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest::setUpBeforeClass +Test Suite Finished (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/exception-in-before-test-method.phpt b/tests/end-to-end/event/exception-in-before-test-method.phpt new file mode 100644 index 00000000000..48eb284df00 --- /dev/null +++ b/tests/end-to-end/event/exception-in-before-test-method.phpt @@ -0,0 +1,32 @@ +--TEST-- +The right events are emitted in the right order for when an exception is raised in setUp() +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\ExceptionInSetUpTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::testOne) +Before Test Method Called (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::setUp) +Before Test Method Errored (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::setUp) +Before Test Method Finished: +- PHPUnit\TestFixture\Event\ExceptionInSetUpTest::setUp +Test Preparation Errored (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::testOne) +Test Errored (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\Event\ExceptionInSetUpTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/exception-in-setup-before-class.phpt b/tests/end-to-end/event/exception-in-setup-before-class.phpt deleted file mode 100644 index d2a558aed9d..00000000000 --- a/tests/end-to-end/event/exception-in-setup-before-class.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -The right events are emitted in the right order for when an exception is raised in setUpBeforeClass() ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); ---EXPECTF-- -PHPUnit Started (PHPUnit %s using %s) -Test Runner Configured -Test Suite Loaded (1 test) -Event Facade Sealed -Test Runner Started -Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest, 1 test) -Before First Test Method Called (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest::setUpBeforeClass) -Before First Test Method Errored (PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest::setUpBeforeClass) -Before First Test Method Finished: -- PHPUnit\TestFixture\Event\ExceptionInSetUpBeforeClassTest::setUpBeforeClass -Test Runner Execution Finished -Test Runner Finished -PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/exception-in-setup.phpt b/tests/end-to-end/event/exception-in-setup.phpt deleted file mode 100644 index 2a897cfdb26..00000000000 --- a/tests/end-to-end/event/exception-in-setup.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -The right events are emitted in the right order for when an exception is raised in setUp() ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); ---EXPECTF-- -PHPUnit Started (PHPUnit %s using %s) -Test Runner Configured -Test Suite Loaded (1 test) -Event Facade Sealed -Test Runner Started -Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\ExceptionInSetUpTest, 1 test) -Test Preparation Started (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::testOne) -Before Test Method Called (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::setUp) -Before Test Method Finished: -- PHPUnit\TestFixture\Event\ExceptionInSetUpTest::setUp -Test Errored (PHPUnit\TestFixture\Event\ExceptionInSetUpTest::testOne) -Test Suite Finished (PHPUnit\TestFixture\Event\ExceptionInSetUpTest, 1 test) -Test Runner Execution Finished -Test Runner Finished -PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/expectation-on-output.phpt b/tests/end-to-end/event/expectation-on-output.phpt index 3d3311ef43a..55c4d59cdad 100644 --- a/tests/end-to-end/event/expectation-on-output.phpt +++ b/tests/end-to-end/event/expectation-on-output.phpt @@ -1,51 +1,35 @@ --TEST-- The right events are emitted in the right order for a test with an output expectation ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (3 tests) Event Facade Sealed +Test Suite Loaded (3 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (3 tests) Test Suite Started (PHPUnit\TestFixture\Issue445Test, 3 tests) Test Preparation Started (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationBefore) Test Prepared (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationBefore) -Assertion Succeeded (Constraint: is identical to 'test', Value: 'test') Test Passed (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationBefore) Test Finished (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationBefore) Test Preparation Started (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationAfter) Test Prepared (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationAfter) -Assertion Succeeded (Constraint: is identical to 'test', Value: 'test') Test Passed (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationAfter) Test Finished (PHPUnit\TestFixture\Issue445Test::testOutputWithExpectationAfter) Test Preparation Started (PHPUnit\TestFixture\Issue445Test::testNotMatchingOutput) Test Prepared (PHPUnit\TestFixture\Issue445Test::testNotMatchingOutput) -Assertion Failed (Constraint: is identical to 'foo', Value: 'bar') Test Failed (PHPUnit\TestFixture\Issue445Test::testNotMatchingOutput) Failed asserting that two strings are identical. Test Finished (PHPUnit\TestFixture\Issue445Test::testNotMatchingOutput) diff --git a/tests/end-to-end/event/expected-and-unexpected-assertions.phpt b/tests/end-to-end/event/expected-and-unexpected-assertions.phpt new file mode 100644 index 00000000000..8dc28eea4ab --- /dev/null +++ b/tests/end-to-end/event/expected-and-unexpected-assertions.phpt @@ -0,0 +1,45 @@ +--TEST-- +The right events are emitted in the right order when a test that is not expected to perform assertions does not perform assertions and when a test that is not expected to perform assertions does perform assertions +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (4 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (4 tests) +Test Suite Started (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest, 4 tests) +Test Preparation Started (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testOne) +Test Passed (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testTwo) +Test Prepared (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testTwo) +Test Passed (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testTwo) +Test Finished (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testTwo) +Test Preparation Started (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testThree) +Test Prepared (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testThree) +Test Passed (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testThree) +Test Considered Risky (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testThree) +This test is not expected to perform assertions but performed 1 assertion +Test Finished (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testThree) +Test Preparation Started (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testFour) +Test Prepared (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testFour) +Test Passed (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testFour) +Test Considered Risky (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testFour) +This test is not expected to perform assertions but performed 1 assertion +Test Finished (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest::testFour) +Test Suite Finished (PHPUnit\TestFixture\Event\ExpectedAndUnexpectedAssertionsTest, 4 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/failed-mock-expectation.phpt b/tests/end-to-end/event/failed-mock-expectation.phpt index 219d771a32f..cec67ead8ff 100644 --- a/tests/end-to-end/event/failed-mock-expectation.phpt +++ b/tests/end-to-end/event/failed-mock-expectation.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test with a failed expectation on a mock object ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/incomplete-test.phpt b/tests/end-to-end/event/incomplete-test.phpt index 01259c659af..f7fec9b8e58 100644 --- a/tests/end-to-end/event/incomplete-test.phpt +++ b/tests/end-to-end/event/incomplete-test.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for an incomplete test ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/invalid-coverage-metadata.phpt b/tests/end-to-end/event/invalid-coverage-metadata.phpt index 67d86b47fc9..8c7418abb58 100644 --- a/tests/end-to-end/event/invalid-coverage-metadata.phpt +++ b/tests/end-to-end/event/invalid-coverage-metadata.phpt @@ -2,52 +2,47 @@ The right events are emitted in the right order for a test that has invalid code coverage metadata --SKIPIF-- run($_SERVER['argv']); +--CLEAN-- +run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::provider for test method PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testOne) Data Provider Method Finished for PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testOne: - PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::provider Test Triggered PHPUnit Error (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testOne) The data provider specified for PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testOne is invalid -Data set #0 is invalid +Data set #0 is invalid, expected array but got int Test Suite Loaded (1 test) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testTwo) Test Prepared (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testTwo) Test Finished (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest::testTwo) Test Suite Finished (PHPUnit\TestFixture\Event\InvalidDataProviderWithOneTestPassingTest, 1 test) diff --git a/tests/end-to-end/event/invalid-data-provider.phpt b/tests/end-to-end/event/invalid-data-provider.phpt index 7eaa99b5bb9..48a8e9e8e35 100644 --- a/tests/end-to-end/event/invalid-data-provider.phpt +++ b/tests/end-to-end/event/invalid-data-provider.phpt @@ -1,40 +1,27 @@ --TEST-- The right events are emitted in the right order for a test that uses a data provider that returns an invalid array ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Event\InvalidDataProviderTest::provider for test method PHPUnit\TestFixture\Event\InvalidDataProviderTest::testOne) Data Provider Method Finished for PHPUnit\TestFixture\Event\InvalidDataProviderTest::testOne: - PHPUnit\TestFixture\Event\InvalidDataProviderTest::provider Test Triggered PHPUnit Error (PHPUnit\TestFixture\Event\InvalidDataProviderTest::testOne) The data provider specified for PHPUnit\TestFixture\Event\InvalidDataProviderTest::testOne is invalid -Data set #0 is invalid +Data set #0 is invalid, expected array but got int Test Runner Triggered Warning (No tests found in class "PHPUnit\TestFixture\Event\InvalidDataProviderTest".) Test Suite Loaded (0 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (0 tests) diff --git a/tests/end-to-end/event/invalid-test-dependency.phpt b/tests/end-to-end/event/invalid-test-dependency.phpt index ce367cd9a73..1c660608561 100644 --- a/tests/end-to-end/event/invalid-test-dependency.phpt +++ b/tests/end-to-end/event/invalid-test-dependency.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test that has an invalid dependency ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) diff --git a/tests/end-to-end/event/missing-test-dependency.phpt b/tests/end-to-end/event/missing-test-dependency.phpt index b883ce3746e..6979f3d828c 100644 --- a/tests/end-to-end/event/missing-test-dependency.phpt +++ b/tests/end-to-end/event/missing-test-dependency.phpt @@ -1,40 +1,26 @@ --TEST-- The right events are emitted in the right order for a test that has a missing dependency ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\Event\MissingDependencyTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\MissingDependencyTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\MissingDependencyTest::testOne) -Assertion Failed (Constraint: is true, Value: false) Test Failed (PHPUnit\TestFixture\Event\MissingDependencyTest::testOne) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\Event\MissingDependencyTest::testOne) diff --git a/tests/end-to-end/event/mock-object.phpt b/tests/end-to-end/event/mock-object.phpt index 0f407fe0c98..a0067688e30 100644 --- a/tests/end-to-end/event/mock-object.phpt +++ b/tests/end-to-end/event/mock-object.phpt @@ -1,33 +1,23 @@ --TEST-- The right events are emitted in the right order for a test that uses a mock object ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) +Bootstrap Finished (%sExample.php) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) @@ -35,7 +25,6 @@ Test Suite Started (PHPUnit\TestFixture\Event\MockTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\MockTest::testSuccess) Test Prepared (PHPUnit\TestFixture\Event\MockTest::testSuccess) Mock Object Created (PHPUnit\TestFixture\Event\Example) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\MockTest::testSuccess) Test Finished (PHPUnit\TestFixture\Event\MockTest::testSuccess) Test Suite Finished (PHPUnit\TestFixture\Event\MockTest, 1 test) diff --git a/tests/end-to-end/event/php-deprecated.phpt b/tests/end-to-end/event/php-deprecated.phpt index 21e4aceb8d8..ab649d45e98 100644 --- a/tests/end-to-end/event/php-deprecated.phpt +++ b/tests/end-to-end/event/php-deprecated.phpt @@ -1,51 +1,33 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers E_DEPRECATED ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature) Test Prepared (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature) -Test Triggered Suppressed PHP Deprecation (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature) -Creation of dynamic property PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::$foo is deprecated -Test Triggered PHP Deprecation (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature) -Creation of dynamic property PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::$bar is deprecated -Assertion Succeeded (Constraint: is true, Value: true) +Test Triggered PHP Deprecation (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature, unknown if issue was triggered in first-party code or third-party code) in %s:%d +strlen(): Passing null to parameter #1 ($string) of type string is deprecated +Test Triggered PHP Deprecation (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature, unknown if issue was triggered in first-party code or third-party code, suppressed using operator) in %s:%d +strlen(): Passing null to parameter #1 ($string) of type string is deprecated Test Passed (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature) Test Finished (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest::testDeprecatedPhpFeature) Test Suite Finished (PHPUnit\TestFixture\Event\DeprecatedPhpFeatureTest, 1 test) diff --git a/tests/end-to-end/event/php-notice.phpt b/tests/end-to-end/event/php-notice.phpt index 74340442bbe..e998541ec6c 100644 --- a/tests/end-to-end/event/php-notice.phpt +++ b/tests/end-to-end/event/php-notice.phpt @@ -1,45 +1,31 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers E_NOTICE ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\PhpNoticeTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) Test Prepared (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) -Test Triggered PHP Notice (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) +Test Triggered PHP Notice (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) in %s:%d Only variables should be assigned by reference -Test Triggered Suppressed PHP Notice (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) +Test Triggered PHP Notice (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice, suppressed using operator) in %s:%d Only variables should be assigned by reference -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) Test Finished (PHPUnit\TestFixture\Event\PhpNoticeTest::testPhpNotice) Test Suite Finished (PHPUnit\TestFixture\Event\PhpNoticeTest, 1 test) diff --git a/tests/end-to-end/event/php-warning.phpt b/tests/end-to-end/event/php-warning.phpt index db365810191..cc312f7ff7e 100644 --- a/tests/end-to-end/event/php-warning.phpt +++ b/tests/end-to-end/event/php-warning.phpt @@ -1,45 +1,31 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers E_WARNING ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\PhpWarningTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) Test Prepared (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) -Test Triggered PHP Warning (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) +Test Triggered PHP Warning (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) in %s:%d Undefined variable $b -Test Triggered Suppressed PHP Warning (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) +Test Triggered PHP Warning (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning, suppressed using operator) in %s:%d Undefined variable $b -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) Test Finished (PHPUnit\TestFixture\Event\PhpWarningTest::testPhpWarning) Test Suite Finished (PHPUnit\TestFixture\Event\PhpWarningTest, 1 test) diff --git a/tests/end-to-end/event/phpt-clean-process-polluting.phpt b/tests/end-to-end/event/phpt-clean-process-polluting.phpt new file mode 100644 index 00000000000..a584b4a443f --- /dev/null +++ b/tests/end-to-end/event/phpt-clean-process-polluting.phpt @@ -0,0 +1,33 @@ +--TEST-- +The right events are emitted in the right order for a PHPT test with a CLEAN section which pollutes the process +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%sphpt-clean-process-polluting.phpt, 1 test) +Test Preparation Started (%sphpt-clean-process-polluting.phpt) +Test Prepared (%sphpt-clean-process-polluting.phpt) +Child Process Started +Child Process Finished +Test Passed (%sphpt-clean-process-polluting.phpt) +Child Process Started +Child Process Finished +Test Finished (%sphpt-clean-process-polluting.phpt) +Test Suite Finished (%sphpt-clean-process-polluting.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-clean-with-io.phpt b/tests/end-to-end/event/phpt-clean-with-io.phpt new file mode 100644 index 00000000000..ab6f09d48b2 --- /dev/null +++ b/tests/end-to-end/event/phpt-clean-with-io.phpt @@ -0,0 +1,31 @@ +--TEST-- +The right events are emitted in the right order for a PHPT test with a CLEAN section which pollutes the process +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%sphpt-clean-with-io.phpt, 1 test) +Test Preparation Started (%sphpt-clean-with-io.phpt) +Test Prepared (%sphpt-clean-with-io.phpt) +Child Process Started +Child Process Finished +Test Passed (%sphpt-clean-with-io.phpt) +Test Finished (%sphpt-clean-with-io.phpt) +Test Suite Finished (%sphpt-clean-with-io.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-clean.phpt b/tests/end-to-end/event/phpt-clean.phpt new file mode 100644 index 00000000000..f023db4ebaf --- /dev/null +++ b/tests/end-to-end/event/phpt-clean.phpt @@ -0,0 +1,31 @@ +--TEST-- +The right events are emitted in the right order for a PHPT test with a CLEAN section +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%sphpt-clean.phpt, 1 test) +Test Preparation Started (%sphpt-clean.phpt) +Test Prepared (%sphpt-clean.phpt) +Child Process Started +Child Process Finished +Test Passed (%sphpt-clean.phpt) +Test Finished (%sphpt-clean.phpt) +Test Suite Finished (%sphpt-clean.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-ini-clean.phpt b/tests/end-to-end/event/phpt-ini-clean.phpt new file mode 100644 index 00000000000..06de7864d46 --- /dev/null +++ b/tests/end-to-end/event/phpt-ini-clean.phpt @@ -0,0 +1,33 @@ +--TEST-- +The right events are emitted in the right order for a PHPT test using a subprocess via --INI-- +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%ephpt-ini-clean.phpt, 1 test) +Test Preparation Started (%s%ephpt-ini-clean.phpt) +Test Prepared (%s%ephpt-ini-clean.phpt) +Child Process Started +Child Process Finished +Test Passed (%sphpt-ini-clean.phpt) +Child Process Started +Child Process Finished +Test Finished (%s%ephpt-ini-clean.phpt) +Test Suite Finished (%s%ephpt-ini-clean.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-ini-subprocess.phpt b/tests/end-to-end/event/phpt-ini-subprocess.phpt new file mode 100644 index 00000000000..4231077f803 --- /dev/null +++ b/tests/end-to-end/event/phpt-ini-subprocess.phpt @@ -0,0 +1,33 @@ +--TEST-- +The right events are emitted in the right order for a PHPT test using a subprocess via --INI-- +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%ephpt-ini-subprocess.phpt, 1 test) +Test Preparation Started (%s%ephpt-ini-subprocess.phpt) +Test Prepared (%s%ephpt-ini-subprocess.phpt) +Child Process Started +Child Process Finished +Child Process Started +Child Process Finished +Test Passed (%s%ephpt-ini-subprocess.phpt) +Test Finished (%s%ephpt-ini-subprocess.phpt) +Test Suite Finished (%s%ephpt-ini-subprocess.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-skipif-closing-php-tag.phpt b/tests/end-to-end/event/phpt-skipif-closing-php-tag.phpt new file mode 100644 index 00000000000..c4447974aba --- /dev/null +++ b/tests/end-to-end/event/phpt-skipif-closing-php-tag.phpt @@ -0,0 +1,30 @@ +--TEST-- +The right events are emitted in the right order for a skipped PHPT test +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%ephpt-skipif-closing-php-tag.phpt, 1 test) +Test Preparation Started (%s%ephpt-skipif-closing-php-tag.phpt) +Test Prepared (%s%ephpt-skipif-closing-php-tag.phpt) +Test Skipped (%s%ephpt-skipif-closing-php-tag.phpt) +something terrible happened +Test Finished (%s%ephpt-skipif-closing-php-tag.phpt) +Test Suite Finished (%s%ephpt-skipif-closing-php-tag.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-skipif-io.phpt b/tests/end-to-end/event/phpt-skipif-io.phpt new file mode 100644 index 00000000000..e16219bec76 --- /dev/null +++ b/tests/end-to-end/event/phpt-skipif-io.phpt @@ -0,0 +1,31 @@ +--TEST-- +The right events are emitted in the right order for PHPT test using IO in skipif +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%ephpt-skipif-io.phpt, 1 test) +Test Preparation Started (%s%ephpt-skipif-io.phpt) +Test Prepared (%s%ephpt-skipif-io.phpt) +Child Process Started +Child Process Finished +Test Passed (%s%ephpt-skipif-io.phpt) +Test Finished (%s%ephpt-skipif-io.phpt) +Test Suite Finished (%s%ephpt-skipif-io.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-skipif-require.phpt b/tests/end-to-end/event/phpt-skipif-require.phpt new file mode 100644 index 00000000000..aaf4a57a2f2 --- /dev/null +++ b/tests/end-to-end/event/phpt-skipif-require.phpt @@ -0,0 +1,33 @@ +--TEST-- +The right events are emitted in the right order for PHPT test using require() in skipif +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%ephpt-skipif-require.phpt, 1 test) +Test Preparation Started (%s%ephpt-skipif-require.phpt) +Test Prepared (%s%ephpt-skipif-require.phpt) +Child Process Started +Child Process Finished +Child Process Started +Child Process Finished +Test Passed (%s%ephpt-skipif-require.phpt) +Test Finished (%s%ephpt-skipif-require.phpt) +Test Suite Finished (%s%ephpt-skipif-require.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-skipif-subprocess.phpt b/tests/end-to-end/event/phpt-skipif-subprocess.phpt new file mode 100644 index 00000000000..e54c99c158d --- /dev/null +++ b/tests/end-to-end/event/phpt-skipif-subprocess.phpt @@ -0,0 +1,32 @@ +--TEST-- +The right events are emitted in the right order for a skipped PHPT test using a skipif subprocess +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%ephpt-skipif-exit-subprocess.phpt, 1 test) +Test Preparation Started (%s%ephpt-skipif-exit-subprocess.phpt) +Test Prepared (%s%ephpt-skipif-exit-subprocess.phpt) +Child Process Started +Child Process Finished +Test Skipped (%s%ephpt-skipif-exit-subprocess.phpt) +is test +Test Finished (%s%ephpt-skipif-exit-subprocess.phpt) +Test Suite Finished (%s%ephpt-skipif-exit-subprocess.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpt-skipif.phpt b/tests/end-to-end/event/phpt-skipif.phpt index 238ed9b0cc4..f3bb2853bed 100644 --- a/tests/end-to-end/event/phpt-skipif.phpt +++ b/tests/end-to-end/event/phpt-skipif.phpt @@ -1,43 +1,30 @@ --TEST-- The right events are emitted in the right order for a skipped PHPT test ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) -Test Suite Started (%s/phpt-skipif-location-hint-example.phpt, 1 test) -Test Preparation Started (%s/phpt-skipif-location-hint-example.phpt) -Test Prepared (%s/phpt-skipif-location-hint-example.phpt) -Test Skipped (%s/phpt-skipif-location-hint-example.phpt) +Test Suite Started (%s%ephpt-skipif-location-hint-example.phpt, 1 test) +Test Preparation Started (%s%ephpt-skipif-location-hint-example.phpt) +Test Prepared (%s%ephpt-skipif-location-hint-example.phpt) +Test Skipped (%s%ephpt-skipif-location-hint-example.phpt) something terrible happened -Test Finished (%s/phpt-skipif-location-hint-example.phpt) -Test Suite Finished (%s/phpt-skipif-location-hint-example.phpt, 1 test) +Test Finished (%s%ephpt-skipif-location-hint-example.phpt) +Test Suite Finished (%s%ephpt-skipif-location-hint-example.phpt, 1 test) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpunit-deprecated-ignored.phpt b/tests/end-to-end/event/phpunit-deprecated-ignored.phpt new file mode 100644 index 00000000000..d7f36d52076 --- /dev/null +++ b/tests/end-to-end/event/phpunit-deprecated-ignored.phpt @@ -0,0 +1,29 @@ +--TEST-- +The right events are emitted in the right order for a test that uses a deprecated PHPUnit feature when PHPUnit deprecations are ignored using attribute +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\IgnoredDeprecatedPhpunitFeatureTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\IgnoredDeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) +Test Prepared (PHPUnit\TestFixture\Event\IgnoredDeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) +Test Passed (PHPUnit\TestFixture\Event\IgnoredDeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) +Test Finished (PHPUnit\TestFixture\Event\IgnoredDeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) +Test Suite Finished (PHPUnit\TestFixture\Event\IgnoredDeprecatedPhpunitFeatureTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpunit-deprecated.phpt b/tests/end-to-end/event/phpunit-deprecated.phpt index bc517195808..42b74026e6e 100644 --- a/tests/end-to-end/event/phpunit-deprecated.phpt +++ b/tests/end-to-end/event/phpunit-deprecated.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test that uses a deprecated PHPUnit feature ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) @@ -36,7 +23,6 @@ Test Preparation Started (PHPUnit\TestFixture\Event\DeprecatedPhpunitFeatureTest Test Prepared (PHPUnit\TestFixture\Event\DeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) Test Triggered PHPUnit Deprecation (PHPUnit\TestFixture\Event\DeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\DeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) Test Finished (PHPUnit\TestFixture\Event\DeprecatedPhpunitFeatureTest::testDeprecatedPhpunitFeature) Test Suite Finished (PHPUnit\TestFixture\Event\DeprecatedPhpunitFeatureTest, 1 test) diff --git a/tests/end-to-end/event/phpunit-notice.phpt b/tests/end-to-end/event/phpunit-notice.phpt new file mode 100644 index 00000000000..d38ca29310c --- /dev/null +++ b/tests/end-to-end/event/phpunit-notice.phpt @@ -0,0 +1,32 @@ +--TEST-- +The right events are emitted in the right order for a test that runs code which triggers E_USER_WARNING +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\PhpunitNoticeTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\PhpunitNoticeTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\PhpunitNoticeTest::testOne) +Test Triggered PHPUnit Notice (PHPUnit\TestFixture\Event\PhpunitNoticeTest::testOne) +message +Test Runner Triggered Notice (message) +Test Passed (PHPUnit\TestFixture\Event\PhpunitNoticeTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\PhpunitNoticeTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\Event\PhpunitNoticeTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/phpunit-warning.phpt b/tests/end-to-end/event/phpunit-warning.phpt index 55ad702b347..c21366a43da 100644 --- a/tests/end-to-end/event/phpunit-warning.phpt +++ b/tests/end-to-end/event/phpunit-warning.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers a PHPUnit warning ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) @@ -36,7 +23,6 @@ Test Preparation Started (PHPUnit\TestFixture\Event\PhpunitWarningTest::testPhpu Test Prepared (PHPUnit\TestFixture\Event\PhpunitWarningTest::testPhpunitWarning) Test Triggered PHPUnit Warning (PHPUnit\TestFixture\Event\PhpunitWarningTest::testPhpunitWarning) message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\PhpunitWarningTest::testPhpunitWarning) Test Finished (PHPUnit\TestFixture\Event\PhpunitWarningTest::testPhpunitWarning) Test Suite Finished (PHPUnit\TestFixture\Event\PhpunitWarningTest, 1 test) diff --git a/tests/end-to-end/event/process-isolation-disable-xdebug.phpt b/tests/end-to-end/event/process-isolation-disable-xdebug.phpt new file mode 100644 index 00000000000..a3dbefafb1f --- /dev/null +++ b/tests/end-to-end/event/process-isolation-disable-xdebug.phpt @@ -0,0 +1,40 @@ +--TEST-- +Subprocesses auto-disable xdebug when no debugger is attached. +--SKIPIF-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\XdebugIsDisabled, 1 test) +Child Process Started +Test Preparation Started (PHPUnit\TestFixture\Event\XdebugIsDisabled::testOne) +Test Prepared (PHPUnit\TestFixture\Event\XdebugIsDisabled::testOne) +Test Passed (PHPUnit\TestFixture\Event\XdebugIsDisabled::testOne) +Test Finished (PHPUnit\TestFixture\Event\XdebugIsDisabled::testOne) +Child Process Finished +Test Suite Finished (PHPUnit\TestFixture\Event\XdebugIsDisabled, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/process-isolation-fatal.phpt b/tests/end-to-end/event/process-isolation-fatal.phpt index e116c66d435..f3d71d320a4 100644 --- a/tests/end-to-end/event/process-isolation-fatal.phpt +++ b/tests/end-to-end/event/process-isolation-fatal.phpt @@ -1,43 +1,32 @@ --TEST-- The right events are emitted in the right order for a test that is run in process isolation and triggers a fatal error ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\FatalTest, 1 test) +Child Process Started Test Preparation Started (PHPUnit\TestFixture\Event\FatalTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\FatalTest::testOne) Test Errored (PHPUnit\TestFixture\Event\FatalTest::testOne) Call to undefined function PHPUnit\TestFixture\Event\doesNotExist() Test Finished (PHPUnit\TestFixture\Event\FatalTest::testOne) +Child Process Finished Test Suite Finished (PHPUnit\TestFixture\Event\FatalTest, 1 test) Test Runner Execution Finished Test Runner Finished diff --git a/tests/end-to-end/event/registered-failure-interface.phpt b/tests/end-to-end/event/registered-failure-interface.phpt index 9ae71e11196..d05df537573 100644 --- a/tests/end-to-end/event/registered-failure-interface.phpt +++ b/tests/end-to-end/event/registered-failure-interface.phpt @@ -1,40 +1,29 @@ --TEST-- The right events are emitted in the right order for a test that registers a failure interface ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) +Bootstrap Finished (%sbootstrap.php) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Suite Started (PHPUnit\TestFixture\Event\CustomFailureInterfaceTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\CustomFailureInterfaceTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\CustomFailureInterfaceTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Failed (PHPUnit\TestFixture\Event\CustomFailureInterfaceTest::testOne) this should be treated as a failure Test Finished (PHPUnit\TestFixture\Event\CustomFailureInterfaceTest::testOne) diff --git a/tests/end-to-end/event/risky-depends-on-larger-test.phpt b/tests/end-to-end/event/risky-depends-on-larger-test.phpt index 0d469d6f422..42ae306fdac 100644 --- a/tests/end-to-end/event/risky-depends-on-larger-test.phpt +++ b/tests/end-to-end/event/risky-depends-on-larger-test.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test that is considered risky because it depends on a larger test ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -35,13 +22,16 @@ Test Suite Started (CLI Arguments, 2 tests) Test Suite Started (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\LargeTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\LargeTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\LargeTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\LargeTest::testOne) Test Finished (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\LargeTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\LargeTest, 1 test) Test Suite Started (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest, 1 test) Test Considered Risky (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest::testOne) This test depends on a test that is larger than itself +Test Preparation Started (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest::testOne) +Test Passed (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Event\RiskyBecauseDependencyOnLargerTest\SmallTest, 1 test) Test Suite Finished (CLI Arguments, 2 tests) Test Runner Execution Finished diff --git a/tests/end-to-end/event/risky-global-state-modification.phpt b/tests/end-to-end/event/risky-global-state-modification.phpt index b996e1c2cc1..0f7db3330de 100644 --- a/tests/end-to-end/event/risky-global-state-modification.phpt +++ b/tests/end-to-end/event/risky-global-state-modification.phpt @@ -1,35 +1,22 @@ --TEST-- The right events are emitted in the right order for a test that is considered risky because it modified global state ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/risky-no-assertions-isolation.phpt b/tests/end-to-end/event/risky-no-assertions-isolation.phpt index 74ba3bf6d5c..93390874eaa 100644 --- a/tests/end-to-end/event/risky-no-assertions-isolation.phpt +++ b/tests/end-to-end/event/risky-no-assertions-isolation.phpt @@ -1,44 +1,33 @@ --TEST-- The right events are emitted in the right order for a test that is run in an isolated process and is considered risky because it did not perform assertions ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest, 1 test) +Child Process Started Test Preparation Started (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest::testOne) Test Passed (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest::testOne) Test Considered Risky (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest::testOne) This test did not perform any assertions Test Finished (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest::testOne) +Child Process Finished Test Suite Finished (PHPUnit\TestFixture\Event\RiskyBecauseNoAssertionsTest, 1 test) Test Runner Execution Finished Test Runner Finished diff --git a/tests/end-to-end/event/risky-no-assertions.phpt b/tests/end-to-end/event/risky-no-assertions.phpt index a42dc4abde6..e42b0c0110c 100644 --- a/tests/end-to-end/event/risky-no-assertions.phpt +++ b/tests/end-to-end/event/risky-no-assertions.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test that is considered risky because it did not perform assertions ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/risky-output.phpt b/tests/end-to-end/event/risky-output.phpt index f2ffc0cee06..387cdb97731 100644 --- a/tests/end-to-end/event/risky-output.phpt +++ b/tests/end-to-end/event/risky-output.phpt @@ -1,46 +1,32 @@ --TEST-- The right events are emitted in the right order for a test that is considered risky because it prints output ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest::testOne) Test Printed Unexpected Output * Test Considered Risky (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest::testOne) -This test printed output: * +Test code or tested code printed unexpected output: * Test Finished (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest::testOne) Test Suite Finished (PHPUnit\TestFixture\Event\RiskyBecauseOutputTest, 1 test) Test Runner Execution Finished diff --git a/tests/end-to-end/event/risky-time-limit-exceeded.phpt b/tests/end-to-end/event/risky-time-limit-exceeded.phpt index bce5bb96729..c78d6cc73e1 100644 --- a/tests/end-to-end/event/risky-time-limit-exceeded.phpt +++ b/tests/end-to-end/event/risky-time-limit-exceeded.phpt @@ -2,40 +2,29 @@ The right events are emitted in the right order for a test that is considered risky because it timed out --SKIPIF-- run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\RiskyBecauseTimeLimitExceededTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\RiskyBecauseTimeLimitExceededTest::testOne) Test Prepared (PHPUnit\TestFixture\Event\RiskyBecauseTimeLimitExceededTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Considered Risky (PHPUnit\TestFixture\Event\RiskyBecauseTimeLimitExceededTest::testOne) This test was aborted after 1 second Test Finished (PHPUnit\TestFixture\Event\RiskyBecauseTimeLimitExceededTest::testOne) diff --git a/tests/end-to-end/event/risky-with-multiple-reasons.phpt b/tests/end-to-end/event/risky-with-multiple-reasons.phpt index c16686a47a8..60d01435d6d 100644 --- a/tests/end-to-end/event/risky-with-multiple-reasons.phpt +++ b/tests/end-to-end/event/risky-with-multiple-reasons.phpt @@ -1,35 +1,22 @@ --TEST-- The right events are emitted in the right order for a test that is considered risky for multiple reasons ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/success-process-isolation.phpt b/tests/end-to-end/event/success-process-isolation.phpt index 4e18011dedf..7b4f2368adc 100644 --- a/tests/end-to-end/event/success-process-isolation.phpt +++ b/tests/end-to-end/event/success-process-isolation.phpt @@ -1,43 +1,31 @@ --TEST-- The right events are emitted in the right order for a successful test that is run in an isolated process ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\SuccessTest, 1 test) +Child Process Started Test Preparation Started (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Prepared (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Finished (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) +Child Process Finished Test Suite Finished (PHPUnit\TestFixture\Event\SuccessTest, 1 test) Test Runner Execution Finished Test Runner Finished diff --git a/tests/end-to-end/event/success-verbose.phpt b/tests/end-to-end/event/success-verbose.phpt index e216b25e84d..2f7ba5ed546 100644 --- a/tests/end-to-end/event/success-verbose.phpt +++ b/tests/end-to-end/event/success-verbose.phpt @@ -1,10 +1,5 @@ --TEST-- The right events are emitted in the right order for a successful test with extended information ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\SuccessTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Prepared (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Finished (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Suite Finished (PHPUnit\TestFixture\Event\SuccessTest, 1 test) diff --git a/tests/end-to-end/event/successful-mock-expectation.phpt b/tests/end-to-end/event/successful-mock-expectation.phpt index 78ac1080baa..1f7d881f420 100644 --- a/tests/end-to-end/event/successful-mock-expectation.phpt +++ b/tests/end-to-end/event/successful-mock-expectation.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test with a successful expectation on a mock object ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/suppressed-user-notice.phpt b/tests/end-to-end/event/suppressed-user-notice.phpt index 42d4c0b1b09..192513d5b95 100644 --- a/tests/end-to-end/event/suppressed-user-notice.phpt +++ b/tests/end-to-end/event/suppressed-user-notice.phpt @@ -1,45 +1,37 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers a suppressed E_USER_NOTICE ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest, 1 test) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNotice) Test Prepared (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNotice) -Assertion Succeeded (Constraint: is true, Value: true) -Test Triggered Suppressed Notice (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNotice) +Test Triggered Notice (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNotice, suppressed using operator) in %s:%d message Test Passed (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNotice) Test Finished (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNotice) -Test Suite Finished (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNoticeErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNoticeErrorGetLast) +Test Triggered Notice (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNoticeErrorGetLast, suppressed using operator) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNoticeErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest::testSuppressedUserNoticeErrorGetLast) +Test Suite Finished (PHPUnit\TestFixture\Event\SuppressedUserNoticeTest, 2 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/suppressed-user-warning.phpt b/tests/end-to-end/event/suppressed-user-warning.phpt index 1ba2962bac6..68906bd5b28 100644 --- a/tests/end-to-end/event/suppressed-user-warning.phpt +++ b/tests/end-to-end/event/suppressed-user-warning.phpt @@ -1,45 +1,37 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers a suppressed E_USER_WARNING ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\SuppressedUserWarningTest, 1 test) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\SuppressedUserWarningTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarning) Test Prepared (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarning) -Assertion Succeeded (Constraint: is true, Value: true) -Test Triggered Suppressed Warning (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarning) +Test Triggered Warning (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarning, suppressed using operator) in %s:%d message Test Passed (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarning) Test Finished (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarning) -Test Suite Finished (PHPUnit\TestFixture\Event\SuppressedUserWarningTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarningErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarningErrorGetLast) +Test Triggered Warning (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarningErrorGetLast, suppressed using operator) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarningErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\SuppressedUserWarningTest::testSuppressedUserWarningErrorGetLast) +Test Suite Finished (PHPUnit\TestFixture\Event\SuppressedUserWarningTest, 2 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/template-methods-isolation.phpt b/tests/end-to-end/event/template-methods-isolation.phpt new file mode 100644 index 00000000000..ea1761f95c7 --- /dev/null +++ b/tests/end-to-end/event/template-methods-isolation.phpt @@ -0,0 +1,80 @@ +--TEST-- +The right events are emitted in the right order for the template methods of a test class that is run in process isolation +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\TemplateMethodsTest, 2 tests) +Before First Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::setUpBeforeClass) +Before First Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::setUpBeforeClass +Child Process Started +Test Preparation Started (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) +Before First Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::setUpBeforeClass) +Before First Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::setUpBeforeClass +Before Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::setUp) +Before Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::setUp +Pre Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPreConditions) +Pre Condition Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPreConditions +Test Prepared (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) +Post Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions) +Post Condition Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions +After Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown) +After Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown +After Last Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass) +After Last Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass +Test Passed (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) +Child Process Finished +Child Process Started +Test Preparation Started (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) +Before First Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::setUpBeforeClass) +Before First Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::setUpBeforeClass +Before Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::setUp) +Before Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::setUp +Pre Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPreConditions) +Pre Condition Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPreConditions +Test Prepared (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) +Post Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions) +Post Condition Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions +After Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown) +After Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown +After Last Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass) +After Last Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass +Test Passed (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) +Test Finished (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) +Child Process Finished +After Last Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass) +After Last Test Method Finished: +- PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass +Test Suite Finished (PHPUnit\TestFixture\Event\TemplateMethodsTest, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/template-methods.phpt b/tests/end-to-end/event/template-methods.phpt index 3968ef079fc..ab5df524a8d 100644 --- a/tests/end-to-end/event/template-methods.phpt +++ b/tests/end-to-end/event/template-methods.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for the template methods of a test class ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) @@ -43,14 +30,13 @@ Pre Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::asse Pre Condition Method Finished: - PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPreConditions Test Prepared (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Post Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions) Post Condition Method Finished: - PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions -Test Passed (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) After Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown) After Test Method Finished: - PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown +Test Passed (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) Test Finished (PHPUnit\TestFixture\Event\TemplateMethodsTest::testOne) Test Preparation Started (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) Before Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::setUp) @@ -60,14 +46,13 @@ Pre Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::asse Pre Condition Method Finished: - PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPreConditions Test Prepared (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Post Condition Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions) Post Condition Method Finished: - PHPUnit\TestFixture\Event\TemplateMethodsTest::assertPostConditions -Test Passed (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) After Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown) After Test Method Finished: - PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDown +Test Passed (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) Test Finished (PHPUnit\TestFixture\Event\TemplateMethodsTest::testTwo) After Last Test Method Called (PHPUnit\TestFixture\Event\TemplateMethodsTest::tearDownAfterClass) After Last Test Method Finished: diff --git a/tests/end-to-end/event/test-method-builder-cannot-find-testcase-object.phpt b/tests/end-to-end/event/test-method-builder-cannot-find-testcase-object.phpt index 6d5ec7962f2..297aa8eb02d 100644 --- a/tests/end-to-end/event/test-method-builder-cannot-find-testcase-object.phpt +++ b/tests/end-to-end/event/test-method-builder-cannot-find-testcase-object.phpt @@ -1,11 +1,15 @@ --TEST-- The right exception is raised when TestMethodBuilder::fromCallStack() cannot find a TestCase object +--SKIPIF-- +run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/test-skipped-in-setup.phpt b/tests/end-to-end/event/test-skipped-in-setup.phpt index c02da064dce..aa6bc9806f9 100644 --- a/tests/end-to-end/event/test-skipped-in-setup.phpt +++ b/tests/end-to-end/event/test-skipped-in-setup.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a test skipped in setUp() ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/test-skipped.phpt b/tests/end-to-end/event/test-skipped.phpt index c01201f383d..7d3f68453d4 100644 --- a/tests/end-to-end/event/test-skipped.phpt +++ b/tests/end-to-end/event/test-skipped.phpt @@ -1,33 +1,20 @@ --TEST-- The right events are emitted in the right order for a skipped test ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) diff --git a/tests/end-to-end/event/test-stub.phpt b/tests/end-to-end/event/test-stub.phpt index 1e7d6b2cd88..9a2667b567b 100644 --- a/tests/end-to-end/event/test-stub.phpt +++ b/tests/end-to-end/event/test-stub.phpt @@ -1,33 +1,23 @@ --TEST-- The right events are emitted in the right order for a test that uses a test stub ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) +Bootstrap Finished (%sExample.php) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) @@ -35,7 +25,6 @@ Test Suite Started (PHPUnit\TestFixture\Event\StubTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\StubTest::testSuccess) Test Prepared (PHPUnit\TestFixture\Event\StubTest::testSuccess) Test Stub Created (PHPUnit\TestFixture\Event\Example) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\StubTest::testSuccess) Test Finished (PHPUnit\TestFixture\Event\StubTest::testSuccess) Test Suite Finished (PHPUnit\TestFixture\Event\StubTest, 1 test) diff --git a/tests/end-to-end/event/testwith-annotation.phpt b/tests/end-to-end/event/testwith-annotation.phpt deleted file mode 100644 index 93fb9be3e1b..00000000000 --- a/tests/end-to-end/event/testwith-annotation.phpt +++ /dev/null @@ -1,45 +0,0 @@ ---TEST-- -The right events are emitted in the right order for a successful test that uses the TestWith and TestWithJson attributes ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); ---EXPECTF-- -PHPUnit Started (PHPUnit %s using %s) -Test Runner Configured -Test Suite Loaded (1 test) -Event Facade Sealed -Test Runner Started -Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest, 1 test) -Test Suite Started (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest::testOne, 1 test) -Test Preparation Started (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest::testOne#0) -Test Prepared (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest::testOne#0) -Assertion Succeeded (Constraint: is true, Value: true) -Test Passed (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest::testOne#0) -Test Finished (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest::testOne#0) -Test Suite Finished (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest::testOne, 1 test) -Test Suite Finished (PHPUnit\TestFixture\Metadata\Annotation\TestWithTest, 1 test) -Test Runner Execution Finished -Test Runner Finished -PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/testwith-attribute.phpt b/tests/end-to-end/event/testwith-attribute.phpt index 80957f96ed1..d5761afdc7c 100644 --- a/tests/end-to-end/event/testwith-attribute.phpt +++ b/tests/end-to-end/event/testwith-attribute.phpt @@ -1,52 +1,49 @@ --TEST-- The right events are emitted in the right order for a successful test that uses the TestWith and TestWithJson attributes ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (4 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (2 tests) -Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 2 tests) +Test Runner Execution Started (4 tests) +Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 4 tests) Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne, 1 test) Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0) Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0) Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0) Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne, 1 test) +Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1) +Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1) +Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1) +Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1) +Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName, 1 test) Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo, 1 test) Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0) Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0) Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0) Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo, 1 test) -Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 2 tests) +Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2) +Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2) +Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2) +Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2) +Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName, 1 test) +Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 4 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/too-few-columns.phpt b/tests/end-to-end/event/too-few-columns.phpt index 2fd2007a27b..2b7ee09d69f 100644 --- a/tests/end-to-end/event/too-few-columns.phpt +++ b/tests/end-to-end/event/too-few-columns.phpt @@ -1,43 +1,29 @@ --TEST-- The right events are emitted in the right order when too few columns are requested ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Triggered Warning (Less than 16 columns requested, number of columns set to 16) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\SuccessTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Prepared (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Finished (PHPUnit\TestFixture\Event\SuccessTest::testSuccess) Test Suite Finished (PHPUnit\TestFixture\Event\SuccessTest, 1 test) diff --git a/tests/end-to-end/event/unexpected-end-of-test-in-separate-process.phpt b/tests/end-to-end/event/unexpected-end-of-test-in-separate-process.phpt index a50a7d660cf..f9871b4d786 100644 --- a/tests/end-to-end/event/unexpected-end-of-test-in-separate-process.phpt +++ b/tests/end-to-end/event/unexpected-end-of-test-in-separate-process.phpt @@ -1,40 +1,29 @@ --TEST-- The right events are emitted in the right order for a test run in a separate process that ends unexpectedly ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) Test Suite Started (PHPUnit\TestFixture\Event\SeparateProcessesTest, 1 test) +Child Process Started Test Errored (PHPUnit\TestFixture\Event\SeparateProcessesTest::testOne) Test was run in child process and ended unexpectedly Test Finished (PHPUnit\TestFixture\Event\SeparateProcessesTest::testOne) +Child Process Finished Test Suite Finished (PHPUnit\TestFixture\Event\SeparateProcessesTest, 1 test) Test Runner Execution Finished Test Runner Finished diff --git a/tests/end-to-end/event/unsatisfied-requirement-before-class-method.phpt b/tests/end-to-end/event/unsatisfied-requirement-before-class-method.phpt new file mode 100644 index 00000000000..e0ecee2339d --- /dev/null +++ b/tests/end-to-end/event/unsatisfied-requirement-before-class-method.phpt @@ -0,0 +1,25 @@ +--TEST-- +The right events are emitted in the right order for a test that has an unsatisfied requirement (before class method) +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementBeforeClassMethodTest, 1 test) +Test Suite Skipped (PHPUnit\TestFixture\Event\UnsatisfiedRequirementBeforeClassMethodTest, PHP 100 is required.) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/unsatisfied-requirement-class.phpt b/tests/end-to-end/event/unsatisfied-requirement-class.phpt new file mode 100644 index 00000000000..043cec3d53e --- /dev/null +++ b/tests/end-to-end/event/unsatisfied-requirement-class.phpt @@ -0,0 +1,28 @@ +--TEST-- +The right events are emitted in the right order for a test that has an unsatisfied requirement (class level) +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementClassTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementClassTest::testOne) +Test Skipped (PHPUnit\TestFixture\Event\UnsatisfiedRequirementClassTest::testOne) +PHP 100 is required. +Test Suite Finished (PHPUnit\TestFixture\Event\UnsatisfiedRequirementClassTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/unsatisfied-requirement-method.phpt b/tests/end-to-end/event/unsatisfied-requirement-method.phpt new file mode 100644 index 00000000000..447367927f6 --- /dev/null +++ b/tests/end-to-end/event/unsatisfied-requirement-method.phpt @@ -0,0 +1,28 @@ +--TEST-- +The right events are emitted in the right order for a test that has an unsatisfied requirement (method level) +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementMethodTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementMethodTest::testOne) +Test Skipped (PHPUnit\TestFixture\Event\UnsatisfiedRequirementMethodTest::testOne) +PHP 100 is required. +Test Suite Finished (PHPUnit\TestFixture\Event\UnsatisfiedRequirementMethodTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/unsatisfied-requirement.phpt b/tests/end-to-end/event/unsatisfied-requirement.phpt deleted file mode 100644 index 6ea2ff67278..00000000000 --- a/tests/end-to-end/event/unsatisfied-requirement.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -The right events are emitted in the right order for a test that has an unsatisfied requirement ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); ---EXPECTF-- -PHPUnit Started (PHPUnit %s using %s) -Test Runner Configured -Test Suite Loaded (1 test) -Event Facade Sealed -Test Runner Started -Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementTest, 1 test) -Test Preparation Started (PHPUnit\TestFixture\Event\UnsatisfiedRequirementTest::testOne) -Test Skipped (PHPUnit\TestFixture\Event\UnsatisfiedRequirementTest::testOne) -PHP >= 100 is required. -Test Suite Finished (PHPUnit\TestFixture\Event\UnsatisfiedRequirementTest, 1 test) -Test Runner Execution Finished -Test Runner Finished -PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/user-deprecated.phpt b/tests/end-to-end/event/user-deprecated.phpt index b81d5c608bb..b12840e4ce5 100644 --- a/tests/end-to-end/event/user-deprecated.phpt +++ b/tests/end-to-end/event/user-deprecated.phpt @@ -1,47 +1,39 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers E_USER_DEPRECATED ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\DeprecatedFeatureTest, 1 test) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\DeprecatedFeatureTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature) Test Prepared (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature) -Test Triggered Deprecation (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature, unknown if issue was triggered in first-party code or third-party code) in %s:%d message -Test Triggered Suppressed Deprecation (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature, unknown if issue was triggered in first-party code or third-party code, suppressed using operator) in %s:%d message -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature) Test Finished (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedFeature) -Test Suite Finished (PHPUnit\TestFixture\Event\DeprecatedFeatureTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedSuppressedErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedSuppressedErrorGetLast) +Test Triggered Deprecation (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedSuppressedErrorGetLast, unknown if issue was triggered in first-party code or third-party code, suppressed using operator) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedSuppressedErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\DeprecatedFeatureTest::testDeprecatedSuppressedErrorGetLast) +Test Suite Finished (PHPUnit\TestFixture\Event\DeprecatedFeatureTest, 2 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/user-error-php-84.phpt b/tests/end-to-end/event/user-error-php-84.phpt new file mode 100644 index 00000000000..58d909979b1 --- /dev/null +++ b/tests/end-to-end/event/user-error-php-84.phpt @@ -0,0 +1,48 @@ +--TEST-- +The right events are emitted in the right order for a test that runs code which triggers E_USER_ERROR +--SKIPIF-- +')) { + print 'skip: PHP 8.4 is required.'; +} +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\UserErrorTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +Test Prepared (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +Test Triggered PHP Deprecation (PHPUnit\TestFixture\Event\UserErrorTest::testUserError, unknown if issue was triggered in first-party code or third-party code) in %s:%d +Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead +Test Triggered Error (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) in %s:%d +message +Test Errored (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +E_USER_ERROR was triggered +Test Finished (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +Test Preparation Started (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +Test Prepared (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +Test Triggered PHP Deprecation (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution, unknown if issue was triggered in first-party code or third-party code) in %s:%d +Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead +Test Triggered Error (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) in %s:%d +message +Test Errored (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +E_USER_ERROR was triggered +Test Finished (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +Test Suite Finished (PHPUnit\TestFixture\Event\UserErrorTest, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/user-error.phpt b/tests/end-to-end/event/user-error.phpt index c7ea30422b8..2212f04504b 100644 --- a/tests/end-to-end/event/user-error.phpt +++ b/tests/end-to-end/event/user-error.phpt @@ -2,44 +2,43 @@ The right events are emitted in the right order for a test that runs code which triggers E_USER_ERROR --SKIPIF-- =')) { + print 'skip: PHP < 8.4 is required.'; } --FILE-- run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\UserErrorTest, 1 test) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\UserErrorTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) Test Prepared (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) -Assertion Succeeded (Constraint: is true, Value: true) -Test Triggered Error (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +Test Triggered Error (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) in %s:%d message -Test Passed (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +Test Errored (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) +E_USER_ERROR was triggered Test Finished (PHPUnit\TestFixture\Event\UserErrorTest::testUserError) -Test Suite Finished (PHPUnit\TestFixture\Event\UserErrorTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +Test Prepared (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +Test Triggered Error (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) in %s:%d +message +Test Errored (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +E_USER_ERROR was triggered +Test Finished (PHPUnit\TestFixture\Event\UserErrorTest::testUserErrorMustAbortExecution) +Test Suite Finished (PHPUnit\TestFixture\Event\UserErrorTest, 2 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/event/user-notice.phpt b/tests/end-to-end/event/user-notice.phpt index c39cccd57ed..8b2ba0e0b2d 100644 --- a/tests/end-to-end/event/user-notice.phpt +++ b/tests/end-to-end/event/user-notice.phpt @@ -1,45 +1,37 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers an E_USER_NOTICE ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\UserNoticeTest, 1 test) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\UserNoticeTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNotice) Test Prepared (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNotice) -Assertion Succeeded (Constraint: is true, Value: true) -Test Triggered Notice (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNotice) +Test Triggered Notice (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNotice) in %s:%d message Test Passed (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNotice) Test Finished (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNotice) -Test Suite Finished (PHPUnit\TestFixture\Event\UserNoticeTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNoticeErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNoticeErrorGetLast) +Test Triggered Notice (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNoticeErrorGetLast) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNoticeErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\UserNoticeTest::testUserNoticeErrorGetLast) +Test Suite Finished (PHPUnit\TestFixture\Event\UserNoticeTest, 2 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/event/user-warning.phpt b/tests/end-to-end/event/user-warning.phpt index 3fd847305b6..a43703d3f85 100644 --- a/tests/end-to-end/event/user-warning.phpt +++ b/tests/end-to-end/event/user-warning.phpt @@ -1,45 +1,37 @@ --TEST-- The right events are emitted in the right order for a test that runs code which triggers E_USER_WARNING ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (1 test) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (PHPUnit\TestFixture\Event\UserWarningTest, 1 test) +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Event\UserWarningTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarning) Test Prepared (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarning) -Assertion Succeeded (Constraint: is true, Value: true) -Test Triggered Warning (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarning) +Test Triggered Warning (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarning) in %s:%d message Test Passed (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarning) Test Finished (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarning) -Test Suite Finished (PHPUnit\TestFixture\Event\UserWarningTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarningErrorGetLast) +Test Prepared (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarningErrorGetLast) +Test Triggered Warning (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarningErrorGetLast) in %s:%d +message +Test Passed (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarningErrorGetLast) +Test Finished (PHPUnit\TestFixture\Event\UserWarningTest::testUserWarningErrorGetLast) +Test Suite Finished (PHPUnit\TestFixture\Event\UserWarningTest, 2 tests) Test Runner Execution Finished Test Runner Finished PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/execution-order/_files/ClonedDependencyTest.php b/tests/end-to-end/execution-order/_files/ClonedDependencyTest.php index e557414f091..05dc1a140b4 100644 --- a/tests/end-to-end/execution-order/_files/ClonedDependencyTest.php +++ b/tests/end-to-end/execution-order/_files/ClonedDependencyTest.php @@ -9,6 +9,9 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DependsUsingDeepClone; +use PHPUnit\Framework\Attributes\DependsUsingShallowClone; use PHPUnit\Framework\TestCase; use stdClass; @@ -28,41 +31,31 @@ public function testOne() return self::$dependency; } - /** - * @depends testOne - */ + #[Depends('testOne')] public function testTwo($dependency): void { $this->assertSame(self::$dependency, $dependency); } - /** - * @depends !clone testOne - */ + #[Depends('testOne')] public function testThree($dependency): void { $this->assertSame(self::$dependency, $dependency); } - /** - * @depends clone testOne - */ + #[DependsUsingDeepClone('testOne')] public function testFour($dependency): void { $this->assertNotSame(self::$dependency, $dependency); } - /** - * @depends !shallowClone testOne - */ + #[Depends('testOne')] public function testFive($dependency): void { $this->assertSame(self::$dependency, $dependency); } - /** - * @depends shallowClone testOne - */ + #[DependsUsingShallowClone('testOne')] public function testSix($dependency): void { $this->assertNotSame(self::$dependency, $dependency); diff --git a/tests/end-to-end/execution-order/_files/StackTest.php b/tests/end-to-end/execution-order/_files/StackTest.php index d11fd67e0b9..f8c2b8da813 100644 --- a/tests/end-to-end/execution-order/_files/StackTest.php +++ b/tests/end-to-end/execution-order/_files/StackTest.php @@ -11,6 +11,7 @@ use function array_pop; use function end; +use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\TestCase; class StackTest extends TestCase @@ -27,9 +28,7 @@ public function testPush() return $stack; } - /** - * @depends testPush - */ + #[Depends('testPush')] public function testPop(array $stack): void { $this->assertEquals('foo', array_pop($stack)); diff --git a/tests/end-to-end/execution-order/_files/TestWithDifferentDurations.phpunit.result.cache.txt b/tests/end-to-end/execution-order/_files/TestWithDifferentDurations.phpunit.result.cache.txt index 5cfa53d2f1d..abbaa02a4c8 100644 --- a/tests/end-to-end/execution-order/_files/TestWithDifferentDurations.phpunit.result.cache.txt +++ b/tests/end-to-end/execution-order/_files/TestWithDifferentDurations.phpunit.result.cache.txt @@ -1 +1 @@ -{"version":1,"defects":[],"times":{"TestWithDifferentDurations::testOne":1,"TestWithDifferentDurations::testTwo":0.5,"TestWithDifferentDurations::testThree":1.5}} +{"version":1,"defects":[],"times":{"PHPUnit\\TestFixture\\TestWithDifferentDurations::testOne":2.006,"PHPUnit\\TestFixture\\TestWithDifferentDurations::testTwo":0,"PHPUnit\\TestFixture\\TestWithDifferentDurations::testThree":3.001,"PHPUnit\\TestFixture\\ExampleTest::testOne":2.006,"PHPUnit\\TestFixture\\ExampleTest::testTwo":3.001,"PHPUnit\\TestFixture\\ExampleTest::testThree":0}} \ No newline at end of file diff --git a/tests/end-to-end/execution-order/_files/order-by-duration.phpunit.xml b/tests/end-to-end/execution-order/_files/order-by-duration.phpunit.xml new file mode 100644 index 00000000000..2ef82a66223 --- /dev/null +++ b/tests/end-to-end/execution-order/_files/order-by-duration.phpunit.xml @@ -0,0 +1,11 @@ + + + + ./TestWithDifferentDurations.php + + + diff --git a/tests/end-to-end/execution-order/cache-result.phpt b/tests/end-to-end/execution-order/cache-result.phpt index b855577d074..7800e94e781 100644 --- a/tests/end-to-end/execution-order/cache-result.phpt +++ b/tests/end-to-end/execution-order/cache-result.phpt @@ -9,7 +9,7 @@ $_SERVER['argv'][] = '--ignore-dependencies'; // keep coverage for legacy CLI $_SERVER['argv'][] = '--order-by=reverse'; $_SERVER['argv'][] = '--cache-result'; $_SERVER['argv'][] = '--cache-directory=' . $cacheDirectory; -$_SERVER['argv'][] = realpath(__DIR__ . '/../execution-order/_files/MultiDependencyTest.php'); +$_SERVER['argv'][] = __DIR__ . '/../../_files/MultiDependencyTest.php'; require_once __DIR__ . '/../../bootstrap.php'; diff --git a/tests/end-to-end/execution-order/dependencies-isolation.phpt b/tests/end-to-end/execution-order/dependencies-isolation.phpt index 4b924f59e57..85225d015e5 100644 --- a/tests/end-to-end/execution-order/dependencies-isolation.phpt +++ b/tests/end-to-end/execution-order/dependencies-isolation.phpt @@ -22,10 +22,10 @@ Time: %s, Memory: %s There were 2 errors: -1) PHPUnit\TestFixture\DependencyFailureTest::testHandlesDependsAnnotationForNonexistentTests +1) PHPUnit\TestFixture\DependencyFailureTest::testHandlesDependencyOnTestMethodThatDoesNotExist This test depends on "PHPUnit\TestFixture\DependencyFailureTest::doesNotExist" which does not exist -2) PHPUnit\TestFixture\DependencyFailureTest::testHandlesDependsAnnotationWithNoMethodSpecified +2) PHPUnit\TestFixture\DependencyFailureTest::testHandlesDependencyOnTestMethodWithEmptyName This test has an invalid dependency -- diff --git a/tests/end-to-end/execution-order/depends-multiple-parameter-with-isolation.phpt b/tests/end-to-end/execution-order/depends-multiple-parameter-with-isolation.phpt index a629ad66fe5..9f19922e0aa 100644 --- a/tests/end-to-end/execution-order/depends-multiple-parameter-with-isolation.phpt +++ b/tests/end-to-end/execution-order/depends-multiple-parameter-with-isolation.phpt @@ -5,7 +5,7 @@ phpunit --process-isolation _files/MultiDependencyTest.php $_SERVER['argv'][] = '--do-not-cache-result'; $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--process-isolation'; -$_SERVER['argv'][] = \realpath(__DIR__ . '/_files/MultiDependencyTest.php'); +$_SERVER['argv'][] = __DIR__ . '/../../_files/MultiDependencyTest.php'; require_once __DIR__ . '/../../bootstrap.php'; diff --git a/tests/end-to-end/execution-order/depends-multiple-parameters.phpt b/tests/end-to-end/execution-order/depends-multiple-parameters.phpt index 99f71a43d19..02c6884b487 100644 --- a/tests/end-to-end/execution-order/depends-multiple-parameters.phpt +++ b/tests/end-to-end/execution-order/depends-multiple-parameters.phpt @@ -4,7 +4,7 @@ phpunit _files/MultiDependencyTest.php run($_SERVER['argv']); + +unlink($cacheDirectory . DIRECTORY_SEPARATOR . 'test-results'); +rmdir($cacheDirectory); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using PHP %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (3 tests) +Test Suite Started (PHPUnit\TestFixture\TestWithDifferentDurations, 3 tests) +Test Preparation Started (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Prepared (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Passed (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Finished (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Preparation Started (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Prepared (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Passed (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Finished (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Preparation Started (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Prepared (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Passed (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Finished (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Suite Finished (PHPUnit\TestFixture\TestWithDifferentDurations, 3 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/execution-order/order-by-duration-via-phpunit-xml.phpt b/tests/end-to-end/execution-order/order-by-duration-via-phpunit-xml.phpt new file mode 100644 index 00000000000..46f01c62f4f --- /dev/null +++ b/tests/end-to-end/execution-order/order-by-duration-via-phpunit-xml.phpt @@ -0,0 +1,55 @@ +--TEST-- +phpunit --configuration=order-by-duration.phpunit.xml +--FILE-- +run($_SERVER['argv']); + +unlink($cacheDirectory . DIRECTORY_SEPARATOR . 'test-results'); +rmdir($cacheDirectory); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using PHP %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (3 tests) +Test Suite Started (%sorder-by-duration.phpunit.xml, 3 tests) +Test Suite Started (order-by-duration, 3 tests) +Test Suite Started (PHPUnit\TestFixture\TestWithDifferentDurations, 3 tests) +Test Preparation Started (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Prepared (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Passed (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Finished (PHPUnit\TestFixture\TestWithDifferentDurations::testTwo) +Test Preparation Started (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Prepared (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Passed (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Finished (PHPUnit\TestFixture\TestWithDifferentDurations::testOne) +Test Preparation Started (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Prepared (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Passed (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Finished (PHPUnit\TestFixture\TestWithDifferentDurations::testThree) +Test Suite Finished (PHPUnit\TestFixture\TestWithDifferentDurations, 3 tests) +Test Suite Finished (order-by-duration, 3 tests) +Test Suite Finished (%sorder-by-duration.phpunit.xml, 3 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/execution-order/test-order-randomized-with-dependency-resolution.phpt b/tests/end-to-end/execution-order/test-order-randomized-with-dependency-resolution.phpt index 6f767e30069..2c81c005efb 100644 --- a/tests/end-to-end/execution-order/test-order-randomized-with-dependency-resolution.phpt +++ b/tests/end-to-end/execution-order/test-order-randomized-with-dependency-resolution.phpt @@ -6,7 +6,7 @@ $_SERVER['argv'][] = '--do-not-cache-result'; $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--resolve-dependencies'; // keep coverage for legacy CLI option $_SERVER['argv'][] = '--order-by=depends,random'; -$_SERVER['argv'][] = \realpath(__DIR__ . '/../execution-order/_files/MultiDependencyTest.php'); +$_SERVER['argv'][] = __DIR__ . '/../../_files/MultiDependencyTest.php'; require_once __DIR__ . '/../../bootstrap.php'; diff --git a/tests/end-to-end/extension-cli/_files/class-does-not-exist/phpunit.xml b/tests/end-to-end/extension-cli/_files/class-does-not-exist/phpunit.xml new file mode 100644 index 00000000000..c4d7b898f3f --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/class-does-not-exist/phpunit.xml @@ -0,0 +1,9 @@ + + + + + tests + + + diff --git a/tests/end-to-end/extension/_files/class-does-not-exist/tests/Test.php b/tests/end-to-end/extension-cli/_files/class-does-not-exist/tests/Test.php similarity index 100% rename from tests/end-to-end/extension/_files/class-does-not-exist/tests/Test.php rename to tests/end-to-end/extension-cli/_files/class-does-not-exist/tests/Test.php diff --git a/tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/phpunit.xml b/tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/phpunit.xml new file mode 100644 index 00000000000..aaa950205d4 --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/phpunit.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/end-to-end/extension/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php similarity index 100% rename from tests/end-to-end/extension/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php rename to tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php diff --git a/tests/end-to-end/extension/_files/class-does-not-implement-interface/src/autoload.php b/tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/src/autoload.php similarity index 100% rename from tests/end-to-end/extension/_files/class-does-not-implement-interface/src/autoload.php rename to tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/src/autoload.php diff --git a/tests/end-to-end/extension/_files/class-does-not-implement-interface/tests/Test.php b/tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/tests/Test.php similarity index 100% rename from tests/end-to-end/extension/_files/class-does-not-implement-interface/tests/Test.php rename to tests/end-to-end/extension-cli/_files/class-does-not-implement-interface/tests/Test.php diff --git a/tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/phpunit.xml b/tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/phpunit.xml new file mode 100644 index 00000000000..aaa950205d4 --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/phpunit.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/src/autoload.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/src/autoload.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/src/autoload.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/src/autoload.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/tests/Test.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/tests/Test.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/tests/Test.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-bootstrap-method/tests/Test.php diff --git a/tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/phpunit.xml b/tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/phpunit.xml new file mode 100644 index 00000000000..aaa950205d4 --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/phpunit.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/end-to-end/extension/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-constructor/src/autoload.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/src/autoload.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-constructor/src/autoload.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/src/autoload.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-constructor/tests/Test.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/tests/Test.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-constructor/tests/Test.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-constructor/tests/Test.php diff --git a/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/phpunit.xml b/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/phpunit.xml new file mode 100644 index 00000000000..aaa950205d4 --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/phpunit.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/end-to-end/extension/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-subscriber/src/autoload.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/src/autoload.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-subscriber/src/autoload.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/src/autoload.php diff --git a/tests/end-to-end/extension/_files/exception-in-extension-subscriber/tests/Test.php b/tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/tests/Test.php similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-subscriber/tests/Test.php rename to tests/end-to-end/extension-cli/_files/exception-in-extension-subscriber/tests/Test.php diff --git a/tests/end-to-end/extension-cli/_files/extension-bootstrap/phpunit.xml b/tests/end-to-end/extension-cli/_files/extension-bootstrap/phpunit.xml new file mode 100644 index 00000000000..aaa950205d4 --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/extension-bootstrap/phpunit.xml @@ -0,0 +1,10 @@ + + + + + tests + + + diff --git a/tests/end-to-end/extension/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php b/tests/end-to-end/extension-cli/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php similarity index 100% rename from tests/end-to-end/extension/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php rename to tests/end-to-end/extension-cli/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php diff --git a/tests/end-to-end/extension-cli/_files/extension-bootstrap/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-cli/_files/extension-bootstrap/src/MyExtensionBootstrap.php new file mode 100644 index 00000000000..a80872588d0 --- /dev/null +++ b/tests/end-to-end/extension-cli/_files/extension-bootstrap/src/MyExtensionBootstrap.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Runner\Extension\Extension; +use PHPUnit\Runner\Extension\Facade; +use PHPUnit\Runner\Extension\ParameterCollection; +use PHPUnit\TextUI\Configuration\Configuration; + +final class MyExtensionBootstrap implements Extension +{ + public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void + { + $facade->registerSubscriber( + new MyExecutionFinishedSubscriber( + 'the-message', + ), + ); + } +} diff --git a/tests/end-to-end/extension/_files/extension-bootstrap/src/autoload.php b/tests/end-to-end/extension-cli/_files/extension-bootstrap/src/autoload.php similarity index 100% rename from tests/end-to-end/extension/_files/extension-bootstrap/src/autoload.php rename to tests/end-to-end/extension-cli/_files/extension-bootstrap/src/autoload.php diff --git a/tests/end-to-end/extension/_files/extension-bootstrap/tests/Test.php b/tests/end-to-end/extension-cli/_files/extension-bootstrap/tests/Test.php similarity index 100% rename from tests/end-to-end/extension/_files/extension-bootstrap/tests/Test.php rename to tests/end-to-end/extension-cli/_files/extension-bootstrap/tests/Test.php diff --git a/tests/end-to-end/extension-cli/bootstrap.phpt b/tests/end-to-end/extension-cli/bootstrap.phpt new file mode 100644 index 00000000000..256a9e4fd33 --- /dev/null +++ b/tests/end-to-end/extension-cli/bootstrap.phpt @@ -0,0 +1,17 @@ +--TEST-- +A PHPUnit extension can subscribe to events +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit\TestFixture\Event\MyExtension\MyExecutionFinishedSubscriber::notify +the-message diff --git a/tests/end-to-end/extension-cli/class-does-not-exist.phpt b/tests/end-to-end/extension-cli/class-does-not-exist.phpt new file mode 100644 index 00000000000..0b9a8991c16 --- /dev/null +++ b/tests/end-to-end/extension-cli/class-does-not-exist.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test runner exits with error when configured extension class does not exit +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot bootstrap extension because class Does\Not\Exist does not exist + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/extension-cli/class-does-not-implement-interface.phpt b/tests/end-to-end/extension-cli/class-does-not-implement-interface.phpt new file mode 100644 index 00000000000..f9b6a0b116d --- /dev/null +++ b/tests/end-to-end/extension-cli/class-does-not-implement-interface.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test runner exits with error when configured extension class does not implement the interface +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot bootstrap extension because class PHPUnit\TestFixture\Event\MyExtension\MyExtensionBootstrap does not implement interface PHPUnit\Runner\Extension\Extension + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/extension-cli/exception-in-extension-bootstrap-method.phpt b/tests/end-to-end/extension-cli/exception-in-extension-bootstrap-method.phpt new file mode 100644 index 00000000000..7725280a7c0 --- /dev/null +++ b/tests/end-to-end/extension-cli/exception-in-extension-bootstrap-method.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test runner warning is triggered when an exception is triggered in an extension's bootstrap method +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Bootstrapping of extension PHPUnit\TestFixture\Event\MyExtension\MyExtensionBootstrap failed: message +%A + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/extension-cli/exception-in-extension-constructor.phpt b/tests/end-to-end/extension-cli/exception-in-extension-constructor.phpt new file mode 100644 index 00000000000..37fe9b9988a --- /dev/null +++ b/tests/end-to-end/extension-cli/exception-in-extension-constructor.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test runner warning is triggered when an exception is triggered in an extension's bootstrap class' constructor +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Bootstrapping of extension PHPUnit\TestFixture\Event\MyExtension\MyExtensionBootstrap failed: message +%A + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/extension-cli/exception-in-extension-subscriber.phpt b/tests/end-to-end/extension-cli/exception-in-extension-subscriber.phpt new file mode 100644 index 00000000000..7013dfb5267 --- /dev/null +++ b/tests/end-to-end/extension-cli/exception-in-extension-subscriber.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test runner warning is triggered when an exception is triggered in an extension's event subscriber +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Exception in third-party event subscriber: message +%A + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/extension/_files/class-does-not-exist/phpunit.xml b/tests/end-to-end/extension-xml/_files/class-does-not-exist/phpunit.xml similarity index 100% rename from tests/end-to-end/extension/_files/class-does-not-exist/phpunit.xml rename to tests/end-to-end/extension-xml/_files/class-does-not-exist/phpunit.xml diff --git a/tests/end-to-end/extension-xml/_files/class-does-not-exist/tests/Test.php b/tests/end-to-end/extension-xml/_files/class-does-not-exist/tests/Test.php new file mode 100644 index 00000000000..a6c86c85797 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/class-does-not-exist/tests/Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/extension/_files/class-does-not-implement-interface/phpunit.xml b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/phpunit.xml similarity index 100% rename from tests/end-to-end/extension/_files/class-does-not-implement-interface/phpunit.xml rename to tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/phpunit.xml diff --git a/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php new file mode 100644 index 00000000000..0fbf797d553 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/src/MyExtensionBootstrap.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +final class MyExtensionBootstrap +{ +} diff --git a/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/src/autoload.php b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/src/autoload.php new file mode 100644 index 00000000000..c84415b0429 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/src/autoload.php @@ -0,0 +1,11 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/MyExtensionBootstrap.php'; diff --git a/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/tests/Test.php b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/tests/Test.php new file mode 100644 index 00000000000..a6c86c85797 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/class-does-not-implement-interface/tests/Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/phpunit.xml b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/phpunit.xml similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-bootstrap-method/phpunit.xml rename to tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/phpunit.xml diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php new file mode 100644 index 00000000000..763a61a70b9 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/src/MyExtensionBootstrap.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Runner\Extension\Extension; +use PHPUnit\Runner\Extension\Facade; +use PHPUnit\Runner\Extension\ParameterCollection; +use PHPUnit\TextUI\Configuration\Configuration; +use RuntimeException; + +final class MyExtensionBootstrap implements Extension +{ + public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void + { + throw new RuntimeException('message'); + } +} diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/src/autoload.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/src/autoload.php new file mode 100644 index 00000000000..c84415b0429 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/src/autoload.php @@ -0,0 +1,11 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/MyExtensionBootstrap.php'; diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/tests/Test.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/tests/Test.php new file mode 100644 index 00000000000..a6c86c85797 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-bootstrap-method/tests/Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/extension/_files/exception-in-extension-constructor/phpunit-without-parameter.xml b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/phpunit-without-parameter.xml similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-constructor/phpunit-without-parameter.xml rename to tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/phpunit-without-parameter.xml diff --git a/tests/end-to-end/extension/_files/exception-in-extension-constructor/phpunit.xml b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/phpunit.xml similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-constructor/phpunit.xml rename to tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/phpunit.xml diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php new file mode 100644 index 00000000000..5967340604c --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/src/MyExtensionBootstrap.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Runner\Extension\Extension; +use PHPUnit\Runner\Extension\Facade; +use PHPUnit\Runner\Extension\ParameterCollection; +use PHPUnit\TextUI\Configuration\Configuration; +use RuntimeException; + +final class MyExtensionBootstrap implements Extension +{ + public function __construct() + { + throw new RuntimeException('message'); + } + + public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void + { + } +} diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/src/autoload.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/src/autoload.php new file mode 100644 index 00000000000..c84415b0429 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/src/autoload.php @@ -0,0 +1,11 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/MyExtensionBootstrap.php'; diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/tests/Test.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/tests/Test.php new file mode 100644 index 00000000000..a6c86c85797 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-constructor/tests/Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/extension/_files/exception-in-extension-subscriber/phpunit.xml b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/phpunit.xml similarity index 100% rename from tests/end-to-end/extension/_files/exception-in-extension-subscriber/phpunit.xml rename to tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/phpunit.xml diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php new file mode 100644 index 00000000000..2c4719b88b3 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/MyExecutionFinishedSubscriber.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Event\TestRunner\ExecutionFinished; +use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber; +use RuntimeException; + +final class MyExecutionFinishedSubscriber implements ExecutionFinishedSubscriber +{ + public function notify(ExecutionFinished $event): void + { + throw new RuntimeException('message'); + } +} diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php new file mode 100644 index 00000000000..9c0174a96cd --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/MyExtensionBootstrap.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Runner\Extension\Extension; +use PHPUnit\Runner\Extension\Facade; +use PHPUnit\Runner\Extension\ParameterCollection; +use PHPUnit\TextUI\Configuration\Configuration; + +final class MyExtensionBootstrap implements Extension +{ + public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void + { + $facade->registerSubscriber(new MyExecutionFinishedSubscriber); + } +} diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/autoload.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/autoload.php new file mode 100644 index 00000000000..98531b415f5 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/src/autoload.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/MyExtensionBootstrap.php'; + +require __DIR__ . '/MyExecutionFinishedSubscriber.php'; diff --git a/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/tests/Test.php b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/tests/Test.php new file mode 100644 index 00000000000..a6c86c85797 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/exception-in-extension-subscriber/tests/Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/extension/_files/extension-bootstrap/phpunit-without-parameter.xml b/tests/end-to-end/extension-xml/_files/extension-bootstrap/phpunit-without-parameter.xml similarity index 100% rename from tests/end-to-end/extension/_files/extension-bootstrap/phpunit-without-parameter.xml rename to tests/end-to-end/extension-xml/_files/extension-bootstrap/phpunit-without-parameter.xml diff --git a/tests/end-to-end/extension/_files/extension-bootstrap/phpunit.xml b/tests/end-to-end/extension-xml/_files/extension-bootstrap/phpunit.xml similarity index 100% rename from tests/end-to-end/extension/_files/extension-bootstrap/phpunit.xml rename to tests/end-to-end/extension-xml/_files/extension-bootstrap/phpunit.xml diff --git a/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php b/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php new file mode 100644 index 00000000000..7e7be91538d --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/MyExecutionFinishedSubscriber.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use const PHP_EOL; +use PHPUnit\Event\TestRunner\ExecutionFinished; +use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber; + +final class MyExecutionFinishedSubscriber implements ExecutionFinishedSubscriber +{ + private readonly string $message; + + public function __construct(string $message) + { + $this->message = $message; + } + + public function notify(ExecutionFinished $event): void + { + print __METHOD__ . PHP_EOL; + print $this->message . PHP_EOL; + } +} diff --git a/tests/end-to-end/extension/_files/extension-bootstrap/src/MyExtensionBootstrap.php b/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/MyExtensionBootstrap.php similarity index 100% rename from tests/end-to-end/extension/_files/extension-bootstrap/src/MyExtensionBootstrap.php rename to tests/end-to-end/extension-xml/_files/extension-bootstrap/src/MyExtensionBootstrap.php diff --git a/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/autoload.php b/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/autoload.php new file mode 100644 index 00000000000..98531b415f5 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/extension-bootstrap/src/autoload.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/MyExtensionBootstrap.php'; + +require __DIR__ . '/MyExecutionFinishedSubscriber.php'; diff --git a/tests/end-to-end/extension-xml/_files/extension-bootstrap/tests/Test.php b/tests/end-to-end/extension-xml/_files/extension-bootstrap/tests/Test.php new file mode 100644 index 00000000000..a6c86c85797 --- /dev/null +++ b/tests/end-to-end/extension-xml/_files/extension-bootstrap/tests/Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event\MyExtension; + +use PHPUnit\Framework\TestCase; + +final class Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/extension/bootstrap-with-parameter.phpt b/tests/end-to-end/extension-xml/bootstrap-with-parameter.phpt similarity index 100% rename from tests/end-to-end/extension/bootstrap-with-parameter.phpt rename to tests/end-to-end/extension-xml/bootstrap-with-parameter.phpt diff --git a/tests/end-to-end/extension/bootstrap-without-parameter.phpt b/tests/end-to-end/extension-xml/bootstrap-without-parameter.phpt similarity index 100% rename from tests/end-to-end/extension/bootstrap-without-parameter.phpt rename to tests/end-to-end/extension-xml/bootstrap-without-parameter.phpt diff --git a/tests/end-to-end/extension/class-does-not-exist.phpt b/tests/end-to-end/extension-xml/class-does-not-exist.phpt similarity index 100% rename from tests/end-to-end/extension/class-does-not-exist.phpt rename to tests/end-to-end/extension-xml/class-does-not-exist.phpt diff --git a/tests/end-to-end/extension/class-does-not-implement-interface.phpt b/tests/end-to-end/extension-xml/class-does-not-implement-interface.phpt similarity index 100% rename from tests/end-to-end/extension/class-does-not-implement-interface.phpt rename to tests/end-to-end/extension-xml/class-does-not-implement-interface.phpt diff --git a/tests/end-to-end/extension/exception-in-extension-bootstrap-method.phpt b/tests/end-to-end/extension-xml/exception-in-extension-bootstrap-method.phpt similarity index 100% rename from tests/end-to-end/extension/exception-in-extension-bootstrap-method.phpt rename to tests/end-to-end/extension-xml/exception-in-extension-bootstrap-method.phpt diff --git a/tests/end-to-end/extension/exception-in-extension-constructor.phpt b/tests/end-to-end/extension-xml/exception-in-extension-constructor.phpt similarity index 100% rename from tests/end-to-end/extension/exception-in-extension-constructor.phpt rename to tests/end-to-end/extension-xml/exception-in-extension-constructor.phpt diff --git a/tests/end-to-end/extension/exception-in-extension-subscriber.phpt b/tests/end-to-end/extension-xml/exception-in-extension-subscriber.phpt similarity index 100% rename from tests/end-to-end/extension/exception-in-extension-subscriber.phpt rename to tests/end-to-end/extension-xml/exception-in-extension-subscriber.phpt diff --git a/tests/end-to-end/extension-xml/phar-extension.phpt b/tests/end-to-end/extension-xml/phar-extension.phpt new file mode 100644 index 00000000000..55a9405df79 --- /dev/null +++ b/tests/end-to-end/extension-xml/phar-extension.phpt @@ -0,0 +1,35 @@ +--TEST-- +The right events are emitted in the right order when a PHPUnit extension from a PHAR is loaded +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Extension Loaded from PHAR (phpunit/phpunit-test-extension 1.0.0) +Extension Bootstrapped (PHPUnit\TestFixture\MyExtension\MyExtensionBootstrap) +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s%etests%eend-to-end%e_files%ephar-extension%ephpunit.xml, 1 test) +Test Suite Started (default, 1 test) +Test Suite Started (PHPUnit\TestFixture\Event\MyExtension\Test, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) +Test Prepared (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) +Test Passed (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) +Test Finished (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) +Test Suite Finished (PHPUnit\TestFixture\Event\MyExtension\Test, 1 test) +Test Suite Finished (default, 1 test) +Test Suite Finished (%s%etests%eend-to-end%e_files%ephar-extension%ephpunit.xml, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/extension/phar-extension.phpt b/tests/end-to-end/extension/phar-extension.phpt deleted file mode 100644 index 1709a77fb86..00000000000 --- a/tests/end-to-end/extension/phar-extension.phpt +++ /dev/null @@ -1,49 +0,0 @@ ---TEST-- -The right events are emitted in the right order when a PHPUnit extension from a PHAR is loaded ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); ---EXPECTF-- -PHPUnit Started (PHPUnit %s using %s) -Test Runner Configured -Test Suite Loaded (1 test) -Extension Loaded from PHAR (phpunit/phpunit-test-extension 1.0.0) -Extension Bootstrapped (PHPUnit\TestFixture\MyExtension\MyExtensionBootstrap) -Event Facade Sealed -Test Runner Started -Test Suite Sorted -Test Runner Execution Started (1 test) -Test Suite Started (%s/tests/end-to-end/_files/phar-extension/phpunit.xml, 1 test) -Test Suite Started (default, 1 test) -Test Suite Started (PHPUnit\TestFixture\Event\MyExtension\Test, 1 test) -Test Preparation Started (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) -Test Prepared (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) -Assertion Succeeded (Constraint: is true, Value: true) -Test Passed (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) -Test Finished (PHPUnit\TestFixture\Event\MyExtension\Test::testOne) -Test Suite Finished (PHPUnit\TestFixture\Event\MyExtension\Test, 1 test) -Test Suite Finished (default, 1 test) -Test Suite Finished (%s/tests/end-to-end/_files/phar-extension/phpunit.xml, 1 test) -Test Runner Execution Finished -Test Runner Finished -PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/generic/_files/ExpectErrorLogFailTest.php b/tests/end-to-end/generic/_files/ExpectErrorLogFailTest.php new file mode 100644 index 00000000000..e27a6911158 --- /dev/null +++ b/tests/end-to-end/generic/_files/ExpectErrorLogFailTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\ExpectNoErrorLog; + +use PHPUnit\Framework\TestCase; + +class FooBar +{ + public function doFoo() + { + return ''; + } +} + +final class ExpectErrorLogFailTest extends TestCase +{ + public function testOne(): void + { + $foo = new FooBar; + + $this->assertSame('', $foo->doFoo()); + $this->expectErrorLog(); + } +} diff --git a/tests/end-to-end/generic/_files/ExpectErrorLogTest.php b/tests/end-to-end/generic/_files/ExpectErrorLogTest.php new file mode 100644 index 00000000000..2c374c1fa01 --- /dev/null +++ b/tests/end-to-end/generic/_files/ExpectErrorLogTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\ExpectErrorLog; + +use function error_log; +use PHPUnit\Framework\TestCase; + +class Foo +{ + public function doFoo() + { + error_log('logged a side effect'); + + return ''; + } +} + +final class ExpectErrorLogTest extends TestCase +{ + public function testOne(): void + { + $foo = new Foo; + + $this->assertSame('', $foo->doFoo()); + $this->expectErrorLog(); + } +} diff --git a/tests/end-to-end/generic/_files/TestForDeprecatedFeatureTest.php b/tests/end-to-end/generic/_files/TestForDeprecatedFeatureTest.php new file mode 100644 index 00000000000..e322b407fa7 --- /dev/null +++ b/tests/end-to-end/generic/_files/TestForDeprecatedFeatureTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\TestCase; + +final class TestForDeprecatedFeatureTest extends TestCase +{ + #[IgnoreDeprecations] + public function testExpectationOnExactDeprecationMessageWorksWhenExpectedDeprecationIsTriggered(): void + { + $this->expectUserDeprecationMessage('message'); + + @trigger_error('message', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testExpectationsOnExactDeprecationMessagesWorkWhenExpectedDeprecationsAreTriggered(): void + { + $this->expectUserDeprecationMessage('message'); + $this->expectUserDeprecationMessage('another message'); + + @trigger_error('message', E_USER_DEPRECATED); + @trigger_error('another message', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testExpectationOnExactDeprecationMessageWorksWhenExpectedDeprecationIsNotTriggered(): void + { + $this->expectUserDeprecationMessage('message'); + } + + #[IgnoreDeprecations] + public function testExpectationOnExactDeprecationMessageWorksWhenUnexpectedDeprecationIsTriggered(): void + { + $this->expectUserDeprecationMessage('message'); + + @trigger_error('another message', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenExpectedDeprecationIsTriggered(): void + { + $this->expectUserDeprecationMessageMatches('/message/'); + + @trigger_error('...message...', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testExpectationsOnDeprecationMessagesMatchingRegularExpressionsWorkWhenExpectedDeprecationsAreTriggered(): void + { + $this->expectUserDeprecationMessageMatches('/foo/'); + $this->expectUserDeprecationMessageMatches('/bar/'); + + @trigger_error('...foo...', E_USER_DEPRECATED); + @trigger_error('...bar...', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenExpectedDeprecationIsNotTriggered(): void + { + $this->expectUserDeprecationMessageMatches('/message/'); + } + + #[IgnoreDeprecations] + public function testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenUnepectedDeprecationIsTriggered(): void + { + $this->expectUserDeprecationMessageMatches('/message/'); + + @trigger_error('something else', E_USER_DEPRECATED); + } +} diff --git a/tests/end-to-end/generic/assertion.phpt b/tests/end-to-end/generic/assertion.phpt index 680738b6d7a..bff737be7b3 100644 --- a/tests/end-to-end/generic/assertion.phpt +++ b/tests/end-to-end/generic/assertion.phpt @@ -5,10 +5,6 @@ phpunit ../../_files/AssertionExampleTest.php if (ini_get('zend.assertions') != 1) { print 'skip: zend.assertions=1 is required' . PHP_EOL; } - -if (ini_get('assert.exception') != 1) { - print 'skip: assert.exception=1 is required' . PHP_EOL; -} --FILE-- run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Could not read XML from file "does-not-exist.xml" diff --git a/tests/end-to-end/generic/controlled-garbage-collection.phpt b/tests/end-to-end/generic/controlled-garbage-collection.phpt index b8451ae4387..5e08f9c6401 100644 --- a/tests/end-to-end/generic/controlled-garbage-collection.phpt +++ b/tests/end-to-end/generic/controlled-garbage-collection.phpt @@ -1,56 +1,41 @@ --TEST-- phpunit --configuration=__DIR__.'/../_files/controlled-garbage-collection' ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured -Test Suite Loaded (2 tests) Event Facade Sealed +Test Suite Loaded (2 tests) Test Runner Started Test Suite Sorted Test Runner Execution Started (2 tests) Test Runner Disabled Garbage Collection Test Runner Triggered Garbage Collection -Test Suite Started (%s/phpunit.xml, 2 tests) +Test Suite Started (%s%ephpunit.xml, 2 tests) Test Suite Started (default, 2 tests) Test Suite Started (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest, 2 tests) Test Preparation Started (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testOne) Test Prepared (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testOne) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testOne) Test Finished (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testOne) Test Runner Triggered Garbage Collection Test Preparation Started (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testTwo) Test Prepared (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testTwo) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testTwo) Test Finished (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest::testTwo) Test Runner Triggered Garbage Collection Test Suite Finished (PHPUnit\TestFixture\GarbageCollection\GarbageCollectionTest, 2 tests) Test Suite Finished (default, 2 tests) -Test Suite Finished (%s/phpunit.xml, 2 tests) +Test Suite Finished (%s%ephpunit.xml, 2 tests) Test Runner Execution Finished Test Runner Triggered Garbage Collection Test Runner Enabled Garbage Collection diff --git a/tests/end-to-end/generic/cwd-restore-after-test.phpt b/tests/end-to-end/generic/cwd-restore-after-test.phpt new file mode 100644 index 00000000000..cfd976f2a87 --- /dev/null +++ b/tests/end-to-end/generic/cwd-restore-after-test.phpt @@ -0,0 +1,18 @@ +--TEST-- +phpunit ../../_files/CwdRestoreTest.php +--FILE-- +run($_SERVER['argv']); + +var_dump($cwd === getcwd()); +--EXPECTF-- +bool(true) diff --git a/tests/end-to-end/generic/dataprovider-issue-2833.phpt b/tests/end-to-end/generic/dataprovider-issue-2833.phpt deleted file mode 100644 index ea4a31c1c6f..00000000000 --- a/tests/end-to-end/generic/dataprovider-issue-2833.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -phpunit ../../_files/DataProviderIssue2833/ ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s - -.. 2 / 2 (100%) - -Time: %s, Memory: %s - -OK (2 tests, 2 assertions) diff --git a/tests/end-to-end/generic/dataprovider-issue-2859.phpt b/tests/end-to-end/generic/dataprovider-issue-2859.phpt deleted file mode 100644 index 5168fd08d29..00000000000 --- a/tests/end-to-end/generic/dataprovider-issue-2859.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -phpunit -c ../../_files/DataProviderIssue2859/phpunit.xml ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s -Configuration: %s - -. 1 / 1 (100%) - -Time: %s, Memory: %s - -OK (1 test, 1 assertion) diff --git a/tests/end-to-end/generic/dataprovider-log-xml-isolation.phpt b/tests/end-to-end/generic/dataprovider-log-xml-isolation.phpt deleted file mode 100644 index f674d803966..00000000000 --- a/tests/end-to-end/generic/dataprovider-log-xml-isolation.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -phpunit --process-isolation --log-junit php://stdout ../../_files/DataProviderTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($logfile); - -unlink($logfile); ---EXPECTF-- - - - - - - - - PHPUnit\TestFixture\DataProviderTest::testAdd with data set #2%A -Failed asserting that 2 matches expected 3. -%A -%s:%i - - - - - diff --git a/tests/end-to-end/generic/dataprovider-log-xml.phpt b/tests/end-to-end/generic/dataprovider-log-xml.phpt deleted file mode 100644 index 541cb9f5b5f..00000000000 --- a/tests/end-to-end/generic/dataprovider-log-xml.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -phpunit --log-junit php://stdout ../../_files/DataProviderTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($logfile); - -unlink($logfile); ---EXPECTF-- - - - - - - - - PHPUnit\TestFixture\DataProviderTest::testAdd with data set #2%A -Failed asserting that 2 matches expected 3. -%A -%s:%i - - - - - diff --git a/tests/end-to-end/generic/dataprovider-named-arguments.phpt b/tests/end-to-end/generic/dataprovider-named-arguments.phpt new file mode 100644 index 00000000000..8f14acebce7 --- /dev/null +++ b/tests/end-to-end/generic/dataprovider-named-arguments.phpt @@ -0,0 +1,20 @@ +--TEST-- +phpunit ../../_files/DataProviderNamedArgumentsTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.. 2 / 2 (100%) + +Time: %s, Memory: %s + +OK (2 tests, 2 assertions) diff --git a/tests/end-to-end/generic/debug-with-telemetry.phpt b/tests/end-to-end/generic/debug-with-telemetry.phpt new file mode 100644 index 00000000000..58a8ee1d1cb --- /dev/null +++ b/tests/end-to-end/generic/debug-with-telemetry.phpt @@ -0,0 +1,30 @@ +--TEST-- +phpunit --debug --with-telemetry ../../_files/BankAccountTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] PHPUnit Started (PHPUnit %s using %s) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Runner Configured +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Event Facade Sealed +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Suite Loaded (1 test) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Runner Started +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Suite Sorted +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Runner Execution Started (1 test) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Suite Started (PHPUnit\TestFixture\Basic\SuccessTest, 1 test) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Preparation Started (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Prepared (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Passed (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Finished (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Suite Finished (PHPUnit\TestFixture\Basic\SuccessTest, 1 test) +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Runner Execution Finished +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] Test Runner Finished +[%s:%s:%s.%s / %s:%s:%s.%s] [%s bytes] PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/generic/debug.phpt b/tests/end-to-end/generic/debug.phpt new file mode 100644 index 00000000000..d43cd14c4b3 --- /dev/null +++ b/tests/end-to-end/generic/debug.phpt @@ -0,0 +1,29 @@ +--TEST-- +phpunit --debug ../../_files/BankAccountTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Basic\SuccessTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +Test Prepared (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +Test Passed (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +Test Finished (PHPUnit\TestFixture\Basic\SuccessTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\Basic\SuccessTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/generic/deprecation-can-be-expected.phpt b/tests/end-to-end/generic/deprecation-can-be-expected.phpt new file mode 100644 index 00000000000..17ea5eda2e1 --- /dev/null +++ b/tests/end-to-end/generic/deprecation-can-be-expected.phpt @@ -0,0 +1,36 @@ +--TEST-- +E_USER_DEPRECATED issues can be expected +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +..FF..FF 8 / 8 (100%) + +Time: %s, Memory: %s + +There were 4 failures: + +1) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnExactDeprecationMessageWorksWhenExpectedDeprecationIsNotTriggered +Expected deprecation with message "message" was not triggered + +2) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnExactDeprecationMessageWorksWhenUnexpectedDeprecationIsTriggered +Expected deprecation with message "message" was not triggered + +3) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenExpectedDeprecationIsNotTriggered +Expected deprecation with message matching regular expression "/message/" was not triggered + +4) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenUnepectedDeprecationIsTriggered +Expected deprecation with message matching regular expression "/message/" was not triggered + +FAILURES! +Tests: 8, Assertions: 10, Failures: 4. diff --git a/tests/end-to-end/generic/deprecations-can-be-ignored-using-attribute.phpt b/tests/end-to-end/generic/deprecations-can-be-ignored-using-attribute.phpt index a08eb198123..cb741279e6e 100644 --- a/tests/end-to-end/generic/deprecations-can-be-ignored-using-attribute.phpt +++ b/tests/end-to-end/generic/deprecations-can-be-ignored-using-attribute.phpt @@ -15,19 +15,17 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s -.D 2 / 2 (100%) +.D.D 4 / 4 (100%) Time: %s, Memory: %s -1 test triggered 1 deprecation: +2 tests triggered 2 deprecations: 1) %sIgnoreDeprecationsTest.php:%d message -Triggered by: - -* PHPUnit\TestFixture\Event\IgnoreDeprecationsTest::testTwo - %sIgnoreDeprecationsTest.php:%d +2) %sIgnoreDeprecationsTest.php:%d +message OK, but there were issues! -Tests: 2, Assertions: 2, Deprecations: 1. +Tests: 4, Assertions: 6, Deprecations: 2. diff --git a/tests/end-to-end/generic/exception-in-mock-destructor.phpt b/tests/end-to-end/generic/exception-in-mock-destructor.phpt new file mode 100644 index 00000000000..3a2e2e4f86f --- /dev/null +++ b/tests/end-to-end/generic/exception-in-mock-destructor.phpt @@ -0,0 +1,28 @@ +--TEST-- +phpunit ../../_files/ExceptionInMockDestructorTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +E 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 error: + +1) PHPUnit\TestFixture\ExceptionInMockDestructorTest::testOne +Exception: Some exception. + +%sExceptionInMockDestructor.php:%d + +ERRORS! +Tests: 1, Assertions: 1, Errors: 1. diff --git a/tests/end-to-end/generic/expect-error-log-fail.phpt b/tests/end-to-end/generic/expect-error-log-fail.phpt new file mode 100644 index 00000000000..8ef6d1203f6 --- /dev/null +++ b/tests/end-to-end/generic/expect-error-log-fail.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/2155 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +F 1 / 1 (100%) + +Time: %s, Memory: %s + +Expect Error Log Fail (PHPUnit\TestFixture\ExpectNoErrorLog\ExpectErrorLogFail) + ✘ One + │ + │ Test did not call error_log(). + │ Failed asserting that a string is not empty. + + │ + +FAILURES! +Tests: 1, Assertions: 2, Failures: 1. diff --git a/tests/end-to-end/generic/expect-error-log.phpt b/tests/end-to-end/generic/expect-error-log.phpt new file mode 100644 index 00000000000..4dcfc08f6f8 --- /dev/null +++ b/tests/end-to-end/generic/expect-error-log.phpt @@ -0,0 +1,25 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/2155 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +Expect Error Log (PHPUnit\TestFixture\ExpectErrorLog\ExpectErrorLog) + ✔ One + +OK (1 test, 2 assertions) diff --git a/tests/end-to-end/generic/failure-isolation.phpt b/tests/end-to-end/generic/failure-isolation.phpt index 85428568f1e..6461d69cb5f 100644 --- a/tests/end-to-end/generic/failure-isolation.phpt +++ b/tests/end-to-end/generic/failure-isolation.phpt @@ -139,4 +139,4 @@ Failed asserting that string matches format description. %s:%i FAILURES! -Tests: 13, Assertions: 14, Failures: 13. +Tests: 13, Assertions: 15, Failures: 13. diff --git a/tests/end-to-end/generic/failure.phpt b/tests/end-to-end/generic/failure.phpt index bb6edfcb0e6..2c46c7fb45b 100644 --- a/tests/end-to-end/generic/failure.phpt +++ b/tests/end-to-end/generic/failure.phpt @@ -138,4 +138,4 @@ Failed asserting that string matches format description. %s:%i FAILURES! -Tests: 13, Assertions: 14, Failures: 13. +Tests: 13, Assertions: 15, Failures: 13. diff --git a/tests/end-to-end/generic/outcome-and-issues-individual-options.phpt b/tests/end-to-end/generic/outcome-and-issues-individual-options.phpt new file mode 100644 index 00000000000..968644811f4 --- /dev/null +++ b/tests/end-to-end/generic/outcome-and-issues-individual-options.phpt @@ -0,0 +1,161 @@ +--TEST-- +phpunit --display-incomplete --display-skipped --display-deprecations --display-errors --display-notices --display-warnings ../_files/OutcomesAndIssuesTest +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.RDNWFFFEEEDNWDNW 17 / 17 (100%) + +Time: %s, Memory: %s + +There were 3 errors: + +1) PHPUnit\TestFixture\OutcomesAndIssuesTest::testErrorWithDeprecation +Exception: exception message + +%sOutcomesAndIssuesTest.php:%d + +2) PHPUnit\TestFixture\OutcomesAndIssuesTest::testErrorWithNotice +Exception: exception message + +%sOutcomesAndIssuesTest.php:%d + +3) PHPUnit\TestFixture\OutcomesAndIssuesTest::testErrorWithWarning +Exception: exception message + +%sOutcomesAndIssuesTest.php:%d + +-- + +There were 3 failures: + +1) PHPUnit\TestFixture\OutcomesAndIssuesTest::testFailWithDeprecation +Failed asserting that false is true. + +%sOutcomesAndIssuesTest.php:%d + +2) PHPUnit\TestFixture\OutcomesAndIssuesTest::testFailWithNotice +Failed asserting that false is true. + +%sOutcomesAndIssuesTest.php:%d + +3) PHPUnit\TestFixture\OutcomesAndIssuesTest::testFailWithWarning +Failed asserting that false is true. + +%sOutcomesAndIssuesTest.php:%d + +-- + +There was 1 risky test: + +1) PHPUnit\TestFixture\OutcomesAndIssuesTest::testSuccessWithRisky +This test did not perform any assertions + +%sOutcomesAndIssuesTest.php:%d + +-- + +There were 3 incomplete tests: + +1) PHPUnit\TestFixture\OutcomesAndIssuesTest::testIncompleteWithDeprecation +incomplete message + +%sOutcomesAndIssuesTest.php:%d + +2) PHPUnit\TestFixture\OutcomesAndIssuesTest::testIncompleteWithNotice +incomplete message + +%sOutcomesAndIssuesTest.php:%d + +3) PHPUnit\TestFixture\OutcomesAndIssuesTest::testIncompleteWithWarning +incomplete message + +%sOutcomesAndIssuesTest.php:%d + +-- + +There were 3 skipped tests: + +1) PHPUnit\TestFixture\OutcomesAndIssuesTest::testSkippedWithDeprecation +skipped message + +2) PHPUnit\TestFixture\OutcomesAndIssuesTest::testSkippedWithNotice +skipped message + +3) PHPUnit\TestFixture\OutcomesAndIssuesTest::testSkippedWithWarning +skipped message + +-- + +5 tests triggered 5 warnings: + +1) %sOutcomesAndIssuesTest.php:%d +warning message + +2) %sOutcomesAndIssuesTest.php:%d +warning message + +3) %sOutcomesAndIssuesTest.php:%d +warning message + +4) %sOutcomesAndIssuesTest.php:%d +warning message + +5) %sOutcomesAndIssuesTest.php:%d +warning message + +-- + +5 tests triggered 5 notices: + +1) %sOutcomesAndIssuesTest.php:%d +notice message + +2) %sOutcomesAndIssuesTest.php:%d +notice message + +3) %sOutcomesAndIssuesTest.php:%d +notice message + +4) %sOutcomesAndIssuesTest.php:%d +notice message + +5) %sOutcomesAndIssuesTest.php:%d +notice message + +-- + +5 tests triggered 5 deprecations: + +1) %sOutcomesAndIssuesTest.php:%d +deprecation message + +2) %sOutcomesAndIssuesTest.php:%d +deprecation message + +3) %sOutcomesAndIssuesTest.php:%d +deprecation message + +4) %sOutcomesAndIssuesTest.php:%d +deprecation message + +5) %sOutcomesAndIssuesTest.php:%d +deprecation message + +ERRORS! +Tests: 17, Assertions: 7, Errors: 3, Failures: 3, Warnings: 5, Deprecations: 5, Notices: 5, Skipped: 3, Incomplete: 3, Risky: 1. diff --git a/tests/end-to-end/generic/outcome-and-issues.phpt b/tests/end-to-end/generic/outcome-and-issues.phpt index 472b3de7165..a3e35b42722 100644 --- a/tests/end-to-end/generic/outcome-and-issues.phpt +++ b/tests/end-to-end/generic/outcome-and-issues.phpt @@ -1,15 +1,10 @@ --TEST-- -phpunit --display-incomplete --display-skipped --display-deprecations --display-errors --display-notices --display-warnings ../_files/OutcomesAndIssuesTest +phpunit --display-all-issues ../_files/OutcomesAndIssuesTest --FILE-- run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s - -RRRRRRRRSSSSSRRRRSSSSRSSSSSSSRRSSSSSSSRRSSSSSSSSSSSSSSSSSSSS 60 / 60 (100%) - -Time: %s, Memory: %s - -There were 17 risky tests: - -1) PHPUnit\TestFixture\RequirementsTest::testOne -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -2) PHPUnit\TestFixture\RequirementsTest::testTwo -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -3) PHPUnit\TestFixture\RequirementsTest::testThree -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -4) PHPUnit\TestFixture\RequirementsTest::testFour -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -5) PHPUnit\TestFixture\RequirementsTest::testFive -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -6) PHPUnit\TestFixture\RequirementsTest::testSix -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -7) PHPUnit\TestFixture\RequirementsTest::testSeven -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -8) PHPUnit\TestFixture\RequirementsTest::testEight -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -9) PHPUnit\TestFixture\RequirementsTest::testExistingFunction -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -10) PHPUnit\TestFixture\RequirementsTest::testExistingMethod -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -11) PHPUnit\TestFixture\RequirementsTest::testExistingExtension -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -12) PHPUnit\TestFixture\RequirementsTest::testExistingOs -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -13) PHPUnit\TestFixture\RequirementsTest::testSpace -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -14) PHPUnit\TestFixture\RequirementsTest::testPHPVersionOperatorBangEquals -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -15) PHPUnit\TestFixture\RequirementsTest::testPHPVersionOperatorNotEquals -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -16) PHPUnit\TestFixture\RequirementsTest::testPHPUnitVersionOperatorBangEquals -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -17) PHPUnit\TestFixture\RequirementsTest::testPHPUnitVersionOperatorNotEquals -This test did not perform any assertions - -%stests%e_files%eRequirementsTest.php:%d - -OK, but there were issues! -Tests: 60, Assertions: 0, Skipped: 43, Risky: 17. diff --git a/tests/end-to-end/generic/test-suffix-multiple.phpt b/tests/end-to-end/generic/test-suffix-multiple.phpt index 14e56c637ce..d4b7b552463 100644 --- a/tests/end-to-end/generic/test-suffix-multiple.phpt +++ b/tests/end-to-end/generic/test-suffix-multiple.phpt @@ -5,7 +5,9 @@ phpunit --test-suffix .test.php,.my.php ../../_files/ $_SERVER['argv'][] = '--do-not-cache-result'; $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--test-suffix'; -$_SERVER['argv'][] = '.test.php,.my.php'; +$_SERVER['argv'][] = '.test.php'; +$_SERVER['argv'][] = '--test-suffix'; +$_SERVER['argv'][] = '.my.php'; $_SERVER['argv'][] = __DIR__ . '/../../_files/'; require_once __DIR__ . '/../../bootstrap.php'; diff --git a/tests/end-to-end/generic/unexpected-output-with-progress-printing.phpt b/tests/end-to-end/generic/unexpected-output-with-progress-printing.phpt index a6e5260718b..b18c34a5ae1 100644 --- a/tests/end-to-end/generic/unexpected-output-with-progress-printing.phpt +++ b/tests/end-to-end/generic/unexpected-output-with-progress-printing.phpt @@ -1,5 +1,9 @@ --TEST-- phpunit ../../_files/UnexpectedOutputTest.php +--SKIPIF-- + + + + + tests/foo + tests/bar-baz + tests/AnotherTest.php + + + diff --git a/tests/_files/Metadata/Annotation/tests/AnotherTest.php b/tests/end-to-end/groups-from-configuration/_files/tests/AnotherTest.php similarity index 81% rename from tests/_files/Metadata/Annotation/tests/AnotherTest.php rename to tests/end-to-end/groups-from-configuration/_files/tests/AnotherTest.php index e3a18012214..53430c9eefd 100644 --- a/tests/_files/Metadata/Annotation/tests/AnotherTest.php +++ b/tests/end-to-end/groups-from-configuration/_files/tests/AnotherTest.php @@ -7,7 +7,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace PHPUnit\TestFixture\Metadata\Annotation; +namespace PHPUnit\TestFixture\GroupsFromConfiguration; use PHPUnit\Framework\TestCase; @@ -15,5 +15,6 @@ final class AnotherTest extends TestCase { public function testOne(): void { + $this->assertTrue(true); } } diff --git a/tests/end-to-end/groups-from-configuration/_files/tests/bar-baz/BarBazTest.php b/tests/end-to-end/groups-from-configuration/_files/tests/bar-baz/BarBazTest.php new file mode 100644 index 00000000000..fa3412dc9d5 --- /dev/null +++ b/tests/end-to-end/groups-from-configuration/_files/tests/bar-baz/BarBazTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\GroupsFromConfiguration; + +use PHPUnit\Framework\TestCase; + +final class BarBazTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/groups-from-configuration/_files/tests/foo/FooTest.php b/tests/end-to-end/groups-from-configuration/_files/tests/foo/FooTest.php new file mode 100644 index 00000000000..a5019fd098a --- /dev/null +++ b/tests/end-to-end/groups-from-configuration/_files/tests/foo/FooTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\GroupsFromConfiguration; + +use PHPUnit\Framework\TestCase; + +final class FooTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/groups-from-configuration/group-another-group.phpt b/tests/end-to-end/groups-from-configuration/group-another-group.phpt new file mode 100644 index 00000000000..02b04573196 --- /dev/null +++ b/tests/end-to-end/groups-from-configuration/group-another-group.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --configuration _files/phpunit.xml --list-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (%sphpunit.xml, 1 test) +Test Suite Started (default, 1 test) +Test Suite Started (PHPUnit\TestFixture\GroupsFromConfiguration\AnotherTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\GroupsFromConfiguration\AnotherTest::testOne) +Test Prepared (PHPUnit\TestFixture\GroupsFromConfiguration\AnotherTest::testOne) +Test Passed (PHPUnit\TestFixture\GroupsFromConfiguration\AnotherTest::testOne) +Test Finished (PHPUnit\TestFixture\GroupsFromConfiguration\AnotherTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\GroupsFromConfiguration\AnotherTest, 1 test) +Test Suite Finished (default, 1 test) +Test Suite Finished (%sphpunit.xml, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/groups-from-configuration/group-bar.phpt b/tests/end-to-end/groups-from-configuration/group-bar.phpt new file mode 100644 index 00000000000..8d241423263 --- /dev/null +++ b/tests/end-to-end/groups-from-configuration/group-bar.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --configuration _files/phpunit.xml --list-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (%sphpunit.xml, 1 test) +Test Suite Started (default, 1 test) +Test Suite Started (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Prepared (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Passed (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Finished (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest, 1 test) +Test Suite Finished (default, 1 test) +Test Suite Finished (%sphpunit.xml, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/groups-from-configuration/group-baz.phpt b/tests/end-to-end/groups-from-configuration/group-baz.phpt new file mode 100644 index 00000000000..a407cec537a --- /dev/null +++ b/tests/end-to-end/groups-from-configuration/group-baz.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --configuration _files/phpunit.xml --list-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (%sphpunit.xml, 1 test) +Test Suite Started (default, 1 test) +Test Suite Started (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Prepared (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Passed (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Finished (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\GroupsFromConfiguration\BarBazTest, 1 test) +Test Suite Finished (default, 1 test) +Test Suite Finished (%sphpunit.xml, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/groups-from-configuration/group-foo.phpt b/tests/end-to-end/groups-from-configuration/group-foo.phpt new file mode 100644 index 00000000000..38e19a13bc2 --- /dev/null +++ b/tests/end-to-end/groups-from-configuration/group-foo.phpt @@ -0,0 +1,36 @@ +--TEST-- +phpunit --configuration _files/phpunit.xml --list-groups +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (3 tests) +Test Runner Started +Test Suite Sorted +Test Suite Filtered (1 test) +Test Runner Execution Started (1 test) +Test Suite Started (%sphpunit.xml, 1 test) +Test Suite Started (default, 1 test) +Test Suite Started (PHPUnit\TestFixture\GroupsFromConfiguration\FooTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\GroupsFromConfiguration\FooTest::testOne) +Test Prepared (PHPUnit\TestFixture\GroupsFromConfiguration\FooTest::testOne) +Test Passed (PHPUnit\TestFixture\GroupsFromConfiguration\FooTest::testOne) +Test Finished (PHPUnit\TestFixture\GroupsFromConfiguration\FooTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\GroupsFromConfiguration\FooTest, 1 test) +Test Suite Finished (default, 1 test) +Test Suite Finished (%sphpunit.xml, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/logging/_files/CaseWithDollarSignTest.php b/tests/end-to-end/logging/_files/CaseWithDollarSignTest.php new file mode 100644 index 00000000000..725ad4f2f32 --- /dev/null +++ b/tests/end-to-end/logging/_files/CaseWithDollarSignTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +final class CaseWithDollarSignTest extends TestCase +{ + public static function dataProvider(): iterable + { + yield ['$12.34']; + + yield ['Some text before the price $5.67']; + + yield ['Dollar sign followed by letter $Q']; + + yield ['Alone $ surrounded by spaces']; + } + + #[DataProvider('dataProvider')] + #[TestDox('The "$x" is used for this test')] + public function testSomething(string $x): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/logging/_files/DataProviderTest.php b/tests/end-to-end/logging/_files/DataProviderTest.php new file mode 100644 index 00000000000..4095ae2090a --- /dev/null +++ b/tests/end-to-end/logging/_files/DataProviderTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TeamCity; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class DataProviderTest extends TestCase +{ + public static function provider(): array + { + return [ + [true], + [false], + ]; + } + + #[DataProvider('provider')] + public function testOne(bool $value): void + { + $this->assertTrue($value); + } +} diff --git a/tests/end-to-end/logging/junit/invalid-path.phpt b/tests/end-to-end/logging/junit/invalid-path.phpt new file mode 100644 index 00000000000..970506851df --- /dev/null +++ b/tests/end-to-end/logging/junit/invalid-path.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --log-junit is used with an invalid target path +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in JUnit XML format to "": Directory "" does not exist and could not be created + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/junit/invalid-socket.phpt b/tests/end-to-end/logging/junit/invalid-socket.phpt new file mode 100644 index 00000000000..f52243ce7ee --- /dev/null +++ b/tests/end-to-end/logging/junit/invalid-socket.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --log-junit is used with an invalid socket +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in JUnit XML format to "socket://hostname:port:wrong": "socket://hostname:port:wrong" does not match "socket://hostname:port" format + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/junit/skipped-before-class-method.phpt b/tests/end-to-end/logging/junit/skipped-before-class-method.phpt new file mode 100644 index 00000000000..b4d6d85a334 --- /dev/null +++ b/tests/end-to-end/logging/junit/skipped-before-class-method.phpt @@ -0,0 +1,19 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5354 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + diff --git a/tests/end-to-end/logging/junit/skipped-test-method.phpt b/tests/end-to-end/logging/junit/skipped-test-method.phpt new file mode 100644 index 00000000000..03033bea76b --- /dev/null +++ b/tests/end-to-end/logging/junit/skipped-test-method.phpt @@ -0,0 +1,24 @@ +--TEST-- +JUnit XML: test skipped in test method +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + + + + + + diff --git a/tests/end-to-end/logging/junit/to-file.phpt b/tests/end-to-end/logging/junit/to-file.phpt new file mode 100644 index 00000000000..3754ea3fc8a --- /dev/null +++ b/tests/end-to-end/logging/junit/to-file.phpt @@ -0,0 +1,70 @@ +--TEST-- +phpunit --log-junit junit.xml _files/StatusTest.php +--FILE-- +run($_SERVER['argv']); + +print file_get_contents($logfile); + +unlink($logfile); +--EXPECTF-- + + + + + + PHPUnit\TestFixture\Basic\StatusTest::testFailure%A +Failed asserting that false is true. +%A +%sStatusTest.php:%d + + + PHPUnit\TestFixture\Basic\StatusTest::testError%A +RuntimeException:%w +%A +%sStatusTest.php:%d + + + + + + + + + + + PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage%A +failure with custom message +Failed asserting that false is true. +%A +%sStatusTest.php:%d + + + PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage%A +RuntimeException: error with custom message +%A +%sStatusTest.php:%d + + + + + + + + + + + + + diff --git a/tests/end-to-end/logging/junit/to-stdout.phpt b/tests/end-to-end/logging/junit/to-stdout.phpt new file mode 100644 index 00000000000..54014851bed --- /dev/null +++ b/tests/end-to-end/logging/junit/to-stdout.phpt @@ -0,0 +1,64 @@ +--TEST-- +phpunit --log-junit php://stdout _files/StatusTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + + + PHPUnit\TestFixture\Basic\StatusTest::testFailure%A +Failed asserting that false is true. +%A +%sStatusTest.php:%d + + + PHPUnit\TestFixture\Basic\StatusTest::testError%A +RuntimeException:%w +%A +%sStatusTest.php:%d + + + + + + + + + + + PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage%A +failure with custom message +Failed asserting that false is true. +%A +%sStatusTest.php:%d + + + PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage%A +RuntimeException: error with custom message +%A +%sStatusTest.php:%d + + + + + + + + + + + + + diff --git a/tests/end-to-end/logging/junit/with-progress-with-errors.phpt b/tests/end-to-end/logging/junit/with-progress-with-errors.phpt new file mode 100644 index 00000000000..39afc52b8eb --- /dev/null +++ b/tests/end-to-end/logging/junit/with-progress-with-errors.phpt @@ -0,0 +1,54 @@ +--TEST-- +phpunit --log-junit php://stdout _files/TypeErrorTest.php +--SKIPIF-- +run($_SERVER['argv']); + +print file_get_contents($logfile); + +unlink($logfile); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +E 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 error: + +1) PHPUnit\TestFixture\TypeErrorTest::testMe +TypeError: Cannot assign DateTime to property PHPUnit\TestFixture\TypeErrorTest::$dateTime of type DateTimeImmutable + +%sTypeErrorTest.php:%d + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. + + + + + PHPUnit\TestFixture\TypeErrorTest::testMe +TypeError: Cannot assign DateTime to property PHPUnit\TestFixture\TypeErrorTest::$dateTime of type DateTimeImmutable + +%sTypeErrorTest.php:%s + + + diff --git a/tests/end-to-end/logging/log-junit-to-file.phpt b/tests/end-to-end/logging/log-junit-to-file.phpt deleted file mode 100644 index 872378a2471..00000000000 --- a/tests/end-to-end/logging/log-junit-to-file.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -phpunit --log-junit junit.xml _files/StatusTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($logfile); - -unlink($logfile); ---EXPECTF-- - - - - - - PHPUnit\TestFixture\Basic\StatusTest::testFailure%A -Failed asserting that false is true. -%A -%sStatusTest.php:%d - - - PHPUnit\TestFixture\Basic\StatusTest::testError%A -RuntimeException:%w -%A -%sStatusTest.php:%d - - - - - - - - - - - PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage%A -failure with custom message -Failed asserting that false is true. -%A -%sStatusTest.php:%d - - - PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage%A -RuntimeException: error with custom message -%A -%sStatusTest.php:%d - - - - - - - - - - - - - diff --git a/tests/end-to-end/logging/log-junit-to-stdout.phpt b/tests/end-to-end/logging/log-junit-to-stdout.phpt deleted file mode 100644 index f481658b6e7..00000000000 --- a/tests/end-to-end/logging/log-junit-to-stdout.phpt +++ /dev/null @@ -1,64 +0,0 @@ ---TEST-- -phpunit --log-junit php://stdout _files/StatusTest.php ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- - - - - - - PHPUnit\TestFixture\Basic\StatusTest::testFailure%A -Failed asserting that false is true. -%A -%sStatusTest.php:%d - - - PHPUnit\TestFixture\Basic\StatusTest::testError%A -RuntimeException:%w -%A -%sStatusTest.php:%d - - - - - - - - - - - PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage%A -failure with custom message -Failed asserting that false is true. -%A -%sStatusTest.php:%d - - - PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage%A -RuntimeException: error with custom message -%A -%sStatusTest.php:%d - - - - - - - - - - - - - diff --git a/tests/end-to-end/logging/log-junit-with-progress-with-errors.phpt b/tests/end-to-end/logging/log-junit-with-progress-with-errors.phpt deleted file mode 100644 index 56b74113b7e..00000000000 --- a/tests/end-to-end/logging/log-junit-with-progress-with-errors.phpt +++ /dev/null @@ -1,54 +0,0 @@ ---TEST-- -phpunit --log-junit php://stdout _files/TypeErrorTest.php ---SKIPIF-- -run($_SERVER['argv']); - -print file_get_contents($logfile); - -unlink($logfile); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s - -E 1 / 1 (100%) - -Time: %s, Memory: %s - -There was 1 error: - -1) PHPUnit\TestFixture\TypeErrorTest::testMe -TypeError: Cannot assign DateTime to property PHPUnit\TestFixture\TypeErrorTest::$dateTime of type DateTimeImmutable - -%sTypeErrorTest.php:%d - -ERRORS! -Tests: 1, Assertions: 0, Errors: 1. - - - - - PHPUnit\TestFixture\TypeErrorTest::testMe -TypeError: Cannot assign DateTime to property PHPUnit\TestFixture\TypeErrorTest::$dateTime of type DateTimeImmutable - -%sTypeErrorTest.php:%s - - - diff --git a/tests/end-to-end/logging/log-teamcity-comparisonfailure.phpt b/tests/end-to-end/logging/teamcity/log-teamcity-comparisonfailure.phpt similarity index 89% rename from tests/end-to-end/logging/log-teamcity-comparisonfailure.phpt rename to tests/end-to-end/logging/teamcity/log-teamcity-comparisonfailure.phpt index c99317f4d7d..9dfab66f197 100644 --- a/tests/end-to-end/logging/log-teamcity-comparisonfailure.phpt +++ b/tests/end-to-end/logging/teamcity/log-teamcity-comparisonfailure.phpt @@ -7,20 +7,15 @@ $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--no-output'; $_SERVER['argv'][] = '--log-teamcity'; $_SERVER['argv'][] = 'php://stdout'; -$_SERVER['argv'][] = \realpath(__DIR__ . '/../../_files/ComparisonFailureTest.php'); +$_SERVER['argv'][] = \realpath(__DIR__ . '/../../../_files/ComparisonFailureTest.php'); -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='1' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\ComparisonFailureTest' locationHint='php_qn:%sComparisonFailureTest.php::\PHPUnit\TestFixture\ComparisonFailureTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='php_qn:%sComparisonFailureTest.php::\PHPUnit\TestFixture\ComparisonFailureTest::testOne' flowId='%d'] - ##teamcity[testFailed name='testOne' message='Failed asserting that false matches expected true.' details='%sComparisonFailureTest.php:%d|n' duration='%s' type='comparisonFailure' actual='false' expected='true' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%s' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\ComparisonFailureTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity/log-teamcity-invalid-path.phpt b/tests/end-to-end/logging/teamcity/log-teamcity-invalid-path.phpt new file mode 100644 index 00000000000..9ac9fe8d520 --- /dev/null +++ b/tests/end-to-end/logging/teamcity/log-teamcity-invalid-path.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --log-teamcity is used with an invalid target path +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in TeamCity format to "": Directory "" does not exist and could not be created + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/teamcity/log-teamcity-invalid-socket.phpt b/tests/end-to-end/logging/teamcity/log-teamcity-invalid-socket.phpt new file mode 100644 index 00000000000..56364addb3b --- /dev/null +++ b/tests/end-to-end/logging/teamcity/log-teamcity-invalid-socket.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --log-teamcity is used with an invalid socket +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in TeamCity format to "socket://hostname:port:wrong": "socket://hostname:port:wrong" does not match "socket://hostname:port" format + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/log-teamcity.phpt b/tests/end-to-end/logging/teamcity/log-teamcity.phpt similarity index 96% rename from tests/end-to-end/logging/log-teamcity.phpt rename to tests/end-to-end/logging/teamcity/log-teamcity.phpt index 57a4841c7ff..0b0efd9078e 100644 --- a/tests/end-to-end/logging/log-teamcity.phpt +++ b/tests/end-to-end/logging/teamcity/log-teamcity.phpt @@ -7,84 +7,47 @@ $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--no-output'; $_SERVER['argv'][] = '--log-teamcity'; $_SERVER['argv'][] = 'php://stdout'; -$_SERVER['argv'][] = __DIR__ . '/../_files/basic/unit/StatusTest.php'; +$_SERVER['argv'][] = __DIR__ . '/../../_files/basic/unit/StatusTest.php'; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='13' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Basic\StatusTest' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest' flowId='%d'] - ##teamcity[testStarted name='testSuccess' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSuccess' flowId='%d'] - ##teamcity[testFinished name='testSuccess' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testFailure' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testFailure' flowId='%d'] - ##teamcity[testFailed name='testFailure' message='Failed asserting that false is true.' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testFailure' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testError' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testError' flowId='%d'] - ##teamcity[testFailed name='testError' message='RuntimeException' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testError' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testIncomplete' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testIncomplete' flowId='%d'] - ##teamcity[testIgnored name='testIncomplete' message='' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testIncomplete' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSkipped' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSkipped' flowId='%d'] - ##teamcity[testIgnored name='testSkipped' message='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testSkipped' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testRisky' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testRisky' flowId='%d'] - ##teamcity[testFailed name='testRisky' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testRisky' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSuccessWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSuccessWithMessage' flowId='%d'] - ##teamcity[testFinished name='testSuccessWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testFailureWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage' flowId='%d'] - ##teamcity[testFailed name='testFailureWithMessage' message='failure with custom message|nFailed asserting that false is true.' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testFailureWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testErrorWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage' flowId='%d'] - ##teamcity[testFailed name='testErrorWithMessage' message='RuntimeException: error with custom message' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testErrorWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testIncompleteWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testIncompleteWithMessage' flowId='%d'] - ##teamcity[testIgnored name='testIncompleteWithMessage' message='incomplete with custom message' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testIncompleteWithMessage' duration='%d' flowId='%d'] - ##teamcity[testIgnored name='testSkippedByMetadata' message='PHP > 9000 is required.' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSkippedWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSkippedWithMessage' flowId='%d'] - ##teamcity[testIgnored name='testSkippedWithMessage' message='skipped with custom message' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testSkippedWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testRiskyWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testRiskyWithMessage' flowId='%d'] - ##teamcity[testFailed name='testRiskyWithMessage' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testRiskyWithMessage' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\StatusTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity/teamcity-data-provider.phpt b/tests/end-to-end/logging/teamcity/teamcity-data-provider.phpt new file mode 100644 index 00000000000..fa6808e411a --- /dev/null +++ b/tests/end-to-end/logging/teamcity/teamcity-data-provider.phpt @@ -0,0 +1,38 @@ +--TEST-- +phpunit --teamcity _files/DataProviderTest.php +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +##teamcity[testCount count='2' flowId='%d'] +##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TeamCity\DataProviderTest' locationHint='php_qn://%sDataProviderTest.php::\PHPUnit\TestFixture\TeamCity\DataProviderTest' flowId='%d'] +##teamcity[testSuiteStarted name='testOne' locationHint='php_qn://%sDataProviderTest.php::\PHPUnit\TestFixture\TeamCity\DataProviderTest::testOne' flowId='%d'] +##teamcity[testStarted name='testOne with data set #0' locationHint='php_qn://%sDataProviderTest.php::\PHPUnit\TestFixture\TeamCity\DataProviderTest::testOne with data set #0' flowId='%d'] +##teamcity[testFinished name='testOne with data set #0' duration='%d' flowId='%d'] +##teamcity[testStarted name='testOne with data set #1' locationHint='php_qn://%sDataProviderTest.php::\PHPUnit\TestFixture\TeamCity\DataProviderTest::testOne with data set #1' flowId='%d'] +##teamcity[testFailed name='testOne with data set #1' message='Failed asserting that false is true.' details='%sDataProviderTest.php:28|n' duration='%d' flowId='%d'] +##teamcity[testFinished name='testOne with data set #1' duration='%d' flowId='%d'] +##teamcity[testSuiteFinished name='testOne' flowId='%d'] +##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TeamCity\DataProviderTest' flowId='%d'] +Time: %s, Memory: %s + +There was 1 failure: + +1) PHPUnit\TestFixture\TeamCity\DataProviderTest::testOne with data set #1 (false) +Failed asserting that false is true. + +%sDataProviderTest.php:28 + +FAILURES! +Tests: 2, Assertions: 2, Failures: 1. diff --git a/tests/end-to-end/logging/teamcity-directory.phpt b/tests/end-to-end/logging/teamcity/teamcity-directory.phpt similarity index 90% rename from tests/end-to-end/logging/teamcity-directory.phpt rename to tests/end-to-end/logging/teamcity/teamcity-directory.phpt index 2b53a40af4d..03d9afb00dd 100644 --- a/tests/end-to-end/logging/teamcity-directory.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-directory.phpt @@ -5,9 +5,9 @@ phpunit --teamcity ../../basic/unit/ $_SERVER['argv'][] = '--do-not-cache-result'; $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--teamcity'; -$_SERVER['argv'][] = __DIR__ . '/../_files/basic/unit/'; +$_SERVER['argv'][] = __DIR__ . '/../../_files/basic/unit/'; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); --EXPECTF-- @@ -15,111 +15,63 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s - ##teamcity[testCount count='19' flowId='%d'] - ##teamcity[testSuiteStarted name='CLI Arguments' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Basic\SetUpBeforeClassTest' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eSetUpBeforeClassTest.php::\PHPUnit\TestFixture\Basic\SetUpBeforeClassTest' flowId='%d'] - +##teamcity[testFailed name='PHPUnit\TestFixture\Basic\SetUpBeforeClassTest' message='Exception: forcing an Exception in setUpBeforeClass()' details='%stests%eend-to-end%e_files%ebasic%eunit%eSetUpBeforeClassTest.php:32|n' duration='%d' flowId='%d'] +##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\SetUpBeforeClassTest' message='Exception: forcing an Exception in setUpBeforeClass()' details='%stests%eend-to-end%e_files%ebasic%eunit%eSetUpBeforeClassTest.php:32|n' duration='%d' flowId='%d'] +##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\SetUpBeforeClassTest' flowId='%d'] ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Basic\SetUpTest' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eSetUpTest.php::\PHPUnit\TestFixture\Basic\SetUpTest' flowId='%d'] - ##teamcity[testFailed name='testOneWithSetUpException' message='RuntimeException: throw exception in setUp' details='%stests%eend-to-end%e_files%ebasic%eunit%eSetUpTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFailed name='testTwoWithSetUpException' message='RuntimeException: throw exception in setUp' details='%stests%eend-to-end%e_files%ebasic%eunit%eSetUpTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\SetUpTest' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Basic\StatusTest' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest' flowId='%d'] - ##teamcity[testStarted name='testSuccess' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSuccess' flowId='%d'] - ##teamcity[testFinished name='testSuccess' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testFailure' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testFailure' flowId='%d'] - ##teamcity[testFailed name='testFailure' message='Failed asserting that false is true.' details='%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testFailure' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testError' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testError' flowId='%d'] - ##teamcity[testFailed name='testError' message='RuntimeException' details='%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testError' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testIncomplete' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testIncomplete' flowId='%d'] - ##teamcity[testIgnored name='testIncomplete' message='' details='%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testIncomplete' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSkipped' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSkipped' flowId='%d'] - ##teamcity[testIgnored name='testSkipped' message='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testSkipped' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testRisky' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testRisky' flowId='%d'] - ##teamcity[testFailed name='testRisky' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testRisky' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSuccessWithMessage' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSuccessWithMessage' flowId='%d'] - ##teamcity[testFinished name='testSuccessWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testFailureWithMessage' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage' flowId='%d'] - ##teamcity[testFailed name='testFailureWithMessage' message='failure with custom message|nFailed asserting that false is true.' details='%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testFailureWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testErrorWithMessage' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage' flowId='%d'] - ##teamcity[testFailed name='testErrorWithMessage' message='RuntimeException: error with custom message' details='%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testErrorWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testIncompleteWithMessage' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testIncompleteWithMessage' flowId='%d'] - ##teamcity[testIgnored name='testIncompleteWithMessage' message='incomplete with custom message' details='%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testIncompleteWithMessage' duration='%d' flowId='%d'] - ##teamcity[testIgnored name='testSkippedByMetadata' message='PHP > 9000 is required.' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSkippedWithMessage' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSkippedWithMessage' flowId='%d'] - ##teamcity[testIgnored name='testSkippedWithMessage' message='skipped with custom message' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testSkippedWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testRiskyWithMessage' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testRiskyWithMessage' flowId='%d'] - ##teamcity[testFailed name='testRiskyWithMessage' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testRiskyWithMessage' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\StatusTest' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Basic\TearDownAfterClassTest' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eTearDownAfterClassTest.php::\PHPUnit\TestFixture\Basic\TearDownAfterClassTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eTearDownAfterClassTest.php::\PHPUnit\TestFixture\Basic\TearDownAfterClassTest::testOne' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='php_qn://%stests%eend-to-end%e_files%ebasic%eunit%eTearDownAfterClassTest.php::\PHPUnit\TestFixture\Basic\TearDownAfterClassTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\TearDownAfterClassTest' flowId='%d'] - ##teamcity[testSuiteFinished name='CLI Arguments' flowId='%d'] Time: %s, Memory: %s -There were 5 errors: +There were 6 errors: 1) PHPUnit\TestFixture\Basic\SetUpBeforeClassTest Exception: forcing an Exception in setUpBeforeClass() @@ -146,6 +98,11 @@ RuntimeException: error with custom message %s%eStatusTest.php:%d +6) PHPUnit\TestFixture\Basic\TearDownAfterClassTest +Exception: forcing an Exception in tearDownAfterClass() + +%s%eTearDownAfterClassTest.php:%d + -- There were 2 failures: @@ -176,4 +133,4 @@ This test did not perform any assertions %s%eStatusTest.php:%d ERRORS! -Tests: 18, Assertions: 6, Errors: 5, Failures: 2, Skipped: 3, Incomplete: 2, Risky: 2. +Tests: 18, Assertions: 6, Errors: 6, Failures: 2, Skipped: 3, Incomplete: 2, Risky: 2. diff --git a/tests/end-to-end/logging/teamcity-file.phpt b/tests/end-to-end/logging/teamcity/teamcity-file.phpt similarity index 97% rename from tests/end-to-end/logging/teamcity-file.phpt rename to tests/end-to-end/logging/teamcity/teamcity-file.phpt index 8e850f95ab7..bd21ba11f1f 100644 --- a/tests/end-to-end/logging/teamcity-file.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-file.phpt @@ -5,9 +5,9 @@ phpunit --teamcity ../../basic/unit/StatusTest.php $_SERVER['argv'][] = '--do-not-cache-result'; $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--teamcity'; -$_SERVER['argv'][] = __DIR__ . '/../_files/basic/unit/StatusTest.php'; +$_SERVER['argv'][] = __DIR__ . '/../../_files/basic/unit/StatusTest.php'; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); --EXPECTF-- @@ -15,81 +15,43 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s - ##teamcity[testCount count='13' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Basic\StatusTest' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest' flowId='%d'] - ##teamcity[testStarted name='testSuccess' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSuccess' flowId='%d'] - ##teamcity[testFinished name='testSuccess' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testFailure' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testFailure' flowId='%d'] - ##teamcity[testFailed name='testFailure' message='Failed asserting that false is true.' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testFailure' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testError' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testError' flowId='%d'] - ##teamcity[testFailed name='testError' message='RuntimeException' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testError' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testIncomplete' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testIncomplete' flowId='%d'] - ##teamcity[testIgnored name='testIncomplete' message='' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testIncomplete' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSkipped' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSkipped' flowId='%d'] - ##teamcity[testIgnored name='testSkipped' message='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testSkipped' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testRisky' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testRisky' flowId='%d'] - ##teamcity[testFailed name='testRisky' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testRisky' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSuccessWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSuccessWithMessage' flowId='%d'] - ##teamcity[testFinished name='testSuccessWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testFailureWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testFailureWithMessage' flowId='%d'] - ##teamcity[testFailed name='testFailureWithMessage' message='failure with custom message|nFailed asserting that false is true.' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testFailureWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testErrorWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testErrorWithMessage' flowId='%d'] - ##teamcity[testFailed name='testErrorWithMessage' message='RuntimeException: error with custom message' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testErrorWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testIncompleteWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testIncompleteWithMessage' flowId='%d'] - ##teamcity[testIgnored name='testIncompleteWithMessage' message='incomplete with custom message' details='%sStatusTest.php:%d|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testIncompleteWithMessage' duration='%d' flowId='%d'] - ##teamcity[testIgnored name='testSkippedByMetadata' message='PHP > 9000 is required.' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testSkippedWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testSkippedWithMessage' flowId='%d'] - ##teamcity[testIgnored name='testSkippedWithMessage' message='skipped with custom message' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testSkippedWithMessage' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testRiskyWithMessage' locationHint='php_qn://%sStatusTest.php::\PHPUnit\TestFixture\Basic\StatusTest::testRiskyWithMessage' flowId='%d'] - ##teamcity[testFailed name='testRiskyWithMessage' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testRiskyWithMessage' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Basic\StatusTest' flowId='%d'] Time: %s, Memory: %s diff --git a/tests/end-to-end/logging/teamcity-inner-exceptions.phpt b/tests/end-to-end/logging/teamcity/teamcity-inner-exceptions.phpt similarity index 88% rename from tests/end-to-end/logging/teamcity-inner-exceptions.phpt rename to tests/end-to-end/logging/teamcity/teamcity-inner-exceptions.phpt index a61d6671934..317052bae75 100644 --- a/tests/end-to-end/logging/teamcity-inner-exceptions.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-inner-exceptions.phpt @@ -8,26 +8,18 @@ $_SERVER['argv'][] = '--dont-report-useless-tests'; $_SERVER['argv'][] = '--no-output'; $_SERVER['argv'][] = '--log-teamcity'; $_SERVER['argv'][] = 'php://stdout'; -$_SERVER['argv'][] = \realpath(__DIR__ . '/../../_files/ExceptionStackTest.php'); +$_SERVER['argv'][] = __DIR__ . '/../../../_files/ExceptionStackTest.php'; -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\ExceptionStackTest' locationHint='php_qn://%s%etests%e_files%eExceptionStackTest.php::\PHPUnit\TestFixture\ExceptionStackTest' flowId='%d'] - ##teamcity[testStarted name='testPrintingChildException' locationHint='php_qn://%s%etests%e_files%eExceptionStackTest.php::\PHPUnit\TestFixture\ExceptionStackTest::testPrintingChildException' flowId='%d'] - ##teamcity[testFailed name='testPrintingChildException' message='Child exception|nmessage|nFailed asserting that two arrays are equal.|n--- Expected|n+++ Actual|n@@ @@|n Array (|n- 0 => 1|n+ 0 => 2|n )|n' details='%s%etests%e_files%eExceptionStackTest.php:27|n|nCaused by|nmessage|nFailed asserting that two arrays are equal.|n--- Expected|n+++ Actual|n@@ @@|n Array (|n- 0 => 1|n+ 0 => 2|n )|n|n%s%etests%e_files%eExceptionStackTest.php:23|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testPrintingChildException' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testNestedExceptions' locationHint='php_qn://%s%etests%e_files%eExceptionStackTest.php::\PHPUnit\TestFixture\ExceptionStackTest::testNestedExceptions' flowId='%d'] - -##teamcity[testFailed name='testNestedExceptions' message='Exception: One' details='%s%etests%e_files%eExceptionStackTest.php:34|n|nCaused by|nInvalidArgumentException: Two|n|n%s%etests%e_files%eExceptionStackTest.php:33|n|nCaused by|nException: Three|n|n%s%etests%e_files%eExceptionStackTest.php:33|n' duration='%d' flowId='%d'] - +##teamcity[testFailed name='testNestedExceptions' message='Exception: One' details='%s%etests%e_files%eExceptionStackTest.php:35|n|nCaused by|nInvalidArgumentException: Two|n|n%s%etests%e_files%eExceptionStackTest.php:34|n|nCaused by|nException: Three|n|n%s%etests%e_files%eExceptionStackTest.php:33|n' duration='%d' flowId='%d'] ##teamcity[testFinished name='testNestedExceptions' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\ExceptionStackTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-deprecation.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-deprecation.phpt similarity index 76% rename from tests/end-to-end/logging/teamcity-print-deprecation.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-deprecation.phpt index 15d483ae3ac..9aee5a41262 100644 --- a/tests/end-to-end/logging/teamcity-print-deprecation.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-deprecation.phpt @@ -2,31 +2,24 @@ TeamCity: print deprecation message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='%d' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest' locationHint='%sDeprecationTest.php::\PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sDeprecationTest.php::\PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testOne' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sDeprecationTest.php::\PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - +##teamcity[testStarted name='testThree' locationHint='php_qn:%sDeprecationTest.php::\PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest::testThree' flowId='%d'] +##teamcity[testFinished name='testThree' duration='%d' flowId='%d'] ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\DeprecationTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-error.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-error.phpt similarity index 86% rename from tests/end-to-end/logging/teamcity-print-error.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-error.phpt index 448539f80f4..ee97b1af979 100644 --- a/tests/end-to-end/logging/teamcity-print-error.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-error.phpt @@ -2,33 +2,23 @@ TeamCity: print error message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\ErrorTest' locationHint='%sErrorTest.php::\PHPUnit\TestFixture\TestRunnerStopping\ErrorTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sErrorTest.php::\PHPUnit\TestFixture\TestRunnerStopping\ErrorTest::testOne' flowId='%d'] - ##teamcity[testFailed name='testOne' message='Exception: message' details='%sErrorTest.php:19|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sErrorTest.php::\PHPUnit\TestFixture\TestRunnerStopping\ErrorTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\ErrorTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-failure.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-failure.phpt similarity index 86% rename from tests/end-to-end/logging/teamcity-print-failure.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-failure.phpt index 4c753f880e8..1cd2b680011 100644 --- a/tests/end-to-end/logging/teamcity-print-failure.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-failure.phpt @@ -2,32 +2,22 @@ TeamCity: print failure message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='%d' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\FailureTest' locationHint='%sFailureTest.php::\PHPUnit\TestFixture\TestRunnerStopping\FailureTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sFailureTest.php::\PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testOne' flowId='%d'] - ##teamcity[testFailed name='testOne' message='Failed asserting that false is true.' details='%sFailureTest.php:18|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sFailureTest.php::\PHPUnit\TestFixture\TestRunnerStopping\FailureTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\FailureTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-incomplete.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-incomplete.phpt similarity index 86% rename from tests/end-to-end/logging/teamcity-print-incomplete.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-incomplete.phpt index 8f9449cba45..79ed061ba0a 100644 --- a/tests/end-to-end/logging/teamcity-print-incomplete.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-incomplete.phpt @@ -2,33 +2,23 @@ TeamCity: print incomplete message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest' locationHint='%sIncompleteTest.php::\PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sIncompleteTest.php::\PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testOne' flowId='%d'] - ##teamcity[testIgnored name='testOne' message='message' details='%sIncompleteTest.php:18|n' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sIncompleteTest.php::\PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\IncompleteTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-notice.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-notice.phpt similarity index 85% rename from tests/end-to-end/logging/teamcity-print-notice.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-notice.phpt index 7efc9231683..7dc22aeaab0 100644 --- a/tests/end-to-end/logging/teamcity-print-notice.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-notice.phpt @@ -2,31 +2,22 @@ TeamCity: print notice message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\NoticeTest' locationHint='%sNoticeTest.php::\PHPUnit\TestFixture\TestRunnerStopping\NoticeTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sNoticeTest.php::\PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testOne' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sNoticeTest.php::\PHPUnit\TestFixture\TestRunnerStopping\NoticeTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\NoticeTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-risky.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-risky.phpt similarity index 86% rename from tests/end-to-end/logging/teamcity-print-risky.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-risky.phpt index 6b7a86ba981..bac507578c3 100644 --- a/tests/end-to-end/logging/teamcity-print-risky.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-risky.phpt @@ -2,32 +2,22 @@ TeamCity: print risky message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\RiskyTest' locationHint='%sRiskyTest.php::\PHPUnit\TestFixture\TestRunnerStopping\RiskyTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sRiskyTest.php::\PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testOne' flowId='%d'] - ##teamcity[testFailed name='testOne' message='This test did not perform any assertions' details='' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sRiskyTest.php::\PHPUnit\TestFixture\TestRunnerStopping\RiskyTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\RiskyTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity/teamcity-print-skipped-before-class.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-skipped-before-class.phpt new file mode 100644 index 00000000000..3049779f431 --- /dev/null +++ b/tests/end-to-end/logging/teamcity/teamcity-print-skipped-before-class.phpt @@ -0,0 +1,19 @@ +--TEST-- +TeamCity: test skipped in before-class method +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +##teamcity[testCount count='1' flowId='%d'] +##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\SkippedBeforeClassTest' locationHint='php_qn:%sSkippedBeforeClassTest.php::\PHPUnit\TestFixture\TestRunnerStopping\SkippedBeforeClassTest' flowId='%d'] +##teamcity[testIgnored name='PHPUnit\TestFixture\TestRunnerStopping\SkippedBeforeClassTest' message='message' duration='%d' flowId='%d'] +##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\SkippedBeforeClassTest' message='message' duration='%d' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-skipped.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-skipped.phpt similarity index 78% rename from tests/end-to-end/logging/teamcity-print-skipped.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-skipped.phpt index 7d7135d29b5..13ded3d259e 100644 --- a/tests/end-to-end/logging/teamcity-print-skipped.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-skipped.phpt @@ -1,34 +1,23 @@ --TEST-- -TeamCity: print skipped message +TeamCity: test skipped in test method --FILE-- -run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\SkippedTest' locationHint='%sSkippedTest.php::\PHPUnit\TestFixture\TestRunnerStopping\SkippedTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sSkippedTest.php::\PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testOne' flowId='%d'] - ##teamcity[testIgnored name='testOne' message='message' duration='%d' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sSkippedTest.php::\PHPUnit\TestFixture\TestRunnerStopping\SkippedTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\SkippedTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-print-warning.phpt b/tests/end-to-end/logging/teamcity/teamcity-print-warning.phpt similarity index 85% rename from tests/end-to-end/logging/teamcity-print-warning.phpt rename to tests/end-to-end/logging/teamcity/teamcity-print-warning.phpt index 765471fb6a5..cc6530e37d4 100644 --- a/tests/end-to-end/logging/teamcity-print-warning.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-print-warning.phpt @@ -2,31 +2,22 @@ TeamCity: print warning message --FILE-- run($_SERVER['argv']); --EXPECTF-- ##teamcity[testCount count='2' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\TestRunnerStopping\WarningTest' locationHint='%sWarningTest.php::\PHPUnit\TestFixture\TestRunnerStopping\WarningTest' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='%sWarningTest.php::\PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testOne' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%d' flowId='%d'] - ##teamcity[testStarted name='testTwo' locationHint='%sWarningTest.php::\PHPUnit\TestFixture\TestRunnerStopping\WarningTest::testTwo' flowId='%d'] - ##teamcity[testFinished name='testTwo' duration='%d' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\TestRunnerStopping\WarningTest' flowId='%d'] diff --git a/tests/end-to-end/logging/teamcity-warning.phpt b/tests/end-to-end/logging/teamcity/teamcity-warning.phpt similarity index 92% rename from tests/end-to-end/logging/teamcity-warning.phpt rename to tests/end-to-end/logging/teamcity/teamcity-warning.phpt index 3d683df210d..4c00bb70f9b 100644 --- a/tests/end-to-end/logging/teamcity-warning.phpt +++ b/tests/end-to-end/logging/teamcity/teamcity-warning.phpt @@ -4,10 +4,10 @@ phpunit --teamcity ../../basic/unit/StatusTest.php run($_SERVER['argv']); --EXPECTF-- @@ -16,23 +16,14 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s Configuration: %s - ##teamcity[testCount count='1' flowId='%d'] - ##teamcity[testSuiteStarted name='%s%etests%eend-to-end%elogging%e_files%eteamcity-warning%ephpunit.xml' flowId='%d'] - ##teamcity[testSuiteStarted name='default' flowId='%d'] - ##teamcity[testSuiteStarted name='PHPUnit\TestFixture\Test' locationHint='php_qn://%s%eteamcity-warning%etests%eTest.php::\PHPUnit\TestFixture\Test' flowId='%d'] - ##teamcity[testStarted name='testOne' locationHint='php_qn://%s%eteamcity-warning%etests%eTest.php::\PHPUnit\TestFixture\Test::testOne' flowId='%d'] - ##teamcity[testFinished name='testOne' duration='%s' flowId='%d'] - ##teamcity[testSuiteFinished name='PHPUnit\TestFixture\Test' flowId='%d'] - ##teamcity[testSuiteFinished name='default' flowId='%d'] - ##teamcity[testSuiteFinished name='%s%etests%eend-to-end%elogging%e_files%eteamcity-warning%ephpunit.xml' flowId='%d'] Time: %s, Memory: %s diff --git a/tests/end-to-end/logging/testdox-print-deprecation.phpt b/tests/end-to-end/logging/testdox-print-deprecation.phpt deleted file mode 100644 index 753a390b031..00000000000 --- a/tests/end-to-end/logging/testdox-print-deprecation.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Testdox: print deprecation message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Deprecation (PHPUnit\TestFixture\TestRunnerStopping\Deprecation) - [x] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-error.phpt b/tests/end-to-end/logging/testdox-print-error.phpt deleted file mode 100644 index d70369c5ee4..00000000000 --- a/tests/end-to-end/logging/testdox-print-error.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Testdox: print error message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Error (PHPUnit\TestFixture\TestRunnerStopping\Error) - [ ] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-failure.phpt b/tests/end-to-end/logging/testdox-print-failure.phpt deleted file mode 100644 index 23abef31899..00000000000 --- a/tests/end-to-end/logging/testdox-print-failure.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -Testdox: print failure message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Failure (PHPUnit\TestFixture\TestRunnerStopping\Failure) - [ ] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-incomplete.phpt b/tests/end-to-end/logging/testdox-print-incomplete.phpt deleted file mode 100644 index e5527ed16cd..00000000000 --- a/tests/end-to-end/logging/testdox-print-incomplete.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Testdox: print incomplete message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Incomplete (PHPUnit\TestFixture\TestRunnerStopping\Incomplete) - [ ] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-notice.phpt b/tests/end-to-end/logging/testdox-print-notice.phpt deleted file mode 100644 index 2127090d854..00000000000 --- a/tests/end-to-end/logging/testdox-print-notice.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Testdox: print notice message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Notice (PHPUnit\TestFixture\TestRunnerStopping\Notice) - [x] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-risky.phpt b/tests/end-to-end/logging/testdox-print-risky.phpt deleted file mode 100644 index e0d3021db2a..00000000000 --- a/tests/end-to-end/logging/testdox-print-risky.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -Testdox: print risky message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Risky (PHPUnit\TestFixture\TestRunnerStopping\Risky) - [ ] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-skipped.phpt b/tests/end-to-end/logging/testdox-print-skipped.phpt deleted file mode 100644 index d53074bfe1b..00000000000 --- a/tests/end-to-end/logging/testdox-print-skipped.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Testdox: print skipped message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Skipped (PHPUnit\TestFixture\TestRunnerStopping\Skipped) - [ ] One - [x] Two diff --git a/tests/end-to-end/logging/testdox-print-warning.phpt b/tests/end-to-end/logging/testdox-print-warning.phpt deleted file mode 100644 index 2c6db08148b..00000000000 --- a/tests/end-to-end/logging/testdox-print-warning.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Testdox: print warning message ---FILE-- -run($_SERVER['argv']); ---EXPECTF-- -Warning (PHPUnit\TestFixture\TestRunnerStopping\Warning) - [x] One - [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-case-with-dollar-sign.phpt b/tests/end-to-end/logging/testdox/testdox-case-with-dollar-sign.phpt new file mode 100644 index 00000000000..16f28d0da1d --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-case-with-dollar-sign.phpt @@ -0,0 +1,28 @@ +--TEST-- +Testdox: output containing dollar signs in the value from data provider +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.... 4 / 4 (100%) + +Time: %s, Memory: %s + +Case With Dollar Sign (PHPUnit\TestFixture\CaseWithDollarSign) + ✔ The "$12.34" is used for this test + ✔ The "Some text before the price $5.67" is used for this test + ✔ The "Dollar sign followed by letter $Q" is used for this test + ✔ The "Alone $ surrounded by spaces" is used for this test + +OK (4 tests, 4 assertions) diff --git a/tests/end-to-end/logging/testdox/testdox-html-invalid-path.phpt b/tests/end-to-end/logging/testdox/testdox-html-invalid-path.phpt new file mode 100644 index 00000000000..61cbef595de --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-html-invalid-path.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --testdox-html is used with an invalid target path +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in TestDox HTML format to "": Directory "" does not exist and could not be created + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/testdox/testdox-html-invalid-socket.phpt b/tests/end-to-end/logging/testdox/testdox-html-invalid-socket.phpt new file mode 100644 index 00000000000..597bbb6ac9a --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-html-invalid-socket.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --testdox-html is used with an invalid socket +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in TestDox HTML format to "socket://hostname:port:wrong": "socket://hostname:port:wrong" does not match "socket://hostname:port" format + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/testdox-html.phpt b/tests/end-to-end/logging/testdox/testdox-html.phpt similarity index 94% rename from tests/end-to-end/logging/testdox-html.phpt rename to tests/end-to-end/logging/testdox/testdox-html.phpt index 0dbdfa582d5..987277e2c07 100644 --- a/tests/end-to-end/logging/testdox-html.phpt +++ b/tests/end-to-end/logging/testdox/testdox-html.phpt @@ -9,9 +9,9 @@ $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--no-output'; $_SERVER['argv'][] = '--testdox-html'; $_SERVER['argv'][] = $output; -$_SERVER['argv'][] = \realpath(__DIR__ . '/../../_files/BankAccountTest.php'); +$_SERVER['argv'][] = \realpath(__DIR__ . '/../../../_files/BankAccountTest.php'); -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); diff --git a/tests/end-to-end/logging/testdox/testdox-print-deprecation.phpt b/tests/end-to-end/logging/testdox/testdox-print-deprecation.phpt new file mode 100644 index 00000000000..fb6c33b5e1c --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-deprecation.phpt @@ -0,0 +1,20 @@ +--TEST-- +Testdox: print deprecation message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Deprecation (PHPUnit\TestFixture\TestRunnerStopping\Deprecation) + [x] One + [x] Two + [x] Three diff --git a/tests/end-to-end/logging/testdox/testdox-print-error.phpt b/tests/end-to-end/logging/testdox/testdox-print-error.phpt new file mode 100644 index 00000000000..9e1036a7b4e --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-error.phpt @@ -0,0 +1,19 @@ +--TEST-- +Testdox: print error message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Error (PHPUnit\TestFixture\TestRunnerStopping\Error) + [ ] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-print-failure.phpt b/tests/end-to-end/logging/testdox/testdox-print-failure.phpt new file mode 100644 index 00000000000..ae968326d3e --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-failure.phpt @@ -0,0 +1,18 @@ +--TEST-- +Testdox: print failure message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Failure (PHPUnit\TestFixture\TestRunnerStopping\Failure) + [ ] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-print-incomplete.phpt b/tests/end-to-end/logging/testdox/testdox-print-incomplete.phpt new file mode 100644 index 00000000000..bd74a312e32 --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-incomplete.phpt @@ -0,0 +1,19 @@ +--TEST-- +Testdox: print incomplete message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Incomplete (PHPUnit\TestFixture\TestRunnerStopping\Incomplete) + [ ] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-print-notice.phpt b/tests/end-to-end/logging/testdox/testdox-print-notice.phpt new file mode 100644 index 00000000000..e1e2f4c3c96 --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-notice.phpt @@ -0,0 +1,19 @@ +--TEST-- +Testdox: print notice message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Notice (PHPUnit\TestFixture\TestRunnerStopping\Notice) + [x] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-print-risky.phpt b/tests/end-to-end/logging/testdox/testdox-print-risky.phpt new file mode 100644 index 00000000000..d9e76790a43 --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-risky.phpt @@ -0,0 +1,18 @@ +--TEST-- +Testdox: print risky message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Risky (PHPUnit\TestFixture\TestRunnerStopping\Risky) + [x] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-print-skipped.phpt b/tests/end-to-end/logging/testdox/testdox-print-skipped.phpt new file mode 100644 index 00000000000..84d7bcf22b5 --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-skipped.phpt @@ -0,0 +1,19 @@ +--TEST-- +Testdox: print skipped message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Skipped (PHPUnit\TestFixture\TestRunnerStopping\Skipped) + [ ] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-print-warning.phpt b/tests/end-to-end/logging/testdox/testdox-print-warning.phpt new file mode 100644 index 00000000000..c2b74136aa9 --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-print-warning.phpt @@ -0,0 +1,19 @@ +--TEST-- +Testdox: print warning message +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +Warning (PHPUnit\TestFixture\TestRunnerStopping\Warning) + [x] One + [x] Two diff --git a/tests/end-to-end/logging/testdox/testdox-text-invalid-path.phpt b/tests/end-to-end/logging/testdox/testdox-text-invalid-path.phpt new file mode 100644 index 00000000000..0a957628d8a --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-text-invalid-path.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --testdox-text is used with an invalid target path +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in TestDox plain text format to "": Directory "" does not exist and could not be created + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/testdox/testdox-text-invalid-socket.phpt b/tests/end-to-end/logging/testdox/testdox-text-invalid-socket.phpt new file mode 100644 index 00000000000..15dd577c278 --- /dev/null +++ b/tests/end-to-end/logging/testdox/testdox-text-invalid-socket.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test runner emits warning when --testdox-text is used with an invalid socket +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 PHPUnit test runner warning: + +1) Cannot log test results in TestDox plain text format to "socket://hostname:port:wrong": "socket://hostname:port:wrong" does not match "socket://hostname:port" format + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/logging/testdox-text.phpt b/tests/end-to-end/logging/testdox/testdox-text.phpt similarity index 81% rename from tests/end-to-end/logging/testdox-text.phpt rename to tests/end-to-end/logging/testdox/testdox-text.phpt index 105276ce13c..cadd880e23a 100644 --- a/tests/end-to-end/logging/testdox-text.phpt +++ b/tests/end-to-end/logging/testdox/testdox-text.phpt @@ -9,9 +9,9 @@ $_SERVER['argv'][] = '--no-configuration'; $_SERVER['argv'][] = '--no-output'; $_SERVER['argv'][] = '--testdox-text'; $_SERVER['argv'][] = $output; -$_SERVER['argv'][] = \realpath(__DIR__ . '/../../_files/BankAccountTest.php'); +$_SERVER['argv'][] = \realpath(__DIR__ . '/../../../_files/BankAccountTest.php'); -require_once __DIR__ . '/../../bootstrap.php'; +require_once __DIR__ . '/../../../bootstrap.php'; (new PHPUnit\TextUI\Application)->run($_SERVER['argv']); diff --git a/tests/end-to-end/metadata/before-test-method-configured-with-attribute.phpt b/tests/end-to-end/metadata/before-test-method-configured-with-attribute.phpt new file mode 100644 index 00000000000..c16340e0e40 --- /dev/null +++ b/tests/end-to-end/metadata/before-test-method-configured-with-attribute.phpt @@ -0,0 +1,32 @@ +--TEST-- +The right events are emitted in the right order for a successful test that has a before-test method that is configured with attribute +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest, 1 test) +Test Preparation Started (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest::testOne) +Before Test Method Called (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest::beforeMethod) +Before Test Method Finished: +- PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest::beforeMethod +Test Prepared (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest::testOne) +Test Passed (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest::testOne) +Test Finished (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest::testOne) +Test Suite Finished (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithAttributeTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/metadata/before-test-method-configured-with-prioritized-attribute.phpt b/tests/end-to-end/metadata/before-test-method-configured-with-prioritized-attribute.phpt new file mode 100644 index 00000000000..81d37999619 --- /dev/null +++ b/tests/end-to-end/metadata/before-test-method-configured-with-prioritized-attribute.phpt @@ -0,0 +1,36 @@ +--TEST-- +The right events are emitted in the right order for a successful test that has a before-test method that is configured with attribute +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest, 1 test) +Test Preparation Started (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::testOne) +Before Test Method Called (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::beforeMethodWithHighPriority) +Before Test Method Called (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::setUp) +Before Test Method Called (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::beforeMethodWithLowPriority) +Before Test Method Finished: +- PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::beforeMethodWithHighPriority +- PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::setUp +- PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::beforeMethodWithLowPriority +Test Prepared (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::testOne) +Test Passed (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::testOne) +Test Finished (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest::testOne) +Test Suite Finished (PHPUnit\DeprecatedAnnotationsTestFixture\BeforeTestMethodWithPrioritizedAttributeTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/metadata/hook-methods-order.phpt b/tests/end-to-end/metadata/hook-methods-order.phpt new file mode 100644 index 00000000000..dd5ba156680 --- /dev/null +++ b/tests/end-to-end/metadata/hook-methods-order.phpt @@ -0,0 +1,58 @@ +--TEST-- +The right events are emitted in the right order for a successful test that has a before-test method that is configured with annotation +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Bootstrap Finished (%sHookMethodsOrderTestCase.php) +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\HookMethodsOrderTest, 1 test) +Test Preparation Started (PHPUnit\TestFixture\HookMethodsOrderTest::testOne) +Before Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::beforeWithPriorityInParent) +Before Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::beforeWithPriority) +Before Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::beforeInParent) +Before Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::beforeFirst) +Before Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::beforeSecond) +Before Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::setUp) +Before Test Method Finished: +- PHPUnit\TestFixture\HookMethodsOrderTest::beforeWithPriorityInParent +- PHPUnit\TestFixture\HookMethodsOrderTest::beforeWithPriority +- PHPUnit\TestFixture\HookMethodsOrderTest::beforeInParent +- PHPUnit\TestFixture\HookMethodsOrderTest::beforeFirst +- PHPUnit\TestFixture\HookMethodsOrderTest::beforeSecond +- PHPUnit\TestFixture\HookMethodsOrderTest::setUp +Test Prepared (PHPUnit\TestFixture\HookMethodsOrderTest::testOne) +After Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::afterWithPriority) +After Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::afterWithPriorityInParent) +After Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::tearDown) +After Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::afterFirst) +After Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::afterSecond) +After Test Method Called (PHPUnit\TestFixture\HookMethodsOrderTest::afterInParent) +After Test Method Finished: +- PHPUnit\TestFixture\HookMethodsOrderTest::afterWithPriority +- PHPUnit\TestFixture\HookMethodsOrderTest::afterWithPriorityInParent +- PHPUnit\TestFixture\HookMethodsOrderTest::tearDown +- PHPUnit\TestFixture\HookMethodsOrderTest::afterFirst +- PHPUnit\TestFixture\HookMethodsOrderTest::afterSecond +- PHPUnit\TestFixture\HookMethodsOrderTest::afterInParent +Test Passed (PHPUnit\TestFixture\HookMethodsOrderTest::testOne) +Test Finished (PHPUnit\TestFixture\HookMethodsOrderTest::testOne) +Test Suite Finished (PHPUnit\TestFixture\HookMethodsOrderTest, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/metadata/requires-environment-variable.phpt b/tests/end-to-end/metadata/requires-environment-variable.phpt new file mode 100644 index 00000000000..50d88ab98df --- /dev/null +++ b/tests/end-to-end/metadata/requires-environment-variable.phpt @@ -0,0 +1,36 @@ +--TEST-- +Tests are correctly ran based on environment variables requirements +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +SSS.... 7 / 7 (100%) + +Time: %s, Memory: %s + +There were 3 skipped tests: + +1) PHPUnit\TestFixture\requires_environment_variable\SomeTest::testShouldNotRunFOOHasWrongValue +Environment variable "FOO" is required to be "bar". + +2) PHPUnit\TestFixture\requires_environment_variable\SomeTest::testShouldNotRunBARIsEmpty +Environment variable "BAR" is required. + +3) PHPUnit\TestFixture\requires_environment_variable\SomeTest::testShouldNotRunBAZDoesNotExist +Environment variable "BAZ" is required. + +OK, but some tests were skipped! +Tests: 7, Assertions: 4, Skipped: 3. + diff --git a/tests/end-to-end/metadata/with-environment-variable.phpt b/tests/end-to-end/metadata/with-environment-variable.phpt new file mode 100644 index 00000000000..355d11ff44a --- /dev/null +++ b/tests/end-to-end/metadata/with-environment-variable.phpt @@ -0,0 +1,24 @@ +--TEST-- +Tests are correctly ran based on environment variables requirements +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +............... 15 / 15 (100%) + +Time: %s, Memory: %s + +OK (15 tests, 42 assertions) + diff --git a/tests/end-to-end/migration/_files/migration-from-100/phpunit-10.0.xml b/tests/end-to-end/migration/_files/migration-from-100/phpunit-10.0.xml index 9c00f88e05c..9967d0551ea 100644 --- a/tests/end-to-end/migration/_files/migration-from-100/phpunit-10.0.xml +++ b/tests/end-to-end/migration/_files/migration-from-100/phpunit-10.0.xml @@ -1,7 +1,8 @@ + bootstrap="src/Greeter.php" + registerMockObjectsFromTestArgumentsRecursively="true"> tests diff --git a/tests/end-to-end/migration/_files/migration-from-110/phpunit-11.0.xml b/tests/end-to-end/migration/_files/migration-from-110/phpunit-11.0.xml new file mode 100644 index 00000000000..684b3b9159d --- /dev/null +++ b/tests/end-to-end/migration/_files/migration-from-110/phpunit-11.0.xml @@ -0,0 +1,12 @@ + + + + + tests + + + + + diff --git a/tests/end-to-end/migration/_files/possibility-to-migrate-from-100-is-detected/tests/GreeterTest.php b/tests/end-to-end/migration/_files/possibility-to-migrate-from-100-is-detected/tests/GreeterTest.php index edd7e75aa99..65f3dfd1d9e 100644 --- a/tests/end-to-end/migration/_files/possibility-to-migrate-from-100-is-detected/tests/GreeterTest.php +++ b/tests/end-to-end/migration/_files/possibility-to-migrate-from-100-is-detected/tests/GreeterTest.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture\Migration; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -/** - * @covers \PHPUnit\TestFixture\Migration\Greeter - */ +#[CoversClass(Greeter::class)] final class GreeterTest extends TestCase { public function testGreets(): void diff --git a/tests/end-to-end/migration/_files/possibility-to-migrate-from-85-is-detected/tests/GreeterTest.php b/tests/end-to-end/migration/_files/possibility-to-migrate-from-85-is-detected/tests/GreeterTest.php index edd7e75aa99..65f3dfd1d9e 100644 --- a/tests/end-to-end/migration/_files/possibility-to-migrate-from-85-is-detected/tests/GreeterTest.php +++ b/tests/end-to-end/migration/_files/possibility-to-migrate-from-85-is-detected/tests/GreeterTest.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture\Migration; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -/** - * @covers \PHPUnit\TestFixture\Migration\Greeter - */ +#[CoversClass(Greeter::class)] final class GreeterTest extends TestCase { public function testGreets(): void diff --git a/tests/end-to-end/migration/_files/possibility-to-migrate-from-92-is-detected/tests/GreeterTest.php b/tests/end-to-end/migration/_files/possibility-to-migrate-from-92-is-detected/tests/GreeterTest.php index edd7e75aa99..65f3dfd1d9e 100644 --- a/tests/end-to-end/migration/_files/possibility-to-migrate-from-92-is-detected/tests/GreeterTest.php +++ b/tests/end-to-end/migration/_files/possibility-to-migrate-from-92-is-detected/tests/GreeterTest.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture\Migration; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -/** - * @covers \PHPUnit\TestFixture\Migration\Greeter - */ +#[CoversClass(Greeter::class)] final class GreeterTest extends TestCase { public function testGreets(): void diff --git a/tests/end-to-end/migration/_files/possibility-to-migrate-from-95-is-detected/tests/GreeterTest.php b/tests/end-to-end/migration/_files/possibility-to-migrate-from-95-is-detected/tests/GreeterTest.php index edd7e75aa99..65f3dfd1d9e 100644 --- a/tests/end-to-end/migration/_files/possibility-to-migrate-from-95-is-detected/tests/GreeterTest.php +++ b/tests/end-to-end/migration/_files/possibility-to-migrate-from-95-is-detected/tests/GreeterTest.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture\Migration; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -/** - * @covers \PHPUnit\TestFixture\Migration\Greeter - */ +#[CoversClass(Greeter::class)] final class GreeterTest extends TestCase { public function testGreets(): void diff --git a/tests/end-to-end/migration/_files/unsupported-schema/phpunit.xml b/tests/end-to-end/migration/_files/unsupported-schema/phpunit.xml new file mode 100644 index 00000000000..b24baa02e01 --- /dev/null +++ b/tests/end-to-end/migration/_files/unsupported-schema/phpunit.xml @@ -0,0 +1,6 @@ + + + bar + diff --git a/tests/end-to-end/migration/migration-from-110.phpt b/tests/end-to-end/migration/migration-from-110.phpt new file mode 100644 index 00000000000..749d9ff04eb --- /dev/null +++ b/tests/end-to-end/migration/migration-from-110.phpt @@ -0,0 +1,22 @@ +--TEST-- +Configuration migration from PHPUnit 11.0 format works +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Created backup: %sphpunit.xml.bak +Migrated configuration: %sphpunit.xml +--CLEAN-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Migration of %s failed: +The file does not validate against any known schema +--CLEAN-- +generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function speak() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -77,9 +86,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'speak', $__phpunit_arguments, '', $this, true + 'Foo', 'speak', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/3154_namespaced_constant_resolving.phpt b/tests/end-to-end/mock-objects/generator/3154_namespaced_constant_resolving.phpt index ff103fdd4b4..b965f871e20 100644 --- a/tests/end-to-end/mock-objects/generator/3154_namespaced_constant_resolving.phpt +++ b/tests/end-to-end/mock-objects/generator/3154_namespaced_constant_resolving.phpt @@ -28,13 +28,10 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Issue3154::class, - true, - true, - [], - 'Issue3154Mock', - true, - true + type: Issue3154::class, + mockObject: true, + methods: [], + mockClassName: 'Issue3154Mock', ); print $mock->classCode(); @@ -45,16 +42,28 @@ class Issue3154Mock extends Is\Namespaced\Issue3154 implements PHPUnit\Framework { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function a(int $i = %d, int $j = 17, string $v = '%s', string $z = '#'): string { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$i, $j, $v, $z]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 4) { + if (4 !== null && $__phpunit_count > 4) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 4; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -62,9 +71,11 @@ class Issue3154Mock extends Is\Namespaced\Issue3154 implements PHPUnit\Framework } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Is\Namespaced\Issue3154', 'a', $__phpunit_arguments, 'string', $this, true + 'Is\Namespaced\Issue3154', 'a', $__phpunit_arguments, 'string', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/3530.phpt b/tests/end-to-end/mock-objects/generator/3530.phpt deleted file mode 100644 index 686a0f7eb85..00000000000 --- a/tests/end-to-end/mock-objects/generator/3530.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generateClassFromWsdl('3530.wsdl', 'Test') ---SKIPIF-- -generateClassFromWsdl( - __DIR__ . '/../../../_files/3530.wsdl', - 'Test' -); ---EXPECTF-- -declare(strict_types=1); - -class Test extends \SoapClient -{ - public function __construct($wsdl, array $options) - { - parent::__construct('%s/3530.wsdl', $options); - } - - public function Contact_Information($Contact_Id) - { - } -} diff --git a/tests/end-to-end/mock-objects/generator/3967.phpt b/tests/end-to-end/mock-objects/generator/3967.phpt index 47b2998b9aa..e7fb931a981 100644 --- a/tests/end-to-end/mock-objects/generator/3967.phpt +++ b/tests/end-to-end/mock-objects/generator/3967.phpt @@ -16,13 +16,10 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Baz', - true, - true, - [], - 'MockBaz', - true, - true + type: 'Baz', + mockObject: true, + methods: [], + mockClassName: 'MockBaz', ); print $mock->classCode(); @@ -33,16 +30,28 @@ class MockBaz extends Exception implements Baz, PHPUnit\Framework\MockObject\Moc { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\ProxiedCloneMethod; public function foo(): string { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -50,9 +59,11 @@ class MockBaz extends Exception implements Baz, PHPUnit\Framework\MockObject\Moc } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Bar', 'foo', $__phpunit_arguments, 'string', $this, true + 'Bar', 'foo', $__phpunit_arguments, 'string', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/397.phpt b/tests/end-to-end/mock-objects/generator/397.phpt index 07144fef04c..321fcc7ceaa 100644 --- a/tests/end-to-end/mock-objects/generator/397.phpt +++ b/tests/end-to-end/mock-objects/generator/397.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - C::class, - true, - true, - [], - 'MockC', - true, - true + type: C::class, + mockObject: true, + methods: [], + mockClassName: 'MockC', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockC extends C implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function m(?C $other): C { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$other]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockC extends C implements PHPUnit\Framework\MockObject\MockObjectInternal } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'C', 'm', $__phpunit_arguments, 'C', $this, true + 'C', 'm', $__phpunit_arguments, 'C', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/4139.phpt b/tests/end-to-end/mock-objects/generator/4139.phpt index 067c0e2f921..50309c50b35 100644 --- a/tests/end-to-end/mock-objects/generator/4139.phpt +++ b/tests/end-to-end/mock-objects/generator/4139.phpt @@ -10,7 +10,10 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; -$mock = $generator->generate(InterfaceWithConstructor::class, true, true); +$mock = $generator->generate( + type: InterfaceWithConstructor::class, + mockObject: true, +); print $mock->classCode(); --EXPECTF-- @@ -20,16 +23,28 @@ class %s implements PHPUnit\Framework\MockObject\MockObjectInternal, InterfaceWi { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function __construct() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -37,9 +52,11 @@ class %s implements PHPUnit\Framework\MockObject\MockObjectInternal, InterfaceWi } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'InterfaceWithConstructor', '__construct', $__phpunit_arguments, '', $this, true + 'InterfaceWithConstructor', '__construct', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/abstract_class.phpt b/tests/end-to-end/mock-objects/generator/abstract_class.phpt index d816760f8d3..de69f4bee64 100644 --- a/tests/end-to-end/mock-objects/generator/abstract_class.phpt +++ b/tests/end-to-end/mock-objects/generator/abstract_class.phpt @@ -18,33 +18,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function one() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -52,9 +61,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'one', $__phpunit_arguments, '', $this, true + 'Foo', 'one', $__phpunit_arguments, '', $this ) ); @@ -63,10 +74,23 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte public function two() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -74,9 +98,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'two', $__phpunit_arguments, '', $this, true + 'Foo', 'two', $__phpunit_arguments, '', $this ) ); @@ -85,10 +111,23 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte protected function three() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -96,9 +135,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'three', $__phpunit_arguments, '', $this, true + 'Foo', 'three', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class.phpt b/tests/end-to-end/mock-objects/generator/class.phpt index 900ce27886e..77470588c31 100644 --- a/tests/end-to-end/mock-objects/generator/class.phpt +++ b/tests/end-to-end/mock-objects/generator/class.phpt @@ -18,33 +18,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -52,9 +61,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); @@ -63,10 +74,23 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte public function baz(Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -74,9 +98,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'baz', $__phpunit_arguments, '', $this, true + 'Foo', 'baz', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class_call_parent_clone.phpt b/tests/end-to-end/mock-objects/generator/class_call_parent_clone.phpt index 21c6e32bf48..a4fdcab1835 100644 --- a/tests/end-to-end/mock-objects/generator/class_call_parent_clone.phpt +++ b/tests/end-to-end/mock-objects/generator/class_call_parent_clone.phpt @@ -4,7 +4,7 @@ generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\ProxiedCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/class_call_parent_constructor.phpt index 1b9a017a478..1558555b381 100644 --- a/tests/end-to-end/mock-objects/generator/class_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/class_call_parent_constructor.phpt @@ -14,23 +14,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_dont_call_parent_clone.phpt b/tests/end-to-end/mock-objects/generator/class_dont_call_parent_clone.phpt index f42a165b7db..805288c37eb 100644 --- a/tests/end-to-end/mock-objects/generator/class_dont_call_parent_clone.phpt +++ b/tests/end-to-end/mock-objects/generator/class_dont_call_parent_clone.phpt @@ -14,23 +14,21 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - false + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', + callOriginalClone: false ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_dont_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/class_dont_call_parent_constructor.phpt index 1b9a017a478..1558555b381 100644 --- a/tests/end-to-end/mock-objects/generator/class_dont_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/class_dont_call_parent_constructor.phpt @@ -14,23 +14,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_implementing_interface_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/class_implementing_interface_call_parent_constructor.phpt index 8ec3ad96886..6c1ff3accd8 100644 --- a/tests/end-to-end/mock-objects/generator/class_implementing_interface_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/class_implementing_interface_call_parent_constructor.phpt @@ -19,23 +19,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_implementing_interface_dont_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/class_implementing_interface_dont_call_parent_constructor.phpt index 8ec3ad96886..6c1ff3accd8 100644 --- a/tests/end-to-end/mock-objects/generator/class_implementing_interface_dont_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/class_implementing_interface_dont_call_parent_constructor.phpt @@ -19,23 +19,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_nonexistent_method.phpt b/tests/end-to-end/mock-objects/generator/class_nonexistent_method.phpt index 4f80ec31699..8d5df73f928 100644 --- a/tests/end-to-end/mock-objects/generator/class_nonexistent_method.phpt +++ b/tests/end-to-end/mock-objects/generator/class_nonexistent_method.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - ['bar'], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: ['bar'], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class_partial.phpt b/tests/end-to-end/mock-objects/generator/class_partial.phpt index 8a029962076..2fa52500c34 100644 --- a/tests/end-to-end/mock-objects/generator/class_partial.phpt +++ b/tests/end-to-end/mock-objects/generator/class_partial.phpt @@ -18,33 +18,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - ['bar'], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: ['bar'], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -52,9 +61,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class_with_deprecated_method.phpt b/tests/end-to-end/mock-objects/generator/class_with_deprecated_method.phpt index bd21e5292cd..3506ad581f4 100644 --- a/tests/end-to-end/mock-objects/generator/class_with_deprecated_method.phpt +++ b/tests/end-to-end/mock-objects/generator/class_with_deprecated_method.phpt @@ -18,24 +18,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'ClassWithDeprecatedMethod', - true, - true, - [], - 'MockFoo', - TRUE, - TRUE + type: 'ClassWithDeprecatedMethod', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends ClassWithDeprecatedMethod implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; @@ -43,10 +39,23 @@ class MockFoo extends ClassWithDeprecatedMethod implements PHPUnit\Framework\Moc { @trigger_error('The ClassWithDeprecatedMethod::deprecatedMethod method is deprecated (this method is deprecated).', E_USER_DEPRECATED); + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -54,9 +63,11 @@ class MockFoo extends ClassWithDeprecatedMethod implements PHPUnit\Framework\Moc } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithDeprecatedMethod', 'deprecatedMethod', $__phpunit_arguments, '', $this, true + 'ClassWithDeprecatedMethod', 'deprecatedMethod', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class_with_final_method.phpt b/tests/end-to-end/mock-objects/generator/class_with_final_method.phpt index ed0763c0258..d68911c1451 100644 --- a/tests/end-to-end/mock-objects/generator/class_with_final_method.phpt +++ b/tests/end-to-end/mock-objects/generator/class_with_final_method.phpt @@ -14,24 +14,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'ClassWithFinalMethod', - true, - true, - [], - 'MockFoo', - true, - true + type: 'ClassWithFinalMethod', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends ClassWithFinalMethod implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/class_with_method_named_method.phpt b/tests/end-to-end/mock-objects/generator/class_with_method_named_method.phpt deleted file mode 100644 index 00214983b84..00000000000 --- a/tests/end-to-end/mock-objects/generator/class_with_method_named_method.phpt +++ /dev/null @@ -1,58 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generate('Foo', [], 'MockFoo', true, true) ---FILE-- -generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true -); - -print $mock->classCode(); ---EXPECTF-- -declare(strict_types=1); - -class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal -{ - use PHPUnit\Framework\MockObject\StubApi; - use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; - use PHPUnit\Framework\MockObject\DoubledCloneMethod; - - public function method() - { - $__phpunit_arguments = []; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 0) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'method', $__phpunit_arguments, '', $this, true - ) - ); - - return $__phpunit_result; - } -} diff --git a/tests/end-to-end/mock-objects/generator/class_with_method_with_nullable_typehinted_variadic_arguments.phpt b/tests/end-to-end/mock-objects/generator/class_with_method_with_nullable_typehinted_variadic_arguments.phpt index b36dce16adb..67947213d74 100644 --- a/tests/end-to-end/mock-objects/generator/class_with_method_with_nullable_typehinted_variadic_arguments.phpt +++ b/tests/end-to-end/mock-objects/generator/class_with_method_with_nullable_typehinted_variadic_arguments.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'ClassWithMethodWithNullableTypehintedVariadicArguments', - true, - true, - [], - 'MockFoo', - true, - true + type: 'ClassWithMethodWithNullableTypehintedVariadicArguments', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends ClassWithMethodWithNullableTypehintedVariadicArguments implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function methodWithNullableTypehintedVariadicArguments($a, ?string ...$parameters) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$a]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends ClassWithMethodWithNullableTypehintedVariadicArguments imp } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithMethodWithNullableTypehintedVariadicArguments', 'methodWithNullableTypehintedVariadicArguments', $__phpunit_arguments, '', $this, true + 'ClassWithMethodWithNullableTypehintedVariadicArguments', 'methodWithNullableTypehintedVariadicArguments', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class_with_method_with_typehinted_variadic_arguments.phpt b/tests/end-to-end/mock-objects/generator/class_with_method_with_typehinted_variadic_arguments.phpt index f8751f1bc69..9c264fb08df 100644 --- a/tests/end-to-end/mock-objects/generator/class_with_method_with_typehinted_variadic_arguments.phpt +++ b/tests/end-to-end/mock-objects/generator/class_with_method_with_typehinted_variadic_arguments.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'ClassWithMethodWithTypehintedVariadicArguments', - true, - true, - [], - 'MockFoo', - true, - true + type: 'ClassWithMethodWithTypehintedVariadicArguments', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends ClassWithMethodWithTypehintedVariadicArguments implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function methodWithTypehintedVariadicArguments($a, string ...$parameters) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$a]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends ClassWithMethodWithTypehintedVariadicArguments implements } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithMethodWithTypehintedVariadicArguments', 'methodWithTypehintedVariadicArguments', $__phpunit_arguments, '', $this, true + 'ClassWithMethodWithTypehintedVariadicArguments', 'methodWithTypehintedVariadicArguments', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/class_with_method_with_variadic_arguments.phpt b/tests/end-to-end/mock-objects/generator/class_with_method_with_variadic_arguments.phpt index df7c6954ef0..1f74bafda4b 100644 --- a/tests/end-to-end/mock-objects/generator/class_with_method_with_variadic_arguments.phpt +++ b/tests/end-to-end/mock-objects/generator/class_with_method_with_variadic_arguments.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'ClassWithMethodWithVariadicArguments', - true, - true, - [], - 'MockFoo', - true, - true + type: 'ClassWithMethodWithVariadicArguments', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends ClassWithMethodWithVariadicArguments implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function methodWithVariadicArguments($a, ...$parameters) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$a]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends ClassWithMethodWithVariadicArguments implements PHPUnit\Fr } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithMethodWithVariadicArguments', 'methodWithVariadicArguments', $__phpunit_arguments, '', $this, true + 'ClassWithMethodWithVariadicArguments', 'methodWithVariadicArguments', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/constant_as_parameter_default_value.phpt b/tests/end-to-end/mock-objects/generator/constant_as_parameter_default_value.phpt index 3b81d1ebed0..89a5b0079a1 100644 --- a/tests/end-to-end/mock-objects/generator/constant_as_parameter_default_value.phpt +++ b/tests/end-to-end/mock-objects/generator/constant_as_parameter_default_value.phpt @@ -14,13 +14,10 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); @@ -31,16 +28,28 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(int $baz = %d) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/extendable_class_with_final_property.phpt b/tests/end-to-end/mock-objects/generator/extendable_class_with_final_property.phpt new file mode 100644 index 00000000000..9a5ba4d1941 --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/extendable_class_with_final_property.phpt @@ -0,0 +1,39 @@ +--TEST-- +Extendable class with property with final property +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo extends Foo implements PHPUnit\Framework\MockObject\StubInternal +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; +} diff --git a/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_final_get_hook.phpt b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_final_get_hook.phpt new file mode 100644 index 00000000000..753c04237d4 --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_final_get_hook.phpt @@ -0,0 +1,39 @@ +--TEST-- +Extendable class with property with final get property hook +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo extends Foo implements PHPUnit\Framework\MockObject\StubInternal +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; +} diff --git a/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_get_and_set_hooks.phpt b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_get_and_set_hooks.phpt new file mode 100644 index 00000000000..18de0cdb2da --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_get_and_set_hooks.phpt @@ -0,0 +1,61 @@ +--TEST-- +Extendable class with property with non-final get and set property hooks +--SKIPIF-- +bar = $value; + } + } +} + +require_once __DIR__ . '/../../../bootstrap.php'; + +$generator = new \PHPUnit\Framework\MockObject\Generator\Generator; + +$testDoubleClass = $generator->generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo extends Foo implements PHPUnit\Framework\MockObject\StubInternal +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public string $bar { + get { + return $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::get', [], 'string', $this + ) + ); + } + + set (string $value) { + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::set', [$value], 'void', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_get_hook.phpt b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_get_hook.phpt new file mode 100644 index 00000000000..1108a49905d --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_get_hook.phpt @@ -0,0 +1,49 @@ +--TEST-- +Extendable class with property with non-final get property hook +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo extends Foo implements PHPUnit\Framework\MockObject\StubInternal +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public string $bar { + get { + return $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::get', [], 'string', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_set_hook.phpt b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_set_hook.phpt new file mode 100644 index 00000000000..df0c7ba0c60 --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/extendable_class_with_property_with_set_hook.phpt @@ -0,0 +1,49 @@ +--TEST-- +Extendable class with property with non-final set property hook +--SKIPIF-- +bar = $value; + } + } +} + +require_once __DIR__ . '/../../../bootstrap.php'; + +$generator = new \PHPUnit\Framework\MockObject\Generator\Generator; + +$testDoubleClass = $generator->generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo extends Foo implements PHPUnit\Framework\MockObject\StubInternal +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public string $bar { + set (string $value) { + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::set', [$value], 'void', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/interface.phpt b/tests/end-to-end/mock-objects/generator/interface.phpt index 78584fa6c15..540b502757c 100644 --- a/tests/end-to-end/mock-objects/generator/interface.phpt +++ b/tests/end-to-end/mock-objects/generator/interface.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/interface_with_nullable_property_with_get_hook.phpt b/tests/end-to-end/mock-objects/generator/interface_with_nullable_property_with_get_hook.phpt new file mode 100644 index 00000000000..5ee090dd1ba --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/interface_with_nullable_property_with_get_hook.phpt @@ -0,0 +1,45 @@ +--TEST-- +Interface with nullable property with get property hook +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo implements PHPUnit\Framework\MockObject\StubInternal, Foo +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public ?string $bar { + get { + return $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::get', [], '?string', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/interface_with_nullable_property_with_set_hook.phpt b/tests/end-to-end/mock-objects/generator/interface_with_nullable_property_with_set_hook.phpt new file mode 100644 index 00000000000..a0be2b822a2 --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/interface_with_nullable_property_with_set_hook.phpt @@ -0,0 +1,45 @@ +--TEST-- +Interface with nullable property with set property hook +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo implements PHPUnit\Framework\MockObject\StubInternal, Foo +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public ?string $bar { + set (?string $value) { + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::set', [$value], 'void', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/interface_with_property_with_get_and_set_hooks.phpt b/tests/end-to-end/mock-objects/generator/interface_with_property_with_get_and_set_hooks.phpt new file mode 100644 index 00000000000..cf61e81f91e --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/interface_with_property_with_get_and_set_hooks.phpt @@ -0,0 +1,53 @@ +--TEST-- +Interface with property with get and set property hooks +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo implements PHPUnit\Framework\MockObject\StubInternal, Foo +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public string $bar { + get { + return $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::get', [], 'string', $this + ) + ); + } + + set (string $value) { + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::set', [$value], 'void', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/interface_with_property_with_get_hook.phpt b/tests/end-to-end/mock-objects/generator/interface_with_property_with_get_hook.phpt new file mode 100644 index 00000000000..442393e34fe --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/interface_with_property_with_get_hook.phpt @@ -0,0 +1,45 @@ +--TEST-- +Interface with property with get property hook +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo implements PHPUnit\Framework\MockObject\StubInternal, Foo +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public string $bar { + get { + return $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::get', [], 'string', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/interface_with_property_with_set_hook.phpt b/tests/end-to-end/mock-objects/generator/interface_with_property_with_set_hook.phpt new file mode 100644 index 00000000000..5502369225b --- /dev/null +++ b/tests/end-to-end/mock-objects/generator/interface_with_property_with_set_hook.phpt @@ -0,0 +1,45 @@ +--TEST-- +Interface with property with set property hook +--SKIPIF-- +generate( + type: Foo::class, + mockObject: false, + methods: [], + mockClassName: 'TestStubFoo', +); + +print $testDoubleClass->classCode(); +--EXPECT-- +declare(strict_types=1); + +class TestStubFoo implements PHPUnit\Framework\MockObject\StubInternal, Foo +{ + use PHPUnit\Framework\MockObject\StubApi; + use PHPUnit\Framework\MockObject\Method; + use PHPUnit\Framework\MockObject\DoubledCloneMethod; + + public string $bar { + set (string $value) { + $this->__phpunit_getInvocationHandler()->invoke( + new \PHPUnit\Framework\MockObject\Invocation( + 'TestStubFoo', '$bar::set', [$value], 'void', $this + ) + ); + } + } +} diff --git a/tests/end-to-end/mock-objects/generator/invocation_object_clone_object.phpt b/tests/end-to-end/mock-objects/generator/invocation_object_clone_object.phpt deleted file mode 100644 index dc6f78c2c06..00000000000 --- a/tests/end-to-end/mock-objects/generator/invocation_object_clone_object.phpt +++ /dev/null @@ -1,86 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generate('Foo', [], 'MockFoo', true, true, true) ---FILE-- -generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true, - true -); - -print $mock->classCode(); ---EXPECTF-- -declare(strict_types=1); - -class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal -{ - use PHPUnit\Framework\MockObject\StubApi; - use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; - use PHPUnit\Framework\MockObject\Method; - use PHPUnit\Framework\MockObject\DoubledCloneMethod; - - public function bar(Foo $foo) - { - $__phpunit_arguments = [$foo]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 1) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true - ) - ); - - return $__phpunit_result; - } - - public function baz(Foo $foo) - { - $__phpunit_arguments = [$foo]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 1) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'baz', $__phpunit_arguments, '', $this, true - ) - ); - - return $__phpunit_result; - } -} diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class.phpt index 842a5c776af..75d013545ba 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class.phpt @@ -20,33 +20,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(NS\Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -54,9 +63,11 @@ class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectI } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'NS\Foo', 'bar', $__phpunit_arguments, '', $this, true + 'NS\Foo', 'bar', $__phpunit_arguments, '', $this ) ); @@ -65,10 +76,23 @@ class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectI public function baz(NS\Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -76,9 +100,11 @@ class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectI } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'NS\Foo', 'baz', $__phpunit_arguments, '', $this, true + 'NS\Foo', 'baz', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_clone.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_clone.phpt index 59286197333..ec75e561f28 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_clone.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_clone.phpt @@ -16,23 +16,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\ProxiedCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_constructor.phpt index 11ade685327..4af90495f11 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_call_parent_constructor.phpt @@ -16,23 +16,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_clone.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_clone.phpt index f503112da54..6fd9e032a9e 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_clone.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_clone.phpt @@ -16,23 +16,21 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - false + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', + callOriginalClone: false, ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_constructor.phpt index 11ade685327..4af90495f11 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_dont_call_parent_constructor.phpt @@ -16,23 +16,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_call_parent_constructor.phpt index 840f7733d34..322732cff74 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_call_parent_constructor.phpt @@ -21,23 +21,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt index 840f7733d34..322732cff74 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_implementing_interface_dont_call_parent_constructor.phpt @@ -21,23 +21,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; } diff --git a/tests/end-to-end/mock-objects/generator/namespaced_class_partial.phpt b/tests/end-to-end/mock-objects/generator/namespaced_class_partial.phpt index aa4e3fce052..503117b634e 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_class_partial.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_class_partial.phpt @@ -20,33 +20,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - ['bar'], - 'MockFoo', - true, - true + type: 'NS\Foo', + mockObject: true, + methods: ['bar'], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(NS\Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -54,9 +63,11 @@ class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectI } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'NS\Foo', 'bar', $__phpunit_arguments, '', $this, true + 'NS\Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/namespaced_interface.phpt b/tests/end-to-end/mock-objects/generator/namespaced_interface.phpt index 50f21a80e13..e204034f8c9 100644 --- a/tests/end-to-end/mock-objects/generator/namespaced_interface.phpt +++ b/tests/end-to-end/mock-objects/generator/namespaced_interface.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'NS\Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, NS\Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(NS\Foo $foo) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, NS\Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'NS\Foo', 'bar', $__phpunit_arguments, '', $this, true + 'NS\Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/nonexistent_class.phpt b/tests/end-to-end/mock-objects/generator/nonexistent_class.phpt deleted file mode 100644 index a674e6697b8..00000000000 --- a/tests/end-to-end/mock-objects/generator/nonexistent_class.phpt +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generate('NonExistentClass', [], 'MockFoo', true, true) ---FILE-- -generate( - 'NonExistentClass', - true, - true, - [], - 'MockFoo', - true, - true -); - -print $mock->classCode(); ---EXPECTF-- -declare(strict_types=1); - -class NonExistentClass -{ -} - -class MockFoo extends NonExistentClass implements PHPUnit\Framework\MockObject\MockObjectInternal -{ - use PHPUnit\Framework\MockObject\StubApi; - use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; - use PHPUnit\Framework\MockObject\Method; - use PHPUnit\Framework\MockObject\DoubledCloneMethod; -} diff --git a/tests/end-to-end/mock-objects/generator/nonexistent_class_with_namespace.phpt b/tests/end-to-end/mock-objects/generator/nonexistent_class_with_namespace.phpt deleted file mode 100644 index 4c981d9b713..00000000000 --- a/tests/end-to-end/mock-objects/generator/nonexistent_class_with_namespace.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generate('Foo', [], 'MockFoo', true, true) ---FILE-- -generate( - 'NS\Foo', - true, - true, - [], - 'MockFoo', - true, - true -); - -print $mock->classCode(); ---EXPECTF-- -declare(strict_types=1); - -namespace NS { - -class Foo -{ -} - -} - -namespace { - -class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal -{ - use PHPUnit\Framework\MockObject\StubApi; - use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; - use PHPUnit\Framework\MockObject\Method; - use PHPUnit\Framework\MockObject\DoubledCloneMethod; -} - -} diff --git a/tests/end-to-end/mock-objects/generator/nonexistent_class_with_namespace_starting_with_separator.phpt b/tests/end-to-end/mock-objects/generator/nonexistent_class_with_namespace_starting_with_separator.phpt deleted file mode 100644 index 0a31e9fc572..00000000000 --- a/tests/end-to-end/mock-objects/generator/nonexistent_class_with_namespace_starting_with_separator.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generate('Foo', [], 'MockFoo', true, true) ---FILE-- -generate( - '\NS\Foo', - true, - true, - [], - 'MockFoo', - true, - true -); - -print $mock->classCode(); ---EXPECTF-- -declare(strict_types=1); - -namespace NS { - -class Foo -{ -} - -} - -namespace { - -class MockFoo extends NS\Foo implements PHPUnit\Framework\MockObject\MockObjectInternal -{ - use PHPUnit\Framework\MockObject\StubApi; - use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; - use PHPUnit\Framework\MockObject\Method; - use PHPUnit\Framework\MockObject\DoubledCloneMethod; -} - -} diff --git a/tests/end-to-end/mock-objects/generator/nullable_types.phpt b/tests/end-to-end/mock-objects/generator/nullable_types.phpt index 64f50357037..168886b527c 100644 --- a/tests/end-to-end/mock-objects/generator/nullable_types.phpt +++ b/tests/end-to-end/mock-objects/generator/nullable_types.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(?int $x) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$x]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/nullable_union_type_parameter.phpt b/tests/end-to-end/mock-objects/generator/nullable_union_type_parameter.phpt index 24aeb38bf46..5ff35f55b17 100644 --- a/tests/end-to-end/mock-objects/generator/nullable_union_type_parameter.phpt +++ b/tests/end-to-end/mock-objects/generator/nullable_union_type_parameter.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(bool|int $baz, Foo|null|stdClass $other) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz, $other]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 2) { + if (2 !== null && $__phpunit_count > 2) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/nullable_union_type_return.phpt b/tests/end-to-end/mock-objects/generator/nullable_union_type_return.phpt index eaf5e2af433..35db70f87a5 100644 --- a/tests/end-to-end/mock-objects/generator/nullable_union_type_return.phpt +++ b/tests/end-to-end/mock-objects/generator/nullable_union_type_return.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): bool|int|null { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'bool|int|null', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'bool|int|null', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/parameter_dnf.phpt b/tests/end-to-end/mock-objects/generator/parameter_dnf.phpt index c3434356669..6d3c2aab50b 100644 --- a/tests/end-to-end/mock-objects/generator/parameter_dnf.phpt +++ b/tests/end-to-end/mock-objects/generator/parameter_dnf.phpt @@ -22,33 +22,42 @@ require_once __DIR__ . '/../../../../vendor/autoload.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar((A&B)|int|null $baz) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -56,9 +65,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/parameter_false.phpt b/tests/end-to-end/mock-objects/generator/parameter_false.phpt index e33e0ba13fe..356451aaeef 100644 --- a/tests/end-to-end/mock-objects/generator/parameter_false.phpt +++ b/tests/end-to-end/mock-objects/generator/parameter_false.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(false $baz): void { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'void', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'void', $this ) ); } diff --git a/tests/end-to-end/mock-objects/generator/parameter_intersection.phpt b/tests/end-to-end/mock-objects/generator/parameter_intersection.phpt index bd0655a1f66..e30514eff55 100644 --- a/tests/end-to-end/mock-objects/generator/parameter_intersection.phpt +++ b/tests/end-to-end/mock-objects/generator/parameter_intersection.phpt @@ -22,33 +22,42 @@ require_once __DIR__ . '/../../../../vendor/autoload.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(AnInterface&AnotherInterface $baz) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -56,9 +65,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/parameter_null.phpt b/tests/end-to-end/mock-objects/generator/parameter_null.phpt index 8bc21a8a582..8c091b99f06 100644 --- a/tests/end-to-end/mock-objects/generator/parameter_null.phpt +++ b/tests/end-to-end/mock-objects/generator/parameter_null.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(null $baz): void { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'void', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'void', $this ) ); } diff --git a/tests/end-to-end/mock-objects/generator/parameter_true.phpt b/tests/end-to-end/mock-objects/generator/parameter_true.phpt index 326d30e02d2..8f1f8b85c67 100644 --- a/tests/end-to-end/mock-objects/generator/parameter_true.phpt +++ b/tests/end-to-end/mock-objects/generator/parameter_true.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(true $baz): void { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'void', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'void', $this ) ); } diff --git a/tests/end-to-end/mock-objects/generator/parameter_union.phpt b/tests/end-to-end/mock-objects/generator/parameter_union.phpt index 261f6a89d55..2d1007bb48d 100644 --- a/tests/end-to-end/mock-objects/generator/parameter_union.phpt +++ b/tests/end-to-end/mock-objects/generator/parameter_union.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(bool|int $baz, Foo|stdClass $other) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz, $other]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 2) { + if (2 !== null && $__phpunit_count > 2) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/proxy.phpt b/tests/end-to-end/mock-objects/generator/proxy.phpt deleted file mode 100644 index 3b1d00864cd..00000000000 --- a/tests/end-to-end/mock-objects/generator/proxy.phpt +++ /dev/null @@ -1,83 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generate('Foo', null, 'ProxyFoo', true, true, true, true) ---FILE-- -generate( - 'Foo', true, true, [], 'ProxyFoo', true, true, true, true -); - -print $mock->classCode(); ---EXPECTF-- -declare(strict_types=1); - -class ProxyFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal -{ - use PHPUnit\Framework\MockObject\StubApi; - use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; - use PHPUnit\Framework\MockObject\Method; - use PHPUnit\Framework\MockObject\DoubledCloneMethod; - - public function bar(Foo $foo) - { - $__phpunit_arguments = [$foo]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 1) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "bar"], $__phpunit_arguments); - - return $__phpunit_result; - } - - public function baz(Foo $foo) - { - $__phpunit_arguments = [$foo]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 1) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'baz', $__phpunit_arguments, '', $this, true, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "baz"], $__phpunit_arguments); - - return $__phpunit_result; - } -} diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_closure.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_closure.phpt index e599204d9ae..70a39582f42 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_closure.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_closure.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): Closure { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'Closure', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'Closure', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_dnf.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_dnf.phpt index c14b85f22fe..c1b3e2cbac9 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_dnf.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_dnf.phpt @@ -22,33 +22,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): (A&B)|int|null { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -56,9 +65,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '(A&B)|int|null', $this, true + 'Foo', 'bar', $__phpunit_arguments, '(A&B)|int|null', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_false.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_false.phpt index 704326d9da2..4ee84e1b33e 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_false.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_false.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): false { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'false', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'false', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_final.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_final.phpt index 59d105ef3b6..40af8ddc812 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_final.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_final.phpt @@ -19,33 +19,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): FinalClass { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -53,9 +62,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'FinalClass', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'FinalClass', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_generator.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_generator.phpt index 11bd536613d..227d95a419e 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_generator.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_generator.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): Generator { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'Generator', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'Generator', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_generator_empty_by_default.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_generator_empty_by_default.phpt index 01e328e8a7d..5797c8711a0 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_generator_empty_by_default.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_generator_empty_by_default.phpt @@ -1,5 +1,9 @@ --TEST-- Iterable return types should return empty array by default +--SKIPIF-- +testDouble('Foo', false, false); +$mock = $generator->testDouble( + type: 'Foo', + mockObject: false, +); var_dump(iterator_to_array($mock->forTraversable())); var_dump(iterator_to_array($mock->forGenerator())); var_dump(iterator_to_array($mock->forIterable())); ---EXPECTF-- +--EXPECT-- array(0) { } array(0) { diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_intersection.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_intersection.phpt index 2fc91062941..f4695d45944 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_intersection.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_intersection.phpt @@ -22,33 +22,42 @@ require_once __DIR__ . '/../../../../vendor/autoload.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): AnInterface&AnotherInterface { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -56,9 +65,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'AnInterface&AnotherInterface', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'AnInterface&AnotherInterface', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_never.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_never.phpt index 8f63aef5512..5d2512f9f8d 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_never.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_never.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../../vendor/autoload.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(string $baz): never { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'never', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'never', $this ) ); } diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_null.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_null.phpt index 45969d60b36..f783b4da419 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_null.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_null.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): null { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'null', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'null', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_nullable.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_nullable.phpt index df601f93294..c6b2d15c63b 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_nullable.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_nullable.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(string $baz): ?string { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '?string', $this, true + 'Foo', 'bar', $__phpunit_arguments, '?string', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_object_method.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_object_method.phpt index fa756b3ce86..12e368abbe7 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_object_method.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_object_method.phpt @@ -15,33 +15,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(string $baz): Bar { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -49,9 +58,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'Bar', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'Bar', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_parent.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_parent.phpt index 63dbfaee54e..35f8cfc00af 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_parent.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_parent.phpt @@ -19,33 +19,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Bar', - true, - true, - [], - 'MockBar', - true, - true + type: 'Bar', + mockObject: true, + methods: [], + mockClassName: 'MockBar', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockBar extends Bar implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function baz(): Foo { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -53,9 +62,11 @@ class MockBar extends Bar implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Bar', 'baz', $__phpunit_arguments, 'Foo', $this, true + 'Bar', 'baz', $__phpunit_arguments, 'Foo', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_self.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_self.phpt index 20f8c3f504f..53263bb948f 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_self.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_self.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(string $baz): Foo { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'Foo', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'Foo', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_static.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_static.phpt index 7ff19e1e243..e817c58445f 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_static.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_static.phpt @@ -22,33 +22,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'ClassWithStaticReturnTypes', - true, - true, - [], - 'MockClassWithStaticReturnTypes', - true, - true + type: 'ClassWithStaticReturnTypes', + mockObject: true, + methods: [], + mockClassName: 'MockClassWithStaticReturnTypes', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function returnsStatic(): static { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -56,9 +65,11 @@ class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implemen } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithStaticReturnTypes', 'returnsStatic', $__phpunit_arguments, 'static', $this, true + 'ClassWithStaticReturnTypes', 'returnsStatic', $__phpunit_arguments, 'static', $this ) ); @@ -67,10 +78,23 @@ class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implemen public function returnsStaticOrNull(): ?static { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -78,9 +102,11 @@ class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implemen } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithStaticReturnTypes', 'returnsStaticOrNull', $__phpunit_arguments, '?static', $this, true + 'ClassWithStaticReturnTypes', 'returnsStaticOrNull', $__phpunit_arguments, '?static', $this ) ); @@ -89,10 +115,23 @@ class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implemen public function returnsUnionWithStatic(): static|stdClass { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -100,9 +139,11 @@ class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implemen } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'ClassWithStaticReturnTypes', 'returnsUnionWithStatic', $__phpunit_arguments, 'static|stdClass', $this, true + 'ClassWithStaticReturnTypes', 'returnsUnionWithStatic', $__phpunit_arguments, 'static|stdClass', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_static_method.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_static_method.phpt index 156026a34e7..cc4beeeadf2 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_static_method.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_static_method.phpt @@ -15,24 +15,20 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_true.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_true.phpt index 3c8ab7e4934..aca19906fee 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_true.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_true.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): true { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'true', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'true', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_union.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_union.phpt index 2fe38765905..ed575f56c85 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_union.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_union.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - Foo::class, - true, - true, - [], - 'MockFoo', - true, - true + type: Foo::class, + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(): bool|int { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'bool|int', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'bool|int', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/return_type_declarations_void.phpt b/tests/end-to-end/mock-objects/generator/return_type_declarations_void.phpt index 1a98b16b5ca..cf007f95a16 100644 --- a/tests/end-to-end/mock-objects/generator/return_type_declarations_void.phpt +++ b/tests/end-to-end/mock-objects/generator/return_type_declarations_void.phpt @@ -12,33 +12,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(string $baz): void { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -46,9 +55,11 @@ class MockFoo implements PHPUnit\Framework\MockObject\MockObjectInternal, Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'void', $this, true + 'Foo', 'bar', $__phpunit_arguments, 'void', $this ) ); } diff --git a/tests/end-to-end/mock-objects/generator/scalar_type_declarations.phpt b/tests/end-to-end/mock-objects/generator/scalar_type_declarations.phpt index 52e54a028fc..b3203db2ea9 100644 --- a/tests/end-to-end/mock-objects/generator/scalar_type_declarations.phpt +++ b/tests/end-to-end/mock-objects/generator/scalar_type_declarations.phpt @@ -14,33 +14,42 @@ require_once __DIR__ . '/../../../bootstrap.php'; $generator = new \PHPUnit\Framework\MockObject\Generator\Generator; $mock = $generator->generate( - 'Foo', - true, - true, - [], - 'MockFoo', - true, - true + type: 'Foo', + mockObject: true, + methods: [], + mockClassName: 'MockFoo', ); print $mock->classCode(); ---EXPECTF-- +--EXPECT-- declare(strict_types=1); class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInternal { use PHPUnit\Framework\MockObject\StubApi; use PHPUnit\Framework\MockObject\MockObjectApi; - use PHPUnit\Framework\MockObject\GeneratedAsMockObject; use PHPUnit\Framework\MockObject\Method; use PHPUnit\Framework\MockObject\DoubledCloneMethod; public function bar(string $baz) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$baz]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -48,9 +57,11 @@ class MockFoo extends Foo implements PHPUnit\Framework\MockObject\MockObjectInte } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/generator/wsdl_class.phpt b/tests/end-to-end/mock-objects/generator/wsdl_class.phpt deleted file mode 100644 index e199b70debd..00000000000 --- a/tests/end-to-end/mock-objects/generator/wsdl_class.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generateClassFromWsdl('GoogleSearch.wsdl', 'GoogleSearch') ---SKIPIF-- -generateClassFromWsdl( - __DIR__ . '/../../../_files/GoogleSearch.wsdl', - 'GoogleSearch' -); ---EXPECTF-- -declare(strict_types=1); - -class GoogleSearch extends \SoapClient -{ - public function __construct($wsdl, array $options) - { - parent::__construct('%s/GoogleSearch.wsdl', $options); - } - - public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe) - { - } - - public function doGetCachedPage($key, $url) - { - } - - public function doSpellingSuggestion($key, $phrase) - { - } -} diff --git a/tests/end-to-end/mock-objects/generator/wsdl_class_namespace.phpt b/tests/end-to-end/mock-objects/generator/wsdl_class_namespace.phpt deleted file mode 100644 index 1b9ab946949..00000000000 --- a/tests/end-to-end/mock-objects/generator/wsdl_class_namespace.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generateClassFromWsdl('GoogleSearch.wsdl', 'GoogleSearch') ---SKIPIF-- -generateClassFromWsdl( - __DIR__ . '/../../../_files/GoogleSearch.wsdl', - 'My\\Space\\GoogleSearch' -); ---EXPECTF-- -declare(strict_types=1); - -namespace My\Space; - -class GoogleSearch extends \SoapClient -{ - public function __construct($wsdl, array $options) - { - parent::__construct('%s/GoogleSearch.wsdl', $options); - } - - public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe) - { - } - - public function doGetCachedPage($key, $url) - { - } - - public function doSpellingSuggestion($key, $phrase) - { - } -} diff --git a/tests/end-to-end/mock-objects/generator/wsdl_class_partial.phpt b/tests/end-to-end/mock-objects/generator/wsdl_class_partial.phpt deleted file mode 100644 index fbb3996f8f5..00000000000 --- a/tests/end-to-end/mock-objects/generator/wsdl_class_partial.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -\PHPUnit\Framework\MockObject\Generator\Generator::generateClassFromWsdl('GoogleSearch.wsdl', 'GoogleSearch', ['doGoogleSearch']) ---SKIPIF-- -generateClassFromWsdl( - __DIR__ . '/../../../_files/GoogleSearch.wsdl', - 'GoogleSearch', - ['doGoogleSearch'] -); ---EXPECTF-- -declare(strict_types=1); - -class GoogleSearch extends \SoapClient -{ - public function __construct($wsdl, array $options) - { - parent::__construct('%s/GoogleSearch.wsdl', $options); - } - - public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe) - { - } -} diff --git a/tests/end-to-end/mock-objects/mock-method/call_original.phpt b/tests/end-to-end/mock-objects/mock-method/call_original.phpt deleted file mode 100644 index e0509b89a5e..00000000000 --- a/tests/end-to-end/mock-objects/mock-method/call_original.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -Mock method and call original method ---FILE-- -getMethod('bar'), - true, - false -); - -$code = $mockMethod->generateCode(); - -print $code; ---EXPECT-- - - public function bar() - { - $__phpunit_arguments = []; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 0) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "bar"], $__phpunit_arguments); - - return $__phpunit_result; - } diff --git a/tests/end-to-end/mock-objects/mock-method/call_original_with_argument.phpt b/tests/end-to-end/mock-objects/mock-method/call_original_with_argument.phpt deleted file mode 100644 index 1cfb0430b66..00000000000 --- a/tests/end-to-end/mock-objects/mock-method/call_original_with_argument.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -Mock method and call original method with argument ---FILE-- -getMethod('bar'), - true, - false -); - -$code = $mockMethod->generateCode(); - -print $code; ---EXPECT-- - -private function bar($arg) - { - $__phpunit_arguments = [$arg]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 1) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "bar"], $__phpunit_arguments); - - return $__phpunit_result; - } diff --git a/tests/end-to-end/mock-objects/mock-method/call_original_with_argument_variadic.phpt b/tests/end-to-end/mock-objects/mock-method/call_original_with_argument_variadic.phpt deleted file mode 100644 index d22d321820c..00000000000 --- a/tests/end-to-end/mock-objects/mock-method/call_original_with_argument_variadic.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -Mock method and call original method with variadic argument ---FILE-- -getMethod('bar'), - true, - false -); - -$code = $mockMethod->generateCode(); - -print $code; ---EXPECT-- - -private function bar(...$args) - { - $__phpunit_arguments = []; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 0) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "bar"], $__phpunit_arguments); - - return $__phpunit_result; - } diff --git a/tests/end-to-end/mock-objects/mock-method/call_original_with_return_type_void.phpt b/tests/end-to-end/mock-objects/mock-method/call_original_with_return_type_void.phpt deleted file mode 100644 index 0dda3cce175..00000000000 --- a/tests/end-to-end/mock-objects/mock-method/call_original_with_return_type_void.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -Mock method and call original method that has a return type of void ---FILE-- -getMethod('bar'), - true, - false -); - -$code = $mockMethod->generateCode(); - -print $code; ---EXPECTF-- - - public function bar(): void - { - $__phpunit_arguments = []; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 0) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'void', $this, false, true - ) - ); - - $__phpunit_result = call_user_func_array([$this->__phpunit_originalObject, "bar"], $__phpunit_arguments); - } diff --git a/tests/end-to-end/mock-objects/mock-method/clone_method_arguments.phpt b/tests/end-to-end/mock-objects/mock-method/clone_method_arguments.phpt deleted file mode 100644 index 5aef17c57dd..00000000000 --- a/tests/end-to-end/mock-objects/mock-method/clone_method_arguments.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -Mock method and clone method arguments ---FILE-- -getMethod('bar'), - false, - true -); - -$code = $mockMethod->generateCode(); - -print $code; ---EXPECT-- - - public function bar() - { - $__phpunit_arguments = []; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > 0) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( - new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, true - ) - ); - - return $__phpunit_result; - } diff --git a/tests/end-to-end/mock-objects/mock-method/deprecated_with_description.phpt b/tests/end-to-end/mock-objects/mock-method/deprecated_with_description.phpt index c5b5211c07c..f74e48c11d5 100644 --- a/tests/end-to-end/mock-objects/mock-method/deprecated_with_description.phpt +++ b/tests/end-to-end/mock-objects/mock-method/deprecated_with_description.phpt @@ -13,7 +13,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -28,10 +28,23 @@ public function bar() { @trigger_error('The Foo::bar method is deprecated (some explanation).', E_USER_DEPRECATED); + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -39,9 +52,11 @@ public function bar() } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/deprecated_without_description.phpt b/tests/end-to-end/mock-objects/mock-method/deprecated_without_description.phpt index e8fc33ec6e1..0b6435cd139 100644 --- a/tests/end-to-end/mock-objects/mock-method/deprecated_without_description.phpt +++ b/tests/end-to-end/mock-objects/mock-method/deprecated_without_description.phpt @@ -13,7 +13,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -28,10 +28,23 @@ public function bar() { @trigger_error('The Foo::bar method is deprecated ().', E_USER_DEPRECATED); + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -39,9 +52,11 @@ public function bar() } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/private_method.phpt b/tests/end-to-end/mock-objects/mock-method/private_method.phpt index ca62fee8c73..dd529809c15 100644 --- a/tests/end-to-end/mock-objects/mock-method/private_method.phpt +++ b/tests/end-to-end/mock-objects/mock-method/private_method.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar() } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/protected_method.phpt b/tests/end-to-end/mock-objects/mock-method/protected_method.phpt index eee3bf255a6..1d60f29750b 100644 --- a/tests/end-to-end/mock-objects/mock-method/protected_method.phpt +++ b/tests/end-to-end/mock-objects/mock-method/protected_method.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; protected function bar() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ protected function bar() } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/return_by_reference.phpt b/tests/end-to-end/mock-objects/mock-method/return_by_reference.phpt index 5ee4280e40f..33da1456e9a 100644 --- a/tests/end-to-end/mock-objects/mock-method/return_by_reference.phpt +++ b/tests/end-to-end/mock-objects/mock-method/return_by_reference.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; public function &bar() { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ public function &bar() } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/return_by_reference_with_return_type.phpt b/tests/end-to-end/mock-objects/mock-method/return_by_reference_with_return_type.phpt index 3413cd8288c..244af33f2e9 100644 --- a/tests/end-to-end/mock-objects/mock-method/return_by_reference_with_return_type.phpt +++ b/tests/end-to-end/mock-objects/mock-method/return_by_reference_with_return_type.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; public function &bar(): string { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ public function &bar(): string } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'string', $this, false + 'Foo', 'bar', $__phpunit_arguments, 'string', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/return_type.phpt b/tests/end-to-end/mock-objects/mock-method/return_type.phpt index 948406e32bf..b40628173a5 100644 --- a/tests/end-to-end/mock-objects/mock-method/return_type.phpt +++ b/tests/end-to-end/mock-objects/mock-method/return_type.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; public function bar(): string { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ public function bar(): string } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'string', $this, false + 'Foo', 'bar', $__phpunit_arguments, 'string', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/return_type_parent.phpt b/tests/end-to-end/mock-objects/mock-method/return_type_parent.phpt index d4e058b68a2..8538678d3c9 100644 --- a/tests/end-to-end/mock-objects/mock-method/return_type_parent.phpt +++ b/tests/end-to-end/mock-objects/mock-method/return_type_parent.phpt @@ -14,7 +14,7 @@ class Foo extends Baz require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -27,10 +27,23 @@ print $code; public function bar(): Baz { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -38,9 +51,11 @@ public function bar(): Baz } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'Baz', $this, false + 'Foo', 'bar', $__phpunit_arguments, 'Baz', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/return_type_self.phpt b/tests/end-to-end/mock-objects/mock-method/return_type_self.phpt index 0d0a8237f3e..a7661a9cb48 100644 --- a/tests/end-to-end/mock-objects/mock-method/return_type_self.phpt +++ b/tests/end-to-end/mock-objects/mock-method/return_type_self.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; public function bar(): Foo { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ public function bar(): Foo } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, 'Foo', $this, false + 'Foo', 'bar', $__phpunit_arguments, 'Foo', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/static_method.phpt b/tests/end-to-end/mock-objects/mock-method/static_method.phpt index 37c1d451d9a..4f975ed29c4 100644 --- a/tests/end-to-end/mock-objects/mock-method/static_method.phpt +++ b/tests/end-to-end/mock-objects/mock-method/static_method.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false diff --git a/tests/end-to-end/mock-objects/mock-method/static_method_with_return_type.phpt b/tests/end-to-end/mock-objects/mock-method/static_method_with_return_type.phpt index 9ea537d473b..13f014722e7 100644 --- a/tests/end-to-end/mock-objects/mock-method/static_method_with_return_type.phpt +++ b/tests/end-to-end/mock-objects/mock-method/static_method_with_return_type.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument.phpt index 981ac9640c0..aab00186b9a 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar($arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar($arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_default.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_default.phpt index 2659db33e47..46e02d5be56 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_default.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_default.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar($arg = false) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar($arg = false) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_default_constant.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_default_constant.phpt index c23a7870076..d27fcff2fc2 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_default_constant.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_default_constant.phpt @@ -15,7 +15,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -28,10 +28,23 @@ print $code; private function bar($a = 1, $b = 2, $c = 3, $d = 4) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$a, $b, $c, $d]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 4) { + if (4 !== null && $__phpunit_count > 4) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 4; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -39,9 +52,11 @@ private function bar($a = 1, $b = 2, $c = 3, $d = 4) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_default_new_expression.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_default_new_expression.phpt index 9d68fc2b08d..1960713225b 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_default_new_expression.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_default_new_expression.phpt @@ -17,7 +17,7 @@ require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass(Bar::class); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('method'), false, false @@ -30,10 +30,23 @@ print $code; public function method(Foo $foo = new \Foo(1, 2, 3)) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$foo]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -41,9 +54,11 @@ public function method(Foo $foo = new \Foo(1, 2, 3)) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Bar', 'method', $__phpunit_arguments, '', $this, false + 'Bar', 'method', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_default_null.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_default_null.phpt index 40119c3e2fd..9c0eeb94f2c 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_default_null.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_default_null.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar($arg = NULL) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar($arg = NULL) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_nullable.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_nullable.phpt index 87e70a312fc..c06ebd8d363 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_nullable.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_nullable.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(?string $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(?string $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_reference.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_reference.phpt index 859187f9cb7..02d9c799461 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_reference.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_reference.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(&$arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [&$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(&$arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_array.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_array.phpt index 8fe5b89b44b..80856caabfa 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_array.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_array.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(array $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(array $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_callable.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_callable.phpt index c8e39a1468d..10422b5fd2f 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_callable.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_callable.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(callable $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(callable $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_class.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_class.phpt index 56fcbcbf3a1..da6aee4ec22 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_class.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_class.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(Exception $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(Exception $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_scalar.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_scalar.phpt index 43c97200e93..eddbac31dd3 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_scalar.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_scalar.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(string $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(string $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_self.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_self.phpt index 23fbd8cf57d..5be3ad94c9a 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_self.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_self.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(Foo $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(Foo $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_unkown_class.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_unkown_class.phpt index e8e781fda8f..cbbb4720eab 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_unkown_class.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_unkown_class.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(UnknownClass $arg) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 1) { + if (1 !== null && $__phpunit_count > 1) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(UnknownClass $arg) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_variadic.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_variadic.phpt index 17fc225ef9d..556bda3a7eb 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_typed_variadic.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_typed_variadic.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(string ...$args) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(string ...$args) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_argument_variadic.phpt b/tests/end-to-end/mock-objects/mock-method/with_argument_variadic.phpt index 9bedd96e2a3..5cc69f5295d 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_argument_variadic.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_argument_variadic.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar(...$args) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = []; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 0) { + if (0 !== null && $__phpunit_count > 0) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar(...$args) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/mock-objects/mock-method/with_arguments.phpt b/tests/end-to-end/mock-objects/mock-method/with_arguments.phpt index a3f143e6730..0d3827cfa3e 100644 --- a/tests/end-to-end/mock-objects/mock-method/with_arguments.phpt +++ b/tests/end-to-end/mock-objects/mock-method/with_arguments.phpt @@ -10,7 +10,7 @@ class Foo require_once __DIR__ . '/../../../bootstrap.php'; $class = new ReflectionClass('Foo'); -$mockMethod = \PHPUnit\Framework\MockObject\Generator\MockMethod::fromReflection( +$mockMethod = \PHPUnit\Framework\MockObject\Generator\DoubledMethod::fromReflection( $class->getMethod('bar'), false, false @@ -23,10 +23,23 @@ print $code; private function bar($arg1, $arg2) { + $__phpunit_definedVariables = get_defined_vars(); + $__phpunit_namedVariadicParameters = []; + + foreach ($__phpunit_definedVariables as $__phpunit_definedVariableName => $__phpunit_definedVariableValue) { + if ((new ReflectionParameter([__CLASS__, __FUNCTION__], $__phpunit_definedVariableName))->isVariadic()) { + foreach ($__phpunit_definedVariableValue as $__phpunit_key => $__phpunit_namedValue) { + if (is_string($__phpunit_key)) { + $__phpunit_namedVariadicParameters[$__phpunit_key] = $__phpunit_namedValue; + } + } + } + } + $__phpunit_arguments = [$arg1, $arg2]; $__phpunit_count = func_num_args(); - if ($__phpunit_count > 2) { + if (2 !== null && $__phpunit_count > 2) { $__phpunit_arguments_tmp = func_get_args(); for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { @@ -34,9 +47,11 @@ private function bar($arg1, $arg2) } } + $__phpunit_arguments = array_merge($__phpunit_arguments, $__phpunit_namedVariadicParameters); + $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke( new \PHPUnit\Framework\MockObject\Invocation( - 'Foo', 'bar', $__phpunit_arguments, '', $this, false + 'Foo', 'bar', $__phpunit_arguments, '', $this ) ); diff --git a/tests/end-to-end/phar/tests/standard/GreeterTest.php b/tests/end-to-end/phar/tests/standard/GreeterTest.php index 62faaedf12e..136d29cd2ba 100644 --- a/tests/end-to-end/phar/tests/standard/GreeterTest.php +++ b/tests/end-to-end/phar/tests/standard/GreeterTest.php @@ -9,11 +9,12 @@ */ namespace PHPUnit\TestFixture\Phar; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\TestCase; -/** - * @covers \PHPUnit\TestFixture\Phar\Greeter - */ +#[CoversClass(Greeter::class)] final class GreeterTest extends TestCase { public function testGreets(): void @@ -21,11 +22,8 @@ public function testGreets(): void $this->assertSame('Hello world!', (new Greeter)->greet()); } - /** - * @runInSeparateProcess - * - * @ticket https://github.com/sebastianbergmann/phpunit/issues/4412 - */ + #[RunInSeparateProcess] + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/4412')] public function testGreetsInIsolation(): void { $this->assertSame('Hello world!', (new Greeter)->greet()); diff --git a/tests/end-to-end/phpt/expect-location-hint.phpt b/tests/end-to-end/phpt/expect-location-hint.phpt index d2494d76465..a7dee4ce2c9 100644 --- a/tests/end-to-end/phpt/expect-location-hint.phpt +++ b/tests/end-to-end/phpt/expect-location-hint.phpt @@ -1,5 +1,9 @@ --TEST-- PHPT EXPECT comparison returns correct code location hint +--SKIPIF-- +run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s - -E 1 / 1 (100%) - -Time: %s, Memory: %s - -There was 1 error: - -1) %stests%e_files%ephpt-unsupported-section.phpt -PHPUnit\Runner\UnsupportedPhptSectionException: PHPUnit does not support PHPT GET sections - -ERRORS! -Tests: 1, Assertions: 0, Errors: 1. diff --git a/tests/end-to-end/regression/1149.phpt b/tests/end-to-end/regression/1149.phpt index 36b5a542252..0196b446e35 100644 --- a/tests/end-to-end/regression/1149.phpt +++ b/tests/end-to-end/regression/1149.phpt @@ -13,7 +13,7 @@ PHPUnit %s by Sebastian Bergmann and contributors. Runtime: %s -1.2. 2 / 2 (100%)2 +1.2. 2 / 2 (100%) Time: %s, Memory: %s diff --git a/tests/end-to-end/regression/1149/Issue1149Test.php b/tests/end-to-end/regression/1149/Issue1149Test.php index c0e027886a8..e5eb518bf91 100644 --- a/tests/end-to-end/regression/1149/Issue1149Test.php +++ b/tests/end-to-end/regression/1149/Issue1149Test.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; class Issue1149Test extends TestCase @@ -19,9 +20,7 @@ public function testOne(): void print '1'; } - /** - * @runInSeparateProcess - */ + #[RunInSeparateProcess] public function testTwo(): void { $this->assertTrue(true); diff --git a/tests/end-to-end/regression/1335/Issue1335Test.php b/tests/end-to-end/regression/1335/Issue1335Test.php index a97938b5aa6..d706d44d98c 100644 --- a/tests/end-to-end/regression/1335/Issue1335Test.php +++ b/tests/end-to-end/regression/1335/Issue1335Test.php @@ -9,13 +9,12 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\TestCase; -/** - * @runTestsInSeparateProcesses - * - * @preserveGlobalState enabled - */ +#[RunTestsInSeparateProcesses] +#[PreserveGlobalState(true)] class Issue1335Test extends TestCase { public function testGlobalString(): void diff --git a/tests/end-to-end/regression/1337/Issue1337Test.php b/tests/end-to-end/regression/1337/Issue1337Test.php index ce406f58741..9abd356fad9 100644 --- a/tests/end-to-end/regression/1337/Issue1337Test.php +++ b/tests/end-to-end/regression/1337/Issue1337Test.php @@ -9,11 +9,12 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class Issue1337Test extends TestCase { - public static function dataProvider() + public static function dataProvider(): array { return [ 'c:\\' => [true], @@ -22,9 +23,7 @@ public static function dataProvider() ]; } - /** - * @dataProvider dataProvider - */ + #[DataProvider('dataProvider')] public function testProvider($a): void { $this->assertTrue($a); diff --git a/tests/end-to-end/regression/1348.phpt b/tests/end-to-end/regression/1348.phpt deleted file mode 100644 index d99217442a6..00000000000 --- a/tests/end-to-end/regression/1348.phpt +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -https://github.com/sebastianbergmann/phpunit/issues/1348 ---SKIPIF-- -run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s - -. -STDOUT does not break test result -E 2 / 2 (100%) - -Time: %s, Memory: %s - -There was 1 error: - -1) PHPUnit\TestFixture\Issue1348Test::testSTDERR -PHPUnit\Framework\Exception: STDERR works as usual. - -ERRORS! -Tests: 2, Assertions: 1, Errors: 1. diff --git a/tests/end-to-end/regression/1348/Issue1348Test.php b/tests/end-to-end/regression/1348/Issue1348Test.php deleted file mode 100644 index 20e1dd1d203..00000000000 --- a/tests/end-to-end/regression/1348/Issue1348Test.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture; - -use const STDERR; -use const STDOUT; -use function fwrite; -use PHPUnit\Framework\TestCase; - -class Issue1348Test extends TestCase -{ - public function testSTDOUT(): void - { - fwrite(STDOUT, "\nSTDOUT does not break test result\n"); - $this->assertTrue(true); - } - - public function testSTDERR(): void - { - fwrite(STDERR, 'STDERR works as usual.'); - } -} diff --git a/tests/end-to-end/regression/1374/Issue1374Test.php b/tests/end-to-end/regression/1374/Issue1374Test.php index 55eb630c895..70f32b3b7d4 100644 --- a/tests/end-to-end/regression/1374/Issue1374Test.php +++ b/tests/end-to-end/regression/1374/Issue1374Test.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; -/** - * @requires extension I_DO_NOT_EXIST - */ +#[RequiresPhpExtension('I_DO_NOT_EXIST')] class Issue1374Test extends TestCase { protected function setUp(): void diff --git a/tests/end-to-end/regression/1570.phpt b/tests/end-to-end/regression/1570.phpt index 1b42d1332d9..d49e876e880 100644 --- a/tests/end-to-end/regression/1570.phpt +++ b/tests/end-to-end/regression/1570.phpt @@ -21,7 +21,7 @@ Time: %s, Memory: %s There was 1 risky test: 1) PHPUnit\TestFixture\Issue1570Test::testOne -This test printed output: * +Test code or tested code printed unexpected output: * %s:%d diff --git a/tests/end-to-end/regression/2137-filter.phpt b/tests/end-to-end/regression/2137-filter.phpt index dfcc749d296..7b56bcda1c4 100644 --- a/tests/end-to-end/regression/2137-filter.phpt +++ b/tests/end-to-end/regression/2137-filter.phpt @@ -19,13 +19,13 @@ There were 2 PHPUnit errors: 1) PHPUnit\TestFixture\Issue2137Test::testBrandService The data provider specified for PHPUnit\TestFixture\Issue2137Test::testBrandService is invalid -Data set #0 is invalid +Data set #0 is invalid, expected array but got stdClass %s:%d 2) PHPUnit\TestFixture\Issue2137Test::testSomethingElseInvalid The data provider specified for PHPUnit\TestFixture\Issue2137Test::testSomethingElseInvalid is invalid -Data set #0 is invalid +Data set #0 is invalid, expected array but got stdClass %s:%d diff --git a/tests/end-to-end/regression/2137-no_filter.phpt b/tests/end-to-end/regression/2137-no_filter.phpt index 38632c11b9a..2f68ffbbfaf 100644 --- a/tests/end-to-end/regression/2137-no_filter.phpt +++ b/tests/end-to-end/regression/2137-no_filter.phpt @@ -17,13 +17,13 @@ There were 2 PHPUnit errors: 1) PHPUnit\TestFixture\Issue2137Test::testBrandService The data provider specified for PHPUnit\TestFixture\Issue2137Test::testBrandService is invalid -Data set #0 is invalid +Data set #0 is invalid, expected array but got stdClass %s:%d 2) PHPUnit\TestFixture\Issue2137Test::testSomethingElseInvalid The data provider specified for PHPUnit\TestFixture\Issue2137Test::testSomethingElseInvalid is invalid -Data set #0 is invalid +Data set #0 is invalid, expected array but got stdClass %s:%d diff --git a/tests/end-to-end/regression/2137/Issue2137Test.php b/tests/end-to-end/regression/2137/Issue2137Test.php index 8dd8fa87468..f046b562d42 100644 --- a/tests/end-to-end/regression/2137/Issue2137Test.php +++ b/tests/end-to-end/regression/2137/Issue2137Test.php @@ -9,7 +9,10 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; +use SebastianBergmann\RecursionContext\InvalidArgumentException; use stdClass; class Issue2137Test extends TestCase @@ -23,24 +26,22 @@ public static function provideBrandService() } /** - * @dataProvider provideBrandService - * - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ + #[DataProvider('provideBrandService')] public function testBrandService($provided, $expected): void { $this->assertSame($provided, $expected); } /** - * @dataProvider provideBrandService - * * @throws \Exception - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ExpectationFailedException + * @throws InvalidArgumentException */ + #[DataProvider('provideBrandService')] public function testSomethingElseInvalid($provided, $expected): void { $this->assertSame($provided, $expected); diff --git a/tests/end-to-end/regression/2155-no-expects-log.phpt b/tests/end-to-end/regression/2155-no-expects-log.phpt new file mode 100644 index 00000000000..af9e476d934 --- /dev/null +++ b/tests/end-to-end/regression/2155-no-expects-log.phpt @@ -0,0 +1,26 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/2155 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +logged a side effect +. 1 / 1 (100%) + +Time: %s, Memory: %s + +Issue2155Test_No Expects Log (PHPUnit\TestFixture\Issue2155\Issue2155Test_NoExpectsLog) + ✔ One + +OK (1 test, 1 assertion) diff --git a/tests/end-to-end/regression/2155/Issue2155Test_NoExpectsLog.php b/tests/end-to-end/regression/2155/Issue2155Test_NoExpectsLog.php new file mode 100644 index 00000000000..341bdba4f52 --- /dev/null +++ b/tests/end-to-end/regression/2155/Issue2155Test_NoExpectsLog.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue2155; + +use function error_log; +use PHPUnit\Framework\TestCase; + +class Foo +{ + public function doFoo() + { + error_log('logged a side effect'); + + return ''; + } +} + +final class Issue2155Test_NoExpectsLog extends TestCase +{ + public function testOne(): void + { + $foo = new Foo; + + $this->assertSame('', $foo->doFoo()); + } +} diff --git a/tests/end-to-end/regression/2158/Issue2158Test.php b/tests/end-to-end/regression/2158/Issue2158Test.php index 049519f38f3..4c755287940 100644 --- a/tests/end-to-end/regression/2158/Issue2158Test.php +++ b/tests/end-to-end/regression/2158/Issue2158Test.php @@ -10,11 +10,11 @@ namespace PHPUnit\TestFixture; use function defined; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; -/** - * @preserveGlobalState enabled - */ +#[PreserveGlobalState(true)] class Issue2158Test extends TestCase { /** @@ -29,9 +29,8 @@ public function testSomething(): void /** * Constant defined previously in main process constant should be available and * no errors should be yielded by reload of included files. - * - * @runInSeparateProcess */ + #[RunInSeparateProcess] public function testSomethingElse(): void { $this->assertTrue(defined('TEST_CONSTANT')); diff --git a/tests/end-to-end/regression/2380/Issue2380Test.php b/tests/end-to-end/regression/2380/Issue2380Test.php index 826b471003e..bd1eafe6eb3 100644 --- a/tests/end-to-end/regression/2380/Issue2380Test.php +++ b/tests/end-to-end/regression/2380/Issue2380Test.php @@ -10,21 +10,17 @@ namespace PHPUnit\TestFixture; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class Issue2380Test extends TestCase { - /** - * @return Generator - */ - public static function generatorData() + public static function generatorData(): Generator { yield ['testing']; } - /** - * @dataProvider generatorData - */ + #[DataProvider('generatorData')] public function testGeneratorProvider($data): void { $this->assertNotEmpty($data); diff --git a/tests/end-to-end/regression/2724/SeparateClassRunMethodInNewProcessTest.php b/tests/end-to-end/regression/2724/SeparateClassRunMethodInNewProcessTest.php index 936d86a5163..01ebccd7220 100644 --- a/tests/end-to-end/regression/2724/SeparateClassRunMethodInNewProcessTest.php +++ b/tests/end-to-end/regression/2724/SeparateClassRunMethodInNewProcessTest.php @@ -14,18 +14,17 @@ use function file_get_contents; use function getmypid; use function unlink; +use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; use PHPUnit\Framework\TestCase; -/** - * @runClassInSeparateProcess - */ +#[RunClassInSeparateProcess] final class SeparateClassRunMethodInNewProcessTest extends TestCase { - public const PROCESS_ID_FILE_PATH = __DIR__ . '/parent_process_id.txt'; - public const INITIAL_PARENT_PROCESS_ID = 0; - public const INITIAL_PROCESS_ID = 1; - public static $parentProcessId = self::INITIAL_PARENT_PROCESS_ID; - public static $processId = self::INITIAL_PROCESS_ID; + public const string PROCESS_ID_FILE_PATH = __DIR__ . '/parent_process_id.txt'; + public const int INITIAL_PARENT_PROCESS_ID = 0; + public const int INITIAL_PROCESS_ID = 1; + public static $parentProcessId = self::INITIAL_PARENT_PROCESS_ID; + public static $processId = self::INITIAL_PROCESS_ID; public static function setUpBeforeClass(): void { diff --git a/tests/end-to-end/regression/2725/BeforeAfterClassPidTest.php b/tests/end-to-end/regression/2725/BeforeAfterClassPidTest.php index 9740d517e50..797d65602f1 100644 --- a/tests/end-to-end/regression/2725/BeforeAfterClassPidTest.php +++ b/tests/end-to-end/regression/2725/BeforeAfterClassPidTest.php @@ -10,26 +10,23 @@ namespace PHPUnit\TestFixture\Issue2725; use function getmypid; +use PHPUnit\Framework\Attributes\AfterClass; +use PHPUnit\Framework\Attributes\BeforeClass; +use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; use PHPUnit\Framework\TestCase; -/** - * @runClassInSeparateProcess - */ +#[RunClassInSeparateProcess] class BeforeAfterClassPidTest extends TestCase { public const PID_VARIABLE = 'current_pid'; - /** - * @beforeClass - */ + #[BeforeClass] public static function showPidBefore(): void { $GLOBALS[static::PID_VARIABLE] = getmypid(); } - /** - * @afterClass - */ + #[AfterClass] public static function showPidAfter(): void { if ($GLOBALS[static::PID_VARIABLE] - getmypid() !== 0) { diff --git a/tests/end-to-end/regression/2830.phpt b/tests/end-to-end/regression/2830.phpt index 946184912ca..f16b86f8934 100644 --- a/tests/end-to-end/regression/2830.phpt +++ b/tests/end-to-end/regression/2830.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-2830: @runClassInSeparateProcess fails for tests with a @dataProvider +GH-2830: @runClassInSeparateProcess fails for tests with a data provider --FILE-- assertTrue(true); diff --git a/tests/end-to-end/regression/2833.phpt b/tests/end-to-end/regression/2833.phpt new file mode 100644 index 00000000000..77b74fc40ec --- /dev/null +++ b/tests/end-to-end/regression/2833.phpt @@ -0,0 +1,21 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/2833 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.. 2 / 2 (100%) + +Time: %s, Memory: %s + +OK (2 tests, 2 assertions) diff --git a/tests/end-to-end/regression/3093/Issue3093Test.php b/tests/end-to-end/regression/3093/Issue3093Test.php index 42f84d9c8f6..40b1a86e8d9 100644 --- a/tests/end-to-end/regression/3093/Issue3093Test.php +++ b/tests/end-to-end/regression/3093/Issue3093Test.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\TestCase; class Issue3093Test extends TestCase @@ -23,11 +25,8 @@ public function testFirstWithoutDependencies(): void $this->assertTrue(true); } - /** - * @depends testFirstWithoutDependencies - * - * @dataProvider someDataProvider - */ + #[Depends('testFirstWithoutDependencies')] + #[DataProvider('someDataProvider')] public function testSecondThatDependsOnFirstAndDataprovider($value): void { $this->assertTrue(true); diff --git a/tests/end-to-end/regression/3156/Issue3156Test.php b/tests/end-to-end/regression/3156/Issue3156Test.php index 198db92683c..f43dc312371 100644 --- a/tests/end-to-end/regression/3156/Issue3156Test.php +++ b/tests/end-to-end/regression/3156/Issue3156Test.php @@ -9,19 +9,14 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\TestCase; use stdClass; class Issue3156Test extends TestCase { - public function testConstants(): stdClass - { - $this->assertStringEndsWith('/', '/'); - - return new stdClass; - } - - public function dataSelectOperatorsProvider(): array + public static function dataSelectOperatorsProvider(): array { return [ ['1'], @@ -29,11 +24,15 @@ public function dataSelectOperatorsProvider(): array ]; } - /** - * @depends testConstants - * - * @dataProvider dataSelectOperatorsProvider - */ + public function testConstants(): stdClass + { + $this->assertStringEndsWith('/', '/'); + + return new stdClass; + } + + #[Depends('testConstants')] + #[DataProvider('dataSelectOperatorsProvider')] public function testDependsRequire(string $val, stdClass $obj): void { $this->assertStringEndsWith('/', '/'); diff --git a/tests/end-to-end/regression/4232.phpt b/tests/end-to-end/regression/4232.phpt index b020d4e6bb6..be898dcb671 100644 --- a/tests/end-to-end/regression/4232.phpt +++ b/tests/end-to-end/regression/4232.phpt @@ -4,6 +4,8 @@ https://github.com/sebastianbergmann/phpunit/issues/4232 setValue(null, $version); +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +SS 2 / 2 (100%) + +Time: %s, Memory: %s + +OK, but some tests were skipped! +Tests: 2, Assertions: 0, Skipped: 2. diff --git a/tests/end-to-end/regression/4391-separate-class-requires-in-method.phpt b/tests/end-to-end/regression/4391-separate-class-requires-in-method.phpt new file mode 100644 index 00000000000..b5bdcb5cb01 --- /dev/null +++ b/tests/end-to-end/regression/4391-separate-class-requires-in-method.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/4391 +--INI-- +disable_functions=proc_open +--FILE-- +setValue(null, $version); +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +SS 2 / 2 (100%) + +Time: %s, Memory: %s + +OK, but some tests were skipped! +Tests: 2, Assertions: 0, Skipped: 2. diff --git a/tests/end-to-end/regression/4391-separate-requires-in-class.phpt b/tests/end-to-end/regression/4391-separate-requires-in-class.phpt new file mode 100644 index 00000000000..a05dbe9baa6 --- /dev/null +++ b/tests/end-to-end/regression/4391-separate-requires-in-class.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/4391 +--INI-- +disable_functions=proc_open +--FILE-- +setValue(null, $version); +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +S 1 / 1 (100%) + +Time: %s, Memory: %s + +OK, but some tests were skipped! +Tests: 1, Assertions: 0, Skipped: 1. diff --git a/tests/end-to-end/regression/4391-separate-requires-in-method.phpt b/tests/end-to-end/regression/4391-separate-requires-in-method.phpt new file mode 100644 index 00000000000..6ff019b69a4 --- /dev/null +++ b/tests/end-to-end/regression/4391-separate-requires-in-method.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/4391 +--INI-- +disable_functions=proc_open +--FILE-- +setValue(null, $version); +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +S 1 / 1 (100%) + +Time: %s, Memory: %s + +OK, but some tests were skipped! +Tests: 1, Assertions: 0, Skipped: 1. diff --git a/tests/end-to-end/regression/4391-separate-tests-requires-in-class.phpt b/tests/end-to-end/regression/4391-separate-tests-requires-in-class.phpt new file mode 100644 index 00000000000..1c78606064a --- /dev/null +++ b/tests/end-to-end/regression/4391-separate-tests-requires-in-class.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/4391 +--INI-- +disable_functions=proc_open +--FILE-- +setValue(null, $version); +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +SS 2 / 2 (100%) + +Time: %s, Memory: %s + +OK, but some tests were skipped! +Tests: 2, Assertions: 0, Skipped: 2. diff --git a/tests/end-to-end/regression/4391-separate-tests-requires-in-method.phpt b/tests/end-to-end/regression/4391-separate-tests-requires-in-method.phpt new file mode 100644 index 00000000000..2f39ecc322b --- /dev/null +++ b/tests/end-to-end/regression/4391-separate-tests-requires-in-method.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/4391 +--INI-- +disable_functions=proc_open +--FILE-- +setValue(null, $version); +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +SS 2 / 2 (100%) + +Time: %s, Memory: %s + +OK, but some tests were skipped! +Tests: 2, Assertions: 0, Skipped: 2. diff --git a/tests/end-to-end/regression/4391/RunClassInSeparateProcessClassTest.php b/tests/end-to-end/regression/4391/RunClassInSeparateProcessClassTest.php new file mode 100644 index 00000000000..2c17f7c2b44 --- /dev/null +++ b/tests/end-to-end/regression/4391/RunClassInSeparateProcessClassTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue4391; + +use Exception; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; +use PHPUnit\Framework\TestCase; + +#[RunClassInSeparateProcess] +#[RequiresPhpunit('< 10')] +final class RunClassInSeparateProcessClassTest extends TestCase +{ + public function testOne(): void + { + throw new Exception('message'); + } + + public function testTwo(): void + { + throw new Exception('message'); + } +} diff --git a/tests/end-to-end/regression/4391/RunClassInSeparateProcessMethodTest.php b/tests/end-to-end/regression/4391/RunClassInSeparateProcessMethodTest.php new file mode 100644 index 00000000000..79522cbf045 --- /dev/null +++ b/tests/end-to-end/regression/4391/RunClassInSeparateProcessMethodTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue4391; + +use Exception; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; +use PHPUnit\Framework\TestCase; + +#[RunClassInSeparateProcess] +final class RunClassInSeparateProcessMethodTest extends TestCase +{ + #[RequiresPhpunit('< 10')] + public function testOne(): void + { + throw new Exception('message'); + } + + #[RequiresPhpunit('< 10')] + public function testTwo(): void + { + throw new Exception('message'); + } +} diff --git a/tests/end-to-end/regression/4391/RunInSeparateProcessClassTest.php b/tests/end-to-end/regression/4391/RunInSeparateProcessClassTest.php new file mode 100644 index 00000000000..9e7df4d1c63 --- /dev/null +++ b/tests/end-to-end/regression/4391/RunInSeparateProcessClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue4391; + +use Exception; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\TestCase; + +#[RequiresPhpunit('< 10')] +final class RunInSeparateProcessClassTest extends TestCase +{ + #[RunInSeparateProcess] + public function testOne(): void + { + throw new Exception('message'); + } +} diff --git a/tests/end-to-end/regression/4391/RunInSeparateProcessMethodTest.php b/tests/end-to-end/regression/4391/RunInSeparateProcessMethodTest.php new file mode 100644 index 00000000000..88fcd68fb39 --- /dev/null +++ b/tests/end-to-end/regression/4391/RunInSeparateProcessMethodTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue4391; + +use Exception; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\TestCase; + +final class RunInSeparateProcessMethodTest extends TestCase +{ + #[RunInSeparateProcess] + #[RequiresPhpunit('< 10')] + public function testOne(): void + { + throw new Exception('message'); + } +} diff --git a/tests/end-to-end/regression/4391/RunTestsInSeparateProcessesClassTest.php b/tests/end-to-end/regression/4391/RunTestsInSeparateProcessesClassTest.php new file mode 100644 index 00000000000..b6c5513ea0c --- /dev/null +++ b/tests/end-to-end/regression/4391/RunTestsInSeparateProcessesClassTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue4391; + +use Exception; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; +use PHPUnit\Framework\TestCase; + +#[RunTestsInSeparateProcesses] +#[RequiresPhpunit('< 10')] +final class RunTestsInSeparateProcessesClassTest extends TestCase +{ + public function testOne(): void + { + throw new Exception('message'); + } + + public function testTwo(): void + { + throw new Exception('message'); + } +} diff --git a/tests/end-to-end/regression/4391/RunTestsInSeparateProcessesMethodTest.php b/tests/end-to-end/regression/4391/RunTestsInSeparateProcessesMethodTest.php new file mode 100644 index 00000000000..9a6dbe62ddb --- /dev/null +++ b/tests/end-to-end/regression/4391/RunTestsInSeparateProcessesMethodTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue4391; + +use Exception; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; +use PHPUnit\Framework\TestCase; + +#[RunTestsInSeparateProcesses] +final class RunTestsInSeparateProcessesMethodTest extends TestCase +{ + #[RequiresPhpunit('< 10')] + public function testOne(): void + { + throw new Exception('message'); + } + + #[RequiresPhpunit('< 10')] + public function testTwo(): void + { + throw new Exception('message'); + } +} diff --git a/tests/end-to-end/regression/498/Issue498Test.php b/tests/end-to-end/regression/498/Issue498Test.php index f059399dd4c..a28111831a2 100644 --- a/tests/end-to-end/regression/498/Issue498Test.php +++ b/tests/end-to-end/regression/498/Issue498Test.php @@ -10,48 +10,37 @@ namespace PHPUnit\TestFixture; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; class Issue498Test extends TestCase { - public static function shouldBeTrueDataProvider() + public static function shouldBeTrueDataProvider(): array { - // throw new Exception("Can't create the data"); return [ [true], [false], ]; } - public static function shouldBeFalseDataProvider() + public static function shouldBeFalseDataProvider(): array { throw new Exception("Can't create the data"); - - return [ - [true], - [false], - ]; } - /** - * @test - * - * @dataProvider shouldBeTrueDataProvider - * - * @group falseOnly - */ + #[Test] + #[DataProvider('shouldBeTrueDataProvider')] + #[Group('falseOnly')] public function shouldBeTrue($testData): void { $this->assertTrue(true); } - /** - * @test - * - * @dataProvider shouldBeFalseDataProvider - * - * @group trueOnly - */ + #[Test] + #[DataProvider('shouldBeFalseDataProvider')] + #[Group('trueOnly')] public function shouldBeFalse($testData): void { $this->assertFalse(false); diff --git a/tests/end-to-end/regression/5020/Under/Score/Issue5020Test.php b/tests/end-to-end/regression/5020/Under/Score/Issue5020Test.php index 6bb5f5d0b72..9c71d0befe8 100644 --- a/tests/end-to-end/regression/5020/Under/Score/Issue5020Test.php +++ b/tests/end-to-end/regression/5020/Under/Score/Issue5020Test.php @@ -7,7 +7,17 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Under_Score_Issue5020Test extends PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; + +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +class Under_Score_Issue5020Test extends TestCase { public function testTrue(): void { diff --git a/tests/end-to-end/regression/5172.phpt b/tests/end-to-end/regression/5172.phpt index 9ef0b1be364..7a0a720aa6c 100644 --- a/tests/end-to-end/regression/5172.phpt +++ b/tests/end-to-end/regression/5172.phpt @@ -3,6 +3,7 @@ https://github.com/sebastianbergmann/phpunit/issues/5172 --FILE-- run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (PHPUnit %s using %s) Test Runner Configured +Bootstrap Finished (%sMyClassTest.php) +Event Facade Sealed Data Provider Method Called (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::provide for test method PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test) Data Provider Method Finished for PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test: - PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::provide Test Suite Loaded (3 tests) -Event Facade Sealed Test Runner Started Test Suite Sorted Test Runner Execution Started (3 tests) @@ -39,7 +29,6 @@ Test Suite Started (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest, 1 test) Test Suite Started (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test, 1 test) Test Preparation Started (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test#0) Test Prepared (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test#0) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test#0) Test Finished (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test#0) Test Suite Finished (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest::test, 1 test) @@ -47,7 +36,6 @@ Test Suite Finished (PHPUnit\TestFixture\Issue5278\A\AnotherClassTest, 1 test) Test Suite Started (PHPUnit\TestFixture\Issue5278\B\MyClassTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Issue5278\B\MyClassTest::test) Test Prepared (PHPUnit\TestFixture\Issue5278\B\MyClassTest::test) -Assertion Failed (Constraint: is true, Value: false) Test Failed (PHPUnit\TestFixture\Issue5278\B\MyClassTest::test) Failed asserting that false is true. Test Finished (PHPUnit\TestFixture\Issue5278\B\MyClassTest::test) @@ -55,7 +43,6 @@ Test Suite Finished (PHPUnit\TestFixture\Issue5278\B\MyClassTest, 1 test) Test Suite Started (PHPUnit\TestFixture\Issue5278\C\MyClassTest, 1 test) Test Preparation Started (PHPUnit\TestFixture\Issue5278\C\MyClassTest::test) Test Prepared (PHPUnit\TestFixture\Issue5278\C\MyClassTest::test) -Assertion Succeeded (Constraint: is true, Value: true) Test Passed (PHPUnit\TestFixture\Issue5278\C\MyClassTest::test) Test Finished (PHPUnit\TestFixture\Issue5278\C\MyClassTest::test) Test Suite Finished (PHPUnit\TestFixture\Issue5278\C\MyClassTest, 1 test) diff --git a/tests/end-to-end/regression/5287/A/AnotherClassTest.php b/tests/end-to-end/regression/5287/A/AnotherClassTest.php index ee996b72e20..6026157d2d7 100644 --- a/tests/end-to-end/regression/5287/A/AnotherClassTest.php +++ b/tests/end-to-end/regression/5287/A/AnotherClassTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Issue5278\A; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\Issue5278\C\MyClassTest; @@ -19,7 +20,7 @@ public static function provide(): array return [[MyClassTest::VALUE]]; } - /** @dataProvider provide */ + #[DataProvider('provide')] public function test(bool $value): void { $this->assertTrue($value); diff --git a/tests/end-to-end/regression/5287/C/MyClassTest.php b/tests/end-to-end/regression/5287/C/MyClassTest.php index a071501f03a..e1503a1d43d 100644 --- a/tests/end-to-end/regression/5287/C/MyClassTest.php +++ b/tests/end-to-end/regression/5287/C/MyClassTest.php @@ -13,7 +13,7 @@ final class MyClassTest extends TestCase { - public const VALUE = true; + public const bool VALUE = true; public function test(): void { diff --git a/tests/end-to-end/regression/5288/Issue5288Test.php b/tests/end-to-end/regression/5288/Issue5288Test.php index e7549aa989d..90358ccc5b1 100644 --- a/tests/end-to-end/regression/5288/Issue5288Test.php +++ b/tests/end-to-end/regression/5288/Issue5288Test.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TestFixture\Issue5288; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class Issue5288Test extends TestCase @@ -18,9 +19,7 @@ public static function provider(): array return [[true]]; } - /** - * @dataProvider provider() - */ + #[DataProvider('provider')] public function testOne(bool $value): void { $this->assertTrue($value); diff --git a/tests/end-to-end/regression/5340.phpt b/tests/end-to-end/regression/5340.phpt index ba4429a1f0f..b1a583432d1 100644 --- a/tests/end-to-end/regression/5340.phpt +++ b/tests/end-to-end/regression/5340.phpt @@ -34,12 +34,12 @@ Failed asserting that false is true. There were 2 risky tests: 1) Issue5340Test::testOne -This test printed output: output printed from passing test +Test code or tested code printed unexpected output: output printed from passing test %s%eIssue5340Test.php:%d 2) Issue5340Test::testTwo -This test printed output: +Test code or tested code printed unexpected output: output printed from failing test %s%eIssue5340Test.php:%d diff --git a/tests/end-to-end/regression/5340/Issue5340Test.php b/tests/end-to-end/regression/5340/Issue5340Test.php index fa54eac6ef3..2820b8c7ac5 100644 --- a/tests/end-to-end/regression/5340/Issue5340Test.php +++ b/tests/end-to-end/regression/5340/Issue5340Test.php @@ -13,14 +13,14 @@ final class Issue5340Test extends TestCase { public function testOne(): void { - print 'output printed from passing test' . PHP_EOL; + print 'output printed from passing test' . \PHP_EOL; $this->assertTrue(true); } public function testTwo(): void { - print PHP_EOL . 'output printed from failing test' . PHP_EOL; + print \PHP_EOL . 'output printed from failing test' . \PHP_EOL; $this->assertTrue(false); } diff --git a/tests/end-to-end/regression/5351.phpt b/tests/end-to-end/regression/5351.phpt new file mode 100644 index 00000000000..d2ed180b995 --- /dev/null +++ b/tests/end-to-end/regression/5351.phpt @@ -0,0 +1,47 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5351 +--INI-- +pcov.directory=tests/end-to-end/regression/5351/src/ +--SKIPIF-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +W 1 / 1 (100%) + +Time: %s, Memory: %s MB + +1 test triggered 1 PHPUnit warning: + +1) PHPUnit\TestFixture\Issue5351\GreeterTest::testGreets +Class PHPUnit\TestFixture\Issue5351\DoesNotExist is not a valid target for code coverage + +%sGreeterTest.php:18 + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 1. + + +Code Coverage Report: + %s + + Summary: + Classes: 0.00% (0/1) + Methods: 0.00% (0/1) + Lines: 0.00% (0/1) + diff --git a/tests/end-to-end/regression/5351/phpunit.xml b/tests/end-to-end/regression/5351/phpunit.xml new file mode 100644 index 00000000000..5ff287ed27f --- /dev/null +++ b/tests/end-to-end/regression/5351/phpunit.xml @@ -0,0 +1,22 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/regression/5351/src/Greeter.php b/tests/end-to-end/regression/5351/src/Greeter.php new file mode 100644 index 00000000000..745685a4275 --- /dev/null +++ b/tests/end-to-end/regression/5351/src/Greeter.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5351; + +final class Greeter +{ + public function greet(): string + { + return 'Hello world!'; + } +} diff --git a/tests/end-to-end/regression/5351/tests/GreeterTest.php b/tests/end-to-end/regression/5351/tests/GreeterTest.php new file mode 100644 index 00000000000..d8131219204 --- /dev/null +++ b/tests/end-to-end/regression/5351/tests/GreeterTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5351; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass('PHPUnit\TestFixture\Issue5351\DoesNotExist')] +final class GreeterTest extends TestCase +{ + public function testGreets(): void + { + $this->assertSame('Hello world!', (new Greeter)->greet()); + } +} diff --git a/tests/end-to-end/regression/5364.phpt b/tests/end-to-end/regression/5364.phpt index 6f7b6ed67a4..28b2281bd9d 100644 --- a/tests/end-to-end/regression/5364.phpt +++ b/tests/end-to-end/regression/5364.phpt @@ -19,7 +19,7 @@ Time: %s, Memory: %s There was 1 PHPUnit test runner warning: -1) Class BarTest cannot be found in %sBarTest.php +1) Class PHPUnit\TestFixture\Issue5364\BarTest declared in %sBarTest.php does not extend PHPUnit\Framework\TestCase WARNINGS! Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/regression/5451.phpt b/tests/end-to-end/regression/5451.phpt index 12bda0bb29e..942cf44bf21 100644 --- a/tests/end-to-end/regression/5451.phpt +++ b/tests/end-to-end/regression/5451.phpt @@ -1,5 +1,9 @@ --TEST-- https://github.com/sebastianbergmann/phpunit/issues/5451 +--SKIPIF-- +run($_SERVER['argv']); ---EXPECTF-- -PHPUnit %s by Sebastian Bergmann and contributors. - -Runtime: %s -Configuration: %s - -.. 2 / 2 (100%) - -Time: %s, Memory: %s - -OK (2 tests, 2 assertions) diff --git a/tests/end-to-end/regression/5487/phpunit.xml b/tests/end-to-end/regression/5487/phpunit.xml deleted file mode 100644 index 1509c385e9d..00000000000 --- a/tests/end-to-end/regression/5487/phpunit.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - tests - tests/foo - tests/bar - - - diff --git a/tests/end-to-end/regression/5487/tests/bar/BarTest.php b/tests/end-to-end/regression/5487/tests/bar/BarTest.php deleted file mode 100644 index 0e68b778c47..00000000000 --- a/tests/end-to-end/regression/5487/tests/bar/BarTest.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Issue5487; - -use PHPUnit\Framework\TestCase; - -final class BarTest extends TestCase -{ - public function testOne(): void - { - $this->assertTrue(true); - } -} diff --git a/tests/end-to-end/regression/5487/tests/foo/FooTest.php b/tests/end-to-end/regression/5487/tests/foo/FooTest.php deleted file mode 100644 index a4e55f37776..00000000000 --- a/tests/end-to-end/regression/5487/tests/foo/FooTest.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TestFixture\Issue5487; - -use PHPUnit\Framework\TestCase; - -final class FooTest extends TestCase -{ - public function testOne(): void - { - $this->assertTrue(true); - } -} diff --git a/tests/end-to-end/regression/5498.phpt b/tests/end-to-end/regression/5498.phpt index 0076a660853..120d32b7f4a 100644 --- a/tests/end-to-end/regression/5498.phpt +++ b/tests/end-to-end/regression/5498.phpt @@ -2,27 +2,22 @@ https://github.com/sebastianbergmann/phpunit/issues/5498 --FILE-- run($_SERVER['argv']); - -print file_get_contents($traceFile); - -unlink($traceFile); --EXPECTF-- PHPUnit Started (%s) Test Runner Configured -Test Suite Loaded (1 test) +Bootstrap Finished (%sTestCase.php) Event Facade Sealed +Test Suite Loaded (1 test) Test Runner Started Test Suite Sorted Test Runner Execution Started (1 test) @@ -35,13 +30,12 @@ Before Test Method Finished: - PHPUnit\TestFixture\Issue5498\Test::parentBefore - PHPUnit\TestFixture\Issue5498\Test::before Test Prepared (PHPUnit\TestFixture\Issue5498\Test::testOne) -Assertion Succeeded (Constraint: is true, Value: true) -Test Passed (PHPUnit\TestFixture\Issue5498\Test::testOne) After Test Method Called (PHPUnit\TestFixture\Issue5498\Test::after) After Test Method Called (PHPUnit\TestFixture\Issue5498\Test::parentAfter) After Test Method Finished: - PHPUnit\TestFixture\Issue5498\Test::after - PHPUnit\TestFixture\Issue5498\Test::parentAfter +Test Passed (PHPUnit\TestFixture\Issue5498\Test::testOne) Test Finished (PHPUnit\TestFixture\Issue5498\Test::testOne) Test Suite Finished (PHPUnit\TestFixture\Issue5498\Test, 1 test) Test Suite Finished (CLI Arguments, 1 test) diff --git a/tests/end-to-end/regression/5561.phpt b/tests/end-to-end/regression/5561.phpt new file mode 100644 index 00000000000..5c033944a5e --- /dev/null +++ b/tests/end-to-end/regression/5561.phpt @@ -0,0 +1,26 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5561 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + + PHPUnit\TestFixture\Issue5561\Issue5561Test::testOne%A +Failed asserting that false is true. +%A +%sIssue5561Test.php:18 + + + diff --git a/tests/end-to-end/regression/5561/Issue5561Test.php b/tests/end-to-end/regression/5561/Issue5561Test.php new file mode 100644 index 00000000000..b81c334dfb7 --- /dev/null +++ b/tests/end-to-end/regression/5561/Issue5561Test.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5561; + +use PHPUnit\Framework\TestCase; + +final class Issue5561Test extends TestCase +{ + protected function setUp(): void + { + $this->assertTrue(false); + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/5567.phpt b/tests/end-to-end/regression/5567.phpt new file mode 100644 index 00000000000..abcd46020ff --- /dev/null +++ b/tests/end-to-end/regression/5567.phpt @@ -0,0 +1,32 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5567 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +F 1 / 1 (100%) + +Time: %s, Memory: %s MB + +There was 1 failure: + +1) PHPUnit\TestFixture\Issue5567\Issue5567Test::testAnythingThatFailsWithRecursiveArray +Failed asserting that Array &0 [ + 'self' => Array &1 [ + 'self' => Array &1, + ], +] is false. + +%sIssue5567Test.php:%d + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1. diff --git a/tests/end-to-end/regression/5567/Issue5567Test.php b/tests/end-to-end/regression/5567/Issue5567Test.php new file mode 100644 index 00000000000..cbbcec219ad --- /dev/null +++ b/tests/end-to-end/regression/5567/Issue5567Test.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5567; + +use PHPUnit\Framework\TestCase; + +final class Issue5567Test extends TestCase +{ + public function testAnythingThatFailsWithRecursiveArray(): void + { + $array = []; + $array['self'] = &$array; + + $this->assertFalse($array); + } +} diff --git a/tests/end-to-end/regression/5574.phpt b/tests/end-to-end/regression/5574.phpt new file mode 100644 index 00000000000..bb1718ffb44 --- /dev/null +++ b/tests/end-to-end/regression/5574.phpt @@ -0,0 +1,33 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5574 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +E 1 / 1 (100%) + +Time: %s, Memory: %s MB + +There was 1 error: + +1) PHPUnit\TestFixture\Issue5574\Issue5574Test::testThrownWrappedThrowablesOutputsCorrectStackTraceForEach +Exception: My exception + +%sIssue5574Test.php:22 + +Caused by +Error: Inner Exception + +%sIssue5574Test.php:21 + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. diff --git a/tests/end-to-end/regression/5574/Issue5574Test.php b/tests/end-to-end/regression/5574/Issue5574Test.php new file mode 100644 index 00000000000..4fde4db842e --- /dev/null +++ b/tests/end-to-end/regression/5574/Issue5574Test.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5574; + +use Error; +use Exception; +use PHPUnit\Framework\TestCase; + +final class Issue5574Test extends TestCase +{ + public function testThrownWrappedThrowablesOutputsCorrectStackTraceForEach(): void + { + $innerException = new Error('Inner Exception'); + $outerException = new Exception('My exception', 0, $innerException); + + throw $outerException; + } +} diff --git a/tests/end-to-end/regression/5592-process-isolation.phpt b/tests/end-to-end/regression/5592-process-isolation.phpt new file mode 100644 index 00000000000..9f7e1b0e3e9 --- /dev/null +++ b/tests/end-to-end/regression/5592-process-isolation.phpt @@ -0,0 +1,55 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/pull/5592 +--FILE-- + null); + +require_once __DIR__ . '/../../bootstrap.php'; +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.FF.FF 6 / 6 (100%) + +Time: %s, Memory: %s + +There were 4 failures: + +1) PHPUnit\TestFixture\Issue5592Test::testAddedErrorHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +2) PHPUnit\TestFixture\Issue5592Test::testRemovedErrorHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +3) PHPUnit\TestFixture\Issue5592Test::testAddedExceptionHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +4) PHPUnit\TestFixture\Issue5592Test::testRemovedExceptionHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +-- + +There was 1 risky test: + +1) PHPUnit\TestFixture\Issue5592Test::testRemovedErrorHandler +Test code or tested code removed error handlers other than its own + +%sIssue5592Test.php:%i + +FAILURES! +Tests: 6, Assertions: 6, Failures: 4, Risky: 1. diff --git a/tests/end-to-end/regression/5592.phpt b/tests/end-to-end/regression/5592.phpt new file mode 100644 index 00000000000..2c3d50bdc09 --- /dev/null +++ b/tests/end-to-end/regression/5592.phpt @@ -0,0 +1,69 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/pull/5592 +--FILE-- + null); + +require_once __DIR__ . '/../../bootstrap.php'; +(new PHPUnit\TextUI\Application)->run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.FF.FF 6 / 6 (100%) + +Time: %s, Memory: %s + +There were 4 failures: + +1) PHPUnit\TestFixture\Issue5592Test::testAddedErrorHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +2) PHPUnit\TestFixture\Issue5592Test::testRemovedErrorHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +3) PHPUnit\TestFixture\Issue5592Test::testAddedExceptionHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +4) PHPUnit\TestFixture\Issue5592Test::testRemovedExceptionHandler +Failed asserting that false is true. + +%sIssue5592Test.php:%i + +-- + +There were 4 risky tests: + +1) PHPUnit\TestFixture\Issue5592Test::testAddedErrorHandler +Test code or tested code did not remove its own error handlers + +%sIssue5592Test.php:%i + +2) PHPUnit\TestFixture\Issue5592Test::testRemovedErrorHandler +Test code or tested code removed error handlers other than its own + +%sIssue5592Test.php:%i + +3) PHPUnit\TestFixture\Issue5592Test::testAddedExceptionHandler +Test code or tested code did not remove its own exception handlers + +%sIssue5592Test.php:%i + +4) PHPUnit\TestFixture\Issue5592Test::testRemovedExceptionHandler +Test code or tested code removed exception handlers other than its own + +%sIssue5592Test.php:%i + +FAILURES! +Tests: 6, Assertions: 6, Failures: 4, Risky: 4. diff --git a/tests/end-to-end/regression/5592/Issue5592Test.php b/tests/end-to-end/regression/5592/Issue5592Test.php new file mode 100644 index 00000000000..7c5cb81dc12 --- /dev/null +++ b/tests/end-to-end/regression/5592/Issue5592Test.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture; + +use function restore_error_handler; +use function restore_exception_handler; +use function set_error_handler; +use function set_exception_handler; +use PHPUnit\Framework\TestCase; + +class Issue5592Test extends TestCase +{ + public function testAddedAndRemovedErrorHandler(): void + { + set_error_handler(static fn () => false); + restore_error_handler(); + $this->assertTrue(true); + } + + public function testAddedErrorHandler(): void + { + set_error_handler(static fn () => false); + $this->assertTrue(false); + } + + public function testRemovedErrorHandler(): void + { + restore_error_handler(); + $this->assertTrue(false); + } + + public function testAddedAndRemovedExceptionHandler(): void + { + set_exception_handler(static fn () => null); + restore_exception_handler(); + $this->assertTrue(true); + } + + public function testAddedExceptionHandler(): void + { + set_exception_handler(static fn () => null); + $this->assertTrue(false); + } + + public function testRemovedExceptionHandler(): void + { + restore_exception_handler(); + $this->assertTrue(false); + } +} diff --git a/tests/end-to-end/regression/5614.phpt b/tests/end-to-end/regression/5614.phpt new file mode 100644 index 00000000000..ac69cf4de35 --- /dev/null +++ b/tests/end-to-end/regression/5614.phpt @@ -0,0 +1,20 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5614 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s MB + +OK (1 test, 1 assertion) diff --git a/tests/end-to-end/regression/5614/Issue5614Test.php b/tests/end-to-end/regression/5614/Issue5614Test.php new file mode 100644 index 00000000000..4866aeb99ad --- /dev/null +++ b/tests/end-to-end/regression/5614/Issue5614Test.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5614; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class Issue5614Test extends TestCase +{ + public static function provideRecursiveArray(): iterable + { + $array = []; + $array[0] = &$array; + + yield [$array]; + } + + #[DataProvider('provideRecursiveArray')] + public function testRecursiveArray(array $array): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/5616.phpt b/tests/end-to-end/regression/5616.phpt new file mode 100644 index 00000000000..0e06b6ea16c --- /dev/null +++ b/tests/end-to-end/regression/5616.phpt @@ -0,0 +1,28 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5616 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +F 1 / 1 (100%) + +Time: %s, Memory: %s MB + +There was 1 failure: + +1) PHPUnit\TestFixture\Issue5616\Issue5616Test::testOne with data set #0 (1, '2', 3.0, true) +Failed asserting that false is true. + +%sIssue5616Test.php:%d + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1. diff --git a/tests/end-to-end/regression/5616/Issue5616Test.php b/tests/end-to-end/regression/5616/Issue5616Test.php new file mode 100644 index 00000000000..48f670ea436 --- /dev/null +++ b/tests/end-to-end/regression/5616/Issue5616Test.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5616; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class Issue5616Test extends TestCase +{ + public static function provider(): array + { + return [ + [1, '2', 3.0, true], + ]; + } + + #[DataProvider('provider')] + public function testOne(int $a, string $b, float $c, bool $d): void + { + $this->assertTrue(false); + } +} diff --git a/tests/end-to-end/regression/5760.phpt b/tests/end-to-end/regression/5760.phpt new file mode 100644 index 00000000000..03cf2c61358 --- /dev/null +++ b/tests/end-to-end/regression/5760.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5760 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +E 1 / 1 (100%) + +Time: %s, Memory: %s + +Issue5760 (PHPUnit\TestFixture\Issue5760\Issue5760) + ✘ One + │ + │ Exception: message + │ + │ %s:19 + │ + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. diff --git a/tests/end-to-end/regression/5760/Issue5760Test.php b/tests/end-to-end/regression/5760/Issue5760Test.php new file mode 100644 index 00000000000..b4babfb9f11 --- /dev/null +++ b/tests/end-to-end/regression/5760/Issue5760Test.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5760; + +use Exception; +use PHPUnit\Framework\TestCase; + +final class Issue5760Test extends TestCase +{ + protected function setUp(): void + { + throw new Exception('message'); + } + + public function testOne(): void + { + } +} diff --git a/tests/end-to-end/regression/5764/5764.phpt b/tests/end-to-end/regression/5764/5764.phpt new file mode 100644 index 00000000000..5c56917a27e --- /dev/null +++ b/tests/end-to-end/regression/5764/5764.phpt @@ -0,0 +1,21 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5764 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +There was 1 PHPUnit test runner warning: + +1) No tests found in class "PHPUnit\TestFixture\Issue5764\Issue5764Test". + +No tests executed! diff --git a/tests/end-to-end/regression/5764/error-handler.php b/tests/end-to-end/regression/5764/error-handler.php new file mode 100644 index 00000000000..0c245d77722 --- /dev/null +++ b/tests/end-to-end/regression/5764/error-handler.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +\set_error_handler(static function (int $err_lvl, string $err_msg, string $err_file, int $err_line): bool +{ + throw new ErrorException($err_msg, 0, $err_lvl, $err_file, $err_line); +}); diff --git a/tests/end-to-end/regression/5764/phpunit.xml b/tests/end-to-end/regression/5764/phpunit.xml new file mode 100644 index 00000000000..e3f2c4b08fd --- /dev/null +++ b/tests/end-to-end/regression/5764/phpunit.xml @@ -0,0 +1,11 @@ + + + + + tests + + + diff --git a/tests/end-to-end/regression/5764/tests/Issue5764Test.php b/tests/end-to-end/regression/5764/tests/Issue5764Test.php new file mode 100644 index 00000000000..c1d2614fd43 --- /dev/null +++ b/tests/end-to-end/regression/5764/tests/Issue5764Test.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5764; + +use PHPUnit\Framework\TestCase; + +final class Issue5764Test extends TestCase +{ +} diff --git a/tests/end-to-end/regression/5771.phpt b/tests/end-to-end/regression/5771.phpt new file mode 100644 index 00000000000..c9e6c1a972a --- /dev/null +++ b/tests/end-to-end/regression/5771.phpt @@ -0,0 +1,24 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5771 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + + PHPUnit\TestFixture\Issue5771\Issue5771Test::testOne%A +Test was run in child process and ended unexpectedly + + + diff --git a/tests/end-to-end/regression/5771/Issue5771Test.php b/tests/end-to-end/regression/5771/Issue5771Test.php new file mode 100644 index 00000000000..33147cdfac6 --- /dev/null +++ b/tests/end-to-end/regression/5771/Issue5771Test.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5771; + +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\TestCase; + +final class Issue5771Test extends TestCase +{ + #[RunInSeparateProcess] + public function testOne(): void + { + exit; + } +} diff --git a/tests/end-to-end/regression/5807.phpt b/tests/end-to-end/regression/5807.phpt new file mode 100644 index 00000000000..ed2f9e6a93d --- /dev/null +++ b/tests/end-to-end/regression/5807.phpt @@ -0,0 +1,22 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5807 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +OK (1 test, 1 assertion) diff --git a/tests/end-to-end/regression/5807/phpunit.xml b/tests/end-to-end/regression/5807/phpunit.xml new file mode 100644 index 00000000000..c298a260cbd --- /dev/null +++ b/tests/end-to-end/regression/5807/phpunit.xml @@ -0,0 +1,17 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/regression/5807/src/Greeter.php b/tests/end-to-end/regression/5807/src/Greeter.php new file mode 100644 index 00000000000..afa3b4afb53 --- /dev/null +++ b/tests/end-to-end/regression/5807/src/Greeter.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5807; + +final class Greeter +{ + public function greet(): string + { + return 'Hello world!'; + } +} diff --git a/tests/end-to-end/regression/5807/tests/GreeterTest.php b/tests/end-to-end/regression/5807/tests/GreeterTest.php new file mode 100644 index 00000000000..b748b91c801 --- /dev/null +++ b/tests/end-to-end/regression/5807/tests/GreeterTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5807; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; + +#[CoversMethod(Greeter::class, 'greet')] +final class GreeterTest extends TestCase +{ + public function testGreets(): void + { + $this->assertSame('Hello world!', (new Greeter)->greet()); + } +} diff --git a/tests/end-to-end/regression/5822.phpt b/tests/end-to-end/regression/5822.phpt new file mode 100644 index 00000000000..2440499ae14 --- /dev/null +++ b/tests/end-to-end/regression/5822.phpt @@ -0,0 +1,22 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/pull/5592 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +D 1 / 1 (100%) + +Time: %s, Memory: %s + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 1. diff --git a/tests/end-to-end/regression/5822/phpunit.xml b/tests/end-to-end/regression/5822/phpunit.xml new file mode 100644 index 00000000000..867b70dc246 --- /dev/null +++ b/tests/end-to-end/regression/5822/phpunit.xml @@ -0,0 +1,15 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/regression/5822/src/.keep b/tests/end-to-end/regression/5822/src/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/end-to-end/regression/5822/tests/Issue5822Test.php b/tests/end-to-end/regression/5822/tests/Issue5822Test.php new file mode 100644 index 00000000000..7e746ffb447 --- /dev/null +++ b/tests/end-to-end/regression/5822/tests/Issue5822Test.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5822; + +use const E_USER_DEPRECATED; +use function call_user_func; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class Issue5822Test extends TestCase +{ + public function testDebugBacktrace(): void + { + $this->callUserFuncExample(); + $this->assertTrue(true); + } + + private function callUserFuncExample(): void + { + call_user_func([$this, 'exampleCallback']); + } + + private function exampleCallback(): void + { + trigger_error('My Deprecation Error', E_USER_DEPRECATED); + } +} diff --git a/tests/end-to-end/regression/5844.phpt b/tests/end-to-end/regression/5844.phpt new file mode 100644 index 00000000000..a4907d37366 --- /dev/null +++ b/tests/end-to-end/regression/5844.phpt @@ -0,0 +1,34 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5844 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Bootstrap Finished (%s5844%sbootstrap.php) +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Issue5844\Issue5844Test, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Issue5844\Issue5844Test::testOne) +Test Considered Risky (PHPUnit\TestFixture\Issue5844\Issue5844Test::testOne) +At least one error handler is not callable outside the scope it was registered in +Test Prepared (PHPUnit\TestFixture\Issue5844\Issue5844Test::testOne) +Test Passed (PHPUnit\TestFixture\Issue5844\Issue5844Test::testOne) +Test Finished (PHPUnit\TestFixture\Issue5844\Issue5844Test::testOne) +Test Suite Finished (PHPUnit\TestFixture\Issue5844\Issue5844Test, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/regression/5844/Issue5844Test.php b/tests/end-to-end/regression/5844/Issue5844Test.php new file mode 100644 index 00000000000..5fe84a20279 --- /dev/null +++ b/tests/end-to-end/regression/5844/Issue5844Test.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5844; + +use PHPUnit\Framework\TestCase; + +final class Issue5844Test extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/5844/bootstrap.php b/tests/end-to-end/regression/5844/bootstrap.php new file mode 100644 index 00000000000..47bdc13d701 --- /dev/null +++ b/tests/end-to-end/regression/5844/bootstrap.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5844; + +use function set_error_handler; + +final class CustomErrorHandler +{ + public function __construct() + { + set_error_handler([$this, 'handleError']); + } + + private function handleError(): void + { + } +} + +new CustomErrorHandler; diff --git a/tests/end-to-end/regression/5875.phpt b/tests/end-to-end/regression/5875.phpt new file mode 100644 index 00000000000..67b658c9044 --- /dev/null +++ b/tests/end-to-end/regression/5875.phpt @@ -0,0 +1,21 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5875 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +..... 5 / 5 (100%) + +Time: %s, Memory: %s + +OK (5 tests, 5 assertions) diff --git a/tests/end-to-end/regression/5875/Issue5875Test.php b/tests/end-to-end/regression/5875/Issue5875Test.php new file mode 100644 index 00000000000..3940e8cf51d --- /dev/null +++ b/tests/end-to-end/regression/5875/Issue5875Test.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5875; + +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; + +final class Issue5875Test extends TestCase +{ + private static int $destructsDone = 0; + + public function __destruct() + { + self::$destructsDone++; + } + + public function testFirstTest(): void + { + $this->assertSame(0, self::$destructsDone); + } + + #[Depends('testFirstTest')] + public function testSecondTest(): void + { + $this->assertSame(1, self::$destructsDone); + } + + #[Depends('testSecondTest')] + #[TestWith([2])] + #[TestWith([3])] + #[TestWith([4])] + public function testThirdTestWhichUsesDataProvider($numberOfTestsBeforeThisOne): void + { + $this->assertSame($numberOfTestsBeforeThisOne, self::$destructsDone); + } +} diff --git a/tests/end-to-end/regression/5884.phpt b/tests/end-to-end/regression/5884.phpt new file mode 100644 index 00000000000..1a1105b2c01 --- /dev/null +++ b/tests/end-to-end/regression/5884.phpt @@ -0,0 +1,36 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5884 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +Time: %s, Memory: %s + +Foo (PHPUnit\TestFixture\Issue5884\Foo) + ⚠ Expect user deprecation message n o t ignoring deprecations + ✔ Expect user deprecation message a n d ignoring deprecations + ✔ Pcre has utf 8 support + ✔ Stream to non writable file with p h p unit error handler + ✔ Stream to non writable file without p h p unit error handler + ✔ Stream to invalid file + +1 test triggered 1 deprecation: + +1) %sFooTest.php:31 +foo + +OK, but there were issues! +Tests: 6, Assertions: 7, Deprecations: 1. diff --git a/tests/end-to-end/regression/5884/phpunit.xml b/tests/end-to-end/regression/5884/phpunit.xml new file mode 100644 index 00000000000..517ddd75e48 --- /dev/null +++ b/tests/end-to-end/regression/5884/phpunit.xml @@ -0,0 +1,21 @@ + + + + + tests + + + diff --git a/tests/end-to-end/regression/5884/src/Foo.php b/tests/end-to-end/regression/5884/src/Foo.php new file mode 100644 index 00000000000..6f973bc4fd7 --- /dev/null +++ b/tests/end-to-end/regression/5884/src/Foo.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5884; + +use function error_get_last; +use function fopen; +use function is_array; +use function preg_match; +use Exception; + +final class Foo +{ + public static function pcreHasUtf8Support() + { + // This regex deliberately has a compile error to demonstrate the issue. + return (bool) @preg_match('/^.[/u', 'a'); + } + + public static function openFile($filename): void + { + // Silenced the PHP native warning in favour of throwing an exception. + $download = @fopen($filename, 'wb'); + + if ($download === false) { + $error = error_get_last(); + + if (!is_array($error)) { + // Shouldn't be possible, but can happen in test situations. + $error = ['message' => 'Failed to open stream']; + } + + throw new Exception($error['message']); + } + } +} diff --git a/tests/end-to-end/regression/5884/tests/FooTest.php b/tests/end-to-end/regression/5884/tests/FooTest.php new file mode 100644 index 00000000000..a1a5ce0213a --- /dev/null +++ b/tests/end-to-end/regression/5884/tests/FooTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5884; + +use const E_USER_DEPRECATED; +use function chmod; +use function file_get_contents; +use function file_put_contents; +use function sys_get_temp_dir; +use function tempnam; +use function trigger_error; +use function unlink; +use Exception; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use PHPUnit\Framework\TestCase; + +final class FooTest extends TestCase +{ + public function testExpectUserDeprecationMessageNOTIgnoringDeprecations(): void + { + $this->expectUserDeprecationMessage('foo'); + + trigger_error('foo', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testExpectUserDeprecationMessageANDIgnoringDeprecations(): void + { + $this->expectUserDeprecationMessage('foo'); + + trigger_error('foo', E_USER_DEPRECATED); + } + + public function testPcreHasUtf8Support(): void + { + $this->assertIsBool(Foo::pcreHasUtf8Support()); + } + + public function testStreamToNonWritableFileWithPHPUnitErrorHandler(): void + { + // Create an unwritable file. + $filename = tempnam(sys_get_temp_dir(), 'RLT'); + + if (file_put_contents($filename, 'foo')) { + chmod($filename, 0o444); + } + + try { + Foo::openFile($filename); + } catch (Exception $e) { + // This "Failed to open stream" exception is expected. + } + + // Now verify the original file is unchanged. + $contents = file_get_contents($filename); + + chmod($filename, 0o755); + unlink($filename); + + $this->assertSame('foo', $contents); + } + + #[WithoutErrorHandler] + public function testStreamToNonWritableFileWithoutPHPUnitErrorHandler(): void + { + // Create an unwritable file. + $filename = tempnam(sys_get_temp_dir(), 'RLT'); + + if (file_put_contents($filename, 'foo')) { + chmod($filename, 0o444); + } + + try { + Foo::openFile($filename); + } catch (Exception $e) { + // This "Failed to open stream" exception is expected. + } + + // Now verify the original file is unchanged. + $contents = file_get_contents($filename); + + chmod($filename, 0o755); + unlink($filename); + + $this->assertSame('foo', $contents); + } + + public function testStreamToInvalidFile(): void + { + $filename = tempnam(sys_get_temp_dir(), 'RLT') . '/missing/directory'; + + $this->expectException(Exception::class); + // First character (F) can be upper or lowercase depending on PHP version. + $this->expectExceptionMessage('ailed to open stream'); + + Foo::openFile($filename); + } +} diff --git a/tests/end-to-end/regression/5891.phpt b/tests/end-to-end/regression/5891.phpt new file mode 100644 index 00000000000..f10afad8236 --- /dev/null +++ b/tests/end-to-end/regression/5891.phpt @@ -0,0 +1,20 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5891 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +.. 2 / 2 (100%) + +Time: %s, Memory: %s MB + +OK (2 tests, 4 assertions) diff --git a/tests/end-to-end/regression/5891/Issue5891Test.php b/tests/end-to-end/regression/5891/Issue5891Test.php new file mode 100644 index 00000000000..d22c5ed846d --- /dev/null +++ b/tests/end-to-end/regression/5891/Issue5891Test.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5891; + +use PHPUnit\Framework\TestCase; + +final class Issue5891Test extends TestCase +{ + public function testVariadicParam(): void + { + $mock = $this->createMock(ArgumentList::class); + $mock + ->method('foo') + ->with($this->callback(static function (...$items): bool + { + self::assertSame([1, 2, 3], $items); + + return true; + })); + + $mock->foo(1, 2, 3); + } + + public function testTwoParametersAndVariadicParam(): void + { + $mock = $this->createMock(TwoParametersAndArgumentList::class); + $mock + ->method('foo') + ->with($this->callback(static function (...$items): bool + { + self::assertSame(['1st', '2nd', '3rd', '4th'], $items); + + return true; + })); + + $mock->foo('1st', '2nd', '3rd', '4th'); + } +} + +interface ArgumentList +{ + public function foo(int ...$items): void; +} + +interface TwoParametersAndArgumentList +{ + public function foo(string $first, string $second, string ...$rest): void; +} diff --git a/tests/end-to-end/regression/5898.phpt b/tests/end-to-end/regression/5898.phpt new file mode 100644 index 00000000000..b65a285f62d --- /dev/null +++ b/tests/end-to-end/regression/5898.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5898 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (%s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (%s5898.phpt, 1 test) +Test Preparation Started (%s5898.phpt) +Test Prepared (%s5898.phpt) +Child Process Started +Child Process Finished +Test Passed (%s5898.phpt) +Test Finished (%s5898.phpt) +Test Suite Finished (%s5898.phpt, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/regression/5898/5898.phpt b/tests/end-to-end/regression/5898/5898.phpt new file mode 100644 index 00000000000..3cdf2a2d8ee --- /dev/null +++ b/tests/end-to-end/regression/5898/5898.phpt @@ -0,0 +1,7 @@ +--TEST-- +PHPT test that passes +--FILE-- +run($_SERVER['argv']); + +unlink($file); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +There were errors: + +The data provider specified for PHPUnit\TestFixture\Issue5908\Issue5908Test::testOne is invalid +message diff --git a/tests/end-to-end/regression/5908-list-tests.phpt b/tests/end-to-end/regression/5908-list-tests.phpt new file mode 100644 index 00000000000..88757a15215 --- /dev/null +++ b/tests/end-to-end/regression/5908-list-tests.phpt @@ -0,0 +1,19 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5908 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +There were errors: + +The data provider specified for PHPUnit\TestFixture\Issue5908\Issue5908Test::testOne is invalid +message diff --git a/tests/end-to-end/regression/5908/Issue5908Test.php b/tests/end-to-end/regression/5908/Issue5908Test.php new file mode 100644 index 00000000000..e3e6658d2a6 --- /dev/null +++ b/tests/end-to-end/regression/5908/Issue5908Test.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5908; + +use Exception; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +final class Issue5908Test extends TestCase +{ + public static function provider(): array + { + throw new Exception('message'); + } + + #[DataProvider('provider')] + public function testOne(int $value): void + { + } +} diff --git a/tests/end-to-end/regression/5943.phpt b/tests/end-to-end/regression/5943.phpt new file mode 100644 index 00000000000..6745c8946c8 --- /dev/null +++ b/tests/end-to-end/regression/5943.phpt @@ -0,0 +1,20 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5943 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Available test groups: + - bar (1 test) + - barbara (1 test) + - baz (1 test) + - foo (1 test) diff --git a/tests/end-to-end/regression/5943/phpunit.xml b/tests/end-to-end/regression/5943/phpunit.xml new file mode 100644 index 00000000000..bb66b5abc66 --- /dev/null +++ b/tests/end-to-end/regression/5943/phpunit.xml @@ -0,0 +1,11 @@ + + + + + tests/a + tests/b + + + diff --git a/tests/end-to-end/regression/5943/tests/a/OneTest.php b/tests/end-to-end/regression/5943/tests/a/OneTest.php new file mode 100644 index 00000000000..1b5ead72569 --- /dev/null +++ b/tests/end-to-end/regression/5943/tests/a/OneTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5943; + +use PHPUnit\Framework\TestCase; + +final class OneTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/5943/tests/b/TwoTest.php b/tests/end-to-end/regression/5943/tests/b/TwoTest.php new file mode 100644 index 00000000000..6479eacece7 --- /dev/null +++ b/tests/end-to-end/regression/5943/tests/b/TwoTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5943; + +use PHPUnit\Framework\TestCase; + +final class TwoTest extends TestCase +{ + public function testTwo(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/5949.phpt b/tests/end-to-end/regression/5949.phpt new file mode 100644 index 00000000000..4bbe1b277d0 --- /dev/null +++ b/tests/end-to-end/regression/5949.phpt @@ -0,0 +1,41 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5949 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +........ 8 / 8 (100%) + +Time: %s, Memory: %s + +Issue5949 (PHPUnit\TestFixture\Issue5949\Issue5949) + ✔ Test 1. No dollar sign. + + ✔ Test 2. No dollar sign. + + ✔ Test 3. Dollar sign ($). + + ✔ Test 4. No dollar sign. + + ✔ Test 5. Dollar $ sign. + More text. + + ✔ Test 6. No dollar sign. + + ✔ Test 7. No dollar sign. + + ✔ Test 8. No dollar sign. + + +OK (8 tests, 8 assertions) diff --git a/tests/end-to-end/regression/5949/Issue5949Test.php b/tests/end-to-end/regression/5949/Issue5949Test.php new file mode 100644 index 00000000000..28610250275 --- /dev/null +++ b/tests/end-to-end/regression/5949/Issue5949Test.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5949; + +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +class Issue5949Test extends TestCase +{ + #[TestDox("Test 1. No dollar sign.\n")] + public function test1(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 2. No dollar sign.\n")] + public function test2(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 3. Dollar sign (\$).\n")] + public function test3(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 4. No dollar sign.\n")] + public function test4(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 5. Dollar \$ sign.\n More text.\n")] + public function test5(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 6. No dollar sign.\n")] + public function test6(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 7. No dollar sign.\n")] + public function test7(): void + { + $this->assertTrue(true); + } + + #[TestDox("Test 8. No dollar sign.\n")] + public function test8(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/5965.phpt b/tests/end-to-end/regression/5965.phpt new file mode 100644 index 00000000000..3d61438eb7b --- /dev/null +++ b/tests/end-to-end/regression/5965.phpt @@ -0,0 +1,35 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5965 +--SKIPIF-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (1 test) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (1 test) +Test Suite Started (PHPUnit\TestFixture\Issue5891\Issue5965Test, 1 test) +Test Preparation Started (PHPUnit\TestFixture\Issue5891\Issue5965Test::testOne) +Test Prepared (PHPUnit\TestFixture\Issue5891\Issue5965Test::testOne) +Test Errored (PHPUnit\TestFixture\Issue5891\Issue5965Test::testOne) +(exception code: HY000) +Test Finished (PHPUnit\TestFixture\Issue5891\Issue5965Test::testOne) +Test Suite Finished (PHPUnit\TestFixture\Issue5891\Issue5965Test, 1 test) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 2) diff --git a/tests/end-to-end/regression/5965/Issue5965Test.php b/tests/end-to-end/regression/5965/Issue5965Test.php new file mode 100644 index 00000000000..2345a1fccee --- /dev/null +++ b/tests/end-to-end/regression/5965/Issue5965Test.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5891; + +use IteratorAggregate; +use PDOException; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +use Traversable; + +#[RequiresPhpExtension('pdo')] +final class Issue5965Test extends TestCase +{ + public function testOne(): void + { + $exception = new PDOException; + $reflector = new ReflectionClass($exception); + + $property = $reflector->getProperty('code'); + $property->setValue($exception, 'HY000'); + + $this->assertIsString($exception->getCode()); + + $iterator = new class($exception) implements IteratorAggregate + { + public PDOException $exception; + + public function __construct($exception) + { + $this->exception = $exception; + } + + public function getIterator(): Traversable + { + throw $this->exception; + } + }; + + $this->assertCount(0, $iterator); + } +} diff --git a/tests/end-to-end/regression/5976.phpt b/tests/end-to-end/regression/5976.phpt new file mode 100644 index 00000000000..66d70c84127 --- /dev/null +++ b/tests/end-to-end/regression/5976.phpt @@ -0,0 +1,30 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5976 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +E 1 / 1 (100%) + +Time: %s, Memory: %s + +These before-first-test methods errored: + +1) PHPUnit\TestFixture\Issue5967\Issue5976Test::setUpBeforeClass +Exception: message + +%sIssue5976Test.php:%d + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. diff --git a/tests/end-to-end/regression/5976/Issue5976Test.php b/tests/end-to-end/regression/5976/Issue5976Test.php new file mode 100644 index 00000000000..adbae81ecc4 --- /dev/null +++ b/tests/end-to-end/regression/5976/Issue5976Test.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5967; + +use Exception; +use PHPUnit\Framework\TestCase; + +final class Issue5976Test extends TestCase +{ + /** + * @throws Exception + */ + public static function setUpBeforeClass(): void + { + throw new Exception('message'); + } + + public function testOne(): void + { + } +} diff --git a/tests/end-to-end/regression/6094.phpt b/tests/end-to-end/regression/6094.phpt new file mode 100644 index 00000000000..d90631a65c6 --- /dev/null +++ b/tests/end-to-end/regression/6094.phpt @@ -0,0 +1,29 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6094 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 error: + +1) PHPUnit\TestFixture\Issue6094\Issue6094Test +Exception: message + +%sIssue6094Test.php:19 + +ERRORS! +Tests: 1, Assertions: 1, Errors: 1. diff --git a/tests/end-to-end/regression/6094/Issue6094Test.php b/tests/end-to-end/regression/6094/Issue6094Test.php new file mode 100644 index 00000000000..c213f926373 --- /dev/null +++ b/tests/end-to-end/regression/6094/Issue6094Test.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6094; + +use Exception; +use PHPUnit\Framework\TestCase; + +final class Issue6094Test extends TestCase +{ + public static function tearDownAfterClass(): void + { + throw new Exception('message'); + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/6095.phpt b/tests/end-to-end/regression/6095.phpt new file mode 100644 index 00000000000..33b0898e028 --- /dev/null +++ b/tests/end-to-end/regression/6095.phpt @@ -0,0 +1,29 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6095 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +F 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 failure: + +1) PHPUnit\TestFixture\Issue6095\Issue6095Test::testOne +PHPUnit\TestFixture\MockObject\AnInterface::doSomething(): bool was not expected to be called more than once. + +%sIssue6095Test.php:26 + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1. diff --git a/tests/end-to-end/regression/6095/Issue6095Test.php b/tests/end-to-end/regression/6095/Issue6095Test.php new file mode 100644 index 00000000000..9c737beaca1 --- /dev/null +++ b/tests/end-to-end/regression/6095/Issue6095Test.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6095; + +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\AnInterface; + +final class Issue6095Test extends TestCase +{ + public function testOne(): void + { + $mock = $this->createMock(AnInterface::class); + + $mock + ->expects($this->once()) + ->method('doSomething'); + + $mock->doSomething(); + $mock->doSomething(); + } +} diff --git a/tests/end-to-end/regression/6098.phpt b/tests/end-to-end/regression/6098.phpt new file mode 100644 index 00000000000..400cca6ba30 --- /dev/null +++ b/tests/end-to-end/regression/6098.phpt @@ -0,0 +1,23 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6098 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + + output + + + diff --git a/tests/end-to-end/regression/6098/Issue6098Test.php b/tests/end-to-end/regression/6098/Issue6098Test.php new file mode 100644 index 00000000000..4ef2f374411 --- /dev/null +++ b/tests/end-to-end/regression/6098/Issue6098Test.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6098; + +use PHPUnit\Framework\TestCase; + +final class Issue6098Test extends TestCase +{ + public function testOne(): void + { + print 'output'; + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/6100.phpt b/tests/end-to-end/regression/6100.phpt new file mode 100644 index 00000000000..f4be3532fb7 --- /dev/null +++ b/tests/end-to-end/regression/6100.phpt @@ -0,0 +1,37 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6100 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Event Facade Sealed +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (PHPUnit\TestFixture\Issue6100\Issue6100Test, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\Issue6100\Issue6100Test::testOne) +Test Prepared (PHPUnit\TestFixture\Issue6100\Issue6100Test::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\Issue6100\Issue6100Test::testOne, unknown if issue was triggered in first-party code or third-party code, suppressed using operator) in %s:%d +test +Test Passed (PHPUnit\TestFixture\Issue6100\Issue6100Test::testOne) +Test Finished (PHPUnit\TestFixture\Issue6100\Issue6100Test::testOne) +Test Preparation Started (PHPUnit\TestFixture\Issue6100\Issue6100Test::testTwo) +Test Prepared (PHPUnit\TestFixture\Issue6100\Issue6100Test::testTwo) +Test Passed (PHPUnit\TestFixture\Issue6100\Issue6100Test::testTwo) +Test Finished (PHPUnit\TestFixture\Issue6100\Issue6100Test::testTwo) +Test Suite Finished (PHPUnit\TestFixture\Issue6100\Issue6100Test, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/regression/6100/Issue6100Test.php b/tests/end-to-end/regression/6100/Issue6100Test.php new file mode 100644 index 00000000000..7432d150e17 --- /dev/null +++ b/tests/end-to-end/regression/6100/Issue6100Test.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6100; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class Issue6100Test extends TestCase +{ + public function testOne(): void + { + @trigger_error('test', E_USER_DEPRECATED); + + $this->assertTrue(true); + } + + public function testTwo(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/6102.phpt b/tests/end-to-end/regression/6102.phpt new file mode 100644 index 00000000000..5b6c346b8f4 --- /dev/null +++ b/tests/end-to-end/regression/6102.phpt @@ -0,0 +1,39 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6102 +--XFAIL-- +https://github.com/sebastianbergmann/phpunit/issues/6102 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +..FF..FF 8 / 8 (100%) + +Time: %s, Memory: %s + +There were 4 failures: + +1) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnExactDeprecationMessageWorksWhenExpectedDeprecationIsNotTriggered +Expected deprecation with message "message" was not triggered + +2) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnExactDeprecationMessageWorksWhenUnexpectedDeprecationIsTriggered +Expected deprecation with message "message" was not triggered + +3) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenExpectedDeprecationIsNotTriggered +Expected deprecation with message matching regular expression "/message/" was not triggered + +4) PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testExpectationOnDeprecationMessageMatchingRegularExpressionWorksWhenUnepectedDeprecationIsTriggered +Expected deprecation with message matching regular expression "/message/" was not triggered + +FAILURES! +Tests: 8, Assertions: 10, Failures: 4. diff --git a/tests/end-to-end/regression/6103.phpt b/tests/end-to-end/regression/6103.phpt new file mode 100644 index 00000000000..cbebf70c862 --- /dev/null +++ b/tests/end-to-end/regression/6103.phpt @@ -0,0 +1,21 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6103 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +*. 1 / 1 (100%) + +Time: %s, Memory: %s + +OK (1 test, 1 assertion) diff --git a/tests/end-to-end/regression/6103/Issue6103Test.php b/tests/end-to-end/regression/6103/Issue6103Test.php new file mode 100644 index 00000000000..be6488a2644 --- /dev/null +++ b/tests/end-to-end/regression/6103/Issue6103Test.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6103; + +use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use PHPUnit\Framework\TestCase; + +final class Issue6103Test extends TestCase +{ + #[RunInSeparateProcess] + public function testOne(): void + { + print '*'; + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/6109.phpt b/tests/end-to-end/regression/6109.phpt new file mode 100644 index 00000000000..090d73c970a --- /dev/null +++ b/tests/end-to-end/regression/6109.phpt @@ -0,0 +1,23 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6109 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- + + + + + + + + diff --git a/tests/end-to-end/regression/6109/Issue6109Test.php b/tests/end-to-end/regression/6109/Issue6109Test.php new file mode 100644 index 00000000000..131ab6bdb28 --- /dev/null +++ b/tests/end-to-end/regression/6109/Issue6109Test.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6109; + +use PHPUnit\Framework\TestCase; + +final class Issue6109Test extends TestCase +{ + protected function setUp(): void + { + print '*'; + + $this->markTestSkipped('message'); + } + + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/6115.phpt b/tests/end-to-end/regression/6115.phpt new file mode 100644 index 00000000000..36342e2e6e3 --- /dev/null +++ b/tests/end-to-end/regression/6115.phpt @@ -0,0 +1,25 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6115 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +Issue6115 (PHPUnit\TestFixture\Issue6115\Issue6115) + ✔ 1 + +OK (1 test, 1 assertion) diff --git a/tests/end-to-end/regression/6115/Issue6115Test.php b/tests/end-to-end/regression/6115/Issue6115Test.php new file mode 100644 index 00000000000..c323b05511d --- /dev/null +++ b/tests/end-to-end/regression/6115/Issue6115Test.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6115; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +enum Enumeration: int +{ + case A = 1; +} + +final class Issue6115Test extends TestCase +{ + public static function provider(): array + { + return [ + [ + Enumeration::A, + ], + ]; + } + + #[DataProvider('provider')] + #[TestDox('$enumeration')] + public function testOne(Enumeration $enumeration): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/regression/6138.phpt b/tests/end-to-end/regression/6138.phpt new file mode 100644 index 00000000000..ad8f3cb5a1f --- /dev/null +++ b/tests/end-to-end/regression/6138.phpt @@ -0,0 +1,38 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6138 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +F 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 failure: + +1) PHPUnit\TestFixture\Issue6138\Issue6138Test::testOne +Expectation failed for method name is "m" when invoked 1 time +Parameter 0 for invocation PHPUnit\TestFixture\Issue6138\I::m(PHPUnit\TestFixture\Issue6138\C Object (...)): void does not match expected value. +Failed asserting that two objects are equal. +--- Expected ++++ Actual +@@ @@ + PHPUnit\TestFixture\Issue6138\C Object ( +- 'foo' => 'bar' ++ 'foo' => 'baz' + ) + +%sIssue6138Test.php:%d + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1. diff --git a/tests/end-to-end/regression/6138/Issue6138Test.php b/tests/end-to-end/regression/6138/Issue6138Test.php new file mode 100644 index 00000000000..36fa00341ff --- /dev/null +++ b/tests/end-to-end/regression/6138/Issue6138Test.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6138; + +use PHPUnit\Framework\TestCase; + +final class C +{ + private string $foo; + + public function __construct(string $foo) + { + $this->foo = $foo; + } +} + +interface I +{ + public function m(C $c): void; +} + +final class Issue6138Test extends TestCase +{ + public function testOne(): void + { + $i = $this->createMock(I::class); + + $i->expects($this->once())->method('m')->with(new C('bar')); + + $i->m(new C('baz')); + } +} diff --git a/tests/end-to-end/regression/6142.phpt b/tests/end-to-end/regression/6142.phpt new file mode 100644 index 00000000000..52e695b40f8 --- /dev/null +++ b/tests/end-to-end/regression/6142.phpt @@ -0,0 +1,38 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6142 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +F 1 / 1 (100%) + +Time: %s, Memory: %s + +There was 1 failure: + +1) PHPUnit\TestFixture\Issue6142\Issue6142Test::testOne +Failed asserting that '{"key": false}\n +' matches JSON string "{"key": true} +". +--- Expected ++++ Actual +@@ @@ + { +- "key": true ++ "key": false + } + +%sIssue6142Test.php:%d + +FAILURES! +Tests: 1, Assertions: 7, Failures: 1. diff --git a/tests/end-to-end/regression/6142/Issue6142Test.php b/tests/end-to-end/regression/6142/Issue6142Test.php new file mode 100644 index 00000000000..246189ca935 --- /dev/null +++ b/tests/end-to-end/regression/6142/Issue6142Test.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6142; + +use PHPUnit\Framework\TestCase; + +final class Issue6142Test extends TestCase +{ + public function testOne(): void + { + $expected = __DIR__ . '/expected.json'; + $actual = __DIR__ . '/actual.json'; + + $this->assertJsonFileEqualsJsonFile($expected, $actual); + } +} diff --git a/tests/end-to-end/regression/6142/actual.json b/tests/end-to-end/regression/6142/actual.json new file mode 100644 index 00000000000..f96f9eb3185 --- /dev/null +++ b/tests/end-to-end/regression/6142/actual.json @@ -0,0 +1 @@ +{"key": false} diff --git a/tests/end-to-end/regression/6142/expected.json b/tests/end-to-end/regression/6142/expected.json new file mode 100644 index 00000000000..95ff2f8c32c --- /dev/null +++ b/tests/end-to-end/regression/6142/expected.json @@ -0,0 +1 @@ +{"key": true} diff --git a/tests/end-to-end/regression/6173.phpt b/tests/end-to-end/regression/6173.phpt new file mode 100644 index 00000000000..df6fce927b2 --- /dev/null +++ b/tests/end-to-end/regression/6173.phpt @@ -0,0 +1,31 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/6173 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +hello, success! +.hello, fail! +F 2 / 2 (100%) + +Time: %s, Memory: %s + +There was 1 failure: + +1) PHPUnit\TestFixture\Issue6173\Issue6173Test::test_log_fail +Failed asserting that false is true. + +%sIssue6173Test.php:%d + +FAILURES! +Tests: 2, Assertions: 2, Failures: 1. diff --git a/tests/end-to-end/regression/6173/Issue6173Test.php b/tests/end-to-end/regression/6173/Issue6173Test.php new file mode 100644 index 00000000000..87b34efb218 --- /dev/null +++ b/tests/end-to-end/regression/6173/Issue6173Test.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue6173; + +use function error_log; +use PHPUnit\Framework\TestCase; + +final class Issue6173Test extends TestCase +{ + public function test_log_success(): void + { + error_log('hello, success!'); + $this->assertTrue(true); + } + + public function test_log_fail(): void + { + error_log('hello, fail!'); + $this->assertTrue(false); + } +} diff --git a/tests/end-to-end/regression/765.phpt b/tests/end-to-end/regression/765.phpt index 2cf908286b5..0aac3d9e8d1 100644 --- a/tests/end-to-end/regression/765.phpt +++ b/tests/end-to-end/regression/765.phpt @@ -1,5 +1,9 @@ --TEST-- GH-765: Fatal error triggered in PHPUnit when exception is thrown in data provider of a test with a dependency +--SKIPIF-- +assertTrue(true); } - /** - * @depends testDependee - * - * @dataProvider dependentProvider - */ + #[Depends('testDependee')] + #[DataProvider('dependentProvider')] public function testDependent($a): void { $this->assertTrue(true); diff --git a/tests/end-to-end/regression/797/Issue797Test.php b/tests/end-to-end/regression/797/Issue797Test.php index 4772145e148..07ed61ac5b7 100644 --- a/tests/end-to-end/regression/797/Issue797Test.php +++ b/tests/end-to-end/regression/797/Issue797Test.php @@ -9,11 +9,10 @@ */ namespace PHPUnit\TestFixture; +use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\TestCase; -/** - * @preserveGlobalState enabled - */ +#[PreserveGlobalState(true)] class Issue797Test extends TestCase { public function testBootstrapPhpIsExecutedInIsolation(): void diff --git a/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/phpunit.xml b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/phpunit.xml new file mode 100644 index 00000000000..20c26dddd85 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/phpunit.xml @@ -0,0 +1,16 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/src/.gitkeep b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/src/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/tests/DeprecationInTestCodeTest.php b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/tests/DeprecationInTestCodeTest.php new file mode 100644 index 00000000000..1213b7f2d2e --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code-ignored/tests/DeprecationInTestCodeTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class DeprecationInTestCodeTest extends TestCase +{ + public function testOne(): void + { + trigger_error('message', E_USER_DEPRECATED); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/phpunit.xml b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/phpunit.xml new file mode 100644 index 00000000000..a501786e420 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/phpunit.xml @@ -0,0 +1,16 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/src/.gitkeep b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/src/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/tests/DeprecationInTestCodeTest.php b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/tests/DeprecationInTestCodeTest.php new file mode 100644 index 00000000000..1213b7f2d2e --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/deprecation-in-test-code/tests/DeprecationInTestCodeTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class DeprecationInTestCodeTest extends TestCase +{ + public function testOne(): void + { + trigger_error('message', E_USER_DEPRECATED); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/invalid-deprecation-trigger/phpunit.xml b/tests/end-to-end/self-direct-indirect/_files/invalid-deprecation-trigger/phpunit.xml new file mode 100644 index 00000000000..f2237a09c1d --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/invalid-deprecation-trigger/phpunit.xml @@ -0,0 +1,18 @@ + + + + + tests + + + + + + does_not_exist + invalid-string + DoesNotExist::doesNotExist + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/invalid-deprecation-trigger/tests/ExampleTest.php b/tests/end-to-end/self-direct-indirect/_files/invalid-deprecation-trigger/tests/ExampleTest.php new file mode 100644 index 00000000000..e99f90f5b79 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/invalid-deprecation-trigger/tests/ExampleTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class ExampleTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/phpunit.xml b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/phpunit.xml new file mode 100644 index 00000000000..5c0fcac4da1 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/phpunit.xml @@ -0,0 +1,17 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/src/FirstPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/src/FirstPartyClass.php new file mode 100644 index 00000000000..aca0b8b8d05 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/src/FirstPartyClass.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; + +final class FirstPartyClass +{ + public function method(): true + { + (new ThirdPartyClass)->method(); + + @trigger_error('deprecation in first-party code', E_USER_DEPRECATED); + + return true; + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/tests/FirstPartyClassTest.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/tests/FirstPartyClassTest.php new file mode 100644 index 00000000000..1abafab73ab --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/tests/FirstPartyClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class FirstPartyClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue((new FirstPartyClass)->method()); + } + + public function testTwo(): void + { + $this->assertTrue((new ThirdPartyClass)->anotherMethod()); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/vendor/ThirdPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/vendor/ThirdPartyClass.php new file mode 100644 index 00000000000..b373e4addad --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/vendor/ThirdPartyClass.php @@ -0,0 +1,15 @@ +method(); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/vendor/autoload.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/vendor/autoload.php new file mode 100644 index 00000000000..95f0626f283 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct-indirect/vendor/autoload.php @@ -0,0 +1,3 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/src/FirstPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/src/FirstPartyClass.php new file mode 100644 index 00000000000..aca0b8b8d05 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/src/FirstPartyClass.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; + +final class FirstPartyClass +{ + public function method(): true + { + (new ThirdPartyClass)->method(); + + @trigger_error('deprecation in first-party code', E_USER_DEPRECATED); + + return true; + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/tests/FirstPartyClassTest.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/tests/FirstPartyClassTest.php new file mode 100644 index 00000000000..1abafab73ab --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/tests/FirstPartyClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class FirstPartyClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue((new FirstPartyClass)->method()); + } + + public function testTwo(): void + { + $this->assertTrue((new ThirdPartyClass)->anotherMethod()); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/vendor/ThirdPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/vendor/ThirdPartyClass.php new file mode 100644 index 00000000000..b373e4addad --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/vendor/ThirdPartyClass.php @@ -0,0 +1,15 @@ +method(); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/vendor/autoload.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/vendor/autoload.php new file mode 100644 index 00000000000..95f0626f283 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self-direct/vendor/autoload.php @@ -0,0 +1,3 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/src/FirstPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/src/FirstPartyClass.php new file mode 100644 index 00000000000..aca0b8b8d05 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/src/FirstPartyClass.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; + +final class FirstPartyClass +{ + public function method(): true + { + (new ThirdPartyClass)->method(); + + @trigger_error('deprecation in first-party code', E_USER_DEPRECATED); + + return true; + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/tests/FirstPartyClassTest.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/tests/FirstPartyClassTest.php new file mode 100644 index 00000000000..1abafab73ab --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/tests/FirstPartyClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class FirstPartyClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue((new FirstPartyClass)->method()); + } + + public function testTwo(): void + { + $this->assertTrue((new ThirdPartyClass)->anotherMethod()); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/vendor/ThirdPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/vendor/ThirdPartyClass.php new file mode 100644 index 00000000000..b373e4addad --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/vendor/ThirdPartyClass.php @@ -0,0 +1,15 @@ +method(); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/vendor/autoload.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/vendor/autoload.php new file mode 100644 index 00000000000..95f0626f283 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation-report-self/vendor/autoload.php @@ -0,0 +1,3 @@ + + + + + tests + + + + + + src + + + diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation/src/FirstPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/src/FirstPartyClass.php new file mode 100644 index 00000000000..aca0b8b8d05 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/src/FirstPartyClass.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use const E_USER_DEPRECATED; +use function trigger_error; + +final class FirstPartyClass +{ + public function method(): true + { + (new ThirdPartyClass)->method(); + + @trigger_error('deprecation in first-party code', E_USER_DEPRECATED); + + return true; + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation/tests/FirstPartyClassTest.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/tests/FirstPartyClassTest.php new file mode 100644 index 00000000000..1abafab73ab --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/tests/FirstPartyClassTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\SelfDirectIndirect; + +use PHPUnit\Framework\TestCase; + +final class FirstPartyClassTest extends TestCase +{ + public function testOne(): void + { + $this->assertTrue((new FirstPartyClass)->method()); + } + + public function testTwo(): void + { + $this->assertTrue((new ThirdPartyClass)->anotherMethod()); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation/vendor/ThirdPartyClass.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/vendor/ThirdPartyClass.php new file mode 100644 index 00000000000..b373e4addad --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/vendor/ThirdPartyClass.php @@ -0,0 +1,15 @@ +method(); + } +} diff --git a/tests/end-to-end/self-direct-indirect/_files/user-deprecation/vendor/autoload.php b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/vendor/autoload.php new file mode 100644 index 00000000000..95f0626f283 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/_files/user-deprecation/vendor/autoload.php @@ -0,0 +1,3 @@ +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %sphpunit.xml + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +OK (1 test, 1 assertion) diff --git a/tests/end-to-end/self-direct-indirect/deprecation-in-test-code.phpt b/tests/end-to-end/self-direct-indirect/deprecation-in-test-code.phpt new file mode 100644 index 00000000000..e9d2738339e --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/deprecation-in-test-code.phpt @@ -0,0 +1,29 @@ +--TEST-- +Deprecation in test code is reported when it is configured to be reported +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %sphpunit.xml + +D 1 / 1 (100%) + +Time: %s, Memory: %s + +1 test triggered 1 deprecation: + +1) %sDeprecationInTestCodeTest.php:20 +message + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 1. diff --git a/tests/end-to-end/self-direct-indirect/invalid-deprecation-trigger.phpt b/tests/end-to-end/self-direct-indirect/invalid-deprecation-trigger.phpt new file mode 100644 index 00000000000..5d5e7dd1746 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/invalid-deprecation-trigger.phpt @@ -0,0 +1,32 @@ +--TEST-- +Test Runner warnings are displayed correctly when invalid deprecation triggers are configured in the XML configuration file +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s + +There were 3 PHPUnit test runner warnings: + +1) Function does_not_exist cannot be configured as a deprecation trigger because it is not declared + +2) invalid-string cannot be configured as a deprecation trigger because it is not in ClassName::methodName format + +3) Method DoesNotExist::doesNotExist cannot be configured as a deprecation trigger because it is not declared + +WARNINGS! +Tests: 1, Assertions: 1, Warnings: 3. diff --git a/tests/end-to-end/self-direct-indirect/user-deprecation-report-self-direct-indirect.phpt b/tests/end-to-end/self-direct-indirect/user-deprecation-report-self-direct-indirect.phpt new file mode 100644 index 00000000000..d0bd429c5e1 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/user-deprecation-report-self-direct-indirect.phpt @@ -0,0 +1,48 @@ +--TEST-- +All deprecations are reported when deprecations triggered from first-party code and deprecations triggered from third-party code should be reported +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +DD 2 / 2 (100%) + +Time: %s, Memory: %s + +2 tests triggered 2 deprecations: + +1) %sThirdPartyClass.php:8 +deprecation in third-party code + +Triggered by: + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne + %s:16 + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo + %s:21 + +2) %sFirstPartyClass.php:21 +deprecation in first-party code + +Triggered by: + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne + %sFirstPartyClassTest.php:16 + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo + %sFirstPartyClassTest.php:21 + +OK, but there were issues! +Tests: 2, Assertions: 2, Deprecations: 2. diff --git a/tests/end-to-end/self-direct-indirect/user-deprecation-report-self-direct.phpt b/tests/end-to-end/self-direct-indirect/user-deprecation-report-self-direct.phpt new file mode 100644 index 00000000000..cc3f8a3e302 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/user-deprecation-report-self-direct.phpt @@ -0,0 +1,45 @@ +--TEST-- +The correct deprecations are reported when deprecations triggered from third-party code should be ignored +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +DD 2 / 2 (100%) + +Time: %s, Memory: %s + +2 tests triggered 2 deprecations: + +1) %sThirdPartyClass.php:8 +deprecation in third-party code + +Triggered by: + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne + %s:16 + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo + %s:21 + +2) %sFirstPartyClass.php:21 +deprecation in first-party code + +Triggered by: + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne + %sFirstPartyClassTest.php:16 + +OK, but there were issues! +Tests: 2, Assertions: 2, Deprecations: 2. diff --git a/tests/end-to-end/self-direct-indirect/user-deprecation-report-self.phpt b/tests/end-to-end/self-direct-indirect/user-deprecation-report-self.phpt new file mode 100644 index 00000000000..52362fca891 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/user-deprecation-report-self.phpt @@ -0,0 +1,34 @@ +--TEST-- +The correct deprecations are reported when only deprecations in first-party code triggered from first-party code should be reported +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +D. 2 / 2 (100%) + +Time: %s, Memory: %s + +1 test triggered 1 deprecation: + +1) %sFirstPartyClass.php:21 +deprecation in first-party code + +Triggered by: + +* PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne + %sFirstPartyClassTest.php:16 + +OK, but there were issues! +Tests: 2, Assertions: 2, Deprecations: 1. diff --git a/tests/end-to-end/self-direct-indirect/user-deprecation.phpt b/tests/end-to-end/self-direct-indirect/user-deprecation.phpt new file mode 100644 index 00000000000..0d9bb5cf2a3 --- /dev/null +++ b/tests/end-to-end/self-direct-indirect/user-deprecation.phpt @@ -0,0 +1,46 @@ +--TEST-- +The right events are emitted in the right order for a test that runs code which triggers E_USER_DEPRECATED +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Bootstrap Finished (%sautoload.php) +Event Facade Sealed +Test Suite Loaded (2 tests) +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (2 tests) +Test Suite Started (%sphpunit.xml, 2 tests) +Test Suite Started (default, 2 tests) +Test Suite Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 2 tests) +Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by first-party code calling into third-party code, suppressed using operator) in %s:%d +deprecation in third-party code +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by first-party code calling into first-party code, suppressed using operator) in %s:%d +deprecation in first-party code +Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo, issue triggered by first-party code calling into third-party code, suppressed using operator) in %s:%d +deprecation in third-party code +Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo, issue triggered by third-party code, suppressed using operator) in %s:%d +deprecation in first-party code +Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testTwo) +Test Suite Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 2 tests) +Test Suite Finished (default, 2 tests) +Test Suite Finished (%sphpunit.xml, 2 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 0) diff --git a/tests/end-to-end/testdox/_files/DeprecationTest.php b/tests/end-to-end/testdox/_files/DeprecationTest.php new file mode 100644 index 00000000000..14377d714c6 --- /dev/null +++ b/tests/end-to-end/testdox/_files/DeprecationTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestDox; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class DeprecationTest extends TestCase +{ + public function testDeprecation(): void + { + trigger_error('deprecation', E_USER_DEPRECATED); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/testdox/_files/NoticeTest.php b/tests/end-to-end/testdox/_files/NoticeTest.php new file mode 100644 index 00000000000..94f71d4d02e --- /dev/null +++ b/tests/end-to-end/testdox/_files/NoticeTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestDox; + +use const E_USER_NOTICE; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class NoticeTest extends TestCase +{ + public function testNotice(): void + { + trigger_error('notice', E_USER_NOTICE); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/testdox/_files/OutcomeAndIssuesTest.php b/tests/end-to-end/testdox/_files/OutcomeAndIssuesTest.php new file mode 100644 index 00000000000..225df8c2594 --- /dev/null +++ b/tests/end-to-end/testdox/_files/OutcomeAndIssuesTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestDox; + +use const E_USER_DEPRECATED; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use function trigger_error; +use Exception; +use PHPUnit\Framework\TestCase; + +final class OutcomeAndIssuesTest extends TestCase +{ + public function testSuccess(): void + { + $this->assertTrue(true); + } + + public function testSuccessButRisky(): void + { + } + + public function testSuccessButDeprecation(): void + { + $this->assertTrue(true); + + trigger_error('message', E_USER_DEPRECATED); + } + + public function testSuccessButNotice(): void + { + $this->assertTrue(true); + + trigger_error('message', E_USER_NOTICE); + } + + public function testSuccessButWarning(): void + { + $this->assertTrue(true); + + trigger_error('message', E_USER_WARNING); + } + + public function testFailure(): void + { + $this->assertTrue(false); + } + + public function testError(): void + { + throw new Exception('message'); + } + + public function testIncomplete(): void + { + $this->markTestIncomplete('message'); + } + + public function testSkipped(): void + { + $this->markTestSkipped('message'); + } +} diff --git a/tests/end-to-end/testdox/_files/WarningTest.php b/tests/end-to-end/testdox/_files/WarningTest.php new file mode 100644 index 00000000000..c92c5c4a61f --- /dev/null +++ b/tests/end-to-end/testdox/_files/WarningTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\TestDox; + +use const E_USER_WARNING; +use function trigger_error; +use PHPUnit\Framework\TestCase; + +final class WarningTest extends TestCase +{ + public function testWarning(): void + { + trigger_error('warning', E_USER_WARNING); + + $this->assertTrue(true); + } +} diff --git a/tests/end-to-end/testdox/_files/bootstrap.php b/tests/end-to-end/testdox/_files/bootstrap.php new file mode 100644 index 00000000000..cea41cada23 --- /dev/null +++ b/tests/end-to-end/testdox/_files/bootstrap.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +require __DIR__ . '/Foo.php'; + +require __DIR__ . '/Bar.php'; diff --git a/tests/end-to-end/testdox/metadata-with-placeholders-data-provider-with-numeric-data-set-name-colorized.phpt b/tests/end-to-end/testdox/metadata-with-placeholders-data-provider-with-numeric-data-set-name-colorized.phpt index 89477ada8dd..b7fc160f2c7 100644 --- a/tests/end-to-end/testdox/metadata-with-placeholders-data-provider-with-numeric-data-set-name-colorized.phpt +++ b/tests/end-to-end/testdox/metadata-with-placeholders-data-provider-with-numeric-data-set-name-colorized.phpt @@ -4,6 +4,8 @@ TestDox: Default output; Data Provider with numeric data set name; TestDox metad run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Outcome And Issues (PHPUnit\TestFixture\TestDox\OutcomeAndIssues) + ✔ Success + ⚠ Success but risky + ⚠ Success but deprecation + ⚠ Success but notice + ⚠ Success but warning + ✘ Failure + │ + │ Failed asserting that false is true. + │ + │ %sOutcomeAndIssuesTest.php:53 + │ + ✘ Error + │ + │ Exception: message + │ + │ %sOutcomeAndIssuesTest.php:58 + │ + ∅ Incomplete + │ + │ message + │ + │ %sOutcomeAndIssuesTest.php:63 + │ + ↩ Skipped + +Summary of tests with errors, failures, or issues: + +Outcome And Issues (PHPUnit\TestFixture\TestDox\OutcomeAndIssues) + ⚠ Success but risky + ⚠ Success but deprecation + ⚠ Success but notice + ⚠ Success but warning + ✘ Failure + │ + │ Failed asserting that false is true. + │ + │ %sOutcomeAndIssuesTest.php:53 + │ + ✘ Error + │ + │ Exception: message + │ + │ %sOutcomeAndIssuesTest.php:58 + │ + ∅ Incomplete + │ + │ message + │ + │ %sOutcomeAndIssuesTest.php:63 + │ + ↩ Skipped + +There was 1 risky test: + +1) PHPUnit\TestFixture\TestDox\OutcomeAndIssuesTest::testSuccessButRisky +This test did not perform any assertions + +%sOutcomeAndIssuesTest.php:26 + +-- + +1 test triggered 1 warning: + +1) %sOutcomeAndIssuesTest.php:48 +message + +-- + +1 test triggered 1 notice: + +1) %sOutcomeAndIssuesTest.php:41 +message + +-- + +1 test triggered 1 deprecation: + +1) %sOutcomeAndIssuesTest.php:34 +message + +ERRORS! +Tests: 9, Assertions: 5, Errors: 1, Failures: 1, Warnings: 1, Deprecations: 1, Notices: 1, Skipped: 1, Incomplete: 1, Risky: 1. diff --git a/tests/end-to-end/testdox/outcome-and-issues-without-summary.phpt b/tests/end-to-end/testdox/outcome-and-issues-without-summary.phpt new file mode 100644 index 00000000000..3a423e8f627 --- /dev/null +++ b/tests/end-to-end/testdox/outcome-and-issues-without-summary.phpt @@ -0,0 +1,82 @@ +--TEST-- +Different outcomes and issues (without TestDox summary) +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Outcome And Issues (PHPUnit\TestFixture\TestDox\OutcomeAndIssues) + ✔ Success + ⚠ Success but risky + ⚠ Success but deprecation + ⚠ Success but notice + ⚠ Success but warning + ✘ Failure + │ + │ Failed asserting that false is true. + │ + │ %sOutcomeAndIssuesTest.php:53 + │ + ✘ Error + │ + │ Exception: message + │ + │ %sOutcomeAndIssuesTest.php:58 + │ + ∅ Incomplete + │ + │ message + │ + │ %sOutcomeAndIssuesTest.php:63 + │ + ↩ Skipped + +There was 1 risky test: + +1) PHPUnit\TestFixture\TestDox\OutcomeAndIssuesTest::testSuccessButRisky +This test did not perform any assertions + +%sOutcomeAndIssuesTest.php:26 + +-- + +1 test triggered 1 warning: + +1) %sOutcomeAndIssuesTest.php:48 +message + +-- + +1 test triggered 1 notice: + +1) %sOutcomeAndIssuesTest.php:41 +message + +-- + +1 test triggered 1 deprecation: + +1) %sOutcomeAndIssuesTest.php:34 +message + +ERRORS! +Tests: 9, Assertions: 5, Errors: 1, Failures: 1, Warnings: 1, Deprecations: 1, Notices: 1, Skipped: 1, Incomplete: 1, Risky: 1. diff --git a/tests/end-to-end/testdox/risky-test-colorized.phpt b/tests/end-to-end/testdox/risky-test-colorized.phpt index b3e30403ae0..6b36a558ff2 100644 --- a/tests/end-to-end/testdox/risky-test-colorized.phpt +++ b/tests/end-to-end/testdox/risky-test-colorized.phpt @@ -20,7 +20,14 @@ Runtime: %s Time: %s, Memory: %s Risky (PHPUnit\TestFixture\TestDox\Risky) - ☢ This is a useless test that does not test anything + ⚠ This is a useless test that does not test anything + +There was 1 risky test: + +1) PHPUnit\TestFixture\TestDox\RiskyTest::test_this_is_a_useless_test_that_does_not_test_anything +This test did not perform any assertions + +%s:16 OK, but there were issues! Tests: 1, Assertions: 0, Risky: 1. diff --git a/tests/end-to-end/testdox/risky-test.phpt b/tests/end-to-end/testdox/risky-test.phpt index 2b7586b1efc..4cf3ec43df4 100644 --- a/tests/end-to-end/testdox/risky-test.phpt +++ b/tests/end-to-end/testdox/risky-test.phpt @@ -20,7 +20,14 @@ Runtime: %s Time: %s, Memory: %s Risky (PHPUnit\TestFixture\TestDox\Risky) - ☢ This is a useless test that does not test anything + ⚠ This is a useless test that does not test anything + +There was 1 risky test: + +1) PHPUnit\TestFixture\TestDox\RiskyTest::test_this_is_a_useless_test_that_does_not_test_anything +This test did not perform any assertions + +%s:16 OK, but there were issues! Tests: 1, Assertions: 0, Risky: 1. diff --git a/tests/end-to-end/testdox/test-that-triggers-deprecation-default.phpt b/tests/end-to-end/testdox/test-that-triggers-deprecation-default.phpt new file mode 100644 index 00000000000..e1a02cdf59e --- /dev/null +++ b/tests/end-to-end/testdox/test-that-triggers-deprecation-default.phpt @@ -0,0 +1,26 @@ +--TEST-- +TestDox: Test triggers deprecation and --display-deprecations is not used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Deprecation (PHPUnit\TestFixture\TestDox\Deprecation) + ⚠ Deprecation + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 1. diff --git a/tests/end-to-end/testdox/test-that-triggers-deprecation-details.phpt b/tests/end-to-end/testdox/test-that-triggers-deprecation-details.phpt new file mode 100644 index 00000000000..619114598f6 --- /dev/null +++ b/tests/end-to-end/testdox/test-that-triggers-deprecation-details.phpt @@ -0,0 +1,32 @@ +--TEST-- +TestDox: Test triggers deprecation and --display-deprecations is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Deprecation (PHPUnit\TestFixture\TestDox\Deprecation) + ⚠ Deprecation + +1 test triggered 1 deprecation: + +1) %sDeprecationTest.php:20 +deprecation + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 1. diff --git a/tests/end-to-end/testdox/test-that-triggers-notice-default.phpt b/tests/end-to-end/testdox/test-that-triggers-notice-default.phpt new file mode 100644 index 00000000000..eda8af96993 --- /dev/null +++ b/tests/end-to-end/testdox/test-that-triggers-notice-default.phpt @@ -0,0 +1,26 @@ +--TEST-- +TestDox: Test triggers notice and --display-notices is not used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Notice (PHPUnit\TestFixture\TestDox\Notice) + ⚠ Notice + +OK, but there were issues! +Tests: 1, Assertions: 1, Notices: 1. diff --git a/tests/end-to-end/testdox/test-that-triggers-notice-details.phpt b/tests/end-to-end/testdox/test-that-triggers-notice-details.phpt new file mode 100644 index 00000000000..2c26f02999c --- /dev/null +++ b/tests/end-to-end/testdox/test-that-triggers-notice-details.phpt @@ -0,0 +1,32 @@ +--TEST-- +TestDox: Test triggers notice and --display-notices is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Notice (PHPUnit\TestFixture\TestDox\Notice) + ⚠ Notice + +1 test triggered 1 notice: + +1) %sNoticeTest.php:20 +notice + +OK, but there were issues! +Tests: 1, Assertions: 1, Notices: 1. diff --git a/tests/end-to-end/testdox/test-that-triggers-warning-default.phpt b/tests/end-to-end/testdox/test-that-triggers-warning-default.phpt new file mode 100644 index 00000000000..34123752399 --- /dev/null +++ b/tests/end-to-end/testdox/test-that-triggers-warning-default.phpt @@ -0,0 +1,26 @@ +--TEST-- +TestDox: Test triggers warning and --display-warning is not used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Warning (PHPUnit\TestFixture\TestDox\Warning) + ⚠ Warning + +OK, but there were issues! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/end-to-end/testdox/test-that-triggers-warning-details.phpt b/tests/end-to-end/testdox/test-that-triggers-warning-details.phpt new file mode 100644 index 00000000000..1cdc34efb24 --- /dev/null +++ b/tests/end-to-end/testdox/test-that-triggers-warning-details.phpt @@ -0,0 +1,32 @@ +--TEST-- +TestDox: Test triggers warning and --display-warning is used +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s + +Time: %s, Memory: %s + +Warning (PHPUnit\TestFixture\TestDox\Warning) + ⚠ Warning + +1 test triggered 1 warning: + +1) %sWarningTest.php:20 +warning + +OK, but there were issues! +Tests: 1, Assertions: 1, Warnings: 1. diff --git a/tests/static-analysis/TestUsingCallbacks.php b/tests/static-analysis/TestUsingCallbacks.php deleted file mode 100644 index d72f685cad2..00000000000 --- a/tests/static-analysis/TestUsingCallbacks.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\StaticAnalysis; - -use PHPUnit\Framework\TestCase; - -/** @see https://www.youtube.com/watch?v=rXwMrBb2x1Q */ -interface SayHello -{ - public function hey(string $toPerson): string; -} - -/** @small */ -final class TestUsingCallbacks extends TestCase -{ - public function testWillSayHelloAndCheckCallbackInput(): void - { - $mock = $this->createMock(SayHello::class); - - $mock - ->expects(self::once()) - ->method('hey') - ->with(self::callback(static function (string $input): bool { - self::assertStringContainsString('Joe', $input); - - return true; - })) - ->willReturn('Hey Joe!'); - - self::assertSame('Hey Joe!', $mock->hey('Joe')); - } - - public function testWillSayHelloAndCheckCallbackWithoutAnyInput(): void - { - $mock = $this->createMock(SayHello::class); - - $mock - ->expects(self::once()) - ->method('hey') - ->with(self::callback(static fn(): bool => true)) - ->willReturn('Hey Joe!'); - - self::assertSame('Hey Joe!', $mock->hey('Joe')); - } -} diff --git a/tests/static-analysis/TestUsingMocks.php b/tests/static-analysis/TestUsingMocks.php deleted file mode 100644 index 24770e4c0d6..00000000000 --- a/tests/static-analysis/TestUsingMocks.php +++ /dev/null @@ -1,94 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\StaticAnalysis; - -use PHPUnit\Framework\TestCase; - -class HelloWorldClass -{ - public function sayHello(): string - { - return 'hello world!'; - } -} - -/** - * @small - */ -final class TestUsingMocks extends TestCase -{ - public function testWillSayHelloThroughCreateMock(): void - { - $mock = $this->createMock(HelloWorldClass::class); - - $mock - ->method('sayHello') - ->willReturn('hello mock!'); - - self::assertSame('hello mock!', $mock->sayHello()); - } - - public function testWillSayHelloThroughCreateStub(): void - { - $mock = $this->createStub(HelloWorldClass::class); - - $mock - ->method('sayHello') - ->willReturn('hello stub!'); - - self::assertSame('hello stub!', $mock->sayHello()); - } - - public function testWillSayHelloThroughCreateConfiguredMock(): void - { - $mock = $this->createConfiguredMock(HelloWorldClass::class, []); - - $mock - ->method('sayHello') - ->willReturn('hello mock!'); - - self::assertSame('hello mock!', $mock->sayHello()); - } - - public function testWillSayHelloThroughCreatePartialMock(): void - { - $mock = $this->createPartialMock(HelloWorldClass::class, []); - - $mock - ->method('sayHello') - ->willReturn('hello mock!'); - - self::assertSame('hello mock!', $mock->sayHello()); - } - - public function testWillSayHelloThroughCreateTestProxy(): void - { - $mock = $this->createTestProxy(HelloWorldClass::class, []); - - $mock - ->method('sayHello') - ->willReturn('hello mock!'); - - self::assertSame('hello mock!', $mock->sayHello()); - } - - public function testWillSayHelloThroughGetMockBuilder(): void - { - $mock = $this - ->getMockBuilder(HelloWorldClass::class) - ->getMock(); - - $mock - ->method('sayHello') - ->willReturn('hello mock!'); - - self::assertSame('hello mock!', $mock->sayHello()); - } -} diff --git a/tests/static-analysis/happy-path/assert-empty.php b/tests/static-analysis/happy-path/assert-empty.php deleted file mode 100644 index 24fd7749f48..00000000000 --- a/tests/static-analysis/happy-path/assert-empty.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath; - -use PHPUnit\Framework\Assert; - -/** @return null */ -function consume(?object $value) -{ - Assert::assertEmpty($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-false.php b/tests/static-analysis/happy-path/assert-false.php deleted file mode 100644 index 2278fb04264..00000000000 --- a/tests/static-analysis/happy-path/assert-false.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertFalse; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @return false - */ -function consume($value) -{ - Assert::assertFalse($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-instance-of.php b/tests/static-analysis/happy-path/assert-instance-of.php deleted file mode 100644 index 5f098943917..00000000000 --- a/tests/static-analysis/happy-path/assert-instance-of.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertInstanceOf; - -use PHPUnit\Framework\Assert; - -function consume(object $value): \stdClass -{ - Assert::assertInstanceOf(\stdClass::class, $value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-array.php b/tests/static-analysis/happy-path/assert-is-array.php deleted file mode 100644 index 2571be3e6ca..00000000000 --- a/tests/static-analysis/happy-path/assert-is-array.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsArray; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): array -{ - Assert::assertIsArray($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-bool.php b/tests/static-analysis/happy-path/assert-is-bool.php deleted file mode 100644 index 91617e741f7..00000000000 --- a/tests/static-analysis/happy-path/assert-is-bool.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsBool; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): bool -{ - Assert::assertIsBool($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-callable.php b/tests/static-analysis/happy-path/assert-is-callable.php deleted file mode 100644 index d331c76541c..00000000000 --- a/tests/static-analysis/happy-path/assert-is-callable.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsCallable; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): callable -{ - Assert::assertIsCallable($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-closed-resource.php b/tests/static-analysis/happy-path/assert-is-closed-resource.php deleted file mode 100644 index cf4efdb5d1a..00000000000 --- a/tests/static-analysis/happy-path/assert-is-closed-resource.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsClosedResource; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @return resource - */ -function consume($value) -{ - Assert::assertIsClosedResource($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-float.php b/tests/static-analysis/happy-path/assert-is-float.php deleted file mode 100644 index 228f6fee2e6..00000000000 --- a/tests/static-analysis/happy-path/assert-is-float.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsFloat; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): float -{ - Assert::assertIsFloat($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-int.php b/tests/static-analysis/happy-path/assert-is-int.php deleted file mode 100644 index 8b72dfc5426..00000000000 --- a/tests/static-analysis/happy-path/assert-is-int.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsInt; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): int -{ - Assert::assertIsInt($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-iterable.php b/tests/static-analysis/happy-path/assert-is-iterable.php deleted file mode 100644 index 489472ccdb9..00000000000 --- a/tests/static-analysis/happy-path/assert-is-iterable.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsIterable; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): iterable -{ - Assert::assertIsIterable($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-array.php b/tests/static-analysis/happy-path/assert-is-not-array.php deleted file mode 100644 index 815987cb501..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-array.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotArray; - -use PHPUnit\Framework\Assert; - -/** @param array|int $value */ -function consume($value): int -{ - Assert::assertIsNotArray($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-bool.php b/tests/static-analysis/happy-path/assert-is-not-bool.php deleted file mode 100644 index b0c7b4c72f1..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-bool.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotBool; - -use PHPUnit\Framework\Assert; - -/** @param bool|int $value */ -function consume($value): int -{ - Assert::assertIsNotBool($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-callable.php b/tests/static-analysis/happy-path/assert-is-not-callable.php deleted file mode 100644 index 853b7b810e8..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-callable.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsCallable; - -use PHPUnit\Framework\Assert; - -/** @param callable|int $value */ -function consume($value): int -{ - Assert::assertIsNotCallable($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-closed-resource.php b/tests/static-analysis/happy-path/assert-is-not-closed-resource.php deleted file mode 100644 index f04171266fe..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-closed-resource.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotClosedResource; - -use PHPUnit\Framework\Assert; - -/** @param int|resource $value */ -function consume($value): int -{ - Assert::assertIsNotClosedResource($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-float.php b/tests/static-analysis/happy-path/assert-is-not-float.php deleted file mode 100644 index 6ef3d47225a..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-float.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotFloat; - -use PHPUnit\Framework\Assert; - -/** @param float|int $value */ -function consume($value): int -{ - Assert::assertIsNotFloat($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-int.php b/tests/static-analysis/happy-path/assert-is-not-int.php deleted file mode 100644 index 5787921339c..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-int.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotInt; - -use PHPUnit\Framework\Assert; - -/** @param float|int $value */ -function consume($value): float -{ - Assert::assertIsNotInt($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-iterable.php b/tests/static-analysis/happy-path/assert-is-not-iterable.php deleted file mode 100644 index 0cb93f22593..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-iterable.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotIterable; - -use PHPUnit\Framework\Assert; - -/** @param int|iterable $value */ -function consume($value): int -{ - Assert::assertIsNotIterable($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-numeric.php b/tests/static-analysis/happy-path/assert-is-not-numeric.php deleted file mode 100644 index c1d63c2e2ba..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-numeric.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotNumeric; - -use PHPUnit\Framework\Assert; - -/** @param array|numeric $value */ -function consume($value): array -{ - Assert::assertIsNotNumeric($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-object.php b/tests/static-analysis/happy-path/assert-is-not-object.php deleted file mode 100644 index 87056210295..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-object.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotObject; - -use PHPUnit\Framework\Assert; - -/** @param int|object $value */ -function consume($value): int -{ - Assert::assertIsNotObject($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-resource.php b/tests/static-analysis/happy-path/assert-is-not-resource.php deleted file mode 100644 index 882f711ef7c..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-resource.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotResource; - -use PHPUnit\Framework\Assert; - -/** @param int|resource $value */ -function consume($value): int -{ - Assert::assertIsNotResource($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-scalar.php b/tests/static-analysis/happy-path/assert-is-not-scalar.php deleted file mode 100644 index 361264eb463..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-scalar.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotScalar; - -use PHPUnit\Framework\Assert; - -/** @param object|scalar $value */ -function consume($value): object -{ - Assert::assertIsNotScalar($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-not-string.php b/tests/static-analysis/happy-path/assert-is-not-string.php deleted file mode 100644 index 045510f18a0..00000000000 --- a/tests/static-analysis/happy-path/assert-is-not-string.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotString; - -use PHPUnit\Framework\Assert; - -/** @param int|string $value */ -function consume($value): int -{ - Assert::assertIsNotString($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-numeric.php b/tests/static-analysis/happy-path/assert-is-numeric.php deleted file mode 100644 index 17e0f3739f9..00000000000 --- a/tests/static-analysis/happy-path/assert-is-numeric.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNumeric; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @return numeric - */ -function consume($value) -{ - Assert::assertIsNumeric($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-object.php b/tests/static-analysis/happy-path/assert-is-object.php deleted file mode 100644 index 33a132ab6be..00000000000 --- a/tests/static-analysis/happy-path/assert-is-object.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsObject; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): object -{ - Assert::assertIsObject($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-resource.php b/tests/static-analysis/happy-path/assert-is-resource.php deleted file mode 100644 index 8cfc606fd9e..00000000000 --- a/tests/static-analysis/happy-path/assert-is-resource.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsResource; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @return resource - */ -function consume($value) -{ - Assert::assertIsResource($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-scalar.php b/tests/static-analysis/happy-path/assert-is-scalar.php deleted file mode 100644 index e83ecc1cb85..00000000000 --- a/tests/static-analysis/happy-path/assert-is-scalar.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsScalar; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @psalm-return scalar - */ -function consume($value) -{ - Assert::assertIsScalar($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-is-string.php b/tests/static-analysis/happy-path/assert-is-string.php deleted file mode 100644 index a15fcf148d2..00000000000 --- a/tests/static-analysis/happy-path/assert-is-string.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsString; - -use PHPUnit\Framework\Assert; - -/** @param mixed $value */ -function consume($value): string -{ - Assert::assertIsString($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-not-empty.php b/tests/static-analysis/happy-path/assert-not-empty.php deleted file mode 100644 index 323724b1918..00000000000 --- a/tests/static-analysis/happy-path/assert-not-empty.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertNotEmpty; - -use PHPUnit\Framework\Assert; - -function consume(?int $value): int -{ - Assert::assertNotEmpty($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-not-false.php b/tests/static-analysis/happy-path/assert-not-false.php deleted file mode 100644 index 582c7dcc416..00000000000 --- a/tests/static-analysis/happy-path/assert-not-false.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertNotFalse; - -use PHPUnit\Framework\Assert; - -/** @param false|int $value */ -function consume($value): int -{ - Assert::assertNotFalse($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-not-instance-of.php b/tests/static-analysis/happy-path/assert-not-instance-of.php deleted file mode 100644 index 88d981657c4..00000000000 --- a/tests/static-analysis/happy-path/assert-not-instance-of.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertNotInstanceOf; - -use PHPUnit\Framework\Assert; - -class A -{ -} -class B -{ -} - -/** @param A|B $value */ -function consume(object $value): B -{ - Assert::assertNotInstanceOf(A::class, $value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-not-null.php b/tests/static-analysis/happy-path/assert-not-null.php deleted file mode 100644 index f84d87ee96a..00000000000 --- a/tests/static-analysis/happy-path/assert-not-null.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertNotNull; - -use PHPUnit\Framework\Assert; - -function consume(?int $value): int -{ - Assert::assertNotNull($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-not-true.php b/tests/static-analysis/happy-path/assert-not-true.php deleted file mode 100644 index ba43275cfe1..00000000000 --- a/tests/static-analysis/happy-path/assert-not-true.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertNotTrue; - -use PHPUnit\Framework\Assert; - -/** @param int|true $value */ -function consume($value): int -{ - Assert::assertNotTrue($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-null.php b/tests/static-analysis/happy-path/assert-null.php deleted file mode 100644 index b7696b17327..00000000000 --- a/tests/static-analysis/happy-path/assert-null.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertNull; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @return null - */ -function consume($value) -{ - Assert::assertNull($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/assert-same.php b/tests/static-analysis/happy-path/assert-same.php deleted file mode 100644 index ae6ddaecf3a..00000000000 --- a/tests/static-analysis/happy-path/assert-same.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertSame; - -use PHPUnit\Framework\Assert; - -function consume(\stdClass $a, object $b): \stdClass -{ - Assert::assertSame($a, $b); - - return $b; -} diff --git a/tests/static-analysis/happy-path/assert-true.php b/tests/static-analysis/happy-path/assert-true.php deleted file mode 100644 index 5ee070111f3..00000000000 --- a/tests/static-analysis/happy-path/assert-true.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertTrue; - -use PHPUnit\Framework\Assert; - -/** - * @param mixed $value - * - * @return true - */ -function consume($value): bool -{ - Assert::assertTrue($value); - - return $value; -} diff --git a/tests/static-analysis/happy-path/fail.php b/tests/static-analysis/happy-path/fail.php deleted file mode 100644 index 100d2980918..00000000000 --- a/tests/static-analysis/happy-path/fail.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertEmpty\Fail; - -use PHPUnit\Framework\Assert; - -/** @param int|string $value */ -function consume($value): int -{ - if (\is_string($value)) { - Assert::fail(); - } - - return $value; -} diff --git a/tests/unit/Event/Emitter/DispatchingEmitterTest.php b/tests/unit/Event/Emitter/DispatchingEmitterTest.php index 46b99d056c5..0b146d0b825 100644 --- a/tests/unit/Event/Emitter/DispatchingEmitterTest.php +++ b/tests/unit/Event/Emitter/DispatchingEmitterTest.php @@ -9,24 +9,45 @@ */ namespace PHPUnit\Event; -use function array_map; -use function array_values; -use function explode; -use function get_class_methods; -use function version_compare; use Exception; +use PHPUnit\Event\Code\ClassMethod; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Event\Code\TestCollection; use PHPUnit\Event\Code\TestDoxBuilder; +use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\ThrowableBuilder; -use PHPUnit\Event\Telemetry\Php81GarbageCollectorStatusProvider; -use PHPUnit\Event\Telemetry\Php83GarbageCollectorStatusProvider; +use PHPUnit\Event\Telemetry\SystemGarbageCollectorStatusProvider; use PHPUnit\Event\TestData\TestDataCollection; +use PHPUnit\Event\TestRunner\ChildProcessFinished; +use PHPUnit\Event\TestRunner\ChildProcessFinishedSubscriber; +use PHPUnit\Event\TestRunner\ChildProcessStarted; +use PHPUnit\Event\TestRunner\ChildProcessStartedSubscriber; +use PHPUnit\Event\TestRunner\DeprecationTriggered as TestRunnerDeprecationTriggered; +use PHPUnit\Event\TestRunner\DeprecationTriggeredSubscriber as TestRunnerDeprecationTriggeredSubscriber; +use PHPUnit\Event\TestRunner\ExecutionAborted; +use PHPUnit\Event\TestRunner\ExecutionAbortedSubscriber; +use PHPUnit\Event\TestRunner\ExecutionFinished; +use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber; use PHPUnit\Event\TestRunner\ExecutionStarted; use PHPUnit\Event\TestRunner\ExecutionStartedSubscriber; +use PHPUnit\Event\TestRunner\GarbageCollectionDisabled; +use PHPUnit\Event\TestRunner\GarbageCollectionDisabledSubscriber; +use PHPUnit\Event\TestRunner\GarbageCollectionEnabled; +use PHPUnit\Event\TestRunner\GarbageCollectionEnabledSubscriber; +use PHPUnit\Event\TestRunner\GarbageCollectionTriggered; +use PHPUnit\Event\TestRunner\GarbageCollectionTriggeredSubscriber; +use PHPUnit\Event\TestRunner\NoticeTriggered as TestRunnerNoticeTriggered; +use PHPUnit\Event\TestRunner\NoticeTriggeredSubscriber as TestRunnerNoticeTriggeredSubscriber; +use PHPUnit\Event\TestRunner\WarningTriggered as TestRunnerWarningTriggered; +use PHPUnit\Event\TestRunner\WarningTriggeredSubscriber as TestRunnerWarningTriggeredSubscriber; +use PHPUnit\Event\TestSuite\Filtered as TestSuiteFiltered; +use PHPUnit\Event\TestSuite\FilteredSubscriber as TestSuiteFilteredSubscriber; use PHPUnit\Event\TestSuite\Finished as TestSuiteFinished; use PHPUnit\Event\TestSuite\FinishedSubscriber as TestSuiteFinishedSubscriber; use PHPUnit\Event\TestSuite\Loaded as TestSuiteLoaded; use PHPUnit\Event\TestSuite\LoadedSubscriber as TestSuiteLoadedSubscriber; +use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; +use PHPUnit\Event\TestSuite\SkippedSubscriber as TestSuiteSkippedSubscriber; use PHPUnit\Event\TestSuite\Sorted as TestSuiteSorted; use PHPUnit\Event\TestSuite\SortedSubscriber as TestSuiteSortedSubscriber; use PHPUnit\Event\TestSuite\Started as TestSuiteStarted; @@ -34,16 +55,119 @@ use PHPUnit\Event\TestSuite\TestSuiteWithName; use PHPUnit\Framework; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Metadata\MetadataCollection; -use PHPUnit\TestFixture; use PHPUnit\TestFixture\RecordingSubscriber; -use SebastianBergmann\Exporter\Exporter; -use stdClass; +use PHPUnit\TextUI\CliArguments\Builder; +use PHPUnit\TextUI\Configuration\Merger; +use PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration; #[CoversClass(DispatchingEmitter::class)] +#[Small] final class DispatchingEmitterTest extends Framework\TestCase { - public function testTestRunnerStartedDispatchesTestRunnerStartedEvent(): void + #[TestDox('applicationStarted() emits Application\Started event')] + public function testApplicationStartedEmitsApplicationStartedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Application\StartedSubscriber + { + public function notify(Application\Started $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Application\StartedSubscriber::class, + Application\Started::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->applicationStarted(); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(Application\Started::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerStartedStaticAnalysisForCodeCoverage() emits TestRunner\StaticAnalysisForCodeCoverageStarted event')] + public function testTestRunnerStartedStaticAnalysisForCodeCoverageDispatchesStaticAnalysisForCodeCoverageStartedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestRunner\StaticAnalysisForCodeCoverageStartedSubscriber + { + public function notify(TestRunner\StaticAnalysisForCodeCoverageStarted $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestRunner\StaticAnalysisForCodeCoverageStartedSubscriber::class, + TestRunner\StaticAnalysisForCodeCoverageStarted::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerStartedStaticAnalysisForCodeCoverage(); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(TestRunner\StaticAnalysisForCodeCoverageStarted::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerFinishedStaticAnalysisForCodeCoverage() emits TestRunner\StaticAnalysisForCodeCoverageFinished event')] + public function testTestRunnerFinishedStaticAnalysisForCodeCoverageDispatchesStaticAnalysisForCodeCoverageFinishedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestRunner\StaticAnalysisForCodeCoverageFinishedSubscriber + { + public function notify(TestRunner\StaticAnalysisForCodeCoverageFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestRunner\StaticAnalysisForCodeCoverageFinishedSubscriber::class, + TestRunner\StaticAnalysisForCodeCoverageFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $cacheHits = 1; + $cacheMisses = 2; + + $emitter->testRunnerFinishedStaticAnalysisForCodeCoverage($cacheHits, $cacheMisses); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestRunner\StaticAnalysisForCodeCoverageFinished::class, $event); + + $this->assertSame($cacheHits, $event->cacheHits()); + $this->assertSame($cacheMisses, $event->cacheMisses()); + } + + #[TestDox('testRunnerStarted() emits TestRunner\Started event')] + public function testTestRunnerStartedEmitsTestRunnerStartedEvent(): void { $subscriber = new class extends RecordingSubscriber implements TestRunner\StartedSubscriber { @@ -72,19 +196,20 @@ public function notify(TestRunner\Started $event): void $this->assertInstanceOf(TestRunner\Started::class, $subscriber->lastRecordedEvent()); } - public function testTestRunnerFinishedDispatchesTestRunnerFinishedEvent(): void + #[TestDox('testRunnerConfigured() emits TestRunner\Configured event')] + public function testTestRunnerConfiguredEmitsTestRunnerConfiguredEvent(): void { - $subscriber = new class extends RecordingSubscriber implements TestRunner\FinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements TestRunner\ConfiguredSubscriber { - public function notify(TestRunner\Finished $event): void + public function notify(TestRunner\Configured $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - TestRunner\FinishedSubscriber::class, - TestRunner\Finished::class, + TestRunner\ConfiguredSubscriber::class, + TestRunner\Configured::class, $subscriber, ); @@ -95,29 +220,1660 @@ public function notify(TestRunner\Finished $event): void $telemetrySystem, ); - $emitter->testRunnerFinished(); + $configuration = (new Merger)->merge( + (new Builder)->fromParameters([]), + DefaultConfiguration::create(), + ); + + $emitter->testRunnerConfigured($configuration); $this->assertSame(1, $subscriber->recordedEventCount()); - $this->assertInstanceOf(TestRunner\Finished::class, $subscriber->lastRecordedEvent()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestRunner\Configured::class, $event); + $this->assertSame($configuration, $event->configuration()); } - public function testAssertionSucceededDispatchesAssertionSucceededEvent(): void + #[TestDox('bootstrapFinished() emits TestRunner\BootstrapFinished event')] + public function testBootstrapFinishedEmitsBootstrapFinishedEvent(): void { - $value = 'value'; - $constraint = new Framework\Constraint\IsEqual('Ok'); - $message = 'message'; + $filename = __FILE__; + + $subscriber = new class extends RecordingSubscriber implements TestRunner\BootstrapFinishedSubscriber + { + public function notify(TestRunner\BootstrapFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestRunner\BootstrapFinishedSubscriber::class, + TestRunner\BootstrapFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerBootstrapFinished($filename); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestRunner\BootstrapFinished::class, $event); + + $this->assertSame($filename, $event->filename()); + } + + #[TestDox('testRunnerLoadedExtensionFromPhar() emits TestRunner\ExtensionLoadedFromPhar event')] + public function testTestRunnerLoadedExtensionFromPharEmitsExtensionLoadedFromPharEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestRunner\ExtensionLoadedFromPharSubscriber + { + public function notify(TestRunner\ExtensionLoadedFromPhar $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestRunner\ExtensionLoadedFromPharSubscriber::class, + TestRunner\ExtensionLoadedFromPhar::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerLoadedExtensionFromPhar( + 'filename', + 'example-extension', + '1.2.3', + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(TestRunner\ExtensionLoadedFromPhar::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerBootstrappedExtension() emits TestRunner\ExtensionBootstrapped event')] + public function testTestRunnerBootstrappedExtensionEmitsExtensionBootstrappedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestRunner\ExtensionBootstrappedSubscriber + { + public function notify(TestRunner\ExtensionBootstrapped $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestRunner\ExtensionBootstrappedSubscriber::class, + TestRunner\ExtensionBootstrapped::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $className = 'the-extension'; + $parameters = ['foo' => 'bar']; + + $emitter->testRunnerBootstrappedExtension($className, $parameters); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestRunner\ExtensionBootstrapped::class, $event); + $this->assertSame($className, $event->className()); + $this->assertSame($parameters, $event->parameters()); + } + + #[TestDox('dataProviderMethodCalled() emits Test\DataProviderMethodCalled event')] + public function testDataProviderMethodCalledEmitsDataProviderMethodCalledEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\DataProviderMethodCalledSubscriber + { + public function notify(Test\DataProviderMethodCalled $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\DataProviderMethodCalledSubscriber::class, + Test\DataProviderMethodCalled::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = new ClassMethod('test-class', 'test-method'); + $dataProviderMethod = new ClassMethod('test-class', 'data-provider-method'); + + $emitter->dataProviderMethodCalled($testMethod, $dataProviderMethod); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\DataProviderMethodCalled::class, $event); + $this->assertSame($testMethod, $event->testMethod()); + $this->assertSame($dataProviderMethod, $event->dataProviderMethod()); + } + + #[TestDox('dataProviderMethodFinished() emits Test\DataProviderMethodFinished event')] + public function testDataProviderMethodFinishedEmitsDataProviderMethodFinishedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\DataProviderMethodFinishedSubscriber + { + public function notify(Test\DataProviderMethodFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\DataProviderMethodFinishedSubscriber::class, + Test\DataProviderMethodFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = new ClassMethod('test-class', 'test-method'); + $dataProviderMethod = new ClassMethod('test-class', 'data-provider-method'); + + $emitter->dataProviderMethodFinished($testMethod, $dataProviderMethod); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\DataProviderMethodFinished::class, $event); + $this->assertSame($testMethod, $event->testMethod()); + $this->assertSame([$dataProviderMethod], $event->calledMethods()); + } + + #[TestDox('testSuiteLoaded() emits TestSuite\Loaded event')] + public function testTestSuiteLoadedEmitsTestSuiteLoadedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestSuiteLoadedSubscriber + { + public function notify(TestSuiteLoaded $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestSuiteLoadedSubscriber::class, + TestSuiteLoaded::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testSuiteLoaded($this->testSuiteValueObject()); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(TestSuiteLoaded::class, $subscriber->lastRecordedEvent()); + } - $subscriber = new class extends RecordingSubscriber implements Test\AssertionSucceededSubscriber + #[TestDox('testSuiteFiltered() emits TestSuite\Filtered event')] + public function testTestSuiteFilteredEmitsTestSuiteFilteredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestSuiteFilteredSubscriber + { + public function notify(TestSuiteFiltered $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestSuiteFilteredSubscriber::class, + TestSuiteFiltered::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testSuite = $this->testSuiteValueObject(); + + $emitter->testSuiteFiltered($testSuite); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestSuiteFiltered::class, $event); + + $this->assertSame($testSuite, $event->testSuite()); + } + + #[TestDox('testSuiteSorted() emits TestSuite\Sorted event')] + public function testTestSuiteSortedEmitsTestSuiteSortedEvent(): void + { + $executionOrder = 9001; + $executionOrderDefects = 5; + $resolveDependencies = true; + + $subscriber = new class extends RecordingSubscriber implements TestSuiteSortedSubscriber + { + public function notify(TestSuiteSorted $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestSuiteSortedSubscriber::class, + TestSuiteSorted::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testSuiteSorted( + $executionOrder, + $executionOrderDefects, + $resolveDependencies, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestSuiteSorted::class, $event); + + $this->assertSame($executionOrder, $event->executionOrder()); + $this->assertSame($executionOrderDefects, $event->executionOrderDefects()); + $this->assertSame($resolveDependencies, $event->resolveDependencies()); + } + + #[TestDox('testRunnerEventFacadeSealed() emits TestRunner\EventFacadeSealed event')] + public function testTestRunnerEventFacadeSealedEmitsTestRunnerEventFacadeSealedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestRunner\EventFacadeSealedSubscriber + { + public function notify(TestRunner\EventFacadeSealed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestRunner\EventFacadeSealedSubscriber::class, + TestRunner\EventFacadeSealed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerEventFacadeSealed(); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(TestRunner\EventFacadeSealed::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerExecutionStarted() emits TestRunner\ExecutionStarted event')] + public function testTestRunnerExecutionStartedEmitsTestRunnerExecutionStartedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements ExecutionStartedSubscriber + { + public function notify(ExecutionStarted $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + ExecutionStartedSubscriber::class, + ExecutionStarted::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testSuite = $this->testSuiteValueObject(); + + $emitter->testRunnerExecutionStarted($testSuite); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(ExecutionStarted::class, $event); + $this->assertSame($testSuite, $event->testSuite()); + } + + #[TestDox('testRunnerDisabledGarbageCollection() emits TestRunner\GarbageCollectionDisabled event')] + public function testTestRunnerDisabledGarbageCollectionEmitsTestRunnerGarbageCollectionDisabledEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements GarbageCollectionDisabledSubscriber + { + public function notify(GarbageCollectionDisabled $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + GarbageCollectionDisabledSubscriber::class, + GarbageCollectionDisabled::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerDisabledGarbageCollection(); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(GarbageCollectionDisabled::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerTriggeredGarbageCollection() emits TestRunner\GarbageCollectionTriggered event')] + public function testTestRunnerTriggeredGarbageCollectionEmitsTestRunnerGarbageCollectionTriggeredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements GarbageCollectionTriggeredSubscriber + { + public function notify(GarbageCollectionTriggered $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + GarbageCollectionTriggeredSubscriber::class, + GarbageCollectionTriggered::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerTriggeredGarbageCollection(); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(GarbageCollectionTriggered::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerStartedChildProcess() emits TestRunner\ChildProcessStarted event')] + public function testTestRunnerStartedChildProcessEmitsTestRunnerChildProcessStartedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements ChildProcessStartedSubscriber + { + public function notify(ChildProcessStarted $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + ChildProcessStartedSubscriber::class, + ChildProcessStarted::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $emitter->testRunnerStartedChildProcess(); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(ChildProcessStarted::class, $subscriber->lastRecordedEvent()); + } + + #[TestDox('testRunnerFinishedChildProcess() emits TestRunner\ChildProcessFinished event')] + public function testTestRunnerFinishedChildProcessEmitsTestRunnerChildProcessFinishedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements ChildProcessFinishedSubscriber + { + public function notify(ChildProcessFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + ChildProcessFinishedSubscriber::class, + ChildProcessFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $stdout = 'stdout'; + $stderr = 'stderr'; + + $emitter->testRunnerFinishedChildProcess($stdout, $stderr); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(ChildProcessFinished::class, $event); + $this->assertSame($stdout, $event->stdout()); + $this->assertSame($stderr, $event->stderr()); + } + + #[TestDox('testSuiteSkipped() emits TestSuite\Skipped event')] + public function testTestSuiteSkippedEmitsTestSuiteSkippedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestSuiteSkippedSubscriber + { + public function notify(TestSuiteSkipped $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestSuiteSkippedSubscriber::class, + TestSuiteSkipped::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testSuite = $this->testSuiteValueObject(); + $message = 'message'; + + $emitter->testSuiteSkipped($testSuite, $message); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestSuiteSkipped::class, $event); + $this->assertSame($testSuite, $event->testSuite()); + $this->assertSame($message, $event->message()); + } + + #[TestDox('testSuiteStarted() emits TestSuite\Started event')] + public function testTestSuiteStartedEmitsTestSuiteStartedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements TestSuiteStartedSubscriber + { + public function notify(TestSuiteStarted $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + TestSuiteStartedSubscriber::class, + TestSuiteStarted::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testSuite = $this->testSuiteValueObject(); + + $emitter->testSuiteStarted($testSuite); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(TestSuiteStarted::class, $event); + $this->assertSame($testSuite, $event->testSuite()); + } + + #[TestDox('testPreparationStarted() emits Test\PreparationStarted event')] + public function testTestPreparationStartedEmitsTestPreparationStartedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreparationStartedSubscriber + { + public function notify(Test\PreparationStarted $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreparationStartedSubscriber::class, + Test\PreparationStarted::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + + $emitter->testPreparationStarted($test); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreparationStarted::class, $event); + $this->assertSame($test, $event->test()); + } + + #[TestDox('testPreparationErrored() emits Test\PreparationErrored event')] + public function testTestPreparationErroredEmitsTestPreparationErroredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreparationErroredSubscriber + { + public function notify(Test\PreparationErrored $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreparationErroredSubscriber::class, + Test\PreparationErrored::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->testPreparationErrored($test, $throwable); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreparationErrored::class, $event); + $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('testPreparationFailed() emits Test\PreparationFailed event')] + public function testTestPreparationFailedEmitsTestPreparationFailedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreparationFailedSubscriber + { + public function notify(Test\PreparationFailed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreparationFailedSubscriber::class, + Test\PreparationFailed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->testPreparationFailed($test, $throwable); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreparationFailed::class, $event); + $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('beforeFirstTestMethodCalled() emits Test\BeforeFirstTestMethodCalled event')] + public function testTestBeforeFirstTestMethodCalledEmitsTestBeforeFirstTestMethodEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeFirstTestMethodCalledSubscriber + { + public function notify(Test\BeforeFirstTestMethodCalled $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeFirstTestMethodCalledSubscriber::class, + Test\BeforeFirstTestMethodCalled::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->beforeFirstTestMethodCalled( + $testClassName, + $calledMethod, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeFirstTestMethodCalled::class, $event); + + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + } + + #[TestDox('beforeFirstTestMethodErrored() emits Test\BeforeFirstTestMethodErrored event')] + public function testTestBeforeFirstTestMethodErroredEmitsTestBeforeFirstTestMethodErroredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeFirstTestMethodErroredSubscriber + { + public function notify(Test\BeforeFirstTestMethodErrored $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeFirstTestMethodErroredSubscriber::class, + Test\BeforeFirstTestMethodErrored::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->beforeFirstTestMethodErrored( + $testClassName, + $calledMethod, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeFirstTestMethodErrored::class, $event); + + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('beforeFirstTestMethodFailed() emits Test\BeforeFirstTestMethodFailed event')] + public function testTestBeforeFirstTestMethodFailedEmitsTestBeforeFirstTestMethodFailedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeFirstTestMethodFailedSubscriber + { + public function notify(Test\BeforeFirstTestMethodFailed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeFirstTestMethodFailedSubscriber::class, + Test\BeforeFirstTestMethodFailed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->beforeFirstTestMethodFailed( + $testClassName, + $calledMethod, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeFirstTestMethodFailed::class, $event); + + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('beforeFirstTestMethodFinished() emits Test\BeforeFirstTestMethodFinished event')] + public function testTestBeforeFirstTestMethodFinishedEmitsTestBeforeFirstTestMethodFinishedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeFirstTestMethodFinishedSubscriber + { + public function notify(Test\BeforeFirstTestMethodFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeFirstTestMethodFinishedSubscriber::class, + Test\BeforeFirstTestMethodFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->beforeFirstTestMethodFinished( + $testClassName, + $calledMethod, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeFirstTestMethodFinished::class, $event); + + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame([$calledMethod], $event->calledMethods()); + } + + #[TestDox('beforeTestMethodCalled() emits Test\BeforeTestMethodCalled event')] + public function testTestBeforeTestMethodCalledEmitsTestBeforeTestMethodEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeTestMethodCalledSubscriber + { + public function notify(Test\BeforeTestMethodCalled $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeTestMethodCalledSubscriber::class, + Test\BeforeTestMethodCalled::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->beforeTestMethodCalled( + $testMethod, + $calledMethod, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeTestMethodCalled::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + } + + #[TestDox('beforeTestMethodErrored() emits Test\BeforeTestMethodErrored event')] + public function testTestBeforeTestMethodErroredEmitsTestBeforeTestMethodErroredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeTestMethodErroredSubscriber + { + public function notify(Test\BeforeTestMethodErrored $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeTestMethodErroredSubscriber::class, + Test\BeforeTestMethodErrored::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->beforeTestMethodErrored( + $testMethod, + $calledMethod, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeTestMethodErrored::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('beforeTestMethodFailed() emits Test\BeforeTestMethodFailed event')] + public function testTestBeforeTestMethodFailedEmitsTestBeforeTestMethodFailedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeTestMethodFailedSubscriber + { + public function notify(Test\BeforeTestMethodFailed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeTestMethodFailedSubscriber::class, + Test\BeforeTestMethodFailed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->beforeTestMethodFailed( + $testMethod, + $calledMethod, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeTestMethodFailed::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('beforeTestMethodFinished() emits Test\BeforeTestMethodFinished event')] + public function testTestBeforeTestMethodFinishedEmitsTestBeforeTestMethodFinishedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\BeforeTestMethodFinishedSubscriber + { + public function notify(Test\BeforeTestMethodFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\BeforeTestMethodFinishedSubscriber::class, + Test\BeforeTestMethodFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->beforeTestMethodFinished( + $testMethod, + $calledMethod, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\BeforeTestMethodFinished::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame([$calledMethod], $event->calledMethods()); + } + + #[TestDox('preConditionCalled() emits Test\PreConditionCalled event')] + public function testPreConditionCalledEmitsTestPreConditionCalledEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreConditionCalledSubscriber + { + public function notify(Test\PreConditionCalled $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreConditionCalledSubscriber::class, + Test\PreConditionCalled::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->preConditionCalled( + $testMethod, + $calledMethod, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreConditionCalled::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + } + + #[TestDox('preConditionErrored() emits Test\PreConditionErrored event')] + public function testPreConditionErroredEmitsTestPreConditionErroredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreConditionErroredSubscriber + { + public function notify(Test\PreConditionErrored $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreConditionErroredSubscriber::class, + Test\PreConditionErrored::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->preConditionErrored( + $testMethod, + $calledMethod, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreConditionErrored::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('preConditionFailed() emits Test\PreConditionFailed event')] + public function testPreConditionFailedEmitsTestPreConditionFailedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreConditionFailedSubscriber + { + public function notify(Test\PreConditionFailed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreConditionFailedSubscriber::class, + Test\PreConditionFailed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->preConditionFailed( + $testMethod, + $calledMethod, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreConditionFailed::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('preConditionFinished() emits Test\PreConditionFinished event')] + public function testPreConditionFinishedEmitsTestPreConditionFinishedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreConditionFinishedSubscriber + { + public function notify(Test\PreConditionFinished $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreConditionFinishedSubscriber::class, + Test\PreConditionFinished::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->preConditionFinished( + $testMethod, + $calledMethod, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PreConditionFinished::class, $event); + + $this->assertSame($testMethod, $event->test()); + $this->assertSame([$calledMethod], $event->calledMethods()); + } + + #[TestDox('testPrepared() emits Test\Prepared event')] + public function testTestPreparedEmitsTestPreparedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PreparedSubscriber + { + public function notify(Test\Prepared $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PreparedSubscriber::class, + Test\Prepared::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + + $emitter->testPrepared($test); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\Prepared::class, $event); + + $this->assertSame($test, $event->test()); + } + + #[TestDox('testRegisteredComparator() emits Test\ComparatorRegistered event')] + public function testComparatorRegisteredEmitsComparatorRegisteredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\ComparatorRegisteredSubscriber + { + public function notify(Test\ComparatorRegistered $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\ComparatorRegisteredSubscriber::class, + Test\ComparatorRegistered::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $className = 'the-class'; + + $emitter->testRegisteredComparator($className); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\ComparatorRegistered::class, $event); + + $this->assertSame($className, $event->className()); + } + + #[TestDox('testCreatedMockObject() emits Test\MockObjectCreated event')] + public function testTestCreatedMockObjectEmitsTestMockObjectCreatedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\MockObjectCreatedSubscriber + { + public function notify(Test\MockObjectCreated $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\MockObjectCreatedSubscriber::class, + Test\MockObjectCreated::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $className = 'the-class'; + + $emitter->testCreatedMockObject($className); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\MockObjectCreated::class, $event); + + $this->assertSame($className, $event->className()); + } + + #[TestDox('testCreatedMockObjectForIntersectionOfInterfaces() emits Test\MockObjectForIntersectionOfInterfacesCreated event')] + public function testTestCreatedMockObjectForIntersectionOfInterfacesEmitsTestMockObjectForIntersectionOfInterfacesCreatedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\MockObjectForIntersectionOfInterfacesCreatedSubscriber + { + public function notify(Test\MockObjectForIntersectionOfInterfacesCreated $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\MockObjectForIntersectionOfInterfacesCreatedSubscriber::class, + Test\MockObjectForIntersectionOfInterfacesCreated::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $interfaces = ['a', 'b']; + + $emitter->testCreatedMockObjectForIntersectionOfInterfaces($interfaces); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\MockObjectForIntersectionOfInterfacesCreated::class, $event); + + $this->assertSame($interfaces, $event->interfaces()); + } + + #[TestDox('testCreatedPartialMockObject() emits Test\PartialMockObjectCreated event')] + public function testTestCreatedPartialMockObjectEmitsTestPartialMockObjectCreatedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PartialMockObjectCreatedSubscriber + { + public function notify(Test\PartialMockObjectCreated $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PartialMockObjectCreatedSubscriber::class, + Test\PartialMockObjectCreated::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $className = 'the-class'; + $methodNames = ['foo', 'bar', 'baz']; + + $emitter->testCreatedPartialMockObject( + $className, + ...$methodNames, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PartialMockObjectCreated::class, $event); + + $this->assertSame($className, $event->className()); + $this->assertSame($methodNames, $event->methodNames()); + } + + #[TestDox('testCreatedStub() emits Test\TestStubCreated event')] + public function testTestStubCreatedEmitsTestTestStubCreatedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\TestStubCreatedSubscriber + { + public function notify(Test\TestStubCreated $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\TestStubCreatedSubscriber::class, + Test\TestStubCreated::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $className = 'the-class'; + + $emitter->testCreatedStub($className); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\TestStubCreated::class, $event); + + $this->assertSame($className, $event->className()); + } + + #[TestDox('testCreatedStubForIntersectionOfInterfaces() emits Test\TestStubForIntersectionOfInterfacesCreated event')] + public function testTestCreatedTestStubForIntersectionOfInterfacesEmitsTestTestStubForIntersectionOfInterfacesCreatedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\TestStubForIntersectionOfInterfacesCreatedSubscriber + { + public function notify(Test\TestStubForIntersectionOfInterfacesCreated $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\TestStubForIntersectionOfInterfacesCreatedSubscriber::class, + Test\TestStubForIntersectionOfInterfacesCreated::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $interfaces = ['a', 'b']; + + $emitter->testCreatedStubForIntersectionOfInterfaces($interfaces); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\TestStubForIntersectionOfInterfacesCreated::class, $event); + + $this->assertSame($interfaces, $event->interfaces()); + } + + #[TestDox('testErrored() emits Test\Errored event')] + public function testTestErroredEmitsTestErroredEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\ErroredSubscriber + { + public function notify(Test\Errored $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\ErroredSubscriber::class, + Test\Errored::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('error')); + + $emitter->testErrored( + $test, + $throwable, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\Errored::class, $event); + + $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); + } + + #[TestDox('testFailed() emits Test\Failed event')] + public function testTestFailedEmitsTestFailedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\FailedSubscriber + { + public function notify(Test\Failed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\FailedSubscriber::class, + Test\Failed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('failure')); + $failure = null; + + $emitter->testFailed( + $test, + $throwable, + $failure, + ); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\Failed::class, $event); + + $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); + $this->assertFalse($event->hasComparisonFailure()); + } + + #[TestDox('testPassed() emits Test\Passed event')] + public function testTestPassedEmitsTestPassedEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\PassedSubscriber + { + public function notify(Test\Passed $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\PassedSubscriber::class, + Test\Passed::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + + $emitter->testPassed($test); + + $this->assertSame(1, $subscriber->recordedEventCount()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\Passed::class, $event); + + $this->assertSame($test, $event->test()); + } + + #[TestDox('testConsideredRisky() emits Test\ConsideredRisky event')] + public function testTestConsideredRiskyEmitsTestConsideredRiskyEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\ConsideredRiskySubscriber + { + public function notify(Test\ConsideredRisky $event): void + { + $this->record($event); + } + }; + + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Test\ConsideredRiskySubscriber::class, + Test\ConsideredRisky::class, + $subscriber, + ); + + $telemetrySystem = $this->telemetrySystem(); + + $emitter = new DispatchingEmitter( + $dispatcher, + $telemetrySystem, + ); + + $test = $this->testValueObject(); + $message = 'message'; + + $emitter->testConsideredRisky($test, $message); + + $this->assertSame(1, $subscriber->recordedEventCount()); + $this->assertInstanceOf(Test\ConsideredRisky::class, $subscriber->lastRecordedEvent()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); + } + + #[TestDox('testMarkedAsIncomplete() emits Test\MarkedIncomplete event')] + public function testTestMarkedIncompleteEmitsTestMarkedIncompleteEvent(): void + { + $subscriber = new class extends RecordingSubscriber implements Test\MarkedIncompleteSubscriber { - public function notify(Test\AssertionSucceeded $event): void + public function notify(Test\MarkedIncomplete $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\AssertionSucceededSubscriber::class, - Test\AssertionSucceeded::class, + Test\MarkedIncompleteSubscriber::class, + Test\MarkedIncomplete::class, $subscriber, ); @@ -128,39 +1884,38 @@ public function notify(Test\AssertionSucceeded $event): void $telemetrySystem, ); - $emitter->testAssertionSucceeded( - $value, - $constraint, - $message, + $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('incomplete')); + + $emitter->testMarkedAsIncomplete( + $test, + $throwable, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\AssertionSucceeded::class, $event); + $this->assertInstanceOf(Test\MarkedIncomplete::class, $event); - $this->assertSame(1, $event->count()); - $this->assertSame($message, $event->message()); + $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); } - public function testAssertionFailedDispatchesAssertionFailedEvent(): void + #[TestDox('testSkipped() emits Test\Skipped event')] + public function testTestSkippedEmitsTestSkippedEvent(): void { - $value = 'value'; - $constraint = new Framework\Constraint\IsEqual('Ok'); - $message = 'message'; - - $subscriber = new class extends RecordingSubscriber implements Test\AssertionFailedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\SkippedSubscriber { - public function notify(Test\AssertionFailed $event): void + public function notify(Test\Skipped $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\AssertionFailedSubscriber::class, - Test\AssertionFailed::class, + Test\SkippedSubscriber::class, + Test\Skipped::class, $subscriber, ); @@ -171,9 +1926,11 @@ public function notify(Test\AssertionFailed $event): void $telemetrySystem, ); - $emitter->testAssertionFailed( - $value, - $constraint, + $test = $this->testValueObject(); + $message = 'skipped'; + + $emitter->testSkipped( + $test, $message, ); @@ -181,28 +1938,26 @@ public function notify(Test\AssertionFailed $event): void $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\AssertionFailed::class, $event); + $this->assertInstanceOf(Test\Skipped::class, $event); - $this->assertSame((new Exporter)->export('value'), $event->value()); - $this->assertSame(1, $event->count()); + $this->assertSame($test, $event->test()); $this->assertSame($message, $event->message()); } - public function testBootstrapFinishedDispatchesBootstrapFinishedEvent(): void + #[TestDox('testTriggeredPhpunitDeprecation() emits Test\PhpunitDeprecationTriggered event')] + public function testTestTriggeredPhpunitDeprecationEmitsTestPhpunitDeprecationTriggeredEvent(): void { - $filename = __FILE__; - - $subscriber = new class extends RecordingSubscriber implements TestRunner\BootstrapFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpunitDeprecationTriggeredSubscriber { - public function notify(TestRunner\BootstrapFinished $event): void + public function notify(Test\PhpunitDeprecationTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - TestRunner\BootstrapFinishedSubscriber::class, - TestRunner\BootstrapFinished::class, + Test\PhpunitDeprecationTriggeredSubscriber::class, + Test\PhpunitDeprecationTriggered::class, $subscriber, ); @@ -213,32 +1968,38 @@ public function notify(TestRunner\BootstrapFinished $event): void $telemetrySystem, ); - $emitter->testRunnerBootstrapFinished($filename); + $test = $this->testValueObject(); + $message = 'message'; + + $emitter->testTriggeredPhpunitDeprecation( + $test, + $message, + ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(TestRunner\BootstrapFinished::class, $event); + $this->assertInstanceOf(Test\PhpunitDeprecationTriggered::class, $event); - $this->assertSame($filename, $event->filename()); + $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); } - public function testComparatorRegisteredDispatchesComparatorRegisteredEvent(): void + #[TestDox('testTriggeredPhpunitNotice() emits Test\PhpunitNoticeTriggered event')] + public function testTestTriggeredPhpunitNoticeEmitsTestPhpunitNoticeTriggeredEvent(): void { - $className = self::class; - - $subscriber = new class extends RecordingSubscriber implements Test\ComparatorRegisteredSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpunitNoticeTriggeredSubscriber { - public function notify(Test\ComparatorRegistered $event): void + public function notify(Test\PhpunitNoticeTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\ComparatorRegisteredSubscriber::class, - Test\ComparatorRegistered::class, + Test\PhpunitNoticeTriggeredSubscriber::class, + Test\PhpunitNoticeTriggered::class, $subscriber, ); @@ -249,30 +2010,38 @@ public function notify(Test\ComparatorRegistered $event): void $telemetrySystem, ); - $emitter->testRegisteredComparator($className); + $test = $this->testValueObject(); + $message = 'message'; + + $emitter->testTriggeredPhpunitNotice( + $test, + $message, + ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\ComparatorRegistered::class, $event); + $this->assertInstanceOf(Test\PhpunitNoticeTriggered::class, $event); - $this->assertSame($className, $event->className()); + $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); } - public function testExtensionLoadedDispatchesExtensionLoadedEvent(): void + #[TestDox('testTriggeredPhpDeprecation() emits Test\PhpDeprecationTriggered event')] + public function testTestTriggeredPhpDeprecationEmitsTestPhpDeprecationTriggeredEvent(): void { - $subscriber = new class extends RecordingSubscriber implements TestRunner\ExtensionLoadedFromPharSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpDeprecationTriggeredSubscriber { - public function notify(TestRunner\ExtensionLoadedFromPhar $event): void + public function notify(Test\PhpDeprecationTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - TestRunner\ExtensionLoadedFromPharSubscriber::class, - TestRunner\ExtensionLoadedFromPhar::class, + Test\PhpDeprecationTriggeredSubscriber::class, + Test\PhpDeprecationTriggered::class, $subscriber, ); @@ -283,31 +2052,55 @@ public function notify(TestRunner\ExtensionLoadedFromPhar $event): void $telemetrySystem, ); - $emitter->testRunnerLoadedExtensionFromPhar( - 'filename', - 'example-extension', - '1.2.3', + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; + $ignoredByBaseline = false; + $ignoredByTest = false; + $trigger = IssueTrigger::unknown(); + + $emitter->testTriggeredPhpDeprecation( + $test, + $message, + $file, + $line, + $suppressed, + $ignoredByBaseline, + $ignoredByTest, + $trigger, ); $this->assertSame(1, $subscriber->recordedEventCount()); - $this->assertInstanceOf(TestRunner\ExtensionLoadedFromPhar::class, $subscriber->lastRecordedEvent()); + + $event = $subscriber->lastRecordedEvent(); + + $this->assertInstanceOf(Test\PhpDeprecationTriggered::class, $event); + $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); + $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); + $this->assertSame($ignoredByTest, $event->ignoredByTest()); + $this->assertSame($trigger, $event->trigger()); } - public function testTestErroredDispatchesTestErroredEvent(): void + #[TestDox('testTriggeredDeprecation() emits Test\DeprecationTriggered event')] + public function testTestTriggeredDeprecationEmitsTestDeprecationTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\ErroredSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\DeprecationTriggeredSubscriber { - public function notify(Test\Errored $event): void + public function notify(Test\DeprecationTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\ErroredSubscriber::class, - Test\Errored::class, + Test\DeprecationTriggeredSubscriber::class, + Test\DeprecationTriggered::class, $subscriber, ); @@ -318,38 +2111,58 @@ public function notify(Test\Errored $event): void $telemetrySystem, ); - $throwable = ThrowableBuilder::from(new Exception('error')); + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; + $ignoredByBaseline = false; + $ignoredByTest = false; + $trigger = IssueTrigger::unknown(); + $stackTrace = 'stack-trace'; - $emitter->testErrored( + $emitter->testTriggeredDeprecation( $test, - $throwable, + $message, + $file, + $line, + $suppressed, + $ignoredByBaseline, + $ignoredByTest, + $trigger, + $stackTrace, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\Errored::class, $event); - + $this->assertInstanceOf(Test\DeprecationTriggered::class, $event); $this->assertSame($test, $event->test()); - $this->assertSame($throwable, $event->throwable()); + $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); + $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); + $this->assertSame($ignoredByTest, $event->ignoredByTest()); + $this->assertSame($trigger, $event->trigger()); + $this->assertSame($stackTrace, $event->stackTrace()); } - public function testTestFailedDispatchesTestFailedEvent(): void + #[TestDox('testTriggeredError() emits Test\ErrorTriggered event')] + public function testTestTriggeredErrorEmitsTestErrorTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\FailedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\ErrorTriggeredSubscriber { - public function notify(Test\Failed $event): void + public function notify(Test\ErrorTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\FailedSubscriber::class, - Test\Failed::class, + Test\ErrorTriggeredSubscriber::class, + Test\ErrorTriggered::class, $subscriber, ); @@ -360,41 +2173,46 @@ public function notify(Test\Failed $event): void $telemetrySystem, ); - $throwable = ThrowableBuilder::from(new Exception('failure')); - $failure = null; + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; - $emitter->testFailed( + $emitter->testTriggeredError( $test, - $throwable, - $failure, + $message, + $file, + $line, + $suppressed, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\Failed::class, $event); - + $this->assertInstanceOf(Test\ErrorTriggered::class, $event); $this->assertSame($test, $event->test()); - $this->assertSame($throwable, $event->throwable()); - $this->assertFalse($event->hasComparisonFailure()); + $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); } - public function testTestFinishedDispatchesTestFinishedEvent(): void + #[TestDox('testTriggeredNotice() emits Test\NoticeTriggered event')] + public function testTestTriggeredNoticeEmitsTestNoticeTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\FinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\NoticeTriggeredSubscriber { - public function notify(Test\Finished $event): void + public function notify(Test\NoticeTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\FinishedSubscriber::class, - Test\Finished::class, + Test\NoticeTriggeredSubscriber::class, + Test\NoticeTriggered::class, $subscriber, ); @@ -405,33 +2223,49 @@ public function notify(Test\Finished $event): void $telemetrySystem, ); - $emitter->testFinished($test, 1); + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; + $ignoredByBaseline = false; + + $emitter->testTriggeredNotice( + $test, + $message, + $file, + $line, + $suppressed, + $ignoredByBaseline, + ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\Finished::class, $event); - + $this->assertInstanceOf(Test\NoticeTriggered::class, $event); $this->assertSame($test, $event->test()); - $this->assertSame(1, $event->numberOfAssertionsPerformed()); + $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); + $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); } - public function testTestPassedDispatchesTestPassedEvent(): void + #[TestDox('testTriggeredPhpNotice() emits Test\PhpNoticeTriggered event')] + public function testTestTriggeredPhpNoticeEmitsTestPhpNoticeTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\PassedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpNoticeTriggeredSubscriber { - public function notify(Test\Passed $event): void + public function notify(Test\PhpNoticeTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PassedSubscriber::class, - Test\Passed::class, + Test\PhpNoticeTriggeredSubscriber::class, + Test\PhpNoticeTriggered::class, $subscriber, ); @@ -442,32 +2276,49 @@ public function notify(Test\Passed $event): void $telemetrySystem, ); - $emitter->testPassed($test); + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; + $ignoredByBaseline = false; + + $emitter->testTriggeredPhpNotice( + $test, + $message, + $file, + $line, + $suppressed, + $ignoredByBaseline, + ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\Passed::class, $event); - + $this->assertInstanceOf(Test\PhpNoticeTriggered::class, $event); $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); + $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); } - public function testTestConsideredRiskyDispatchesTestConsideredRiskyEvent(): void + #[TestDox('testTriggeredWarning() emits Test\WarningTriggered event')] + public function testTestTriggeredWarningEmitsTestWarningTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\ConsideredRiskySubscriber + $subscriber = new class extends RecordingSubscriber implements Test\WarningTriggeredSubscriber { - public function notify(Test\ConsideredRisky $event): void + public function notify(Test\WarningTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\ConsideredRiskySubscriber::class, - Test\ConsideredRisky::class, + Test\WarningTriggeredSubscriber::class, + Test\WarningTriggered::class, $subscriber, ); @@ -478,34 +2329,49 @@ public function notify(Test\ConsideredRisky $event): void $telemetrySystem, ); - $message = 'message'; + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; + $ignoredByBaseline = false; - $emitter->testConsideredRisky($test, $message); + $emitter->testTriggeredWarning( + $test, + $message, + $file, + $line, + $suppressed, + $ignoredByBaseline, + ); $this->assertSame(1, $subscriber->recordedEventCount()); - $this->assertInstanceOf(Test\ConsideredRisky::class, $subscriber->lastRecordedEvent()); $event = $subscriber->lastRecordedEvent(); + $this->assertInstanceOf(Test\WarningTriggered::class, $event); $this->assertSame($test, $event->test()); $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); + $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); } - public function testTestMarkedIncompleteDispatchesTestMarkedIncompleteEvent(): void + #[TestDox('testTriggeredPhpWarning() emits Test\PhpWarningTriggered event')] + public function testTestTriggeredPhpWarningEmitsTestPhpWarningTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\MarkedIncompleteSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpWarningTriggeredSubscriber { - public function notify(Test\MarkedIncomplete $event): void + public function notify(Test\PhpWarningTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\MarkedIncompleteSubscriber::class, - Test\MarkedIncomplete::class, + Test\PhpWarningTriggeredSubscriber::class, + Test\PhpWarningTriggered::class, $subscriber, ); @@ -516,38 +2382,49 @@ public function notify(Test\MarkedIncomplete $event): void $telemetrySystem, ); - $throwable = ThrowableBuilder::from(new Exception('incomplete')); + $test = $this->testValueObject(); + $message = 'message'; + $file = 'file.php'; + $line = 1; + $suppressed = false; + $ignoredByBaseline = false; - $emitter->testMarkedAsIncomplete( + $emitter->testTriggeredPhpWarning( $test, - $throwable, + $message, + $file, + $line, + $suppressed, + $ignoredByBaseline, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\MarkedIncomplete::class, $event); - + $this->assertInstanceOf(Test\PhpWarningTriggered::class, $event); $this->assertSame($test, $event->test()); - $this->assertSame($throwable, $event->throwable()); + $this->assertSame($message, $event->message()); + $this->assertSame($file, $event->file()); + $this->assertSame($line, $event->line()); + $this->assertSame($suppressed, $event->wasSuppressed()); + $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); } - public function testTestSkippedDispatchesTestSkippedEvent(): void + #[TestDox('testTriggeredPhpunitError() emits Test\PhpunitErrorTriggered event')] + public function testTestTriggeredPhpunitErrorEmitsTestPhpunitErrorTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\SkippedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpunitErrorTriggeredSubscriber { - public function notify(Test\Skipped $event): void + public function notify(Test\PhpunitErrorTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\SkippedSubscriber::class, - Test\Skipped::class, + Test\PhpunitErrorTriggeredSubscriber::class, + Test\PhpunitErrorTriggered::class, $subscriber, ); @@ -558,9 +2435,10 @@ public function notify(Test\Skipped $event): void $telemetrySystem, ); - $message = 'skipped'; + $test = $this->testValueObject(); + $message = 'message'; - $emitter->testSkipped( + $emitter->testTriggeredPhpunitError( $test, $message, ); @@ -569,27 +2447,25 @@ public function notify(Test\Skipped $event): void $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\Skipped::class, $event); - + $this->assertInstanceOf(Test\PhpunitErrorTriggered::class, $event); $this->assertSame($test, $event->test()); $this->assertSame($message, $event->message()); } - public function testTestPreparedDispatchesTestPreparedEvent(): void + #[TestDox('testTriggeredPhpunitWarning() emits Test\PhpunitWarningTriggered event')] + public function testTestTriggeredPhpunitWarningEmitsTestPhpunitWarningTriggeredEvent(): void { - $test = $this->testValueObject(); - - $subscriber = new class extends RecordingSubscriber implements Test\PreparedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PhpunitWarningTriggeredSubscriber { - public function notify(Test\Prepared $event): void + public function notify(Test\PhpunitWarningTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PreparedSubscriber::class, - Test\Prepared::class, + Test\PhpunitWarningTriggeredSubscriber::class, + Test\PhpunitWarningTriggered::class, $subscriber, ); @@ -600,39 +2476,37 @@ public function notify(Test\Prepared $event): void $telemetrySystem, ); - $emitter->testPrepared($test); + $test = $this->testValueObject(); + $message = 'message'; + + $emitter->testTriggeredPhpunitWarning( + $test, + $message, + ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\Prepared::class, $event); - + $this->assertInstanceOf(Test\PhpunitWarningTriggered::class, $event); $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); } - public function testTestAfterTestMethodFinishedDispatchesTestAfterTestMethodFinishedEvent(): void + #[TestDox('testPrintedUnexpectedOutput() emits Test\PrintedUnexpectedOutput event')] + public function testTestPrintedUnexpectedOutputEmitsTestPrintedUnexpectedOutputEvent(): void { - $testClassName = self::class; - $calledMethods = array_map( - static fn (string $methodName): Code\ClassMethod => new Code\ClassMethod( - self::class, - $methodName, - ), - get_class_methods($this), - ); - - $subscriber = new class extends RecordingSubscriber implements Test\AfterTestMethodFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PrintedUnexpectedOutputSubscriber { - public function notify(Test\AfterTestMethodFinished $event): void + public function notify(Test\PrintedUnexpectedOutput $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\AfterTestMethodFinishedSubscriber::class, - Test\AfterTestMethodFinished::class, + Test\PrintedUnexpectedOutputSubscriber::class, + Test\PrintedUnexpectedOutput::class, $subscriber, ); @@ -643,40 +2517,34 @@ public function notify(Test\AfterTestMethodFinished $event): void $telemetrySystem, ); - $emitter->testAfterTestMethodFinished( - $testClassName, - ...$calledMethods, + $output = 'output'; + + $emitter->testPrintedUnexpectedOutput( + $output, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\AfterTestMethodFinished::class, $event); - - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethods, $event->calledMethods()); + $this->assertInstanceOf(Test\PrintedUnexpectedOutput::class, $event); + $this->assertSame($output, $event->output()); } - public function testTestAfterTestMethodCalledDispatchesTestAfterTestMethodCalledEvent(): void + #[TestDox('testFinished() emits Test\Finished event')] + public function testTestFinishedEmitsTestFinishedEvent(): void { - $testClassName = self::class; - $calledMethod = new Code\ClassMethod(...array_values(explode( - '::', - __METHOD__, - ))); - - $subscriber = new class extends RecordingSubscriber implements Test\AfterTestMethodCalledSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\FinishedSubscriber { - public function notify(Test\AfterTestMethodCalled $event): void + public function notify(Test\Finished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\AfterTestMethodCalledSubscriber::class, - Test\AfterTestMethodCalled::class, + Test\FinishedSubscriber::class, + Test\Finished::class, $subscriber, ); @@ -687,43 +2555,34 @@ public function notify(Test\AfterTestMethodCalled $event): void $telemetrySystem, ); - $emitter->testAfterTestMethodCalled( - $testClassName, - $calledMethod, - ); + $test = $this->testValueObject(); + + $emitter->testFinished($test, 1); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\AfterTestMethodCalled::class, $event); + $this->assertInstanceOf(Test\Finished::class, $event); - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($test, $event->test()); + $this->assertSame(1, $event->numberOfAssertionsPerformed()); } - public function testTestAfterLastTestMethodFinishedDispatchesTestAfterLastTestMethodFinishedEvent(): void + #[TestDox('postConditionCalled() emits Test\PostConditionCalled event')] + public function testPostConditionCalledEmitsTestPostConditionCalledEvent(): void { - $testClassName = self::class; - $calledMethods = array_map( - static fn (string $methodName): Code\ClassMethod => new Code\ClassMethod( - self::class, - $methodName, - ), - get_class_methods($this), - ); - - $subscriber = new class extends RecordingSubscriber implements Test\AfterLastTestMethodFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PostConditionCalledSubscriber { - public function notify(Test\AfterLastTestMethodFinished $event): void + public function notify(Test\PostConditionCalled $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\AfterLastTestMethodFinishedSubscriber::class, - Test\AfterLastTestMethodFinished::class, + Test\PostConditionCalledSubscriber::class, + Test\PostConditionCalled::class, $subscriber, ); @@ -734,40 +2593,38 @@ public function notify(Test\AfterLastTestMethodFinished $event): void $telemetrySystem, ); - $emitter->testAfterLastTestMethodFinished( - $testClassName, - ...$calledMethods, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->postConditionCalled( + $testMethod, + $calledMethod, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\AfterLastTestMethodFinished::class, $event); + $this->assertInstanceOf(Test\PostConditionCalled::class, $event); - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethods, $event->calledMethods()); + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); } - public function testTestBeforeFirstTestMethodCalledDispatchesTestBeforeFirstTestMethodEvent(): void + #[TestDox('postConditionErrored() emits Test\PostConditionErrored event')] + public function testPostConditionErroredEmitsTestPostConditionErroredEvent(): void { - $testClassName = self::class; - $calledMethod = new Code\ClassMethod(...array_values(explode( - '::', - __METHOD__, - ))); - - $subscriber = new class extends RecordingSubscriber implements Test\BeforeFirstTestMethodCalledSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PostConditionErroredSubscriber { - public function notify(Test\BeforeFirstTestMethodCalled $event): void + public function notify(Test\PostConditionErrored $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\BeforeFirstTestMethodCalledSubscriber::class, - Test\BeforeFirstTestMethodCalled::class, + Test\PostConditionErroredSubscriber::class, + Test\PostConditionErrored::class, $subscriber, ); @@ -778,43 +2635,41 @@ public function notify(Test\BeforeFirstTestMethodCalled $event): void $telemetrySystem, ); - $emitter->testBeforeFirstTestMethodCalled( - $testClassName, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->postConditionErrored( + $testMethod, $calledMethod, + $throwable, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\BeforeFirstTestMethodCalled::class, $event); + $this->assertInstanceOf(Test\PostConditionErrored::class, $event); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($testMethod, $event->test()); $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); } - public function testTestBeforeFirstTestMethodFinishedDispatchesTestBeforeFirstTestMethodFinishedEvent(): void + #[TestDox('postConditionFailed() emits Test\PostConditionFailed event')] + public function testPostConditionFailedEmitsTestPostConditionFailedEvent(): void { - $testClassName = self::class; - $calledMethods = array_map( - static fn (string $methodName): Code\ClassMethod => new Code\ClassMethod( - self::class, - $methodName, - ), - get_class_methods($this), - ); - - $subscriber = new class extends RecordingSubscriber implements Test\BeforeFirstTestMethodFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PostConditionFailedSubscriber { - public function notify(Test\BeforeFirstTestMethodFinished $event): void + public function notify(Test\PostConditionFailed $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\BeforeFirstTestMethodFinishedSubscriber::class, - Test\BeforeFirstTestMethodFinished::class, + Test\PostConditionFailedSubscriber::class, + Test\PostConditionFailed::class, $subscriber, ); @@ -825,40 +2680,41 @@ public function notify(Test\BeforeFirstTestMethodFinished $event): void $telemetrySystem, ); - $emitter->testBeforeFirstTestMethodFinished( - $testClassName, - ...$calledMethods, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->postConditionFailed( + $testMethod, + $calledMethod, + $throwable, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\BeforeFirstTestMethodFinished::class, $event); + $this->assertInstanceOf(Test\PostConditionFailed::class, $event); - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethods, $event->calledMethods()); + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); } - public function testTestBeforeTestMethodCalledDispatchesTestBeforeTestMethodEvent(): void + #[TestDox('postConditionFinished() emits Test\PostConditionFinished event')] + public function testPostConditionFinishedEmitsTestPostConditionFinishedEvent(): void { - $testClassName = self::class; - $calledMethod = new Code\ClassMethod(...array_values(explode( - '::', - __METHOD__, - ))); - - $subscriber = new class extends RecordingSubscriber implements Test\BeforeTestMethodCalledSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\PostConditionFinishedSubscriber { - public function notify(Test\BeforeTestMethodCalled $event): void + public function notify(Test\PostConditionFinished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\BeforeTestMethodCalledSubscriber::class, - Test\BeforeTestMethodCalled::class, + Test\PostConditionFinishedSubscriber::class, + Test\PostConditionFinished::class, $subscriber, ); @@ -869,8 +2725,11 @@ public function notify(Test\BeforeTestMethodCalled $event): void $telemetrySystem, ); - $emitter->testBeforeTestMethodCalled( - $testClassName, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->postConditionFinished( + $testMethod, $calledMethod, ); @@ -878,31 +2737,26 @@ public function notify(Test\BeforeTestMethodCalled $event): void $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\BeforeTestMethodCalled::class, $event); + $this->assertInstanceOf(Test\PostConditionFinished::class, $event); - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($testMethod, $event->test()); + $this->assertSame([$calledMethod], $event->calledMethods()); } - public function testTestPreConditionCalledDispatchesTestPreConditionCalledEvent(): void + #[TestDox('afterTestMethodCalled() emits Test\AfterTestMethodCalled event')] + public function testTestAfterTestMethodCalledEmitsTestAfterTestMethodEvent(): void { - $testClassName = self::class; - $calledMethod = new Code\ClassMethod(...array_values(explode( - '::', - __METHOD__, - ))); - - $subscriber = new class extends RecordingSubscriber implements Test\PreConditionCalledSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterTestMethodCalledSubscriber { - public function notify(Test\PreConditionCalled $event): void + public function notify(Test\AfterTestMethodCalled $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PreConditionCalledSubscriber::class, - Test\PreConditionCalled::class, + Test\AfterTestMethodCalledSubscriber::class, + Test\AfterTestMethodCalled::class, $subscriber, ); @@ -913,8 +2767,11 @@ public function notify(Test\PreConditionCalled $event): void $telemetrySystem, ); - $emitter->testPreConditionCalled( - $testClassName, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->afterTestMethodCalled( + $testMethod, $calledMethod, ); @@ -922,31 +2779,26 @@ public function notify(Test\PreConditionCalled $event): void $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\PreConditionCalled::class, $event); + $this->assertInstanceOf(Test\AfterTestMethodCalled::class, $event); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($testMethod, $event->test()); $this->assertSame($calledMethod, $event->calledMethod()); } - public function testTestPostConditionCalledDispatchesTestPostConditionCalledEvent(): void + #[TestDox('afterTestMethodErrored() emits Test\AfterTestMethodErrored event')] + public function testTestAfterTestMethodErroredEmitsTestAfterTestMethodErroredEvent(): void { - $testClassName = self::class; - $calledMethod = new Code\ClassMethod(...array_values(explode( - '::', - __METHOD__, - ))); - - $subscriber = new class extends RecordingSubscriber implements Test\PostConditionCalledSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterTestMethodErroredSubscriber { - public function notify(Test\PostConditionCalled $event): void + public function notify(Test\AfterTestMethodErrored $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PostConditionCalledSubscriber::class, - Test\PostConditionCalled::class, + Test\AfterTestMethodErroredSubscriber::class, + Test\AfterTestMethodErrored::class, $subscriber, ); @@ -957,43 +2809,41 @@ public function notify(Test\PostConditionCalled $event): void $telemetrySystem, ); - $emitter->testPostConditionCalled( - $testClassName, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->afterTestMethodErrored( + $testMethod, $calledMethod, + $throwable, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\PostConditionCalled::class, $event); + $this->assertInstanceOf(Test\AfterTestMethodErrored::class, $event); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($testMethod, $event->test()); $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); } - public function testTestPostConditionFinishedDispatchesTestPostConditionFinishedEvent(): void + #[TestDox('afterTestMethodFailed() emits Test\AfterTestMethodFailed event')] + public function testTestAfterTestMethodFailedEmitsTestAfterTestMethodFailedEvent(): void { - $testClassName = self::class; - $calledMethods = array_map( - static fn (string $methodName): Code\ClassMethod => new Code\ClassMethod( - self::class, - $methodName, - ), - get_class_methods($this), - ); - - $subscriber = new class extends RecordingSubscriber implements Test\PostConditionFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterTestMethodFailedSubscriber { - public function notify(Test\PostConditionFinished $event): void + public function notify(Test\AfterTestMethodFailed $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PostConditionFinishedSubscriber::class, - Test\PostConditionFinished::class, + Test\AfterTestMethodFailedSubscriber::class, + Test\AfterTestMethodFailed::class, $subscriber, ); @@ -1004,43 +2854,41 @@ public function notify(Test\PostConditionFinished $event): void $telemetrySystem, ); - $emitter->testPostConditionFinished( - $testClassName, - ...$calledMethods, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->afterTestMethodFailed( + $testMethod, + $calledMethod, + $throwable, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\PostConditionFinished::class, $event); + $this->assertInstanceOf(Test\AfterTestMethodFailed::class, $event); - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethods, $event->calledMethods()); + $this->assertSame($testMethod, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); } - public function testTestBeforeTestMethodFinishedDispatchesTestBeforeTestMethodFinishedEvent(): void + #[TestDox('afterTestMethodFinished() emits Test\AfterTestMethodFinished event')] + public function testTestAfterTestMethodFinishedEmitsTestAfterTestMethodFinishedEvent(): void { - $testClassName = self::class; - $calledMethods = array_map( - static fn (string $methodName): Code\ClassMethod => new Code\ClassMethod( - self::class, - $methodName, - ), - get_class_methods($this), - ); - - $subscriber = new class extends RecordingSubscriber implements Test\BeforeTestMethodFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterTestMethodFinishedSubscriber { - public function notify(Test\BeforeTestMethodFinished $event): void + public function notify(Test\AfterTestMethodFinished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\BeforeTestMethodFinishedSubscriber::class, - Test\BeforeTestMethodFinished::class, + Test\AfterTestMethodFinishedSubscriber::class, + Test\AfterTestMethodFinished::class, $subscriber, ); @@ -1051,43 +2899,38 @@ public function notify(Test\BeforeTestMethodFinished $event): void $telemetrySystem, ); - $emitter->testBeforeTestMethodFinished( - $testClassName, - ...$calledMethods, + $testMethod = $this->testMethod(); + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->afterTestMethodFinished( + $testMethod, + $calledMethod, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\BeforeTestMethodFinished::class, $event); + $this->assertInstanceOf(Test\AfterTestMethodFinished::class, $event); - $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethods, $event->calledMethods()); + $this->assertSame($testMethod, $event->test()); + $this->assertSame([$calledMethod], $event->calledMethods()); } - public function testTestPreConditionFinishedDispatchesTestPreConditionFinishedEvent(): void + #[TestDox('afterLastTestMethodCalled() emits Test\AfterLastTestMethodCalled event')] + public function testTestAfterLastTestMethodCalledEmitsTestAfterLastTestMethodEvent(): void { - $testClassName = self::class; - $calledMethods = array_map( - static fn (string $methodName): Code\ClassMethod => new Code\ClassMethod( - self::class, - $methodName, - ), - get_class_methods($this), - ); - - $subscriber = new class extends RecordingSubscriber implements Test\PreConditionFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterLastTestMethodCalledSubscriber { - public function notify(Test\PreConditionFinished $event): void + public function notify(Test\AfterLastTestMethodCalled $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PreConditionFinishedSubscriber::class, - Test\PreConditionFinished::class, + Test\AfterLastTestMethodCalledSubscriber::class, + Test\AfterLastTestMethodCalled::class, $subscriber, ); @@ -1098,40 +2941,38 @@ public function notify(Test\PreConditionFinished $event): void $telemetrySystem, ); - $emitter->testPreConditionFinished( + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->afterLastTestMethodCalled( $testClassName, - ...$calledMethods, + $calledMethod, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\PreConditionFinished::class, $event); + $this->assertInstanceOf(Test\AfterLastTestMethodCalled::class, $event); $this->assertSame($testClassName, $event->testClassName()); - $this->assertSame($calledMethods, $event->calledMethods()); + $this->assertSame($calledMethod, $event->calledMethod()); } - public function testTestAfterLastTestMethodCalledDispatchesTestAfterLastTestMethodCalledEvent(): void + #[TestDox('afterLastTestMethodErrored() emits Test\AfterLastTestMethodErrored event')] + public function testTestAfterLastTestMethodErroredEmitsTestAfterLastTestMethodErroredEvent(): void { - $testClassName = self::class; - $calledMethod = new Code\ClassMethod(...array_values(explode( - '::', - __METHOD__, - ))); - - $subscriber = new class extends RecordingSubscriber implements Test\AfterLastTestMethodCalledSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterLastTestMethodErroredSubscriber { - public function notify(Test\AfterLastTestMethodCalled $event): void + public function notify(Test\AfterLastTestMethodErrored $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\AfterLastTestMethodCalledSubscriber::class, - Test\AfterLastTestMethodCalled::class, + Test\AfterLastTestMethodErroredSubscriber::class, + Test\AfterLastTestMethodErrored::class, $subscriber, ); @@ -1142,36 +2983,41 @@ public function notify(Test\AfterLastTestMethodCalled $event): void $telemetrySystem, ); - $emitter->testAfterLastTestMethodCalled( + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->afterLastTestMethodErrored( $testClassName, $calledMethod, + $throwable, ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\AfterLastTestMethodCalled::class, $event); + $this->assertInstanceOf(Test\AfterLastTestMethodErrored::class, $event); $this->assertSame($testClassName, $event->testClassName()); $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); } - public function testTestMockObjectCreatedDispatchesTestDoubleMockObjectCreatedEvent(): void + #[TestDox('afterLastTestMethodFailed() emits Test\AfterLastTestMethodFailed event')] + public function testTestAfterLastTestMethodFailedEmitsTestAfterLastTestMethodFailedEvent(): void { - $className = self::class; - - $subscriber = new class extends RecordingSubscriber implements Test\MockObjectCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterLastTestMethodFailedSubscriber { - public function notify(Test\MockObjectCreated $event): void + public function notify(Test\AfterLastTestMethodFailed $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\MockObjectCreatedSubscriber::class, - Test\MockObjectCreated::class, + Test\AfterLastTestMethodFailedSubscriber::class, + Test\AfterLastTestMethodFailed::class, $subscriber, ); @@ -1182,32 +3028,41 @@ public function notify(Test\MockObjectCreated $event): void $telemetrySystem, ); - $emitter->testCreatedMockObject($className); + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $emitter->afterLastTestMethodFailed( + $testClassName, + $calledMethod, + $throwable, + ); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\MockObjectCreated::class, $event); + $this->assertInstanceOf(Test\AfterLastTestMethodFailed::class, $event); - $this->assertSame($className, $event->className()); + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); } - public function testTestMockObjectCreatedForTraitDispatchesTestDoubleMockObjectCreatedForTraitEvent(): void + #[TestDox('afterLastTestMethodFinished() emits Test\AfterLastTestMethodFinished event')] + public function testTestAfterLastTestMethodFinishedEmitsTestAfterLastTestMethodFinishedEvent(): void { - $traitName = TestFixture\MockObject\ExampleTrait::class; - - $subscriber = new class extends RecordingSubscriber implements Test\MockObjectForTraitCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements Test\AfterLastTestMethodFinishedSubscriber { - public function notify(Test\MockObjectForTraitCreated $event): void + public function notify(Test\AfterLastTestMethodFinished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\MockObjectForTraitCreatedSubscriber::class, - Test\MockObjectForTraitCreated::class, + Test\AfterLastTestMethodFinishedSubscriber::class, + Test\AfterLastTestMethodFinished::class, $subscriber, ); @@ -1218,31 +3073,38 @@ public function notify(Test\MockObjectForTraitCreated $event): void $telemetrySystem, ); - $emitter->testCreatedMockObjectForTrait($traitName); + $testClassName = 'test-class'; + $calledMethod = new ClassMethod('test-class', 'method'); + + $emitter->afterLastTestMethodFinished( + $testClassName, + $calledMethod, + ); $this->assertSame(1, $subscriber->recordedEventCount()); + $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\MockObjectForTraitCreated::class, $event); + $this->assertInstanceOf(Test\AfterLastTestMethodFinished::class, $event); - $this->assertSame($traitName, $event->traitName()); + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame([$calledMethod], $event->calledMethods()); } - public function testTestMockObjectCreatedForAbstractClassDispatchesTestDoubleMockObjectCreatedForAbstractClassEvent(): void + #[TestDox('testSuiteFinished() emits TestSuite\Finished event')] + public function testTestSuiteFinishedEmitsTestSuiteFinishedEvent(): void { - $className = stdClass::class; - - $subscriber = new class extends RecordingSubscriber implements Test\MockObjectForAbstractClassCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements TestSuiteFinishedSubscriber { - public function notify(Test\MockObjectForAbstractClassCreated $event): void + public function notify(TestSuiteFinished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\MockObjectForAbstractClassCreatedSubscriber::class, - Test\MockObjectForAbstractClassCreated::class, + TestSuiteFinishedSubscriber::class, + TestSuiteFinished::class, $subscriber, ); @@ -1253,44 +3115,32 @@ public function notify(Test\MockObjectForAbstractClassCreated $event): void $telemetrySystem, ); - $emitter->testCreatedMockObjectForAbstractClass($className); + $emitter->testSuiteFinished($this->testSuiteValueObject()); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\MockObjectForAbstractClassCreated::class, $event); + $this->assertInstanceOf(TestSuiteFinished::class, $event); - $this->assertSame($className, $event->className()); + $this->assertSame('Test Suite', $event->testSuite()->name()); + $this->assertSame(0, $event->testSuite()->count()); } - public function testTestMockObjectCreatedFromWsdlDispatchesTestDoubleMockObjectCreatedFromWsdlEvent(): void + #[TestDox('testRunnerTriggeredPhpunitDeprecation() emits TestRunner\DeprecationTriggered event')] + public function testTestRunnerTriggeredPhpunitDeprecationEmitsTestRunnerDeprecationTriggeredEvent(): void { - $wsdlFile = __FILE__; - $originalClassName = self::class; - $mockClassName = stdClass::class; - $methods = [ - 'foo', - 'bar', - ]; - $callOriginalConstructor = false; - $options = [ - 'foo' => 'bar', - 'bar' => 'baz', - 'baz' => 9000, - ]; - - $subscriber = new class extends RecordingSubscriber implements Test\MockObjectFromWsdlCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements TestRunnerDeprecationTriggeredSubscriber { - public function notify(Test\MockObjectFromWsdlCreated $event): void + public function notify(TestRunnerDeprecationTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\MockObjectFromWsdlCreatedSubscriber::class, - Test\MockObjectFromWsdlCreated::class, + TestRunnerDeprecationTriggeredSubscriber::class, + TestRunnerDeprecationTriggered::class, $subscriber, ); @@ -1301,49 +3151,33 @@ public function notify(Test\MockObjectFromWsdlCreated $event): void $telemetrySystem, ); - $emitter->testCreatedMockObjectFromWsdl( - $wsdlFile, - $originalClassName, - $mockClassName, - $methods, - $callOriginalConstructor, - $options, - ); + $message = 'message'; + + $emitter->testRunnerTriggeredPhpunitDeprecation($message); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\MockObjectFromWsdlCreated::class, $event); + $this->assertInstanceOf(TestRunnerDeprecationTriggered::class, $event); - $this->assertSame($wsdlFile, $event->wsdlFile()); - $this->assertSame($originalClassName, $event->originalClassName()); - $this->assertSame($mockClassName, $event->mockClassName()); - $this->assertSame($methods, $event->methods()); - $this->assertSame($callOriginalConstructor, $event->callOriginalConstructor()); - $this->assertSame($options, $event->options()); + $this->assertSame($message, $event->message()); } - public function testTestPartialMockObjectCreatedDispatchesTestDoublePartialMockObjectCreatedEvent(): void + #[TestDox('testRunnerTriggeredPhpunitNotice() emits TestRunner\NoticeTriggered event')] + public function testTestRunnerTriggeredPhpunitNoticeEmitsTestRunnerNoticeTriggeredEvent(): void { - $className = self::class; - $methodNames = [ - 'foo', - 'bar', - 'baz', - ]; - - $subscriber = new class extends RecordingSubscriber implements Test\PartialMockObjectCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements TestRunnerNoticeTriggeredSubscriber { - public function notify(Test\PartialMockObjectCreated $event): void + public function notify(TestRunnerNoticeTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\PartialMockObjectCreatedSubscriber::class, - Test\PartialMockObjectCreated::class, + TestRunnerNoticeTriggeredSubscriber::class, + TestRunnerNoticeTriggered::class, $subscriber, ); @@ -1354,37 +3188,33 @@ public function notify(Test\PartialMockObjectCreated $event): void $telemetrySystem, ); - $emitter->testCreatedPartialMockObject( - $className, - ...$methodNames, - ); + $message = 'message'; + + $emitter->testRunnerTriggeredPhpunitNotice($message); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\PartialMockObjectCreated::class, $event); + $this->assertInstanceOf(TestRunnerNoticeTriggered::class, $event); - $this->assertSame($className, $event->className()); - $this->assertSame($methodNames, $event->methodNames()); + $this->assertSame($message, $event->message()); } - public function testTestTestProxyCreatedDispatchesTestDoubleTestProxyCreatedEvent(): void + #[TestDox('testRunnerTriggeredPhpunitWarning() emits TestRunner\WarningTriggered event')] + public function testTestRunnerTriggeredPhpunitWarningEmitsTestRunnerWarningTriggeredEvent(): void { - $className = self::class; - $constructorArguments = ['foo']; - - $subscriber = new class extends RecordingSubscriber implements Test\TestProxyCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements TestRunnerWarningTriggeredSubscriber { - public function notify(Test\TestProxyCreated $event): void + public function notify(TestRunnerWarningTriggered $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\TestProxyCreatedSubscriber::class, - Test\TestProxyCreated::class, + TestRunnerWarningTriggeredSubscriber::class, + TestRunnerWarningTriggered::class, $subscriber, ); @@ -1395,36 +3225,33 @@ public function notify(Test\TestProxyCreated $event): void $telemetrySystem, ); - $emitter->testCreatedTestProxy( - $className, - $constructorArguments, - ); + $message = 'message'; + + $emitter->testRunnerTriggeredPhpunitWarning($message); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(Test\TestProxyCreated::class, $event); + $this->assertInstanceOf(TestRunnerWarningTriggered::class, $event); - $this->assertSame($className, $event->className()); - $this->assertSame("Array &0 [\n 0 => 'foo',\n]", $event->constructorArguments()); + $this->assertSame($message, $event->message()); } - public function testTestTestStubCreatedDispatchesTestDoubleTestStubCreatedEvent(): void + #[TestDox('testRunnerEnabledGarbageCollection() emits TestRunner\GarbageCollectionEnabled event')] + public function testTestRunnerEnabledGarbageCollectionEmitsTestRunnerGarbageCollectionEnabledEvent(): void { - $className = self::class; - - $subscriber = new class extends RecordingSubscriber implements Test\TestStubCreatedSubscriber + $subscriber = new class extends RecordingSubscriber implements GarbageCollectionEnabledSubscriber { - public function notify(Test\TestStubCreated $event): void + public function notify(GarbageCollectionEnabled $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - Test\TestStubCreatedSubscriber::class, - Test\TestStubCreated::class, + GarbageCollectionEnabledSubscriber::class, + GarbageCollectionEnabled::class, $subscriber, ); @@ -1435,30 +3262,26 @@ public function notify(Test\TestStubCreated $event): void $telemetrySystem, ); - $emitter->testCreatedStub($className); + $emitter->testRunnerEnabledGarbageCollection(); $this->assertSame(1, $subscriber->recordedEventCount()); - - $event = $subscriber->lastRecordedEvent(); - - $this->assertInstanceOf(Test\TestStubCreated::class, $event); - - $this->assertSame($className, $event->className()); + $this->assertInstanceOf(GarbageCollectionEnabled::class, $subscriber->lastRecordedEvent()); } - public function testTestSuiteLoadedDispatchesTestSuiteLoadedEvent(): void + #[TestDox('testRunnerExecutionAborted() emits TestRunner\ExecutionAborted event')] + public function testTestRunnerExecutionAbortedEmitsTestRunnerExecutionAbortedEvent(): void { - $subscriber = new class extends RecordingSubscriber implements TestSuiteLoadedSubscriber + $subscriber = new class extends RecordingSubscriber implements ExecutionAbortedSubscriber { - public function notify(TestSuiteLoaded $event): void + public function notify(ExecutionAborted $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - TestSuiteLoadedSubscriber::class, - TestSuiteLoaded::class, + ExecutionAbortedSubscriber::class, + ExecutionAborted::class, $subscriber, ); @@ -1469,25 +3292,26 @@ public function notify(TestSuiteLoaded $event): void $telemetrySystem, ); - $emitter->testSuiteLoaded($this->testSuiteValueObject()); + $emitter->testRunnerExecutionAborted(); $this->assertSame(1, $subscriber->recordedEventCount()); - $this->assertInstanceOf(TestSuiteLoaded::class, $subscriber->lastRecordedEvent()); + $this->assertInstanceOf(ExecutionAborted::class, $subscriber->lastRecordedEvent()); } - public function testTestSuiteFinishedDispatchesTestSuiteFinishedEvent(): void + #[TestDox('testRunnerExecutionFinished() emits TestRunner\ExecutionFinished event')] + public function testTestRunnerExecutionFinishedEmitsTestRunnerExecutionFinishedEvent(): void { - $subscriber = new class extends RecordingSubscriber implements TestSuiteFinishedSubscriber + $subscriber = new class extends RecordingSubscriber implements ExecutionFinishedSubscriber { - public function notify(TestSuiteFinished $event): void + public function notify(ExecutionFinished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - TestSuiteFinishedSubscriber::class, - TestSuiteFinished::class, + ExecutionFinishedSubscriber::class, + ExecutionFinished::class, $subscriber, ); @@ -1498,35 +3322,26 @@ public function notify(TestSuiteFinished $event): void $telemetrySystem, ); - $emitter->testSuiteFinished($this->testSuiteValueObject()); + $emitter->testRunnerExecutionFinished(); $this->assertSame(1, $subscriber->recordedEventCount()); - - $event = $subscriber->lastRecordedEvent(); - - $this->assertInstanceOf(TestSuiteFinished::class, $event); - - $this->assertSame('Test Suite', $event->testSuite()->name()); - $this->assertSame(0, $event->testSuite()->count()); + $this->assertInstanceOf(ExecutionFinished::class, $subscriber->lastRecordedEvent()); } - public function testTestSuiteSortedDispatchesTestSuiteSortedEvent(): void + #[TestDox('testRunnerFinished() emits TestRunner\Finished event')] + public function testTestRunnerFinishedEmitsTestRunnerFinishedEvent(): void { - $executionOrder = 9001; - $executionOrderDefects = 5; - $resolveDependencies = true; - - $subscriber = new class extends RecordingSubscriber implements TestSuiteSortedSubscriber + $subscriber = new class extends RecordingSubscriber implements TestRunner\FinishedSubscriber { - public function notify(TestSuiteSorted $event): void + public function notify(TestRunner\Finished $event): void { $this->record($event); } }; $dispatcher = $this->dispatcherWithRegisteredSubscriber( - TestSuiteSortedSubscriber::class, - TestSuiteSorted::class, + TestRunner\FinishedSubscriber::class, + TestRunner\Finished::class, $subscriber, ); @@ -1537,46 +3352,27 @@ public function notify(TestSuiteSorted $event): void $telemetrySystem, ); - $emitter->testSuiteSorted( - $executionOrder, - $executionOrderDefects, - $resolveDependencies, - ); + $emitter->testRunnerFinished(); $this->assertSame(1, $subscriber->recordedEventCount()); - - $event = $subscriber->lastRecordedEvent(); - - $this->assertInstanceOf(TestSuiteSorted::class, $event); - - $this->assertSame($executionOrder, $event->executionOrder()); - $this->assertSame($executionOrderDefects, $event->executionOrderDefects()); - $this->assertSame($resolveDependencies, $event->resolveDependencies()); + $this->assertInstanceOf(TestRunner\Finished::class, $subscriber->lastRecordedEvent()); } - public function testTestSuiteStartedDispatchesTestSuiteStartedEvent(): void + #[TestDox('applicationFinished() emits Application\Finished event')] + public function testApplicationFinishedEmitsApplicationFinishedEvent(): void { - $subscriber = new class extends RecordingSubscriber implements TestSuiteStartedSubscriber + $subscriber = new class extends RecordingSubscriber implements Application\FinishedSubscriber { - public function notify(TestSuiteStarted $event): void + public function notify(Application\Finished $event): void { $this->record($event); } }; - $dispatcher = $this->dispatcherWithRegisteredSubscribers( - TestSuiteStartedSubscriber::class, - TestSuiteStarted::class, + $dispatcher = $this->dispatcherWithRegisteredSubscriber( + Application\FinishedSubscriber::class, + Application\Finished::class, $subscriber, - ExecutionStartedSubscriber::class, - ExecutionStarted::class, - new class extends RecordingSubscriber implements ExecutionStartedSubscriber - { - public function notify(ExecutionStarted $event): void - { - $this->record($event); - } - }, ); $telemetrySystem = $this->telemetrySystem(); @@ -1586,16 +3382,16 @@ public function notify(ExecutionStarted $event): void $telemetrySystem, ); - $emitter->testSuiteStarted($this->testSuiteValueObject()); + $shellExitCode = 0; + + $emitter->applicationFinished($shellExitCode); $this->assertSame(1, $subscriber->recordedEventCount()); $event = $subscriber->lastRecordedEvent(); - $this->assertInstanceOf(TestSuiteStarted::class, $event); - - $this->assertSame('Test Suite', $event->testSuite()->name()); - $this->assertSame(0, $event->testSuite()->count()); + $this->assertInstanceOf(Application\Finished::class, $event); + $this->assertSame($shellExitCode, $event->shellExitCode()); } private function testSuiteValueObject(): TestSuiteWithName @@ -1623,46 +3419,18 @@ private function dispatcherWithRegisteredSubscriber(string $subscriberInterface, return $dispatcher; } - private function dispatcherWithRegisteredSubscribers(string $subscriberInterfaceOne, string $eventClassOne, Subscriber $subscriberOne, string $subscriberInterfaceTwo, string $eventClassTwo, Subscriber $subscriberTwo): DirectDispatcher - { - $typeMap = new TypeMap; - - $typeMap->addMapping( - $subscriberInterfaceOne, - $eventClassOne, - ); - - $typeMap->addMapping( - $subscriberInterfaceTwo, - $eventClassTwo, - ); - - $dispatcher = new DirectDispatcher($typeMap); - - $dispatcher->registerSubscriber($subscriberOne); - $dispatcher->registerSubscriber($subscriberTwo); - - return $dispatcher; - } - private function telemetrySystem(): Telemetry\System { - if (version_compare('8.3.0', PHP_VERSION, '>')) { - $garbageCollectorStatusProvider = new Php81GarbageCollectorStatusProvider; - } else { - $garbageCollectorStatusProvider = new Php83GarbageCollectorStatusProvider; - } - return new Telemetry\System( new Telemetry\SystemStopWatch, new Telemetry\SystemMemoryMeter, - $garbageCollectorStatusProvider, + new SystemGarbageCollectorStatusProvider, ); } - private function testValueObject(): Code\TestMethod + private function testValueObject(): TestMethod { - return new Code\TestMethod( + return new TestMethod( 'FooTest', 'testBar', 'FooTest.php', @@ -1672,4 +3440,17 @@ private function testValueObject(): Code\TestMethod TestDataCollection::fromArray([]), ); } + + private function testMethod(): TestMethod + { + return new TestMethod( + 'TestClass', + 'testMethod', + 'TestClass.php', + 1, + new Code\TestDox('', '', ''), + MetadataCollection::fromArray([]), + TestDataCollection::fromArray([]), + ); + } } diff --git a/tests/unit/Event/Events/Test/Assertion/AssertionFailedTest.php b/tests/unit/Event/Events/Test/Assertion/AssertionFailedTest.php deleted file mode 100644 index dd5ad3cd0cc..00000000000 --- a/tests/unit/Event/Events/Test/Assertion/AssertionFailedTest.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\AbstractEventTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(AssertionFailed::class)] -#[Small] -final class AssertionFailedTest extends AbstractEventTestCase -{ - public function testConstructorSetsValues(): void - { - $telemetryInfo = $this->telemetryInfo(); - $value = 'value'; - $constraint = 'constraint'; - $count = 1; - $message = 'message'; - - $event = new AssertionFailed( - $telemetryInfo, - $value, - $constraint, - $count, - $message, - ); - - $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($value, $event->value()); - $this->assertSame($count, $event->count()); - $this->assertSame($message, $event->message()); - $this->assertSame('Assertion Failed (Constraint: constraint, Value: value, Message: message)', $event->asString()); - } -} diff --git a/tests/unit/Event/Events/Test/Assertion/AssertionSucceededTest.php b/tests/unit/Event/Events/Test/Assertion/AssertionSucceededTest.php deleted file mode 100644 index 0474366842d..00000000000 --- a/tests/unit/Event/Events/Test/Assertion/AssertionSucceededTest.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\AbstractEventTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(AssertionSucceeded::class)] -#[Small] -final class AssertionSucceededTest extends AbstractEventTestCase -{ - public function testConstructorSetsValues(): void - { - $telemetryInfo = $this->telemetryInfo(); - $constraint = 'constraint'; - $value = 'value'; - $count = 1; - $message = 'message'; - - $event = new AssertionSucceeded( - $telemetryInfo, - $value, - $constraint, - $count, - $message, - ); - - $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($value, $event->value()); - $this->assertSame($count, $event->count()); - $this->assertSame($message, $event->message()); - $this->assertSame('Assertion Succeeded (Constraint: constraint, Value: value, Message: message)', $event->asString()); - } -} diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodErroredTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodErroredTest.php new file mode 100644 index 00000000000..f79c58599d6 --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodErroredTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(AfterLastTestMethodErrored::class)] +#[Small] +final class AfterLastTestMethodErroredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $testClassName = 'Test'; + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new AfterLastTestMethodErrored( + $telemetryInfo, + $testClassName, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new AfterLastTestMethodErrored( + $this->telemetryInfo(), + 'test class name', + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +After Last Test Method Errored (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFailedTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFailedTest.php new file mode 100644 index 00000000000..2ce12a3410b --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFailedTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(AfterLastTestMethodFailed::class)] +#[Small] +final class AfterLastTestMethodFailedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $testClassName = 'Test'; + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new AfterLastTestMethodFailed( + $telemetryInfo, + $testClassName, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new AfterLastTestMethodFailed( + $this->telemetryInfo(), + 'test class name', + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +After Last Test Method Failed (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedTest.php index 1612cab5aec..472d1c33bb6 100644 --- a/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/AfterLastTestMethodFinishedTest.php @@ -55,7 +55,7 @@ public function testCanBeRepresentedAsString(): void } /** - * @psalm-return list + * @return list */ private function calledMethods(): array { diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodCalledTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodCalledTest.php index 1960efdfc11..9d94822d885 100644 --- a/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodCalledTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodCalledTest.php @@ -21,17 +21,18 @@ final class AfterTestMethodCalledTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethod = $this->calledMethod(); $event = new AfterTestMethodCalled( $telemetryInfo, - $testClassName, + $test, $calledMethod, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethod, $event->calledMethod()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new AfterTestMethodCalled( $this->telemetryInfo(), - 'test class name', + $this->testValueObject(), $this->calledMethod(), ); diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodErroredTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodErroredTest.php new file mode 100644 index 00000000000..18cedd56e0f --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodErroredTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(AfterTestMethodErrored::class)] +#[Small] +final class AfterTestMethodErroredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new AfterTestMethodErrored( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new AfterTestMethodErrored( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +After Test Method Errored (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFailedTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFailedTest.php new file mode 100644 index 00000000000..dd70b70bdb0 --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFailedTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(AfterTestMethodFailed::class)] +#[Small] +final class AfterTestMethodFailedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new AfterTestMethodFailed( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new AfterTestMethodFailed( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +After Test Method Failed (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFinishedTest.php b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFinishedTest.php index 4dbc8aa1d64..341b3b91232 100644 --- a/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFinishedTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/AfterTestMethodFinishedTest.php @@ -21,17 +21,18 @@ final class AfterTestMethodFinishedTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethods = $this->calledMethods(); $event = new AfterTestMethodFinished( $telemetryInfo, - $testClassName, + $test, ...$calledMethods, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethods, $event->calledMethods()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new AfterTestMethodFinished( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), ...$this->calledMethods(), ); @@ -54,7 +55,7 @@ public function testCanBeRepresentedAsString(): void } /** - * @psalm-return list + * @return list */ private function calledMethods(): array { diff --git a/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailedTest.php b/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailedTest.php new file mode 100644 index 00000000000..a08bb3f3b42 --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFailedTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(BeforeFirstTestMethodFailed::class)] +#[Small] +final class BeforeFirstTestMethodFailedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $testClassName = 'Test'; + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new BeforeFirstTestMethodFailed( + $telemetryInfo, + $testClassName, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new BeforeFirstTestMethodFailed( + $this->telemetryInfo(), + 'Test', + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Before First Test Method Failed (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedTest.php b/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedTest.php index 6e9eda92367..c88dd24d5c7 100644 --- a/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/BeforeFirstTestMethodFinishedTest.php @@ -54,7 +54,7 @@ public function testCanBeRepresentedAsString(): void } /** - * @psalm-return list + * @return list */ private function calledMethods(): array { diff --git a/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodCalledTest.php b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodCalledTest.php index 22f3c3bad82..5c0bdc66f39 100644 --- a/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodCalledTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodCalledTest.php @@ -21,17 +21,18 @@ final class BeforeTestMethodCalledTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethod = $this->calledMethod(); $event = new BeforeTestMethodCalled( $telemetryInfo, - $testClassName, + $test, $calledMethod, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethod, $event->calledMethod()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new BeforeTestMethodCalled( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), $this->calledMethod(), ); diff --git a/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodErroredTest.php b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodErroredTest.php new file mode 100644 index 00000000000..efe5dd87f8b --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodErroredTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(BeforeTestMethodErrored::class)] +#[Small] +final class BeforeTestMethodErroredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new BeforeTestMethodErrored( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new BeforeTestMethodErrored( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Before Test Method Errored (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFailedTest.php b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFailedTest.php new file mode 100644 index 00000000000..cbd3e21497b --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFailedTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(BeforeTestMethodFailed::class)] +#[Small] +final class BeforeTestMethodFailedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new BeforeTestMethodFailed( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new BeforeTestMethodFailed( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Before Test Method Failed (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedTest.php b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedTest.php index cd86ffa9943..d1bd6a57c3e 100644 --- a/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/BeforeTestMethodFinishedTest.php @@ -21,17 +21,18 @@ final class BeforeTestMethodFinishedTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethods = $this->calledMethods(); $event = new BeforeTestMethodFinished( $telemetryInfo, - $testClassName, + $test, ...$calledMethods, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethods, $event->calledMethods()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new BeforeTestMethodFinished( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), ...$this->calledMethods(), ); @@ -54,7 +55,7 @@ public function testCanBeRepresentedAsString(): void } /** - * @psalm-return list + * @return list */ private function calledMethods(): array { diff --git a/tests/unit/Event/Events/Test/HookMethod/PostConditionCalledTest.php b/tests/unit/Event/Events/Test/HookMethod/PostConditionCalledTest.php index f801570ff40..6902a9fcce0 100644 --- a/tests/unit/Event/Events/Test/HookMethod/PostConditionCalledTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/PostConditionCalledTest.php @@ -21,17 +21,18 @@ final class PostConditionCalledTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethod = $this->calledMethod(); $event = new PostConditionCalled( $telemetryInfo, - $testClassName, + $test, $calledMethod, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethod, $event->calledMethod()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new PostConditionCalled( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), $this->calledMethod(), ); diff --git a/tests/unit/Event/Events/Test/HookMethod/PostConditionErroredTest.php b/tests/unit/Event/Events/Test/HookMethod/PostConditionErroredTest.php new file mode 100644 index 00000000000..17111efdf66 --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/PostConditionErroredTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(PostConditionErrored::class)] +#[Small] +final class PostConditionErroredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new PostConditionErrored( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new PostConditionErrored( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Post Condition Method Errored (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/PostConditionFailedTest.php b/tests/unit/Event/Events/Test/HookMethod/PostConditionFailedTest.php new file mode 100644 index 00000000000..9b9c776a068 --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/PostConditionFailedTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(PostConditionFailed::class)] +#[Small] +final class PostConditionFailedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new PostConditionFailed( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new PostConditionFailed( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Post Condition Method Failed (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/PostConditionFinishedTest.php b/tests/unit/Event/Events/Test/HookMethod/PostConditionFinishedTest.php index 515e74c26f6..9baf7353137 100644 --- a/tests/unit/Event/Events/Test/HookMethod/PostConditionFinishedTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/PostConditionFinishedTest.php @@ -21,17 +21,18 @@ final class PostConditionFinishedTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethods = $this->calledMethods(); $event = new PostConditionFinished( $telemetryInfo, - $testClassName, + $test, ...$calledMethods, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethods, $event->calledMethods()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new PostConditionFinished( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), ...$this->calledMethods(), ); @@ -54,7 +55,7 @@ public function testCanBeRepresentedAsString(): void } /** - * @psalm-return list + * @return list */ private function calledMethods(): array { diff --git a/tests/unit/Event/Events/Test/HookMethod/PreConditionCalledTest.php b/tests/unit/Event/Events/Test/HookMethod/PreConditionCalledTest.php index e6a105dded5..76b7e727b8a 100644 --- a/tests/unit/Event/Events/Test/HookMethod/PreConditionCalledTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/PreConditionCalledTest.php @@ -21,17 +21,18 @@ final class PreConditionCalledTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethod = $this->calledMethod(); $event = new PreConditionCalled( $telemetryInfo, - $testClassName, + $test, $calledMethod, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethod, $event->calledMethod()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new PreConditionCalled( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), $this->calledMethod(), ); diff --git a/tests/unit/Event/Events/Test/HookMethod/PreConditionErroredTest.php b/tests/unit/Event/Events/Test/HookMethod/PreConditionErroredTest.php new file mode 100644 index 00000000000..510852c2e5e --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/PreConditionErroredTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(PreConditionErrored::class)] +#[Small] +final class PreConditionErroredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new PreConditionErrored( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new PreConditionErrored( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Pre Condition Method Errored (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/PreConditionFailedTest.php b/tests/unit/Event/Events/Test/HookMethod/PreConditionFailedTest.php new file mode 100644 index 00000000000..2d8e4c54ee3 --- /dev/null +++ b/tests/unit/Event/Events/Test/HookMethod/PreConditionFailedTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(PreConditionFailed::class)] +#[Small] +final class PreConditionFailedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $calledMethod = $this->calledMethod(); + $throwable = Code\ThrowableBuilder::from(new Exception('message')); + + $event = new PreConditionFailed( + $telemetryInfo, + $test, + $calledMethod, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame($calledMethod, $event->calledMethod()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new PreConditionFailed( + $this->telemetryInfo(), + $this->testValueObject(), + $this->calledMethod(), + Code\ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertStringEqualsStringIgnoringLineEndings( + <<<'EOT' +Pre Condition Method Failed (HookClass::hookMethod) +message +EOT, + $event->asString(), + ); + } + + private function calledMethod(): Code\ClassMethod + { + return new Code\ClassMethod('HookClass', 'hookMethod'); + } +} diff --git a/tests/unit/Event/Events/Test/HookMethod/PreConditionFinishedTest.php b/tests/unit/Event/Events/Test/HookMethod/PreConditionFinishedTest.php index 707fa1555e2..c920cf7818d 100644 --- a/tests/unit/Event/Events/Test/HookMethod/PreConditionFinishedTest.php +++ b/tests/unit/Event/Events/Test/HookMethod/PreConditionFinishedTest.php @@ -21,17 +21,18 @@ final class PreConditionFinishedTest extends AbstractEventTestCase public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); - $testClassName = 'Test'; + $test = $this->testValueObject(); $calledMethods = $this->calledMethods(); $event = new PreConditionFinished( $telemetryInfo, - $testClassName, + $test, ...$calledMethods, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($testClassName, $event->testClassName()); + $this->assertSame($test, $event->test()); + $this->assertSame('FooTest', $event->testClassName()); $this->assertSame($calledMethods, $event->calledMethods()); } @@ -39,7 +40,7 @@ public function testCanBeRepresentedAsString(): void { $event = new PreConditionFinished( $this->telemetryInfo(), - 'Test', + $this->testValueObject(), ...$this->calledMethods(), ); @@ -54,7 +55,7 @@ public function testCanBeRepresentedAsString(): void } /** - * @psalm-return list + * @return list */ private function calledMethods(): array { diff --git a/tests/unit/Event/Events/Test/Issue/DeprecationTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/DeprecationTriggeredTest.php index f91097acc6a..c796b622817 100644 --- a/tests/unit/Event/Events/Test/Issue/DeprecationTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/DeprecationTriggeredTest.php @@ -11,6 +11,7 @@ use const PHP_EOL; use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; @@ -28,6 +29,8 @@ public function testConstructorSetsValues(): void $suppressed = false; $ignoredByBaseline = false; $ignoredByTest = false; + $trigger = IssueTrigger::unknown(); + $stackTrace = 'stack trace'; $event = new DeprecationTriggered( $telemetryInfo, @@ -38,6 +41,8 @@ public function testConstructorSetsValues(): void $suppressed, $ignoredByBaseline, $ignoredByTest, + $trigger, + $stackTrace, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); @@ -48,7 +53,9 @@ public function testConstructorSetsValues(): void $this->assertSame($suppressed, $event->wasSuppressed()); $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); $this->assertSame($ignoredByTest, $event->ignoredByTest()); - $this->assertSame('Test Triggered Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code) in file:1' . PHP_EOL . 'message', $event->asString()); + $this->assertSame($trigger, $event->trigger()); + $this->assertSame($stackTrace, $event->stackTrace()); } public function testCanBeIgnoredByBaseline(): void @@ -62,10 +69,12 @@ public function testCanBeIgnoredByBaseline(): void false, true, false, + IssueTrigger::unknown(), + 'stack trace', ); $this->assertTrue($event->ignoredByBaseline()); - $this->assertSame('Test Triggered Baseline-Ignored Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code, ignored by baseline) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeIgnoredByTest(): void @@ -79,10 +88,12 @@ public function testCanBeIgnoredByTest(): void false, false, true, + IssueTrigger::unknown(), + 'stack trace', ); $this->assertTrue($event->ignoredByTest()); - $this->assertSame('Test Triggered Test-Ignored Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code, ignored by test) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeSuppressed(): void @@ -96,9 +107,11 @@ public function testCanBeSuppressed(): void true, false, false, + IssueTrigger::unknown(), + 'stack trace', ); $this->assertTrue($event->wasSuppressed()); - $this->assertSame('Test Triggered Suppressed Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Issue/ErrorTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/ErrorTriggeredTest.php index 4051910c739..63c5f3eebea 100644 --- a/tests/unit/Event/Events/Test/Issue/ErrorTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/ErrorTriggeredTest.php @@ -42,6 +42,21 @@ public function testConstructorSetsValues(): void $this->assertSame($file, $event->file()); $this->assertSame($line, $event->line()); $this->assertSame($suppressed, $event->wasSuppressed()); - $this->assertSame('Test Triggered Error (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Error (FooTest::testBar) in file:1' . PHP_EOL . 'message', $event->asString()); + } + + public function testCanBeSuppressed(): void + { + $event = new ErrorTriggered( + $this->telemetryInfo(), + $this->testValueObject(), + 'message', + 'file', + 1, + true, + ); + + $this->assertTrue($event->wasSuppressed()); + $this->assertSame('Test Triggered Error (FooTest::testBar, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Issue/NoticeTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/NoticeTriggeredTest.php index 9211afd5f54..7bab2867d13 100644 --- a/tests/unit/Event/Events/Test/Issue/NoticeTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/NoticeTriggeredTest.php @@ -45,7 +45,7 @@ public function testConstructorSetsValues(): void $this->assertSame($line, $event->line()); $this->assertSame($suppressed, $event->wasSuppressed()); $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); - $this->assertSame('Test Triggered Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Notice (FooTest::testBar) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeIgnoredByBaseline(): void @@ -61,7 +61,7 @@ public function testCanBeIgnoredByBaseline(): void ); $this->assertTrue($event->ignoredByBaseline()); - $this->assertSame('Test Triggered Baseline-Ignored Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Notice (FooTest::testBar, ignored by baseline) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeSuppressed(): void @@ -77,6 +77,6 @@ public function testCanBeSuppressed(): void ); $this->assertTrue($event->wasSuppressed()); - $this->assertSame('Test Triggered Suppressed Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Notice (FooTest::testBar, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Issue/PhpDeprecationTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/PhpDeprecationTriggeredTest.php index becc988ba68..b7653146091 100644 --- a/tests/unit/Event/Events/Test/Issue/PhpDeprecationTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/PhpDeprecationTriggeredTest.php @@ -11,6 +11,7 @@ use const PHP_EOL; use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code\IssueTrigger\IssueTrigger; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; @@ -28,6 +29,7 @@ public function testConstructorSetsValues(): void $suppressed = false; $ignoredByBaseline = false; $ignoredByTest = false; + $trigger = IssueTrigger::unknown(); $event = new PhpDeprecationTriggered( $telemetryInfo, @@ -38,6 +40,7 @@ public function testConstructorSetsValues(): void $suppressed, $ignoredByBaseline, $ignoredByTest, + $trigger, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); @@ -48,7 +51,8 @@ public function testConstructorSetsValues(): void $this->assertSame($suppressed, $event->wasSuppressed()); $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); $this->assertSame($ignoredByTest, $event->ignoredByTest()); - $this->assertSame('Test Triggered PHP Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code) in file:1' . PHP_EOL . 'message', $event->asString()); + $this->assertSame($trigger, $event->trigger()); } public function testCanBeIgnoredByBaseline(): void @@ -62,10 +66,11 @@ public function testCanBeIgnoredByBaseline(): void false, true, false, + IssueTrigger::unknown(), ); $this->assertTrue($event->ignoredByBaseline()); - $this->assertSame('Test Triggered Baseline-Ignored PHP Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code, ignored by baseline) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeIgnoredByTest(): void @@ -79,10 +84,11 @@ public function testCanBeIgnoredByTest(): void false, false, true, + IssueTrigger::unknown(), ); $this->assertTrue($event->ignoredByTest()); - $this->assertSame('Test Triggered Test-Ignored PHP Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code, ignored by test) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeSuppressed(): void @@ -96,9 +102,10 @@ public function testCanBeSuppressed(): void true, false, false, + IssueTrigger::unknown(), ); $this->assertTrue($event->wasSuppressed()); - $this->assertSame('Test Triggered Suppressed PHP Deprecation (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Deprecation (FooTest::testBar, unknown if issue was triggered in first-party code or third-party code, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Issue/PhpNoticeTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/PhpNoticeTriggeredTest.php index 3ac97f0f077..8140ff3184f 100644 --- a/tests/unit/Event/Events/Test/Issue/PhpNoticeTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/PhpNoticeTriggeredTest.php @@ -45,7 +45,7 @@ public function testConstructorSetsValues(): void $this->assertSame($line, $event->line()); $this->assertSame($suppressed, $event->wasSuppressed()); $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); - $this->assertSame('Test Triggered PHP Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Notice (FooTest::testBar) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeIgnoredByBaseline(): void @@ -61,7 +61,7 @@ public function testCanBeIgnoredByBaseline(): void ); $this->assertTrue($event->ignoredByBaseline()); - $this->assertSame('Test Triggered Baseline-Ignored PHP Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Notice (FooTest::testBar, ignored by baseline) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeSuppressed(): void @@ -77,6 +77,6 @@ public function testCanBeSuppressed(): void ); $this->assertTrue($event->wasSuppressed()); - $this->assertSame('Test Triggered Suppressed PHP Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Notice (FooTest::testBar, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Issue/PhpWarningTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/PhpWarningTriggeredTest.php index 1fa7cc99c47..a6fed8356f4 100644 --- a/tests/unit/Event/Events/Test/Issue/PhpWarningTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/PhpWarningTriggeredTest.php @@ -45,7 +45,7 @@ public function testConstructorSetsValues(): void $this->assertSame($line, $event->line()); $this->assertSame($suppressed, $event->wasSuppressed()); $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); - $this->assertSame('Test Triggered PHP Warning (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Warning (FooTest::testBar) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeIgnoredByBaseline(): void @@ -61,7 +61,7 @@ public function testCanBeIgnoredByBaseline(): void ); $this->assertTrue($event->ignoredByBaseline()); - $this->assertSame('Test Triggered Baseline-Ignored PHP Warning (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Warning (FooTest::testBar, ignored by baseline) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeSuppressed(): void @@ -77,6 +77,6 @@ public function testCanBeSuppressed(): void ); $this->assertTrue($event->wasSuppressed()); - $this->assertSame('Test Triggered Suppressed PHP Warning (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered PHP Warning (FooTest::testBar, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Issue/PhpunitNoticeTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/PhpunitNoticeTriggeredTest.php new file mode 100644 index 00000000000..bb734e46158 --- /dev/null +++ b/tests/unit/Event/Events/Test/Issue/PhpunitNoticeTriggeredTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(PhpunitNoticeTriggered::class)] +#[Small] +final class PhpunitNoticeTriggeredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $message = 'message'; + + $event = new PhpunitNoticeTriggered( + $telemetryInfo, + $test, + $message, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame($message, $event->message()); + $this->assertSame('Test Triggered PHPUnit Notice (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + } +} diff --git a/tests/unit/Event/Events/Test/Issue/WarningTriggeredTest.php b/tests/unit/Event/Events/Test/Issue/WarningTriggeredTest.php index c95ecb6bdea..394af14b2cc 100644 --- a/tests/unit/Event/Events/Test/Issue/WarningTriggeredTest.php +++ b/tests/unit/Event/Events/Test/Issue/WarningTriggeredTest.php @@ -45,7 +45,7 @@ public function testConstructorSetsValues(): void $this->assertSame($line, $event->line()); $this->assertSame($suppressed, $event->wasSuppressed()); $this->assertSame($ignoredByBaseline, $event->ignoredByBaseline()); - $this->assertSame('Test Triggered Warning (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Warning (FooTest::testBar) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeIgnoredByBaseline(): void @@ -61,7 +61,7 @@ public function testCanBeIgnoredByBaseline(): void ); $this->assertTrue($event->ignoredByBaseline()); - $this->assertSame('Test Triggered Baseline-Ignored Warning (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Warning (FooTest::testBar, ignored by baseline) in file:1' . PHP_EOL . 'message', $event->asString()); } public function testCanBeSuppressed(): void @@ -77,6 +77,6 @@ public function testCanBeSuppressed(): void ); $this->assertTrue($event->wasSuppressed()); - $this->assertSame('Test Triggered Suppressed Warning (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + $this->assertSame('Test Triggered Warning (FooTest::testBar, suppressed using operator) in file:1' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/Test/Lifecycle/PreparationErroredTest.php b/tests/unit/Event/Events/Test/Lifecycle/PreparationErroredTest.php new file mode 100644 index 00000000000..9ae9d1154c4 --- /dev/null +++ b/tests/unit/Event/Events/Test/Lifecycle/PreparationErroredTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Test; + +use const PHP_EOL; +use Exception; +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code\ThrowableBuilder; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(PreparationErrored::class)] +#[Small] +final class PreparationErroredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('message')); + + $event = new PreparationErrored( + $telemetryInfo, + $test, + $throwable, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new PreparationErrored( + $this->telemetryInfo(), + $this->testValueObject(), + ThrowableBuilder::from(new Exception('message')), + ); + + $this->assertSame('Test Preparation Errored (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); + } +} diff --git a/tests/unit/Event/Events/Test/Lifecycle/PreparationFailedTest.php b/tests/unit/Event/Events/Test/Lifecycle/PreparationFailedTest.php index 0a69d7cd217..c85ef9f3799 100644 --- a/tests/unit/Event/Events/Test/Lifecycle/PreparationFailedTest.php +++ b/tests/unit/Event/Events/Test/Lifecycle/PreparationFailedTest.php @@ -9,7 +9,10 @@ */ namespace PHPUnit\Event\Test; +use const PHP_EOL; +use Exception; use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Event\Code\ThrowableBuilder; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; @@ -21,14 +24,17 @@ public function testConstructorSetsValues(): void { $telemetryInfo = $this->telemetryInfo(); $test = $this->testValueObject(); + $throwable = ThrowableBuilder::from(new Exception('message')); $event = new PreparationFailed( $telemetryInfo, $test, + $throwable, ); $this->assertSame($telemetryInfo, $event->telemetryInfo()); $this->assertSame($test, $event->test()); + $this->assertSame($throwable, $event->throwable()); } public function testCanBeRepresentedAsString(): void @@ -36,8 +42,9 @@ public function testCanBeRepresentedAsString(): void $event = new PreparationFailed( $this->telemetryInfo(), $this->testValueObject(), + ThrowableBuilder::from(new Exception('message')), ); - $this->assertSame('Test Preparation Failed (FooTest::testBar)', $event->asString()); + $this->assertSame('Test Preparation Failed (FooTest::testBar)' . PHP_EOL . 'message', $event->asString()); } } diff --git a/tests/unit/Event/Events/TestDouble/MockObjectForAbstractClassCreatedTest.php b/tests/unit/Event/Events/TestDouble/MockObjectForAbstractClassCreatedTest.php deleted file mode 100644 index cddbfb6b5c6..00000000000 --- a/tests/unit/Event/Events/TestDouble/MockObjectForAbstractClassCreatedTest.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\AbstractEventTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(MockObjectForAbstractClassCreated::class)] -#[Small] -final class MockObjectForAbstractClassCreatedTest extends AbstractEventTestCase -{ - public function testConstructorSetsValues(): void - { - $telemetryInfo = $this->telemetryInfo(); - $className = 'OriginalType'; - - $event = new MockObjectForAbstractClassCreated( - $telemetryInfo, - $className, - ); - - $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($className, $event->className()); - } - - public function testCanBeRepresentedAsString(): void - { - $event = new MockObjectForAbstractClassCreated( - $this->telemetryInfo(), - 'OriginalType', - ); - - $this->assertSame('Mock Object Created (OriginalType)', $event->asString()); - } -} diff --git a/tests/unit/Event/Events/TestDouble/MockObjectForTraitCreatedTest.php b/tests/unit/Event/Events/TestDouble/MockObjectForTraitCreatedTest.php deleted file mode 100644 index 788e5ee8913..00000000000 --- a/tests/unit/Event/Events/TestDouble/MockObjectForTraitCreatedTest.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\AbstractEventTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(MockObjectForTraitCreated::class)] -#[Small] -final class MockObjectForTraitCreatedTest extends AbstractEventTestCase -{ - public function testConstructorSetsValues(): void - { - $telemetryInfo = $this->telemetryInfo(); - $traitName = 'TraitName'; - - $event = new MockObjectForTraitCreated( - $telemetryInfo, - $traitName, - ); - - $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($traitName, $event->traitName()); - } - - public function testCanBeRepresentedAsString(): void - { - $event = new MockObjectForTraitCreated( - $this->telemetryInfo(), - 'TraitName', - ); - - $this->assertSame('Mock Object Created (TraitName)', $event->asString()); - } -} diff --git a/tests/unit/Event/Events/TestDouble/MockObjectFromWsdlCreatedTest.php b/tests/unit/Event/Events/TestDouble/MockObjectFromWsdlCreatedTest.php deleted file mode 100644 index fda80017627..00000000000 --- a/tests/unit/Event/Events/TestDouble/MockObjectFromWsdlCreatedTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\AbstractEventTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(MockObjectFromWsdlCreated::class)] -#[Small] -final class MockObjectFromWsdlCreatedTest extends AbstractEventTestCase -{ - public function testConstructorSetsValues(): void - { - $telemetryInfo = $this->telemetryInfo(); - $wsdlFile = 'test.wsdl'; - $originalClassName = 'OriginalClassName'; - $mockClassName = 'MockClassName'; - $methods = [ - 'foo', - 'bar', - ]; - $callOriginalConstructor = false; - $options = [ - 'foo' => 'bar', - 'bar' => 'baz', - 'baz' => 9000, - ]; - - $event = new MockObjectFromWsdlCreated( - $telemetryInfo, - $wsdlFile, - $originalClassName, - $mockClassName, - $methods, - $callOriginalConstructor, - $options, - ); - - $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($wsdlFile, $event->wsdlFile()); - $this->assertSame($originalClassName, $event->originalClassName()); - $this->assertSame($mockClassName, $event->mockClassName()); - $this->assertSame($methods, $event->methods()); - $this->assertSame($callOriginalConstructor, $event->callOriginalConstructor()); - $this->assertSame($options, $event->options()); - } - - public function testCanBeRepresentedAsString(): void - { - $event = new MockObjectFromWsdlCreated( - $this->telemetryInfo(), - 'test.wsdl', - 'OriginalClassName', - 'MockClassName', - [], - false, - [], - ); - - $this->assertSame('Mock Object Created (test.wsdl)', $event->asString()); - } -} diff --git a/tests/unit/Event/Events/TestDouble/TestProxyCreatedTest.php b/tests/unit/Event/Events/TestDouble/TestProxyCreatedTest.php deleted file mode 100644 index 6f22316d1d8..00000000000 --- a/tests/unit/Event/Events/TestDouble/TestProxyCreatedTest.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Test; - -use PHPUnit\Event\AbstractEventTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(TestProxyCreated::class)] -#[Small] -final class TestProxyCreatedTest extends AbstractEventTestCase -{ - public function testConstructorSetsValues(): void - { - $telemetryInfo = $this->telemetryInfo(); - $className = 'OriginalType'; - $constructorArguments = 'exported constructor arguments'; - - $event = new TestProxyCreated( - $telemetryInfo, - $className, - $constructorArguments, - ); - - $this->assertSame($telemetryInfo, $event->telemetryInfo()); - $this->assertSame($className, $event->className()); - $this->assertSame($constructorArguments, $event->constructorArguments()); - } - - public function testCanBeRepresentedAsString(): void - { - $event = new TestProxyCreated( - $this->telemetryInfo(), - 'OriginalType', - 'exported constructor arguments', - ); - - $this->assertSame('Test Proxy Created (OriginalType)', $event->asString()); - } -} diff --git a/tests/unit/Event/Events/TestRunner/ChildProcessFinishedTest.php b/tests/unit/Event/Events/TestRunner/ChildProcessFinishedTest.php new file mode 100644 index 00000000000..79bfaab0ca8 --- /dev/null +++ b/tests/unit/Event/Events/TestRunner/ChildProcessFinishedTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(ChildProcessFinished::class)] +#[Small] +final class ChildProcessFinishedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $stdout = 'output'; + $stderr = 'error'; + + $event = new ChildProcessFinished( + $telemetryInfo, + $stdout, + $stderr, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($stdout, $event->stdout()); + $this->assertSame($stderr, $event->stderr()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new ChildProcessFinished( + $this->telemetryInfo(), + 'output', + 'error', + ); + + $this->assertSame('Child Process Finished', $event->asString()); + } +} diff --git a/tests/unit/Event/Events/TestRunner/ChildProcessStartedTest.php b/tests/unit/Event/Events/TestRunner/ChildProcessStartedTest.php new file mode 100644 index 00000000000..80a4864bd90 --- /dev/null +++ b/tests/unit/Event/Events/TestRunner/ChildProcessStartedTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(ChildProcessStarted::class)] +#[Small] +final class ChildProcessStartedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + + $event = new ChildProcessStarted( + $telemetryInfo, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new ChildProcessStarted( + $this->telemetryInfo(), + ); + + $this->assertSame('Child Process Started', $event->asString()); + } +} diff --git a/tests/unit/Event/Events/TestRunner/NoticeTriggeredTest.php b/tests/unit/Event/Events/TestRunner/NoticeTriggeredTest.php new file mode 100644 index 00000000000..ebd065cbf1e --- /dev/null +++ b/tests/unit/Event/Events/TestRunner/NoticeTriggeredTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(NoticeTriggered::class)] +#[Small] +final class NoticeTriggeredTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $message = 'message'; + + $event = new NoticeTriggered( + $telemetryInfo, + $message, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($message, $event->message()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new NoticeTriggered( + $this->telemetryInfo(), + 'message', + ); + + $this->assertSame('Test Runner Triggered Notice (message)', $event->asString()); + } +} diff --git a/tests/unit/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinishedTest.php b/tests/unit/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinishedTest.php new file mode 100644 index 00000000000..34d66209dbb --- /dev/null +++ b/tests/unit/Event/Events/TestRunner/StaticAnalysisForCodeCoverageFinishedTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(StaticAnalysisForCodeCoverageFinished::class)] +#[Small] +final class StaticAnalysisForCodeCoverageFinishedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + $cacheHits = 1; + $cacheMisses = 2; + + $event = new StaticAnalysisForCodeCoverageFinished( + $telemetryInfo, + $cacheHits, + $cacheMisses, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + $this->assertSame($cacheHits, $event->cacheHits()); + $this->assertSame($cacheMisses, $event->cacheMisses()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new StaticAnalysisForCodeCoverageFinished( + $this->telemetryInfo(), + 1, + 2, + ); + + $this->assertSame('Static Analysis for Code Coverage Finished (1 cache hits, 2 cache misses)', $event->asString()); + } +} diff --git a/tests/unit/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStartedTest.php b/tests/unit/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStartedTest.php new file mode 100644 index 00000000000..214675385f5 --- /dev/null +++ b/tests/unit/Event/Events/TestRunner/StaticAnalysisForCodeCoverageStartedTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestRunner; + +use PHPUnit\Event\AbstractEventTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; + +#[CoversClass(StaticAnalysisForCodeCoverageStarted::class)] +#[Small] +final class StaticAnalysisForCodeCoverageStartedTest extends AbstractEventTestCase +{ + public function testConstructorSetsValues(): void + { + $telemetryInfo = $this->telemetryInfo(); + + $event = new StaticAnalysisForCodeCoverageStarted( + $telemetryInfo, + ); + + $this->assertSame($telemetryInfo, $event->telemetryInfo()); + } + + public function testCanBeRepresentedAsString(): void + { + $event = new StaticAnalysisForCodeCoverageStarted( + $this->telemetryInfo(), + ); + + $this->assertSame('Static Analysis for Code Coverage Started', $event->asString()); + } +} diff --git a/tests/unit/Event/FacadeTest.php b/tests/unit/Event/FacadeTest.php new file mode 100644 index 00000000000..42d4df6dcb0 --- /dev/null +++ b/tests/unit/Event/FacadeTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event; + +use PHPUnit\Event\Tracer\Tracer; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Facade::class)] +#[Small] +final class FacadeTest extends TestCase +{ + public function testSubscriberRegistrationDoesNotWorkWhenEventFacadeIsSealed(): void + { + $this->expectException(EventFacadeIsSealedException::class); + + Facade::instance()->registerSubscriber( + new class implements Subscriber + {}, + ); + } + + public function testTracerRegistrationDoesNotWorkWhenEventFacadeIsSealed(): void + { + $this->expectException(EventFacadeIsSealedException::class); + + Facade::instance()->registerTracer( + new class implements Tracer + { + public function trace(Event $event): void + { + } + }, + ); + } +} diff --git a/tests/unit/Event/Value/ComparisonFailureBuilderTest.php b/tests/unit/Event/Value/ComparisonFailureBuilderTest.php new file mode 100644 index 00000000000..ff7f2f15b4c --- /dev/null +++ b/tests/unit/Event/Value/ComparisonFailureBuilderTest.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code; + +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\Comparator\ComparisonFailure; +use stdClass; + +#[CoversClass(ComparisonFailureBuilder::class)] +#[Small] +final class ComparisonFailureBuilderTest extends TestCase +{ + public static function provider(): array + { + return [ + 'type of expected value: string, string representation of expected value: not available, type of actual value: string, string representation of actual value: not available' => [ + 'expected', + 'actual', + '', + new ExpectationFailedException( + 'message', + new ComparisonFailure( + 'expected', + 'actual', + '', + '', + 'message', + ), + ), + ], + + 'type of expected value: true, string representation of expected value: not available, type of actual value: false, string representation of actual value: not available' => [ + 'true', + 'false', + '', + new ExpectationFailedException( + 'message', + new ComparisonFailure( + true, + false, + '', + '', + 'message', + ), + ), + ], + + 'type of expected value: null, string representation of expected value: not available, type of actual value: null, string representation of actual value: not available' => [ + 'null', + 'null', + '', + new ExpectationFailedException( + 'message', + new ComparisonFailure( + null, + null, + '', + '', + 'message', + ), + ), + ], + + 'type of expected value: array, string representation of expected value: not available, type of actual value: object, string representation of actual value: not available' => [ + '', + '', + '', + new ExpectationFailedException( + 'message', + new ComparisonFailure( + [], + new stdClass, + '', + '', + 'message', + ), + ), + ], + + 'type of expected value: string, string representation of expected value: available, type of actual value: string, string representation of actual value: available' => [ + 'expected-string', + 'actual-string', + <<<'EOT' + +--- Expected ++++ Actual +@@ @@ +-expected-string ++actual-string + +EOT, + new ExpectationFailedException( + 'message', + new ComparisonFailure( + 'expected', + 'actual', + 'expected-string', + 'actual-string', + 'message', + ), + ), + ], + ]; + } + + #[TestDox('Maps exception that is not of type ExpectationFailedException to null')] + public function testMapsGenericThrowableToNull(): void + { + $this->assertNull( + ComparisonFailureBuilder::from(new Exception), + ); + } + + #[TestDox('Maps ExpectationFailedException that does not aggregate a ComparisonFailure object to null')] + public function testMapsExpectationFailedExceptionWithoutComparisonFailureToNull(): void + { + $this->assertNull( + ComparisonFailureBuilder::from( + new ExpectationFailedException('message'), + ), + ); + } + + #[DataProvider('provider')] + #[TestDox('Maps ExpectationFailedException that aggregates a ComparisonFailure object to value object')] + public function testMapsExpectationFailedExceptionWithComparisonFailureToValueObject(string $expected, string $actual, string $diff, ExpectationFailedException $exception): void + { + $comparisonFailure = ComparisonFailureBuilder::from($exception); + + $this->assertSame($expected, $comparisonFailure->expected()); + $this->assertSame($actual, $comparisonFailure->actual()); + $this->assertSame($diff, $comparisonFailure->diff()); + } +} diff --git a/tests/unit/Event/Value/Runtime/PHPTest.php b/tests/unit/Event/Value/Runtime/PHPTest.php index 75f069dfdcb..061d3d8c2b1 100644 --- a/tests/unit/Event/Value/Runtime/PHPTest.php +++ b/tests/unit/Event/Value/Runtime/PHPTest.php @@ -15,6 +15,7 @@ use const PHP_RELEASE_VERSION; use const PHP_SAPI; use const PHP_VERSION; +use const PHP_VERSION_ID; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; @@ -62,6 +63,6 @@ public function testHasExtensions(): void { $this->assertNotEmpty((new PHP)->extensions()); $this->assertIsList((new PHP)->extensions()); - $this->assertContainsOnly('string', (new PHP)->extensions()); + $this->assertContainsOnlyString((new PHP)->extensions()); } } diff --git a/tests/unit/Event/Value/Telemetry/DurationTest.php b/tests/unit/Event/Value/Telemetry/DurationTest.php index 9f17f3c8245..5d4a5206d36 100644 --- a/tests/unit/Event/Value/Telemetry/DurationTest.php +++ b/tests/unit/Event/Value/Telemetry/DurationTest.php @@ -21,7 +21,7 @@ final class DurationTest extends TestCase { /** - * @psalm-return array> + * @return array */ public static function provideDurationAndStringRepresentation(): array { diff --git a/tests/unit/Event/Value/Telemetry/GarbageCollectorStatusTest.php b/tests/unit/Event/Value/Telemetry/GarbageCollectorStatusTest.php index d962c717d65..608bdaaec55 100644 --- a/tests/unit/Event/Value/Telemetry/GarbageCollectorStatusTest.php +++ b/tests/unit/Event/Value/Telemetry/GarbageCollectorStatusTest.php @@ -9,7 +9,6 @@ */ namespace PHPUnit\Event\Telemetry; -use PHPUnit\Event\RuntimeException; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; @@ -20,121 +19,65 @@ final class GarbageCollectorStatusTest extends TestCase { public function testHasRuns(): void { - $this->assertSame(1, $this->withoutDetails()->runs()); + $this->assertSame(1, $this->garbageCollectorStatus()->runs()); } public function testHasCollected(): void { - $this->assertSame(2, $this->withoutDetails()->collected()); + $this->assertSame(2, $this->garbageCollectorStatus()->collected()); } public function testHasThreshold(): void { - $this->assertSame(3, $this->withoutDetails()->threshold()); + $this->assertSame(3, $this->garbageCollectorStatus()->threshold()); } public function testHasRoots(): void { - $this->assertSame(4, $this->withoutDetails()->roots()); - } - - public function testMayHaveExtendedInformation(): void - { - $this->assertTrue($this->withDetails()->hasExtendedInformation()); + $this->assertSame(4, $this->garbageCollectorStatus()->roots()); } public function testMayHaveRunning(): void { - $this->assertTrue($this->withDetails()->isRunning()); + $this->assertTrue($this->garbageCollectorStatus()->isRunning()); } public function testMayHaveApplicationTime(): void { - $this->assertSame(5.0, $this->withDetails()->applicationTime()); + $this->assertSame(5.0, $this->garbageCollectorStatus()->applicationTime()); } public function testMayHaveCollectorTime(): void { - $this->assertSame(6.0, $this->withDetails()->collectorTime()); + $this->assertSame(6.0, $this->garbageCollectorStatus()->collectorTime()); } public function testMayHaveDestructorTime(): void { - $this->assertSame(7.0, $this->withDetails()->destructorTime()); + $this->assertSame(7.0, $this->garbageCollectorStatus()->destructorTime()); } public function testMayHaveFreeTime(): void { - $this->assertSame(8.0, $this->withDetails()->freeTime()); + $this->assertSame(8.0, $this->garbageCollectorStatus()->freeTime()); } public function testMayHaveProtected(): void { - $this->assertTrue($this->withDetails()->isProtected()); + $this->assertTrue($this->garbageCollectorStatus()->isProtected()); } public function testMayHaveFull(): void { - $this->assertTrue($this->withDetails()->isFull()); + $this->assertTrue($this->garbageCollectorStatus()->isFull()); } public function testMayHaveBufferSize(): void { - $this->assertSame(9, $this->withDetails()->bufferSize()); - } - - public function testMayNotHaveExtendedInformation(): void - { - $this->assertFalse($this->withoutDetails()->hasExtendedInformation()); - } - - public function testMayNotHaveRunning(): void - { - $this->expectException(RuntimeException::class); - - $this->withoutDetails()->isRunning(); - } - - public function testMayNotHaveProtected(): void - { - $this->expectException(RuntimeException::class); - - $this->withoutDetails()->isProtected(); - } - - public function testMayNotHaveFull(): void - { - $this->expectException(RuntimeException::class); - - $this->withoutDetails()->isFull(); - } - - public function testMayNotHaveBufferSize(): void - { - $this->expectException(RuntimeException::class); - - $this->withoutDetails()->bufferSize(); - } - - private function withoutDetails(): GarbageCollectorStatus - { - return new GarbageCollectorStatus( - 1, - 2, - 3, - 4, - null, - null, - null, - null, - null, - null, - null, - null, - ); + $this->assertSame(9, $this->garbageCollectorStatus()->bufferSize()); } - private function withDetails(): GarbageCollectorStatus + private function garbageCollectorStatus(): GarbageCollectorStatus { return new GarbageCollectorStatus( 1, diff --git a/tests/unit/Event/Value/Telemetry/HRTimeTest.php b/tests/unit/Event/Value/Telemetry/HRTimeTest.php index 94bd50b09b8..5ac5da9d65e 100644 --- a/tests/unit/Event/Value/Telemetry/HRTimeTest.php +++ b/tests/unit/Event/Value/Telemetry/HRTimeTest.php @@ -20,7 +20,7 @@ final class HRTimeTest extends TestCase { /** - * @return array + * @return array */ public static function provideStartGreaterThanEnd(): array { @@ -47,7 +47,7 @@ public static function provideStartGreaterThanEnd(): array } /** - * @return array + * @return array */ public static function provideStartEndAndDuration(): array { @@ -124,7 +124,7 @@ public function testFromSecondsAndNanosecondsReturnsHRTime(): void } #[DataProvider('provideStartGreaterThanEnd')] - public function testDurationRejectsStartGreaterThanEnd(int $startSeconds, int $startNanoseconds, int $endSeconds, int $endNanoseconds): void + public function testDurationIgnoresStartGreaterThanEnd(int $startSeconds, int $startNanoseconds, int $endSeconds, int $endNanoseconds): void { $start = HRTime::fromSecondsAndNanoseconds( $startSeconds, @@ -136,10 +136,10 @@ public function testDurationRejectsStartGreaterThanEnd(int $startSeconds, int $s $endNanoseconds, ); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Start needs to be smaller.'); + $duration = $end->duration($start); - $end->duration($start); + $this->assertSame(0, $duration->seconds()); + $this->assertSame(0, $duration->nanoseconds()); } #[DataProvider('provideStartEndAndDuration')] diff --git a/tests/unit/Event/Value/Telemetry/InfoTest.php b/tests/unit/Event/Value/Telemetry/InfoTest.php index 6efcb99f459..1bec91ff83c 100644 --- a/tests/unit/Event/Value/Telemetry/InfoTest.php +++ b/tests/unit/Event/Value/Telemetry/InfoTest.php @@ -9,7 +9,6 @@ */ namespace PHPUnit\Event\Telemetry; -use function version_compare; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; @@ -81,16 +80,10 @@ private function info(): Info private function telemetrySystem(): System { - if (version_compare('8.3.0', PHP_VERSION, '>')) { - $garbageCollectorStatusProvider = new Php81GarbageCollectorStatusProvider; - } else { - $garbageCollectorStatusProvider = new Php83GarbageCollectorStatusProvider; - } - return new System( new SystemStopWatch, new SystemMemoryMeter, - $garbageCollectorStatusProvider, + new SystemGarbageCollectorStatusProvider, ); } } diff --git a/tests/unit/Event/Value/Telemetry/SystemMemoryMeterTest.php b/tests/unit/Event/Value/Telemetry/SystemMemoryMeterTest.php deleted file mode 100644 index 842a18dfb93..00000000000 --- a/tests/unit/Event/Value/Telemetry/SystemMemoryMeterTest.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Event\Telemetry; - -use function memory_get_peak_usage; -use function memory_get_usage; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; - -#[CoversClass(SystemMemoryMeter::class)] -#[Small] -final class SystemMemoryMeterTest extends TestCase -{ - public function testMemoryUsageReturnsMemoryUsage(): void - { - $memoryMeter = new SystemMemoryMeter; - - $memoryUsage = MemoryUsage::fromBytes(memory_get_usage(true)); - - $this->assertEquals($memoryUsage, $memoryMeter->memoryUsage()); - } - - public function testPeakMemoryUsageReturnsMemoryPeakUsage(): void - { - $memoryMeter = new SystemMemoryMeter; - - $peakMemoryUsage = MemoryUsage::fromBytes(memory_get_peak_usage(true)); - - $this->assertEquals($peakMemoryUsage, $memoryMeter->peakMemoryUsage()); - } -} diff --git a/tests/unit/Event/Value/Telemetry/SystemTest.php b/tests/unit/Event/Value/Telemetry/SystemTest.php index 87047b64850..40d5c0579fe 100644 --- a/tests/unit/Event/Value/Telemetry/SystemTest.php +++ b/tests/unit/Event/Value/Telemetry/SystemTest.php @@ -24,7 +24,7 @@ public function testSnapshotReturnsSnapshot(): void $clock = new class($time) implements StopWatch { - private readonly \PHPUnit\Event\Telemetry\HRTime $time; + private readonly HRTime $time; public function __construct(HRTime $time) { diff --git a/tests/unit/Event/Value/Test/IssueTriggerTest.php b/tests/unit/Event/Value/Test/IssueTriggerTest.php new file mode 100644 index 00000000000..cbfe4b4ac6d --- /dev/null +++ b/tests/unit/Event/Value/Test/IssueTriggerTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\Code\IssueTrigger; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(IssueTrigger::class)] +#[CoversClassesThatExtendClass(IssueTrigger::class)] +#[Small] +final class IssueTriggerTest extends TestCase +{ + public function testCanBeTest(): void + { + $trigger = IssueTrigger::test(); + + $this->assertTrue($trigger->isTest()); + $this->assertFalse($trigger->isSelf()); + $this->assertFalse($trigger->isDirect()); + $this->assertFalse($trigger->isIndirect()); + $this->assertFalse($trigger->isUnknown()); + $this->assertSame('issue triggered by test code', $trigger->asString()); + } + + public function testCanBeSelf(): void + { + $trigger = IssueTrigger::self(); + + $this->assertTrue($trigger->isSelf()); + $this->assertFalse($trigger->isTest()); + $this->assertFalse($trigger->isDirect()); + $this->assertFalse($trigger->isIndirect()); + $this->assertFalse($trigger->isUnknown()); + $this->assertSame('issue triggered by first-party code calling into first-party code', $trigger->asString()); + } + + public function testCanBeDirect(): void + { + $trigger = IssueTrigger::direct(); + + $this->assertTrue($trigger->isDirect()); + $this->assertFalse($trigger->isTest()); + $this->assertFalse($trigger->isSelf()); + $this->assertFalse($trigger->isIndirect()); + $this->assertFalse($trigger->isUnknown()); + $this->assertSame('issue triggered by first-party code calling into third-party code', $trigger->asString()); + } + + public function testCanBeIndirect(): void + { + $trigger = IssueTrigger::indirect(); + + $this->assertTrue($trigger->isIndirect()); + $this->assertFalse($trigger->isTest()); + $this->assertFalse($trigger->isSelf()); + $this->assertFalse($trigger->isDirect()); + $this->assertFalse($trigger->isUnknown()); + $this->assertSame('issue triggered by third-party code', $trigger->asString()); + } + + public function testCanBeUnknown(): void + { + $trigger = IssueTrigger::unknown(); + + $this->assertFalse($trigger->isTest()); + $this->assertFalse($trigger->isSelf()); + $this->assertFalse($trigger->isDirect()); + $this->assertFalse($trigger->isIndirect()); + $this->assertTrue($trigger->isUnknown()); + $this->assertSame('unknown if issue was triggered in first-party code or third-party code', $trigger->asString()); + } +} diff --git a/tests/unit/Event/Value/Test/TestData/TestDataCollectionTest.php b/tests/unit/Event/Value/Test/TestData/TestDataCollectionTest.php new file mode 100644 index 00000000000..2158c20f95c --- /dev/null +++ b/tests/unit/Event/Value/Test/TestData/TestDataCollectionTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestDataCollection::class)] +#[CoversClass(TestDataCollectionIterator::class)] +#[UsesClass(TestData::class)] +#[UsesClass(DataFromDataProvider::class)] +#[UsesClass(DataFromTestDependency::class)] +#[Small] +final class TestDataCollectionTest extends TestCase +{ + public function testMayBeEmpty(): void + { + $collection = TestDataCollection::fromArray([]); + + $this->assertCount(0, $collection); + $this->assertFalse($collection->hasDataFromDataProvider()); + } + + public function testMayContainDataFromDataProvider(): void + { + $data = $this->dataFromDataProvider(); + $collection = TestDataCollection::fromArray([$data]); + + $this->assertTrue($collection->hasDataFromDataProvider()); + $this->assertSame([$data], $collection->asArray()); + $this->assertSame($data, $collection->dataFromDataProvider()); + } + + public function testMayContainDataFromDependedUponTest(): void + { + $data = $this->dataFromDependedUponTest(); + $collection = TestDataCollection::fromArray([$data]); + + $this->assertFalse($collection->hasDataFromDataProvider()); + $this->assertSame([$data], $collection->asArray()); + } + + public function testExceptionIsRaisedWhenDataFromDataProviderIsAccessedButDoesNotExist(): void + { + $collection = TestDataCollection::fromArray([]); + + $this->expectException(NoDataSetFromDataProviderException::class); + + $collection->dataFromDataProvider(); + } + + public function testIsIterable(): void + { + $data = $this->dataFromDataProvider(); + $collection = TestDataCollection::fromArray([$data]); + + foreach ($collection as $index => $element) { + $this->assertSame(0, $index); + $this->assertSame($data, $element); + } + } + + private function dataFromDataProvider(): DataFromDataProvider + { + return DataFromDataProvider::from( + 'data-set-name', + 'data-as-string', + 'data-as-string-for-output', + ); + } + + private function dataFromDependedUponTest(): DataFromTestDependency + { + return DataFromTestDependency::from('data-as-string'); + } +} diff --git a/tests/unit/Event/Value/Test/TestData/TestDataTest.php b/tests/unit/Event/Value/Test/TestData/TestDataTest.php new file mode 100644 index 00000000000..ee1db09b0a0 --- /dev/null +++ b/tests/unit/Event/Value/Test/TestData/TestDataTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestData; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestData::class)] +#[CoversClassesThatExtendClass(TestData::class)] +#[Small] +final class TestDataTest extends TestCase +{ + public function testDataCanBeFromDataProvider(): void + { + $name = 'data-set-name'; + $dataAsString = 'data-as-string'; + $dataAsStringForOutput = 'data-as-string-for-output'; + + $data = DataFromDataProvider::from( + $name, + $dataAsString, + $dataAsStringForOutput, + ); + + $this->assertTrue($data->isFromDataProvider()); + $this->assertFalse($data->isFromTestDependency()); + $this->assertSame($name, $data->dataSetName()); + $this->assertSame($dataAsString, $data->data()); + $this->assertSame($dataAsStringForOutput, $data->dataAsStringForResultOutput()); + } + + public function testDataCanBeFromDependedUponTest(): void + { + $dataAsString = 'data-as-string'; + + $data = DataFromTestDependency::from($dataAsString); + + $this->assertTrue($data->isFromTestDependency()); + $this->assertFalse($data->isFromDataProvider()); + $this->assertSame($dataAsString, $data->data()); + } +} diff --git a/tests/unit/Event/Value/Test/TestMethodTest.php b/tests/unit/Event/Value/Test/TestMethodTest.php index ab9c918caa5..e87dc676132 100644 --- a/tests/unit/Event/Value/Test/TestMethodTest.php +++ b/tests/unit/Event/Value/Test/TestMethodTest.php @@ -86,6 +86,7 @@ public function testNameReturnsNameWhenTestHasDataFromDataProviderAndDataSetName DataFromDataProvider::from( $dataSetName, 'data', + 'data as string for result output', ), ], ), @@ -99,6 +100,8 @@ public function testNameReturnsNameWhenTestHasDataFromDataProviderAndDataSetName $this->assertSame($expected, $test->name()); $this->assertSame('FooTest::testBar#9000', $test->id()); + $this->assertSame('data', $test->testData()->dataFromDataProvider()->data()); + $this->assertSame('data as string for result output', $test->testData()->dataFromDataProvider()->dataAsStringForResultOutput()); } public function testNameReturnsNameWhenTestHasDataFromDataProviderAndDataSetNameIsString(): void @@ -117,6 +120,7 @@ public function testNameReturnsNameWhenTestHasDataFromDataProviderAndDataSetName DataFromDataProvider::from( $dataSetName, 'data', + 'data as string for result output', ), ], ), @@ -130,5 +134,7 @@ public function testNameReturnsNameWhenTestHasDataFromDataProviderAndDataSetName $this->assertSame($expected, $test->name()); $this->assertSame('FooTest::testBar#bar-9000', $test->id()); + $this->assertSame('data', $test->testData()->dataFromDataProvider()->data()); + $this->assertSame('data as string for result output', $test->testData()->dataFromDataProvider()->dataAsStringForResultOutput()); } } diff --git a/tests/unit/Event/Value/TestSuite/TestSuiteBuilderTest.php b/tests/unit/Event/Value/TestSuite/TestSuiteBuilderTest.php index de36fdf7d90..31e463d6b58 100644 --- a/tests/unit/Event/Value/TestSuite/TestSuiteBuilderTest.php +++ b/tests/unit/Event/Value/TestSuite/TestSuiteBuilderTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestSuite as FrameworkTestSuite; +use PHPUnit\Runner\Filter\Factory; use PHPUnit\TextUI\CliArguments\Builder as CliArgumentsBuilder; use PHPUnit\TextUI\Configuration\Merger as ConfigurationMerger; use PHPUnit\TextUI\XmlConfiguration\Loader as XmlConfigurationLoader; @@ -29,9 +30,23 @@ public function test_Builds_TestSuite_value_object_for_test_suite_loaded_from_XM $this->assertTrue($testSuite->isWithName()); $this->assertStringEndsWith('phpunit.xml', $testSuite->name()); $this->assertSame(3, $testSuite->count()); + $this->assertSame(3, $testSuite->tests()->count()); $this->assertCount(3, $testSuite->tests()); } + public function testBuildCountWithFilter(): void + { + $testSuite = $this->testSuiteFromXmlConfiguration(); + $filterFactory = new Factory; + $filterFactory->addIncludeNameFilter('one'); + $testSuite->injectFilter($filterFactory); + $testSuite = TestSuiteBuilder::from($testSuite); + + $this->assertSame(1, $testSuite->count()); + $this->assertSame(1, $testSuite->tests()->count()); + $this->assertCount(1, $testSuite->tests()); + } + public function test_Builds_TestSuite_value_object_for_test_case_class(): void { $testSuite = TestSuiteBuilder::from($this->testSuiteFromXmlConfiguration()->tests()[0]->tests()[0]); diff --git a/tests/unit/Event/Value/TestSuite/TestSuiteTest.php b/tests/unit/Event/Value/TestSuite/TestSuiteTest.php new file mode 100644 index 00000000000..3c780b13899 --- /dev/null +++ b/tests/unit/Event/Value/TestSuite/TestSuiteTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Event\TestSuite; + +use PHPUnit\Event\Code\TestCollection; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestSuite::class)] +#[CoversClassesThatExtendClass(TestSuite::class)] +#[Small] +final class TestSuiteTest extends TestCase +{ + public function testCanBeTestSuiteForTestClass(): void + { + $className = 'ExampleTest'; + $size = 0; + $tests = TestCollection::fromArray([]); + $file = 'ExampleTest.php'; + $line = 1; + + $testSuite = new TestSuiteForTestClass($className, $size, $tests, $file, $line); + + $this->assertTrue($testSuite->isForTestClass()); + $this->assertFalse($testSuite->isForTestMethodWithDataProvider()); + $this->assertFalse($testSuite->isWithName()); + + $this->assertSame($className, $testSuite->className()); + $this->assertSame($className, $testSuite->name()); + $this->assertSame($size, $testSuite->count()); + $this->assertSame($tests, $testSuite->tests()); + $this->assertSame($file, $testSuite->file()); + $this->assertSame($line, $testSuite->line()); + } + + public function testCanBeTestSuiteForTestMethodWithDataProvider(): void + { + $name = 'ExampleTest::testOne'; + $className = 'ExampleTest'; + $methodName = 'testOne'; + $size = 0; + $tests = TestCollection::fromArray([]); + $file = 'ExampleTest.php'; + $line = 1; + + $testSuite = new TestSuiteForTestMethodWithDataProvider($name, $size, $tests, $className, $methodName, $file, $line); + + $this->assertFalse($testSuite->isForTestClass()); + $this->assertTrue($testSuite->isForTestMethodWithDataProvider()); + $this->assertFalse($testSuite->isWithName()); + + $this->assertSame($name, $testSuite->name()); + $this->assertSame($className, $testSuite->className()); + $this->assertSame($methodName, $testSuite->methodName()); + $this->assertSame($size, $testSuite->count()); + $this->assertSame($tests, $testSuite->tests()); + $this->assertSame($file, $testSuite->file()); + $this->assertSame($line, $testSuite->line()); + } + + public function testCanBeTestSuiteWithName(): void + { + $name = 'the-name'; + $size = 0; + $tests = TestCollection::fromArray([]); + + $testSuite = new TestSuiteWithName($name, $size, $tests); + + $this->assertFalse($testSuite->isForTestClass()); + $this->assertFalse($testSuite->isForTestMethodWithDataProvider()); + $this->assertTrue($testSuite->isWithName()); + + $this->assertSame($name, $testSuite->name()); + $this->assertSame($size, $testSuite->count()); + $this->assertSame($tests, $testSuite->tests()); + } +} diff --git a/tests/unit/Event/Value/ThrowableTest.php b/tests/unit/Event/Value/ThrowableTest.php index 1cf89266d30..3dc846331e6 100644 --- a/tests/unit/Event/Value/ThrowableTest.php +++ b/tests/unit/Event/Value/ThrowableTest.php @@ -29,7 +29,7 @@ public function testCanBeCreatedForThrowableWithoutPrevious(): void $this->assertSame(Exception::class, $t->className()); $this->assertSame('message', $t->message()); $this->assertSame("Exception: message\n", $t->description()); - $this->assertSame(Filter::getFilteredStacktrace($e), $t->stackTrace()); + $this->assertSame(Filter::stackTraceFromThrowableAsString($e), $t->stackTrace()); $this->assertFalse($t->hasPrevious()); $this->expectException(NoPreviousThrowableException::class); @@ -46,7 +46,7 @@ public function testCanBeCreatedForThrowableWithPrevious(): void $this->assertSame(Exception::class, $t->className()); $this->assertSame('second message', $t->message()); $this->assertSame("Exception: second message\n", $t->description()); - $this->assertSame(Filter::getFilteredStacktrace($second), $t->stackTrace()); + $this->assertSame(Filter::stackTraceFromThrowableAsString($second, false), $t->stackTrace()); $this->assertTrue($t->hasPrevious()); $previous = $t->previous(); @@ -54,7 +54,7 @@ public function testCanBeCreatedForThrowableWithPrevious(): void $this->assertSame(Exception::class, $previous->className()); $this->assertSame('first message', $previous->message()); $this->assertSame("Exception: first message\n", $previous->description()); - $this->assertSame(Filter::getFilteredStacktrace($first), $t->stackTrace()); + $this->assertSame(Filter::stackTraceFromThrowableAsString($first), $previous->stackTrace()); $this->assertStringMatchesFormat( <<<'EOD' diff --git a/tests/unit/Framework/Assert/assertArrayHasKeyTest.php b/tests/unit/Framework/Assert/assertArrayHasKeyTest.php new file mode 100644 index 00000000000..82335a66fe6 --- /dev/null +++ b/tests/unit/Framework/Assert/assertArrayHasKeyTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use ArrayAccess; +use ArrayObject; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\TestFixture\SampleArrayAccess; + +#[CoversMethod(Assert::class, 'assertArrayHasKey')] +#[TestDox('assertArrayHasKey()')] +#[Small] +final class assertArrayHasKeyTest extends TestCase +{ + /** + * @return non-empty-list|ArrayAccess}> + */ + public static function successProvider(): array + { + $arrayAccess = new SampleArrayAccess; + $arrayAccess['foo'] = 'bar'; + + $arrayObject = new ArrayObject; + $arrayObject['foo'] = 'bar'; + + return [ + [0, ['foo']], + ['foo', ['foo' => 'bar']], + ['foo', $arrayAccess], + ['foo', $arrayObject], + ]; + } + + /** + * @return non-empty-list|ArrayAccess}> + */ + public static function failureProvider(): array + { + $arrayAccess = new SampleArrayAccess; + $arrayAccess['foo'] = 'bar'; + + $arrayObject = new ArrayObject; + $arrayObject['foo'] = 'bar'; + + return [ + [1, ['foo']], + ['bar', ['foo' => 'bar']], + ['bar', $arrayAccess], + ['bar', $arrayObject], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(int|string $key, array|ArrayAccess $array): void + { + $this->assertArrayHasKey($key, $array); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(int|string $key, array|ArrayAccess $array): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertArrayHasKey($key, $array); + } +} diff --git a/tests/unit/Framework/Assert/assertArrayIsEqualToArrayIgnoringListOfKeysTest.php b/tests/unit/Framework/Assert/assertArrayIsEqualToArrayIgnoringListOfKeysTest.php new file mode 100644 index 00000000000..54249358482 --- /dev/null +++ b/tests/unit/Framework/Assert/assertArrayIsEqualToArrayIgnoringListOfKeysTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertArrayIsEqualToArrayIgnoringListOfKeys')] +#[TestDox('assertArrayIsEqualToArrayIgnoringListOfKeys()')] +#[Small] +final class assertArrayIsEqualToArrayIgnoringListOfKeysTest extends TestCase +{ + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function successProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['b', 1], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + [2.0], + ], + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + [0 => 1, 1 => 3, 'a' => 'b', 'b' => 'b'], + ['b', 1], + ], + ]; + } + + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function failureProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['b'], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + ['1'], + ], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(array $expected, array $actual, array $keysToBeIgnored): void + { + $this->assertArrayIsEqualToArrayIgnoringListOfKeys($expected, $actual, $keysToBeIgnored); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(array $expected, array $actual, array $keysToBeIgnored): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertArrayIsEqualToArrayIgnoringListOfKeys($expected, $actual, $keysToBeIgnored); + } +} diff --git a/tests/unit/Framework/Assert/assertArrayIsEqualToArrayOnlyConsideringListOfKeysTest.php b/tests/unit/Framework/Assert/assertArrayIsEqualToArrayOnlyConsideringListOfKeysTest.php new file mode 100644 index 00000000000..bb33eef2514 --- /dev/null +++ b/tests/unit/Framework/Assert/assertArrayIsEqualToArrayOnlyConsideringListOfKeysTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertArrayIsEqualToArrayOnlyConsideringListOfKeys')] +#[TestDox('assertArrayIsEqualToArrayOnlyConsideringListOfKeys()')] +#[Small] +final class assertArrayIsEqualToArrayOnlyConsideringListOfKeysTest extends TestCase +{ + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function successProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['a', 0], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + [0, '1', '3.0'], + ], + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + [0 => 1, 1 => 3, 'a' => 'b', 'b' => 'b'], + ['a', 0], + ], + ]; + } + + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function failureProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['b'], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + ['1', 2.0, '3.0'], + ], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(array $expected, array $actual, array $keysToBeConsidered): void + { + $this->assertArrayIsEqualToArrayOnlyConsideringListOfKeys($expected, $actual, $keysToBeConsidered); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(array $expected, array $actual, array $keysToBeConsidered): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertArrayIsEqualToArrayOnlyConsideringListOfKeys($expected, $actual, $keysToBeConsidered); + } +} diff --git a/tests/unit/Framework/Assert/assertArrayIsIdenticalToArrayIgnoringListOfKeysTest.php b/tests/unit/Framework/Assert/assertArrayIsIdenticalToArrayIgnoringListOfKeysTest.php new file mode 100644 index 00000000000..d3701c7c952 --- /dev/null +++ b/tests/unit/Framework/Assert/assertArrayIsIdenticalToArrayIgnoringListOfKeysTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertArrayIsIdenticalToArrayIgnoringListOfKeys')] +#[TestDox('assertArrayIsIdenticalToArrayIgnoringListOfKeys()')] +#[Small] +final class assertArrayIsIdenticalToArrayIgnoringListOfKeysTest extends TestCase +{ + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function successProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['b', 1], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + [2.0], + ], + ]; + } + + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function failureProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['b'], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + ['1'], + ], + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + [0 => 1, 1 => 3, 'a' => 'b', 'b' => 'b'], + ['b', 1], + ], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(array $expected, array $actual, array $keysToBeIgnored): void + { + $this->assertArrayIsIdenticalToArrayIgnoringListOfKeys($expected, $actual, $keysToBeIgnored); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(array $expected, array $actual, array $keysToBeIgnored): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertArrayIsIdenticalToArrayIgnoringListOfKeys($expected, $actual, $keysToBeIgnored); + } +} diff --git a/tests/unit/Framework/Assert/assertArrayIsIdenticalToArrayOnlyConsideringListOfKeysTest.php b/tests/unit/Framework/Assert/assertArrayIsIdenticalToArrayOnlyConsideringListOfKeysTest.php new file mode 100644 index 00000000000..7dcdd5c64d5 --- /dev/null +++ b/tests/unit/Framework/Assert/assertArrayIsIdenticalToArrayOnlyConsideringListOfKeysTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys')] +#[TestDox('assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys()')] +#[Small] +final class assertArrayIsIdenticalToArrayOnlyConsideringListOfKeysTest extends TestCase +{ + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function successProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['a', 0], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + [0, '1', '3.0'], + ], + ]; + } + + /** + * @return non-empty-list, 1: array, 2: array}> + */ + public static function failureProvider(): array + { + return [ + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + ['a' => 'b', 'b' => 'b', 0 => 1, 1 => 3], + ['b'], + ], + [ + [0 => 1, '1' => 2, 2.0 => 3, '3.0' => 4], + [0 => 1, '1' => 2, 2.0 => 2, '3.0' => 4], + ['1', 2.0, '3.0'], + ], + [ + ['a' => 'b', 'b' => 'c', 0 => 1, 1 => 2], + [0 => 1, 1 => 3, 'a' => 'b', 'b' => 'b'], + ['a', 0], + ], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(array $expected, array $actual, array $keysToBeConsidered): void + { + $this->assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys($expected, $actual, $keysToBeConsidered); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(array $expected, array $actual, array $keysToBeConsidered): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertArrayIsIdenticalToArrayOnlyConsideringListOfKeys($expected, $actual, $keysToBeConsidered); + } +} diff --git a/tests/unit/Framework/Assert/assertArrayNotHasKeyTest.php b/tests/unit/Framework/Assert/assertArrayNotHasKeyTest.php new file mode 100644 index 00000000000..983bc1948fc --- /dev/null +++ b/tests/unit/Framework/Assert/assertArrayNotHasKeyTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use ArrayAccess; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertArrayNotHasKey')] +#[TestDox('assertArrayNotHasKey()')] +#[Small] +final class assertArrayNotHasKeyTest extends TestCase +{ + #[DataProviderExternal(assertArrayHasKeyTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(int|string $key, array|ArrayAccess $array): void + { + $this->assertArrayNotHasKey($key, $array); + } + + #[DataProviderExternal(assertArrayHasKeyTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(int|string $key, array|ArrayAccess $array): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertArrayNotHasKey($key, $array); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsEqualsTest.php b/tests/unit/Framework/Assert/assertContainsEqualsTest.php new file mode 100644 index 00000000000..814fa70c342 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsEqualsTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertContainsEquals')] +#[TestDox('assertContainsEquals()')] +#[Small] +final class assertContainsEqualsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + $a = new stdClass; + $a->foo = 'bar'; + + $b = new stdClass; + $b->foo = 'bar'; + + return [ + [0, [0]], + [0, ['0']], + [0, [0.0]], + [0, [false]], + [0, [null]], + ['string', ['string']], + [['string'], [['string']]], + [$a, [$b]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $a = new stdClass; + $a->foo = 'bar'; + + $b = new stdClass; + $b->foo = 'baz'; + + return [ + [1, [0]], + [1, [0.0]], + [1, [false]], + [1, [null]], + ['string', ['another-string']], + [['string'], [['another-string']]], + [$a, [$b]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $needle, iterable $haystack): void + { + $this->assertContainsEquals($needle, $haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $needle, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsEquals($needle, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyArrayTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyArrayTest.php new file mode 100644 index 00000000000..6bd29ce7cf4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyArrayTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyArray')] +#[TestDox('assertContainsNotOnlyArray()')] +#[Small] +final class assertContainsNotOnlyArrayTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyArrayTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyArray($haystack); + } + + #[DataProviderExternal(assertContainsOnlyArrayTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyArray($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyBoolTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyBoolTest.php new file mode 100644 index 00000000000..ad6ad8a079c --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyBoolTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyBool')] +#[TestDox('assertContainsNotOnlyBool()')] +#[Small] +final class assertContainsNotOnlyBoolTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyBoolTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyBool($haystack); + } + + #[DataProviderExternal(assertContainsOnlyBoolTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyBool($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyCallableTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyCallableTest.php new file mode 100644 index 00000000000..7fe565c385a --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyCallableTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyCallable')] +#[TestDox('assertContainsNotOnlyCallable()')] +#[Small] +final class assertContainsNotOnlyCallableTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyCallableTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyCallable($haystack); + } + + #[DataProviderExternal(assertContainsOnlyCallableTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyCallable($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyClosedResourceTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyClosedResourceTest.php new file mode 100644 index 00000000000..f4a503184be --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyClosedResourceTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyClosedResource')] +#[TestDox('assertContainsNotOnlyClosedResource()')] +#[Small] +final class assertContainsNotOnlyClosedResourceTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyClosedResourceTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyClosedResource($haystack); + } + + #[DataProviderExternal(assertContainsOnlyClosedResourceTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyClosedResource($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyFloatTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyFloatTest.php new file mode 100644 index 00000000000..7d49a99eea0 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyFloatTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyFloat')] +#[TestDox('assertContainsNotOnlyFloat()')] +#[Small] +final class assertContainsNotOnlyFloatTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyFloatTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyFloat($haystack); + } + + #[DataProviderExternal(assertContainsOnlyFloatTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyFloat($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyInstancesOfTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyInstancesOfTest.php new file mode 100644 index 00000000000..f466c8476fb --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyInstancesOfTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyInstancesOf')] +#[TestDox('assertContainsNotOnlyInstancesOf()')] +#[Small] +final class assertContainsNotOnlyInstancesOfTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyInstancesOfTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, iterable $haystack): void + { + $this->assertContainsNotOnlyInstancesOf($type, $haystack); + } + + #[DataProviderExternal(assertContainsOnlyInstancesOfTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyInstancesOf($type, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyIntTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyIntTest.php new file mode 100644 index 00000000000..c60b24c5e07 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyIntTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyInt')] +#[TestDox('assertContainsNotOnlyInt()')] +#[Small] +final class assertContainsNotOnlyIntTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyIntTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyInt($haystack); + } + + #[DataProviderExternal(assertContainsOnlyIntTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyInt($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyIterableTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyIterableTest.php new file mode 100644 index 00000000000..fad89055b1c --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyIterableTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyIterable')] +#[TestDox('assertContainsNotOnlyIterable()')] +#[Small] +final class assertContainsNotOnlyIterableTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyIterableTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyIterable($haystack); + } + + #[DataProviderExternal(assertContainsOnlyIterableTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyIterable($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyNullTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyNullTest.php new file mode 100644 index 00000000000..61ca652ce87 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyNullTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyNull')] +#[TestDox('assertContainsNotOnlyNull()')] +#[Small] +final class assertContainsNotOnlyNullTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyNullTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyNull($haystack); + } + + #[DataProviderExternal(assertContainsOnlyNullTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyNull($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyNumericTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyNumericTest.php new file mode 100644 index 00000000000..e4d302a58dd --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyNumericTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyNumeric')] +#[TestDox('assertContainsNotOnlyNumeric()')] +#[Small] +final class assertContainsNotOnlyNumericTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyNumericTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyNumeric($haystack); + } + + #[DataProviderExternal(assertContainsOnlyNumericTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyNumeric($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyObjectTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyObjectTest.php new file mode 100644 index 00000000000..6931f382686 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyObjectTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyObject')] +#[TestDox('assertContainsNotOnlyObject()')] +#[Small] +final class assertContainsNotOnlyObjectTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyObjectTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyObject($haystack); + } + + #[DataProviderExternal(assertContainsOnlyObjectTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyObject($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyResourceTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyResourceTest.php new file mode 100644 index 00000000000..075386d4c23 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyResourceTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyResource')] +#[TestDox('assertContainsNotOnlyResource()')] +#[Small] +final class assertContainsNotOnlyResourceTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyResourceTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyResource($haystack); + } + + #[DataProviderExternal(assertContainsOnlyResourceTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyResource($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyScalarTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyScalarTest.php new file mode 100644 index 00000000000..eb24151adc4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyScalarTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyScalar')] +#[TestDox('assertContainsNotOnlyScalar()')] +#[Small] +final class assertContainsNotOnlyScalarTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyScalarTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyScalar($haystack); + } + + #[DataProviderExternal(assertContainsOnlyScalarTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyScalar($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsNotOnlyStringTest.php b/tests/unit/Framework/Assert/assertContainsNotOnlyStringTest.php new file mode 100644 index 00000000000..771bf0b092e --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsNotOnlyStringTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsNotOnlyString')] +#[TestDox('assertContainsNotOnlyString()')] +#[Small] +final class assertContainsNotOnlyStringTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyStringTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsNotOnlyString($haystack); + } + + #[DataProviderExternal(assertContainsOnlyStringTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsNotOnlyString($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php b/tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php new file mode 100644 index 00000000000..adb56c27fdd --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyArray')] +#[TestDox('assertContainsOnlyArray()')] +#[Small] +final class assertContainsOnlyArrayTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[[]]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyArray($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyArray($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyBoolTest.php b/tests/unit/Framework/Assert/assertContainsOnlyBoolTest.php new file mode 100644 index 00000000000..2ef525be2c1 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyBoolTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyBool')] +#[TestDox('assertContainsOnlyBool()')] +#[Small] +final class assertContainsOnlyBoolTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[true]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyBool($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyBool($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyCallableTest.php b/tests/unit/Framework/Assert/assertContainsOnlyCallableTest.php new file mode 100644 index 00000000000..0344fafbb26 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyCallableTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyCallable')] +#[TestDox('assertContainsOnlyCallable()')] +#[Small] +final class assertContainsOnlyCallableTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + $callable = static function (): void + {}; + + return [ + [[$callable]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyCallable($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyCallable($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyClosedResourceTest.php b/tests/unit/Framework/Assert/assertContainsOnlyClosedResourceTest.php new file mode 100644 index 00000000000..2545d1997e2 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyClosedResourceTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyClosedResource')] +#[TestDox('assertContainsOnlyClosedResource()')] +#[Small] +final class assertContainsOnlyClosedResourceTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + $resource = fopen(__FILE__, 'r'); + + fclose($resource); + + return [ + [[$resource]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyClosedResource($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyClosedResource($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyFloatTest.php b/tests/unit/Framework/Assert/assertContainsOnlyFloatTest.php new file mode 100644 index 00000000000..3c51bc740a5 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyFloatTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyFloat')] +#[TestDox('assertContainsOnlyFloat()')] +#[Small] +final class assertContainsOnlyFloatTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[0.0]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyFloat($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyFloat($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyInstancesOfTest.php b/tests/unit/Framework/Assert/assertContainsOnlyInstancesOfTest.php new file mode 100644 index 00000000000..47a703aaf81 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyInstancesOfTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertContainsOnlyInstancesOf')] +#[TestDox('assertContainsOnlyInstancesOf()')] +#[Small] +final class assertContainsOnlyInstancesOfTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [stdClass::class, [new stdClass]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [stdClass::class, [null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, iterable $haystack): void + { + $this->assertContainsOnlyInstancesOf($type, $haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyInstancesOf($type, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyIntTest.php b/tests/unit/Framework/Assert/assertContainsOnlyIntTest.php new file mode 100644 index 00000000000..e433ffb9b74 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyIntTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyInt')] +#[TestDox('assertContainsOnlyInt()')] +#[Small] +final class assertContainsOnlyIntTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[0]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyInt($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyInt($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyIterableTest.php b/tests/unit/Framework/Assert/assertContainsOnlyIterableTest.php new file mode 100644 index 00000000000..d959e0210d8 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyIterableTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyIterable')] +#[TestDox('assertContainsOnlyIterable()')] +#[Small] +final class assertContainsOnlyIterableTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[[]]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyIterable($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyIterable($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyNullTest.php b/tests/unit/Framework/Assert/assertContainsOnlyNullTest.php new file mode 100644 index 00000000000..6ddcf346b71 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyNullTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyNull')] +#[TestDox('assertContainsOnlyNull()')] +#[Small] +final class assertContainsOnlyNullTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[null]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[true]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyNull($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyNull($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyNumericTest.php b/tests/unit/Framework/Assert/assertContainsOnlyNumericTest.php new file mode 100644 index 00000000000..85d90e006b9 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyNumericTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyNumeric')] +#[TestDox('assertContainsOnlyNumeric()')] +#[Small] +final class assertContainsOnlyNumericTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [['1.0']], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyNumeric($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyNumeric($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyObjectTest.php b/tests/unit/Framework/Assert/assertContainsOnlyObjectTest.php new file mode 100644 index 00000000000..875a033881a --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyObjectTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertContainsOnlyObject')] +#[TestDox('assertContainsOnlyObject()')] +#[Small] +final class assertContainsOnlyObjectTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[new stdClass]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyObject($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyObject($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyResourceTest.php b/tests/unit/Framework/Assert/assertContainsOnlyResourceTest.php new file mode 100644 index 00000000000..21ec16022e4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyResourceTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyResource')] +#[TestDox('assertContainsOnlyResource()')] +#[Small] +final class assertContainsOnlyResourceTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + $resource = fopen(__FILE__, 'r'); + + return [ + [[$resource]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyResource($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyResource($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyScalarTest.php b/tests/unit/Framework/Assert/assertContainsOnlyScalarTest.php new file mode 100644 index 00000000000..c5275fc1af3 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyScalarTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyScalar')] +#[TestDox('assertContainsOnlyScalar()')] +#[Small] +final class assertContainsOnlyScalarTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [['string']], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyScalar($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyScalar($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyStringTest.php b/tests/unit/Framework/Assert/assertContainsOnlyStringTest.php new file mode 100644 index 00000000000..1ea75ddc544 --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyStringTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertContainsOnlyString')] +#[TestDox('assertContainsOnlyString()')] +#[Small] +final class assertContainsOnlyStringTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [['string']], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(iterable $haystack): void + { + $this->assertContainsOnlyString($haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnlyString($haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsOnlyTest.php b/tests/unit/Framework/Assert/assertContainsOnlyTest.php new file mode 100644 index 00000000000..b57970ce89d --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsOnlyTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertContainsOnly')] +#[TestDox('assertContainsOnly()')] +#[Small] +#[IgnorePhpunitDeprecations] +final class assertContainsOnlyTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['array', [[1, 2, 3]]], + ['boolean', [true, false]], + ['bool', [true, false]], + ['float', [1.0, 2.0, 3.0]], + ['integer', [1, 2, 3]], + ['int', [1, 2, 3]], + ['null', [null]], + ['numeric', [1, 2.0, '3', '4.0']], + ['object', [new stdClass]], + ['resource', [fopen(__FILE__, 'r')]], + ['scalar', [true, 1.0, 1, 'string']], + ['string', ['string']], + [stdClass::class, [new stdClass]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['array', [[1, 2, 3], null]], + ['boolean', [true, false, null]], + ['bool', [true, false, null]], + ['float', [1.0, 2.0, 3.0, null]], + ['integer', [1, 2, 3, null]], + ['int', [1, 2, 3, null]], + ['null', [null, 0]], + ['numeric', [1, 2.0, '3', '4.0', null]], + ['object', [new stdClass, null]], + ['resource', [fopen(__FILE__, 'r'), null]], + ['scalar', [true, 1.0, 1, 'string', null]], + ['string', ['string', null]], + [stdClass::class, [new stdClass, null]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, iterable $haystack): void + { + $this->assertContainsOnly($type, $haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContainsOnly($type, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertContainsTest.php b/tests/unit/Framework/Assert/assertContainsTest.php new file mode 100644 index 00000000000..c471f00084b --- /dev/null +++ b/tests/unit/Framework/Assert/assertContainsTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertContains')] +#[TestDox('assertContains()')] +#[Small] +final class assertContainsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + $a = new stdClass; + + return [ + [0, [0]], + [0.0, [0.0]], + [false, [false]], + [null, [null]], + ['string', ['string']], + [['string'], [['string']]], + [$a, [$a]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [0, ['0']], + [0, [0.0]], + [0, [false]], + [0, [null]], + [new stdClass, [new stdClass]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $needle, iterable $haystack): void + { + $this->assertContains($needle, $haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $needle, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertContains($needle, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertCountTest.php b/tests/unit/Framework/Assert/assertCountTest.php new file mode 100644 index 00000000000..ba1ab1cc847 --- /dev/null +++ b/tests/unit/Framework/Assert/assertCountTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function PHPUnit\TestFixture\Generator\f; +use ArrayIterator; +use Countable; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertCount')] +#[CoversClass(GeneratorNotSupportedException::class)] +#[TestDox('assertCount()')] +#[Small] +final class assertCountTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [2, [1, 2]], + [2, new ArrayIterator([1, 2])], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [2, [1, 2, 3]], + [2, new ArrayIterator([1, 2, 3])], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(int $expectedCount, Countable|iterable $haystack): void + { + $this->assertCount($expectedCount, $haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(int $expectedCount, Countable|iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertCount($expectedCount, $haystack); + } + + public function testDoesNotSupportGenerators(): void + { + $this->expectException(GeneratorNotSupportedException::class); + $this->expectExceptionMessage('Passing an argument of type Generator for the $haystack parameter is not supported'); + + $this->assertCount(0, f()); + } +} diff --git a/tests/unit/Framework/Assert/assertDirectoryDoesNotExistTest.php b/tests/unit/Framework/Assert/assertDirectoryDoesNotExistTest.php new file mode 100644 index 00000000000..1248bbb65c2 --- /dev/null +++ b/tests/unit/Framework/Assert/assertDirectoryDoesNotExistTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDirectoryDoesNotExist')] +#[TestDox('assertDirectoryDoesNotExist()')] +#[Small] +final class assertDirectoryDoesNotExistTest extends TestCase +{ + #[DataProviderExternal(assertDirectoryExistsTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $directory): void + { + $this->assertDirectoryDoesNotExist($directory); + } + + #[DataProviderExternal(assertDirectoryExistsTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $directory): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertDirectoryDoesNotExist($directory); + } +} diff --git a/tests/unit/Framework/Assert/assertDirectoryExistsTest.php b/tests/unit/Framework/Assert/assertDirectoryExistsTest.php new file mode 100644 index 00000000000..9609f7b48fd --- /dev/null +++ b/tests/unit/Framework/Assert/assertDirectoryExistsTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDirectoryExists')] +#[TestDox('assertDirectoryExists()')] +#[Small] +final class assertDirectoryExistsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [__DIR__], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [__DIR__ . '/DoesNotExist'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $directory): void + { + $this->assertDirectoryExists($directory); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $directory): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertDirectoryExists($directory); + } +} diff --git a/tests/unit/Framework/Assert/assertDirectoryIsNotReadableTest.php b/tests/unit/Framework/Assert/assertDirectoryIsNotReadableTest.php new file mode 100644 index 00000000000..6518c9b7f96 --- /dev/null +++ b/tests/unit/Framework/Assert/assertDirectoryIsNotReadableTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DIRECTORY_SEPARATOR; +use const PHP_OS_FAMILY; +use function mkdir; +use function octdec; +use function rmdir; +use function sys_get_temp_dir; +use function uniqid; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDirectoryIsNotReadable')] +#[TestDox('assertDirectoryIsNotReadable()')] +#[Small] +final class assertDirectoryIsNotReadableTest extends TestCase +{ + private string $directory; + + protected function setUp(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + $this->directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(__CLASS__ . '_', true); + } + + protected function tearDown(): void + { + @rmdir($this->directory); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + mkdir($this->directory, octdec('0')); + + $this->assertDirectoryIsNotReadable($this->directory); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertDirectoryIsNotReadable(__DIR__); + } +} diff --git a/tests/unit/Framework/Assert/assertDirectoryIsNotWritableTest.php b/tests/unit/Framework/Assert/assertDirectoryIsNotWritableTest.php new file mode 100644 index 00000000000..d7fd7de6a99 --- /dev/null +++ b/tests/unit/Framework/Assert/assertDirectoryIsNotWritableTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DIRECTORY_SEPARATOR; +use const PHP_OS_FAMILY; +use function mkdir; +use function octdec; +use function rmdir; +use function sys_get_temp_dir; +use function uniqid; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDirectoryIsNotWritable')] +#[TestDox('assertDirectoryIsNotWritable()')] +#[Small] +final class assertDirectoryIsNotWritableTest extends TestCase +{ + private string $directory; + + protected function setUp(): void + { + $this->directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(__CLASS__ . '_', true); + } + + protected function tearDown(): void + { + @rmdir($this->directory); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + mkdir($this->directory, octdec('0')); + + $this->assertDirectoryIsNotWritable($this->directory); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertDirectoryIsNotWritable(__DIR__); + } +} diff --git a/tests/unit/Framework/Assert/assertDirectoryIsReadableTest.php b/tests/unit/Framework/Assert/assertDirectoryIsReadableTest.php new file mode 100644 index 00000000000..3e41b1146f2 --- /dev/null +++ b/tests/unit/Framework/Assert/assertDirectoryIsReadableTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DIRECTORY_SEPARATOR; +use const PHP_OS_FAMILY; +use function mkdir; +use function octdec; +use function rmdir; +use function sys_get_temp_dir; +use function uniqid; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDirectoryIsReadable')] +#[TestDox('assertDirectoryIsReadable()')] +#[Small] +final class assertDirectoryIsReadableTest extends TestCase +{ + private string $directory; + + protected function setUp(): void + { + $this->directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(__CLASS__ . '_', true); + } + + protected function tearDown(): void + { + @rmdir($this->directory); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertDirectoryIsReadable(__DIR__); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + mkdir($this->directory, octdec('0')); + + $this->expectException(AssertionFailedError::class); + + $this->assertDirectoryIsReadable($this->directory); + } +} diff --git a/tests/unit/Framework/Assert/assertDirectoryIsWritableTest.php b/tests/unit/Framework/Assert/assertDirectoryIsWritableTest.php new file mode 100644 index 00000000000..9043913b132 --- /dev/null +++ b/tests/unit/Framework/Assert/assertDirectoryIsWritableTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DIRECTORY_SEPARATOR; +use const PHP_OS_FAMILY; +use function mkdir; +use function octdec; +use function rmdir; +use function sys_get_temp_dir; +use function uniqid; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDirectoryIsWritable')] +#[TestDox('assertDirectoryIsWritable()')] +#[Small] +final class assertDirectoryIsWritableTest extends TestCase +{ + private string $directory; + + protected function setUp(): void + { + $this->directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(__CLASS__ . '_', true); + } + + protected function tearDown(): void + { + @rmdir($this->directory); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertDirectoryIsWritable(__DIR__); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + mkdir($this->directory, octdec('0')); + + $this->expectException(AssertionFailedError::class); + + $this->assertDirectoryIsWritable($this->directory); + } +} diff --git a/tests/unit/Framework/Assert/assertDoesNotMatchRegularExpressionTest.php b/tests/unit/Framework/Assert/assertDoesNotMatchRegularExpressionTest.php new file mode 100644 index 00000000000..953facdba90 --- /dev/null +++ b/tests/unit/Framework/Assert/assertDoesNotMatchRegularExpressionTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertDoesNotMatchRegularExpression')] +#[TestDox('assertDoesNotMatchRegularExpression()')] +#[Small] +final class assertDoesNotMatchRegularExpressionTest extends TestCase +{ + #[DataProviderExternal(assertMatchesRegularExpressionTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $pattern, string $string): void + { + $this->assertDoesNotMatchRegularExpression($pattern, $string); + } + + #[DataProviderExternal(assertMatchesRegularExpressionTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $pattern, string $string): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertDoesNotMatchRegularExpression($pattern, $string); + } +} diff --git a/tests/unit/Framework/Assert/assertEmptyTest.php b/tests/unit/Framework/Assert/assertEmptyTest.php new file mode 100644 index 00000000000..f90fdf27b84 --- /dev/null +++ b/tests/unit/Framework/Assert/assertEmptyTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function PHPUnit\TestFixture\Generator\f; +use Countable; +use EmptyIterator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertEmpty')] +#[CoversClass(GeneratorNotSupportedException::class)] +#[TestDox('assertEmpty()')] +#[Small] +final class assertEmptyTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[]], + [''], + [null], + [false], + ['0'], + [0], + [new EmptyIterator], + [ + new class implements Countable + { + public function count(): int + { + return 0; + } + }, + ], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[0]], + [true], + ['1'], + [ + new class implements Countable + { + public function count(): int + { + return 1; + } + }, + ], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertEmpty($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertEmpty($actual); + } + + public function testDoesNotSupportGenerators(): void + { + $this->expectException(GeneratorNotSupportedException::class); + $this->expectExceptionMessage('Passing an argument of type Generator for the $actual parameter is not supported'); + + $this->assertEmpty(f()); + } +} diff --git a/tests/unit/Framework/Assert/assertEqualsCanonicalizingTest.php b/tests/unit/Framework/Assert/assertEqualsCanonicalizingTest.php new file mode 100644 index 00000000000..6d6268b146d --- /dev/null +++ b/tests/unit/Framework/Assert/assertEqualsCanonicalizingTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertEqualsCanonicalizing')] +#[TestDox('assertEqualsCanonicalizing()')] +#[Small] +final class assertEqualsCanonicalizingTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[3, 2, 1], [2, 3, 1]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[3, 2, 1], [2, 3, 4]], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertEqualsCanonicalizing($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertEqualsCanonicalizing($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertEqualsIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertEqualsIgnoringCaseTest.php new file mode 100644 index 00000000000..6809fa5e64c --- /dev/null +++ b/tests/unit/Framework/Assert/assertEqualsIgnoringCaseTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertEqualsIgnoringCase')] +#[TestDox('assertEqualsIgnoringCase()')] +#[Small] +final class assertEqualsIgnoringCaseTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['a', 'A'], + [['a'], ['A']], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['a', 'B'], + [['a'], ['B']], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertEqualsIgnoringCase($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertEqualsIgnoringCase($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertEqualsTest.php b/tests/unit/Framework/Assert/assertEqualsTest.php new file mode 100644 index 00000000000..8740104aee4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertEqualsTest.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const NAN; +use function acos; +use function array_merge; +use function fopen; +use DateTimeImmutable; +use DateTimeZone; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\TestFixture\Author; +use PHPUnit\TestFixture\Book; +use PHPUnit\TestFixture\ClassWithToString; +use PHPUnit\TestFixture\SampleClass; +use PHPUnit\TestFixture\Struct; +use PHPUnit\Util\Xml\Loader as XmlLoader; +use SplObjectStorage; +use stdClass; + +#[CoversMethod(Assert::class, 'assertEquals')] +#[TestDox('assertEquals()')] +#[Small] +final class assertEqualsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return array_merge(self::equalValues(), assertSameTest::sameValues()); + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return self::notEqualValues(); + } + + /** + * @return non-empty-list + */ + public static function equalValues(): array + { + // cyclic dependencies + $book1 = new Book; + $book1->author = new Author('Terry Pratchett'); + $book1->author->books[] = $book1; + $book2 = new Book; + $book2->author = new Author('Terry Pratchett'); + $book2->author->books[] = $book2; + + $object1 = new SampleClass(4, 8, 15); + $object2 = new SampleClass(4, 8, 15); + $storage1 = new SplObjectStorage; + $storage1->attach($object1); + $storage2 = new SplObjectStorage; + $storage2->attach($object1); + + return [ + // arrays + [['a' => 1, 'b' => 2], ['b' => 2, 'a' => 1]], + [[1], ['1']], + // objects + [$object1, $object2], + [$book1, $book2], + // SplObjectStorage + [$storage1, $storage2], + // DOMDocument + [ + (new XmlLoader)->load(''), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load(''), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load(''), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load("\n \n"), + (new XmlLoader)->load(''), + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + ], + [ + new DateTimeImmutable('2013-03-29', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29', new DateTimeZone('America/New_York')), + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 03:13:35', new DateTimeZone('America/Chicago')), + ], + [ + new DateTimeImmutable('2013-03-30', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 23:00:00', new DateTimeZone('America/Chicago')), + ], + [ + new DateTimeImmutable('@1364616000'), + new DateTimeImmutable('2013-03-29 23:00:00', new DateTimeZone('America/Chicago')), + ], + [ + new DateTimeImmutable('2013-03-29T05:13:35-0500'), + new DateTimeImmutable('2013-03-29T04:13:35-0600'), + ], + // Exception + // array(new Exception('Exception 1'), new Exception('Exception 1')), + // mixed types + [0, '0'], + ['0', 0], + [2.3, '2.3'], + ['2.3', 2.3], + [1, 1.0], + [1.0, '1'], + [1 / 3, '0.3333333333333333'], + [1 - 2 / 3, '0.33333333333333337'], + [5.5E+123, '5.5E+123'], + [5.5E-123, '5.5E-123'], + ['string representation', new ClassWithToString], + [new ClassWithToString, 'string representation'], + ]; + } + + /** + * @return non-empty-list + */ + public static function notEqualValues(): array + { + // cyclic dependencies + $book1 = new Book; + $book1->author = new Author('Terry Pratchett'); + $book1->author->books[] = $book1; + $book2 = new Book; + $book2->author = new Author('Terry Pratch'); + $book2->author->books[] = $book2; + + $book3 = new Book; + $book3->author = 'Terry Pratchett'; + $book4 = new stdClass; + $book4->author = 'Terry Pratchett'; + + $object1 = new SampleClass(4, 8, 15); + $object2 = new SampleClass(16, 23, 42); + $object3 = new SampleClass(4, 8, 15); + $storage1 = new SplObjectStorage; + $storage1->attach($object1); + $storage2 = new SplObjectStorage; + $storage2->attach($object3); // same content, different object + + $file = TEST_FILES_PATH . 'foo.xml'; + + return [ + [true, false], + // strings + ['a', 'b'], + ['a', 'A'], + // https://github.com/sebastianbergmann/phpunit/issues/1023 + ['9E6666666', '9E7777777'], + // integers + [1, 2], + [2, 1], + // floats + [2.3, 4.2], + [2.3, 4.2, 0.5], + [[2.3], [4.2], 0.5], + [[[2.3]], [[4.2]], 0.5], + [new Struct(2.3), new Struct(4.2), 0.5], + [[new Struct(2.3)], [new Struct(4.2)], 0.5], + [1 / 3, 1 - 2 / 3], + [1 / 3, '0.33333333333333337'], + [1 - 2 / 3, '3333333333333333'], + [5.5E+123, 5.6E+123], + [5.5E-123, 5.6E-123], + [5.5E+123, 5.5E-123], + // NAN + [NAN, NAN], + // arrays + [[], [0 => 1]], + [[0 => 1], []], + [[0 => null], []], + [[0 => 1, 1 => 2], [0 => 1, 1 => 3]], + [['a', 'b' => [1, 2]], ['a', 'b' => [2, 1]]], + // objects + [new SampleClass(4, 8, 15), new SampleClass(16, 23, 42)], + [$object1, $object2], + [$book1, $book2], + [$book3, $book4], // same content, different class + // resources + [fopen($file, 'r'), fopen($file, 'r')], + // SplObjectStorage + [$storage1, $storage2], + // DOMDocument + [ + (new XmlLoader)->load(''), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load(''), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load(' bar '), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load(''), + (new XmlLoader)->load(''), + ], + [ + (new XmlLoader)->load(' bar '), + (new XmlLoader)->load(' bir '), + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 03:13:35', new DateTimeZone('America/New_York')), + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 03:13:35', new DateTimeZone('America/New_York')), + 3500, + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 05:13:35', new DateTimeZone('America/New_York')), + 3500, + ], + [ + new DateTimeImmutable('2013-03-29', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-30', new DateTimeZone('America/New_York')), + ], + [ + new DateTimeImmutable('2013-03-29', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-30', new DateTimeZone('America/New_York')), + 43200, + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/Chicago')), + ], + [ + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/Chicago')), + 3500, + ], + [ + new DateTimeImmutable('2013-03-30', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-30', new DateTimeZone('America/Chicago')), + ], + [ + new DateTimeImmutable('2013-03-29T05:13:35-0600'), + new DateTimeImmutable('2013-03-29T04:13:35-0600'), + ], + [ + new DateTimeImmutable('2013-03-29T05:13:35-0600'), + new DateTimeImmutable('2013-03-29T05:13:35-0500'), + ], + // Exception + // array(new Exception('Exception 1'), new Exception('Exception 2')), + // different types + [new SampleClass(4, 8, 15), false], + [false, new SampleClass(4, 8, 15)], + [[0 => 1, 1 => 2], false], + [false, [0 => 1, 1 => 2]], + [[], new stdClass], + [new stdClass, []], + // PHP: 0 == 'Foobar' => true! + // We want these values to differ + [0, 'Foobar'], + ['Foobar', 0], + [3, acos(8)], + [acos(8), 3], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertEquals($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertEquals($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertEqualsWithDeltaTest.php b/tests/unit/Framework/Assert/assertEqualsWithDeltaTest.php new file mode 100644 index 00000000000..93efad122c0 --- /dev/null +++ b/tests/unit/Framework/Assert/assertEqualsWithDeltaTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertEqualsWithDelta')] +#[TestDox('assertEqualsWithDelta()')] +#[Small] +final class assertEqualsWithDeltaTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [2.3, 2.5, 0.5], + [[2.3], [2.5], 0.5], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [2.3, 3.5, 0.5], + [[2.3], [3.5], 0.5], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual, float $delta): void + { + $this->assertEqualsWithDelta($expected, $actual, $delta); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual, float $delta): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertEqualsWithDelta($expected, $actual, $delta); + } +} diff --git a/tests/unit/Framework/Assert/assertFalseTest.php b/tests/unit/Framework/Assert/assertFalseTest.php new file mode 100644 index 00000000000..caea5451d7b --- /dev/null +++ b/tests/unit/Framework/Assert/assertFalseTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFalse')] +#[TestDox('assertFalse()')] +#[Small] +final class assertFalseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFalse(false); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFalse(true); + } +} diff --git a/tests/unit/Framework/Assert/assertFileDoesNotExistTest.php b/tests/unit/Framework/Assert/assertFileDoesNotExistTest.php new file mode 100644 index 00000000000..146bcd753e7 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileDoesNotExistTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileDoesNotExist')] +#[TestDox('assertFileDoesNotExist()')] +#[Small] +final class assertFileDoesNotExistTest extends TestCase +{ + #[DataProviderExternal(assertFileExistsTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $directory): void + { + $this->assertFileDoesNotExist($directory); + } + + #[DataProviderExternal(assertFileExistsTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $directory): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileDoesNotExist($directory); + } +} diff --git a/tests/unit/Framework/Assert/assertFileEqualsCanonicalizingTest.php b/tests/unit/Framework/Assert/assertFileEqualsCanonicalizingTest.php new file mode 100644 index 00000000000..64100d31c13 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileEqualsCanonicalizingTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileEqualsCanonicalizing')] +#[TestDox('assertFileEqualsCanonicalizing()')] +#[Small] +final class assertFileEqualsCanonicalizingTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileNotEquals(TEST_FILES_PATH . 'foo.txt', TEST_FILES_PATH . 'bar.txt'); + $this->assertFileEqualsCanonicalizing(TEST_FILES_PATH . 'foo.txt', TEST_FILES_PATH . 'foo.txt'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileEqualsCanonicalizing(TEST_FILES_PATH . 'foo.txt', TEST_FILES_PATH . 'foo.xml'); + } +} diff --git a/tests/unit/Framework/Assert/assertFileEqualsIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertFileEqualsIgnoringCaseTest.php new file mode 100644 index 00000000000..462914254de --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileEqualsIgnoringCaseTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileEqualsIgnoringCase')] +#[TestDox('assertFileEqualsIgnoringCase()')] +#[Small] +final class assertFileEqualsIgnoringCaseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileEqualsIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'fooUppercase.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileEqualsIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertFileEqualsTest.php b/tests/unit/Framework/Assert/assertFileEqualsTest.php new file mode 100644 index 00000000000..2e1d3c36750 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileEqualsTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileEquals')] +#[TestDox('assertFileEquals()')] +#[Small] +final class assertFileEqualsTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileEquals( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'foo.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileEquals( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertFileExistsTest.php b/tests/unit/Framework/Assert/assertFileExistsTest.php new file mode 100644 index 00000000000..17c67fb501b --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileExistsTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileExists')] +#[TestDox('assertFileExists()')] +#[Small] +final class assertFileExistsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [__FILE__], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [__DIR__ . '/DoesNotExist'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $filename): void + { + $this->assertFileExists($filename); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $filename): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileExists($filename); + } +} diff --git a/tests/unit/Framework/Assert/assertFileIsNotReadableTest.php b/tests/unit/Framework/Assert/assertFileIsNotReadableTest.php new file mode 100644 index 00000000000..4454b7aa3cc --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileIsNotReadableTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_OS_FAMILY; +use function chmod; +use function octdec; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileIsNotReadable')] +#[TestDox('assertFileIsNotReadable()')] +#[Small] +final class assertFileIsNotReadableTest extends TestCase +{ + private string $file; + + protected function setUp(): void + { + $this->file = tempnam(sys_get_temp_dir(), __CLASS__); + } + + protected function tearDown(): void + { + @unlink($this->file); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + chmod($this->file, octdec('0')); + + $this->assertFileIsNotReadable($this->file); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileIsNotReadable(__FILE__); + } +} diff --git a/tests/unit/Framework/Assert/assertFileIsNotWritableTest.php b/tests/unit/Framework/Assert/assertFileIsNotWritableTest.php new file mode 100644 index 00000000000..06977fd1e55 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileIsNotWritableTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_OS_FAMILY; +use function chmod; +use function octdec; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileIsNotWritable')] +#[TestDox('assertFileIsNotWritable()')] +#[Small] +final class assertFileIsNotWritableTest extends TestCase +{ + private string $file; + + protected function setUp(): void + { + $this->file = tempnam(sys_get_temp_dir(), __CLASS__); + } + + protected function tearDown(): void + { + @unlink($this->file); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + chmod($this->file, octdec('0')); + + $this->assertFileIsNotWritable($this->file); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileIsNotWritable(__FILE__); + } +} diff --git a/tests/unit/Framework/Assert/assertFileIsReadableTest.php b/tests/unit/Framework/Assert/assertFileIsReadableTest.php new file mode 100644 index 00000000000..c01fae58a40 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileIsReadableTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_OS_FAMILY; +use function chmod; +use function octdec; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileIsReadable')] +#[TestDox('assertFileIsReadable()')] +#[Small] +final class assertFileIsReadableTest extends TestCase +{ + private string $file; + + protected function setUp(): void + { + $this->file = tempnam(sys_get_temp_dir(), __CLASS__); + } + + protected function tearDown(): void + { + @unlink($this->file); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileIsReadable(__FILE__); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + chmod($this->file, octdec('0')); + + $this->expectException(AssertionFailedError::class); + + $this->assertFileIsReadable($this->file); + } +} diff --git a/tests/unit/Framework/Assert/assertFileIsWritableTest.php b/tests/unit/Framework/Assert/assertFileIsWritableTest.php new file mode 100644 index 00000000000..57bf35b75b4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileIsWritableTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_OS_FAMILY; +use function chmod; +use function octdec; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileIsWritable')] +#[TestDox('assertFileIsWritable()')] +#[Small] +final class assertFileIsWritableTest extends TestCase +{ + private string $file; + + protected function setUp(): void + { + $this->file = tempnam(sys_get_temp_dir(), __CLASS__); + } + + protected function tearDown(): void + { + @unlink($this->file); + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileIsWritable(__FILE__); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + chmod($this->file, octdec('0')); + + $this->expectException(AssertionFailedError::class); + + $this->assertFileIsWritable($this->file); + } +} diff --git a/tests/unit/Framework/Assert/assertFileMatchesFormatFileTest.php b/tests/unit/Framework/Assert/assertFileMatchesFormatFileTest.php new file mode 100644 index 00000000000..d071bcc187d --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileMatchesFormatFileTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileMatchesFormatFile')] +#[TestDox('assertFileMatchesFormatFile()')] +#[Small] +final class assertFileMatchesFormatFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileMatchesFormatFile( + TEST_FILES_PATH . 'expectedFileFormat.txt', + TEST_FILES_PATH . 'expectedFileFormat.txt', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileMatchesFormatFile( + TEST_FILES_PATH . 'expectedFileFormat.txt', + TEST_FILES_PATH . 'actualFileFormat.txt', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertFileMatchesFormatTest.php b/tests/unit/Framework/Assert/assertFileMatchesFormatTest.php new file mode 100644 index 00000000000..a92aeddb02b --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileMatchesFormatTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileMatchesFormat')] +#[TestDox('assertFileMatchesFormat()')] +#[Small] +final class assertFileMatchesFormatTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileMatchesFormat("FOO\n", TEST_FILES_PATH . 'expectedFileFormat.txt'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileMatchesFormat("BAR\n", TEST_FILES_PATH . 'expectedFileFormat.txt'); + } +} diff --git a/tests/unit/Framework/Assert/assertFileNotEqualsCanonicalizingTest.php b/tests/unit/Framework/Assert/assertFileNotEqualsCanonicalizingTest.php new file mode 100644 index 00000000000..4c93eb9e3a5 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileNotEqualsCanonicalizingTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileNotEqualsCanonicalizing')] +#[TestDox('assertFileNotEqualsCanonicalizing()')] +#[Small] +final class assertFileNotEqualsCanonicalizingTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileNotEqualsCanonicalizing( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileNotEqualsCanonicalizing( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'foo.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertFileNotEqualsIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertFileNotEqualsIgnoringCaseTest.php new file mode 100644 index 00000000000..84290d35eb9 --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileNotEqualsIgnoringCaseTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileNotEqualsIgnoringCase')] +#[TestDox('assertFileNotEqualsIgnoringCase()')] +#[Small] +final class assertFileNotEqualsIgnoringCaseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileNotEqualsIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileNotEqualsIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'fooUppercase.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertFileNotEqualsTest.php b/tests/unit/Framework/Assert/assertFileNotEqualsTest.php new file mode 100644 index 00000000000..d56d641a84a --- /dev/null +++ b/tests/unit/Framework/Assert/assertFileNotEqualsTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFileNotEquals')] +#[TestDox('assertFileNotEquals()')] +#[Small] +final class assertFileNotEqualsTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFileNotEquals( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFileNotEquals( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'foo.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertFiniteTest.php b/tests/unit/Framework/Assert/assertFiniteTest.php new file mode 100644 index 00000000000..cce2db0fd5c --- /dev/null +++ b/tests/unit/Framework/Assert/assertFiniteTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const INF; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertFinite')] +#[TestDox('assertFinite()')] +#[Small] +final class assertFiniteTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertFinite(1); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertFinite(INF); + } +} diff --git a/tests/unit/Framework/Assert/assertGreaterThanOrEqualTest.php b/tests/unit/Framework/Assert/assertGreaterThanOrEqualTest.php new file mode 100644 index 00000000000..5eddd82f285 --- /dev/null +++ b/tests/unit/Framework/Assert/assertGreaterThanOrEqualTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertGreaterThanOrEqual')] +#[TestDox('assertGreaterThanOrEqual()')] +#[Small] +final class assertGreaterThanOrEqualTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertGreaterThanOrEqual(1, 2); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertGreaterThanOrEqual(2, 1); + } +} diff --git a/tests/unit/Framework/Assert/assertGreaterThanTest.php b/tests/unit/Framework/Assert/assertGreaterThanTest.php new file mode 100644 index 00000000000..b043e57883e --- /dev/null +++ b/tests/unit/Framework/Assert/assertGreaterThanTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertGreaterThan')] +#[TestDox('assertGreaterThan()')] +#[Small] +final class assertGreaterThanTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertGreaterThan(1, 2); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertGreaterThan(2, 1); + } +} diff --git a/tests/unit/Framework/Assert/assertInfiniteTest.php b/tests/unit/Framework/Assert/assertInfiniteTest.php new file mode 100644 index 00000000000..b96f99d83a6 --- /dev/null +++ b/tests/unit/Framework/Assert/assertInfiniteTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const INF; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertInfinite')] +#[TestDox('assertInfinite()')] +#[Small] +final class assertInfiniteTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertInfinite(INF); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertInfinite(1); + } +} diff --git a/tests/unit/Framework/Assert/assertInstanceOfTest.php b/tests/unit/Framework/Assert/assertInstanceOfTest.php new file mode 100644 index 00000000000..e48cc7a135e --- /dev/null +++ b/tests/unit/Framework/Assert/assertInstanceOfTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertInstanceOf')] +#[CoversClass(UnknownClassOrInterfaceException::class)] +#[TestDox('assertInstanceOf()')] +#[Small] +final class assertInstanceOfTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [stdClass::class, new stdClass], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [self::class, new stdClass], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expected, mixed $actual): void + { + $this->assertInstanceOf($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertInstanceOf($expected, $actual); + } + + public function testDoesNotSupportUnknownTypes(): void + { + $this->expectException(UnknownClassOrInterfaceException::class); + $this->expectExceptionMessage('Class or interface "does-not-exist" does not exist'); + + $this->assertInstanceOf('does-not-exist', new stdClass); + } +} diff --git a/tests/unit/Framework/Assert/assertIsArrayTest.php b/tests/unit/Framework/Assert/assertIsArrayTest.php new file mode 100644 index 00000000000..8965b76ed07 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsArrayTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsArray')] +#[TestDox('assertIsArray()')] +#[Small] +final class assertIsArrayTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertIsArray([]); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsArray($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsBoolTest.php b/tests/unit/Framework/Assert/assertIsBoolTest.php new file mode 100644 index 00000000000..d68f0918782 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsBoolTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsBool')] +#[TestDox('assertIsBool()')] +#[Small] +final class assertIsBoolTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [true], + [false], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsBool($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsBool($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsCallableTest.php b/tests/unit/Framework/Assert/assertIsCallableTest.php new file mode 100644 index 00000000000..1d83cd06898 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsCallableTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsCallable')] +#[TestDox('assertIsCallable()')] +#[Small] +final class assertIsCallableTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [ + static function (): void + { + }, + ], + ['PHPUnit\Framework\assertIsCallable'], + [[Assert::class, 'assertIsCallable']], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsCallable($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsCallable($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsClosedResourceTest.php b/tests/unit/Framework/Assert/assertIsClosedResourceTest.php new file mode 100644 index 00000000000..b3554a2751f --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsClosedResourceTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsClosedResource')] +#[TestDox('assertIsClosedResource()')] +#[Small] +final class assertIsClosedResourceTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + return [ + [[]], + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + $this->assertIsClosedResource($closedResource); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsClosedResource($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsFloatTest.php b/tests/unit/Framework/Assert/assertIsFloatTest.php new file mode 100644 index 00000000000..d6ba3e3094d --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsFloatTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsFloat')] +#[TestDox('assertIsFloat()')] +#[Small] +final class assertIsFloatTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [true], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertIsFloat(0.0); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsFloat($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsIntTest.php b/tests/unit/Framework/Assert/assertIsIntTest.php new file mode 100644 index 00000000000..ef4978e4698 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsIntTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsInt')] +#[TestDox('assertIsInt()')] +#[Small] +final class assertIsIntTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [true], + [0.0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertIsInt(123); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsInt($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsIterableTest.php b/tests/unit/Framework/Assert/assertIsIterableTest.php new file mode 100644 index 00000000000..73374c8b496 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsIterableTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use ArrayIterator; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsIterable')] +#[TestDox('assertIsIterable()')] +#[Small] +final class assertIsIterableTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[]], + [new ArrayIterator([])], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsIterable($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsIterable($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsListTest.php b/tests/unit/Framework/Assert/assertIsListTest.php new file mode 100644 index 00000000000..1773154b5d9 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsListTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsList')] +#[TestDox('assertIsList()')] +#[Small] +final class assertIsListTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [['foo' => 'bar']], + [[1 => 'bar', 4 => 'baz']], + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertIsList([1, 2, 3]); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsList($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotArrayTest.php b/tests/unit/Framework/Assert/assertIsNotArrayTest.php new file mode 100644 index 00000000000..d1c7fcd9074 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotArrayTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotArray')] +#[TestDox('assertIsNotArray()')] +#[Small] +final class assertIsNotArrayTest extends TestCase +{ + #[DataProviderExternal(assertIsArrayTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotArray($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotArray([]); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotBoolTest.php b/tests/unit/Framework/Assert/assertIsNotBoolTest.php new file mode 100644 index 00000000000..eab1d98e309 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotBoolTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotBool')] +#[TestDox('assertIsNotBool()')] +#[Small] +final class assertIsNotBoolTest extends TestCase +{ + #[DataProviderExternal(assertIsBoolTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotBool($actual); + } + + #[DataProviderExternal(assertIsBoolTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotBool($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotCallableTest.php b/tests/unit/Framework/Assert/assertIsNotCallableTest.php new file mode 100644 index 00000000000..718a8593592 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotCallableTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotCallable')] +#[TestDox('assertIsNotCallable()')] +#[Small] +final class assertIsNotCallableTest extends TestCase +{ + #[DataProviderExternal(assertIsCallableTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotCallable($actual); + } + + #[DataProviderExternal(assertIsCallableTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotCallable($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotClosedResourceTest.php b/tests/unit/Framework/Assert/assertIsNotClosedResourceTest.php new file mode 100644 index 00000000000..d49115aaf95 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotClosedResourceTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotClosedResource')] +#[TestDox('assertIsNotClosedResource()')] +#[Small] +final class assertIsNotClosedResourceTest extends TestCase +{ + #[DataProviderExternal(assertIsClosedResourceTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotClosedResource($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotClosedResource($closedResource); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotFloatTest.php b/tests/unit/Framework/Assert/assertIsNotFloatTest.php new file mode 100644 index 00000000000..1735512edab --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotFloatTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotFloat')] +#[TestDox('assertIsNotFloat()')] +#[Small] +final class assertIsNotFloatTest extends TestCase +{ + #[DataProviderExternal(assertIsFloatTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotFloat($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotFloat(0.0); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotIntTest.php b/tests/unit/Framework/Assert/assertIsNotIntTest.php new file mode 100644 index 00000000000..3352a5aa5fb --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotIntTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotInt')] +#[TestDox('assertIsNotInt()')] +#[Small] +final class assertIsNotIntTest extends TestCase +{ + #[DataProviderExternal(assertIsIntTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotInt($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotInt(0); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotIterableTest.php b/tests/unit/Framework/Assert/assertIsNotIterableTest.php new file mode 100644 index 00000000000..8b5beafeaef --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotIterableTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotIterable')] +#[TestDox('assertIsNotIterable()')] +#[Small] +final class assertIsNotIterableTest extends TestCase +{ + #[DataProviderExternal(assertIsIterableTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotIterable($actual); + } + + #[DataProviderExternal(assertIsIterableTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotIterable($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotNumericTest.php b/tests/unit/Framework/Assert/assertIsNotNumericTest.php new file mode 100644 index 00000000000..f4ef42f4a43 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotNumericTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotNumeric')] +#[TestDox('assertIsNotNumeric()')] +#[Small] +final class assertIsNotNumericTest extends TestCase +{ + #[DataProviderExternal(assertIsNumericTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotNumeric($actual); + } + + #[DataProviderExternal(assertIsNumericTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotNumeric($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotObjectTest.php b/tests/unit/Framework/Assert/assertIsNotObjectTest.php new file mode 100644 index 00000000000..5684723311c --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotObjectTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsNotObject')] +#[TestDox('assertIsNotObject()')] +#[Small] +final class assertIsNotObjectTest extends TestCase +{ + #[DataProviderExternal(assertIsObjectTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotObject($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotObject(new stdClass); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotReadableTest.php b/tests/unit/Framework/Assert/assertIsNotReadableTest.php new file mode 100644 index 00000000000..effc8d6fd5c --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotReadableTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_OS_FAMILY; +use function chmod; +use function mkdir; +use function octdec; +use function rmdir; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotReadable')] +#[TestDox('assertIsNotReadable()')] +#[Small] +final class assertIsNotReadableTest extends TestCase +{ + private ?string $directory = null; + private ?string $file = null; + + protected function tearDown(): void + { + if ($this->directory !== null) { + rmdir($this->directory); + } + + if ($this->file !== null) { + unlink($this->file); + } + } + + #[DataProviderExternal(assertIsReadableTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, string $path): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + if ($type === 'directory') { + mkdir($path, octdec('0')); + + $this->directory = $path; + } else { + chmod($path, octdec('0')); + + $this->file = $path; + } + + $this->assertIsNotReadable($path); + } + + #[DataProviderExternal(assertIsReadableTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, string $path): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotReadable($path); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotResourceTest.php b/tests/unit/Framework/Assert/assertIsNotResourceTest.php new file mode 100644 index 00000000000..b25b4b8a5f3 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotResourceTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotResource')] +#[TestDox('assertIsNotResource()')] +#[Small] +final class assertIsNotResourceTest extends TestCase +{ + #[DataProviderExternal(assertIsResourceTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotResource($actual); + } + + #[DataProviderExternal(assertIsResourceTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotResource($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotScalarTest.php b/tests/unit/Framework/Assert/assertIsNotScalarTest.php new file mode 100644 index 00000000000..0a6e621e6a7 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotScalarTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotScalar')] +#[TestDox('assertIsNotScalar()')] +#[Small] +final class assertIsNotScalarTest extends TestCase +{ + #[DataProviderExternal(assertIsScalarTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotScalar($actual); + } + + #[DataProviderExternal(assertIsScalarTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotScalar($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotStringTest.php b/tests/unit/Framework/Assert/assertIsNotStringTest.php new file mode 100644 index 00000000000..5a457a7ec0a --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotStringTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotString')] +#[TestDox('assertIsNotString()')] +#[Small] +final class assertIsNotStringTest extends TestCase +{ + #[DataProviderExternal(assertIsStringTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNotString($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotString('string'); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNotWritableTest.php b/tests/unit/Framework/Assert/assertIsNotWritableTest.php new file mode 100644 index 00000000000..ddb2ed508a5 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNotWritableTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const PHP_OS_FAMILY; +use function chmod; +use function mkdir; +use function octdec; +use function rmdir; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsNotWritable')] +#[TestDox('assertIsNotWritable()')] +#[Small] +final class assertIsNotWritableTest extends TestCase +{ + private ?string $directory = null; + private ?string $file = null; + + protected function tearDown(): void + { + if ($this->directory !== null) { + rmdir($this->directory); + } + + if ($this->file !== null) { + unlink($this->file); + } + } + + #[DataProviderExternal(assertIsWritableTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, string $path): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + if ($type === 'directory') { + mkdir($path, octdec('0')); + + $this->directory = $path; + } else { + chmod($path, octdec('0')); + + $this->file = $path; + } + + $this->assertIsNotWritable($path); + } + + #[DataProviderExternal(assertIsWritableTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, string $path): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNotWritable($path); + } +} diff --git a/tests/unit/Framework/Assert/assertIsNumericTest.php b/tests/unit/Framework/Assert/assertIsNumericTest.php new file mode 100644 index 00000000000..5ff666a3d2f --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsNumericTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsNumeric')] +#[TestDox('assertIsNumeric()')] +#[Small] +final class assertIsNumericTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['123'], + ['123.456'], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [true], + [null], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsNumeric($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsNumeric($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsObjectTest.php b/tests/unit/Framework/Assert/assertIsObjectTest.php new file mode 100644 index 00000000000..f5f550440f5 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsObjectTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsObject')] +#[TestDox('assertIsObject()')] +#[Small] +final class assertIsObjectTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertIsObject(new stdClass); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsObject($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsReadableTest.php b/tests/unit/Framework/Assert/assertIsReadableTest.php new file mode 100644 index 00000000000..3e278262308 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsReadableTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DIRECTORY_SEPARATOR; +use const PHP_OS_FAMILY; +use function chmod; +use function mkdir; +use function octdec; +use function rmdir; +use function sys_get_temp_dir; +use function tempnam; +use function uniqid; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsReadable')] +#[TestDox('assertIsReadable()')] +#[Small] +final class assertIsReadableTest extends TestCase +{ + private ?string $directory = null; + private ?string $file = null; + + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['directory', __DIR__], + ['file', __FILE__], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['directory', sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(__CLASS__ . '_', true)], + ['file', tempnam(sys_get_temp_dir(), __CLASS__)], + ]; + } + + protected function tearDown(): void + { + if ($this->directory !== null) { + rmdir($this->directory); + } + + if ($this->file !== null) { + unlink($this->file); + } + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, string $path): void + { + $this->assertIsReadable($path); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, string $path): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + if ($type === 'directory') { + mkdir($path, octdec('0')); + + $this->directory = $path; + } else { + chmod($path, octdec('0')); + + $this->file = $path; + } + + $this->expectException(AssertionFailedError::class); + + $this->assertIsReadable($path); + } +} diff --git a/tests/unit/Framework/Assert/assertIsResourceTest.php b/tests/unit/Framework/Assert/assertIsResourceTest.php new file mode 100644 index 00000000000..37e475debb3 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsResourceTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsResource')] +#[TestDox('assertIsResource()')] +#[Small] +final class assertIsResourceTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [$openResource], + [$closedResource], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [true], + [0.0], + [0], + [null], + ['123'], + ['string'], + [new stdClass], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsResource($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsResource($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsScalarTest.php b/tests/unit/Framework/Assert/assertIsScalarTest.php new file mode 100644 index 00000000000..4d3164c2752 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsScalarTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsScalar')] +#[TestDox('assertIsScalar()')] +#[Small] +final class assertIsScalarTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [true], + [false], + [0], + [0.0], + ['string'], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [null], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertIsScalar($actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsScalar($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsStringTest.php b/tests/unit/Framework/Assert/assertIsStringTest.php new file mode 100644 index 00000000000..fb9ef44a051 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsStringTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertIsString')] +#[TestDox('assertIsString()')] +#[Small] +final class assertIsStringTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [[]], + [true], + [0.0], + [0], + [null], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertIsString('string'); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertIsString($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertIsWritableTest.php b/tests/unit/Framework/Assert/assertIsWritableTest.php new file mode 100644 index 00000000000..00e27b3db29 --- /dev/null +++ b/tests/unit/Framework/Assert/assertIsWritableTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const DIRECTORY_SEPARATOR; +use const PHP_OS_FAMILY; +use function chmod; +use function mkdir; +use function octdec; +use function rmdir; +use function sys_get_temp_dir; +use function tempnam; +use function uniqid; +use function unlink; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertIsWritable')] +#[TestDox('assertIsWritable()')] +#[Small] +final class assertIsWritableTest extends TestCase +{ + private ?string $directory = null; + private ?string $file = null; + + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['directory', __DIR__], + ['file', __FILE__], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['directory', sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(__CLASS__ . '_', true)], + ['file', tempnam(sys_get_temp_dir(), __CLASS__)], + ]; + } + + protected function tearDown(): void + { + if ($this->directory !== null) { + rmdir($this->directory); + } + + if ($this->file !== null) { + unlink($this->file); + } + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, string $path): void + { + $this->assertIsWritable($path); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, string $path): void + { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('Cannot test this behaviour on Windows'); + } + + if ($type === 'directory') { + mkdir($path, octdec('0')); + + $this->directory = $path; + } else { + chmod($path, octdec('0')); + + $this->file = $path; + } + + $this->expectException(AssertionFailedError::class); + + $this->assertIsWritable($path); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonFileEqualsJsonFileTest.php b/tests/unit/Framework/Assert/assertJsonFileEqualsJsonFileTest.php new file mode 100644 index 00000000000..ef4c485ee76 --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonFileEqualsJsonFileTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJsonFileEqualsJsonFile')] +#[TestDox('assertJsonFileEqualsJsonFile()')] +#[Small] +final class assertJsonFileEqualsJsonFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertJsonFileEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/simpleObject.json', + TEST_FILES_PATH . 'JsonData/simpleObject.json', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJsonFileEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/arrayObject.json', + TEST_FILES_PATH . 'JsonData/simpleObject.json', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonFileNotEqualsJsonFileTest.php b/tests/unit/Framework/Assert/assertJsonFileNotEqualsJsonFileTest.php new file mode 100644 index 00000000000..a9dc84a1a2f --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonFileNotEqualsJsonFileTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJsonFileNotEqualsJsonFile')] +#[TestDox('assertJsonFileNotEqualsJsonFile()')] +#[Small] +final class assertJsonFileNotEqualsJsonFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertJsonFileNotEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/arrayObject.json', + TEST_FILES_PATH . 'JsonData/simpleObject.json', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJsonFileNotEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/simpleObject.json', + TEST_FILES_PATH . 'JsonData/simpleObject.json', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonStringEqualsJsonFileTest.php b/tests/unit/Framework/Assert/assertJsonStringEqualsJsonFileTest.php new file mode 100644 index 00000000000..90df937a830 --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonStringEqualsJsonFileTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function json_encode; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJsonStringEqualsJsonFile')] +#[TestDox('assertJsonStringEqualsJsonFile()')] +#[Small] +final class assertJsonStringEqualsJsonFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertJsonStringEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/simpleObject.json', + json_encode(['Mascott' => 'Tux']), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJsonStringEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/arrayObject.json', + json_encode(['Mascott' => 'Tux']), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonStringEqualsJsonStringTest.php b/tests/unit/Framework/Assert/assertJsonStringEqualsJsonStringTest.php new file mode 100644 index 00000000000..9c165b1d8e9 --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonStringEqualsJsonStringTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJsonStringEqualsJsonString')] +#[TestDox('assertJsonStringEqualsJsonString()')] +#[Small] +final class assertJsonStringEqualsJsonStringTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['{"Mascot" : "elePHPant"}', '{"Mascot" : "elePHPant"}'], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['{"Mascot" : "elePHPant"}', '{"Mascot" : "Tux"}'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expectedJson, string $actualJson): void + { + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expectedJson, string $actualJson): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonStringNotEqualsJsonFileTest.php b/tests/unit/Framework/Assert/assertJsonStringNotEqualsJsonFileTest.php new file mode 100644 index 00000000000..e24fcd0baa3 --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonStringNotEqualsJsonFileTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function json_encode; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJsonStringNotEqualsJsonFile')] +#[TestDox('assertJsonStringNotEqualsJsonFile()')] +#[Small] +final class assertJsonStringNotEqualsJsonFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertJsonStringNotEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/arrayObject.json', + json_encode(['Mascott' => 'Tux']), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJsonStringNotEqualsJsonFile( + TEST_FILES_PATH . 'JsonData/simpleObject.json', + json_encode(['Mascott' => 'Tux']), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonStringNotEqualsJsonStringTest.php b/tests/unit/Framework/Assert/assertJsonStringNotEqualsJsonStringTest.php new file mode 100644 index 00000000000..58014817069 --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonStringNotEqualsJsonStringTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJsonStringNotEqualsJsonString')] +#[TestDox('assertJsonStringNotEqualsJsonString()')] +#[Small] +final class assertJsonStringNotEqualsJsonStringTest extends TestCase +{ + #[DataProviderExternal(assertJsonStringEqualsJsonStringTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expectedJson, string $actualJson): void + { + $this->assertJsonStringNotEqualsJsonString($expectedJson, $actualJson); + } + + #[DataProviderExternal(assertJsonStringEqualsJsonStringTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expectedJson, string $actualJson): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJsonStringNotEqualsJsonString($expectedJson, $actualJson); + } +} diff --git a/tests/unit/Framework/Assert/assertJsonTest.php b/tests/unit/Framework/Assert/assertJsonTest.php new file mode 100644 index 00000000000..eff3daec61c --- /dev/null +++ b/tests/unit/Framework/Assert/assertJsonTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertJson')] +#[TestDox('assertJson()')] +#[Small] +final class assertJsonTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertJson('{}'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertJson(''); + } +} diff --git a/tests/unit/Framework/Assert/assertLessThanOrEqualTest.php b/tests/unit/Framework/Assert/assertLessThanOrEqualTest.php new file mode 100644 index 00000000000..faa7e1dbd12 --- /dev/null +++ b/tests/unit/Framework/Assert/assertLessThanOrEqualTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertLessThanOrEqual')] +#[TestDox('assertLessThanOrEqual()')] +#[Small] +final class assertLessThanOrEqualTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertLessThanOrEqual(2, 1); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertLessThanOrEqual(1, 2); + } +} diff --git a/tests/unit/Framework/Assert/assertLessThanTest.php b/tests/unit/Framework/Assert/assertLessThanTest.php new file mode 100644 index 00000000000..ab46dd699d2 --- /dev/null +++ b/tests/unit/Framework/Assert/assertLessThanTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertLessThan')] +#[TestDox('assertLessThan()')] +#[Small] +final class assertLessThanTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertLessThan(2, 1); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertLessThan(1, 2); + } +} diff --git a/tests/unit/Framework/Assert/assertMatchesRegularExpressionTest.php b/tests/unit/Framework/Assert/assertMatchesRegularExpressionTest.php new file mode 100644 index 00000000000..e38ebba7efe --- /dev/null +++ b/tests/unit/Framework/Assert/assertMatchesRegularExpressionTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertMatchesRegularExpression')] +#[TestDox('assertMatchesRegularExpression()')] +#[Small] +final class assertMatchesRegularExpressionTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['/foo/', 'foobar'], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['/foo/', 'bar'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $pattern, string $string): void + { + $this->assertMatchesRegularExpression($pattern, $string); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $pattern, string $string): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertMatchesRegularExpression($pattern, $string); + } +} diff --git a/tests/unit/Framework/Assert/assertNanTest.php b/tests/unit/Framework/Assert/assertNanTest.php new file mode 100644 index 00000000000..663d450daab --- /dev/null +++ b/tests/unit/Framework/Assert/assertNanTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const NAN; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNan')] +#[TestDox('assertNan()')] +#[Small] +final class assertNanTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertNan(NAN); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNan(1); + } +} diff --git a/tests/unit/Framework/Assert/assertNotContainsEqualsTest.php b/tests/unit/Framework/Assert/assertNotContainsEqualsTest.php new file mode 100644 index 00000000000..a3cd59ea5d9 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotContainsEqualsTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotContainsEquals')] +#[TestDox('assertNotContainsEquals()')] +#[Small] +final class assertNotContainsEqualsTest extends TestCase +{ + #[DataProviderExternal(assertContainsEqualsTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $needle, iterable $haystack): void + { + $this->assertNotContainsEquals($needle, $haystack); + } + + #[DataProviderExternal(assertContainsEqualsTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $needle, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotContainsEquals($needle, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php b/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php new file mode 100644 index 00000000000..04d720864ee --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotContainsOnly')] +#[TestDox('assertNotContainsOnly()')] +#[Small] +#[IgnorePhpunitDeprecations] +final class assertNotContainsOnlyTest extends TestCase +{ + #[DataProviderExternal(assertContainsOnlyTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $type, iterable $haystack): void + { + $this->assertNotContainsOnly($type, $haystack); + } + + #[DataProviderExternal(assertContainsOnlyTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $type, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotContainsOnly($type, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertNotContainsTest.php b/tests/unit/Framework/Assert/assertNotContainsTest.php new file mode 100644 index 00000000000..5db7c369e77 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotContainsTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotContains')] +#[TestDox('assertNotContains()')] +#[Small] +final class assertNotContainsTest extends TestCase +{ + #[DataProviderExternal(assertContainsTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $needle, iterable $haystack): void + { + $this->assertNotContains($needle, $haystack); + } + + #[DataProviderExternal(assertContainsTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $needle, iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotContains($needle, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertNotCountTest.php b/tests/unit/Framework/Assert/assertNotCountTest.php new file mode 100644 index 00000000000..fa44c0ef5e1 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotCountTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function PHPUnit\TestFixture\Generator\f; +use Countable; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotCount')] +#[CoversClass(GeneratorNotSupportedException::class)] +#[TestDox('assertNotCount()')] +#[Small] +final class assertNotCountTest extends TestCase +{ + #[DataProviderExternal(assertCountTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(int $expectedCount, Countable|iterable $haystack): void + { + $this->assertNotCount($expectedCount, $haystack); + } + + #[DataProviderExternal(assertCountTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(int $expectedCount, Countable|iterable $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotCount($expectedCount, $haystack); + } + + public function testDoesNotSupportGenerators(): void + { + $this->expectException(GeneratorNotSupportedException::class); + $this->expectExceptionMessage('Passing an argument of type Generator for the $haystack parameter is not supported'); + + $this->assertNotCount(0, f()); + } +} diff --git a/tests/unit/Framework/Assert/assertNotEmptyTest.php b/tests/unit/Framework/Assert/assertNotEmptyTest.php new file mode 100644 index 00000000000..c2be7c81c49 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotEmptyTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function PHPUnit\TestFixture\Generator\f; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotEmpty')] +#[CoversClass(GeneratorNotSupportedException::class)] +#[TestDox('assertNotEmpty()')] +#[Small] +final class assertNotEmptyTest extends TestCase +{ + #[DataProviderExternal(assertEmptyTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertNotEmpty($actual); + } + + #[DataProviderExternal(assertEmptyTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotEmpty($actual); + } + + public function testDoesNotSupportGenerators(): void + { + $this->expectException(GeneratorNotSupportedException::class); + $this->expectExceptionMessage('Passing an argument of type Generator for the $actual parameter is not supported'); + + $this->assertNotEmpty(f()); + } +} diff --git a/tests/unit/Framework/Assert/assertNotEqualsCanonicalizingTest.php b/tests/unit/Framework/Assert/assertNotEqualsCanonicalizingTest.php new file mode 100644 index 00000000000..534a9d62e04 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotEqualsCanonicalizingTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotEqualsCanonicalizing')] +#[TestDox('assertNotEqualsCanonicalizing()')] +#[Small] +final class assertNotEqualsCanonicalizingTest extends TestCase +{ + #[DataProviderExternal(assertEqualsCanonicalizingTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertNotEqualsCanonicalizing($expected, $actual); + } + + #[DataProviderExternal(assertEqualsCanonicalizingTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotEqualsCanonicalizing($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertNotEqualsIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertNotEqualsIgnoringCaseTest.php new file mode 100644 index 00000000000..896515b3dd1 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotEqualsIgnoringCaseTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotEqualsIgnoringCase')] +#[TestDox('assertNotEqualsIgnoringCase()')] +#[Small] +final class assertNotEqualsIgnoringCaseTest extends TestCase +{ + #[DataProviderExternal(assertEqualsIgnoringCaseTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertNotEqualsIgnoringCase($expected, $actual); + } + + #[DataProviderExternal(assertEqualsIgnoringCaseTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotEqualsIgnoringCase($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertNotEqualsTest.php b/tests/unit/Framework/Assert/assertNotEqualsTest.php new file mode 100644 index 00000000000..051eddf297b --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotEqualsTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotEquals')] +#[TestDox('assertNotEquals()')] +#[Small] +final class assertNotEqualsTest extends TestCase +{ + #[DataProviderExternal(assertEqualsTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertNotEquals($expected, $actual); + } + + #[DataProviderExternal(assertEqualsTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotEquals($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertNotEqualsWithDeltaTest.php b/tests/unit/Framework/Assert/assertNotEqualsWithDeltaTest.php new file mode 100644 index 00000000000..0b7b5026f58 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotEqualsWithDeltaTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotEqualsWithDelta')] +#[TestDox('assertNotEqualsWithDelta()')] +#[Small] +final class assertNotEqualsWithDeltaTest extends TestCase +{ + #[DataProviderExternal(assertEqualsWithDeltaTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual, float $delta): void + { + $this->assertNotEqualsWithDelta($expected, $actual, $delta); + } + + #[DataProviderExternal(assertEqualsWithDeltaTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual, float $delta): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotEqualsWithDelta($expected, $actual, $delta); + } +} diff --git a/tests/unit/Framework/Assert/assertNotFalseTest.php b/tests/unit/Framework/Assert/assertNotFalseTest.php new file mode 100644 index 00000000000..e1c8630f9c3 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotFalseTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotFalse')] +#[TestDox('assertNotFalse()')] +#[Small] +final class assertNotFalseTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [true], + [null], + [0], + [1], + ['false'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertNotFalse($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotFalse(false); + } +} diff --git a/tests/unit/Framework/Assert/assertNotInstanceOfTest.php b/tests/unit/Framework/Assert/assertNotInstanceOfTest.php new file mode 100644 index 00000000000..15444700e77 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotInstanceOfTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertNotInstanceOf')] +#[TestDox('assertNotInstanceOf()')] +#[Small] +final class assertNotInstanceOfTest extends TestCase +{ + #[DataProviderExternal(assertInstanceOfTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expected, mixed $actual): void + { + $this->assertNotInstanceOf($expected, $actual); + } + + #[DataProviderExternal(assertInstanceOfTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotInstanceOf($expected, $actual); + } + + public function testDoesNotSupportUnknownTypes(): void + { + $this->expectException(UnknownClassOrInterfaceException::class); + $this->expectExceptionMessage('Class or interface "does-not-exist" does not exist'); + + $this->assertNotInstanceOf('does-not-exist', new stdClass); + } +} diff --git a/tests/unit/Framework/Assert/assertNotNullTest.php b/tests/unit/Framework/Assert/assertNotNullTest.php new file mode 100644 index 00000000000..bdfd9961252 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotNullTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotNull')] +#[TestDox('assertNotNull()')] +#[Small] +final class assertNotNullTest extends TestCase +{ + #[DataProviderExternal(assertNullTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertNotNull($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotNull(null); + } +} diff --git a/tests/unit/Framework/Assert/assertNotSameSizeTest.php b/tests/unit/Framework/Assert/assertNotSameSizeTest.php new file mode 100644 index 00000000000..15c8da95874 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotSameSizeTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Countable; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotSameSize')] +#[TestDox('assertNotSameSize()')] +#[Small] +final class assertNotSameSizeTest extends TestCase +{ + #[DataProviderExternal(assertSameSizeTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(Countable|iterable $expected, Countable|iterable $actual): void + { + $this->assertNotSameSize($expected, $actual); + } + + #[DataProviderExternal(assertSameSizeTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(Countable|iterable $expected, Countable|iterable $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotSameSize($expected, $actual); + } + + #[DataProviderExternal(assertSameSizeTest::class, 'errorProvider')] + public function testDoesNotSupportGenerators(Countable|iterable $expected, Countable|iterable $actual): void + { + $this->expectException(GeneratorNotSupportedException::class); + + $this->assertNotSameSize($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertNotSameTest.php b/tests/unit/Framework/Assert/assertNotSameTest.php new file mode 100644 index 00000000000..46e38a1c3ad --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotSameTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotSame')] +#[TestDox('assertNotSame()')] +#[Small] +final class assertNotSameTest extends TestCase +{ + #[DataProviderExternal(assertSameTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertNotSame($expected, $actual); + } + + #[DataProviderExternal(assertSameTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotSame($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertNotTrueTest.php b/tests/unit/Framework/Assert/assertNotTrueTest.php new file mode 100644 index 00000000000..8a701859143 --- /dev/null +++ b/tests/unit/Framework/Assert/assertNotTrueTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertNotTrue')] +#[TestDox('assertNotTrue()')] +#[Small] +final class assertNotTrueTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [false], + [null], + [0], + [1], + ['true'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $actual): void + { + $this->assertNotTrue($actual); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNotTrue(true); + } +} diff --git a/tests/unit/Framework/Assert/assertNullTest.php b/tests/unit/Framework/Assert/assertNullTest.php new file mode 100644 index 00000000000..69c69920cba --- /dev/null +++ b/tests/unit/Framework/Assert/assertNullTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertNull')] +#[TestDox('assertNull()')] +#[Small] +final class assertNullTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + $openResource = fopen(__FILE__, 'r'); + + $closedResource = fopen(__FILE__, 'r'); + fclose($closedResource); + + return [ + [['foo' => 'bar']], + [[1 => 'bar', 4 => 'baz']], + [true], + [0.0], + [0], + ['123'], + ['string'], + [new stdClass], + [$openResource], + [$closedResource], + ]; + } + + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertNull(null); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertNull($actual); + } +} diff --git a/tests/unit/Framework/Assert/assertObjectEqualsTest.php b/tests/unit/Framework/Assert/assertObjectEqualsTest.php new file mode 100644 index 00000000000..a25fd26e541 --- /dev/null +++ b/tests/unit/Framework/Assert/assertObjectEqualsTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\TestFixture\ObjectEquals\ValueObject; + +#[CoversMethod(Assert::class, 'assertObjectEquals')] +#[TestDox('assertObjectEquals()')] +#[Small] +final class assertObjectEqualsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [new ValueObject(1), new ValueObject(1), 'equals'], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [new ValueObject(1), new ValueObject(2), 'equals'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(object $expected, object $actual, string $method): void + { + $this->assertObjectEquals($expected, $actual, $method); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(object $expected, object $actual, string $method): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertObjectEquals($expected, $actual, $method); + } +} diff --git a/tests/unit/Framework/Assert/assertObjectHasPropertyTest.php b/tests/unit/Framework/Assert/assertObjectHasPropertyTest.php new file mode 100644 index 00000000000..767e8fc00d8 --- /dev/null +++ b/tests/unit/Framework/Assert/assertObjectHasPropertyTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertObjectHasProperty')] +#[TestDox('assertObjectHasProperty()')] +#[Small] +final class assertObjectHasPropertyTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $object = new stdClass; + $object->theProperty = 'value'; + + $this->assertObjectHasProperty('theProperty', $object); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $object = new stdClass; + + $this->expectException(AssertionFailedError::class); + + $this->assertObjectHasProperty('theProperty', $object); + } +} diff --git a/tests/unit/Framework/Assert/assertObjectNotEqualsTest.php b/tests/unit/Framework/Assert/assertObjectNotEqualsTest.php new file mode 100644 index 00000000000..7c65ce0e495 --- /dev/null +++ b/tests/unit/Framework/Assert/assertObjectNotEqualsTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertObjectNotEquals')] +#[TestDox('assertObjectNotEquals()')] +#[Small] +final class assertObjectNotEqualsTest extends TestCase +{ + #[DataProviderExternal(assertObjectEqualsTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(object $expected, object $actual, string $method): void + { + $this->assertObjectNotEquals($expected, $actual, $method); + } + + #[DataProviderExternal(assertObjectEqualsTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(object $expected, object $actual, string $method): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertObjectNotEquals($expected, $actual, $method); + } +} diff --git a/tests/unit/Framework/Assert/assertObjectNotHasPropertyTest.php b/tests/unit/Framework/Assert/assertObjectNotHasPropertyTest.php new file mode 100644 index 00000000000..ef15842ccba --- /dev/null +++ b/tests/unit/Framework/Assert/assertObjectNotHasPropertyTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use stdClass; + +#[CoversMethod(Assert::class, 'assertObjectNotHasProperty')] +#[TestDox('assertObjectNotHasProperty()')] +#[Small] +final class assertObjectNotHasPropertyTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $object = new stdClass; + + $this->assertObjectNotHasProperty('theProperty', $object); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $object = new stdClass; + $object->theProperty = 'value'; + + $this->expectException(AssertionFailedError::class); + + $this->assertObjectNotHasProperty('theProperty', $object); + } +} diff --git a/tests/unit/Framework/Assert/assertSameSizeTest.php b/tests/unit/Framework/Assert/assertSameSizeTest.php new file mode 100644 index 00000000000..671d4d93e3d --- /dev/null +++ b/tests/unit/Framework/Assert/assertSameSizeTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function PHPUnit\TestFixture\Generator\f; +use Countable; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertSameSize')] +#[CoversClass(GeneratorNotSupportedException::class)] +#[TestDox('assertSameSize()')] +#[Small] +final class assertSameSizeTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + [[1, 2], [3, 4]], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + [[1, 2], [3]], + ]; + } + + /** + * @return non-empty-list + */ + public static function errorProvider(): array + { + return [ + [f(), []], + [[], f()], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(Countable|iterable $expected, Countable|iterable $actual): void + { + $this->assertSameSize($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(Countable|iterable $expected, Countable|iterable $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertSameSize($expected, $actual); + } + + #[DataProvider('errorProvider')] + public function testDoesNotSupportGenerators(Countable|iterable $expected, Countable|iterable $actual): void + { + $this->expectException(GeneratorNotSupportedException::class); + + $this->assertSameSize($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertSameTest.php b/tests/unit/Framework/Assert/assertSameTest.php new file mode 100644 index 00000000000..db0e0c45d77 --- /dev/null +++ b/tests/unit/Framework/Assert/assertSameTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const INF; +use function array_merge; +use function fopen; +use function log; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\TestFixture\SampleClass; + +#[CoversMethod(Assert::class, 'assertSame')] +#[TestDox('assertSame()')] +#[Small] +final class assertSameTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return self::sameValues(); + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return array_merge(assertEqualsTest::notEqualValues(), assertEqualsTest::equalValues()); + } + + /** + * @return non-empty-list + */ + public static function sameValues(): array + { + $object = new SampleClass(4, 8, 15); + $file = TEST_FILES_PATH . 'foo.xml'; + $resource = fopen($file, 'r'); + + return [ + // null + [null, null], + // strings + ['a', 'a'], + // integers + [0, 0], + // floats + [1.0, 1.0], + [2.3, 2.3], + [1 / 3, 1 / 3], + [1 - 2 / 3, 1 - 2 / 3], + [5.5E+123, 5.5E+123], + [5.5E-123, 5.5E-123], + [log(0), log(0)], + [INF, INF], + [-INF, -INF], + // arrays + [[], []], + [[0 => 1], [0 => 1]], + [[0 => null], [0 => null]], + [['a', 'b' => [1, 2]], ['a', 'b' => [1, 2]]], + // objects + [$object, $object], + // resources + [$resource, $resource], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(mixed $expected, mixed $actual): void + { + $this->assertSame($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(mixed $expected, mixed $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertSame($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertStringContainsStringIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertStringContainsStringIgnoringCaseTest.php new file mode 100644 index 00000000000..ae696ed00fa --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringContainsStringIgnoringCaseTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringContainsStringIgnoringCase')] +#[TestDox('assertStringContainsStringIgnoringCase()')] +#[Small] +final class assertStringContainsStringIgnoringCaseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringContainsStringIgnoringCase('BAR', 'foobarbaz'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringContainsStringIgnoringCase('BARBARA', 'foobarbaz'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringContainsStringIgnoringLineEndingsTest.php b/tests/unit/Framework/Assert/assertStringContainsStringIgnoringLineEndingsTest.php new file mode 100644 index 00000000000..b7d2c1a764b --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringContainsStringIgnoringLineEndingsTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringContainsStringIgnoringLineEndings')] +#[TestDox('assertStringContainsStringIgnoringLineEndings()')] +#[Small] +final class assertStringContainsStringIgnoringLineEndingsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ["b\nc", "b\r\nc"], + ["b\nc", "a\r\nb\r\nc\r\nd"], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ["a\nc", "b\r\nc"], + ["a\nc", "a\r\nb\r\nc\r\nd"], + ["b\nc", "\r\nc\r\n"], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $needle, string $haystack): void + { + $this->assertStringContainsStringIgnoringLineEndings($needle, $haystack); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $needle, string $haystack): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringContainsStringIgnoringLineEndings($needle, $haystack); + } +} diff --git a/tests/unit/Framework/Assert/assertStringContainsStringTest.php b/tests/unit/Framework/Assert/assertStringContainsStringTest.php new file mode 100644 index 00000000000..8b5276e4bb5 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringContainsStringTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringContainsString')] +#[TestDox('assertStringContainsString()')] +#[Small] +final class assertStringContainsStringTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringContainsString('bar', 'foobarbaz'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringContainsString('barbara', 'foobarbaz'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringEndsNotWithTest.php b/tests/unit/Framework/Assert/assertStringEndsNotWithTest.php new file mode 100644 index 00000000000..9e3d4e567ba --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringEndsNotWithTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringEndsNotWith')] +#[TestDox('assertStringEndsNotWith()')] +#[Small] +final class assertStringEndsNotWithTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringEndsNotWith('suffix', 'foo'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringEndsNotWith('suffix', 'foosuffix'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringEndsWithTest.php b/tests/unit/Framework/Assert/assertStringEndsWithTest.php new file mode 100644 index 00000000000..0c8e264dd90 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringEndsWithTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringEndsWith')] +#[TestDox('assertStringEndsWith()')] +#[Small] +final class assertStringEndsWithTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringEndsWith('suffix', 'foosuffix'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringEndsWith('suffix', 'foo'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringEqualsFileCanonicalizingTest.php b/tests/unit/Framework/Assert/assertStringEqualsFileCanonicalizingTest.php new file mode 100644 index 00000000000..f4bf4074b49 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringEqualsFileCanonicalizingTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringEqualsFileCanonicalizing')] +#[TestDox('assertStringEqualsFileCanonicalizing()')] +#[Small] +final class assertStringEqualsFileCanonicalizingTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringEqualsFileCanonicalizing( + TEST_FILES_PATH . 'foo.txt', + file_get_contents(TEST_FILES_PATH . 'foo.txt'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringEqualsFileCanonicalizing( + TEST_FILES_PATH . 'foo.txt', + file_get_contents(TEST_FILES_PATH . 'foo.txt') . 'BAR', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertStringEqualsFileIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertStringEqualsFileIgnoringCaseTest.php new file mode 100644 index 00000000000..d1db2408ce4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringEqualsFileIgnoringCaseTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringEqualsFileIgnoringCase')] +#[TestDox('assertStringEqualsFileIgnoringCase()')] +#[Small] +final class assertStringEqualsFileIgnoringCaseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringEqualsFileIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'fooUppercase.xml'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringEqualsFileIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'bar.xml'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertStringEqualsFileTest.php b/tests/unit/Framework/Assert/assertStringEqualsFileTest.php new file mode 100644 index 00000000000..0b53771844a --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringEqualsFileTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringEqualsFile')] +#[TestDox('assertStringEqualsFile()')] +#[Small] +final class assertStringEqualsFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringEqualsFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'foo.xml'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringEqualsFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'bar.xml'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertStringEqualsStringIgnoringLineEndingsTest.php b/tests/unit/Framework/Assert/assertStringEqualsStringIgnoringLineEndingsTest.php new file mode 100644 index 00000000000..5123272c528 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringEqualsStringIgnoringLineEndingsTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringEqualsStringIgnoringLineEndings')] +#[TestDox('assertStringEqualsStringIgnoringLineEndings()')] +#[Small] +final class assertStringEqualsStringIgnoringLineEndingsTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ["a\nb", "a\r\nb"], + ["a\rb", "a\r\nb"], + ["a\r\nb", "a\r\nb"], + ["a\nb", "a\rb"], + ["a\rb", "a\rb"], + ["a\r\nb", "a\rb"], + ["a\nb", "a\nb"], + ["a\rb", "a\nb"], + ["a\r\nb", "a\nb"], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ["a\nb", 'ab'], + ["a\rb", 'ab'], + ["a\r\nb", 'ab'], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expected, string $actual): void + { + $this->assertStringEqualsStringIgnoringLineEndings($expected, $actual); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expected, string $actual): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringEqualsStringIgnoringLineEndings($expected, $actual); + } +} diff --git a/tests/unit/Framework/Assert/assertStringMatchesFormatFileTest.php b/tests/unit/Framework/Assert/assertStringMatchesFormatFileTest.php new file mode 100644 index 00000000000..a19a60fab42 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringMatchesFormatFileTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringMatchesFormatFile')] +#[TestDox('assertStringMatchesFormatFile()')] +#[Small] +final class assertStringMatchesFormatFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringMatchesFormatFile(TEST_FILES_PATH . 'expectedFileFormat.txt', "FOO\n"); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringMatchesFormatFile(TEST_FILES_PATH . 'expectedFileFormat.txt', "BAR\n"); + } +} diff --git a/tests/unit/Framework/Assert/assertStringMatchesFormatTest.php b/tests/unit/Framework/Assert/assertStringMatchesFormatTest.php new file mode 100644 index 00000000000..5e9d80394e8 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringMatchesFormatTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringMatchesFormat')] +#[TestDox('assertStringMatchesFormat()')] +#[Small] +final class assertStringMatchesFormatTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringMatchesFormat('*%s*', '***'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringMatchesFormat('*%s*', '**'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringNotContainsStringIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertStringNotContainsStringIgnoringCaseTest.php new file mode 100644 index 00000000000..00cb4b23ece --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringNotContainsStringIgnoringCaseTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringNotContainsStringIgnoringCase')] +#[TestDox('assertStringNotContainsStringIgnoringCase()')] +#[Small] +final class assertStringNotContainsStringIgnoringCaseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringNotContainsStringIgnoringCase('BARBARA', 'foobarbaz'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringNotContainsStringIgnoringCase('BAR', 'foobarbaz'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringNotContainsStringTest.php b/tests/unit/Framework/Assert/assertStringNotContainsStringTest.php new file mode 100644 index 00000000000..9ec048bd002 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringNotContainsStringTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringNotContainsString')] +#[TestDox('assertStringNotContainsString()')] +#[Small] +final class assertStringNotContainsStringTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringNotContainsString('barbara', 'foobarbaz'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringNotContainsString('bar', 'foobarbaz'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringNotEqualsFileCanonicalizingTest.php b/tests/unit/Framework/Assert/assertStringNotEqualsFileCanonicalizingTest.php new file mode 100644 index 00000000000..b8cd2f85f80 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringNotEqualsFileCanonicalizingTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringNotEqualsFileCanonicalizing')] +#[TestDox('assertStringNotEqualsFileCanonicalizing()')] +#[Small] +final class assertStringNotEqualsFileCanonicalizingTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringNotEqualsFileCanonicalizing( + TEST_FILES_PATH . 'foo.txt', + file_get_contents(TEST_FILES_PATH . 'foo.txt') . 'BAR', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringNotEqualsFileCanonicalizing( + TEST_FILES_PATH . 'foo.txt', + file_get_contents(TEST_FILES_PATH . 'foo.txt'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertStringNotEqualsFileIgnoringCaseTest.php b/tests/unit/Framework/Assert/assertStringNotEqualsFileIgnoringCaseTest.php new file mode 100644 index 00000000000..da141ba288a --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringNotEqualsFileIgnoringCaseTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringNotEqualsFileIgnoringCase')] +#[TestDox('assertStringNotEqualsFileIgnoringCase()')] +#[Small] +final class assertStringNotEqualsFileIgnoringCaseTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringNotEqualsFileIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'bar.xml'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringNotEqualsFileIgnoringCase( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'fooUppercase.xml'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertStringNotEqualsFileTest.php b/tests/unit/Framework/Assert/assertStringNotEqualsFileTest.php new file mode 100644 index 00000000000..5effdea0155 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringNotEqualsFileTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringNotEqualsFile')] +#[TestDox('assertStringNotEqualsFile()')] +#[Small] +final class assertStringNotEqualsFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringNotEqualsFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'bar.xml'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringNotEqualsFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'foo.xml'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertStringStartsNotWithTest.php b/tests/unit/Framework/Assert/assertStringStartsNotWithTest.php new file mode 100644 index 00000000000..4646f27d57e --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringStartsNotWithTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringStartsNotWith')] +#[TestDox('assertStringStartsNotWith()')] +#[Small] +final class assertStringStartsNotWithTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringStartsNotWith('prefix', 'foo'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringStartsNotWith('prefix', 'prefixfoo'); + } +} diff --git a/tests/unit/Framework/Assert/assertStringStartsWithTest.php b/tests/unit/Framework/Assert/assertStringStartsWithTest.php new file mode 100644 index 00000000000..d17898edcf8 --- /dev/null +++ b/tests/unit/Framework/Assert/assertStringStartsWithTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertStringStartsWith')] +#[TestDox('assertStringStartsWith()')] +#[Small] +final class assertStringStartsWithTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertStringStartsWith('prefix', 'prefixfoo'); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertStringStartsWith('prefix', 'foo'); + } +} diff --git a/tests/unit/Framework/Assert/assertThatTest.php b/tests/unit/Framework/Assert/assertThatTest.php new file mode 100644 index 00000000000..79c6faa5115 --- /dev/null +++ b/tests/unit/Framework/Assert/assertThatTest.php @@ -0,0 +1,407 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use const INF; +use const NAN; +use function fclose; +use function fopen; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\TestFixture\Book; +use PHPUnit\TestFixture\ObjectEquals\ValueObject; +use stdClass; + +#[CoversClass(Assert::class)] +#[TestDox('assertThat()')] +#[Small] +final class assertThatTest extends TestCase +{ + #[DoesNotPerformAssertions] + public function testAssertThatAnything(): void + { + $this->assertThat('anything', $this->anything()); + } + + public function testAssertThatIsTrue(): void + { + $this->assertThat(true, $this->isTrue()); + } + + public function testAssertThatIsFalse(): void + { + $this->assertThat(false, $this->isFalse()); + } + + public function testAssertThatIsJson(): void + { + $this->assertThat('{}', $this->isJson()); + } + + #[DoesNotPerformAssertions] + public function testAssertThatAnythingAndAnything(): void + { + $this->assertThat( + 'anything', + $this->logicalAnd( + $this->anything(), + $this->anything(), + ), + ); + } + + #[DoesNotPerformAssertions] + public function testAssertThatAnythingOrAnything(): void + { + $this->assertThat( + 'anything', + $this->logicalOr( + $this->anything(), + $this->anything(), + ), + ); + } + + #[DoesNotPerformAssertions] + public function testAssertThatAnythingXorNotAnything(): void + { + $this->assertThat( + 'anything', + $this->logicalXor( + $this->anything(), + $this->logicalNot($this->anything()), + ), + ); + } + + public function testAssertThatContainsEqual(): void + { + $this->assertThat(['foo'], $this->containsEqual('foo')); + } + + public function testAssertThatContainsIdentical(): void + { + $this->assertThat(['foo'], $this->containsIdentical('foo')); + } + + public function testAssertThatStringMatchesFormatDescription(): void + { + $this->assertThat('foo', $this->matches('%s')); + } + + public function testAssertThatStringContains(): void + { + $this->assertThat('barfoobar', $this->stringContains('foo')); + } + + public function testAssertThatStringStartsWith(): void + { + $this->assertThat('foobar', $this->stringStartsWith('foo')); + } + + public function testAssertThatStringEndsWith(): void + { + $this->assertThat('foobar', $this->stringEndsWith('bar')); + } + + public function testAssertThatStringEqualsStringIgnoringLineEndings(): void + { + $this->assertThat('foo' . "\r\n", $this->stringEqualsStringIgnoringLineEndings('foo' . "\n")); + } + + public function testAssertThatContainsOnly(): void + { + $this->assertThat(['foo'], $this->containsOnly('string')); + } + + public function testAssertThatContainsOnlyArray(): void + { + $this->assertThat([[]], $this->containsOnlyArray()); + } + + public function testAssertThatContainsOnlyBool(): void + { + $this->assertThat([true], $this->containsOnlyBool()); + } + + public function testAssertThatContainsOnlyCallable(): void + { + $callable = static function (): void + {}; + + $this->assertThat([$callable], $this->containsOnlyCallable()); + } + + public function testAssertThatContainsOnlyFloat(): void + { + $this->assertThat([0.0], $this->containsOnlyFloat()); + } + + public function testAssertThatContainsOnlyInt(): void + { + $this->assertThat([0], $this->containsOnlyInt()); + } + + public function testAssertThatContainsOnlyIterable(): void + { + $this->assertThat([[]], $this->containsOnlyIterable()); + } + + public function testAssertThatContainsOnlyNull(): void + { + $this->assertThat([null], $this->containsOnlyNull()); + } + + public function testAssertThatContainsOnlyNumeric(): void + { + $this->assertThat(['0.0'], $this->containsOnlyNumeric()); + } + + public function testAssertThatContainsOnlyObject(): void + { + $this->assertThat([new stdClass], $this->containsOnlyObject()); + } + + public function testAssertThatContainsOnlyResource(): void + { + $resource = fopen(__FILE__, 'r'); + + $this->assertThat([$resource], $this->containsOnlyResource()); + } + + public function testAssertThatContainsOnlyClosedResource(): void + { + $resource = fopen(__FILE__, 'r'); + + fclose($resource); + + $this->assertThat([$resource], $this->containsOnlyClosedResource()); + } + + public function testAssertThatContainsOnlyScalar(): void + { + $this->assertThat(['string'], $this->containsOnlyScalar()); + } + + public function testAssertThatContainsOnlyString(): void + { + $this->assertThat(['string'], $this->containsOnlyString()); + } + + public function testAssertThatContainsOnlyInstancesOf(): void + { + $this->assertThat([new Book], $this->containsOnlyInstancesOf(Book::class)); + } + + public function testAssertThatArrayHasKey(): void + { + $this->assertThat(['foo' => 'bar'], $this->arrayHasKey('foo')); + } + + public function testAssertThatArrayIsList(): void + { + $this->assertThat([0, 1, 2], $this->isList()); + } + + public function testAssertThatEqualTo(): void + { + $this->assertThat('foo', $this->equalTo('foo')); + } + + public function testAssertThatEqualToCanonicalizing(): void + { + $this->assertThat(['foo', 'bar'], $this->equalToCanonicalizing(['bar', 'foo'])); + } + + public function testAssertThatEqualToIgnoringCase(): void + { + $this->assertThat('foo', $this->equalToIgnoringCase('FOO')); + } + + public function testAssertThatEqualToWithDelta(): void + { + $this->assertThat(1.0, $this->equalToWithDelta(1.09, 0.1)); + } + + public function testAssertThatIdenticalTo(): void + { + $value = new stdClass; + $constraint = $this->identicalTo($value); + + $this->assertThat($value, $constraint); + } + + public function testAssertThatIsInstanceOf(): void + { + $this->assertThat(new stdClass, $this->isInstanceOf(stdClass::class)); + } + + #[IgnorePhpunitDeprecations] + public function testAssertThatIsType(): void + { + $this->assertThat('string', $this->isType('string')); + } + + public function testAssertThatIsArray(): void + { + $this->assertThat([], $this->isArray()); + } + + public function testAssertThatIsBool(): void + { + $this->assertThat(true, $this->isBool()); + } + + public function testAssertThatIsCallable(): void + { + $this->assertThat(static function (): void + {}, $this->isCallable()); + } + + public function testAssertThatIsFloat(): void + { + $this->assertThat(0.0, $this->isFloat()); + } + + public function testAssertThatIsInt(): void + { + $this->assertThat(0, $this->isInt()); + } + + public function testAssertThatIsIterable(): void + { + $this->assertThat([], $this->isIterable()); + } + + public function testAssertThatIsNumeric(): void + { + $this->assertThat('0.0', $this->isNumeric()); + } + + public function testAssertThatIsObject(): void + { + $this->assertThat(new stdClass, $this->isObject()); + } + + public function testAssertThatIsResource(): void + { + $this->assertThat(fopen(__FILE__, 'r'), $this->isResource()); + } + + public function testAssertThatIsClosedResource(): void + { + $resource = fopen(__FILE__, 'r'); + + fclose($resource); + + $this->assertThat($resource, $this->isClosedResource()); + } + + public function testAssertThatIsScalar(): void + { + $this->assertThat('string', $this->isScalar()); + } + + public function testAssertThatIsString(): void + { + $this->assertThat('string', $this->isString()); + } + + public function testAssertThatIsEmpty(): void + { + $this->assertThat([], $this->isEmpty()); + } + + public function testAssertThatFileIsReadable(): void + { + $this->assertThat(__FILE__, $this->isReadable()); + } + + public function testAssertThatFileIsWritable(): void + { + $this->assertThat(__FILE__, $this->isWritable()); + } + + public function testAssertThatDirectoryExists(): void + { + $this->assertThat(__DIR__, $this->directoryExists()); + } + + public function testAssertThatFileExists(): void + { + $this->assertThat(__FILE__, $this->fileExists()); + } + + public function testAssertThatGreaterThan(): void + { + $this->assertThat(2, $this->greaterThan(1)); + } + + public function testAssertThatGreaterThanOrEqual(): void + { + $this->assertThat(2, $this->greaterThanOrEqual(1)); + } + + public function testAssertThatLessThan(): void + { + $this->assertThat(1, $this->lessThan(2)); + } + + public function testAssertThatLessThanOrEqual(): void + { + $this->assertThat(1, $this->lessThanOrEqual(2)); + } + + public function testAssertThatMatchesRegularExpression(): void + { + $this->assertThat('foobar', $this->matchesRegularExpression('/foo/')); + } + + public function testAssertThatCallback(): void + { + $this->assertThat( + null, + $this->callback(static fn ($other) => true), + ); + } + + public function testAssertThatCountOf(): void + { + $this->assertThat([1], $this->countOf(1)); + } + + public function testAssertThatNull(): void + { + $this->assertThat(null, $this->isNull()); + } + + public function testAssertThatIsFinite(): void + { + $this->assertThat(0, $this->isFinite()); + } + + public function testAssertThatIsInfinite(): void + { + $this->assertThat(INF, $this->isInfinite()); + } + + public function testAssertThatIsNan(): void + { + $this->assertThat(NAN, $this->isNan()); + } + + public function testAssertThatObjectEquals(): void + { + $this->assertObjectEquals(new ValueObject(1), new ValueObject(1)); + } +} diff --git a/tests/unit/Framework/Assert/assertTrueTest.php b/tests/unit/Framework/Assert/assertTrueTest.php new file mode 100644 index 00000000000..15694f81a43 --- /dev/null +++ b/tests/unit/Framework/Assert/assertTrueTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertTrue')] +#[TestDox('assertTrue()')] +#[Small] +final class assertTrueTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertTrue(true); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + /* @noinspection PhpUnitAssertCanBeReplacedWithFailInspection */ + $this->assertTrue(false); + } +} diff --git a/tests/unit/Framework/Assert/assertXmlFileEqualsXmlFileTest.php b/tests/unit/Framework/Assert/assertXmlFileEqualsXmlFileTest.php new file mode 100644 index 00000000000..9c0c8273681 --- /dev/null +++ b/tests/unit/Framework/Assert/assertXmlFileEqualsXmlFileTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertXmlFileEqualsXmlFile')] +#[TestDox('assertXmlFileEqualsXmlFile()')] +#[Small] +final class assertXmlFileEqualsXmlFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertXmlFileEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'foo.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertXmlFileEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertXmlFileNotEqualsXmlFileTest.php b/tests/unit/Framework/Assert/assertXmlFileNotEqualsXmlFileTest.php new file mode 100644 index 00000000000..7a3c46e3b1c --- /dev/null +++ b/tests/unit/Framework/Assert/assertXmlFileNotEqualsXmlFileTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertXmlFileNotEqualsXmlFile')] +#[TestDox('assertXmlFileNotEqualsXmlFile()')] +#[Small] +final class assertXmlFileNotEqualsXmlFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertXmlFileNotEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'bar.xml', + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertXmlFileNotEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + TEST_FILES_PATH . 'foo.xml', + ); + } +} diff --git a/tests/unit/Framework/Assert/assertXmlStringEqualsXmlFileTest.php b/tests/unit/Framework/Assert/assertXmlStringEqualsXmlFileTest.php new file mode 100644 index 00000000000..074f0e8778c --- /dev/null +++ b/tests/unit/Framework/Assert/assertXmlStringEqualsXmlFileTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertXmlStringEqualsXmlFile')] +#[TestDox('assertXmlStringEqualsXmlFile()')] +#[Small] +final class assertXmlStringEqualsXmlFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertXmlStringEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'foo.xml'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertXmlStringEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'bar.xml'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertXmlStringEqualsXmlStringTest.php b/tests/unit/Framework/Assert/assertXmlStringEqualsXmlStringTest.php new file mode 100644 index 00000000000..b729fd31a1c --- /dev/null +++ b/tests/unit/Framework/Assert/assertXmlStringEqualsXmlStringTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertXmlStringEqualsXmlString')] +#[TestDox('assertXmlStringEqualsXmlString()')] +#[Small] +final class assertXmlStringEqualsXmlStringTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function successProvider(): array + { + return [ + ['', ''], + ['', ''], + [ + <<<'XML' + + + + +XML, + <<<'XML' + + + + +XML + ], + ]; + } + + /** + * @return non-empty-list + */ + public static function failureProvider(): array + { + return [ + ['', ''], + ]; + } + + #[DataProvider('successProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expectedXml, string $actualXml): void + { + $this->assertXmlStringEqualsXmlString($expectedXml, $actualXml); + } + + #[DataProvider('failureProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expectedXml, string $actualXml): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertXmlStringEqualsXmlString($expectedXml, $actualXml); + } +} diff --git a/tests/unit/Framework/Assert/assertXmlStringNotEqualsXmlFileTest.php b/tests/unit/Framework/Assert/assertXmlStringNotEqualsXmlFileTest.php new file mode 100644 index 00000000000..2e8a7721e66 --- /dev/null +++ b/tests/unit/Framework/Assert/assertXmlStringNotEqualsXmlFileTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use function file_get_contents; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertXmlStringNotEqualsXmlFile')] +#[TestDox('assertXmlStringNotEqualsXmlFile()')] +#[Small] +final class assertXmlStringNotEqualsXmlFileTest extends TestCase +{ + public function testSucceedsWhenConstraintEvaluatesToTrue(): void + { + $this->assertXmlStringNotEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'bar.xml'), + ); + } + + public function testFailsWhenConstraintEvaluatesToFalse(): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertXmlStringNotEqualsXmlFile( + TEST_FILES_PATH . 'foo.xml', + file_get_contents(TEST_FILES_PATH . 'foo.xml'), + ); + } +} diff --git a/tests/unit/Framework/Assert/assertXmlStringNotEqualsXmlStringTest.php b/tests/unit/Framework/Assert/assertXmlStringNotEqualsXmlStringTest.php new file mode 100644 index 00000000000..f00a7d0fcf4 --- /dev/null +++ b/tests/unit/Framework/Assert/assertXmlStringNotEqualsXmlStringTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'assertXmlStringNotEqualsXmlString')] +#[TestDox('assertXmlStringNotEqualsXmlString()')] +#[Small] +final class assertXmlStringNotEqualsXmlStringTest extends TestCase +{ + #[DataProviderExternal(assertXmlStringEqualsXmlStringTest::class, 'failureProvider')] + public function testSucceedsWhenConstraintEvaluatesToTrue(string $expectedXml, string $actualXml): void + { + $this->assertXmlStringNotEqualsXmlString($expectedXml, $actualXml); + } + + #[DataProviderExternal(assertXmlStringEqualsXmlStringTest::class, 'successProvider')] + public function testFailsWhenConstraintEvaluatesToFalse(string $expectedXml, string $actualXml): void + { + $this->expectException(AssertionFailedError::class); + + $this->assertXmlStringNotEqualsXmlString($expectedXml, $actualXml); + } +} diff --git a/tests/unit/Framework/Assert/failTest.php b/tests/unit/Framework/Assert/failTest.php new file mode 100644 index 00000000000..07d15b9680c --- /dev/null +++ b/tests/unit/Framework/Assert/failTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'fail')] +#[TestDox('fail()')] +#[Small] +final class failTest extends TestCase +{ + public function testFailsTest(): void + { + $message = 'message'; + + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage($message); + + $this->fail($message); + } +} diff --git a/tests/unit/Framework/Assert/markTestIncompleteTest.php b/tests/unit/Framework/Assert/markTestIncompleteTest.php new file mode 100644 index 00000000000..05878f58d8a --- /dev/null +++ b/tests/unit/Framework/Assert/markTestIncompleteTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'markTestIncomplete')] +#[TestDox('markTestIncomplete()')] +#[Small] +final class markTestIncompleteTest extends TestCase +{ + public function testMarksTestAsIncomplete(): void + { + $message = 'message'; + + $this->expectException(IncompleteTestError::class); + $this->expectExceptionMessage($message); + + $this->markTestIncomplete($message); + } +} diff --git a/tests/unit/Framework/Assert/markTestSkippedTest.php b/tests/unit/Framework/Assert/markTestSkippedTest.php new file mode 100644 index 00000000000..bccf8534e8a --- /dev/null +++ b/tests/unit/Framework/Assert/markTestSkippedTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; + +#[CoversMethod(Assert::class, 'markTestSkipped')] +#[TestDox('markTestSkipped()')] +#[Small] +final class markTestSkippedTest extends TestCase +{ + public function testMarksTestAsSkipped(): void + { + $message = 'message'; + + $this->expectException(SkippedWithMessageException::class); + $this->expectExceptionMessage($message); + + $this->markTestSkipped($message); + } +} diff --git a/tests/unit/Framework/AssertTest.php b/tests/unit/Framework/AssertTest.php deleted file mode 100644 index 2b35f55bee4..00000000000 --- a/tests/unit/Framework/AssertTest.php +++ /dev/null @@ -1,2436 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use const DIRECTORY_SEPARATOR; -use const INF; -use const NAN; -use const PHP_OS_FAMILY; -use function acos; -use function array_merge; -use function chmod; -use function fclose; -use function file_get_contents; -use function fopen; -use function json_encode; -use function log; -use function mkdir; -use function octdec; -use function PHPUnit\TestFixture\Generator\f; -use function rmdir; -use function sys_get_temp_dir; -use function tempnam; -use function uniqid; -use function unlink; -use ArrayIterator; -use ArrayObject; -use DateTime; -use DateTimeZone; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\TestFixture\Author; -use PHPUnit\TestFixture\Book; -use PHPUnit\TestFixture\ClassWithToString; -use PHPUnit\TestFixture\ObjectEquals\ValueObject; -use PHPUnit\TestFixture\SampleArrayAccess; -use PHPUnit\TestFixture\SampleClass; -use PHPUnit\TestFixture\Struct; -use PHPUnit\Util\Xml\Loader as XmlLoader; -use PHPUnit\Util\Xml\XmlException; -use SplObjectStorage; -use stdClass; - -#[CoversClass(Assert::class)] -#[CoversClass(GeneratorNotSupportedException::class)] -#[Small] -final class AssertTest extends TestCase -{ - public static function validInvalidJsonProvider(): array - { - return [ - 'error syntax in expected JSON' => ['{"Mascott"::}', '{"Mascott" : "Tux"}'], - 'error UTF-8 in actual JSON' => ['{"Mascott" : "Tux"}', '{"Mascott" : :}'], - ]; - } - - public static function equalProvider(): array - { - // same |= equal - return array_merge(self::equalValues(), self::sameValues()); - } - - public static function notEqualProvider(): array - { - return self::notEqualValues(); - } - - public static function sameProvider(): array - { - return self::sameValues(); - } - - public static function notSameProvider(): array - { - // not equal |= not same - // equal, ¬same |= not same - return array_merge(self::notEqualValues(), self::equalValues()); - } - - public static function assertStringContainsStringIgnoringLineEndingsProvider(): array - { - return [ - ["b\nc", "b\r\nc"], - ["b\nc", "a\r\nb\r\nc\r\nd"], - ]; - } - - public static function assertStringEqualsStringIgnoringLineEndingsProvider(): array - { - return [ - 'lf-crlf' => ["a\nb", "a\r\nb"], - 'cr-crlf' => ["a\rb", "a\r\nb"], - 'crlf-crlf' => ["a\r\nb", "a\r\nb"], - 'lf-cr' => ["a\nb", "a\rb"], - 'cr-cr' => ["a\rb", "a\rb"], - 'crlf-cr' => ["a\r\nb", "a\rb"], - 'lf-lf' => ["a\nb", "a\nb"], - 'cr-lf' => ["a\rb", "a\nb"], - 'crlf-lf' => ["a\r\nb", "a\nb"], - ]; - } - - public static function assertStringEqualsStringIgnoringLineEndingsProviderNegative(): array - { - return [ - ["a\nb", 'ab'], - ["a\rb", 'ab'], - ["a\r\nb", 'ab'], - ]; - } - - public function testFail(): void - { - try { - $this->fail(); - } catch (AssertionFailedError) { - return; - } - - throw new AssertionFailedError('Fail did not throw fail exception'); - } - - public function testAssertContainsOnlyInstancesOf(): void - { - $test = [new Book, new Book]; - - $this->assertContainsOnlyInstancesOf(Book::class, $test); - $this->assertContainsOnlyInstancesOf(stdClass::class, [new stdClass]); - - $test2 = [new Author('Test')]; - - $this->expectException(AssertionFailedError::class); - - $this->assertContainsOnlyInstancesOf(Book::class, $test2); - } - - public function testAssertArrayHasIntegerKey(): void - { - $this->assertArrayHasKey(0, ['foo']); - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayHasKey(1, ['foo']); - } - - public function testAssertArrayNotHasIntegerKey(): void - { - $this->assertArrayNotHasKey(1, ['foo']); - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayNotHasKey(0, ['foo']); - } - - public function testAssertArrayHasStringKey(): void - { - $this->assertArrayHasKey('foo', ['foo' => 'bar']); - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayHasKey('bar', ['foo' => 'bar']); - } - - public function testAssertArrayNotHasStringKey(): void - { - $this->assertArrayNotHasKey('bar', ['foo' => 'bar']); - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayNotHasKey('foo', ['foo' => 'bar']); - } - - public function testAssertArrayHasKeyAcceptsArrayObjectValue(): void - { - $array = new ArrayObject; - $array['foo'] = 'bar'; - - $this->assertArrayHasKey('foo', $array); - } - - public function testAssertArrayHasKeyProperlyFailsWithArrayObjectValue(): void - { - $array = new ArrayObject; - $array['bar'] = 'bar'; - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayHasKey('foo', $array); - } - - public function testAssertArrayHasKeyAcceptsArrayAccessValue(): void - { - $array = new SampleArrayAccess; - $array['foo'] = 'bar'; - - $this->assertArrayHasKey('foo', $array); - } - - public function testAssertArrayHasKeyProperlyFailsWithArrayAccessValue(): void - { - $array = new SampleArrayAccess; - $array['bar'] = 'bar'; - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayHasKey('foo', $array); - } - - public function testAssertArrayNotHasKeyAcceptsArrayAccessValue(): void - { - $array = new ArrayObject; - $array['foo'] = 'bar'; - - $this->assertArrayNotHasKey('bar', $array); - } - - public function testAssertArrayNotHasKeyProperlyFailsWithArrayAccessValue(): void - { - $array = new ArrayObject; - $array['bar'] = 'bar'; - - $this->expectException(AssertionFailedError::class); - - $this->assertArrayNotHasKey('bar', $array); - } - - public function testAssertIsList(): void - { - $this->assertIsList([0, 1, 2]); - - $this->expectException(AssertionFailedError::class); - - $this->assertIsList([0 => 0, 2 => 2, 3 => 3]); - } - - public function testAssertIsListWithEmptyArray(): void - { - $this->assertIsList([]); - } - - public function testAssertIsListFailsWithStringKeys(): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertIsList(['string' => 0]); - } - - public function testAssertIsListFailsForTypesOtherThanArray(): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertIsList(null); - } - - public function testAssertArrayContainsOnlyIntegers(): void - { - $this->assertContainsOnly('integer', [1, 2, 3]); - - $this->expectException(AssertionFailedError::class); - - $this->assertContainsOnly('integer', ['1', 2, 3]); - } - - public function testAssertArrayNotContainsOnlyIntegers(): void - { - $this->assertNotContainsOnly('integer', ['1', 2, 3]); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotContainsOnly('integer', [1, 2, 3]); - } - - public function testAssertArrayContainsOnlyStdClass(): void - { - $this->assertContainsOnly(stdClass::class, [new stdClass]); - - $this->expectException(AssertionFailedError::class); - - $this->assertContainsOnly(stdClass::class, [stdClass::class]); - } - - public function testAssertArrayNotContainsOnlyStdClass(): void - { - $this->assertNotContainsOnly(stdClass::class, [stdClass::class]); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotContainsOnly(stdClass::class, [new stdClass]); - } - - #[DataProvider('equalProvider')] - public function testAssertEqualsSucceeds(mixed $a, mixed $b): void - { - $this->assertEquals($a, $b); - } - - #[DataProvider('notEqualProvider')] - public function testAssertEqualsFails(mixed $a, mixed $b): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertEquals($a, $b); - } - - #[DataProvider('notEqualProvider')] - public function testAssertNotEqualsSucceeds(mixed $a, mixed $b): void - { - $this->assertNotEquals($a, $b); - } - - #[DataProvider('equalProvider')] - public function testAssertNotEqualsFails(mixed $a, mixed $b): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertNotEquals($a, $b); - } - - #[DataProvider('sameProvider')] - public function testAssertSameSucceeds(mixed $a, mixed $b): void - { - $this->assertSame($a, $b); - } - - #[DataProvider('notSameProvider')] - public function testAssertSameFails(mixed $a, mixed $b): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertSame($a, $b); - } - - #[DataProvider('notSameProvider')] - public function testAssertNotSameSucceeds(mixed $a, mixed $b): void - { - $this->assertNotSame($a, $b); - } - - #[DataProvider('sameProvider')] - public function testAssertNotSameFails(mixed $a, mixed $b): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertNotSame($a, $b); - } - - public function testAssertXmlFileEqualsXmlFile(): void - { - $this->assertXmlFileEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'foo.xml', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertXmlFileEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'bar.xml', - ); - } - - public function testAssertXmlFileNotEqualsXmlFile(): void - { - $this->assertXmlFileNotEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'bar.xml', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertXmlFileNotEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'foo.xml', - ); - } - - public function testAssertXmlStringEqualsXmlFile(): void - { - $this->assertXmlStringEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'foo.xml'), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertXmlStringEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'bar.xml'), - ); - } - - public function testXmlStringNotEqualsXmlFile(): void - { - $this->assertXmlStringNotEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'bar.xml'), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertXmlStringNotEqualsXmlFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'foo.xml'), - ); - } - - public function testAssertXmlStringEqualsXmlString(): void - { - $this->assertXmlStringEqualsXmlString('', ''); - - $this->expectException(AssertionFailedError::class); - - $this->assertXmlStringEqualsXmlString('', ''); - } - - public function testAssertXmlStringEqualsXmlString2(): void - { - $this->expectException(XmlException::class); - - $this->assertXmlStringEqualsXmlString('', ''); - } - - public function testAssertXmlStringEqualsXmlString3(): void - { - $expected = <<<'XML' - - - - -XML; - - $actual = <<<'XML' - - - - -XML; - - $this->assertXmlStringEqualsXmlString($expected, $actual); - } - - public function testAssertXmlStringNotEqualsXmlString(): void - { - $this->assertXmlStringNotEqualsXmlString('', ''); - - $this->expectException(AssertionFailedError::class); - - $this->assertXmlStringNotEqualsXmlString('', ''); - } - - public function testAssertStringEqualsNumeric(): void - { - $this->assertEquals('0', 0); - - $this->expectException(AssertionFailedError::class); - - $this->assertEquals('0', 1); - } - - public function testAssertStringEqualsNumeric2(): void - { - $this->assertNotEquals('A', 0); - } - - public function testAssertIsReadable(): void - { - $this->assertIsReadable(__FILE__); - - $this->expectException(AssertionFailedError::class); - - $this->assertIsReadable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertIsNotReadable(): void - { - $this->assertIsNotReadable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - - $this->expectException(AssertionFailedError::class); - - $this->assertIsNotReadable(__FILE__); - } - - public function testAssertIsWritable(): void - { - $this->assertIsWritable(__FILE__); - - $this->expectException(AssertionFailedError::class); - - $this->assertIsWritable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertNotIsWritable(): void - { - $this->assertIsNotWritable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - - $this->expectException(AssertionFailedError::class); - - $this->assertIsNotWritable(__FILE__); - } - - public function testAssertDirectoryExists(): void - { - $this->assertDirectoryExists(__DIR__); - - $this->expectException(AssertionFailedError::class); - - $this->assertDirectoryExists(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertDirectoryNotExists(): void - { - $this->assertDirectoryDoesNotExist(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - - $this->expectException(AssertionFailedError::class); - - $this->assertDirectoryDoesNotExist(__DIR__); - } - - public function testAssertDirectoryIsReadable(): void - { - $this->assertDirectoryIsReadable(__DIR__); - - $this->expectException(AssertionFailedError::class); - - $this->assertDirectoryIsReadable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertDirectoryIsNotReadable(): void - { - if (PHP_OS_FAMILY === 'Windows') { - $this->markTestSkipped('Cannot test this behaviour on Windows'); - } - - $dirName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('unreadable_dir_', true); - mkdir($dirName, octdec('0')); - - $this->assertDirectoryIsNotReadable($dirName); - - chmod($dirName, octdec('444')); - - try { - $this->assertDirectoryIsNotReadable($dirName); - } catch (AssertionFailedError) { - } - - rmdir($dirName); - } - - public function testAssertDirectoryIsWritable(): void - { - $this->assertDirectoryIsWritable(__DIR__); - - $this->expectException(AssertionFailedError::class); - - $this->assertDirectoryIsWritable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertDirectoryIsNotWritable(): void - { - if (PHP_OS_FAMILY === 'Windows') { - $this->markTestSkipped('Cannot test this behaviour on Windows'); - } - - $dirName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('not_writable_dir_', true); - mkdir($dirName, octdec('444')); - - $this->assertDirectoryIsNotWritable($dirName); - - chmod($dirName, octdec('755')); - - try { - $this->assertDirectoryIsNotWritable($dirName); - } catch (AssertionFailedError) { - } - - rmdir($dirName); - } - - public function testAssertFileExists(): void - { - $this->assertFileExists(__FILE__); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileExists(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertFileNotExists(): void - { - $this->assertFileDoesNotExist(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileDoesNotExist(__FILE__); - } - - public function testAssertFileIsReadable(): void - { - $this->assertFileIsReadable(__FILE__); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileIsReadable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertFileIsNotReadable(): void - { - if (PHP_OS_FAMILY === 'Windows') { - $this->markTestSkipped('Cannot test this behaviour on Windows'); - } - - $tempFile = tempnam( - sys_get_temp_dir(), - 'unreadable', - ); - - chmod($tempFile, octdec('0')); - - $this->assertFileIsNotReadable($tempFile); - - chmod($tempFile, octdec('755')); - - try { - $this->assertFileIsNotReadable($tempFile); - } catch (AssertionFailedError) { - } - - unlink($tempFile); - } - - public function testAssertFileIsNotWritable(): void - { - $tempFile = tempnam(sys_get_temp_dir(), 'not_writable'); - - chmod($tempFile, octdec('0')); - - $this->assertFileIsNotWritable($tempFile); - - chmod($tempFile, octdec('755')); - - try { - $this->assertFileIsNotWritable($tempFile); - } catch (AssertionFailedError) { - } - - unlink($tempFile); - } - - public function testAssertFileIsWritable(): void - { - $this->assertFileIsWritable(__FILE__); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileIsWritable(__DIR__ . DIRECTORY_SEPARATOR . 'NotExisting'); - } - - public function testAssertFinite(): void - { - $this->assertFinite(1); - - $this->expectException(AssertionFailedError::class); - - $this->assertFinite(INF); - } - - public function testAssertInfinite(): void - { - $this->assertInfinite(INF); - - $this->expectException(AssertionFailedError::class); - - $this->assertInfinite(1); - } - - public function testAssertNan(): void - { - $this->assertNan(NAN); - - $this->expectException(AssertionFailedError::class); - - $this->assertNan(1); - } - - public function testAssertNull(): void - { - $this->assertNull(null); - - $this->expectException(AssertionFailedError::class); - - $this->assertNull(new stdClass); - } - - public function testAssertNotNull(): void - { - $this->assertNotNull(new stdClass); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotNull(null); - } - - public function testAssertTrue(): void - { - $this->assertTrue(true); - - $this->expectException(AssertionFailedError::class); - - /* @noinspection PhpUnitAssertCanBeReplacedWithFailInspection */ - /* @noinspection PhpUnitAssertTrueWithIncompatibleTypeArgumentInspection */ - $this->assertTrue(false); - } - - public function testAssertNotTrue(): void - { - $this->assertNotTrue(false); - $this->assertNotTrue(1); - $this->assertNotTrue('true'); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotTrue(true); - } - - public function testAssertFalse(): void - { - $this->assertFalse(false); - - $this->expectException(AssertionFailedError::class); - - /* @noinspection PhpUnitAssertCanBeReplacedWithFailInspection */ - $this->assertFalse(true); - } - - public function testAssertNotFalse(): void - { - $this->assertNotFalse(true); - $this->assertNotFalse(0); - $this->assertNotFalse(''); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotFalse(false); - } - - public function testAssertMatchesRegularExpression(): void - { - $this->assertMatchesRegularExpression('/foo/', 'foobar'); - - $this->expectException(AssertionFailedError::class); - - $this->assertMatchesRegularExpression('/foo/', 'bar'); - } - - public function testAssertDoesNotMatchRegularExpression(): void - { - $this->assertDoesNotMatchRegularExpression('/foo/', 'bar'); - - $this->expectException(AssertionFailedError::class); - - $this->assertDoesNotMatchRegularExpression('/foo/', 'foobar'); - } - - public function testAssertSame(): void - { - $o = new stdClass; - - $this->assertSame($o, $o); - - $this->expectException(AssertionFailedError::class); - - $this->assertSame(new stdClass, new stdClass); - } - - public function testAssertSame2(): void - { - $this->assertSame(true, true); - $this->assertSame(false, false); - - $this->expectException(AssertionFailedError::class); - - $this->assertSame(true, false); - } - - public function testAssertNotSame(): void - { - $this->assertNotSame( - new stdClass, - null, - ); - - $this->assertNotSame( - null, - new stdClass, - ); - - $this->assertNotSame( - new stdClass, - new stdClass, - ); - - $o = new stdClass; - - $this->expectException(AssertionFailedError::class); - - $this->assertNotSame($o, $o); - } - - public function testAssertNotSame2(): void - { - $this->assertNotSame(true, false); - $this->assertNotSame(false, true); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotSame(true, true); - } - - public function testAssertNotSameFailsNull(): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertNotSame(null, null); - } - - public function testGreaterThan(): void - { - $this->assertGreaterThan(1, 2); - - $this->expectException(AssertionFailedError::class); - - $this->assertGreaterThan(2, 1); - } - - public function testGreaterThanOrEqual(): void - { - $this->assertGreaterThanOrEqual(1, 2); - - $this->expectException(AssertionFailedError::class); - - $this->assertGreaterThanOrEqual(2, 1); - } - - public function testLessThan(): void - { - $this->assertLessThan(2, 1); - - try { - $this->assertLessThan(1, 2); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testLessThanOrEqual(): void - { - $this->assertLessThanOrEqual(2, 1); - - $this->expectException(AssertionFailedError::class); - - $this->assertLessThanOrEqual(1, 2); - } - - #[DoesNotPerformAssertions] - public function testAssertThatAnything(): void - { - $this->assertThat('anything', $this->anything()); - } - - public function testAssertThatIsTrue(): void - { - $this->assertThat(true, $this->isTrue()); - } - - public function testAssertThatIsFalse(): void - { - $this->assertThat(false, $this->isFalse()); - } - - public function testAssertThatIsJson(): void - { - $this->assertThat('{}', $this->isJson()); - } - - #[DoesNotPerformAssertions] - public function testAssertThatAnythingAndAnything(): void - { - $this->assertThat( - 'anything', - $this->logicalAnd( - $this->anything(), - $this->anything(), - ), - ); - } - - #[DoesNotPerformAssertions] - public function testAssertThatAnythingOrAnything(): void - { - $this->assertThat( - 'anything', - $this->logicalOr( - $this->anything(), - $this->anything(), - ), - ); - } - - #[DoesNotPerformAssertions] - public function testAssertThatAnythingXorNotAnything(): void - { - $this->assertThat( - 'anything', - $this->logicalXor( - $this->anything(), - $this->logicalNot($this->anything()), - ), - ); - } - - public function testAssertThatContains(): void - { - $this->assertThat(['foo'], $this->containsIdentical('foo')); - } - - public function testAssertThatStringContains(): void - { - $this->assertThat('barfoobar', $this->stringContains('foo')); - } - - public function testAssertThatContainsOnly(): void - { - $this->assertThat(['foo'], $this->containsOnly('string')); - } - - public function testAssertThatContainsOnlyInstancesOf(): void - { - $this->assertThat([new Book], $this->containsOnlyInstancesOf(Book::class)); - } - - public function testAssertThatArrayHasKey(): void - { - $this->assertThat(['foo' => 'bar'], $this->arrayHasKey('foo')); - } - - public function testAssertThatArrayIsList(): void - { - $this->assertThat([0, 1, 2], $this->isList()); - } - - public function testAssertThatEqualTo(): void - { - $this->assertThat('foo', $this->equalTo('foo')); - } - - public function testAssertThatIdenticalTo(): void - { - $value = new stdClass; - $constraint = $this->identicalTo($value); - - $this->assertThat($value, $constraint); - } - - public function testAssertThatIsInstanceOf(): void - { - $this->assertThat(new stdClass, $this->isInstanceOf(stdClass::class)); - } - - public function testAssertThatIsType(): void - { - $this->assertThat('string', $this->isType('string')); - } - - public function testAssertThatIsEmpty(): void - { - $this->assertThat([], $this->isEmpty()); - } - - public function testAssertThatFileExists(): void - { - $this->assertThat(__FILE__, $this->fileExists()); - } - - public function testAssertThatGreaterThan(): void - { - $this->assertThat(2, $this->greaterThan(1)); - } - - public function testAssertThatGreaterThanOrEqual(): void - { - $this->assertThat(2, $this->greaterThanOrEqual(1)); - } - - public function testAssertThatLessThan(): void - { - $this->assertThat(1, $this->lessThan(2)); - } - - public function testAssertThatLessThanOrEqual(): void - { - $this->assertThat(1, $this->lessThanOrEqual(2)); - } - - public function testAssertThatMatchesRegularExpression(): void - { - $this->assertThat('foobar', $this->matchesRegularExpression('/foo/')); - } - - public function testAssertThatCallback(): void - { - $this->assertThat( - null, - $this->callback(static fn ($other) => true), - ); - } - - public function testAssertThatCountOf(): void - { - $this->assertThat([1], $this->countOf(1)); - } - - public function testAssertFileEquals(): void - { - $this->assertFileEquals( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'foo.xml', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileEquals( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'bar.xml', - ); - } - - public function testAssertFileNotEquals(): void - { - $this->assertFileNotEquals( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'bar.xml', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileNotEquals( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'foo.xml', - ); - } - - public function testAssertStringEqualsFile(): void - { - $this->assertStringEqualsFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'foo.xml'), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringEqualsFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'bar.xml'), - ); - } - - public function testAssertStringEqualsFileIgnoringCase(): void - { - $this->assertStringEqualsFileIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'fooUppercase.xml'), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringEqualsFileIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'bar.xml'), - ); - } - - public function testAssertStringNotEqualsFile(): void - { - $this->assertStringNotEqualsFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'bar.xml'), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringNotEqualsFile( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'foo.xml'), - ); - } - - public function testAssertStringNotEqualsFileIgnoringCase(): void - { - $this->assertStringNotEqualsFileIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'bar.xml'), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringNotEqualsFileIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - file_get_contents(TEST_FILES_PATH . 'fooUppercase.xml'), - ); - } - - public function testAssertFileEqualsIgnoringCase(): void - { - $this->assertFileEqualsIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'fooUppercase.xml', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileEqualsIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'bar.xml', - ); - } - - public function testAssertFileNotEqualsIgnoringCase(): void - { - $this->assertFileNotEqualsIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'bar.xml', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileNotEqualsIgnoringCase( - TEST_FILES_PATH . 'foo.xml', - TEST_FILES_PATH . 'fooUppercase.xml', - ); - } - - public function testAssertStringStartsWith(): void - { - $this->assertStringStartsWith('prefix', 'prefixfoo'); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringStartsWith('prefix', 'foo'); - } - - public function testAssertStringStartsNotWith(): void - { - $this->assertStringStartsNotWith('prefix', 'foo'); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringStartsNotWith('prefix', 'prefixfoo'); - } - - public function testAssertStringEndsWith(): void - { - $this->assertStringEndsWith('suffix', 'foosuffix'); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringEndsWith('suffix', 'foo'); - } - - public function testAssertStringEndsNotWith(): void - { - $this->assertStringEndsNotWith('suffix', 'foo'); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringEndsNotWith('suffix', 'foosuffix'); - } - - #[DataProvider('assertStringContainsStringIgnoringLineEndingsProvider')] - public function testAssertStringContainsStringIgnoringLineEndings(string $needle, string $haystack): void - { - $this->assertStringContainsStringIgnoringLineEndings($needle, $haystack); - } - - public function testNotAssertStringContainsStringIgnoringLineEndings(): void - { - $this->expectException(ExpectationFailedException::class); - - $this->assertStringContainsStringIgnoringLineEndings("b\nc", "\r\nc\r\n"); - } - - #[DataProvider('assertStringEqualsStringIgnoringLineEndingsProvider')] - public function testAssertStringEqualsStringIgnoringLineEndings(string $expected, string $actual): void - { - $this->assertStringEqualsStringIgnoringLineEndings($expected, $actual); - } - - #[DataProvider('assertStringEqualsStringIgnoringLineEndingsProviderNegative')] - public function testAssertStringEqualsStringIgnoringLineEndingsNegative(string $expected, string $actual): void - { - $this->expectException(ExpectationFailedException::class); - - $this->assertStringEqualsStringIgnoringLineEndings($expected, $actual); - } - - public function testAssertStringMatchesFormat(): void - { - $this->assertStringMatchesFormat('*%s*', '***'); - } - - public function testAssertStringMatchesFormatFailure(): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertStringMatchesFormat('*%s*', '**'); - } - - #[IgnorePhpunitDeprecations] - public function testAssertStringNotMatchesFormat(): void - { - $this->assertStringNotMatchesFormat('*%s*', '**'); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringMatchesFormat('*%s*', '**'); - } - - public function testAssertEmpty(): void - { - $this->assertEmpty([]); - - $this->expectException(AssertionFailedError::class); - - $this->assertEmpty(['foo']); - } - - public function testAssertEmptyGenerator(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $actual parameter is not supported'); - - $this->assertEmpty($generator); - } - - public function testAssertNotEmpty(): void - { - $this->assertNotEmpty(['foo']); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotEmpty([]); - } - - public function testAssertNotEmptyGenerator(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $actual parameter is not supported'); - - $this->assertNotEmpty($generator); - } - - public function testMarkTestIncomplete(): void - { - try { - $this->markTestIncomplete('incomplete'); - } catch (IncompleteTestError $e) { - $this->assertEquals('incomplete', $e->getMessage()); - - return; - } - - $this->fail(); - } - - public function testMarkTestSkipped(): void - { - try { - $this->markTestSkipped('skipped'); - } catch (SkippedTest $e) { - $this->assertEquals('skipped', $e->getMessage()); - - return; - } - - $this->fail(); - } - - public function testAssertCount(): void - { - $this->assertCount(2, [1, 2]); - - $this->expectException(AssertionFailedError::class); - - $this->assertCount(2, [1, 2, 3]); - } - - public function testAssertCountGenerator(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $haystack parameter is not supported'); - - $this->assertCount(0, $generator); - } - - public function testAssertCountTraversable(): void - { - $this->assertCount(2, new ArrayIterator([1, 2])); - - $this->expectException(AssertionFailedError::class); - - $this->assertCount(2, new ArrayIterator([1, 2, 3])); - } - - public function testAssertNotCount(): void - { - $this->assertNotCount(2, [1, 2, 3]); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotCount(2, [1, 2]); - } - - public function testAssertNotCountGenerator(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $haystack parameter is not supported'); - - $this->assertNotCount(0, $generator); - } - - public function testAssertSameSize(): void - { - $this->assertSameSize([1, 2], [3, 4]); - - $this->expectException(AssertionFailedError::class); - - $this->assertSameSize([1, 2], [1, 2, 3]); - } - - public function testAssertSameSizeGenerator(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $expected parameter is not supported'); - - $this->assertSameSize($generator, []); - } - - public function testAssertSameSizeGenerator2(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $actual parameter is not supported'); - - $this->assertSameSize([], $generator); - } - - public function testAssertNotSameSize(): void - { - $this->assertNotSameSize([1, 2], [1, 2, 3]); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotSameSize([1, 2], [3, 4]); - } - - public function testAssertNotSameSizeGenerator(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $expected parameter is not supported'); - - $this->assertNotSameSize($generator, []); - } - - public function testAssertNotSameSizeGenerator2(): void - { - $generator = f(); - - $this->expectException(GeneratorNotSupportedException::class); - $this->expectExceptionMessage('Passing an argument of type Generator for the $actual parameter is not supported'); - - $this->assertNotSameSize([], $generator); - } - - public function testAssertJson(): void - { - $this->assertJson('{}'); - } - - public function testAssertJsonStringEqualsJsonString(): void - { - $expected = '{"Mascott" : "Tux"}'; - $actual = '{"Mascott" : "Tux"}'; - $message = 'Given Json strings do not match'; - - $this->assertJsonStringEqualsJsonString($expected, $actual, $message); - } - - #[DataProvider('validInvalidJsonProvider')] - public function testAssertJsonStringEqualsJsonStringErrorRaised(string $expected, string $actual): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertJsonStringEqualsJsonString($expected, $actual); - } - - public function testAssertJsonStringNotEqualsJsonString(): void - { - $expected = '{"Mascott" : "Beastie"}'; - $actual = '{"Mascott" : "Tux"}'; - $message = 'Given Json strings do match'; - - $this->assertJsonStringNotEqualsJsonString($expected, $actual, $message); - } - - #[DataProvider('validInvalidJsonProvider')] - public function testAssertJsonStringNotEqualsJsonStringErrorRaised(string $expected, string $actual): void - { - $this->expectException(AssertionFailedError::class); - - $this->assertJsonStringNotEqualsJsonString($expected, $actual); - } - - public function testAssertJsonStringEqualsJsonFile(): void - { - $file = TEST_FILES_PATH . 'JsonData/simpleObject.json'; - $actual = json_encode(['Mascott' => 'Tux']); - $message = ''; - - $this->assertJsonStringEqualsJsonFile($file, $actual, $message); - } - - public function testAssertJsonStringEqualsJsonFileExpectingExpectationFailedException(): void - { - $file = TEST_FILES_PATH . 'JsonData/simpleObject.json'; - $actual = json_encode(['Mascott' => 'Beastie']); - $message = ''; - - try { - $this->assertJsonStringEqualsJsonFile($file, $actual, $message); - } catch (ExpectationFailedException $e) { - $this->assertEquals( - 'Failed asserting that \'{"Mascott":"Beastie"}\' matches JSON string "{"Mascott":"Tux"}".', - $e->getMessage(), - ); - - return; - } - - $this->fail('Expected Exception not thrown.'); - } - - public function testAssertJsonStringNotEqualsJsonFile(): void - { - $file = TEST_FILES_PATH . 'JsonData/simpleObject.json'; - $actual = json_encode(['Mascott' => 'Beastie']); - $message = ''; - - $this->assertJsonStringNotEqualsJsonFile($file, $actual, $message); - } - - public function testAssertJsonFileNotEqualsJsonFile(): void - { - $fileExpected = TEST_FILES_PATH . 'JsonData/simpleObject.json'; - $fileActual = TEST_FILES_PATH . 'JsonData/arrayObject.json'; - $message = ''; - - $this->assertJsonFileNotEqualsJsonFile($fileExpected, $fileActual, $message); - } - - public function testAssertJsonFileEqualsJsonFile(): void - { - $file = TEST_FILES_PATH . 'JsonData/simpleObject.json'; - $message = ''; - - $this->assertJsonFileEqualsJsonFile($file, $file, $message); - } - - public function testAssertInstanceOfThrowsExceptionIfTypeDoesNotExist(): void - { - $this->expectException(Exception::class); - - $this->assertInstanceOf(ClassThatDoesNotExist::class, new stdClass); - } - - public function testAssertInstanceOf(): void - { - $this->assertInstanceOf(stdClass::class, new stdClass); - - $this->expectException(AssertionFailedError::class); - - $this->assertInstanceOf(\Exception::class, new stdClass); - } - - public function testAssertNotInstanceOfThrowsExceptionIfTypeDoesNotExist(): void - { - $this->expectException(Exception::class); - - $this->assertNotInstanceOf(ClassThatDoesNotExist::class, new stdClass); - } - - public function testAssertNotInstanceOf(): void - { - $this->assertNotInstanceOf(\Exception::class, new stdClass); - - $this->expectException(AssertionFailedError::class); - - $this->assertNotInstanceOf(stdClass::class, new stdClass); - } - - public function testAssertFileMatchesFormat(): void - { - $this->assertFileMatchesFormat("FOO\n", TEST_FILES_PATH . 'expectedFileFormat.txt'); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileMatchesFormat("BAR\n", TEST_FILES_PATH . 'expectedFileFormat.txt'); - } - - public function testAssertFileMatchesFormatFile(): void - { - $this->assertFileMatchesFormatFile( - TEST_FILES_PATH . 'expectedFileFormat.txt', - TEST_FILES_PATH . 'expectedFileFormat.txt', - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertFileMatchesFormatFile( - TEST_FILES_PATH . 'expectedFileFormat.txt', - TEST_FILES_PATH . 'actualFileFormat.txt', - ); - } - - public function testAssertStringMatchesFormatFile(): void - { - $this->assertStringMatchesFormatFile(TEST_FILES_PATH . 'expectedFileFormat.txt', "FOO\n"); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringMatchesFormatFile(TEST_FILES_PATH . 'expectedFileFormat.txt', "BAR\n"); - } - - #[IgnorePhpunitDeprecations] - public function testAssertStringNotMatchesFormatFile(): void - { - $this->assertStringNotMatchesFormatFile(TEST_FILES_PATH . 'expectedFileFormat.txt', "BAR\n"); - - $this->expectException(AssertionFailedError::class); - - $this->assertStringNotMatchesFormatFile(TEST_FILES_PATH . 'expectedFileFormat.txt', "FOO\n"); - } - - public function testStringsCanBeComparedForEqualityIgnoringCase(): void - { - $this->assertEqualsIgnoringCase('a', 'A'); - - $this->assertNotEqualsIgnoringCase('a', 'B'); - } - - public function testArraysOfStringsCanBeComparedForEqualityIgnoringCase(): void - { - $this->assertEqualsIgnoringCase(['a'], ['A']); - - $this->assertNotEqualsIgnoringCase(['a'], ['B']); - } - - public function testStringsCanBeComparedForEqualityWithDelta(): void - { - $this->assertEqualsWithDelta(2.3, 2.5, 0.5); - - $this->assertNotEqualsWithDelta(2.3, 3.5, 0.5); - } - - public function testArraysOfStringsCanBeComparedForEqualityWithDelta(): void - { - $this->assertEqualsWithDelta([2.3], [2.5], 0.5); - - $this->assertNotEqualsWithDelta([2.3], [3.5], 0.5); - } - - public function testArraysCanBeComparedForEqualityWithCanonicalization(): void - { - $this->assertEqualsCanonicalizing([3, 2, 1], [2, 3, 1]); - - $this->assertNotEqualsCanonicalizing([3, 2, 1], [2, 3, 4]); - } - - public function testArrayTypeCanBeAsserted(): void - { - $this->assertIsArray([]); - - try { - $this->assertIsArray(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testBoolTypeCanBeAsserted(): void - { - $this->assertIsBool(true); - - try { - $this->assertIsBool(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testFloatTypeCanBeAsserted(): void - { - $this->assertIsFloat(0.0); - - try { - $this->assertIsFloat(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testIntTypeCanBeAsserted(): void - { - $this->assertIsInt(1); - - try { - $this->assertIsInt(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNumericTypeCanBeAsserted(): void - { - $this->assertIsNumeric('1.0'); - - try { - $this->assertIsNumeric('abc'); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testObjectTypeCanBeAsserted(): void - { - $this->assertIsObject(new stdClass); - - try { - $this->assertIsObject(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testResourceTypeCanBeAsserted(): void - { - $this->assertIsResource(fopen(__FILE__, 'r')); - - try { - $this->assertIsResource(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testClosedResourceTypeCanBeAsserted(): void - { - $resource = fopen(__FILE__, 'r'); - fclose($resource); - - $this->assertIsClosedResource($resource); - $this->assertIsResource($resource); - - try { - $this->assertIsClosedResource(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testStringTypeCanBeAsserted(): void - { - $this->assertIsString(''); - - try { - $this->assertIsString(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testScalarTypeCanBeAsserted(): void - { - $this->assertIsScalar(true); - - try { - $this->assertIsScalar(new stdClass); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testCallableTypeCanBeAsserted(): void - { - $this->assertIsCallable(static function (): void - { - }); - - try { - $this->assertIsCallable(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testIterableTypeCanBeAsserted(): void - { - $this->assertIsIterable([]); - - try { - $this->assertIsIterable(null); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotArrayTypeCanBeAsserted(): void - { - $this->assertIsNotArray(null); - - try { - $this->assertIsNotArray([]); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotBoolTypeCanBeAsserted(): void - { - $this->assertIsNotBool(null); - - try { - $this->assertIsNotBool(true); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotFloatTypeCanBeAsserted(): void - { - $this->assertIsNotFloat(null); - - try { - $this->assertIsNotFloat(0.0); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotIntTypeCanBeAsserted(): void - { - $this->assertIsNotInt(null); - - try { - $this->assertIsNotInt(1); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotNumericTypeCanBeAsserted(): void - { - $this->assertIsNotNumeric('abc'); - - try { - $this->assertIsNotNumeric('1.0'); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotObjectTypeCanBeAsserted(): void - { - $this->assertIsNotObject(null); - - try { - $this->assertIsNotObject(new stdClass); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotResourceTypeCanBeAsserted(): void - { - $this->assertIsNotResource(null); - - try { - $this->assertIsNotResource(fopen(__FILE__, 'r')); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotClosedResourceTypeCanBeAsserted(): void - { - $this->assertIsNotClosedResource(null); - - $resource = fopen(__FILE__, 'r'); - fclose($resource); - - try { - $this->assertIsNotClosedResource($resource); - } catch (AssertionFailedError) { - return; - } - - try { - $this->assertIsNotResource($resource); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotScalarTypeCanBeAsserted(): void - { - $this->assertIsNotScalar(new stdClass); - - try { - $this->assertIsNotScalar(true); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotStringTypeCanBeAsserted(): void - { - $this->assertIsNotString(null); - - try { - $this->assertIsNotString(''); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotCallableTypeCanBeAsserted(): void - { - $this->assertIsNotCallable(null); - - try { - $this->assertIsNotCallable(static function (): void - { - }); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testNotIterableTypeCanBeAsserted(): void - { - $this->assertIsNotIterable(null); - - try { - $this->assertIsNotIterable([]); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testLogicalAnd(): void - { - $this->assertThat( - true, - $this->logicalAnd( - $this->isTrue(), - $this->isTrue(), - ), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertThat( - true, - $this->logicalAnd( - $this->isTrue(), - $this->isFalse(), - ), - ); - } - - public function testLogicalOr(): void - { - $this->assertThat( - true, - $this->logicalOr( - $this->isTrue(), - $this->isFalse(), - ), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertThat( - true, - $this->logicalOr( - $this->isFalse(), - $this->isFalse(), - ), - ); - } - - public function testLogicalXor(): void - { - $this->assertThat( - true, - $this->logicalXor( - $this->isTrue(), - $this->isFalse(), - ), - ); - - $this->expectException(AssertionFailedError::class); - - $this->assertThat( - true, - $this->logicalXor( - $this->isTrue(), - $this->isTrue(), - ), - ); - } - - public function testStringContainsStringCanBeAsserted(): void - { - $this->assertStringContainsString('bar', 'foobarbaz'); - - try { - $this->assertStringContainsString('barbara', 'foobarbaz'); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testStringNotContainsStringCanBeAsserted(): void - { - $this->assertStringNotContainsString('barbara', 'foobarbaz'); - - try { - $this->assertStringNotContainsString('bar', 'foobarbaz'); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testStringContainsStringCanBeAssertedIgnoringCase(): void - { - $this->assertStringContainsStringIgnoringCase('BAR', 'foobarbaz'); - - try { - $this->assertStringContainsStringIgnoringCase('BARBARA', 'foobarbaz'); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testStringNotContainsStringCanBeAssertedIgnoringCase(): void - { - $this->assertStringNotContainsStringIgnoringCase('BARBARA', 'foobarbaz'); - - try { - $this->assertStringNotContainsStringIgnoringCase('BAR', 'foobarbaz'); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testIterableContainsSameObjectCanBeAsserted(): void - { - $object = new stdClass; - $iterable = [$object]; - - $this->assertContains($object, $iterable); - - try { - $this->assertContains(new stdClass, $iterable); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testIterableNotContainsSameObjectCanBeAsserted(): void - { - $object = new stdClass; - $iterable = [$object]; - - $this->assertNotContains(new stdClass, $iterable); - - try { - $this->assertNotContains($object, $iterable); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testIterableContainsEqualObjectCanBeAsserted(): void - { - $a = new stdClass; - $a->foo = 'bar'; - - $b = new stdClass; - $b->foo = 'baz'; - - $this->assertContainsEquals($a, [$a]); - - try { - $this->assertContainsEquals($b, [$a]); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testIterableNotContainsEqualObjectCanBeAsserted(): void - { - $a = new stdClass; - $a->foo = 'bar'; - - $b = new stdClass; - $b->foo = 'baz'; - - $this->assertNotContainsEquals($b, [$a]); - - try { - $this->assertNotContainsEquals($a, [$a]); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testTwoObjectsCanBeAssertedToBeEqualUsingComparisonMethod(): void - { - $this->assertObjectEquals(new ValueObject(1), new ValueObject(1)); - - try { - $this->assertObjectEquals(new ValueObject(1), new ValueObject(2)); - } catch (AssertionFailedError) { - return; - } - - $this->fail(); - } - - public function testObjectHasPropertyCanBeAsserted(): void - { - $objectWithProperty = new stdClass; - $objectWithProperty->theProperty = 'value'; - - $this->assertObjectHasProperty('theProperty', $objectWithProperty); - - try { - $this->assertObjectHasProperty('doesNotExist', $objectWithProperty); - } catch (AssertionFailedError $e) { - return; - } - - $this->fail(); - } - - public function testObjectDoesNotHavePropertyCanBeAsserted(): void - { - $objectWithProperty = new stdClass; - $objectWithProperty->theProperty = 'value'; - - $this->assertObjectNotHasProperty('doesNotExist', $objectWithProperty); - - try { - $this->assertObjectNotHasProperty('theProperty', $objectWithProperty); - } catch (AssertionFailedError $e) { - return; - } - - $this->fail(); - } - - protected static function sameValues(): array - { - $object = new SampleClass(4, 8, 15); - $file = TEST_FILES_PATH . 'foo.xml'; - $resource = fopen($file, 'r'); - - return [ - // null - [null, null], - // strings - ['a', 'a'], - // integers - [0, 0], - // floats - [1.0, 1.0], - [2.3, 2.3], - [1 / 3, 1 / 3], - [1 - 2 / 3, 1 - 2 / 3], - [5.5E+123, 5.5E+123], - [5.5E-123, 5.5E-123], - [log(0), log(0)], - [INF, INF], - [-INF, -INF], - // arrays - [[], []], - [[0 => 1], [0 => 1]], - [[0 => null], [0 => null]], - [['a', 'b' => [1, 2]], ['a', 'b' => [1, 2]]], - // objects - [$object, $object], - // resources - [$resource, $resource], - ]; - } - - protected static function notEqualValues(): array - { - // cyclic dependencies - $book1 = new Book; - $book1->author = new Author('Terry Pratchett'); - $book1->author->books[] = $book1; - $book2 = new Book; - $book2->author = new Author('Terry Pratch'); - $book2->author->books[] = $book2; - - $book3 = new Book; - $book3->author = 'Terry Pratchett'; - $book4 = new stdClass; - $book4->author = 'Terry Pratchett'; - - $object1 = new SampleClass(4, 8, 15); - $object2 = new SampleClass(16, 23, 42); - $object3 = new SampleClass(4, 8, 15); - $storage1 = new SplObjectStorage; - $storage1->attach($object1); - $storage2 = new SplObjectStorage; - $storage2->attach($object3); // same content, different object - - $file = TEST_FILES_PATH . 'foo.xml'; - - return [ - // strings - ['a', 'b'], - ['a', 'A'], - // https://github.com/sebastianbergmann/phpunit/issues/1023 - ['9E6666666', '9E7777777'], - // integers - [1, 2], - [2, 1], - // floats - [2.3, 4.2], - [2.3, 4.2, 0.5], - [[2.3], [4.2], 0.5], - [[[2.3]], [[4.2]], 0.5], - [new Struct(2.3), new Struct(4.2), 0.5], - [[new Struct(2.3)], [new Struct(4.2)], 0.5], - [1 / 3, 1 - 2 / 3], - [1 / 3, '0.33333333333333337'], - [1 - 2 / 3, '3333333333333333'], - [5.5E+123, 5.6E+123], - [5.5E-123, 5.6E-123], - [5.5E+123, 5.5E-123], - // NAN - [NAN, NAN], - // arrays - [[], [0 => 1]], - [[0 => 1], []], - [[0 => null], []], - [[0 => 1, 1 => 2], [0 => 1, 1 => 3]], - [['a', 'b' => [1, 2]], ['a', 'b' => [2, 1]]], - // objects - [new SampleClass(4, 8, 15), new SampleClass(16, 23, 42)], - [$object1, $object2], - [$book1, $book2], - [$book3, $book4], // same content, different class - // resources - [fopen($file, 'r'), fopen($file, 'r')], - // SplObjectStorage - [$storage1, $storage2], - // DOMDocument - [ - (new XmlLoader)->load(''), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load(''), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load(' bar '), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load(''), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load(' bar '), - (new XmlLoader)->load(' bir '), - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/New_York')), - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/New_York')), - 3500, - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 05:13:35', new DateTimeZone('America/New_York')), - 3500, - ], - [ - new DateTime('2013-03-29', new DateTimeZone('America/New_York')), - new DateTime('2013-03-30', new DateTimeZone('America/New_York')), - ], - [ - new DateTime('2013-03-29', new DateTimeZone('America/New_York')), - new DateTime('2013-03-30', new DateTimeZone('America/New_York')), - 43200, - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/Chicago')), - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/Chicago')), - 3500, - ], - [ - new DateTime('2013-03-30', new DateTimeZone('America/New_York')), - new DateTime('2013-03-30', new DateTimeZone('America/Chicago')), - ], - [ - new DateTime('2013-03-29T05:13:35-0600'), - new DateTime('2013-03-29T04:13:35-0600'), - ], - [ - new DateTime('2013-03-29T05:13:35-0600'), - new DateTime('2013-03-29T05:13:35-0500'), - ], - // Exception - // array(new Exception('Exception 1'), new Exception('Exception 2')), - // different types - [new SampleClass(4, 8, 15), false], - [false, new SampleClass(4, 8, 15)], - [[0 => 1, 1 => 2], false], - [false, [0 => 1, 1 => 2]], - [[], new stdClass], - [new stdClass, []], - // PHP: 0 == 'Foobar' => true! - // We want these values to differ - [0, 'Foobar'], - ['Foobar', 0], - [3, acos(8)], - [acos(8), 3], - ]; - } - - protected static function equalValues(): array - { - // cyclic dependencies - $book1 = new Book; - $book1->author = new Author('Terry Pratchett'); - $book1->author->books[] = $book1; - $book2 = new Book; - $book2->author = new Author('Terry Pratchett'); - $book2->author->books[] = $book2; - - $object1 = new SampleClass(4, 8, 15); - $object2 = new SampleClass(4, 8, 15); - $storage1 = new SplObjectStorage; - $storage1->attach($object1); - $storage2 = new SplObjectStorage; - $storage2->attach($object1); - - return [ - // arrays - [['a' => 1, 'b' => 2], ['b' => 2, 'a' => 1]], - [[1], ['1']], - // objects - [$object1, $object2], - [$book1, $book2], - // SplObjectStorage - [$storage1, $storage2], - // DOMDocument - [ - (new XmlLoader)->load(''), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load(''), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load(''), - (new XmlLoader)->load(''), - ], - [ - (new XmlLoader)->load("\n \n"), - (new XmlLoader)->load(''), - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - ], - [ - new DateTime('2013-03-29', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29', new DateTimeZone('America/New_York')), - ], - [ - new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/Chicago')), - ], - [ - new DateTime('2013-03-30', new DateTimeZone('America/New_York')), - new DateTime('2013-03-29 23:00:00', new DateTimeZone('America/Chicago')), - ], - [ - new DateTime('@1364616000'), - new DateTime('2013-03-29 23:00:00', new DateTimeZone('America/Chicago')), - ], - [ - new DateTime('2013-03-29T05:13:35-0500'), - new DateTime('2013-03-29T04:13:35-0600'), - ], - // Exception - // array(new Exception('Exception 1'), new Exception('Exception 1')), - // mixed types - [0, '0'], - ['0', 0], - [2.3, '2.3'], - ['2.3', 2.3], - [1, 1.0], - [1.0, '1'], - [1 / 3, '0.3333333333333333'], - [1 - 2 / 3, '0.33333333333333337'], - [5.5E+123, '5.5E+123'], - [5.5E-123, '5.5E-123'], - ['string representation', new ClassWithToString], - [new ClassWithToString, 'string representation'], - ]; - } -} diff --git a/tests/unit/Framework/Constraint/CallbackTest.php b/tests/unit/Framework/Constraint/CallbackTest.php index 1d39fc3722d..75eaac4b940 100644 --- a/tests/unit/Framework/Constraint/CallbackTest.php +++ b/tests/unit/Framework/Constraint/CallbackTest.php @@ -45,6 +45,30 @@ public function testIsCountable(): void $this->assertCount(1, $this->acceptingCallbackConstraint()); } + public function testIsVariadic(): void + { + $class = new class + { + public function __invoke(string ...$values): void + { + } + }; + + $this->assertTrue((new Callback($class))->isVariadic()); + } + + public function testIsNotVariadic(): void + { + $class = new class + { + public function __invoke(string $value): void + { + } + }; + + $this->assertFalse((new Callback($class))->isVariadic()); + } + private function acceptingCallbackConstraint(): Callback { return new Callback(static fn (): bool => true); diff --git a/tests/unit/Framework/Constraint/Cardinality/CountTest.php b/tests/unit/Framework/Constraint/Cardinality/CountTest.php index ce6fcd8814a..5363a564fdd 100644 --- a/tests/unit/Framework/Constraint/Cardinality/CountTest.php +++ b/tests/unit/Framework/Constraint/Cardinality/CountTest.php @@ -16,6 +16,7 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Exception; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\GeneratorNotSupportedException; use PHPUnit\Framework\TestCase; @@ -205,7 +206,7 @@ public function testWrapsExceptionFromIteratorAggregate(): void { $constraint = new Count(0); - $this->expectException(\PHPUnit\Framework\Exception::class); + $this->expectException(Exception::class); $constraint->evaluate(new ExceptionThrowingIteratorAggregate); } diff --git a/tests/unit/Framework/Constraint/Equality/IsEqualCanonicalizingTest.php b/tests/unit/Framework/Constraint/Equality/IsEqualCanonicalizingTest.php index 3d99b917c3d..72ba288bb35 100644 --- a/tests/unit/Framework/Constraint/Equality/IsEqualCanonicalizingTest.php +++ b/tests/unit/Framework/Constraint/Equality/IsEqualCanonicalizingTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Framework\Constraint; +use const PHP_EOL; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; diff --git a/tests/unit/Framework/Constraint/Equality/IsEqualIgnoringCaseTest.php b/tests/unit/Framework/Constraint/Equality/IsEqualIgnoringCaseTest.php index 92297475369..bb09d49f9c5 100644 --- a/tests/unit/Framework/Constraint/Equality/IsEqualIgnoringCaseTest.php +++ b/tests/unit/Framework/Constraint/Equality/IsEqualIgnoringCaseTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Framework\Constraint; +use const PHP_EOL; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; diff --git a/tests/unit/Framework/Constraint/Equality/IsEqualTest.php b/tests/unit/Framework/Constraint/Equality/IsEqualTest.php index 46a124fcb23..82f5b8f20d9 100644 --- a/tests/unit/Framework/Constraint/Equality/IsEqualTest.php +++ b/tests/unit/Framework/Constraint/Equality/IsEqualTest.php @@ -9,11 +9,15 @@ */ namespace PHPUnit\Framework\Constraint; +use const PHP_EOL; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\EnumerationEquals\Example; +use PHPUnit\TestFixture\EnumerationEquals\ExampleInt; +use PHPUnit\TestFixture\EnumerationEquals\ExampleString; use stdClass; #[CoversClass(IsEqual::class)] @@ -140,6 +144,33 @@ public static function provider(): array 'string' . PHP_EOL . 'string', ], + [ + true, + 'is equal to PHPUnit\TestFixture\EnumerationEquals\Example Enum %s (Foo)', + '', + '', + Example::Foo, + Example::Foo, + ], + + [ + true, + 'is equal to PHPUnit\TestFixture\EnumerationEquals\ExampleString Enum %s (Foo, \'foo\')', + '', + '', + ExampleString::Foo, + ExampleString::Foo, + ], + + [ + true, + 'is equal to PHPUnit\TestFixture\EnumerationEquals\ExampleInt Enum %s (Foo, 0)', + '', + '', + ExampleInt::Foo, + ExampleInt::Foo, + ], + [ false, <<<'EOT' @@ -292,6 +323,33 @@ public static function provider(): array "string\nstring", "another-string\nanother-string", ], + + [ + false, + 'is equal to PHPUnit\TestFixture\EnumerationEquals\Example Enum %s (Foo)', + 'Failed asserting that two values of enumeration PHPUnit\TestFixture\EnumerationEquals\Example are equal, Bar does not match expected Foo.', + 'Failed asserting that two values of enumeration PHPUnit\TestFixture\EnumerationEquals\Example are equal, Bar does not match expected Foo.', + Example::Foo, + Example::Bar, + ], + + [ + false, + 'is equal to PHPUnit\TestFixture\EnumerationEquals\ExampleString Enum %s (Foo, \'foo\')', + 'Failed asserting that two values of enumeration PHPUnit\TestFixture\EnumerationEquals\ExampleString are equal, Bar does not match expected Foo.', + 'Failed asserting that two values of enumeration PHPUnit\TestFixture\EnumerationEquals\ExampleString are equal, Bar does not match expected Foo.', + ExampleString::Foo, + ExampleString::Bar, + ], + + [ + false, + 'is equal to PHPUnit\TestFixture\EnumerationEquals\ExampleInt Enum %s (Foo, 0)', + 'Failed asserting that two values of enumeration PHPUnit\TestFixture\EnumerationEquals\ExampleInt are equal, Bar does not match expected Foo.', + 'Failed asserting that two values of enumeration PHPUnit\TestFixture\EnumerationEquals\ExampleInt are equal, Bar does not match expected Foo.', + ExampleInt::Foo, + ExampleInt::Bar, + ], ]; } diff --git a/tests/unit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpressionTest.php b/tests/unit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpressionTest.php index 082124ff53a..c3c527d0d79 100644 --- a/tests/unit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpressionTest.php +++ b/tests/unit/Framework/Constraint/Exception/ExceptionMessageMatchesRegularExpressionTest.php @@ -12,6 +12,7 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Exception; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; @@ -60,7 +61,7 @@ public function testRejectsInvalidRegularExpression(): void { $constraint = new ExceptionMessageMatchesRegularExpression('invalid regular expression'); - $this->expectException(\PHPUnit\Framework\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Invalid expected exception message regular expression given: invalid regular expression'); $constraint->evaluate('abcd'); diff --git a/tests/unit/Framework/Constraint/JsonMatchesTest.php b/tests/unit/Framework/Constraint/JsonMatchesTest.php index 3b34ce91965..adbab49aead 100644 --- a/tests/unit/Framework/Constraint/JsonMatchesTest.php +++ b/tests/unit/Framework/Constraint/JsonMatchesTest.php @@ -40,6 +40,14 @@ public static function provider(): array '{"first":1, "second":"2"}', ], + 'object fields with numeric keys are unordered' => [ + true, + '', + '', + '{"0":null,"a":{},"b":[],"c":"1","d":1,"e":-1,"f":[1,2],"g":[2,1],"h":{"0":"0","1":"1","2":"2"}}', + '{"a":{},"d":1,"b":[],"e":-1,"0":null,"c":"1","f":[1,2],"h":{"2":"2","1":"1","0":"0"},"g":[2,1]}', + ], + 'child object fields are unordered' => [ true, '', @@ -198,6 +206,139 @@ public static function provider(): array '["first", "second"]', ], + 'objects with numeric keys are not arrays' => [ + false, + 'Failed asserting that \'[{}]\' matches JSON string "{"0":{}}".', + <<<'EOT' +Failed asserting that two json values are equal. +--- Expected ++++ Actual +@@ @@ +-{ +- "0": {} +-} ++[ ++ {} ++] + +EOT, + '{"0":{}}', + '[{}]', + ], + + 'child array elements are ordered' => [ + false, + 'Failed asserting that \'{"a":{},"d":1,"b":[],"e":-1,"0":null,"c":"1","f":[2,1],"h":{"2":"2","1":"1","0":"0"},"g":[2,1]}\' matches JSON string "{"0":null,"a":{},"b":[],"c":"1","d":1,"e":-1,"f":[1,2],"g":[2,1],"h":{"0":"0","1":"1","2":"2"}}".', + <<<'EOT' +Failed asserting that two json values are equal. +--- Expected ++++ Actual +@@ @@ + "d": 1, + "e": -1, + "f": [ +- 1, +- 2 ++ 2, ++ 1 + ], + "g": [ + 2, + +EOT, + '{"0":null,"a":{},"b":[],"c":"1","d":1,"e":-1,"f":[1,2],"g":[2,1],"h":{"0":"0","1":"1","2":"2"}}', + '{"a":{},"d":1,"b":[],"e":-1,"0":null,"c":"1","f":[2,1],"h":{"2":"2","1":"1","0":"0"},"g":[2,1]}', + ], + + 'child object with numeric fields stay as object' => [ + false, + 'Failed asserting that \'{"a":{},"d":1,"b":[],"e":-1,"0":null,"c":"1","f":[1,2],"h":["0","1","2"],"g":[2,1]}\' matches JSON string "{"0":null,"a":{},"b":[],"c":"1","d":1,"e":-1,"f":[1,2],"g":[2,1],"h":{"0":"0","1":"1","2":"2"}}".', + <<<'EOT' +Failed asserting that two json values are equal. +--- Expected ++++ Actual +@@ @@ + 2, + 1 + ], +- "h": { +- "0": "0", +- "1": "1", +- "2": "2" +- } ++ "h": [ ++ "0", ++ "1", ++ "2" ++ ] + } + +EOT, + '{"0":null,"a":{},"b":[],"c":"1","d":1,"e":-1,"f":[1,2],"g":[2,1],"h":{"0":"0","1":"1","2":"2"}}', + '{"a":{},"d":1,"b":[],"e":-1,"0":null,"c":"1","f":[1,2],"h":["0","1","2"],"g":[2,1]}', + ], + + 'nested arrays are ordered' => [ + false, + 'Failed asserting that \'[{"1":"1","0":"0"},{"2":"2","3":"3"}]\' matches JSON string "[[1,0],[2,3]]".', + <<<'EOT' +Failed asserting that two json values are equal. +--- Expected ++++ Actual +@@ @@ + [ +- [ +- 1, +- 0 +- ], +- [ +- 2, +- 3 +- ] ++ { ++ "0": "0", ++ "1": "1" ++ }, ++ { ++ "2": "2", ++ "3": "3" ++ } + ] + +EOT, + '[[1,0],[2,3]]', + '[{"1":"1","0":"0"},{"2":"2","3":"3"}]', + ], + + 'child objects in arrays stay in order' => [ + false, + 'Failed asserting that \'[{"2":"2","3":"3"},{"1":"1","0":"0"}]\' matches JSON string "[{"0":"0","1":"1"},{"2":"2","3":"3"}]".', + <<<'EOT' +Failed asserting that two json values are equal. +--- Expected ++++ Actual +@@ @@ + [ + { ++ "2": "2", ++ "3": "3" ++ }, ++ { + "0": "0", + "1": "1" +- }, +- { +- "2": "2", +- "3": "3" + } + ] + +EOT, + + '[{"0":"0","1":"1"},{"2":"2","3":"3"}]', + '[{"2":"2","3":"3"},{"1":"1","0":"0"}]', + ], + 'objects are not arrays' => [ false, 'Failed asserting that \'{}\' matches JSON string "[]".', @@ -213,6 +354,30 @@ public static function provider(): array '[]', '{}', ], + + 'arrays are not objects' => [ + false, + 'Failed asserting that \'{}\' matches JSON string "[]".', + <<<'EOT' +Failed asserting that two json values are equal. +--- Expected ++++ Actual +@@ @@ +-[] ++{} + +EOT, + '[]', + '{}', + ], + + 'objects in arrays are unordered' => [ + true, + '', + '', + '[{"0":"0","1":"1"},{"2":"2","3":"3"}]', + '[{"1":"1","0":"0"},{"2":"2","3":"3"}]', + ], ]; } diff --git a/tests/unit/Framework/Constraint/Operator/LogicalAndTest.php b/tests/unit/Framework/Constraint/Operator/LogicalAndTest.php index cd44d0df70c..51d2421017c 100644 --- a/tests/unit/Framework/Constraint/Operator/LogicalAndTest.php +++ b/tests/unit/Framework/Constraint/Operator/LogicalAndTest.php @@ -27,10 +27,10 @@ public static function provider(): array return [ [ true, - 'is of type boolean and is true', + 'is of type bool and is true', '', self::logicalAnd( - self::isType('boolean'), + self::isBool(), self::isTrue(), ), true, @@ -38,10 +38,10 @@ public static function provider(): array [ true, - 'is of type boolean and is equal to true', + 'is of type bool and is equal to true', '', self::logicalAnd( - self::isType('boolean'), + self::isBool(), true, ), true, @@ -49,10 +49,10 @@ public static function provider(): array [ true, - 'is of type boolean and ( is true or is false )', + 'is of type bool and ( is true or is false )', '', self::logicalAnd( - self::isType('boolean'), + self::isBool(), self::logicalOr( self::isTrue(), self::isFalse(), @@ -63,10 +63,10 @@ public static function provider(): array [ false, - 'is of type boolean and is true', - 'Failed asserting that false is of type boolean and is true.', + 'is of type bool and is true', + 'Failed asserting that false is of type bool and is true.', self::logicalAnd( - self::isType('boolean'), + self::isBool(), self::isTrue(), ), false, @@ -74,10 +74,10 @@ public static function provider(): array [ false, - 'is of type boolean and ( is true or is false )', - 'Failed asserting that \'string\' is of type boolean and ( is true or is false ).', + 'is of type bool and ( is true or is false )', + 'Failed asserting that \'string\' is of type bool and ( is true or is false ).', self::logicalAnd( - self::isType('boolean'), + self::isBool(), self::logicalOr( self::isTrue(), self::isFalse(), @@ -112,7 +112,7 @@ public function testCanBeRepresentedAsString(bool $result, string $constraintAsS public function testIsCountable(): void { $constraint = $this->logicalAnd( - $this->isType('bool'), + $this->isBool(), true, ); diff --git a/tests/unit/Framework/Constraint/Operator/LogicalNotTest.php b/tests/unit/Framework/Constraint/Operator/LogicalNotTest.php index fee82e4d4b3..882f81ed799 100644 --- a/tests/unit/Framework/Constraint/Operator/LogicalNotTest.php +++ b/tests/unit/Framework/Constraint/Operator/LogicalNotTest.php @@ -9,9 +9,12 @@ */ namespace PHPUnit\Framework\Constraint; +use PHPUnit\Framework\Assert; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; @@ -127,4 +130,16 @@ public function testIsCountable(): void $this->assertCount(2, $constraint); } + + #[TestDox('LogicalNot(IsEqual(\'test contains something\')) is handled correctly')] + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/5516')] + public function testForNotEqualsWithStringThatContainsContains(): void + { + $constraint = new LogicalNot(new IsEqual('test contains something')); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage("Failed asserting that 'test contains something' is not equal to 'test contains something'."); + + Assert::assertThat('test contains something', $constraint); + } } diff --git a/tests/unit/Framework/Constraint/Operator/LogicalOrTest.php b/tests/unit/Framework/Constraint/Operator/LogicalOrTest.php index 260f4a890fd..48bf53166ac 100644 --- a/tests/unit/Framework/Constraint/Operator/LogicalOrTest.php +++ b/tests/unit/Framework/Constraint/Operator/LogicalOrTest.php @@ -27,10 +27,10 @@ public static function provider(): array return [ [ true, - 'is of type boolean or is true', + 'is of type bool or is true', '', self::logicalOr( - self::isType('boolean'), + self::isBool(), self::isTrue(), ), true, @@ -38,10 +38,10 @@ public static function provider(): array [ true, - 'is of type boolean or is equal to true', + 'is of type bool or is equal to true', '', self::logicalOr( - self::isType('boolean'), + self::isBool(), true, ), true, @@ -49,12 +49,12 @@ public static function provider(): array [ true, - 'is true or is of type boolean and is false', + 'is true or is of type bool and is false', '', self::logicalOr( self::isTrue(), self::logicalAnd( - self::isType('boolean'), + self::isBool(), self::isFalse(), ), ), @@ -63,11 +63,11 @@ public static function provider(): array [ false, - 'is of type boolean or is of type string', - 'Failed asserting that 0 is of type boolean or is of type string.', + 'is of type bool or is of type string', + 'Failed asserting that 0 is of type bool or is of type string.', self::logicalOr( - self::isType('boolean'), - self::isType('string'), + self::isBool(), + self::isString(), ), 0, ], @@ -98,7 +98,7 @@ public function testCanBeRepresentedAsString(bool $result, string $constraintAsS public function testIsCountable(): void { $constraint = $this->logicalOr( - $this->isType('bool'), + $this->isBool(), true, ); diff --git a/tests/unit/Framework/Constraint/Operator/LogicalXorTest.php b/tests/unit/Framework/Constraint/Operator/LogicalXorTest.php index abf3ab03083..f9fa2dd65f6 100644 --- a/tests/unit/Framework/Constraint/Operator/LogicalXorTest.php +++ b/tests/unit/Framework/Constraint/Operator/LogicalXorTest.php @@ -52,7 +52,7 @@ public static function provider(): array 'is of type bool xor is true', '', self::logicalXor( - self::isType('bool'), + self::isBool(), self::isTrue(), ), false, @@ -63,7 +63,7 @@ public static function provider(): array 'is of type bool xor is true', 'Failed asserting that true is of type bool xor is true.', self::logicalXor( - self::isType('bool'), + self::isBool(), self::isTrue(), ), true, @@ -75,11 +75,11 @@ public static function provider(): array 'Failed asserting that true is of type bool and is true xor is of type bool and is true.', self::logicalXor( self::logicalAnd( - self::isType('bool'), + self::isBool(), self::isTrue(), ), self::logicalAnd( - self::isType('bool'), + self::isBool(), self::isTrue(), ), ), @@ -112,7 +112,7 @@ public function testCanBeRepresentedAsString(bool $result, string $constraintAsS public function testIsCountable(): void { $constraint = $this->logicalXor( - $this->isType('bool'), + $this->isBool(), true, ); diff --git a/tests/unit/Framework/Constraint/String/StringMatchesFormatDescriptionTest.php b/tests/unit/Framework/Constraint/String/StringMatchesFormatDescriptionTest.php index c8fba41bad8..c2891748726 100644 --- a/tests/unit/Framework/Constraint/String/StringMatchesFormatDescriptionTest.php +++ b/tests/unit/Framework/Constraint/String/StringMatchesFormatDescriptionTest.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\Framework\Constraint; +use const DIRECTORY_SEPARATOR; +use const PHP_EOL; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\ExpectationFailedException; @@ -139,12 +141,12 @@ public function testConstraintStringMatchesFloat(): void $this->assertFalse($constraint->evaluate('**', '', true)); $this->assertFalse($constraint->evaluate('***', '', true)); $this->assertFalse($constraint->evaluate('*a*', '', true)); + $this->assertFalse($constraint->evaluate('*1.*', '', true)); $this->assertTrue($constraint->evaluate('*1.0*', '', true)); $this->assertTrue($constraint->evaluate('*0*', '', true)); $this->assertTrue($constraint->evaluate('*12*', '', true)); $this->assertTrue($constraint->evaluate('*.1*', '', true)); - $this->assertTrue($constraint->evaluate('*1.*', '', true)); $this->assertTrue($constraint->evaluate('*2e3*', '', true)); $this->assertTrue($constraint->evaluate('*-2.34e-56*', '', true)); $this->assertTrue($constraint->evaluate('*+2.34e+56*', '', true)); @@ -214,6 +216,13 @@ public function testConstraintStringMatchesNewline(): void $this->assertTrue($constraint->evaluate("\r\n", '', true)); } + public function testConstraintStringMatchesAnythingMultiline(): void + { + $constraint = new StringMatchesFormatDescription("*\n%a\nbar\nbaz"); + + $this->assertFalse($constraint->evaluate("*\n*", '', true)); + } + public function testFailureMessageWithNewlines(): void { $constraint = new StringMatchesFormatDescription("%c\nfoo\n%c"); @@ -236,6 +245,93 @@ public function testFailureMessageWithNewlines(): void $constraint->evaluate("*\nbar\n*"); } + public function testFailureMessageWithNewlinesAndAnythingMatcher(): void + { + $constraint = new StringMatchesFormatDescription("%a\nfoo\n%s\nbar"); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage( + <<<'EOD' +Failed asserting that string matches format description. +--- Expected ++++ Actual +@@ @@ + * + foo + * +-bar ++mismatch + +EOD + ); + + $constraint->evaluate("*\nfoo\n*\nmismatch"); + } + + public function testFailureMessageWithNewlinesAndAnythingMatcherMultilineMatches(): void + { + $constraint = new StringMatchesFormatDescription( + <<<'EOD' +## before first A +%A +## after first A +* +## before second A +%A +## after second A +* +Foo: %s + +EOD + ); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage( + <<<'EOD' +Failed asserting that string matches format description. +--- Expected ++++ Actual +@@ @@ + ## before first A + some multiline ++text for ++A to match + ## after first A + * + ## before second A ++more multiline text ++for A to match ++## after second A + * +-## after second A ++Foo: s match + * +-Foo: %s ++Additional Text that is not matched + +EOD + ); + + $constraint->evaluate( + <<<'EOD' +## before first A +some multiline +text for +A to match +## after first A +* +## before second A +more multiline text +for A to match +## after second A +* +Foo: s match +* +Additional Text that is not matched +EOD + ); + } + public function testCanBeRepresentedAsString(): void { $this->assertSame( diff --git a/tests/unit/Framework/Constraint/Traversable/IsListTest.php b/tests/unit/Framework/Constraint/Traversable/IsListTest.php index 35e432c80d0..1e734aee999 100644 --- a/tests/unit/Framework/Constraint/Traversable/IsListTest.php +++ b/tests/unit/Framework/Constraint/Traversable/IsListTest.php @@ -46,7 +46,7 @@ public static function provider(): array [ false, - 'Failed asserting that an object is a list.', + 'Failed asserting that an instance of class stdClass is a list.', new stdClass, ], diff --git a/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php b/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php index c6f66a34771..0d272f2f4ce 100644 --- a/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php +++ b/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\NativeType; use PHPUnit\Framework\TestCase; use stdClass; @@ -21,45 +22,78 @@ #[Small] final class TraversableContainsOnlyTest extends TestCase { - public static function provider(): array + public static function nativeTypeProvider(): array { return [ [ true, '', - 'integer', - true, + NativeType::Int, [0, 1, 2], ], + [ + false, + <<<'EOT' +Failed asserting that Array &0 [ + 0 => 0, + 1 => '1', + 2 => 2, +] contains only values of type "int". +EOT, + NativeType::Int, + [0, '1', 2], + ], + ]; + } + + public static function classOrInterfaceProvider(): array + { + return [ [ true, '', stdClass::class, - false, - [new stdClass, new stdClass, new stdClass], + [new stdClass, new stdClass], ], [ false, <<<'EOT' Failed asserting that Array &0 [ - 0 => 0, - 1 => '1', - 2 => 2, -] contains only values of type "integer". + 0 => null, +] contains only values of type "stdClass". EOT, - 'integer', - true, - [0, '1', 2], + stdClass::class, + [null], ], ]; } - #[DataProvider('provider')] - public function testCanBeEvaluated(bool $result, string $failureDescription, string $expected, bool $isNativeType, mixed $actual): void + #[DataProvider('nativeTypeProvider')] + public function testCanBeEvaluatedForNativeType(bool $result, string $failureDescription, NativeType $expected, mixed $actual): void + { + $constraint = TraversableContainsOnly::forNativeType($expected); + + $this->assertSame($result, $constraint->evaluate($actual, returnResult: true)); + + if ($result) { + return; + } + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage($failureDescription); + + $constraint->evaluate($actual); + } + + /** + * @param class-string $expected + */ + #[DataProvider('classOrInterfaceProvider')] + public function testCanBeEvaluatedForClassOrInterface(bool $result, string $failureDescription, string $expected, mixed $actual): void { - $constraint = new TraversableContainsOnly($expected, $isNativeType); + $constraint = TraversableContainsOnly::forClassOrInterface($expected); $this->assertSame($result, $constraint->evaluate($actual, returnResult: true)); @@ -73,14 +107,18 @@ public function testCanBeEvaluated(bool $result, string $failureDescription, str $constraint->evaluate($actual); } - public function testCanBeRepresentedAsString(): void + public function testCanBeRepresentedAsStringForNativeType(): void + { + $this->assertSame('contains only values of type "int"', TraversableContainsOnly::forNativeType(NativeType::Int)->toString()); + } + + public function testCanBeRepresentedAsStringForClassOrInterface(): void { - $this->assertSame('contains only values of type "integer"', (new TraversableContainsOnly('integer'))->toString()); - $this->assertSame('contains only values of type "stdClass"', (new TraversableContainsOnly(stdClass::class, false))->toString()); + $this->assertSame('contains only values of type "stdClass"', TraversableContainsOnly::forClassOrInterface(stdClass::class)->toString()); } public function testIsCountable(): void { - $this->assertCount(1, (new TraversableContainsOnly(stdClass::class, false))); + $this->assertCount(1, TraversableContainsOnly::forNativeType(NativeType::Int)); } } diff --git a/tests/unit/Framework/Constraint/Type/IsInstanceOfTest.php b/tests/unit/Framework/Constraint/Type/IsInstanceOfTest.php index 98b93a7e094..2b98f036127 100644 --- a/tests/unit/Framework/Constraint/Type/IsInstanceOfTest.php +++ b/tests/unit/Framework/Constraint/Type/IsInstanceOfTest.php @@ -37,14 +37,22 @@ public static function provider(): array [ false, - 'Failed asserting that an object is an instance of class stdClass.', + 'Failed asserting that an instance of anonymous class created at', + stdClass::class, + new class + {}, + ], + + [ + false, + 'Failed asserting that an instance of class Exception is an instance of class stdClass.', stdClass::class, new Exception, ], [ false, - 'Failed asserting that an object is an instance of interface Throwable.', + 'Failed asserting that an instance of class stdClass is an instance of interface Throwable.', Throwable::class, new stdClass, ], diff --git a/tests/unit/Framework/Constraint/Type/IsTypeTest.php b/tests/unit/Framework/Constraint/Type/IsTypeTest.php index 92540bddffb..01c3ca1c1a1 100644 --- a/tests/unit/Framework/Constraint/Type/IsTypeTest.php +++ b/tests/unit/Framework/Constraint/Type/IsTypeTest.php @@ -15,13 +15,12 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\NativeType; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\UnknownTypeException; use stdClass; #[CoversClass(IsType::class)] #[CoversClass(Constraint::class)] -#[CoversClass(UnknownTypeException::class)] #[Small] final class IsTypeTest extends TestCase { @@ -31,126 +30,98 @@ public static function provider(): array [ true, '', - 'numeric', + NativeType::Numeric, 0, ], [ true, '', - 'int', + NativeType::Int, 0, ], [ true, '', - 'integer', - 0, - ], - - [ - true, - '', - 'double', + NativeType::Float, 0.0, ], [ true, '', - 'float', - 0.0, - ], - - [ - true, - '', - 'real', - 0.0, - ], - - [ - true, - '', - 'string', + NativeType::String, 'string', ], [ true, '', - 'boolean', - false, - ], - - [ - true, - '', - 'bool', + NativeType::Bool, false, ], [ true, '', - 'null', + NativeType::Null, null, ], [ true, '', - 'array', + NativeType::Array, [], ], [ true, '', - 'object', + NativeType::Object, new stdClass, ], [ true, '', - 'resource', + NativeType::Resource, fopen(__FILE__, 'r'), ], [ true, '', - 'resource (closed)', + NativeType::ClosedResource, self::closedResource(), ], [ true, '', - 'scalar', + NativeType::Scalar, 0, ], [ true, '', - 'callable', + NativeType::Callable, static fn () => true, ], [ true, '', - 'iterable', + NativeType::Iterable, [], ], ]; } #[DataProvider('provider')] - public function testCanBeEvaluated(bool $result, string $failureDescription, string $expected, mixed $actual): void + public function testCanBeEvaluated(bool $result, string $failureDescription, NativeType $expected, mixed $actual): void { $constraint = new IsType($expected); @@ -168,19 +139,12 @@ public function testCanBeEvaluated(bool $result, string $failureDescription, str public function testCanBeRepresentedAsString(): void { - $this->assertSame('is of type array', (new IsType('array'))->toString()); + $this->assertSame('is of type array', (new IsType(NativeType::Array))->toString()); } public function testIsCountable(): void { - $this->assertCount(1, new IsType('array')); - } - - public function testRejectsUnknownTypes(): void - { - $this->expectException(UnknownTypeException::class); - - new IsType('does-not-exist'); + $this->assertCount(1, new IsType(NativeType::Array)); } private static function closedResource() diff --git a/tests/unit/Framework/ExecutionOrderDependencyTest.php b/tests/unit/Framework/ExecutionOrderDependencyTest.php index 947a5172d7f..95932d172ba 100644 --- a/tests/unit/Framework/ExecutionOrderDependencyTest.php +++ b/tests/unit/Framework/ExecutionOrderDependencyTest.php @@ -112,4 +112,13 @@ public function testMergeHandlesEmptyDependencyLists(): void 'Right side of merge could be empty', ); } + + public function testEmptyClassOrCallable(): void + { + $empty = new ExecutionOrderDependency(''); + $this->assertFalse($empty->shallowClone()); + $this->assertFalse($empty->deepClone()); + $this->assertFalse($empty->targetIsClass()); + $this->assertSame('', $empty->getTargetClassName()); + } } diff --git a/tests/unit/Framework/MockObject/Creation/CreateConfiguredMockTest.php b/tests/unit/Framework/MockObject/Creation/CreateConfiguredMockTest.php index bca7d0d13f6..51ba60a16ce 100644 --- a/tests/unit/Framework/MockObject/Creation/CreateConfiguredMockTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreateConfiguredMockTest.php @@ -24,7 +24,7 @@ final class CreateConfiguredMockTest extends TestCase { public function testCreatesMockObjectForInterfaceOrExtendableClassWithReturnValueConfigurationForMultipleMethods(): void { - $stub = $this->createConfiguredMock( + $double = $this->createConfiguredMock( InterfaceWithReturnTypeDeclaration::class, [ 'doSomething' => true, @@ -32,7 +32,7 @@ public function testCreatesMockObjectForInterfaceOrExtendableClassWithReturnValu ], ); - $this->assertTrue($stub->doSomething()); - $this->assertSame(1, $stub->doSomethingElse(0)); + $this->assertTrue($double->doSomething()); + $this->assertSame(1, $double->doSomethingElse(0)); } } diff --git a/tests/unit/Framework/MockObject/Creation/CreateConfiguredStubTest.php b/tests/unit/Framework/MockObject/Creation/CreateConfiguredStubTest.php index 8f6fe080f6e..445947fd291 100644 --- a/tests/unit/Framework/MockObject/Creation/CreateConfiguredStubTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreateConfiguredStubTest.php @@ -24,7 +24,7 @@ final class CreateConfiguredStubTest extends TestCase { public function testCreatesTestStubForInterfaceOrExtendableClassWithReturnValueConfigurationForMultipleMethods(): void { - $stub = $this->createConfiguredStub( + $double = $this->createConfiguredStub( InterfaceWithReturnTypeDeclaration::class, [ 'doSomething' => true, @@ -32,7 +32,7 @@ public function testCreatesTestStubForInterfaceOrExtendableClassWithReturnValueC ], ); - $this->assertTrue($stub->doSomething()); - $this->assertSame(1, $stub->doSomethingElse(0)); + $this->assertTrue($double->doSomething()); + $this->assertSame(1, $double->doSomethingElse(0)); } } diff --git a/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesTest.php b/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesTest.php index 05998223e6d..b757801e8d8 100644 --- a/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesTest.php @@ -33,6 +33,20 @@ public function testCreatesMockObjectForIntersectionOfInterfaces(): void $this->assertInstanceOf(AnInterface::class, $double); $this->assertInstanceOf(AnotherInterface::class, $double); $this->assertInstanceOf(Stub::class, $double); + + $double->method('doSomething')->willReturn(true); + $double->method('doSomethingElse')->willReturn(true); + + $this->assertTrue($double->doSomething()); + $this->assertTrue($double->doSomethingElse()); + } + + public function testReturnValueGenerationIsEnabledByDefault(): void + { + $double = $this->createMockForIntersectionOfInterfaces([AnInterface::class, AnotherInterface::class]); + + $this->assertFalse($double->doSomething()); + $this->assertNull($double->doSomethingElse()); } public function testCannotCreateMockObjectForIntersectionOfInterfacesWhenLessThanTwoInterfacesAreSpecified(): void diff --git a/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest.php b/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest.php new file mode 100644 index 00000000000..a72711110b3 --- /dev/null +++ b/tests/unit/Framework/MockObject/Creation/CreateMockForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\AnInterface; +use PHPUnit\TestFixture\MockObject\AnotherInterface; + +#[Group('test-doubles')] +#[Group('test-doubles/creation')] +#[Group('test-doubles/mock-object')] +#[Medium] +#[TestDox('createMockForIntersectionOfInterfaces()')] +#[DisableReturnValueGenerationForTestDoubles] +final class CreateMockForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest extends TestCase +{ + public function testReturnValueGenerationCanBeDisabledWithAttribute(): void + { + $double = $this->createMockForIntersectionOfInterfaces([AnInterface::class, AnotherInterface::class]); + + $this->expectException(ReturnValueNotConfiguredException::class); + + $double->doSomething(); + } +} diff --git a/tests/unit/Framework/MockObject/Creation/CreateMockTest.php b/tests/unit/Framework/MockObject/Creation/CreateMockTest.php index dcf60803c94..4bb54486f74 100644 --- a/tests/unit/Framework/MockObject/Creation/CreateMockTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreateMockTest.php @@ -14,13 +14,13 @@ use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException; use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException; -use PHPUnit\Framework\MockObject\Generator\ClassIsReadonlyException; +use PHPUnit\Framework\MockObject\Generator\UnknownTypeException; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\AnInterface; use PHPUnit\TestFixture\MockObject\Enumeration; use PHPUnit\TestFixture\MockObject\ExtendableClass; +use PHPUnit\TestFixture\MockObject\ExtendableReadonlyClass; use PHPUnit\TestFixture\MockObject\FinalClass; -use PHPUnit\TestFixture\MockObject\ReadonlyClass; #[Group('test-doubles')] #[Group('test-doubles/creation')] @@ -35,6 +35,7 @@ public function testCreatesMockObjectForInterface(): void $this->assertInstanceOf(AnInterface::class, $double); $this->assertInstanceOf(Stub::class, $double); + $this->assertInstanceOf(MockObject::class, $double); } public function testCreatesMockObjectForExtendableClass(): void @@ -43,20 +44,30 @@ public function testCreatesMockObjectForExtendableClass(): void $this->assertInstanceOf(ExtendableClass::class, $double); $this->assertInstanceOf(Stub::class, $double); + $this->assertInstanceOf(MockObject::class, $double); } - public function testCannotCreateMockObjectForFinalClass(): void + public function testCreatesMockObjectForExtendableReadonlyClass(): void { - $this->expectException(ClassIsFinalException::class); + $double = $this->createMock(ExtendableReadonlyClass::class); - $this->createMock(FinalClass::class); + $this->assertInstanceOf(ExtendableReadonlyClass::class, $double); + $this->assertInstanceOf(Stub::class, $double); + $this->assertInstanceOf(MockObject::class, $double); + } + + public function testReturnValueGenerationIsEnabledByDefault(): void + { + $double = $this->createMock(AnInterface::class); + + $this->assertFalse($double->doSomething()); } - public function testCannotCreateMockObjectForReadonlyClass(): void + public function testCannotCreateMockObjectForFinalClass(): void { - $this->expectException(ClassIsReadonlyException::class); + $this->expectException(ClassIsFinalException::class); - $this->createMock(ReadonlyClass::class); + $this->createMock(FinalClass::class); } public function testCannotCreateMockObjectForEnumeration(): void @@ -65,4 +76,11 @@ public function testCannotCreateMockObjectForEnumeration(): void $this->createMock(Enumeration::class); } + + public function testCannotCreateMockObjectForUnknownType(): void + { + $this->expectException(UnknownTypeException::class); + + $this->createMock('this\does\not\exist'); + } } diff --git a/tests/unit/Framework/MockObject/Creation/CreateMockWithDisabledReturnValueGenerationTest.php b/tests/unit/Framework/MockObject/Creation/CreateMockWithDisabledReturnValueGenerationTest.php new file mode 100644 index 00000000000..41b69a7d555 --- /dev/null +++ b/tests/unit/Framework/MockObject/Creation/CreateMockWithDisabledReturnValueGenerationTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\AnInterface; + +#[Group('test-doubles')] +#[Group('test-doubles/creation')] +#[Group('test-doubles/mock-object')] +#[Medium] +#[TestDox('createMock()')] +#[DisableReturnValueGenerationForTestDoubles] +final class CreateMockWithDisabledReturnValueGenerationTest extends TestCase +{ + public function testReturnValueGenerationCanBeDisabledWithAttribute(): void + { + $double = $this->createMock(AnInterface::class); + + $this->expectException(ReturnValueNotConfiguredException::class); + + $double->doSomething(); + } +} diff --git a/tests/unit/Framework/MockObject/Creation/CreatePartialMockTest.php b/tests/unit/Framework/MockObject/Creation/CreatePartialMockTest.php index 0929a7215ab..5f3a7fa3582 100644 --- a/tests/unit/Framework/MockObject/Creation/CreatePartialMockTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreatePartialMockTest.php @@ -12,6 +12,7 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Medium; use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\MockObject\Generator\DuplicateMethodException; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\ExtendableClass; use ReflectionProperty; @@ -25,19 +26,27 @@ final class CreatePartialMockTest extends TestCase { public function testCreatesPartialMockObjectForExtendableClass(): void { - $mock = $this->createPartialMock(ExtendableClass::class, ['doSomethingElse']); + $double = $this->createPartialMock(ExtendableClass::class, ['doSomethingElse']); - $mock->expects($this->once())->method('doSomethingElse')->willReturn(true); + $double->expects($this->once())->method('doSomethingElse')->willReturn(true); - $this->assertTrue($mock->doSomething()); + $this->assertTrue($double->doSomething()); + } + + public function testCannotCreatePartialMockObjectForExtendableClassWithDuplicateMethods(): void + { + $this->expectException(DuplicateMethodException::class); + $this->expectExceptionMessage('Cannot double using a method list that contains duplicates: "doSomethingElse, doSomethingElse" (duplicate: "doSomethingElse")'); + + $this->createPartialMock(ExtendableClass::class, ['doSomethingElse', 'doSomethingElse']); } public function testMethodOfPartialMockThatIsNotConfigurableCannotBeConfigured(): void { - $mock = $this->createPartialMock(ExtendableClass::class, ['doSomethingElse']); + $double = $this->createPartialMock(ExtendableClass::class, ['doSomethingElse']); try { - $mock->expects($this->once())->method('doSomething')->willReturn(true); + $double->expects($this->once())->method('doSomething')->willReturn(true); } catch (MethodCannotBeConfiguredException $e) { $this->assertSame('Trying to configure method "doSomething" which cannot be configured because it does not exist, has not been specified, is final, or is static', $e->getMessage()); diff --git a/tests/unit/Framework/MockObject/Creation/CreatePartialMockWithDisabledReturnValueGenerationTest.php b/tests/unit/Framework/MockObject/Creation/CreatePartialMockWithDisabledReturnValueGenerationTest.php new file mode 100644 index 00000000000..34672c3bd1e --- /dev/null +++ b/tests/unit/Framework/MockObject/Creation/CreatePartialMockWithDisabledReturnValueGenerationTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\ExtendableClass; + +#[Group('test-doubles')] +#[Group('test-doubles/creation')] +#[Group('test-doubles/mock-object')] +#[Medium] +#[TestDox('createPartialMock()')] +#[DisableReturnValueGenerationForTestDoubles] +final class CreatePartialMockWithDisabledReturnValueGenerationTest extends TestCase +{ + public function testReturnValueGenerationCanBeDisabledWithAttribute(): void + { + $double = $this->createPartialMock(ExtendableClass::class, ['doSomething']); + + $this->expectException(ReturnValueNotConfiguredException::class); + + $double->doSomething(); + } +} diff --git a/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesTest.php b/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesTest.php index b902a917cb5..f99f410fd39 100644 --- a/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesTest.php @@ -33,6 +33,20 @@ public function testCreatesTestStubForIntersectionOfInterfaces(): void $this->assertInstanceOf(AnInterface::class, $double); $this->assertInstanceOf(AnotherInterface::class, $double); $this->assertInstanceOf(Stub::class, $double); + + $double->method('doSomething')->willReturn(true); + $double->method('doSomethingElse')->willReturn(true); + + $this->assertTrue($double->doSomething()); + $this->assertTrue($double->doSomethingElse()); + } + + public function testReturnValueGenerationIsEnabledByDefault(): void + { + $double = $this->createStubForIntersectionOfInterfaces([AnInterface::class, AnotherInterface::class]); + + $this->assertFalse($double->doSomething()); + $this->assertNull($double->doSomethingElse()); } public function testCannotCreateTestStubForIntersectionOfInterfacesWhenLessThanTwoInterfacesAreSpecified(): void diff --git a/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest.php b/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest.php new file mode 100644 index 00000000000..4049f87e452 --- /dev/null +++ b/tests/unit/Framework/MockObject/Creation/CreateStubForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\AnInterface; +use PHPUnit\TestFixture\MockObject\AnotherInterface; + +#[Group('test-doubles')] +#[Group('test-doubles/creation')] +#[Group('test-doubles/test-stub')] +#[Medium] +#[TestDox('createStubForIntersectionOfInterfaces()')] +#[DisableReturnValueGenerationForTestDoubles] +final class CreateStubForIntersectionOfInterfacesWithDisabledReturnValueGenerationTest extends TestCase +{ + public function testReturnValueGenerationCanBeDisabledWithAttribute(): void + { + $double = $this->createStubForIntersectionOfInterfaces([AnInterface::class, AnotherInterface::class]); + + $this->expectException(ReturnValueNotConfiguredException::class); + + $double->doSomething(); + } +} diff --git a/tests/unit/Framework/MockObject/Creation/CreateStubTest.php b/tests/unit/Framework/MockObject/Creation/CreateStubTest.php index 7769b59bd4a..0226009ab78 100644 --- a/tests/unit/Framework/MockObject/Creation/CreateStubTest.php +++ b/tests/unit/Framework/MockObject/Creation/CreateStubTest.php @@ -14,13 +14,13 @@ use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException; use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException; -use PHPUnit\Framework\MockObject\Generator\ClassIsReadonlyException; +use PHPUnit\Framework\MockObject\Generator\UnknownTypeException; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\AnInterface; use PHPUnit\TestFixture\MockObject\Enumeration; use PHPUnit\TestFixture\MockObject\ExtendableClass; +use PHPUnit\TestFixture\MockObject\ExtendableReadonlyClass; use PHPUnit\TestFixture\MockObject\FinalClass; -use PHPUnit\TestFixture\MockObject\ReadonlyClass; #[Group('test-doubles')] #[Group('test-doubles/creation')] @@ -45,18 +45,26 @@ public function testCreatesTestStubForExtendableClass(): void $this->assertInstanceOf(Stub::class, $double); } - public function testCannotCreateTestStubForFinalClass(): void + public function testCreatesTestStubForExtendableReadonlyClass(): void { - $this->expectException(ClassIsFinalException::class); + $double = $this->createStub(ExtendableReadonlyClass::class); - $this->createStub(FinalClass::class); + $this->assertInstanceOf(ExtendableReadonlyClass::class, $double); + $this->assertInstanceOf(Stub::class, $double); + } + + public function testReturnValueGenerationIsEnabledByDefault(): void + { + $double = $this->createStub(AnInterface::class); + + $this->assertFalse($double->doSomething()); } - public function testCannotCreateTestStubForReadonlyClass(): void + public function testCannotCreateTestStubForFinalClass(): void { - $this->expectException(ClassIsReadonlyException::class); + $this->expectException(ClassIsFinalException::class); - $this->createStub(ReadonlyClass::class); + $this->createStub(FinalClass::class); } public function testCannotCreateTestStubForEnumeration(): void @@ -65,4 +73,11 @@ public function testCannotCreateTestStubForEnumeration(): void $this->createStub(Enumeration::class); } + + public function testCannotCreateTestStubForUnknownType(): void + { + $this->expectException(UnknownTypeException::class); + + $this->createStub('this\does\not\exist'); + } } diff --git a/tests/unit/Framework/MockObject/Creation/CreateStubWithDisabledReturnValueGenerationTest.php b/tests/unit/Framework/MockObject/Creation/CreateStubWithDisabledReturnValueGenerationTest.php new file mode 100644 index 00000000000..eff448ae4f7 --- /dev/null +++ b/tests/unit/Framework/MockObject/Creation/CreateStubWithDisabledReturnValueGenerationTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\AnInterface; + +#[Group('test-doubles')] +#[Group('test-doubles/creation')] +#[Group('test-doubles/test-stub')] +#[Medium] +#[TestDox('createStub()')] +#[DisableReturnValueGenerationForTestDoubles] +final class CreateStubWithDisabledReturnValueGenerationTest extends TestCase +{ + public function testReturnValueGenerationCanBeDisabledWithAttribute(): void + { + $double = $this->createStub(AnInterface::class); + + $this->expectException(ReturnValueNotConfiguredException::class); + + $double->doSomething(); + } +} diff --git a/tests/unit/Framework/MockObject/Creation/CreateTestProxyTest.php b/tests/unit/Framework/MockObject/Creation/CreateTestProxyTest.php deleted file mode 100644 index 9ca8cd00720..00000000000 --- a/tests/unit/Framework/MockObject/Creation/CreateTestProxyTest.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use function assert; -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; -use PHPUnit\Framework\Attributes\Medium; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; -use PHPUnit\TestFixture\MockObject\TestProxyFixture; - -#[Group('test-doubles')] -#[Group('test-doubles/creation')] -#[Group('test-doubles/test-proxy')] -#[Medium] -#[TestDox('createTestProxy()')] -#[IgnorePhpunitDeprecations] -final class CreateTestProxyTest extends TestCase -{ - public function testCreatesTestProxyForExtendableClass(): void - { - $proxy = $this->createTestProxy(TestProxyFixture::class); - - $proxy->expects($this->once()) - ->method('returnString'); - - assert($proxy instanceof MockObject); - assert($proxy instanceof TestProxyFixture); - - $this->assertSame('result', $proxy->returnString()); - } -} diff --git a/tests/unit/Framework/MockObject/Creation/GetMockForAbstractClassTest.php b/tests/unit/Framework/MockObject/Creation/GetMockForAbstractClassTest.php deleted file mode 100644 index 7a545ac808d..00000000000 --- a/tests/unit/Framework/MockObject/Creation/GetMockForAbstractClassTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; -use PHPUnit\Framework\Attributes\Medium; -use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\MockObject\Generator\UnknownClassException; -use PHPUnit\Framework\TestCase; -use PHPUnit\TestFixture\MockObject\AbstractClass; -use ReflectionProperty; - -#[Group('test-doubles')] -#[Group('test-doubles/creation')] -#[Group('test-doubles/mock-object')] -#[Medium] -#[RequiresPhpExtension('soap')] -#[TestDox('getMockForAbstractClass()')] -#[IgnorePhpunitDeprecations] -final class GetMockForAbstractClassTest extends TestCase -{ - public function testCreatesMockObjectForAbstractClassAndAllowsConfigurationOfAbstractMethods(): void - { - $mock = $this->getMockForAbstractClass(AbstractClass::class); - - $mock->expects($this->once())->method('doSomethingElse')->willReturn(true); - - $this->assertTrue($mock->doSomething()); - } - - public function testCannotCreateMockObjectForAbstractClassThatDoesNotExist(): void - { - $this->expectException(UnknownClassException::class); - $this->expectExceptionMessage('Class "DoesNotExist" does not exist'); - - $this->getMockForAbstractClass('DoesNotExist'); - } - - public function testCreatesMockObjectForAbstractClassAndDoesNotAllowConfigurationOfConcreteMethods(): void - { - $mock = $this->getMockForAbstractClass(AbstractClass::class); - - try { - $mock->expects($this->once())->method('doSomething'); - } catch (MethodCannotBeConfiguredException $e) { - $this->assertSame('Trying to configure method "doSomething" which cannot be configured because it does not exist, has not been specified, is final, or is static', $e->getMessage()); - - return; - } finally { - $this->resetMockObjects(); - } - - $this->fail(); - } - - private function resetMockObjects(): void - { - (new ReflectionProperty(TestCase::class, 'mockObjects'))->setValue($this, []); - } -} diff --git a/tests/unit/Framework/MockObject/Creation/GetMockForTraitTest.php b/tests/unit/Framework/MockObject/Creation/GetMockForTraitTest.php deleted file mode 100644 index 92343d0c30b..00000000000 --- a/tests/unit/Framework/MockObject/Creation/GetMockForTraitTest.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; -use PHPUnit\Framework\Attributes\Medium; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\MockObject\Generator\UnknownTraitException; -use PHPUnit\Framework\TestCase; -use PHPUnit\TestFixture\MockObject\TraitWithConcreteAndAbstractMethod; - -#[Group('test-doubles')] -#[Group('test-doubles/creation')] -#[Group('test-doubles/mock-object')] -#[Medium] -#[TestDox('getMockForTrait()')] -#[IgnorePhpunitDeprecations] -final class GetMockForTraitTest extends TestCase -{ - public function testCreatesMockObjectForTraitAndAllowsConfigurationOfAbstractMethods(): void - { - $mock = $this->getMockForTrait(TraitWithConcreteAndAbstractMethod::class); - - $mock->method('abstractMethod')->willReturn(true); - - $this->assertTrue($mock->concreteMethod()); - } - - public function testCannotCreateMockObjectForTraitThatDoesNotExist(): void - { - $this->expectException(UnknownTraitException::class); - $this->expectExceptionMessage('Trait "TraitThatDoesNotExist" does not exist'); - - $this->getMockForTrait('TraitThatDoesNotExist'); - } -} diff --git a/tests/unit/Framework/MockObject/Creation/GetMockFromWsdlTest.php b/tests/unit/Framework/MockObject/Creation/GetMockFromWsdlTest.php deleted file mode 100644 index 6f517217481..00000000000 --- a/tests/unit/Framework/MockObject/Creation/GetMockFromWsdlTest.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; -use PHPUnit\Framework\Attributes\Medium; -use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; - -#[Group('test-doubles')] -#[Group('test-doubles/creation')] -#[Group('test-doubles/mock-object')] -#[Medium] -#[RequiresPhpExtension('soap')] -#[TestDox('getMockFromWsdl()')] -#[IgnorePhpunitDeprecations] -final class GetMockFromWsdlTest extends TestCase -{ - #[TestDox('Creates mock object from WSDL file')] - public function test_CreatesMockObjectFromWsdlFileWithNonNamespacedClassName(): void - { - $mock = $this->getMockFromWsdl(TEST_FILES_PATH . 'GoogleSearch.wsdl'); - - $this->assertStringStartsWith( - 'MockObject_GoogleSearch_', - $mock::class, - ); - } -} diff --git a/tests/unit/Framework/MockObject/Creation/GetObjectForTraitTest.php b/tests/unit/Framework/MockObject/Creation/GetObjectForTraitTest.php deleted file mode 100644 index 3c90fb3c0ac..00000000000 --- a/tests/unit/Framework/MockObject/Creation/GetObjectForTraitTest.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; -use PHPUnit\Framework\Attributes\Medium; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\MockObject\Generator\UnknownTraitException; -use PHPUnit\Framework\TestCase; -use PHPUnit\TestFixture\MockObject\TraitWithConcreteMethod; - -#[Group('test-doubles')] -#[Group('test-doubles/creation')] -#[Medium] -#[TestDox('getObjectForTrait()')] -#[IgnorePhpunitDeprecations] -final class GetObjectForTraitTest extends TestCase -{ - public function testCreatesObjectForTrait(): void - { - $object = $this->getObjectForTrait(TraitWithConcreteMethod::class); - - $this->assertTrue($object->doSomething()); - } - - public function testCannotCreateObjectForTraitThatDoesNotExist(): void - { - $this->expectException(UnknownTraitException::class); - $this->expectExceptionMessage('Trait "TraitThatDoesNotExist" does not exist'); - - $this->getObjectForTrait('TraitThatDoesNotExist'); - } -} diff --git a/tests/unit/Framework/MockObject/Creation/MockBuilderTest.php b/tests/unit/Framework/MockObject/Creation/MockBuilderTest.php index 67f8297d947..8edcb05c500 100644 --- a/tests/unit/Framework/MockObject/Creation/MockBuilderTest.php +++ b/tests/unit/Framework/MockObject/Creation/MockBuilderTest.php @@ -15,18 +15,27 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\MockObject\Generator\DuplicateMethodException; +use PHPUnit\Framework\MockObject\Generator\InvalidMethodNameException; +use PHPUnit\Framework\MockObject\Generator\NameAlreadyInUseException; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\ExtendableClass; +use PHPUnit\TestFixture\MockObject\ExtendableClassCallingMethodInConstructor; +use PHPUnit\TestFixture\MockObject\ExtendableClassWithConstructorArguments; use PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration; #[CoversClass(MockBuilder::class)] -#[CoversClass(CannotUseAddMethodsException::class)] +#[CoversClass(DuplicateMethodException::class)] +#[CoversClass(InvalidMethodNameException::class)] +#[CoversClass(NameAlreadyInUseException::class)] #[Group('test-doubles')] #[Group('test-doubles/creation')] #[Group('test-doubles/mock-object')] #[Medium] final class MockBuilderTest extends TestCase { + #[TestDox('setMockClassName() can be used to configure the name of the mock object class')] public function testCanCreateMockObjectWithSpecifiedClassName(): void { $className = 'random_' . md5((string) mt_rand()); @@ -38,27 +47,63 @@ public function testCanCreateMockObjectWithSpecifiedClassName(): void $this->assertSame($className, $double::class); } - #[IgnorePhpunitDeprecations] - public function testCanCreateMockObjectForExtendableClassWhileAddingMethodsToIt(): void + #[TestDox('setMockClassName() cannot be used to configure the name of the mock object class when a class with that name already exists')] + public function testCannotCreateMockObjectWithSpecifiedClassNameWhenClassWithThatNameAlreadyExists(): void { - $double = $this->getMockBuilder(ExtendableClass::class) - ->addMethods(['additionalMethod']) + $this->expectException(NameAlreadyInUseException::class); + + $this->getMockBuilder(InterfaceWithReturnTypeDeclaration::class) + ->setMockClassName(__CLASS__) + ->getMock(); + } + + #[TestDox('setConstructorArgs() can be used to configure constructor arguments for a partially mocked class')] + public function testConstructorArgumentsCanBeConfiguredForPartiallyMockedClass(): void + { + $value = 'string'; + + $double = $this->getMockBuilder(ExtendableClassWithConstructorArguments::class) + ->enableOriginalConstructor() + ->setConstructorArgs([$value]) + ->onlyMethods([]) ->getMock(); - $value = 'value'; + $this->assertSame($value, $double->value()); + } + + #[TestDox('onlyMethods() can be used to configure which methods should be doubled')] + public function testCreatesPartialMockObjectForExtendableClass(): void + { + $double = $this->getMockBuilder(ExtendableClass::class) + ->onlyMethods(['doSomethingElse']) + ->getMock(); - $double->method('additionalMethod')->willReturn($value); + $double->expects($this->once())->method('doSomethingElse')->willReturn(true); - $this->assertSame($value, $double->additionalMethod()); + $this->assertTrue($double->doSomething()); } #[IgnorePhpunitDeprecations] - public function testCannotCreateMockObjectForExtendableClassAddingMethodsToItThatItAlreadyHas(): void + public function testDefaultBehaviourCanBeConfiguredExplicitly(): void { - $this->expectException(CannotUseAddMethodsException::class); + $double = $this->getMockBuilder(ExtendableClass::class) + ->enableOriginalConstructor() + ->enableOriginalClone() + ->enableAutoReturnValueGeneration() + ->getMock(); - $this->getMockBuilder(ExtendableClass::class) - ->addMethods(['doSomething']) + $this->assertTrue($double->constructorCalled); + } + + #[TestDox('Mocked methods can be called from the original constructor of a partially mocked class')] + public function testOnlyMethodCalledInConstructorWorks(): void + { + $double = $this->getMockBuilder(ExtendableClassCallingMethodInConstructor::class) + ->onlyMethods(['reset']) ->getMock(); + + $double->expects($this->once())->method('reset'); + + $double->second(); } } diff --git a/tests/unit/Framework/MockObject/Generator/PropertyTest.php b/tests/unit/Framework/MockObject/Generator/PropertyTest.php new file mode 100644 index 00000000000..1beab059452 --- /dev/null +++ b/tests/unit/Framework/MockObject/Generator/PropertyTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Generator; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use SebastianBergmann\Type\Type; + +#[CoversClass(HookedProperty::class)] +#[Group('test-doubles')] +#[Small] +final class PropertyTest extends TestCase +{ + public function testHasName(): void + { + $name = 'property-name'; + + $property = new HookedProperty($name, Type::fromName('string', false), false, false); + + $this->assertSame($name, $property->name()); + } + + public function testHasType(): void + { + $type = Type::fromName('string', false); + + $property = new HookedProperty('property-name', $type, false, false); + + $this->assertSame($type, $property->type()); + } + + public function testMayHaveGetHook(): void + { + $property = new HookedProperty('property-name', Type::fromName('string', false), true, false); + + $this->assertTrue($property->hasGetHook()); + } + + public function testMayNotHaveGetHook(): void + { + $property = new HookedProperty('property-name', Type::fromName('string', false), false, false); + + $this->assertFalse($property->hasGetHook()); + } + + public function testMayHaveSetHook(): void + { + $property = new HookedProperty('property-name', Type::fromName('string', false), false, true); + + $this->assertTrue($property->hasSetHook()); + } + + public function testMayNotHaveSetHook(): void + { + $property = new HookedProperty('property-name', Type::fromName('string', false), false, false); + + $this->assertFalse($property->hasSetHook()); + } +} diff --git a/tests/unit/Framework/MockObject/MockObjectTest.php b/tests/unit/Framework/MockObject/MockObjectTest.php index be1cbef86db..e4f012419fe 100644 --- a/tests/unit/Framework/MockObject/MockObjectTest.php +++ b/tests/unit/Framework/MockObject/MockObjectTest.php @@ -10,16 +10,23 @@ namespace PHPUnit\Framework\MockObject; use function call_user_func_array; +use Exception; use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\Attributes\Group; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\Attributes\RequiresMethod; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\AnInterface; +use PHPUnit\TestFixture\MockObject\ExtendableClassWithCloneMethod; +use PHPUnit\TestFixture\MockObject\ExtendableClassWithPropertyWithSetHook; +use PHPUnit\TestFixture\MockObject\ExtendableReadonlyClassWithCloneMethod; use PHPUnit\TestFixture\MockObject\InterfaceWithImplicitProtocol; +use PHPUnit\TestFixture\MockObject\InterfaceWithPropertyWithSetHook; use PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration; +use PHPUnit\TestFixture\MockObject\MethodWIthVariadicVariables; use ReflectionProperty; #[Group('test-doubles')] @@ -30,20 +37,20 @@ final class MockObjectTest extends TestDoubleTestCase { public function testExpectationThatMethodIsNeverCalledSucceedsWhenMethodIsNotCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->never())->method('doSomething'); + $double->expects($this->never())->method('doSomething'); } public function testExpectationThatMethodIsNeverCalledFailsWhenMethodIsCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->never())->method('doSomething'); + $double->expects($this->never())->method('doSomething'); $this->assertThatMockObjectExpectationFails( - AnInterface::class . '::doSomething() was not expected to be called.', - $mock, + AnInterface::class . '::doSomething(): bool was not expected to be called.', + $double, 'doSomething', ); } @@ -51,35 +58,35 @@ public function testExpectationThatMethodIsNeverCalledFailsWhenMethodIsCalled(): #[DoesNotPerformAssertions] public function testExpectationThatMethodIsCalledZeroOrMoreTimesSucceedsWhenMethodIsNotCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->any())->method('doSomething'); + $double->expects($this->any())->method('doSomething'); } #[DoesNotPerformAssertions] public function testExpectationThatMethodIsCalledZeroOrMoreTimesSucceedsWhenMethodIsCalledOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->any())->method('doSomething'); + $double->expects($this->any())->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledOnceSucceedsWhenMethodIsCalledOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->once())->method('doSomething'); + $double->expects($this->once())->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledOnceFailsWhenMethodIsNeverCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->once())->method('doSomething'); + $double->expects($this->once())->method('doSomething'); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -87,70 +94,70 @@ public function testExpectationThatMethodIsCalledOnceFailsWhenMethodIsNeverCalle Method was expected to be called 1 time, actually called 0 times. EOT, - $mock, + $double, ); } public function testExpectationThatMethodIsCalledOnceFailsWhenMethodIsCalledMoreThanOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->once())->method('doSomething'); + $double->expects($this->once())->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); $this->assertThatMockObjectExpectationFails( - AnInterface::class . '::doSomething() was not expected to be called more than once.', - $mock, + AnInterface::class . '::doSomething(): bool was not expected to be called more than once.', + $double, 'doSomething', ); } public function testExpectationThatMethodIsCalledAtLeastOnceSucceedsWhenMethodIsCalledOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atLeastOnce())->method('doSomething'); + $double->expects($this->atLeastOnce())->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledAtLeastOnceSucceedsWhenMethodIsCalledTwice(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atLeastOnce())->method('doSomething'); + $double->expects($this->atLeastOnce())->method('doSomething'); - $mock->doSomething(); - $mock->doSomething(); + $double->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledAtLeastTwiceSucceedsWhenMethodIsCalledTwice(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atLeast(2))->method('doSomething'); + $double->expects($this->atLeast(2))->method('doSomething'); - $mock->doSomething(); - $mock->doSomething(); + $double->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledAtLeastTwiceSucceedsWhenMethodIsCalledThreeTimes(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atLeast(2))->method('doSomething'); + $double->expects($this->atLeast(2))->method('doSomething'); - $mock->doSomething(); - $mock->doSomething(); - $mock->doSomething(); + $double->doSomething(); + $double->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledAtLeastOnceFailsWhenMethodIsNotCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atLeastOnce())->method('doSomething'); + $double->expects($this->atLeastOnce())->method('doSomething'); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -158,17 +165,17 @@ public function testExpectationThatMethodIsCalledAtLeastOnceFailsWhenMethodIsNot Expected invocation at least once but it never occurred. EOT, - $mock, + $double, ); } public function testExpectationThatMethodIsCalledAtLeastTwiceFailsWhenMethodIsCalledOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atLeast(2))->method('doSomething'); + $double->expects($this->atLeast(2))->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -176,25 +183,25 @@ public function testExpectationThatMethodIsCalledAtLeastTwiceFailsWhenMethodIsCa Expected invocation at least 2 times but it occurred 1 time. EOT, - $mock, + $double, ); } public function testExpectationThatMethodIsCalledTwiceSucceedsWhenMethodIsCalledTwice(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->exactly(2))->method('doSomething'); + $double->expects($this->exactly(2))->method('doSomething'); - $mock->doSomething(); - $mock->doSomething(); + $double->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledTwiceFailsWhenMethodIsNeverCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->exactly(2))->method('doSomething'); + $double->expects($this->exactly(2))->method('doSomething'); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -202,17 +209,17 @@ public function testExpectationThatMethodIsCalledTwiceFailsWhenMethodIsNeverCall Method was expected to be called 2 times, actually called 0 times. EOT, - $mock, + $double, ); } public function testExpectationThatMethodIsCalledTwiceFailsWhenMethodIsCalledOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->exactly(2))->method('doSomething'); + $double->expects($this->exactly(2))->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -220,50 +227,50 @@ public function testExpectationThatMethodIsCalledTwiceFailsWhenMethodIsCalledOnc Method was expected to be called 2 times, actually called 1 time. EOT, - $mock, + $double, ); } public function testExpectationThatMethodIsCalledTwiceFailsWhenMethodIsCalledThreeTimes(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->exactly(2))->method('doSomething'); + $double->expects($this->exactly(2))->method('doSomething'); - $mock->doSomething(); - $mock->doSomething(); + $double->doSomething(); + $double->doSomething(); $this->assertThatMockObjectExpectationFails( - AnInterface::class . '::doSomething() was not expected to be called more than 2 times.', - $mock, + AnInterface::class . '::doSomething(): bool was not expected to be called more than 2 times.', + $double, 'doSomething', ); } public function testExpectationThatMethodIsCalledAtMostOnceSucceedsWhenMethodIsNeverCalled(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atMost(1))->method('doSomething'); + $double->expects($this->atMost(1))->method('doSomething'); } public function testExpectationThatMethodIsCalledAtMostOnceSucceedsWhenMethodIsCalledOnce(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atMost(1))->method('doSomething'); + $double->expects($this->atMost(1))->method('doSomething'); - $mock->doSomething(); + $double->doSomething(); } public function testExpectationThatMethodIsCalledAtMostOnceFailsWhenMethodIsCalledTwice(): void { - $mock = $this->createMock(AnInterface::class); + $double = $this->createMock(AnInterface::class); - $mock->expects($this->atMost(1))->method('doSomething'); + $double->expects($this->atMost(1))->method('doSomething'); - $mock->doSomething(); - $mock->doSomething(); + $double->doSomething(); + $double->doSomething(); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -271,33 +278,33 @@ public function testExpectationThatMethodIsCalledAtMostOnceFailsWhenMethodIsCall Expected invocation at most 1 time but it occurred 2 times. EOT, - $mock, + $double, ); } public function testExpectationThatMethodIsCalledWithAnyParameterSucceedsWhenMethodIsCalledWithParameter(): void { - $mock = $this->createMock(InterfaceWithReturnTypeDeclaration::class); + $double = $this->createMock(InterfaceWithReturnTypeDeclaration::class); - $mock->expects($this->once())->method('doSomethingElse')->withAnyParameters(); + $double->expects($this->once())->method('doSomethingElse')->withAnyParameters(); - $mock->doSomethingElse(1); + $double->doSomethingElse(1); } public function testExpectationThatMethodIsCalledWithParameterSucceedsWhenMethodIsCalledWithExpectedParameter(): void { - $mock = $this->createMock(InterfaceWithReturnTypeDeclaration::class); + $double = $this->createMock(InterfaceWithReturnTypeDeclaration::class); - $mock->expects($this->once())->method('doSomethingElse')->with(1); + $double->expects($this->once())->method('doSomethingElse')->with(1); - $mock->doSomethingElse(1); + $double->doSomethingElse(1); } public function testExpectationThatMethodIsCalledWithParameterFailsWhenMethodIsCalledButWithUnexpectedParameter(): void { - $mock = $this->createMock(InterfaceWithReturnTypeDeclaration::class); + $double = $this->createMock(InterfaceWithReturnTypeDeclaration::class); - $mock->expects($this->once())->method('doSomethingElse')->with(1); + $double->expects($this->once())->method('doSomethingElse')->with(1); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -305,70 +312,70 @@ public function testExpectationThatMethodIsCalledWithParameterFailsWhenMethodIsC Parameter 0 for invocation PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration::doSomethingElse(0): int does not match expected value. Failed asserting that 0 matches expected 1. EOT, - $mock, + $double, 'doSomethingElse', [0], ); } /** - * With $mock->expects($this->once())->method('one')->id($id);, + * With $double->expects($this->once())->method('one')->id($id);, * we configure an expectation that one() is called once. This expectation is given the ID $id. * - * With $mock->expects($this->once())->method('two')->after($id);, + * With $double->expects($this->once())->method('two')->after($id);, * we configure an expectation that two() is called once. However, this expectation will only be verified * if/after one() has been called. */ public function testMethodCallCanBeExpectedContingentOnWhetherAnotherMethodWasPreviouslyCalled(): void { - $id = 'the-id'; - $mock = $this->createMock(InterfaceWithImplicitProtocol::class); + $id = 'the-id'; + $double = $this->createMock(InterfaceWithImplicitProtocol::class); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('one') ->id($id); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('two') ->after($id); - $mock->one(); - $mock->two(); + $double->one(); + $double->two(); } public function testContingentExpectationsAreNotEvaluatedUntilTheirConditionIsMet(): void { - $id = 'the-id'; - $mock = $this->createMock(InterfaceWithImplicitProtocol::class); + $id = 'the-id'; + $double = $this->createMock(InterfaceWithImplicitProtocol::class); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('one') ->id($id); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('two') ->after($id); - $mock->two(); - $mock->one(); - $mock->two(); + $double->two(); + $double->one(); + $double->two(); } public function testContingentExpectationsAreEvaluatedWhenTheirConditionIsMet(): void { - $id = 'the-id'; - $mock = $this->createMock(InterfaceWithImplicitProtocol::class); + $id = 'the-id'; + $double = $this->createMock(InterfaceWithImplicitProtocol::class); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('one') ->id($id); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('two') ->after($id); - $mock->two(); - $mock->one(); + $double->two(); + $double->one(); $this->assertThatMockObjectExpectationFails( <<<'EOT' @@ -376,36 +383,36 @@ public function testContingentExpectationsAreEvaluatedWhenTheirConditionIsMet(): Method was expected to be called 1 time, actually called 0 times. EOT, - $mock, + $double, ); } public function testExpectationCannotBeContingentOnExpectationThatHasNotBeenConfigured(): void { - $mock = $this->createMock(InterfaceWithImplicitProtocol::class); + $double = $this->createMock(InterfaceWithImplicitProtocol::class); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('two') ->after('the-id'); $this->assertThatMockObjectExpectationFails( 'No builder found for match builder identification ', - $mock, + $double, 'two', ); } public function testExpectationsCannotHaveDuplicateIds(): void { - $id = 'the-id'; - $mock = $this->createMock(InterfaceWithImplicitProtocol::class); + $id = 'the-id'; + $double = $this->createMock(InterfaceWithImplicitProtocol::class); - $mock->expects($this->once()) + $double->expects($this->once()) ->method('one') ->id($id); try { - $mock->expects($this->once()) + $double->expects($this->once()) ->method('one') ->id($id); } catch (MatcherAlreadyRegisteredException $e) { @@ -419,28 +426,112 @@ public function testExpectationsCannotHaveDuplicateIds(): void $this->fail(); } - #[IgnorePhpunitDeprecations] - public function testExpectationsCanBeConfiguredOnTestStubs(): void + public function testWillReturnCallbackWithVariadicVariables(): void { - $mock = $this->createStub(AnInterface::class); + $double = $this->createMock(MethodWIthVariadicVariables::class); + $double->expects($this->once())->method('testVariadic') + ->withAnyParameters() + ->willReturnCallback(static fn (string $string, string ...$arguments) => [$string, ...$arguments]); - $mock->expects($this->never())->method('doSomething'); + $testData = ['foo', 'bar', 'biz' => 'kuz']; + $actual = $double->testVariadic(...$testData); - $this->assertTrue(true); + $this->assertSame($testData, $actual); + } + + public function testExpectationsAreClonedWhenTestDoubleIsCloned(): void + { + $double = $this->createMock(InterfaceWithReturnTypeDeclaration::class); + + $double->expects($this->exactly(2))->method('doSomething'); + + $clone = clone $double; + + $double->expects($this->once())->method('doSomethingElse')->willReturn(1); + $clone->expects($this->once())->method('doSomethingElse')->willReturn(2); + + $this->assertFalse($double->doSomething()); + $this->assertFalse($clone->doSomething()); + $this->assertSame(1, $double->doSomethingElse(0)); + $this->assertSame(2, $clone->doSomethingElse(0)); + } + + #[RequiresMethod(ReflectionProperty::class, 'isFinal')] + public function testExpectationCanBeConfiguredForSetHookForPropertyOfInterface(): void + { + $double = $this->createTestDouble(InterfaceWithPropertyWithSetHook::class); + + $double->expects($this->once())->method(PropertyHook::set('property'))->with('value'); + + $double->property = 'value'; + } + + #[RequiresMethod(ReflectionProperty::class, 'isFinal')] + public function testExpectationCanBeConfiguredForSetHookForPropertyOfExtendableClass(): void + { + $double = $this->createTestDouble(ExtendableClassWithPropertyWithSetHook::class); + + $double->expects($this->once())->method(PropertyHook::set('property'))->with('value'); + + $double->property = 'value'; + } + + #[TestDox('__toString() method returns empty string when return value generation is disabled and no return value is configured')] + public function testToStringMethodReturnsEmptyStringWhenReturnValueGenerationIsDisabledAndNoReturnValueIsConfigured(): void + { + $double = $this->getMockBuilder(InterfaceWithReturnTypeDeclaration::class) + ->disableAutoReturnValueGeneration() + ->getMock(); + + $this->assertSame('', $double->__toString()); + } + + public function testMethodDoesNotReturnValueWhenReturnValueGenerationIsDisabledAndNoReturnValueIsConfigured(): void + { + $double = $this->getMockBuilder(InterfaceWithReturnTypeDeclaration::class) + ->disableAutoReturnValueGeneration() + ->getMock(); + + $this->expectException(ReturnValueNotConfiguredException::class); + $this->expectExceptionMessage('No return value is configured for ' . InterfaceWithReturnTypeDeclaration::class . '::doSomething() and return value generation is disabled'); + + $double->doSomething(); + } + + #[TestDox('Original __clone() method can optionally be called when test double object is cloned')] + public function testOriginalCloneMethodCanOptionallyBeCalledWhenTestDoubleObjectIsCloned(): void + { + $double = $this->getMockBuilder(ExtendableClassWithCloneMethod::class)->enableOriginalClone()->getMock(); + + $this->expectException(Exception::class); + $this->expectExceptionMessage(ExtendableClassWithCloneMethod::class . '::__clone'); + + clone $double; + } + + #[TestDox('Original __clone() method can optionally be called when test double object is cloned (readonly class)')] + public function testOriginalCloneMethodCanOptionallyBeCalledWhenTestDoubleObjectOfReadonlyClassIsCloned(): void + { + $double = $this->getMockBuilder(ExtendableReadonlyClassWithCloneMethod::class)->enableOriginalClone()->getMock(); + + $this->expectException(Exception::class); + $this->expectExceptionMessage(ExtendableReadonlyClassWithCloneMethod::class . '::__clone'); + + clone $double; } /** - * @psalm-param class-string $type + * @param class-string $type */ protected function createTestDouble(string $type): object { return $this->createMock($type); } - private function assertThatMockObjectExpectationFails(string $expectationFailureMessage, MockObject $mock, string $methodName = '__phpunit_verify', array $arguments = []): void + private function assertThatMockObjectExpectationFails(string $expectationFailureMessage, MockObject $double, string $methodName = '__phpunit_verify', array $arguments = []): void { try { - call_user_func_array([$mock, $methodName], $arguments); + call_user_func_array([$double, $methodName], $arguments); } catch (ExpectationFailedException|MatchBuilderNotFoundException $e) { $this->assertSame($expectationFailureMessage, $e->getMessage()); diff --git a/tests/unit/Framework/MockObject/ReturnValueGeneratorTest.php b/tests/unit/Framework/MockObject/ReturnValueGeneratorTest.php index 175c360fd44..98691117cce 100644 --- a/tests/unit/Framework/MockObject/ReturnValueGeneratorTest.php +++ b/tests/unit/Framework/MockObject/ReturnValueGeneratorTest.php @@ -9,8 +9,6 @@ */ namespace PHPUnit\Framework\MockObject; -use function assert; -use function interface_exists; use function sprintf; use Generator; use PHPUnit\Framework\Attributes\CoversClass; @@ -18,9 +16,15 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\MockObject\AnInterface; +use PHPUnit\TestFixture\MockObject\AnInterfaceForIssue5593; use PHPUnit\TestFixture\MockObject\AnotherInterface; +use PHPUnit\TestFixture\MockObject\AnotherInterfaceForIssue5593; +use PHPUnit\TestFixture\MockObject\ExtendableClass; +use PHPUnit\TestFixture\MockObject\InterfaceWithMethodThatReturnsSelf; +use PHPUnit\TestFixture\MockObject\InterfaceWithMethodThatReturnsStatic; use PHPUnit\TestFixture\MockObject\YetAnotherInterface; use stdClass; @@ -30,6 +34,9 @@ #[Small] final class ReturnValueGeneratorTest extends TestCase { + /** + * @return non-empty-list + */ public static function unionProvider(): array { return [ @@ -118,17 +125,35 @@ public function test_Generates_callable_for_Closure(): void public function test_Generates_Generator_for_Generator(): void { - $this->assertInstanceOf(Generator::class, $this->generate('Generator')); + $value = $this->generate('Generator'); + + $this->assertInstanceOf(Generator::class, $value); + + foreach ($value as $element) { + $this->assertSame([], $element); + } } public function test_Generates_Generator_for_Traversable(): void { - $this->assertInstanceOf(Generator::class, $this->generate('Traversable')); + $value = $this->generate('Traversable'); + + $this->assertInstanceOf(Generator::class, $value); + + foreach ($value as $element) { + $this->assertSame([], $element); + } } public function test_Generates_Generator_for_iterable(): void { - $this->assertInstanceOf(Generator::class, $this->generate('iterable')); + $value = $this->generate('iterable'); + + $this->assertInstanceOf(Generator::class, $value); + + foreach ($value as $element) { + $this->assertSame([], $element); + } } public function test_Generates_test_stub_for_class_or_interface_name(): void @@ -141,13 +166,6 @@ public function test_Generates_test_stub_for_class_or_interface_name(): void public function test_Generates_test_stub_for_intersection_of_interfaces(): void { - /** - * @todo Figure out why AnotherInterface is not found by the autoloader - * when only the tests of this class are run; the interface is found as - * expected when the entire (unit) test suite is run - */ - assert(interface_exists(AnotherInterface::class)); - $value = $this->generate(AnInterface::class . '&' . AnotherInterface::class); $this->assertInstanceOf(Stub::class, $value); @@ -155,11 +173,40 @@ public function test_Generates_test_stub_for_intersection_of_interfaces(): void $this->assertInstanceOf(AnotherInterface::class, $value); } + public function test_Generates_new_instance_of_test_stub_for_self(): void + { + $stub = $this->createStub(InterfaceWithMethodThatReturnsSelf::class); + + $returnValue = $stub->doSomething(); + + $this->assertNotInstanceOf($stub::class, $returnValue); + $this->assertNotSame($stub, $returnValue); + } + public function test_Generates_new_instance_of_test_stub_for_static(): void { - $stubClassName = ($this->createStub(AnInterface::class))::class; + $stub = $this->createStub(InterfaceWithMethodThatReturnsStatic::class); + + $returnValue = $stub->doSomething(); - $this->assertInstanceOf($stubClassName, $this->generate('static', $stubClassName)); + $this->assertInstanceOf($stub::class, $returnValue); + $this->assertNotSame($stub, $returnValue); + } + + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/5593')] + public function test_Generates_new_instance_of_test_stub_for_static_when_used_recursively(): void + { + $a = $this->createStub(AnInterfaceForIssue5593::class); + + $this->assertInstanceOf(AnInterfaceForIssue5593::class, $a); + + $b = $a->doSomething(); + + $this->assertInstanceOf(AnotherInterfaceForIssue5593::class, $b); + + $c = $b->doSomethingElse(); + + $this->assertInstanceOf(AnotherInterfaceForIssue5593::class, $c); } #[DataProvider('unionProvider')] @@ -191,17 +238,32 @@ public function test_Generates_test_stub_for_first_intersection_of_interfaces_fo $this->assertInstanceOf(AnotherInterface::class, $value); } - public function test_Generates_test_stub_for_unknown_type(): void + public function test_Does_not_handle_union_of_extendable_class_and_interface(): void { - $this->assertInstanceOf(Stub::class, $this->generate('ThisDoesNotExist')); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Return value for OriginalClassName::methodName() cannot be generated because the declared return type is a union, please configure a return value for this method'); + + $this->generate(ExtendableClass::class . '|' . AnInterface::class); + } + + public function test_Does_not_handle_intersection_of_extendable_class_and_interface(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Return value for OriginalClassName::methodName() cannot be generated because the declared return type is an intersection, please configure a return value for this method'); + + $this->generate(ExtendableClass::class . '&' . AnInterface::class); } - private function generate(string $typeDeclaration, string $stubClassName = 'StubClassName'): mixed + private function generate(string $typeDeclaration, ?StubInternal $testStub = null): mixed { + if ($testStub === null) { + $testStub = $this->createStub(AnInterface::class); + } + return (new ReturnValueGenerator)->generate( 'OriginalClassName', 'methodName', - $stubClassName, + $testStub, $typeDeclaration, ); } diff --git a/tests/unit/Framework/MockObject/Runtime/PropertyGetHookTest.php b/tests/unit/Framework/MockObject/Runtime/PropertyGetHookTest.php new file mode 100644 index 00000000000..3ef9b5616fb --- /dev/null +++ b/tests/unit/Framework/MockObject/Runtime/PropertyGetHookTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Runtime; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(PropertyHook::class)] +#[CoversClass(PropertyGetHook::class)] +#[Small] +#[Group('test-doubles')] +final class PropertyGetHookTest extends TestCase +{ + public function testHasPropertyName(): void + { + $propertyName = 'property'; + + $propertyHook = PropertyHook::get($propertyName); + + $this->assertSame($propertyName, $propertyHook->propertyName()); + } + + public function testCanBeRepresentedAsString(): void + { + $propertyName = 'property'; + + $propertyHook = PropertyHook::get($propertyName); + + $this->assertSame('$' . $propertyName . '::get', $propertyHook->asString()); + } +} diff --git a/tests/unit/Framework/MockObject/Runtime/PropertySetHookTest.php b/tests/unit/Framework/MockObject/Runtime/PropertySetHookTest.php new file mode 100644 index 00000000000..57c9c49961d --- /dev/null +++ b/tests/unit/Framework/MockObject/Runtime/PropertySetHookTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Runtime; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(PropertyHook::class)] +#[CoversClass(PropertySetHook::class)] +#[Small] +#[Group('test-doubles')] +final class PropertySetHookTest extends TestCase +{ + public function testHasPropertyName(): void + { + $propertyName = 'property'; + + $propertyHook = PropertyHook::set($propertyName); + + $this->assertSame($propertyName, $propertyHook->propertyName()); + } + + public function testCanBeRepresentedAsString(): void + { + $propertyName = 'property'; + + $propertyHook = PropertyHook::set($propertyName); + + $this->assertSame('$' . $propertyName . '::set', $propertyHook->asString()); + } +} diff --git a/tests/unit/Framework/MockObject/StubTest.php b/tests/unit/Framework/MockObject/StubTest.php index d458b3efc40..747fcd0e392 100644 --- a/tests/unit/Framework/MockObject/StubTest.php +++ b/tests/unit/Framework/MockObject/StubTest.php @@ -20,7 +20,7 @@ final class StubTest extends TestDoubleTestCase { /** - * @psalm-param class-string $type + * @param class-string $type */ protected function createTestDouble(string $type): object { diff --git a/tests/unit/Framework/MockObject/TestDoubleTestCase.php b/tests/unit/Framework/MockObject/TestDoubleTestCase.php index ddb54f65b78..dc985ec6251 100644 --- a/tests/unit/Framework/MockObject/TestDoubleTestCase.php +++ b/tests/unit/Framework/MockObject/TestDoubleTestCase.php @@ -10,13 +10,21 @@ namespace PHPUnit\Framework\MockObject; use Exception; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; +use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\Ticket; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\MockObject\ExtendableClassCallingMethodInDestructor; use PHPUnit\TestFixture\MockObject\ExtendableClassWithCloneMethod; +use PHPUnit\TestFixture\MockObject\ExtendableClassWithPropertyWithGetHook; +use PHPUnit\TestFixture\MockObject\ExtendableReadonlyClassWithCloneMethod; use PHPUnit\TestFixture\MockObject\InterfaceWithMethodThatExpectsObject; +use PHPUnit\TestFixture\MockObject\InterfaceWithMethodThatHasDefaultParameterValues; use PHPUnit\TestFixture\MockObject\InterfaceWithNeverReturningMethod; +use PHPUnit\TestFixture\MockObject\InterfaceWithPropertyWithGetHook; use PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration; +use PHPUnit\TestFixture\MockObject\Issue6174; use stdClass; abstract class TestDoubleTestCase extends TestCase @@ -35,30 +43,6 @@ final public function testMethodReturnsGeneratedValueWhenReturnValueGenerationIs $this->assertFalse($double->doSomething()); } - #[TestDox('__toString() method returns empty string when return value generation is disabled and no return value is configured')] - #[IgnorePhpunitDeprecations] - final public function testToStringMethodReturnsEmptyStringWhenReturnValueGenerationIsDisabledAndNoReturnValueIsConfigured(): void - { - $double = $this->getMockBuilder(InterfaceWithReturnTypeDeclaration::class) - ->disableAutoReturnValueGeneration() - ->getMock(); - - $this->assertSame('', $double->__toString()); - } - - #[IgnorePhpunitDeprecations] - final public function testMethodDoesNotReturnValueWhenReturnValueGenerationIsDisabledAndNoReturnValueIsConfigured(): void - { - $double = $this->getMockBuilder(InterfaceWithReturnTypeDeclaration::class) - ->disableAutoReturnValueGeneration() - ->getMock(); - - $this->expectException(ReturnValueNotConfiguredException::class); - $this->expectExceptionMessage('No return value is configured for ' . InterfaceWithReturnTypeDeclaration::class . '::doSomething() and return value generation is disabled'); - - $double->doSomething(); - } - final public function testMethodReturnsConfiguredValueWhenReturnValueIsConfigured(): void { $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); @@ -88,20 +72,6 @@ public function testObjectsPassedAsArgumentAreNotClonedByDefault(): void $this->assertSame($object, $double->doSomething($object)); } - #[IgnorePhpunitDeprecations] - public function testCloningOfObjectsPassedAsArgumentCanBeEnabled(): void - { - $object = new stdClass; - - $double = $this->getMockBuilder(InterfaceWithMethodThatExpectsObject::class) - ->enableArgumentCloning() - ->getMock(); - - $double->method('doSomething')->willReturnArgument(0); - - $this->assertNotSame($object, $double->doSomething($object)); - } - final public function testMethodCanBeConfiguredToReturnOneOfItsArguments(): void { $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); @@ -141,6 +111,70 @@ final public function testMethodCanBeConfiguredToReturnValuesBasedOnArgumentMapp $this->assertSame(4, $double->doSomethingElse(3)); } + final public function testMethodWithDefaultParameterValuesCanBeConfiguredToReturnValuesBasedOnArgumentMapping(): void + { + $double = $this->createTestDouble(InterfaceWithMethodThatHasDefaultParameterValues::class); + + $double->method('doSomething')->willReturnMap([[1, 2, 3], [4, 5, 6]]); + + $this->assertSame(3, $double->doSomething(1, 2)); + $this->assertSame(6, $double->doSomething(4, 5)); + } + + final public function testMethodWithDefaultParameterValuesCanBeConfiguredToReturnValuesBasedOnArgumentMappingThatOmitsDefaultValues(): void + { + $double = $this->createTestDouble(InterfaceWithMethodThatHasDefaultParameterValues::class); + + $double->method('doSomething')->willReturnMap([[1, 2], [3, 4]]); + + $this->assertSame(2, $double->doSomething(1)); + $this->assertSame(4, $double->doSomething(3)); + } + + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/6174')] + final public function testIssue6174(): void + { + $double = $this->createTestDouble(Issue6174::class); + + $double->method('methodNullDefault')->willReturnMap( + [ + ['A', null, 'result'], + ], + ); + + $this->assertSame('result', $double->methodNullDefault('A')); + + $double = $this->createTestDouble(Issue6174::class); + + $double->method('methodNullDefault')->willReturnMap( + [ + ['A', 'result'], + ], + ); + + $this->assertSame('result', $double->methodNullDefault('A')); + + $double = $this->createTestDouble(Issue6174::class); + + $double->method('methodStringDefault')->willReturnMap( + [ + ['A', 'result'], + ], + ); + + $this->assertSame('result', $double->methodStringDefault('A')); + + $double = $this->createTestDouble(Issue6174::class); + + $double->method('methodStringDefault')->willReturnMap( + [ + [null, 'result'], + ], + ); + + $this->assertSame('result', $double->methodStringDefault(null)); + } + final public function testMethodCanBeConfiguredToReturnValuesUsingCallback(): void { $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); @@ -171,6 +205,21 @@ final public function testMethodCanBeConfiguredToReturnDifferentValuesOnConsecut $this->assertTrue($double->doSomething()); } + final public function testMethodConfiguredToReturnDifferentValuesOnConsecutiveCallsCannotBeCalledMoreOftenThanReturnValuesHaveBeenConfigured(): void + { + $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); + + $double->method('doSomething')->willReturn(false, true); + + $this->assertFalse($double->doSomething()); + $this->assertTrue($double->doSomething()); + + $this->expectException(NoMoreReturnValuesConfiguredException::class); + $this->expectExceptionMessage('Only 2 return values have been configured for PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration::doSomething()'); + + $double->doSomething(); + } + final public function testMethodCanBeConfiguredToReturnDifferentValuesAndThrowExceptionsOnConsecutiveCalls(): void { $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); @@ -226,23 +275,63 @@ final public function testOriginalCloneMethodIsNotCalledByDefaultWhenTestDoubleO $this->assertFalse($double->doSomething()); } - #[TestDox('Original __clone() method can optionally be called when test double object is cloned')] - final public function testOriginalCloneMethodCanOptionallyBeCalledWhenTestDoubleObjectIsCloned(): void + #[TestDox('Original __clone() method is not called by default when test double object is cloned (readonly class)')] + final public function testOriginalCloneMethodIsNotCalledByDefaultWhenTestDoubleObjectOfReadonlyClassIsCloned(): void { - $double = $this->getMockBuilder(ExtendableClassWithCloneMethod::class)->enableOriginalClone()->getMock(); + $double = clone $this->createTestDouble(ExtendableReadonlyClassWithCloneMethod::class); - $this->expectException(Exception::class); - $this->expectExceptionMessage(ExtendableClassWithCloneMethod::class . '::__clone'); + $this->assertFalse($double->doSomething()); + } + + public function testMethodNameCanOnlyBeConfiguredOnce(): void + { + $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); + + $this->expectException(MethodNameAlreadyConfiguredException::class); + + $double + ->method('doSomething') + ->method('doSomething') + ->willReturn(true); + } + + #[Ticket('https://github.com/sebastianbergmann/phpunit/issues/5874')] + public function testDoubledMethodsCanBeCalledFromDestructorOnTestDoubleCreatedByTheReturnValueGenerator(): void + { + $double = $this->createTestDouble(ExtendableClassCallingMethodInDestructor::class); + + $this->assertInstanceOf( + ExtendableClassCallingMethodInDestructor::class, + $double->doSomething(), + ); + } + + #[RequiresPhp('^8.4')] + public function testGetHookForPropertyOfInterfaceCanBeConfigured(): void + { + $double = $this->createTestDouble(InterfaceWithPropertyWithGetHook::class); + + $double->method(PropertyHook::get('property'))->willReturn('value'); + + $this->assertSame('value', $double->property); + } + + #[RequiresPhp('^8.4')] + public function testGetHookForPropertyOfExtendableClassCanBeConfigured(): void + { + $double = $this->createTestDouble(ExtendableClassWithPropertyWithGetHook::class); + + $double->method(PropertyHook::get('property'))->willReturn('value'); - clone $double; + $this->assertSame('value', $double->property); } /** - * @psalm-template RealInstanceType of object + * @template RealInstanceType of object * - * @psalm-param class-string $type + * @param class-string $type * - * @psalm-return (Stub|MockObject)&RealInstanceType + * @return (MockObject|Stub)&RealInstanceType */ abstract protected function createTestDouble(string $type): object; } diff --git a/tests/unit/Framework/TestCaseTest.php b/tests/unit/Framework/TestCaseTest.php index 2165fb3a754..fe940bfb802 100644 --- a/tests/unit/Framework/TestCaseTest.php +++ b/tests/unit/Framework/TestCaseTest.php @@ -45,7 +45,7 @@ public static function tearDownAfterClass(): void $_SERVER['f'], $_FILES['g'], $_REQUEST['h'], - $GLOBALS['i'] + $GLOBALS['i'], ); } diff --git a/tests/unit/Framework/TestRunner/ChildProcessResultProcessorTest.php b/tests/unit/Framework/TestRunner/ChildProcessResultProcessorTest.php new file mode 100644 index 00000000000..ee64fd34aab --- /dev/null +++ b/tests/unit/Framework/TestRunner/ChildProcessResultProcessorTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Event\Emitter; +use PHPUnit\Event\Facade; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\TestFixture\Success; +use PHPUnit\TestRunner\TestResult\PassedTests; + +#[CoversClass(ChildProcessResultProcessor::class)] +#[Small] +final class ChildProcessResultProcessorTest extends TestCase +{ + #[TestDox('Emits Test\Errored event when standard output is not empty')] + public function testEmitsErrorEventWhenStderrIsNotEmpty(): void + { + $emitter = $this->createMock(Emitter::class); + + $emitter + ->expects($this->once()) + ->method('testErrored'); + + $this->processor($emitter)->process(new Success('testOne'), '', 'error'); + } + + #[TestDox('Emits Test\Errored event when process result cannot be unserialized')] + public function testEmitsErrorEventWhenProcessResultCannotBeUnserialized(): void + { + $emitter = $this->createMock(Emitter::class); + + $emitter + ->expects($this->once()) + ->method('testErrored'); + + $this->processor($emitter)->process(new Success('testOne'), '', ''); + } + + private function processor(Emitter $emitter): ChildProcessResultProcessor + { + return new ChildProcessResultProcessor( + new Facade, + $emitter, + new PassedTests, + new CodeCoverage, + ); + } +} diff --git a/tests/unit/Framework/TestSizeTest.php b/tests/unit/Framework/TestSizeTest.php index fe89a854fae..4632c66bb2b 100644 --- a/tests/unit/Framework/TestSizeTest.php +++ b/tests/unit/Framework/TestSizeTest.php @@ -10,16 +10,13 @@ namespace PHPUnit\Framework\TestSize; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -#[CoversClass(Known::class)] -#[CoversClass(Large::class)] -#[CoversClass(Medium::class)] -#[CoversClass(\PHPUnit\Framework\TestSize\Small::class)] #[CoversClass(TestSize::class)] -#[CoversClass(Unknown::class)] +#[CoversClassesThatExtendClass(TestSize::class)] #[Small] final class TestSizeTest extends TestCase { diff --git a/tests/unit/Framework/TestStatusTest.php b/tests/unit/Framework/TestStatusTest.php index 788472d5b92..583c71adf90 100644 --- a/tests/unit/Framework/TestStatusTest.php +++ b/tests/unit/Framework/TestStatusTest.php @@ -10,21 +10,12 @@ namespace PHPUnit\Framework\TestStatus; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; -#[CoversClass(Deprecation::class)] -#[CoversClass(Error::class)] -#[CoversClass(Failure::class)] -#[CoversClass(Incomplete::class)] -#[CoversClass(Known::class)] -#[CoversClass(Notice::class)] -#[CoversClass(Risky::class)] -#[CoversClass(Skipped::class)] -#[CoversClass(Success::class)] #[CoversClass(TestStatus::class)] -#[CoversClass(Unknown::class)] -#[CoversClass(Warning::class)] +#[CoversClassesThatExtendClass(TestStatus::class)] #[Small] final class TestStatusTest extends TestCase { diff --git a/tests/unit/Framework/TestSuiteTest.php b/tests/unit/Framework/TestSuiteTest.php index 9eefb1f2c97..bb90995cf40 100644 --- a/tests/unit/Framework/TestSuiteTest.php +++ b/tests/unit/Framework/TestSuiteTest.php @@ -11,10 +11,12 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; +use PHPUnit\TestFixture\AbstractTestCase; use PHPUnit\TestFixture\DependencyFailureTest; use PHPUnit\TestFixture\DependencyOnClassTest; use PHPUnit\TestFixture\DependencySuccessTest; use PHPUnit\TestFixture\MultiDependencyTest; +use PHPUnit\TestFixture\NoTestCase; use PHPUnit\TestFixture\NotPublicTestCase; use ReflectionClass; @@ -24,14 +26,14 @@ final class TestSuiteTest extends TestCase { public function testNotPublicTestCase(): void { - $suite = TestSuite::fromClassName(NotPublicTestCase::class); + $suite = TestSuite::fromClassReflector(new ReflectionClass(NotPublicTestCase::class)); $this->assertCount(1, $suite); } public function testNormalizeProvidedDependencies(): void { - $suite = TestSuite::fromClassName(MultiDependencyTest::class); + $suite = TestSuite::fromClassReflector(new ReflectionClass(MultiDependencyTest::class)); $this->assertEquals([ MultiDependencyTest::class . '::class', @@ -45,14 +47,16 @@ public function testNormalizeProvidedDependencies(): void public function testNormalizeRequiredDependencies(): void { - $suite = TestSuite::fromClassName(MultiDependencyTest::class); + $suite = TestSuite::fromClassReflector(new ReflectionClass(MultiDependencyTest::class)); $this->assertSame([], $suite->requires()); } public function testDetectMissingDependenciesBetweenTestSuites(): void { - $suite = TestSuite::fromClassName(DependencyOnClassTest::class); + $suite = TestSuite::fromClassReflector( + new ReflectionClass(DependencyOnClassTest::class), + ); $this->assertEquals([ DependencyOnClassTest::class . '::class', @@ -68,7 +72,7 @@ public function testDetectMissingDependenciesBetweenTestSuites(): void public function testResolveDependenciesBetweenTestSuites(): void { - $suite = TestSuite::fromClassName(DependencyOnClassTest::class); + $suite = TestSuite::fromClassReflector(new ReflectionClass(DependencyOnClassTest::class)); $suite->addTestSuite(new ReflectionClass(DependencyFailureTest::class)); $suite->addTestSuite(new ReflectionClass(DependencySuccessTest::class)); @@ -81,8 +85,8 @@ public function testResolveDependenciesBetweenTestSuites(): void DependencyFailureTest::class . '::testTwo', DependencyFailureTest::class . '::testThree', DependencyFailureTest::class . '::testFour', - DependencyFailureTest::class . '::testHandlesDependsAnnotationForNonexistentTests', - DependencyFailureTest::class . '::testHandlesDependsAnnotationWithNoMethodSpecified', + DependencyFailureTest::class . '::testHandlesDependencyOnTestMethodThatDoesNotExist', + DependencyFailureTest::class . '::testHandlesDependencyOnTestMethodWithEmptyName', DependencySuccessTest::class . '::class', DependencySuccessTest::class . '::testOne', DependencySuccessTest::class . '::testTwo', @@ -111,4 +115,22 @@ public function testResolverOnlyUsesSuitesAndCases(): void DependencyFailureTest::class . '::class', ], $suite->requires(), 'Required test names incorrect'); } + + public function testRejectsAbstractTestClass(): void + { + $suite = TestSuite::empty('the-test-suite'); + + $this->expectException(Exception::class); + + $suite->addTestSuite(new ReflectionClass(AbstractTestCase::class)); + } + + public function testRejectsClassThatDoesNotExtendTestClass(): void + { + $suite = TestSuite::empty('the-test-suite'); + + $this->expectException(Exception::class); + + $suite->addTestSuite(new ReflectionClass(NoTestCase::class)); + } } diff --git a/tests/unit/Logging/TestDox/NamePrettifierTest.php b/tests/unit/Logging/TestDox/NamePrettifierTest.php index c6c2b637f80..6cfabc6b0ac 100644 --- a/tests/unit/Logging/TestDox/NamePrettifierTest.php +++ b/tests/unit/Logging/TestDox/NamePrettifierTest.php @@ -13,39 +13,39 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\TestDox\TestDoxAttributeOnTestClassTest; #[CoversClass(NamePrettifier::class)] #[Group('testdox')] #[Small] final class NamePrettifierTest extends TestCase { - public function testTitleHasSensibleDefaults(): void + public function testNameOfTestClassCanBePrettified(): void { - $this->assertEquals('Foo', (new NamePrettifier)->prettifyTestClassName('FooTest')); - $this->assertEquals('Foo', (new NamePrettifier)->prettifyTestClassName('TestFoo')); - $this->assertEquals('Foo', (new NamePrettifier)->prettifyTestClassName('TestFooTest')); - $this->assertEquals('Foo (Test\Foo)', (new NamePrettifier)->prettifyTestClassName('Test\FooTest')); - $this->assertEquals('Foo (Tests\Foo)', (new NamePrettifier)->prettifyTestClassName('Tests\FooTest')); - $this->assertEquals('Unnamed Tests', (new NamePrettifier)->prettifyTestClassName('TestTest')); - $this->assertEquals('Système Testé', (new NamePrettifier)->prettifyTestClassName('SystèmeTestéTest')); - $this->assertEquals('Expression Évaluée', (new NamePrettifier)->prettifyTestClassName('ExpressionÉvaluéeTest')); + $this->assertSame('Foo', (new NamePrettifier)->prettifyTestClassName('FooTest')); + $this->assertSame('Foo', (new NamePrettifier)->prettifyTestClassName('TestFoo')); + $this->assertSame('Foo', (new NamePrettifier)->prettifyTestClassName('TestsFoo')); + $this->assertSame('Foo', (new NamePrettifier)->prettifyTestClassName('TestFooTest')); + $this->assertSame('Foo (Test\Foo)', (new NamePrettifier)->prettifyTestClassName('Test\FooTest')); + $this->assertSame('Foo (Tests\Foo)', (new NamePrettifier)->prettifyTestClassName('Tests\FooTest')); + $this->assertSame('Unnamed Tests', (new NamePrettifier)->prettifyTestClassName('TestTest')); + $this->assertSame('Système Testé', (new NamePrettifier)->prettifyTestClassName('SystèmeTestéTest')); + $this->assertSame('Expression Évaluée', (new NamePrettifier)->prettifyTestClassName('ExpressionÉvaluéeTest')); + $this->assertSame('Custom Title', (new NamePrettifier)->prettifyTestClassName(TestDoxAttributeOnTestClassTest::class)); } - public function testTestNameIsConvertedToASentence(): void + public function testNameOfTestMethodCanBePrettified(): void { - $this->assertEquals('This is a test', (new NamePrettifier)->prettifyTestMethodName('testThisIsATest')); - $this->assertEquals('This is a test', (new NamePrettifier)->prettifyTestMethodName('testThisIsATest2')); - $this->assertEquals('This is a test', (new NamePrettifier)->prettifyTestMethodName('this_is_a_test')); - $this->assertEquals('This is a test', (new NamePrettifier)->prettifyTestMethodName('test_this_is_a_test')); - $this->assertEquals('Foo for bar is 0', (new NamePrettifier)->prettifyTestMethodName('testFooForBarIs0')); - $this->assertEquals('Foo for baz is 1', (new NamePrettifier)->prettifyTestMethodName('testFooForBazIs1')); - $this->assertEquals('This has a 123 in its name', (new NamePrettifier)->prettifyTestMethodName('testThisHasA123InItsName')); - $this->assertEquals('', (new NamePrettifier)->prettifyTestMethodName('test')); - } - - public function testTestNameIsNotGroupedWhenNotInSequence(): void - { - $this->assertEquals('Sets redirect header on 301', (new NamePrettifier)->prettifyTestMethodName('testSetsRedirectHeaderOn301')); - $this->assertEquals('Sets redirect header on 302', (new NamePrettifier)->prettifyTestMethodName('testSetsRedirectHeaderOn302')); + $this->assertSame('', (new NamePrettifier)->prettifyTestMethodName('')); + $this->assertSame('', (new NamePrettifier)->prettifyTestMethodName('test')); + $this->assertSame('This is a test', (new NamePrettifier)->prettifyTestMethodName('testThisIsATest')); + $this->assertSame('This is a test', (new NamePrettifier)->prettifyTestMethodName('testThisIsATest2')); + $this->assertSame('This is a test', (new NamePrettifier)->prettifyTestMethodName('this_is_a_test')); + $this->assertSame('This is a test', (new NamePrettifier)->prettifyTestMethodName('test_this_is_a_test')); + $this->assertSame('Foo for bar is 0', (new NamePrettifier)->prettifyTestMethodName('testFooForBarIs0')); + $this->assertSame('Foo for baz is 1', (new NamePrettifier)->prettifyTestMethodName('testFooForBazIs1')); + $this->assertSame('This has a 123 in its name', (new NamePrettifier)->prettifyTestMethodName('testThisHasA123InItsName')); + $this->assertSame('Sets redirect header on 301', (new NamePrettifier)->prettifyTestMethodName('testSetsRedirectHeaderOn301')); + $this->assertSame('Sets redirect header on 302', (new NamePrettifier)->prettifyTestMethodName('testSetsRedirectHeaderOn302')); } } diff --git a/tests/unit/Metadata/AnnotationsAreNotSupportedForInternalClassesExceptionTest.php b/tests/unit/Metadata/AnnotationsAreNotSupportedForInternalClassesExceptionTest.php deleted file mode 100644 index a493b30e0c1..00000000000 --- a/tests/unit/Metadata/AnnotationsAreNotSupportedForInternalClassesExceptionTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; - -#[CoversClass(AnnotationsAreNotSupportedForInternalClassesException::class)] -#[Small] -final class AnnotationsAreNotSupportedForInternalClassesExceptionTest extends TestCase -{ - public function testConstructsMessageCorrectly(): void - { - $this->assertSame( - 'Annotations can only be parsed for user-defined classes, trying to parse annotations for class "the-class"', - (new AnnotationsAreNotSupportedForInternalClassesException('the-class'))->getMessage(), - ); - } -} diff --git a/tests/unit/Metadata/Api/CodeCoverageTest.php b/tests/unit/Metadata/Api/CodeCoverageTest.php index 19f7db06551..8f6ce67ac0d 100644 --- a/tests/unit/Metadata/Api/CodeCoverageTest.php +++ b/tests/unit/Metadata/Api/CodeCoverageTest.php @@ -9,425 +9,135 @@ */ namespace PHPUnit\Metadata\Api; -use function array_merge; -use function range; +use function array_shift; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\CodeCoverageException; +use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; -use PHPUnit\TestFixture\CoverageClassNothingTest; -use PHPUnit\TestFixture\CoverageClassTest; -use PHPUnit\TestFixture\CoverageClassWithoutAnnotationsTest; -use PHPUnit\TestFixture\CoverageCoversOverridesCoversNothingTest; -use PHPUnit\TestFixture\CoverageFunctionParenthesesTest; -use PHPUnit\TestFixture\CoverageFunctionParenthesesWhitespaceTest; -use PHPUnit\TestFixture\CoverageFunctionTest; -use PHPUnit\TestFixture\CoverageMethodNothingCoversMethod; -use PHPUnit\TestFixture\CoverageMethodNothingTest; -use PHPUnit\TestFixture\CoverageMethodOneLineAnnotationTest; -use PHPUnit\TestFixture\CoverageMethodParenthesesTest; -use PHPUnit\TestFixture\CoverageMethodParenthesesWhitespaceTest; -use PHPUnit\TestFixture\CoverageMethodTest; -use PHPUnit\TestFixture\CoverageNamespacedFunctionTest; -use PHPUnit\TestFixture\CoverageNoneTest; -use PHPUnit\TestFixture\InterfaceTargetTest; -use PHPUnit\TestFixture\InvalidClassTargetWithAnnotationTest; -use PHPUnit\TestFixture\InvalidClassTargetWithAttributeTest; -use PHPUnit\TestFixture\InvalidFunctionTargetTest; -use PHPUnit\TestFixture\MoreThanOneCoversDefaultClassAnnotationTest; -use PHPUnit\TestFixture\MoreThanOneUsesDefaultClassAnnotationTest; -use PHPUnit\TestFixture\NamespaceCoverageClassTest; -use PHPUnit\TestFixture\NamespaceCoverageCoversClassPublicTest; -use PHPUnit\TestFixture\NamespaceCoverageCoversClassTest; -use PHPUnit\TestFixture\NamespaceCoverageMethodTest; -use PHPUnit\TestFixture\Test3194; +use PHPUnit\TestFixture\CoversClassOnClassTest; +use PHPUnit\TestFixture\CoversNothingOnClassTest; +use PHPUnit\TestFixture\CoversNothingOnMethodTest; +use PHPUnit\TestFixture\Metadata\Attribute\CoversTest; +use PHPUnit\TestFixture\Metadata\Attribute\UsesTest; +use PHPUnit\TestFixture\NoCoverageAttributesTest; +use SebastianBergmann\CodeCoverage\Test\Target\Class_; +use SebastianBergmann\CodeCoverage\Test\Target\ClassesThatExtendClass; +use SebastianBergmann\CodeCoverage\Test\Target\ClassesThatImplementInterface; +use SebastianBergmann\CodeCoverage\Test\Target\Function_; +use SebastianBergmann\CodeCoverage\Test\Target\Method; +use SebastianBergmann\CodeCoverage\Test\Target\Namespace_; +use SebastianBergmann\CodeCoverage\Test\Target\Trait_; #[CoversClass(CodeCoverage::class)] #[Small] +#[Group('metadata')] final class CodeCoverageTest extends TestCase { - public static function linesToBeCoveredProvider(): array + /** + * @return non-empty-list + */ + public static function canSkipCoverageProvider(): array { return [ - [ - [], - CoverageNoneTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(29, 46), - ], - CoverageClassTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodOneLineAnnotationTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'CoveredFunction.php' => range(10, 12), - ], - CoverageFunctionTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => range(29, 46), - ], - NamespaceCoverageClassTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => range(31, 35), - ], - NamespaceCoverageMethodTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => array_merge(range(43, 45), range(37, 41), range(31, 35), range(24, 26), range(19, 22), range(14, 17)), - ], - NamespaceCoverageCoversClassTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => range(31, 35), - ], - NamespaceCoverageCoversClassPublicTest::class, - 'testSomething', - ], + [false, NoCoverageAttributesTest::class], + [false, CoversClassOnClassTest::class], + [true, CoversNothingOnClassTest::class], + [true, CoversNothingOnMethodTest::class], + ]; + } - [ - false, - CoverageClassNothingTest::class, - 'testSomething', - ], + #[TestDox('Maps #[Covers*()] metadata to phpunit/php-code-coverage TargetCollection')] + public function testMapsCoversMetadataToCodeCoverageTargetCollection(): void + { + $targets = (new CodeCoverage)->coversTargets(CoversTest::class, 'testOne'); - [ - false, - CoverageMethodNothingTest::class, - 'testSomething', - ], + $this->assertNotFalse($targets); + $this->assertCount(7, $targets); - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageCoversOverridesCoversNothingTest::class, - 'testSomething', - ], + $targets = $targets->asArray(); - [ - false, - CoverageMethodNothingCoversMethod::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Namespace_::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute', $target->namespace()); - [ - [ - TEST_FILES_PATH . 'CoveredFunction.php' => range(10, 12), - ], - CoverageFunctionParenthesesTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Class_::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->className()); - [ - [ - TEST_FILES_PATH . 'CoveredFunction.php' => range(10, 12), - ], - CoverageFunctionParenthesesWhitespaceTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(ClassesThatExtendClass::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->className()); - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodParenthesesTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(ClassesThatImplementInterface::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->interfaceName()); - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodParenthesesWhitespaceTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Trait_::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait', $target->traitName()); - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredFunction.php' => range(12, 15), - ], - CoverageNamespacedFunctionTest::class, - 'testFunc', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Method::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->className()); + $this->assertSame('method', $target->methodName()); - [ - [ - TEST_FILES_PATH . '3194.php' => array_merge(range(14, 20), range(22, 30)), - ], - Test3194::class, - 'testOne', - ], - ]; + $target = array_shift($targets); + $this->assertInstanceOf(Function_::class, $target); + $this->assertSame('f', $target->functionName()); } - public static function linesToBeUsedProvider(): array + #[TestDox('Maps #[Uses*()] metadata to phpunit/php-code-coverage TargetCollection')] + public function testMapsUsesMetadataToCodeCoverageTargetCollection(): void { - return [ - [ - [], - CoverageNoneTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(29, 46), - ], - CoverageClassTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodTest::class, - 'testSomething', - ], + $targets = (new CodeCoverage)->usesTargets(UsesTest::class, 'testOne'); - [ - [ - TEST_FILES_PATH . 'CoveredFunction.php' => range(10, 12), - ], - CoverageFunctionTest::class, - 'testSomething', - ], + $this->assertNotFalse($targets); + $this->assertCount(7, $targets); - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => range(29, 46), - ], - NamespaceCoverageClassTest::class, - 'testSomething', - ], + $targets = $targets->asArray(); - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => range(31, 35), - ], - NamespaceCoverageMethodTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Namespace_::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute', $target->namespace()); - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => array_merge(range(43, 45), range(37, 41), range(31, 35), range(24, 26), range(19, 22), range(14, 17)), - ], - NamespaceCoverageCoversClassTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Class_::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->className()); - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredClass.php' => range(31, 35), - ], - NamespaceCoverageCoversClassPublicTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(ClassesThatExtendClass::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->className()); - [ - [ - TEST_FILES_PATH . 'CoveredFunction.php' => range(10, 12), - ], - CoverageFunctionParenthesesTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(ClassesThatImplementInterface::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->interfaceName()); - [ - [ - TEST_FILES_PATH . 'CoveredFunction.php' => range(10, 12), - ], - CoverageFunctionParenthesesWhitespaceTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Trait_::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait', $target->traitName()); - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodParenthesesTest::class, - 'testSomething', - ], + $target = array_shift($targets); + $this->assertInstanceOf(Method::class, $target); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\Example', $target->className()); + $this->assertSame('method', $target->methodName()); - [ - [ - TEST_FILES_PATH . 'CoveredClass.php' => range(31, 35), - ], - CoverageMethodParenthesesWhitespaceTest::class, - 'testSomething', - ], - - [ - [ - TEST_FILES_PATH . 'NamespaceCoveredFunction.php' => range(12, 15), - ], - CoverageNamespacedFunctionTest::class, - 'testFunc', - ], - ]; - } - - public static function canSkipCoverageProvider(): array - { - return [ - [CoverageClassTest::class, false], - [CoverageClassWithoutAnnotationsTest::class, false], - [CoverageCoversOverridesCoversNothingTest::class, false], - [CoverageClassNothingTest::class, true], - [CoverageMethodNothingTest::class, true], - ]; - } - - /** - * @psalm-param class-string $className - */ - #[DataProvider('linesToBeCoveredProvider')] - public function testLinesToBeCoveredCanBeDetermined(array|false $expected, string $className, string $methodName): void - { - $this->assertEqualsCanonicalizing( - $expected, - (new CodeCoverage)->linesToBeCovered( - $className, - $methodName, - ), - ); - } - - /** - * @psalm-param class-string $className - */ - #[DataProvider('linesToBeUsedProvider')] - public function testLinesToBeUsedCanBeDetermined(array|false $expected, string $className, string $methodName): void - { - $this->assertEqualsCanonicalizing( - $expected, - (new CodeCoverage)->linesToBeUsed( - $className, - $methodName, - ), - ); + $target = array_shift($targets); + $this->assertInstanceOf(Function_::class, $target); + $this->assertSame('f', $target->functionName()); } /** - * @psalm-param class-string $testCase + * @param class-string $testCase */ #[DataProvider('canSkipCoverageProvider')] - public function testWhetherCollectionOfCodeCoverageDataCanBeSkippedCanBeDetermined(string $testCase, bool $expectedCanSkip): void + public function testWhetherCollectionOfCodeCoverageDataCanBeSkippedCanBeDetermined(bool $expected, string $testCase): void { $test = new $testCase('testSomething'); $coverageRequired = (new CodeCoverage)->shouldCodeCoverageBeCollectedFor($test::class, $test->name()); $canSkipCoverage = !$coverageRequired; - $this->assertEquals($expectedCanSkip, $canSkipCoverage); - } - - #[\PHPUnit\Framework\Attributes\TestDox('Rejects more than one @coversDefaultClass annotation')] - public function testRejectsMoreThanOneCoversDefaultClassAnnotation(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('More than one @coversDefaultClass annotation for class'); - - (new CodeCoverage)->linesToBeCovered(MoreThanOneCoversDefaultClassAnnotationTest::class, 'testOne'); - } - - #[\PHPUnit\Framework\Attributes\TestDox('More than one @usesDefaultClass annotation')] - public function testRejectsMoreThanOneUsesDefaultClassAnnotation(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('More than one @usesDefaultClass annotation for class'); - - (new CodeCoverage)->linesToBeUsed(MoreThanOneUsesDefaultClassAnnotationTest::class, 'testOne'); - } - - public function testRejectsInterfaceClassTarget(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('Trying to @cover interface "\Throwable".'); - - (new CodeCoverage)->linesToBeCovered(InterfaceTargetTest::class, 'testOne'); - } - - public function testRejectsInvalidCoversClassTargetWithAttribute(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('Class "InvalidClass" is not a valid target for code coverage'); - - (new CodeCoverage)->linesToBeCovered(InvalidClassTargetWithAttributeTest::class, 'testOne'); - } - - public function testRejectsInvalidUsesClassTargetWithAttribute(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('Class "InvalidClass" is not a valid target for code coverage'); - - (new CodeCoverage)->linesToBeUsed(InvalidClassTargetWithAttributeTest::class, 'testOne'); - } - - public function testRejectsInvalidCoversClassTargetWithAnnotation(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('"@covers InvalidClass" is invalid'); - - (new CodeCoverage)->linesToBeCovered(InvalidClassTargetWithAnnotationTest::class, 'testOne'); - } - - public function testRejectsInvalidUsesClassTargetWithAnnotation(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('"@uses InvalidClass" is invalid'); - - (new CodeCoverage)->linesToBeUsed(InvalidClassTargetWithAnnotationTest::class, 'testOne'); - } - - public function testRejectsInvalidCoversFunctionTarget(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('Function "::invalid_function" is not a valid target for code coverage'); - - (new CodeCoverage)->linesToBeCovered(InvalidFunctionTargetTest::class, 'testOne'); - } - - public function testRejectsInvalidUsesFunctionTarget(): void - { - $this->expectException(CodeCoverageException::class); - $this->expectExceptionMessage('Function "::invalid_function" is not a valid target for code coverage'); - - (new CodeCoverage)->linesToBeUsed(InvalidFunctionTargetTest::class, 'testOne'); + $this->assertSame($expected, $canSkipCoverage); } } diff --git a/tests/unit/Metadata/Api/DataProviderTest.php b/tests/unit/Metadata/Api/DataProviderTest.php index ae3056b86cd..192319609ef 100644 --- a/tests/unit/Metadata/Api/DataProviderTest.php +++ b/tests/unit/Metadata/Api/DataProviderTest.php @@ -10,15 +10,19 @@ namespace PHPUnit\Metadata\Api; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\InvalidDataProviderException; use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\DuplicateKeyDataProvidersTest; use PHPUnit\TestFixture\DuplicateKeyDataProviderTest; use PHPUnit\TestFixture\MultipleDataProviderTest; +use PHPUnit\TestFixture\TestWithAttributeDataProviderTest; use PHPUnit\TestFixture\VariousIterableDataProviderTest; #[CoversClass(DataProvider::class)] #[Small] +#[Group('metadata')] final class DataProviderTest extends TestCase { /** @@ -80,7 +84,6 @@ public function testWithVariousIterableDataProvidersFromParent(): void ['P'], ['Q'], ['R'], - ], $dataSets); } @@ -98,7 +101,6 @@ public function testWithVariousIterableDataProvidersInParent(): void ['P'], ['Q'], ['R'], - ], $dataSets); } @@ -116,7 +118,6 @@ public function testWithVariousIterableAbstractDataProviders(): void ['Y'], ['Z'], ['P'], - ], $dataSets); } @@ -154,7 +155,7 @@ public function testWithVariousIterableNonStaticDataProviders(): void ], $dataSets); } - public function testWithDuplicateKeyDataProviders(): void + public function testWithDuplicateKeyDataProvider(): void { $this->expectException(InvalidDataProviderException::class); $this->expectExceptionMessage('The key "foo" has already been defined by a previous data provider'); @@ -162,4 +163,34 @@ public function testWithDuplicateKeyDataProviders(): void /* @noinspection UnusedFunctionResultInspection */ (new DataProvider)->providedData(DuplicateKeyDataProviderTest::class, 'test'); } + + public function testTestWithAttribute(): void + { + $dataSets = (new DataProvider)->providedData(TestWithAttributeDataProviderTest::class, 'testWithAttribute'); + + $this->assertSame([ + 'foo' => ['a', 'b'], + 'bar' => ['c', 'd'], + 0 => ['e', 'f'], + 1 => ['g', 'h'], + ], $dataSets); + } + + public function testTestWithAttributeWithDuplicateKey(): void + { + $this->expectException(InvalidDataProviderException::class); + $this->expectExceptionMessage('The key "foo" has already been defined by a previous TestWith attribute'); + + /* @noinspection UnusedFunctionResultInspection */ + (new DataProvider)->providedData(TestWithAttributeDataProviderTest::class, 'testWithDuplicateName'); + } + + public function testWithDuplicateKeyDataProviders(): void + { + $this->expectException(InvalidDataProviderException::class); + $this->expectExceptionMessage('The key "bar" has already been defined by a previous data provider'); + + /* @noinspection UnusedFunctionResultInspection */ + (new DataProvider)->providedData(DuplicateKeyDataProvidersTest::class, 'test'); + } } diff --git a/tests/unit/Metadata/Api/GroupsTest.php b/tests/unit/Metadata/Api/GroupsTest.php index 295f037efd5..08ff574e84c 100644 --- a/tests/unit/Metadata/Api/GroupsTest.php +++ b/tests/unit/Metadata/Api/GroupsTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; use PHPUnit\TestFixture\LargeGroupAttributesTest; @@ -21,6 +22,7 @@ #[CoversClass(Groups::class)] #[Small] +#[Group('metadata')] final class GroupsTest extends TestCase { public static function provider(): array @@ -28,7 +30,6 @@ public static function provider(): array return [ [ [ - 'default', ], NoGroupsMetadataTest::class, 'testOne', diff --git a/tests/unit/Metadata/Api/HookMethodsTest.php b/tests/unit/Metadata/Api/HookMethodsTest.php index 5f8e0ef7497..f912b50205a 100644 --- a/tests/unit/Metadata/Api/HookMethodsTest.php +++ b/tests/unit/Metadata/Api/HookMethodsTest.php @@ -9,38 +9,32 @@ */ namespace PHPUnit\Metadata\Api; +use function array_keys; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\HookMethod; +use PHPUnit\Runner\HookMethodCollection; +use PHPUnit\TestFixture\TestWithHookMethodsPrioritizedTest; use PHPUnit\TestFixture\TestWithHookMethodsTest; use PHPUnit\TestFixture\TestWithoutHookMethodsTest; #[CoversClass(HookMethods::class)] #[Small] +#[Group('metadata')] final class HookMethodsTest extends TestCase { public function testReturnsDefaultHookMethodsForClassThatDoesNotExist(): void { - $this->assertSame( + $this->assertEquals( [ - 'beforeClass' => [ - 'setUpBeforeClass', - ], - 'before' => [ - 'setUp', - ], - 'preCondition' => [ - 'assertPreConditions', - ], - 'postCondition' => [ - 'assertPostConditions', - ], - 'after' => [ - 'tearDown', - ], - 'afterClass' => [ - 'tearDownAfterClass', - ], + 'beforeClass' => HookMethodCollection::defaultBeforeClass(), + 'before' => HookMethodCollection::defaultBefore(), + 'preCondition' => HookMethodCollection::defaultPreCondition(), + 'postCondition' => HookMethodCollection::defaultPostCondition(), + 'after' => HookMethodCollection::defaultAfter(), + 'afterClass' => HookMethodCollection::defaultAfterClass(), ], (new HookMethods)->hookMethods('does not exist'), ); @@ -48,26 +42,14 @@ public function testReturnsDefaultHookMethodsForClassThatDoesNotExist(): void public function testReturnsDefaultHookMethodsInTestClassWithoutHookMethods(): void { - $this->assertSame( + $this->assertEquals( [ - 'beforeClass' => [ - 'setUpBeforeClass', - ], - 'before' => [ - 'setUp', - ], - 'preCondition' => [ - 'assertPreConditions', - ], - 'postCondition' => [ - 'assertPostConditions', - ], - 'after' => [ - 'tearDown', - ], - 'afterClass' => [ - 'tearDownAfterClass', - ], + 'beforeClass' => HookMethodCollection::defaultBeforeClass(), + 'before' => HookMethodCollection::defaultBefore(), + 'preCondition' => HookMethodCollection::defaultPreCondition(), + 'postCondition' => HookMethodCollection::defaultPostCondition(), + 'after' => HookMethodCollection::defaultAfter(), + 'afterClass' => HookMethodCollection::defaultAfterClass(), ], (new HookMethods)->hookMethods(TestWithoutHookMethodsTest::class), ); @@ -75,40 +57,61 @@ public function testReturnsDefaultHookMethodsInTestClassWithoutHookMethods(): vo public function testFindsHookMethodsInTestClassWithHookMethods(): void { - $this->assertSame( - [ - 'beforeClass' => [ - 'beforeFirstTestWithAnnotation', - 'beforeFirstTestWithAttribute', - 'setUpBeforeClass', - ], - 'before' => [ - 'beforeEachTestWithAnnotation', - 'beforeEachTestWithAttribute', - 'setUp', - ], - 'preCondition' => [ - 'preConditionsWithAnnotation', - 'preConditionsWithAttribute', - 'assertPreConditions', - ], - 'postCondition' => [ - 'assertPostConditions', - 'postConditionsWithAttribute', - 'postConditionsWithAnnotation', - ], - 'after' => [ - 'tearDown', - 'afterEachTestWithAttribute', - 'afterEachTestWithAnnotation', - ], - 'afterClass' => [ - 'tearDownAfterClass', - 'afterLastTestWithAttribute', - 'afterLastTestWithAnnotation', - ], - ], - (new HookMethods)->hookMethods(TestWithHookMethodsTest::class), - ); + $hookMethods = (new HookMethods)->hookMethods(TestWithHookMethodsTest::class); + $this->assertSame(['beforeClass', 'before', 'preCondition', 'postCondition', 'after', 'afterClass'], array_keys($hookMethods)); + + $beforeClassHooks = HookMethodCollection::defaultBeforeClass(); + $beforeClassHooks->add(new HookMethod('beforeFirstTestWithAttribute', 0)); + $this->assertEquals($beforeClassHooks, $hookMethods['beforeClass']); + + $beforeHooks = HookMethodCollection::defaultBefore(); + $beforeHooks->add(new HookMethod('beforeEachTestWithAttribute', 0)); + $this->assertEquals($beforeHooks, $hookMethods['before']); + + $preConditionHooks = HookMethodCollection::defaultPreCondition(); + $preConditionHooks->add(new HookMethod('preConditionsWithAttribute', 0)); + $this->assertEquals($preConditionHooks, $hookMethods['preCondition']); + + $postConditionHooks = HookMethodCollection::defaultPostCondition(); + $postConditionHooks->add(new HookMethod('postConditionsWithAttribute', 0)); + $this->assertEquals($postConditionHooks, $hookMethods['postCondition']); + + $afterHooks = HookMethodCollection::defaultAfter(); + $afterHooks->add(new HookMethod('afterEachTestWithAttribute', 0)); + $this->assertEquals($afterHooks, $hookMethods['after']); + + $afterClassHooks = HookMethodCollection::defaultAfterClass(); + $afterClassHooks->add(new HookMethod('afterLastTestWithAttribute', 0)); + $this->assertEquals($afterClassHooks, $hookMethods['afterClass']); + } + + public function testFindsHookMethodsInTestClassWithHookMethodsPrioritized(): void + { + $hookMethods = (new HookMethods)->hookMethods(TestWithHookMethodsPrioritizedTest::class); + $this->assertSame(['beforeClass', 'before', 'preCondition', 'postCondition', 'after', 'afterClass'], array_keys($hookMethods)); + + $beforeClassHooks = HookMethodCollection::defaultBeforeClass(); + $beforeClassHooks->add(new HookMethod('beforeFirstTest', 1)); + $this->assertEquals($beforeClassHooks, $hookMethods['beforeClass']); + + $beforeHooks = HookMethodCollection::defaultBefore(); + $beforeHooks->add(new HookMethod('beforeEachTest', 2)); + $this->assertEquals($beforeHooks, $hookMethods['before']); + + $preConditionHooks = HookMethodCollection::defaultPreCondition(); + $preConditionHooks->add(new HookMethod('preConditions', 3)); + $this->assertEquals($preConditionHooks, $hookMethods['preCondition']); + + $postConditionHooks = HookMethodCollection::defaultPostCondition(); + $postConditionHooks->add(new HookMethod('postConditions', 4)); + $this->assertEquals($postConditionHooks, $hookMethods['postCondition']); + + $afterHooks = HookMethodCollection::defaultAfter(); + $afterHooks->add(new HookMethod('afterEachTest', 5)); + $this->assertEquals($afterHooks, $hookMethods['after']); + + $afterClassHooks = HookMethodCollection::defaultAfterClass(); + $afterClassHooks->add(new HookMethod('afterLastTest', 6)); + $this->assertEquals($afterClassHooks, $hookMethods['afterClass']); } } diff --git a/tests/unit/Metadata/Api/RequirementsTest.php b/tests/unit/Metadata/Api/RequirementsTest.php index 7ce48c06663..7e4acd9ead1 100644 --- a/tests/unit/Metadata/Api/RequirementsTest.php +++ b/tests/unit/Metadata/Api/RequirementsTest.php @@ -11,11 +11,14 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\RequirementsEnvironmentVariableTest; #[CoversClass(Requirements::class)] #[Small] +#[Group('metadata')] final class RequirementsTest extends TestCase { public static function missingRequirementsProvider(): array @@ -29,25 +32,25 @@ public static function missingRequirementsProvider(): array 'PHP extension testExt is required.', ]], ['testAlwaysSkip', [ - 'PHPUnit >= 1111111 is required.', + 'PHPUnit 1111111 is required.', ]], ['testAlwaysSkip2', [ - 'PHP >= 9999999 is required.', + 'PHP 9999999 is required.', ]], ['testAlwaysSkip3', [ 'Operating system DOESNOTEXIST is required.', ]], ['testAllPossibleRequirements', [ - 'PHP >= 99-dev is required.', - 'PHP extension testExtOne is required.', - 'PHP extension testExt2 is required.', - 'PHP extension testExtThree >= 2.0 is required.', - 'PHPUnit >= 99-dev is required.', + 'PHP 99-dev is required.', + 'PHPUnit 99-dev is required.', 'Operating system DOESNOTEXIST is required.', 'Operating system DOESNOTEXIST is required.', 'Function testFuncOne() is required.', 'Function testFunc2() is required.', 'Method DoesNotExist::doesNotExist() is required.', + 'PHP extension testExtOne is required.', + 'PHP extension testExt2 is required.', + 'PHP extension testExtThree 2.0 is required.', 'Setting "not_a_setting" is required to be "Off".', ]], ['testPHPVersionOperatorLessThan', [ @@ -121,9 +124,18 @@ public static function missingRequirementsProvider(): array 'PHP ^1.0 is required.', 'PHPUnit ^2.0 is required.', ]], + ['testPHPUnitExtensionRequired', [ + 'PHPUnit extension "PHPUnit\TestFixture\SomeExtension" is required.', + 'PHPUnit extension "PHPUnit\TestFixture\SomeOtherExtension" is required.', + ]], ]; } + protected function tearDown(): void + { + unset($_ENV['FOO'], $_ENV['BAR']); + } + #[DataProvider('missingRequirementsProvider')] public function testGetMissingRequirements(string $test, array $result): void { @@ -132,4 +144,19 @@ public function testGetMissingRequirements(string $test, array $result): void (new Requirements)->requirementsNotSatisfiedFor(\PHPUnit\TestFixture\RequirementsTest::class, $test), ); } + + public function testGetMissingEnvironmentVariableRequirements(): void + { + $_ENV['FOO'] = 'foo'; + $_ENV['BAR'] = ''; + + $this->assertEquals( + [ + 'Environment variable "FOO" is required to be "bar".', + 'Environment variable "BAR" is required.', + 'Environment variable "BAZ" is required.', + ], + (new Requirements)->requirementsNotSatisfiedFor(RequirementsEnvironmentVariableTest::class, 'testRequiresEnvironmentVariable'), + ); + } } diff --git a/tests/unit/Metadata/MetadataCollectionTest.php b/tests/unit/Metadata/MetadataCollectionTest.php index ba30ac3191a..4e6be492158 100644 --- a/tests/unit/Metadata/MetadataCollectionTest.php +++ b/tests/unit/Metadata/MetadataCollectionTest.php @@ -10,11 +10,13 @@ namespace PHPUnit\Metadata; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; use PHPUnit\Metadata\Version\ComparisonRequirement; use PHPUnit\Util\VersionComparisonOperator; +use stdClass; #[CoversClass(MetadataCollection::class)] #[CoversClass(MetadataCollectionIterator::class)] @@ -24,14 +26,14 @@ #[UsesClass(BackupStaticProperties::class)] #[UsesClass(Before::class)] #[UsesClass(BeforeClass::class)] -#[UsesClass(Covers::class)] -#[UsesClass(\PHPUnit\Metadata\CoversClass::class)] -#[UsesClass(CoversDefaultClass::class)] +#[UsesClass(CoversClass::class)] #[UsesClass(CoversFunction::class)] +#[UsesClass(CoversMethod::class)] #[UsesClass(CoversNothing::class)] #[UsesClass(DataProvider::class)] #[UsesClass(DependsOnClass::class)] #[UsesClass(DependsOnMethod::class)] +#[UsesClass(DisableReturnValueGenerationForTestDoubles::class)] #[UsesClass(DoesNotPerformAssertions::class)] #[UsesClass(Group::class)] #[UsesClass(Metadata::class)] @@ -45,6 +47,8 @@ #[UsesClass(RequiresPhp::class)] #[UsesClass(RequiresPhpExtension::class)] #[UsesClass(RequiresPhpunit::class)] +#[UsesClass(RequiresPhpunitExtension::class)] +#[UsesClass(RequiresEnvironmentVariable::class)] #[UsesClass(RequiresSetting::class)] #[UsesClass(RunClassInSeparateProcess::class)] #[UsesClass(RunInSeparateProcess::class)] @@ -52,11 +56,10 @@ #[UsesClass(Test::class)] #[UsesClass(TestDox::class)] #[UsesClass(TestWith::class)] -#[UsesClass(Uses::class)] -#[UsesClass(\PHPUnit\Metadata\UsesClass::class)] -#[UsesClass(UsesDefaultClass::class)] +#[UsesClass(UsesClass::class)] #[UsesClass(UsesFunction::class)] #[Small] +#[Group('metadata')] final class MetadataCollectionTest extends TestCase { public function testCanBeEmpty(): void @@ -100,8 +103,8 @@ public function testIsIterable(): void public function testCanBeMerged(): void { - $a = MetadataCollection::fromArray([Metadata::before()]); - $b = MetadataCollection::fromArray([Metadata::after()]); + $a = MetadataCollection::fromArray([Metadata::before(0)]); + $b = MetadataCollection::fromArray([Metadata::after(0)]); $c = $a->mergeWith($b); $this->assertCount(2, $c); @@ -113,8 +116,8 @@ public function test_Can_be_filtered_for_class_level_metadata(): void { $collection = MetadataCollection::fromArray( [ - Metadata::coversOnClass(''), - Metadata::coversOnMethod(''), + Metadata::coversClass(''), + Metadata::ignoreDeprecationsOnMethod(), ], ); @@ -175,12 +178,12 @@ public function test_Can_be_filtered_for_Before(): void $this->assertTrue($collection->asArray()[0]->isBefore()); } - public function test_Can_be_filtered_for_Covers(): void + public function test_Can_be_filtered_for_CoversNamespace(): void { - $collection = $this->collectionWithOneOfEach()->isCovers(); + $collection = $this->collectionWithOneOfEach()->isCoversNamespace(); $this->assertCount(1, $collection); - $this->assertTrue($collection->asArray()[0]->isCovers()); + $this->assertTrue($collection->asArray()[0]->isCoversNamespace()); } public function test_Can_be_filtered_for_CoversClass(): void @@ -191,12 +194,28 @@ public function test_Can_be_filtered_for_CoversClass(): void $this->assertTrue($collection->asArray()[0]->isCoversClass()); } - public function test_Can_be_filtered_for_CoversDefaultClass(): void + public function test_Can_be_filtered_for_CoversClassesThatExtendClass(): void + { + $collection = $this->collectionWithOneOfEach()->isCoversClassesThatExtendClass(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isCoversClassesThatExtendClass()); + } + + public function test_Can_be_filtered_for_CoversClassesThatImplementInterface(): void + { + $collection = $this->collectionWithOneOfEach()->isCoversClassesThatImplementInterface(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isCoversClassesThatImplementInterface()); + } + + public function test_Can_be_filtered_for_CoversTrait(): void { - $collection = $this->collectionWithOneOfEach()->isCoversDefaultClass(); + $collection = $this->collectionWithOneOfEach()->isCoversTrait(); $this->assertCount(1, $collection); - $this->assertTrue($collection->asArray()[0]->isCoversDefaultClass()); + $this->assertTrue($collection->asArray()[0]->isCoversTrait()); } public function test_Can_be_filtered_for_CoversFunction(): void @@ -207,6 +226,14 @@ public function test_Can_be_filtered_for_CoversFunction(): void $this->assertTrue($collection->asArray()[0]->isCoversFunction()); } + public function test_Can_be_filtered_for_CoversMethod(): void + { + $collection = $this->collectionWithOneOfEach()->isCoversMethod(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isCoversMethod()); + } + public function test_Can_be_filtered_for_CoversNothing(): void { $collection = $this->collectionWithOneOfEach()->isCoversNothing(); @@ -248,6 +275,14 @@ public function test_Can_be_filtered_for_DependsOnMethod(): void $this->assertTrue($collection->asArray()[0]->isDependsOnMethod()); } + public function test_Can_be_filtered_for_DisableReturnValueGenerationForTestDoubles(): void + { + $collection = $this->collectionWithOneOfEach()->isDisableReturnValueGenerationForTestDoubles(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isDisableReturnValueGenerationForTestDoubles()); + } + public function test_Can_be_filtered_for_DoesNotPerformAssertions(): void { $collection = $this->collectionWithOneOfEach()->isDoesNotPerformAssertions(); @@ -288,6 +323,14 @@ public function test_Can_be_filtered_for_IgnoreDeprecations(): void $this->assertTrue($collection->asArray()[0]->isIgnoreDeprecations()); } + public function test_Can_be_filtered_for_IgnorePhpunitDeprecations(): void + { + $collection = $this->collectionWithOneOfEach()->isIgnorePhpunitDeprecations(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isIgnorePhpunitDeprecations()); + } + public function test_Can_be_filtered_for_PostCondition(): void { $collection = $this->collectionWithOneOfEach()->isPostCondition(); @@ -368,6 +411,30 @@ public function test_Can_be_filtered_for_RequiresPhpunit(): void $this->assertTrue($collection->asArray()[0]->isRequiresPhpunit()); } + public function test_Can_be_filtered_for_RequiresPhpunitExtension(): void + { + $collection = $this->collectionWithOneOfEach()->isRequiresPhpunitExtension(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isRequiresPhpunitExtension()); + } + + public function test_Can_be_filtered_for_RequiresEnvironmentVariable(): void + { + $collection = $this->collectionWithOneOfEach()->isRequiresEnvironmentVariable(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isRequiresEnvironmentVariable()); + } + + public function test_Can_be_filtered_for_WithEnvironmentVariable(): void + { + $collection = $this->collectionWithOneOfEach()->isWithEnvironmentVariable(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isWithEnvironmentVariable()); + } + public function test_Can_be_filtered_for_RequiresSetting(): void { $collection = $this->collectionWithOneOfEach()->isRequiresSetting(); @@ -424,12 +491,12 @@ public function test_Can_be_filtered_for_TestWith(): void $this->assertTrue($collection->asArray()[0]->isTestWith()); } - public function test_Can_be_filtered_for_Uses(): void + public function test_Can_be_filtered_for_UsesNamespace(): void { - $collection = $this->collectionWithOneOfEach()->isUses(); + $collection = $this->collectionWithOneOfEach()->isUsesNamespace(); $this->assertCount(1, $collection); - $this->assertTrue($collection->asArray()[0]->isUses()); + $this->assertTrue($collection->asArray()[0]->isUsesNamespace()); } public function test_Can_be_filtered_for_UsesClass(): void @@ -440,12 +507,28 @@ public function test_Can_be_filtered_for_UsesClass(): void $this->assertTrue($collection->asArray()[0]->isUsesClass()); } - public function test_Can_be_filtered_for_UsesDefaultClass(): void + public function test_Can_be_filtered_for_UsesClassesThatExtendClass(): void + { + $collection = $this->collectionWithOneOfEach()->isUsesClassesThatExtendClass(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isUsesClassesThatExtendClass()); + } + + public function test_Can_be_filtered_for_UsesClassesThatImplementInterface(): void + { + $collection = $this->collectionWithOneOfEach()->isUsesClassesThatImplementInterface(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isUsesClassesThatImplementInterface()); + } + + public function test_Can_be_filtered_for_UsesTrait(): void { - $collection = $this->collectionWithOneOfEach()->isUsesDefaultClass(); + $collection = $this->collectionWithOneOfEach()->isUsesTrait(); $this->assertCount(1, $collection); - $this->assertTrue($collection->asArray()[0]->isUsesDefaultClass()); + $this->assertTrue($collection->asArray()[0]->isUsesTrait()); } public function test_Can_be_filtered_for_UsesFunction(): void @@ -456,6 +539,14 @@ public function test_Can_be_filtered_for_UsesFunction(): void $this->assertTrue($collection->asArray()[0]->isUsesFunction()); } + public function test_Can_be_filtered_for_UsesMethod(): void + { + $collection = $this->collectionWithOneOfEach()->isUsesMethod(); + + $this->assertCount(1, $collection); + $this->assertTrue($collection->asArray()[0]->isUsesMethod()); + } + public function test_Can_be_filtered_for_WithoutErrorHandler(): void { $collection = $this->collectionWithOneOfEach()->isWithoutErrorHandler(); @@ -468,27 +559,32 @@ private function collectionWithOneOfEach(): MetadataCollection { return MetadataCollection::fromArray( [ - Metadata::afterClass(), - Metadata::after(), + Metadata::afterClass(0), + Metadata::after(0), Metadata::backupGlobalsOnClass(true), Metadata::backupStaticPropertiesOnClass(true), - Metadata::beforeClass(), - Metadata::before(), - Metadata::coversOnClass(''), + Metadata::beforeClass(0), + Metadata::before(0), + Metadata::coversNamespace(''), Metadata::coversClass(''), - Metadata::coversDefaultClass(''), + Metadata::coversClassesThatExtendClass(''), + Metadata::coversClassesThatImplementInterface(''), + Metadata::coversTrait(''), Metadata::coversFunction(''), + Metadata::coversMethod('', ''), Metadata::coversNothingOnClass(), Metadata::dataProvider('', ''), Metadata::dependsOnClass('', false, false), Metadata::dependsOnMethod('', '', false, false), + Metadata::disableReturnValueGenerationForTestDoubles(), Metadata::doesNotPerformAssertionsOnClass(), Metadata::excludeGlobalVariableFromBackupOnClass(''), Metadata::excludeStaticPropertyFromBackupOnClass('', ''), Metadata::groupOnClass(''), Metadata::ignoreDeprecationsOnClass(), - Metadata::postCondition(), - Metadata::preCondition(), + Metadata::ignorePhpunitDeprecationsOnClass(), + Metadata::postCondition(0), + Metadata::preCondition(0), Metadata::preserveGlobalStateOnClass(true), Metadata::requiresMethodOnClass('', ''), Metadata::requiresFunctionOnClass(''), @@ -507,6 +603,8 @@ private function collectionWithOneOfEach(): MetadataCollection new VersionComparisonOperator('>='), ), ), + Metadata::requiresPhpunitExtensionOnClass(stdClass::class), + Metadata::requiresEnvironmentVariableOnClass('foo', 'bar'), Metadata::requiresSettingOnClass('foo', 'bar'), Metadata::runClassInSeparateProcess(), Metadata::runInSeparateProcess(), @@ -514,10 +612,14 @@ private function collectionWithOneOfEach(): MetadataCollection Metadata::testDoxOnClass(''), Metadata::test(), Metadata::testWith([]), - Metadata::usesOnClass(''), + Metadata::usesNamespace(''), Metadata::usesClass(''), - Metadata::usesDefaultClass(''), + Metadata::usesClassesThatExtendClass(''), + Metadata::usesClassesThatImplementInterface(''), + Metadata::usesTrait(''), Metadata::usesFunction(''), + Metadata::usesMethod('', ''), + Metadata::withEnvironmentVariableOnClass('foo', 'bar'), Metadata::withoutErrorHandler(), ], ); diff --git a/tests/unit/Metadata/MetadataTest.php b/tests/unit/Metadata/MetadataTest.php index fb22586a68b..f6467c5d99b 100644 --- a/tests/unit/Metadata/MetadataTest.php +++ b/tests/unit/Metadata/MetadataTest.php @@ -10,59 +10,23 @@ namespace PHPUnit\Metadata; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; use PHPUnit\Metadata\Version\ComparisonRequirement; +use PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait; use PHPUnit\Util\VersionComparisonOperator; -#[CoversClass(After::class)] -#[CoversClass(AfterClass::class)] -#[CoversClass(BackupGlobals::class)] -#[CoversClass(BackupStaticProperties::class)] -#[CoversClass(Before::class)] -#[CoversClass(BeforeClass::class)] -#[CoversClass(Covers::class)] -#[CoversClass(\PHPUnit\Metadata\CoversClass::class)] -#[CoversClass(CoversDefaultClass::class)] -#[CoversClass(CoversFunction::class)] -#[CoversClass(CoversNothing::class)] -#[CoversClass(DataProvider::class)] -#[CoversClass(DependsOnClass::class)] -#[CoversClass(DependsOnMethod::class)] -#[CoversClass(DoesNotPerformAssertions::class)] -#[CoversClass(ExcludeGlobalVariableFromBackup::class)] -#[CoversClass(ExcludeStaticPropertyFromBackup::class)] -#[CoversClass(Group::class)] -#[CoversClass(IgnoreDeprecations::class)] #[CoversClass(Metadata::class)] -#[CoversClass(PostCondition::class)] -#[CoversClass(PreCondition::class)] -#[CoversClass(PreserveGlobalState::class)] -#[CoversClass(RequiresFunction::class)] -#[CoversClass(RequiresMethod::class)] -#[CoversClass(RequiresOperatingSystem::class)] -#[CoversClass(RequiresOperatingSystemFamily::class)] -#[CoversClass(RequiresPhp::class)] -#[CoversClass(RequiresPhpExtension::class)] -#[CoversClass(RequiresPhpunit::class)] -#[CoversClass(RequiresSetting::class)] -#[CoversClass(RunClassInSeparateProcess::class)] -#[CoversClass(RunInSeparateProcess::class)] -#[CoversClass(RunTestsInSeparateProcesses::class)] -#[CoversClass(Test::class)] -#[CoversClass(TestDox::class)] -#[CoversClass(TestWith::class)] -#[CoversClass(Uses::class)] -#[CoversClass(UsesClass::class)] -#[CoversClass(UsesDefaultClass::class)] -#[CoversClass(UsesFunction::class)] -#[CoversClass(WithoutErrorHandler::class)] +#[CoversClassesThatExtendClass(Metadata::class)] #[Small] +#[Group('metadata')] final class MetadataTest extends TestCase { public function testCanBeAfter(): void { - $metadata = Metadata::after(); + $metadata = Metadata::after(0); $this->assertTrue($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -70,19 +34,24 @@ public function testCanBeAfter(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -97,19 +66,28 @@ public function testCanBeAfter(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } public function testCanBeAfterClass(): void { - $metadata = Metadata::afterClass(); + $metadata = Metadata::afterClass(0); $this->assertFalse($metadata->isAfter()); $this->assertTrue($metadata->isAfterClass()); @@ -117,19 +95,24 @@ public function testCanBeAfterClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -144,14 +127,23 @@ public function testCanBeAfterClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } public function testCanBeBackupGlobalsOnClass(): void @@ -164,19 +156,24 @@ public function testCanBeBackupGlobalsOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -191,13 +188,19 @@ public function testCanBeBackupGlobalsOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertFalse($metadata->enabled()); @@ -215,19 +218,24 @@ public function testCanBeBackupGlobalsOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -242,13 +250,19 @@ public function testCanBeBackupGlobalsOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertFalse($metadata->enabled()); @@ -266,19 +280,24 @@ public function testCanBeBackupStaticPropertiesOnClass(): void $this->assertTrue($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -293,13 +312,19 @@ public function testCanBeBackupStaticPropertiesOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertFalse($metadata->enabled()); @@ -317,19 +342,24 @@ public function testCanBeBackupStaticPropertiesOnMethod(): void $this->assertTrue($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -344,13 +374,19 @@ public function testCanBeBackupStaticPropertiesOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertFalse($metadata->enabled()); @@ -360,7 +396,7 @@ public function testCanBeBackupStaticPropertiesOnMethod(): void public function testCanBeBeforeClass(): void { - $metadata = Metadata::beforeClass(); + $metadata = Metadata::beforeClass(0); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -368,19 +404,24 @@ public function testCanBeBeforeClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertTrue($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -395,19 +436,28 @@ public function testCanBeBeforeClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } public function testCanBeBefore(): void { - $metadata = Metadata::before(); + $metadata = Metadata::before(0); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -415,19 +465,23 @@ public function testCanBeBefore(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertTrue($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -442,19 +496,28 @@ public function testCanBeBefore(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } - public function testCanBeCoversOnClass(): void + public function testCanBeCoversClass(): void { - $metadata = Metadata::coversOnClass(self::class); + $metadata = Metadata::coversClass(self::class); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -462,19 +525,24 @@ public function testCanBeCoversOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertTrue($metadata->isCovers()); - $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertTrue($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -489,23 +557,32 @@ public function testCanBeCoversOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame(self::class, $metadata->target()); + $this->assertSame(self::class, $metadata->className()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeCoversOnMethod(): void + public function testCanBeCoversNamespace(): void { - $metadata = Metadata::coversOnMethod(self::class); + $namespace = 'namespace'; + + $metadata = Metadata::coversNamespace($namespace); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -513,19 +590,24 @@ public function testCanBeCoversOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertTrue($metadata->isCovers()); + $this->assertTrue($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -540,23 +622,30 @@ public function testCanBeCoversOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame(self::class, $metadata->target()); - $this->assertTrue($metadata->isMethodLevel()); - $this->assertFalse($metadata->isClassLevel()); + $this->assertSame($namespace, $metadata->namespace()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeCoversClass(): void + public function testCanBeCoversClassesThatExtendClass(): void { - $metadata = Metadata::coversClass(self::class); + $metadata = Metadata::coversClassesThatExtendClass(self::class); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -564,19 +653,24 @@ public function testCanBeCoversClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); - $this->assertTrue($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertTrue($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -591,22 +685,30 @@ public function testCanBeCoversClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); - $this->assertSame(self::class, $metadata->asStringForCodeUnitMapper()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeCoversDefaultClass(): void + public function testCanBeCoversClassesThatImplementInterface(): void { - $metadata = Metadata::coversDefaultClass(self::class); + $metadata = Metadata::coversClassesThatImplementInterface(self::class); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -614,19 +716,24 @@ public function testCanBeCoversDefaultClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertTrue($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertTrue($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -641,16 +748,25 @@ public function testCanBeCoversDefaultClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame(self::class, $metadata->className()); + $this->assertSame(self::class, $metadata->interfaceName()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } public function testCanBeCoversFunction(): void @@ -663,19 +779,23 @@ public function testCanBeCoversFunction(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertTrue($metadata->isCoversFunction()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -690,17 +810,89 @@ public function testCanBeCoversFunction(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('f', $metadata->functionName()); - $this->assertSame('::f', $metadata->asStringForCodeUnitMapper()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); + } + + public function testCanBeCoversMethod(): void + { + $metadata = Metadata::coversMethod(self::class, 'testCanBeCoversMethod'); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertTrue($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame(self::class, $metadata->className()); + $this->assertSame('testCanBeCoversMethod', $metadata->methodName()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } public function testCanBeCoversNothingOnMethod(): void @@ -713,19 +905,23 @@ public function testCanBeCoversNothingOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertTrue($metadata->isCoversNothing()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -740,13 +936,19 @@ public function testCanBeCoversNothingOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertTrue($metadata->isMethodLevel()); @@ -763,19 +965,84 @@ public function testCanBeCoversNothingOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertTrue($metadata->isCoversNothing()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); + } + + public function testCanBeCoversTrait(): void + { + $metadata = Metadata::coversTrait(ExampleTrait::class); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertTrue($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -790,15 +1057,23 @@ public function testCanBeCoversNothingOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + $this->assertSame(ExampleTrait::class, $metadata->traitName()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -813,19 +1088,24 @@ public function testCanBeDataProvider(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertTrue($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -840,17 +1120,26 @@ public function testCanBeDataProvider(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); $this->assertSame('method', $metadata->methodName()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } public function testCanBeDependsOnClass(): void @@ -863,19 +1152,24 @@ public function testCanBeDependsOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertTrue($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -890,18 +1184,27 @@ public function testCanBeDependsOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); $this->assertFalse($metadata->deepClone()); $this->assertFalse($metadata->shallowClone()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } public function testCanBeDependsOnMethod(): void @@ -914,19 +1217,24 @@ public function testCanBeDependsOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertTrue($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -941,19 +1249,89 @@ public function testCanBeDependsOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); $this->assertSame('method', $metadata->methodName()); $this->assertFalse($metadata->deepClone()); $this->assertFalse($metadata->shallowClone()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } + + public function testCanBeDisableReturnValueGenerationForTestDoubles(): void + { + $metadata = Metadata::disableReturnValueGenerationForTestDoubles(); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertTrue($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } public function testCanBeDoesNotPerformAssertionsOnClass(): void @@ -966,19 +1344,24 @@ public function testCanBeDoesNotPerformAssertionsOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertTrue($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -993,13 +1376,19 @@ public function testCanBeDoesNotPerformAssertionsOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertTrue($metadata->isClassLevel()); @@ -1016,19 +1405,24 @@ public function testCanBeDoesNotPerformAssertionsOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertTrue($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1043,13 +1437,19 @@ public function testCanBeDoesNotPerformAssertionsOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertTrue($metadata->isMethodLevel()); @@ -1066,19 +1466,24 @@ public function testCanBeExcludeGlobalVariableFromBackupOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertTrue($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1093,13 +1498,19 @@ public function testCanBeExcludeGlobalVariableFromBackupOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('variable', $metadata->globalVariableName()); @@ -1117,19 +1528,24 @@ public function testCanBeExcludeGlobalVariableFromBackupOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertTrue($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1144,13 +1560,19 @@ public function testCanBeExcludeGlobalVariableFromBackupOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('variable', $metadata->globalVariableName()); @@ -1168,19 +1590,24 @@ public function testCanBeExcludeStaticPropertyFromBackupOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertTrue($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1195,17 +1622,24 @@ public function testCanBeExcludeStaticPropertyFromBackupOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('class', $metadata->className()); $this->assertSame('property', $metadata->propertyName()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -1220,19 +1654,24 @@ public function testCanBeExcludeStaticPropertyFromBackupOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertTrue($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1247,17 +1686,24 @@ public function testCanBeExcludeStaticPropertyFromBackupOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('class', $metadata->className()); $this->assertSame('property', $metadata->propertyName()); + $this->assertTrue($metadata->isMethodLevel()); $this->assertFalse($metadata->isClassLevel()); } @@ -1272,19 +1718,24 @@ public function testCanBeGroupOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertTrue($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1299,16 +1750,23 @@ public function testCanBeGroupOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('name', $metadata->groupName()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -1323,19 +1781,24 @@ public function testCanBeIgnoreDeprecationsOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertTrue($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1350,14 +1813,23 @@ public function testCanBeIgnoreDeprecationsOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } public function testCanBeIgnoreDeprecationsOnMethod(): void @@ -1370,19 +1842,24 @@ public function testCanBeIgnoreDeprecationsOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertTrue($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1397,19 +1874,28 @@ public function testCanBeIgnoreDeprecationsOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } - public function testCanBeGroupOnMethod(): void + public function testCanBeIgnorePhpunitDeprecationsOnClass(): void { - $metadata = Metadata::groupOnMethod('name'); + $metadata = Metadata::ignorePhpunitDeprecationsOnClass(); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1417,19 +1903,24 @@ public function testCanBeGroupOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); - $this->assertTrue($metadata->isGroup()); + $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertTrue($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1444,23 +1935,28 @@ public function testCanBeGroupOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame('name', $metadata->groupName()); - $this->assertTrue($metadata->isMethodLevel()); - $this->assertFalse($metadata->isClassLevel()); + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeRunTestsInSeparateProcesses(): void + public function testCanBeIgnorePhpunitDeprecationsOnMethod(): void { - $metadata = Metadata::runTestsInSeparateProcesses(); + $metadata = Metadata::ignorePhpunitDeprecationsOnMethod(); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1468,22 +1964,27 @@ public function testCanBeRunTestsInSeparateProcesses(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertTrue($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); - $this->assertTrue($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); $this->assertFalse($metadata->isTest()); $this->assertFalse($metadata->isPreCondition()); $this->assertFalse($metadata->isPostCondition()); @@ -1495,19 +1996,28 @@ public function testCanBeRunTestsInSeparateProcesses(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } - public function testCanBeRunClassInSeparateProcess(): void + public function testCanBeGroupOnMethod(): void { - $metadata = Metadata::runClassInSeparateProcess(); + $metadata = Metadata::groupOnMethod('name'); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1515,20 +2025,25 @@ public function testCanBeRunClassInSeparateProcess(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); - $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); - $this->assertFalse($metadata->isGroup()); + $this->assertTrue($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); - $this->assertTrue($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); $this->assertFalse($metadata->isTest()); @@ -1542,19 +2057,30 @@ public function testCanBeRunClassInSeparateProcess(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame('name', $metadata->groupName()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } - public function testCanBeRunInSeparateProcess(): void + public function testCanBeRunTestsInSeparateProcesses(): void { - $metadata = Metadata::runInSeparateProcess(); + $metadata = Metadata::runTestsInSeparateProcesses(); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1562,22 +2088,27 @@ public function testCanBeRunInSeparateProcess(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); - $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); - $this->assertTrue($metadata->isRunInSeparateProcess()); - $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertTrue($metadata->isRunTestsInSeparateProcesses()); $this->assertFalse($metadata->isTest()); $this->assertFalse($metadata->isPreCondition()); $this->assertFalse($metadata->isPostCondition()); @@ -1589,19 +2120,28 @@ public function testCanBeRunInSeparateProcess(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeTest(): void + public function testCanBeRunClassInSeparateProcess(): void { - $metadata = Metadata::test(); + $metadata = Metadata::runClassInSeparateProcess(); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1609,23 +2149,28 @@ public function testCanBeTest(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); - $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertTrue($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); - $this->assertTrue($metadata->isTest()); + $this->assertFalse($metadata->isTest()); $this->assertFalse($metadata->isPreCondition()); $this->assertFalse($metadata->isPostCondition()); $this->assertFalse($metadata->isPreserveGlobalState()); @@ -1636,19 +2181,28 @@ public function testCanBeTest(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBePreCondition(): void + public function testCanBeRunInSeparateProcess(): void { - $metadata = Metadata::preCondition(); + $metadata = Metadata::runInSeparateProcess(); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1656,24 +2210,29 @@ public function testCanBePreCondition(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); - $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); - $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertTrue($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); $this->assertFalse($metadata->isTest()); - $this->assertTrue($metadata->isPreCondition()); + $this->assertFalse($metadata->isPreCondition()); $this->assertFalse($metadata->isPostCondition()); $this->assertFalse($metadata->isPreserveGlobalState()); $this->assertFalse($metadata->isRequiresMethod()); @@ -1683,19 +2242,150 @@ public function testCanBePreCondition(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - } + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } + + public function testCanBeTest(): void + { + $metadata = Metadata::test(); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertTrue($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } + + public function testCanBePreCondition(): void + { + $metadata = Metadata::preCondition(0); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertTrue($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } public function testCanBePostCondition(): void { - $metadata = Metadata::postCondition(); + $metadata = Metadata::postCondition(0); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -1703,19 +2393,24 @@ public function testCanBePostCondition(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1730,14 +2425,23 @@ public function testCanBePostCondition(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } public function testCanBePreserveGlobalStateOnClass(): void @@ -1750,18 +2454,20 @@ public function testCanBePreserveGlobalStateOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); - $this->assertFalse($metadata->isCoversDefaultClass()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1776,16 +2482,23 @@ public function testCanBePreserveGlobalStateOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertFalse($metadata->enabled()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -1800,18 +2513,20 @@ public function testCanBePreserveGlobalStateOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); - $this->assertFalse($metadata->isCoversDefaultClass()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1826,16 +2541,23 @@ public function testCanBePreserveGlobalStateOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertFalse($metadata->enabled()); + $this->assertTrue($metadata->isMethodLevel()); $this->assertFalse($metadata->isClassLevel()); } @@ -1850,19 +2572,24 @@ public function testCanBeRequiresMethodOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1877,17 +2604,24 @@ public function testCanBeRequiresMethodOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); $this->assertSame(__METHOD__, $metadata->methodName()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -1902,19 +2636,24 @@ public function testCanBeRequiresMethodOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1929,17 +2668,24 @@ public function testCanBeRequiresMethodOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); $this->assertSame(__METHOD__, $metadata->methodName()); + $this->assertTrue($metadata->isMethodLevel()); $this->assertFalse($metadata->isClassLevel()); } @@ -1954,19 +2700,24 @@ public function testCanBeRequiresFunctionOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -1981,16 +2732,23 @@ public function testCanBeRequiresFunctionOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('f', $metadata->functionName()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -2005,19 +2763,24 @@ public function testCanBeRequiresFunctionOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2032,16 +2795,23 @@ public function testCanBeRequiresFunctionOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('f', $metadata->functionName()); + $this->assertTrue($metadata->isMethodLevel()); $this->assertFalse($metadata->isClassLevel()); } @@ -2056,19 +2826,24 @@ public function testCanBeRequiresOperatingSystemOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2083,16 +2858,23 @@ public function testCanBeRequiresOperatingSystemOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('Linux', $metadata->operatingSystem()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -2107,19 +2889,24 @@ public function testCanBeRequiresOperatingSystemOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2134,16 +2921,23 @@ public function testCanBeRequiresOperatingSystemOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('Linux', $metadata->operatingSystem()); + $this->assertTrue($metadata->isMethodLevel()); $this->assertFalse($metadata->isClassLevel()); } @@ -2158,19 +2952,24 @@ public function testCanBeRequiresOperatingSystemFamilyOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2185,16 +2984,23 @@ public function testCanBeRequiresOperatingSystemFamilyOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('Linux', $metadata->operatingSystemFamily()); + $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } @@ -2209,19 +3015,24 @@ public function testCanBeRequiresOperatingSystemFamilyOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2236,16 +3047,23 @@ public function testCanBeRequiresOperatingSystemFamilyOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('Linux', $metadata->operatingSystemFamily()); + $this->assertTrue($metadata->isMethodLevel()); $this->assertFalse($metadata->isClassLevel()); } @@ -2265,19 +3083,24 @@ public function testCanBeRequiresPhpOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2292,13 +3115,19 @@ public function testCanBeRequiresPhpOnClass(): void $this->assertTrue($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('>= 8.0.0', $metadata->versionRequirement()->asString()); @@ -2322,19 +3151,24 @@ public function testCanBeRequiresPhpOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2349,13 +3183,19 @@ public function testCanBeRequiresPhpOnMethod(): void $this->assertTrue($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('>= 8.0.0', $metadata->versionRequirement()->asString()); @@ -2374,19 +3214,24 @@ public function testCanBeRequiresPhpExtensionOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2401,13 +3246,19 @@ public function testCanBeRequiresPhpExtensionOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertTrue($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('test', $metadata->extension()); @@ -2436,19 +3287,24 @@ public function testCanBeRequiresPhpExtensionWithVersionOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2463,13 +3319,19 @@ public function testCanBeRequiresPhpExtensionWithVersionOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertTrue($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('test', $metadata->extension()); @@ -2490,19 +3352,24 @@ public function testCanBeRequiresPhpExtensionOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2517,13 +3384,19 @@ public function testCanBeRequiresPhpExtensionOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertTrue($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('test', $metadata->extension()); @@ -2552,19 +3425,24 @@ public function testCanBeRequiresPhpExtensionWithVersionOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2579,13 +3457,19 @@ public function testCanBeRequiresPhpExtensionWithVersionOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertTrue($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('test', $metadata->extension()); @@ -2611,19 +3495,24 @@ public function testCanBeRequiresPhpunitOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCovers()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2638,13 +3527,19 @@ public function testCanBeRequiresPhpunitOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertTrue($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('>= 10.0.0', $metadata->versionRequirement()->asString()); @@ -2653,6 +3548,69 @@ public function testCanBeRequiresPhpunitOnClass(): void $this->assertFalse($metadata->isMethodLevel()); } + public function testCanBeRequiresPhpunitExtensionOnClass(): void + { + $metadata = Metadata::requiresPhpunitExtensionOnClass(SomeExtension::class); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertTrue($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame(SomeExtension::class, $metadata->extensionClass()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); + } + public function testCanBeRequiresPhpunitOnMethod(): void { $metadata = Metadata::requiresPhpunitOnMethod( @@ -2668,19 +3626,24 @@ public function testCanBeRequiresPhpunitOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCovers()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2695,13 +3658,19 @@ public function testCanBeRequiresPhpunitOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertTrue($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('>= 10.0.0', $metadata->versionRequirement()->asString()); @@ -2710,6 +3679,325 @@ public function testCanBeRequiresPhpunitOnMethod(): void $this->assertFalse($metadata->isClassLevel()); } + public function testCanBeRequiresPhpunitExtensionOnMethod(): void + { + $metadata = Metadata::requiresPhpunitExtensionOnMethod(SomeExtension::class); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertTrue($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame(SomeExtension::class, $metadata->extensionClass()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } + + public function testCanBeRequiresEnvironmentVariableOnMethod(): void + { + $metadata = Metadata::requiresEnvironmentVariableOnMethod('foo', 'bar'); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertTrue($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame('foo', $metadata->environmentVariableName()); + $this->assertSame('bar', $metadata->value()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } + + public function testCanBeRequiresEnvironmentVariableOnClass(): void + { + $metadata = Metadata::requiresEnvironmentVariableOnClass('foo', 'bar'); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertTrue($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame('foo', $metadata->environmentVariableName()); + $this->assertSame('bar', $metadata->value()); + + $this->assertFalse($metadata->isMethodLevel()); + $this->assertTrue($metadata->isClassLevel()); + } + + public function testCanBeWithEnvironmentVariableOnMethod(): void + { + $metadata = Metadata::withEnvironmentVariableOnMethod('foo', 'bar'); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertTrue($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame('foo', $metadata->environmentVariableName()); + $this->assertSame('bar', $metadata->value()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); + } + + public function testCanBeWithEnvironmentVariableOnClass(): void + { + $metadata = Metadata::withEnvironmentVariableOnClass('foo', 'bar'); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertTrue($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame('foo', $metadata->environmentVariableName()); + $this->assertSame('bar', $metadata->value()); + + $this->assertFalse($metadata->isMethodLevel()); + $this->assertTrue($metadata->isClassLevel()); + } + public function testCanBeRequiresSettingOnClass(): void { $metadata = Metadata::requiresSettingOnClass('foo', 'bar'); @@ -2720,19 +4008,24 @@ public function testCanBeRequiresSettingOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCovers()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2747,13 +4040,19 @@ public function testCanBeRequiresSettingOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertTrue($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('foo', $metadata->setting()); @@ -2773,19 +4072,24 @@ public function testCanBeRequiresSettingOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCovers()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2800,13 +4104,19 @@ public function testCanBeRequiresSettingOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertTrue($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('foo', $metadata->setting()); @@ -2826,19 +4136,24 @@ public function testCanBeTestDoxOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2853,13 +4168,19 @@ public function testCanBeTestDoxOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertTrue($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('text', $metadata->text()); @@ -2878,19 +4199,24 @@ public function testCanBeTestDoxOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2905,13 +4231,19 @@ public function testCanBeTestDoxOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertTrue($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('text', $metadata->text()); @@ -2930,19 +4262,24 @@ public function testCanBeTestWith(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -2957,21 +4294,33 @@ public function testCanBeTestWith(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertTrue($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(['a', 'b'], $metadata->data()); + $this->assertFalse($metadata->hasName()); + $this->assertNull($metadata->name()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } - public function testCanBeUsesOnClass(): void + public function testCanBeUsesNamespace(): void { - $metadata = Metadata::usesOnClass(self::class); + $namespace = 'namespace'; + + $metadata = Metadata::usesNamespace($namespace); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -2979,19 +4328,24 @@ public function testCanBeUsesOnClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -3006,24 +4360,30 @@ public function testCanBeUsesOnClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertTrue($metadata->isUses()); + $this->assertTrue($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame(self::class, $metadata->target()); + $this->assertSame($namespace, $metadata->namespace()); $this->assertTrue($metadata->isClassLevel()); $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeUsesOnMethod(): void + public function testCanBeUsesClass(): void { - $metadata = Metadata::usesOnMethod(self::class); + $metadata = Metadata::usesClass(self::class); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -3031,19 +4391,24 @@ public function testCanBeUsesOnMethod(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -3058,24 +4423,30 @@ public function testCanBeUsesOnMethod(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertTrue($metadata->isUses()); - $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertTrue($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame(self::class, $metadata->target()); + $this->assertSame(self::class, $metadata->className()); - $this->assertTrue($metadata->isMethodLevel()); - $this->assertFalse($metadata->isClassLevel()); + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeUsesClass(): void + public function testCanBeUsesClassesThatExtendClass(): void { - $metadata = Metadata::usesClass(self::class); + $metadata = Metadata::usesClassesThatExtendClass(self::class); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -3083,19 +4454,24 @@ public function testCanBeUsesClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -3110,22 +4486,30 @@ public function testCanBeUsesClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); - $this->assertTrue($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertTrue($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame(self::class, $metadata->className()); - $this->assertSame(self::class, $metadata->asStringForCodeUnitMapper()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } - public function testCanBeUsesDefaultClass(): void + public function testCanBeUsesClassesThatImplementInterface(): void { - $metadata = Metadata::usesDefaultClass(self::class); + $metadata = Metadata::usesClassesThatImplementInterface(self::class); $this->assertFalse($metadata->isAfter()); $this->assertFalse($metadata->isAfterClass()); @@ -3133,19 +4517,24 @@ public function testCanBeUsesDefaultClass(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -3160,16 +4549,25 @@ public function testCanBeUsesDefaultClass(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertTrue($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertTrue($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); - $this->assertSame(self::class, $metadata->className()); + $this->assertSame(self::class, $metadata->interfaceName()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } public function testCanBeUsesFunction(): void @@ -3182,19 +4580,24 @@ public function testCanBeUsesFunction(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -3209,17 +4612,151 @@ public function testCanBeUsesFunction(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertTrue($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertFalse($metadata->isWithoutErrorHandler()); $this->assertSame('f', $metadata->functionName()); - $this->assertSame('::f', $metadata->asStringForCodeUnitMapper()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); + } + + public function testCanBeUsesMethod(): void + { + $metadata = Metadata::usesMethod(self::class, 'testCanBeUsesMethod'); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertTrue($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame(self::class, $metadata->className()); + $this->assertSame('testCanBeUsesMethod', $metadata->methodName()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); + } + + public function testCanBeUsesTrait(): void + { + $metadata = Metadata::usesTrait(ExampleTrait::class); + + $this->assertFalse($metadata->isAfter()); + $this->assertFalse($metadata->isAfterClass()); + $this->assertFalse($metadata->isBackupGlobals()); + $this->assertFalse($metadata->isBackupStaticProperties()); + $this->assertFalse($metadata->isBeforeClass()); + $this->assertFalse($metadata->isBefore()); + $this->assertFalse($metadata->isCoversNamespace()); + $this->assertFalse($metadata->isCoversClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); + $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); + $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); + $this->assertFalse($metadata->isDataProvider()); + $this->assertFalse($metadata->isDependsOnClass()); + $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); + $this->assertFalse($metadata->isDoesNotPerformAssertions()); + $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); + $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); + $this->assertFalse($metadata->isGroup()); + $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); + $this->assertFalse($metadata->isRunClassInSeparateProcess()); + $this->assertFalse($metadata->isRunInSeparateProcess()); + $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); + $this->assertFalse($metadata->isTest()); + $this->assertFalse($metadata->isPreCondition()); + $this->assertFalse($metadata->isPostCondition()); + $this->assertFalse($metadata->isPreserveGlobalState()); + $this->assertFalse($metadata->isRequiresMethod()); + $this->assertFalse($metadata->isRequiresFunction()); + $this->assertFalse($metadata->isRequiresOperatingSystem()); + $this->assertFalse($metadata->isRequiresOperatingSystemFamily()); + $this->assertFalse($metadata->isRequiresPhp()); + $this->assertFalse($metadata->isRequiresPhpExtension()); + $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); + $this->assertFalse($metadata->isRequiresSetting()); + $this->assertFalse($metadata->isTestDox()); + $this->assertFalse($metadata->isTestWith()); + $this->assertFalse($metadata->isUsesNamespace()); + $this->assertFalse($metadata->isUsesClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); + $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertTrue($metadata->isUsesTrait()); + $this->assertFalse($metadata->isWithoutErrorHandler()); + + $this->assertSame(ExampleTrait::class, $metadata->traitName()); + + $this->assertTrue($metadata->isClassLevel()); + $this->assertFalse($metadata->isMethodLevel()); } public function testCanBeWithoutErrorHandler(): void @@ -3232,19 +4769,24 @@ public function testCanBeWithoutErrorHandler(): void $this->assertFalse($metadata->isBackupStaticProperties()); $this->assertFalse($metadata->isBeforeClass()); $this->assertFalse($metadata->isBefore()); - $this->assertFalse($metadata->isCovers()); + $this->assertFalse($metadata->isCoversNamespace()); $this->assertFalse($metadata->isCoversClass()); - $this->assertFalse($metadata->isCoversDefaultClass()); + $this->assertFalse($metadata->isCoversClassesThatExtendClass()); + $this->assertFalse($metadata->isCoversClassesThatImplementInterface()); $this->assertFalse($metadata->isCoversFunction()); + $this->assertFalse($metadata->isCoversMethod()); $this->assertFalse($metadata->isCoversNothing()); + $this->assertFalse($metadata->isCoversTrait()); $this->assertFalse($metadata->isDataProvider()); $this->assertFalse($metadata->isDependsOnClass()); $this->assertFalse($metadata->isDependsOnMethod()); + $this->assertFalse($metadata->isDisableReturnValueGenerationForTestDoubles()); $this->assertFalse($metadata->isDoesNotPerformAssertions()); $this->assertFalse($metadata->isExcludeGlobalVariableFromBackup()); $this->assertFalse($metadata->isExcludeStaticPropertyFromBackup()); $this->assertFalse($metadata->isGroup()); $this->assertFalse($metadata->isIgnoreDeprecations()); + $this->assertFalse($metadata->isIgnorePhpunitDeprecations()); $this->assertFalse($metadata->isRunClassInSeparateProcess()); $this->assertFalse($metadata->isRunInSeparateProcess()); $this->assertFalse($metadata->isRunTestsInSeparateProcesses()); @@ -3259,13 +4801,22 @@ public function testCanBeWithoutErrorHandler(): void $this->assertFalse($metadata->isRequiresPhp()); $this->assertFalse($metadata->isRequiresPhpExtension()); $this->assertFalse($metadata->isRequiresPhpunit()); + $this->assertFalse($metadata->isRequiresPhpunitExtension()); + $this->assertFalse($metadata->isRequiresEnvironmentVariable()); + $this->assertFalse($metadata->isWithEnvironmentVariable()); $this->assertFalse($metadata->isRequiresSetting()); $this->assertFalse($metadata->isTestDox()); $this->assertFalse($metadata->isTestWith()); - $this->assertFalse($metadata->isUses()); + $this->assertFalse($metadata->isUsesNamespace()); $this->assertFalse($metadata->isUsesClass()); - $this->assertFalse($metadata->isUsesDefaultClass()); + $this->assertFalse($metadata->isUsesClassesThatExtendClass()); + $this->assertFalse($metadata->isUsesClassesThatImplementInterface()); $this->assertFalse($metadata->isUsesFunction()); + $this->assertFalse($metadata->isUsesMethod()); + $this->assertFalse($metadata->isUsesTrait()); $this->assertTrue($metadata->isWithoutErrorHandler()); + + $this->assertTrue($metadata->isMethodLevel()); + $this->assertFalse($metadata->isClassLevel()); } } diff --git a/tests/unit/Metadata/Parser/Annotation/RegistryTest.php b/tests/unit/Metadata/Parser/Annotation/RegistryTest.php deleted file mode 100644 index 45f8915e823..00000000000 --- a/tests/unit/Metadata/Parser/Annotation/RegistryTest.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Annotation; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\Attributes\UsesClass; -use PHPUnit\Framework\TestCase; -use PHPUnit\Metadata\Annotation\Parser\DocBlock; -use PHPUnit\Metadata\Annotation\Parser\Registry; -use PHPUnit\Metadata\ReflectionException; -use PHPUnit\TestFixture\Metadata\Annotation\CoversTest; -use PHPUnit\TestFixture\NumericGroupAnnotationTest; -use ThisClassDoesNotExist; - -#[CoversClass(Registry::class)] -#[UsesClass(DocBlock::class)] -#[Small] -final class RegistryTest extends TestCase -{ - public function testRegistryLookupWithExistingClassAnnotation(): void - { - $annotation = Registry::getInstance()->forClassName(CoversTest::class); - - $this->assertSame( - [ - 'covers' => [ - '::\PHPUnit\TestFixture\Metadata\Annotation\f()', - '\PHPUnit\TestFixture\Metadata\Annotation\Example', - ], - 'coversNothing' => [''], - 'coversDefaultClass' => [ - '\PHPUnit\TestFixture\Metadata\Annotation\Example', - ], - ], - $annotation->symbolAnnotations(), - ); - - $this->assertSame( - $annotation, - Registry::getInstance()->forClassName(CoversTest::class), - 'Registry memoizes retrieved DocBlock instances', - ); - } - - public function testRegistryLookupWithExistingMethodAnnotation(): void - { - $annotation = Registry::getInstance()->forMethod( - NumericGroupAnnotationTest::class, - 'testTicketAnnotationSupportsNumericValue', - ); - - $this->assertSame( - [ - 'testdox' => ['Empty test for @ticket numeric annotation values'], - 'ticket' => ['3502'], - 'see' => ['https://github.com/sebastianbergmann/phpunit/issues/3502'], - ], - $annotation->symbolAnnotations(), - ); - - $this->assertSame( - $annotation, - Registry::getInstance()->forMethod( - NumericGroupAnnotationTest::class, - 'testTicketAnnotationSupportsNumericValue', - ), - 'Registry memoizes retrieved DocBlock instances', - ); - } - - public function testClassLookupForAClassThatDoesNotExistFails(): void - { - $registry = Registry::getInstance(); - - $this->expectException(ReflectionException::class); - - $registry->forClassName(ThisClassDoesNotExist::class); - } - - public function testMethodLookupForAMethodThatDoesNotExistFails(): void - { - $registry = Registry::getInstance(); - - $this->expectException(ReflectionException::class); - - $registry->forMethod(self::class, 'thisMethodDoesNotExist'); - } -} diff --git a/tests/unit/Metadata/Parser/AnnotationParserTest.php b/tests/unit/Metadata/Parser/AnnotationParserTest.php deleted file mode 100644 index 19b7237d2c3..00000000000 --- a/tests/unit/Metadata/Parser/AnnotationParserTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(AnnotationParser::class)] -#[Small] -final class AnnotationParserTest extends AnnotationParserTestCase -{ - protected function parser(): Parser - { - return new AnnotationParser; - } -} diff --git a/tests/unit/Metadata/Parser/AnnotationParserTestCase.php b/tests/unit/Metadata/Parser/AnnotationParserTestCase.php deleted file mode 100644 index 2d23a2341f0..00000000000 --- a/tests/unit/Metadata/Parser/AnnotationParserTestCase.php +++ /dev/null @@ -1,876 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use function assert; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\TestCase; -use PHPUnit\Metadata\RequiresPhp; -use PHPUnit\Metadata\RequiresPhpExtension; -use PHPUnit\Metadata\RequiresPhpunit; -use PHPUnit\Metadata\RequiresSetting; -use PHPUnit\Metadata\Version\ComparisonRequirement; -use PHPUnit\Metadata\Version\ConstraintRequirement; -use PHPUnit\TestFixture\Metadata\Annotation\AnotherTest; -use PHPUnit\TestFixture\Metadata\Annotation\BackupGlobalsTest; -use PHPUnit\TestFixture\Metadata\Annotation\BackupStaticPropertiesTest; -use PHPUnit\TestFixture\Metadata\Annotation\CoversTest; -use PHPUnit\TestFixture\Metadata\Annotation\DependencyTest; -use PHPUnit\TestFixture\Metadata\Annotation\DoesNotPerformAssertionsTest; -use PHPUnit\TestFixture\Metadata\Annotation\GroupTest; -use PHPUnit\TestFixture\Metadata\Annotation\LargeTest; -use PHPUnit\TestFixture\Metadata\Annotation\MediumTest; -use PHPUnit\TestFixture\Metadata\Annotation\PreserveGlobalStateTest; -use PHPUnit\TestFixture\Metadata\Annotation\ProcessIsolationTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresFunctionTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresOperatingSystemFamilyTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresOperatingSystemTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresPhp2Test; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresPhpExtensionTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresPhpTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresPhpunit2Test; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresPhpunitTest; -use PHPUnit\TestFixture\Metadata\Annotation\RequiresSettingTest; -use PHPUnit\TestFixture\Metadata\Annotation\SmallTest; -use PHPUnit\TestFixture\Metadata\Annotation\TestDoxTest; -use PHPUnit\TestFixture\Metadata\Annotation\UsesTest; - -abstract class AnnotationParserTestCase extends TestCase -{ - public static function provideRequiresPhpTestMethods(): array - { - return [['testOne'], ['testTwo']]; - } - - public function test_Parses_backupGlobals_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(BackupGlobalsTest::class)->isBackupGlobals(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isBackupGlobals()); - $this->assertTrue($metadata->asArray()[0]->enabled()); - } - - public function test_Parses_backupStaticAttributes_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(BackupStaticPropertiesTest::class)->isBackupStaticProperties(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isBackupStaticProperties()); - $this->assertTrue($metadata->asArray()[0]->enabled()); - } - - public function test_Parses_covers_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(CoversTest::class)->isCovers(); - - $this->assertCount(2, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isCovers()); - $this->assertSame('::\PHPUnit\TestFixture\Metadata\Annotation\f', $metadata->asArray()[0]->target()); - - $this->assertTrue($metadata->asArray()[1]->isCovers()); - $this->assertSame('\PHPUnit\TestFixture\Metadata\Annotation\Example', $metadata->asArray()[1]->target()); - } - - public function test_Parses_coversDefaultClass_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(CoversTest::class)->isCoversDefaultClass(); - - $this->assertCount(1, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isCoversDefaultClass()); - $this->assertSame('\PHPUnit\TestFixture\Metadata\Annotation\Example', $metadata->asArray()[0]->className()); - } - - public function test_Parses_coversNothing_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(CoversTest::class)->isCoversNothing(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isCoversNothing()); - } - - public function test_Parses_doesNotPerformAssertions_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(DoesNotPerformAssertionsTest::class)->isDoesNotPerformAssertions(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isDoesNotPerformAssertions()); - } - - public function test_Parses_group_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(GroupTest::class)->isGroup(); - - $this->assertCount(2, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('group', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_large_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(LargeTest::class)->isGroup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('large', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_medium_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(MediumTest::class)->isGroup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('medium', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_preserveGlobalState_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(PreserveGlobalStateTest::class)->isPreserveGlobalState(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isPreserveGlobalState()); - $this->assertTrue($metadata->asArray()[0]->enabled()); - } - - public function test_Parses_requiresFunction_annotation_with_function_on_class(): void - { - $metadata = $this->parser()->forClass(RequiresFunctionTest::class)->isRequiresFunction(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresFunction()); - $this->assertSame('f', $metadata->asArray()[0]->functionName()); - } - - public function test_Parses_requiresFunction_annotation_with_method_on_class(): void - { - $metadata = $this->parser()->forClass(RequiresFunctionTest::class)->isRequiresMethod(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresMethod()); - $this->assertSame('SomeClass', $metadata->asArray()[0]->className()); - $this->assertSame('someMethod', $metadata->asArray()[0]->methodName()); - } - - public function test_Parses_requiresOperatingSystem_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(RequiresOperatingSystemTest::class)->isRequiresOperatingSystem(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresOperatingSystem()); - $this->assertSame('Linux', $metadata->asArray()[0]->operatingSystem()); - } - - public function test_Parses_requiresOperatingSystemFamily_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(RequiresOperatingSystemFamilyTest::class)->isRequiresOperatingSystemFamily(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresOperatingSystemFamily()); - $this->assertSame('Linux', $metadata->asArray()[0]->operatingSystemFamily()); - } - - public function test_Parses_requiresPhp_annotation_on_class_with_version_comparison(): void - { - $metadata = $this->parser()->forClass(RequiresPhpTest::class)->isRequiresPhp(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - assert($requirement instanceof RequiresPhp); - - $this->assertTrue($requirement->isRequiresPhp()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('>= 8.0.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhp_annotation_on_class_with_version_constraint(): void - { - $metadata = $this->parser()->forClass(RequiresPhp2Test::class)->isRequiresPhp(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - assert($requirement instanceof RequiresPhp); - - $this->assertTrue($requirement->isRequiresPhp()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ConstraintRequirement); - - $this->assertSame('^8.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhpExtension_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(RequiresPhpExtensionTest::class)->isRequiresPhpExtension(); - - $this->assertCount(2, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhpExtension()); - $this->assertSame('bar', $requirement->extension()); - $this->assertFalse($requirement->hasVersionRequirement()); - - $requirement = $metadata->asArray()[1]; - - $this->assertTrue($requirement->isRequiresPhpExtension()); - $this->assertSame('foo', $requirement->extension()); - $this->assertTrue($requirement->hasVersionRequirement()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('>= 1.0.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhpunit_annotation_on_class_with_version_comparison(): void - { - $metadata = $this->parser()->forClass(RequiresPhpunitTest::class)->isRequiresPhpunit(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - assert($requirement instanceof RequiresPhpunit); - - $this->assertTrue($requirement->isRequiresPhpunit()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('>= 10.0.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhpunit_annotation_on_class_with_version_constraint(): void - { - $metadata = $this->parser()->forClass(RequiresPhpunit2Test::class)->isRequiresPhpunit(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - assert($requirement instanceof RequiresPhpunit); - - $this->assertTrue($requirement->isRequiresPhpunit()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ConstraintRequirement); - - $this->assertSame('^10.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresSetting_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(RequiresSettingTest::class)->isRequiresSetting(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresSetting()); - - assert($requirement instanceof RequiresSetting); - - $this->assertSame('foo', $requirement->setting()); - $this->assertSame('bar', $requirement->value()); - } - - public function test_Parses_runClassInSeparateProcess_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(ProcessIsolationTest::class)->isRunClassInSeparateProcess(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRunClassInSeparateProcess()); - } - - public function test_Parses_runTestsInSeparateProcesses_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(ProcessIsolationTest::class)->isRunTestsInSeparateProcesses(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRunTestsInSeparateProcesses()); - } - - public function test_Parses_small_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(SmallTest::class)->isGroup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('small', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_testdox_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(TestDoxTest::class)->isTestDox(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isTestDox()); - $this->assertSame('text', $metadata->asArray()[0]->text()); - } - - public function test_Parses_ticket_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(GroupTest::class)->isGroup(); - - $this->assertCount(2, $metadata); - $this->assertTrue($metadata->asArray()[1]->isGroup()); - $this->assertSame('ticket', $metadata->asArray()[1]->groupName()); - } - - public function test_Parses_uses_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(UsesTest::class)->isUses(); - - $this->assertCount(2, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isUses()); - $this->assertSame('::\PHPUnit\TestFixture\Metadata\Annotation\f', $metadata->asArray()[0]->target()); - - $this->assertTrue($metadata->asArray()[1]->isUses()); - $this->assertSame('\PHPUnit\TestFixture\Metadata\Annotation\Example', $metadata->asArray()[1]->target()); - } - - public function test_Parses_usesDefaultClass_annotation_on_class(): void - { - $metadata = $this->parser()->forClass(UsesTest::class)->isUsesDefaultClass(); - - $this->assertCount(1, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isUsesDefaultClass()); - $this->assertSame('\PHPUnit\TestFixture\Metadata\Annotation\Example', $metadata->asArray()[0]->className()); - } - - public function test_Parses_after_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'afterTest')->isAfter(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isAfter()); - } - - public function test_Parses_afterClass_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'afterTests')->isAfterClass(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isAfterClass()); - } - - public function test_Parses_backupGlobals_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(BackupGlobalsTest::class, 'testOne')->isBackupGlobals(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isBackupGlobals()); - $this->assertFalse($metadata->asArray()[0]->enabled()); - } - - public function test_Parses_backupStaticProperties_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(BackupStaticPropertiesTest::class, 'testOne')->isBackupStaticProperties(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isBackupStaticProperties()); - $this->assertFalse($metadata->asArray()[0]->enabled()); - } - - public function test_Parses_before_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'beforeTest')->isBefore(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isBefore()); - } - - public function test_Parses_beforeClass_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'beforeTests')->isBeforeClass(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isBeforeClass()); - } - - public function test_Parses_covers_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(CoversTest::class, 'testTwo')->isCovers(); - - $this->assertCount(1, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isCovers()); - $this->assertSame('Foo::bar', $metadata->asArray()[0]->target()); - } - - public function test_Parses_coversNothing_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(CoversTest::class, 'testOne')->isCoversNothing(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isCoversNothing()); - } - - public function test_Parses_dataProvider_annotation_on_method_for_method_in_same_class(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'testWithDataProvider')->isDataProvider(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isDataProvider()); - $this->assertSame(SmallTest::class, $metadata->asArray()[0]->className()); - $this->assertSame('provider', $metadata->asArray()[0]->methodName()); - } - - public function test_Parses_dataProvider_annotation_on_method_for_method_of_other_class(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'testWithDataProviderExternal')->isDataProvider(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isDataProvider()); - $this->assertSame('\\' . SmallTest::class, $metadata->asArray()[0]->className()); - $this->assertSame('provider', $metadata->asArray()[0]->methodName()); - } - - public function test_Parses_depends_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(DependencyTest::class, 'testOne')->isDependsOnMethod(); - - $this->assertCount(5, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isDependsOnMethod()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[0]->className()); - $this->assertSame('testOne', $metadata->asArray()[0]->methodName()); - $this->assertFalse($metadata->asArray()[0]->deepClone()); - $this->assertFalse($metadata->asArray()[0]->shallowClone()); - - $this->assertTrue($metadata->asArray()[1]->isDependsOnMethod()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[1]->className()); - $this->assertSame('testOne', $metadata->asArray()[1]->methodName()); - $this->assertFalse($metadata->asArray()[1]->deepClone()); - $this->assertFalse($metadata->asArray()[1]->shallowClone()); - - $this->assertTrue($metadata->asArray()[2]->isDependsOnMethod()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[2]->className()); - $this->assertSame('testOne', $metadata->asArray()[2]->methodName()); - $this->assertTrue($metadata->asArray()[2]->deepClone()); - $this->assertFalse($metadata->asArray()[2]->shallowClone()); - - $this->assertTrue($metadata->asArray()[3]->isDependsOnMethod()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[3]->className()); - $this->assertSame('testOne', $metadata->asArray()[3]->methodName()); - $this->assertFalse($metadata->asArray()[3]->deepClone()); - $this->assertFalse($metadata->asArray()[3]->shallowClone()); - - $this->assertTrue($metadata->asArray()[4]->isDependsOnMethod()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[4]->className()); - $this->assertSame('testOne', $metadata->asArray()[4]->methodName()); - $this->assertFalse($metadata->asArray()[4]->deepClone()); - $this->assertTrue($metadata->asArray()[4]->shallowClone()); - - $metadata = $this->parser()->forMethod(DependencyTest::class, 'testTwo')->isDependsOnMethod(); - - $this->assertCount(5, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isDependsOnMethod()); - $this->assertSame(DependencyTest::class, $metadata->asArray()[0]->className()); - $this->assertSame('testOne', $metadata->asArray()[0]->methodName()); - $this->assertFalse($metadata->asArray()[0]->deepClone()); - $this->assertFalse($metadata->asArray()[0]->shallowClone()); - - $this->assertTrue($metadata->asArray()[1]->isDependsOnMethod()); - $this->assertSame(DependencyTest::class, $metadata->asArray()[1]->className()); - $this->assertSame('testOne', $metadata->asArray()[1]->methodName()); - $this->assertFalse($metadata->asArray()[1]->deepClone()); - $this->assertFalse($metadata->asArray()[1]->shallowClone()); - - $this->assertTrue($metadata->asArray()[2]->isDependsOnMethod()); - $this->assertSame(DependencyTest::class, $metadata->asArray()[2]->className()); - $this->assertSame('testOne', $metadata->asArray()[2]->methodName()); - $this->assertTrue($metadata->asArray()[2]->deepClone()); - $this->assertFalse($metadata->asArray()[2]->shallowClone()); - - $this->assertTrue($metadata->asArray()[3]->isDependsOnMethod()); - $this->assertSame(DependencyTest::class, $metadata->asArray()[3]->className()); - $this->assertSame('testOne', $metadata->asArray()[3]->methodName()); - $this->assertFalse($metadata->asArray()[3]->deepClone()); - $this->assertFalse($metadata->asArray()[3]->shallowClone()); - - $this->assertTrue($metadata->asArray()[4]->isDependsOnMethod()); - $this->assertSame(DependencyTest::class, $metadata->asArray()[4]->className()); - $this->assertSame('testOne', $metadata->asArray()[4]->methodName()); - $this->assertFalse($metadata->asArray()[4]->deepClone()); - $this->assertTrue($metadata->asArray()[4]->shallowClone()); - - $metadata = $this->parser()->forMethod(DependencyTest::class, 'testThree')->isDependsOnClass(); - - $this->assertCount(5, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isDependsOnClass()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[0]->className()); - $this->assertFalse($metadata->asArray()[0]->deepClone()); - $this->assertFalse($metadata->asArray()[0]->shallowClone()); - - $this->assertTrue($metadata->asArray()[1]->isDependsOnClass()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[1]->className()); - $this->assertFalse($metadata->asArray()[1]->deepClone()); - $this->assertFalse($metadata->asArray()[1]->shallowClone()); - - $this->assertTrue($metadata->asArray()[2]->isDependsOnClass()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[2]->className()); - $this->assertTrue($metadata->asArray()[2]->deepClone()); - $this->assertFalse($metadata->asArray()[2]->shallowClone()); - - $this->assertTrue($metadata->asArray()[3]->isDependsOnClass()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[3]->className()); - $this->assertFalse($metadata->asArray()[3]->deepClone()); - $this->assertFalse($metadata->asArray()[3]->shallowClone()); - - $this->assertTrue($metadata->asArray()[4]->isDependsOnClass()); - $this->assertSame(AnotherTest::class, $metadata->asArray()[4]->className()); - $this->assertFalse($metadata->asArray()[4]->deepClone()); - $this->assertTrue($metadata->asArray()[4]->shallowClone()); - } - - public function test_Parses_doesNotPerformAssertions_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(DoesNotPerformAssertionsTest::class, 'testOne')->isDoesNotPerformAssertions(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isDoesNotPerformAssertions()); - } - - public function test_Parses_excludeGlobalVariableFromBackup_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(BackupGlobalsTest::class, 'testOne')->isExcludeGlobalVariableFromBackup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isExcludeGlobalVariableFromBackup()); - $this->assertSame('bar', $metadata->asArray()[0]->globalVariableName()); - } - - public function test_Parses_excludeStaticPropertyFromBackup_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(BackupStaticPropertiesTest::class, 'testOne')->isExcludeStaticPropertyFromBackup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isExcludeStaticPropertyFromBackup()); - $this->assertSame('anotherClassName', $metadata->asArray()[0]->className()); - $this->assertSame('propertyName', $metadata->asArray()[0]->propertyName()); - } - - public function test_Parses_group_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(GroupTest::class, 'testOne')->isGroup(); - - $this->assertCount(2, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('another-group', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_large_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(LargeTest::class, 'testWithLargeAnnotation')->isGroup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('large', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_medium_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(MediumTest::class, 'testWithMediumAnnotation')->isGroup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('medium', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_postCondition_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'postCondition')->isPostCondition(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isPostCondition()); - } - - public function test_Parses_preCondition_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'preCondition')->isPreCondition(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isPreCondition()); - } - - public function test_Parses_preserveGlobalState_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(PreserveGlobalStateTest::class, 'testOne')->isPreserveGlobalState(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isPreserveGlobalState()); - $this->assertFalse($metadata->asArray()[0]->enabled()); - } - - public function test_Parses_requiresFunction_annotation_with_function_on_method(): void - { - $metadata = $this->parser()->forMethod(RequiresFunctionTest::class, 'testOne')->isRequiresFunction(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresFunction()); - $this->assertSame('g', $metadata->asArray()[0]->functionName()); - } - - public function test_Parses_requiresFunction_annotation_with_method_on_method(): void - { - $metadata = $this->parser()->forMethod(RequiresFunctionTest::class, 'testOne')->isRequiresMethod(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresMethod()); - $this->assertSame('SomeOtherClass', $metadata->asArray()[0]->className()); - $this->assertSame('someOtherMethod', $metadata->asArray()[0]->methodName()); - } - - public function test_Parses_requiresOperatingSystem_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(RequiresOperatingSystemTest::class, 'testOne')->isRequiresOperatingSystem(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresOperatingSystem()); - $this->assertSame('Linux', $metadata->asArray()[0]->operatingSystem()); - } - - public function test_Parses_requiresOperatingSystemFamily_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(RequiresOperatingSystemFamilyTest::class, 'testOne')->isRequiresOperatingSystemFamily(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRequiresOperatingSystemFamily()); - $this->assertSame('Linux', $metadata->asArray()[0]->operatingSystemFamily()); - } - - #[DataProvider('provideRequiresPhpTestMethods')] - public function test_Parses_requiresPhp_annotation_on_method_with_version_comparison(string $method): void - { - $metadata = $this->parser()->forMethod(RequiresPhpTest::class, $method)->isRequiresPhp(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhp()); - - assert($requirement instanceof RequiresPhp); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('< 9.0.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhp_annotation_on_method_with_version_constraint(): void - { - $metadata = $this->parser()->forMethod(RequiresPhp2Test::class, 'testOne')->isRequiresPhp(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhp()); - - assert($requirement instanceof RequiresPhp); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ConstraintRequirement); - - $this->assertSame('^8.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhpExtension_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(RequiresPhpExtensionTest::class, 'testOne')->isRequiresPhpExtension(); - - $this->assertCount(2, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhpExtension()); - - assert($requirement instanceof RequiresPhpExtension); - - $this->assertSame('foo', $requirement->extension()); - $this->assertFalse($requirement->hasVersionRequirement()); - - $requirement = $metadata->asArray()[1]; - - $this->assertTrue($requirement->isRequiresPhpExtension()); - - assert($requirement instanceof RequiresPhpExtension); - - $this->assertSame('bar', $requirement->extension()); - $this->assertTrue($requirement->hasVersionRequirement()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('>= 1.0.0', $versionRequirement->asString()); - - $metadata = $this->parser()->forMethod(RequiresPhpExtensionTest::class, 'testTwo'); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhpExtension()); - - assert($requirement instanceof RequiresPhpExtension); - - $this->assertSame('baz', $requirement->extension()); - $this->assertTrue($requirement->hasVersionRequirement()); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('< 2.0.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhpunit_annotation_on_method_with_version_comparison(): void - { - $metadata = $this->parser()->forMethod(RequiresPhpunitTest::class, 'testOne')->isRequiresPhpunit(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhpunit()); - - assert($requirement instanceof RequiresPhpunit); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ComparisonRequirement); - - $this->assertSame('< 11.0.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresPhpunit_annotation_on_method_with_version_constraint(): void - { - $metadata = $this->parser()->forMethod(RequiresPhpunit2Test::class, 'testOne')->isRequiresPhpunit(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresPhpunit()); - - assert($requirement instanceof RequiresPhpunit); - - $versionRequirement = $requirement->versionRequirement(); - - assert($versionRequirement instanceof ConstraintRequirement); - - $this->assertSame('^11.0', $versionRequirement->asString()); - } - - public function test_Parses_requiresSetting_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(RequiresSettingTest::class, 'testOne')->isRequiresSetting(); - - $this->assertCount(1, $metadata); - - $requirement = $metadata->asArray()[0]; - - $this->assertTrue($requirement->isRequiresSetting()); - - assert($requirement instanceof RequiresSetting); - - $this->assertSame('bar', $requirement->setting()); - $this->assertSame('baz', $requirement->value()); - } - - public function test_Parses_runInSeparateProcess_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(ProcessIsolationTest::class, 'testOne')->isRunInSeparateProcess(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isRunInSeparateProcess()); - } - - public function test_Parses_small_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'testWithSmallAnnotation')->isGroup(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertSame('small', $metadata->asArray()[0]->groupName()); - } - - public function test_Parses_test_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(SmallTest::class, 'one')->isTest(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isTest()); - } - - public function test_Parses_testdox_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(TestDoxTest::class, 'testOne')->isTestDox(); - - $this->assertCount(1, $metadata); - $this->assertTrue($metadata->asArray()[0]->isTestDox()); - $this->assertSame('text', $metadata->asArray()[0]->text()); - } - - public function test_Parses_ticket_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(GroupTest::class, 'testOne')->isGroup(); - - $this->assertCount(2, $metadata); - $this->assertTrue($metadata->asArray()[1]->isGroup()); - $this->assertSame('another-ticket', $metadata->asArray()[1]->groupName()); - } - - public function test_Parses_uses_annotation_on_method(): void - { - $metadata = $this->parser()->forMethod(UsesTest::class, 'testOne')->isUses(); - - $this->assertCount(1, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isUses()); - $this->assertSame('Foo::bar', $metadata->asArray()[0]->target()); - } - - public function test_Merges_class_level_and_method_level_annotations(): void - { - $metadata = $this->parser()->forClassAndMethod(SmallTest::class, 'testWithDataProvider'); - - $this->assertCount(2, $metadata); - - $this->assertTrue($metadata->asArray()[0]->isGroup()); - $this->assertTrue($metadata->asArray()[1]->isDataProvider()); - } - - abstract protected function parser(): Parser; -} diff --git a/tests/unit/Metadata/Parser/AttributeParserTest.php b/tests/unit/Metadata/Parser/AttributeParserTest.php index 8054d440455..81bb3a45cdb 100644 --- a/tests/unit/Metadata/Parser/AttributeParserTest.php +++ b/tests/unit/Metadata/Parser/AttributeParserTest.php @@ -16,8 +16,13 @@ use PHPUnit\Framework\Attributes\Before; use PHPUnit\Framework\Attributes\BeforeClass; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversClassesThatExtendClass; +use PHPUnit\Framework\Attributes\CoversClassesThatImplementInterface; use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\CoversNamespace; use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\CoversTrait; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\Attributes\Depends; @@ -38,6 +43,7 @@ use PHPUnit\Framework\Attributes\PostCondition; use PHPUnit\Framework\Attributes\PreCondition; use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable; use PHPUnit\Framework\Attributes\RequiresFunction; use PHPUnit\Framework\Attributes\RequiresMethod; use PHPUnit\Framework\Attributes\RequiresOperatingSystem; @@ -45,6 +51,7 @@ use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\RequiresPhpunitExtension; use PHPUnit\Framework\Attributes\RequiresSetting; use PHPUnit\Framework\Attributes\RunClassInSeparateProcess; use PHPUnit\Framework\Attributes\RunInSeparateProcess; @@ -56,8 +63,16 @@ use PHPUnit\Framework\Attributes\TestWithJson; use PHPUnit\Framework\Attributes\Ticket; use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\Attributes\UsesClassesThatExtendClass; +use PHPUnit\Framework\Attributes\UsesClassesThatImplementInterface; use PHPUnit\Framework\Attributes\UsesFunction; +use PHPUnit\Framework\Attributes\UsesMethod; +use PHPUnit\Framework\Attributes\UsesNamespace; +use PHPUnit\Framework\Attributes\UsesTrait; use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use PHPUnit\Metadata\DisableReturnValueGenerationForTestDoubles; +use PHPUnit\Metadata\InvalidAttributeException; +use PHPUnit\Metadata\WithEnvironmentVariable; #[CoversClass(AttributeParser::class)] #[CoversClass(AfterClass::class)] @@ -67,8 +82,13 @@ #[CoversClass(BeforeClass::class)] #[CoversClass(Before::class)] #[CoversClass(CoversClass::class)] +#[CoversClass(CoversClassesThatExtendClass::class)] +#[CoversClass(CoversClassesThatImplementInterface::class)] #[CoversClass(CoversFunction::class)] +#[CoversClass(CoversMethod::class)] +#[CoversClass(CoversNamespace::class)] #[CoversClass(CoversNothing::class)] +#[CoversClass(CoversTrait::class)] #[CoversClass(DataProviderExternal::class)] #[CoversClass(DataProvider::class)] #[CoversClass(DependsExternal::class)] @@ -80,10 +100,12 @@ #[CoversClass(Depends::class)] #[CoversClass(DependsUsingDeepClone::class)] #[CoversClass(DependsUsingShallowClone::class)] +#[CoversClass(DisableReturnValueGenerationForTestDoubles::class)] #[CoversClass(DoesNotPerformAssertions::class)] #[CoversClass(ExcludeGlobalVariableFromBackup::class)] #[CoversClass(ExcludeStaticPropertyFromBackup::class)] #[CoversClass(Group::class)] +#[CoversClass(InvalidAttributeException::class)] #[CoversClass(Large::class)] #[CoversClass(Medium::class)] #[CoversClass(PostCondition::class)] @@ -96,6 +118,8 @@ #[CoversClass(RequiresPhpExtension::class)] #[CoversClass(RequiresPhp::class)] #[CoversClass(RequiresPhpunit::class)] +#[CoversClass(RequiresPhpunitExtension::class)] +#[CoversClass(RequiresEnvironmentVariable::class)] #[CoversClass(RequiresSetting::class)] #[CoversClass(RunClassInSeparateProcess::class)] #[CoversClass(RunInSeparateProcess::class)] @@ -107,9 +131,17 @@ #[CoversClass(TestWith::class)] #[CoversClass(Ticket::class)] #[CoversClass(UsesClass::class)] +#[CoversClass(UsesClassesThatExtendClass::class)] +#[CoversClass(UsesClassesThatImplementInterface::class)] #[CoversClass(UsesFunction::class)] +#[CoversClass(UsesMethod::class)] +#[CoversClass(UsesNamespace::class)] +#[CoversClass(UsesTrait::class)] +#[CoversClass(WithEnvironmentVariable::class)] #[CoversClass(WithoutErrorHandler::class)] #[Small] +#[Group('metadata')] +#[Group('metadata/attributes')] final class AttributeParserTest extends AttributeParserTestCase { protected function parser(): Parser diff --git a/tests/unit/Metadata/Parser/AttributeParserTestCase.php b/tests/unit/Metadata/Parser/AttributeParserTestCase.php index 25a6797a4ee..780578d61fb 100644 --- a/tests/unit/Metadata/Parser/AttributeParserTestCase.php +++ b/tests/unit/Metadata/Parser/AttributeParserTestCase.php @@ -14,15 +14,28 @@ use PHPUnit\Framework\TestCase; use PHPUnit\Metadata\DependsOnClass; use PHPUnit\Metadata\DependsOnMethod; +use PHPUnit\Metadata\InvalidAttributeException; +use PHPUnit\Metadata\RequiresEnvironmentVariable; +use PHPUnit\Metadata\RequiresPhp; +use PHPUnit\Metadata\RequiresPhpExtension; +use PHPUnit\Metadata\RequiresPhpunit; +use PHPUnit\Metadata\RequiresPhpunitExtension; +use PHPUnit\Metadata\RequiresSetting; use PHPUnit\Metadata\Version\ComparisonRequirement; use PHPUnit\Metadata\Version\ConstraintRequirement; +use PHPUnit\Metadata\WithEnvironmentVariable; use PHPUnit\TestFixture\Metadata\Attribute\AnotherTest; use PHPUnit\TestFixture\Metadata\Attribute\BackupGlobalsTest; use PHPUnit\TestFixture\Metadata\Attribute\BackupStaticPropertiesTest; +use PHPUnit\TestFixture\Metadata\Attribute\CoversNothingTest; use PHPUnit\TestFixture\Metadata\Attribute\CoversTest; use PHPUnit\TestFixture\Metadata\Attribute\DependencyTest; +use PHPUnit\TestFixture\Metadata\Attribute\DisableReturnValueGenerationForTestDoublesTest; use PHPUnit\TestFixture\Metadata\Attribute\DoesNotPerformAssertionsTest; +use PHPUnit\TestFixture\Metadata\Attribute\DuplicateSmallAttributeTest; +use PHPUnit\TestFixture\Metadata\Attribute\DuplicateTestAttributeTest; use PHPUnit\TestFixture\Metadata\Attribute\Example; +use PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait; use PHPUnit\TestFixture\Metadata\Attribute\GroupTest; use PHPUnit\TestFixture\Metadata\Attribute\IgnoreDeprecationsClassTest; use PHPUnit\TestFixture\Metadata\Attribute\IgnoreDeprecationsMethodTest; @@ -31,20 +44,24 @@ use PHPUnit\TestFixture\Metadata\Attribute\LargeTest; use PHPUnit\TestFixture\Metadata\Attribute\MediumTest; use PHPUnit\TestFixture\Metadata\Attribute\NonPhpunitAttributeTest; +use PHPUnit\TestFixture\Metadata\Attribute\PhpunitAttributeThatDoesNotExistTest; use PHPUnit\TestFixture\Metadata\Attribute\PreserveGlobalStateTest; use PHPUnit\TestFixture\Metadata\Attribute\ProcessIsolationTest; +use PHPUnit\TestFixture\Metadata\Attribute\RequiresEnvironmentVariableTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresFunctionTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresMethodTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresOperatingSystemFamilyTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresOperatingSystemTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresPhpExtensionTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresPhpTest; +use PHPUnit\TestFixture\Metadata\Attribute\RequiresPhpunitExtensionTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresPhpunitTest; use PHPUnit\TestFixture\Metadata\Attribute\RequiresSettingTest; use PHPUnit\TestFixture\Metadata\Attribute\SmallTest; use PHPUnit\TestFixture\Metadata\Attribute\TestDoxTest; use PHPUnit\TestFixture\Metadata\Attribute\TestWithTest; use PHPUnit\TestFixture\Metadata\Attribute\UsesTest; +use PHPUnit\TestFixture\Metadata\Attribute\WithEnvironmentVariableTest; use PHPUnit\TestFixture\Metadata\Attribute\WithoutErrorHandlerTest; abstract class AttributeParserTestCase extends TestCase @@ -69,6 +86,16 @@ public function test_parses_BackupStaticProperties_attribute_on_class(): void $this->assertTrue($metadata->asArray()[0]->enabled()); } + #[TestDox('Parses #[CoversNamespace] attribute on class')] + public function test_parses_CoversNamespace_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(CoversTest::class)->isCoversNamespace(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isCoversNamespace()); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute', $metadata->asArray()[0]->namespace()); + } + #[TestDox('Parses #[CoversClass] attribute on class')] public function test_parses_CoversClass_attribute_on_class(): void { @@ -79,6 +106,36 @@ public function test_parses_CoversClass_attribute_on_class(): void $this->assertSame(Example::class, $metadata->asArray()[0]->className()); } + #[TestDox('Parses #[CoversClassesThatExtendClass] attribute on class')] + public function test_parses_CoversClassesThatExtendClass_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(CoversTest::class)->isCoversClassesThatExtendClass(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isCoversClassesThatExtendClass()); + $this->assertSame(Example::class, $metadata->asArray()[0]->className()); + } + + #[TestDox('Parses #[CoversClassesThatImplementInterface] attribute on class')] + public function test_parses_CoversClassesThatImplementInterface_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(CoversTest::class)->isCoversClassesThatImplementInterface(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isCoversClassesThatImplementInterface()); + $this->assertSame(Example::class, $metadata->asArray()[0]->interfaceName()); + } + + #[TestDox('Parses #[CoversTrait] attribute on class')] + public function test_parses_CoversTrait_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(CoversTest::class)->isCoversTrait(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isCoversTrait()); + $this->assertSame(ExampleTrait::class, $metadata->asArray()[0]->traitName()); + } + #[TestDox('Parses #[CoversFunction] attribute on class')] public function test_parses_CoversFunction_attribute_on_class(): void { @@ -89,15 +146,35 @@ public function test_parses_CoversFunction_attribute_on_class(): void $this->assertSame('f', $metadata->asArray()[0]->functionName()); } + #[TestDox('Parses #[CoversMethod] attribute on class')] + public function test_parses_CoversMethod_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(CoversTest::class)->isCoversMethod(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isCoversMethod()); + $this->assertSame(Example::class, $metadata->asArray()[0]->className()); + $this->assertSame('method', $metadata->asArray()[0]->methodName()); + } + #[TestDox('Parses #[CoversNothing] attribute on class')] public function test_parses_CoversNothing_attribute_on_class(): void { - $metadata = $this->parser()->forClass(CoversTest::class)->isCoversNothing(); + $metadata = $this->parser()->forClass(CoversNothingTest::class)->isCoversNothing(); $this->assertCount(1, $metadata); $this->assertTrue($metadata->asArray()[0]->isCoversNothing()); } + #[TestDox('Parses #[DisableReturnValueGenerationForTestDoubles] attribute on class')] + public function test_parses_DisableReturnValueGenerationForTestDoubles_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(DisableReturnValueGenerationForTestDoublesTest::class)->isDisableReturnValueGenerationForTestDoubles(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isDisableReturnValueGenerationForTestDoubles()); + } + #[TestDox('Parses #[DoesNotPerformAssertions] attribute on class')] public function test_parses_DoesNotPerformAssertions_attribute_on_class(): void { @@ -238,7 +315,7 @@ public function test_parses_RequiresPhp_attribute_on_class(): void $this->assertTrue($requirement->isRequiresPhp()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresPhp); + assert($requirement instanceof RequiresPhp); $versionRequirement = $requirement->versionRequirement(); @@ -270,7 +347,7 @@ public function test_parses_RequiresPhpunit_attribute_on_class(): void $this->assertTrue($requirement->isRequiresPhpunit()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresPhpunit); + assert($requirement instanceof RequiresPhpunit); $versionRequirement = $requirement->versionRequirement(); @@ -279,6 +356,53 @@ public function test_parses_RequiresPhpunit_attribute_on_class(): void $this->assertSame('>= 10.0.0', $versionRequirement->asString()); } + #[TestDox('Parses #[RequiresPhpunitExtension] attribute on class')] + public function test_parses_RequiresPhpunitExtension_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(RequiresPhpunitExtensionTest::class)->isRequiresPhpunitExtension(); + + $this->assertCount(1, $metadata); + + $requirement = $metadata->asArray()[0]; + + $this->assertTrue($requirement->isRequiresPhpunitExtension()); + + assert($requirement instanceof RequiresPhpunitExtension); + + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\SomeExtension', $requirement->extensionClass()); + } + + #[TestDox('Parses #[RequiresEnvironmentVariable] attribute on class')] + public function test_parses_RequiresEnvironmentVariable_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(RequiresEnvironmentVariableTest::class)->isRequiresEnvironmentVariable(); + + $this->assertCount(1, $metadata); + + $requirement = $metadata->asArray()[0]; + + $this->assertTrue($requirement->isRequiresEnvironmentVariable()); + + assert($requirement instanceof RequiresEnvironmentVariable); + + $this->assertSame('foo', $requirement->environmentVariableName()); + $this->assertSame('bar', $requirement->value()); + } + + #[TestDox('Parses #[WithEnvironmentVariable] attribute on class')] + public function test_parses_WithEnvironmentVariable_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(WithEnvironmentVariableTest::class)->isWithEnvironmentVariable(); + + $this->assertCount(1, $metadata); + + $withEnvironmentVariable = $metadata->asArray()[0]; + $this->assertTrue($withEnvironmentVariable->isWithEnvironmentVariable()); + assert($withEnvironmentVariable instanceof WithEnvironmentVariable); + $this->assertSame('foo', $withEnvironmentVariable->environmentVariableName()); + $this->assertSame('bar', $withEnvironmentVariable->value()); + } + #[TestDox('Parses #[RequiresSetting] attribute on class')] public function test_parses_RequiresSetting_attribute_on_class(): void { @@ -290,7 +414,7 @@ public function test_parses_RequiresSetting_attribute_on_class(): void $this->assertTrue($requirement->isRequiresSetting()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresSetting); + assert($requirement instanceof RequiresSetting); $this->assertSame('setting', $requirement->setting()); $this->assertSame('value', $requirement->value()); @@ -344,6 +468,16 @@ public function test_parses_Ticket_attribute_on_class(): void $this->assertSame('ticket', $metadata->asArray()[1]->groupName()); } + #[TestDox('Parses #[UsesNamespace] attribute on class')] + public function test_parses_UsesNamespace_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(UsesTest::class)->isUsesNamespace(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isUsesNamespace()); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute', $metadata->asArray()[0]->namespace()); + } + #[TestDox('Parses #[UsesClass] attribute on class')] public function test_parses_UsesClass_attribute_on_class(): void { @@ -354,6 +488,36 @@ public function test_parses_UsesClass_attribute_on_class(): void $this->assertSame(Example::class, $metadata->asArray()[0]->className()); } + #[TestDox('Parses #[UsesClassesThatExtendClass] attribute on class')] + public function test_parses_UsesClassesThatExtendClass_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(UsesTest::class)->isUsesClassesThatExtendClass(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isUsesClassesThatExtendClass()); + $this->assertSame(Example::class, $metadata->asArray()[0]->className()); + } + + #[TestDox('Parses #[UsesClassesThatImplementInterface] attribute on class')] + public function test_parses_UsesClassesThatImplementInterface_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(UsesTest::class)->isUsesClassesThatImplementInterface(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isUsesClassesThatImplementInterface()); + $this->assertSame(Example::class, $metadata->asArray()[0]->interfaceName()); + } + + #[TestDox('Parses #[UsesTrait] attribute on class')] + public function test_parses_UsesTrait_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(UsesTest::class)->isUsesTrait(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isUsesTrait()); + $this->assertSame(ExampleTrait::class, $metadata->asArray()[0]->traitName()); + } + #[TestDox('Parses #[UsesFunction] attribute on class')] public function test_parses_UsesFunction_attribute_on_class(): void { @@ -364,6 +528,17 @@ public function test_parses_UsesFunction_attribute_on_class(): void $this->assertSame('f', $metadata->asArray()[0]->functionName()); } + #[TestDox('Parses #[UsesMethod] attribute on class')] + public function test_parses_UsesMethod_attribute_on_class(): void + { + $metadata = $this->parser()->forClass(UsesTest::class)->isUsesMethod(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isUsesMethod()); + $this->assertSame(Example::class, $metadata->asArray()[0]->className()); + $this->assertSame('method', $metadata->asArray()[0]->methodName()); + } + #[TestDox('Parses #[After] attribute on class')] public function test_parses_After_attribute_on_method(): void { @@ -423,7 +598,7 @@ public function test_parses_BeforeClass_attribute_on_method(): void #[TestDox('Parses #[CoversNothing] attribute on method')] public function test_parses_CoversNothing_attribute_on_method(): void { - $metadata = $this->parser()->forMethod(CoversTest::class, 'testOne')->isCoversNothing(); + $metadata = $this->parser()->forMethod(CoversNothingTest::class, 'testOne')->isCoversNothing(); $this->assertCount(1, $metadata); $this->assertTrue($metadata->asArray()[0]->isCoversNothing()); @@ -739,7 +914,7 @@ public function test_parses_RequiresPhp_attribute_on_method(): void $this->assertTrue($requirement->isRequiresPhp()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresPhp); + assert($requirement instanceof RequiresPhp); $versionRequirement = $requirement->versionRequirement(); @@ -759,7 +934,7 @@ public function test_parses_RequiresPhpExtension_attribute_on_method(): void $this->assertTrue($requirement->isRequiresPhpExtension()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresPhpExtension); + assert($requirement instanceof RequiresPhpExtension); $this->assertSame('bar', $requirement->extension()); $this->assertTrue($requirement->hasVersionRequirement()); @@ -778,7 +953,7 @@ public function test_parses_RequiresPhpExtension_attribute_on_method(): void $this->assertTrue($requirement->isRequiresPhpExtension()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresPhpExtension); + assert($requirement instanceof RequiresPhpExtension); $this->assertSame('baz', $requirement->extension()); $this->assertTrue($requirement->hasVersionRequirement()); @@ -801,7 +976,7 @@ public function test_parses_RequiresPhpunit_attribute_on_method(): void $this->assertTrue($requirement->isRequiresPhpunit()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresPhpunit); + assert($requirement instanceof RequiresPhpunit); $versionRequirement = $requirement->versionRequirement(); @@ -810,6 +985,64 @@ public function test_parses_RequiresPhpunit_attribute_on_method(): void $this->assertSame('^10.0', $versionRequirement->asString()); } + #[TestDox('Parses #[RequiresPhpunitExtension] attribute on method')] + public function test_parses_RequiresPhpunitExtension_attribute_on_method(): void + { + $metadata = $this->parser()->forMethod(RequiresPhpunitExtensionTest::class, 'testOne')->isRequiresPhpunitExtension(); + + $this->assertCount(2, $metadata); + + $requirement = $metadata->asArray()[0]; + $this->assertTrue($requirement->isRequiresPhpunitExtension()); + assert($requirement instanceof RequiresPhpunitExtension); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\SomeExtension', $requirement->extensionClass()); + + $requirement = $metadata->asArray()[1]; + $this->assertTrue($requirement->isRequiresPhpunitExtension()); + assert($requirement instanceof RequiresPhpunitExtension); + $this->assertSame('PHPUnit\TestFixture\Metadata\Attribute\SomeOtherExtension', $requirement->extensionClass()); + } + + #[TestDox('Parses #[RequiresEnvironmentVariable] attribute on method')] + public function test_parses_RequiresEnvironmentVariable_attribute_on_method(): void + { + $metadata = $this->parser()->forMethod(RequiresEnvironmentVariableTest::class, 'testOne')->isRequiresEnvironmentVariable(); + + $this->assertCount(2, $metadata); + + $requirement = $metadata->asArray()[0]; + $this->assertTrue($requirement->isRequiresEnvironmentVariable()); + assert($requirement instanceof RequiresEnvironmentVariable); + $this->assertSame('foo', $requirement->environmentVariableName()); + $this->assertNull($requirement->value()); + + $requirement = $metadata->asArray()[1]; + $this->assertTrue($requirement->isRequiresEnvironmentVariable()); + assert($requirement instanceof RequiresEnvironmentVariable); + $this->assertSame('bar', $requirement->environmentVariableName()); + $this->assertSame('baz', $requirement->value()); + } + + #[TestDox('Parses #[WithEnvironmentVariable] attribute on method')] + public function test_parses_WithEnvironmentVariable_attribute_on_method(): void + { + $metadata = $this->parser()->forMethod(WithEnvironmentVariableTest::class, 'testOne')->isWithEnvironmentVariable(); + + $this->assertCount(2, $metadata); + + $withEnvironmentVariable = $metadata->asArray()[0]; + $this->assertTrue($withEnvironmentVariable->isWithEnvironmentVariable()); + assert($withEnvironmentVariable instanceof WithEnvironmentVariable); + $this->assertSame('foo', $withEnvironmentVariable->environmentVariableName()); + $this->assertNull($withEnvironmentVariable->value()); + + $withEnvironmentVariable = $metadata->asArray()[1]; + $this->assertTrue($withEnvironmentVariable->isWithEnvironmentVariable()); + assert($withEnvironmentVariable instanceof WithEnvironmentVariable); + $this->assertSame('bar', $withEnvironmentVariable->environmentVariableName()); + $this->assertSame('baz', $withEnvironmentVariable->value()); + } + #[TestDox('Parses #[RequiresSetting] attribute on method')] public function test_parses_RequiresSetting_attribute_on_method(): void { @@ -821,7 +1054,7 @@ public function test_parses_RequiresSetting_attribute_on_method(): void $this->assertTrue($requirement->isRequiresSetting()); - assert($requirement instanceof \PHPUnit\Metadata\RequiresSetting); + assert($requirement instanceof RequiresSetting); $this->assertSame('another-setting', $requirement->setting()); $this->assertSame('another-value', $requirement->value()); @@ -863,6 +1096,20 @@ public function test_parses_TestWith_attribute_on_method(): void $this->assertCount(1, $metadata); $this->assertTrue($metadata->asArray()[0]->isTestWith()); $this->assertSame([1, 2, 3], $metadata->asArray()[0]->data()); + $this->assertFalse($metadata->asArray()[0]->hasName()); + $this->assertNull($metadata->asArray()[0]->name()); + } + + #[TestDox('Parses #[TestWith] attribute with name on method')] + public function test_parses_TestWith_attribute_with_name_on_method(): void + { + $metadata = $this->parser()->forMethod(TestWithTest::class, 'testOneWithName')->isTestWith(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isTestWith()); + $this->assertSame([1, 2, 3], $metadata->asArray()[0]->data()); + $this->assertTrue($metadata->asArray()[0]->hasName()); + $this->assertSame('Name1', $metadata->asArray()[0]->name()); } #[TestDox('Parses #[TestWithJson] attribute on method')] @@ -873,6 +1120,20 @@ public function test_parses_TestWithJson_attribute_on_method(): void $this->assertCount(1, $metadata); $this->assertTrue($metadata->asArray()[0]->isTestWith()); $this->assertSame([1, 2, 3], $metadata->asArray()[0]->data()); + $this->assertFalse($metadata->asArray()[0]->hasName()); + $this->assertNull($metadata->asArray()[0]->name()); + } + + #[TestDox('Parses #[TestWithJson] attribute with name on method')] + public function test_parses_TestWithJson_attribute_with_name_on_method(): void + { + $metadata = $this->parser()->forMethod(TestWithTest::class, 'testTwoWithName')->isTestWith(); + + $this->assertCount(1, $metadata); + $this->assertTrue($metadata->asArray()[0]->isTestWith()); + $this->assertSame([1, 2, 3], $metadata->asArray()[0]->data()); + $this->assertTrue($metadata->asArray()[0]->hasName()); + $this->assertSame('Name2', $metadata->asArray()[0]->name()); } #[TestDox('Parses #[Ticket] attribute on method')] @@ -900,7 +1161,6 @@ public function test_parses_attributes_for_class_and_method(): void $this->assertCount(1, $metadata->isCoversClass()); $this->assertCount(1, $metadata->isCoversFunction()); - $this->assertCount(2, $metadata->isCoversNothing()); } public function test_ignores_attributes_not_owned_by_PHPUnit(): void @@ -910,5 +1170,26 @@ public function test_ignores_attributes_not_owned_by_PHPUnit(): void $this->assertTrue($metadata->isEmpty()); } + public function test_ignores_attributes_in_PHPUnit_namespace_that_do_not_exist(): void + { + $metadata = $this->parser()->forClassAndMethod(PhpunitAttributeThatDoesNotExistTest::class, 'testOne'); + + $this->assertTrue($metadata->isEmpty()); + } + + public function test_handles_ReflectionException_raised_when_instantiating_attribute_on_class(): void + { + $this->expectException(InvalidAttributeException::class); + + $this->parser()->forClass(DuplicateSmallAttributeTest::class); + } + + public function test_handles_ReflectionException_raised_when_instantiating_attribute_on_method(): void + { + $this->expectException(InvalidAttributeException::class); + + $this->parser()->forMethod(DuplicateTestAttributeTest::class, 'testOne'); + } + abstract protected function parser(): Parser; } diff --git a/tests/unit/Metadata/Parser/CachedAnnotationParserTest.php b/tests/unit/Metadata/Parser/CachedAnnotationParserTest.php deleted file mode 100644 index 3594d8df1a5..00000000000 --- a/tests/unit/Metadata/Parser/CachedAnnotationParserTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; - -#[CoversClass(CachingParser::class)] -#[Small] -final class CachedAnnotationParserTest extends AnnotationParserTestCase -{ - protected function parser(): Parser - { - return new CachingParser(new AnnotationParser); - } -} diff --git a/tests/unit/Metadata/Parser/CachedAttributeParserTest.php b/tests/unit/Metadata/Parser/CachedAttributeParserTest.php index 2e6c1614b5d..ccfd66d5e66 100644 --- a/tests/unit/Metadata/Parser/CachedAttributeParserTest.php +++ b/tests/unit/Metadata/Parser/CachedAttributeParserTest.php @@ -10,10 +10,13 @@ namespace PHPUnit\Metadata\Parser; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; #[CoversClass(CachingParser::class)] #[Small] +#[Group('metadata')] +#[Group('metadata/attributes')] final class CachedAttributeParserTest extends AttributeParserTestCase { protected function parser(): Parser diff --git a/tests/unit/Metadata/Parser/ChainedAnnotationParserTest.php b/tests/unit/Metadata/Parser/ChainedAnnotationParserTest.php deleted file mode 100644 index e7073fd409d..00000000000 --- a/tests/unit/Metadata/Parser/ChainedAnnotationParserTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Metadata\MetadataCollection; - -#[CoversClass(ParserChain::class)] -#[Small] -final class ChainedAnnotationParserTest extends AnnotationParserTestCase -{ - protected function parser(): Parser - { - $attributeReader = $this->createStub(Parser::class); - - $attributeReader->method('forClassAndMethod')->willReturn(MetadataCollection::fromArray([])); - $attributeReader->method('forClass')->willReturn(MetadataCollection::fromArray([])); - $attributeReader->method('forMethod')->willReturn(MetadataCollection::fromArray([])); - - return new ParserChain( - $attributeReader, - new AnnotationParser, - ); - } -} diff --git a/tests/unit/Metadata/Parser/ChainedAttributeParserTest.php b/tests/unit/Metadata/Parser/ChainedAttributeParserTest.php deleted file mode 100644 index d6c2465f37a..00000000000 --- a/tests/unit/Metadata/Parser/ChainedAttributeParserTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Metadata\Parser; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Metadata\MetadataCollection; - -#[CoversClass(ParserChain::class)] -#[Small] -final class ChainedAttributeParserTest extends AttributeParserTestCase -{ - protected function parser(): Parser - { - $annotationReader = $this->createStub(Parser::class); - - $annotationReader->method('forClassAndMethod')->willReturn(MetadataCollection::fromArray([])); - $annotationReader->method('forClass')->willReturn(MetadataCollection::fromArray([])); - $annotationReader->method('forMethod')->willReturn(MetadataCollection::fromArray([])); - - return new ParserChain( - new AttributeParser, - $annotationReader, - ); - } -} diff --git a/tests/unit/Metadata/Version/RequirementTest.php b/tests/unit/Metadata/Version/RequirementTest.php index 9f493081ed7..a1b0a8f02a5 100644 --- a/tests/unit/Metadata/Version/RequirementTest.php +++ b/tests/unit/Metadata/Version/RequirementTest.php @@ -12,6 +12,7 @@ use PharIo\Version\VersionConstraintParser; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; @@ -25,6 +26,7 @@ #[CoversClass(Requirement::class)] #[UsesClass(VersionComparisonOperator::class)] #[Small] +#[Group('metadata')] final class RequirementTest extends TestCase { public static function constraintProvider(): array diff --git a/tests/unit/Runner/Baseline/WriterTest.php b/tests/unit/Runner/Baseline/WriterTest.php index d9fe42ac411..65be3c46b97 100644 --- a/tests/unit/Runner/Baseline/WriterTest.php +++ b/tests/unit/Runner/Baseline/WriterTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\Runner\Baseline; +use const DIRECTORY_SEPARATOR; use function realpath; use function unlink; use PHPUnit\Framework\Attributes\CoversClass; diff --git a/tests/unit/Runner/Filter/ExcludeNameFilterIteratorTest.php b/tests/unit/Runner/Filter/ExcludeNameFilterIteratorTest.php new file mode 100644 index 00000000000..83a77d6c392 --- /dev/null +++ b/tests/unit/Runner/Filter/ExcludeNameFilterIteratorTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\TestFixture\BankAccountTest; + +#[CoversClass(ExcludeNameFilterIterator::class)] +#[Small] +class ExcludeNameFilterIteratorTest extends TestCase +{ + /** + * @throws Exception + */ + public function testCaseSensitiveMatch(): void + { + $this->assertTrue($this->createFilter('NotMatchingPattern')->accept()); + } + + /** + * @throws Exception + */ + public function testCaseInsensitiveMatch(): void + { + $this->assertTrue($this->createFilter('notmatchingbankaccount')->accept()); + } + + /** + * @throws Exception + */ + private function createFilter(string $filter): ExcludeNameFilterIterator + { + $suite = TestSuite::empty('test suite name'); + $suite->addTest(new BankAccountTest('testBalanceIsInitiallyZero')); + + $iterator = new ExcludeNameFilterIterator($suite->getIterator(), $filter); + + $iterator->rewind(); + + return $iterator; + } +} diff --git a/tests/unit/Runner/Filter/IncludeNameFilterIteratorTest.php b/tests/unit/Runner/Filter/IncludeNameFilterIteratorTest.php new file mode 100644 index 00000000000..7ce439efd06 --- /dev/null +++ b/tests/unit/Runner/Filter/IncludeNameFilterIteratorTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\TestFixture\BankAccountTest; + +#[CoversClass(IncludeNameFilterIterator::class)] +#[Small] +final class IncludeNameFilterIteratorTest extends TestCase +{ + /** + * @throws Exception + */ + public function testCaseSensitiveMatch(): void + { + $this->assertTrue($this->createFilter('BankAccountTest')->accept()); + } + + /** + * @throws Exception + */ + public function testCaseInsensitiveMatch(): void + { + $this->assertTrue($this->createFilter('bankaccounttest')->accept()); + } + + /** + * @throws Exception + */ + private function createFilter(string $filter): IncludeNameFilterIterator + { + $suite = TestSuite::empty('test suite name'); + $suite->addTest(new BankAccountTest('testBalanceIsInitiallyZero')); + + $iterator = new IncludeNameFilterIterator($suite->getIterator(), $filter); + + $iterator->rewind(); + + return $iterator; + } +} diff --git a/tests/unit/Runner/Filter/NameFilterIteratorTest.php b/tests/unit/Runner/Filter/NameFilterIteratorTest.php deleted file mode 100644 index 8fe275dced0..00000000000 --- a/tests/unit/Runner/Filter/NameFilterIteratorTest.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\TestFixture\BankAccountTest; - -#[CoversClass(NameFilterIterator::class)] -#[Small] -final class NameFilterIteratorTest extends TestCase -{ - public function testCaseSensitiveMatch(): void - { - $this->assertTrue($this->createFilter('BankAccountTest')->accept()); - } - - public function testCaseInsensitiveMatch(): void - { - $this->assertTrue($this->createFilter('bankaccounttest')->accept()); - } - - private function createFilter(string $filter): NameFilterIterator - { - $suite = TestSuite::empty('test suite name'); - $suite->addTest(new BankAccountTest('testBalanceIsInitiallyZero')); - - $iterator = new NameFilterIterator($suite->getIterator(), $filter); - - $iterator->rewind(); - - return $iterator; - } -} diff --git a/tests/unit/Runner/Filter/TestIdFilterIteratorTest.php b/tests/unit/Runner/Filter/TestIdFilterIteratorTest.php index 11864a8697f..dc448e16b56 100644 --- a/tests/unit/Runner/Filter/TestIdFilterIteratorTest.php +++ b/tests/unit/Runner/Filter/TestIdFilterIteratorTest.php @@ -35,7 +35,7 @@ public function testAcceptsTestsBasedOnTheirId(): void } /** - * @psalm-param list $testIds + * @param list $testIds */ private function testSuiteIterator(array $testIds): Iterator { diff --git a/tests/unit/Runner/HookMethod/HookMethodCollectionTest.php b/tests/unit/Runner/HookMethod/HookMethodCollectionTest.php new file mode 100644 index 00000000000..600bd95c4c7 --- /dev/null +++ b/tests/unit/Runner/HookMethod/HookMethodCollectionTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(HookMethodCollection::class)] +#[Small] +final class HookMethodCollectionTest extends TestCase +{ + public static function provider(): iterable + { + return [ + [ + HookMethodCollection::defaultBeforeClass()->add(new HookMethod('someMethod', 0)), + ['someMethod', 'setUpBeforeClass'], + ], + [ + HookMethodCollection::defaultBefore()->add(new HookMethod('someMethod', 0)), + ['someMethod', 'setUp'], + ], + [ + HookMethodCollection::defaultPreCondition()->add(new HookMethod('someMethod', 0)), + ['someMethod', 'assertPreConditions'], + ], + [ + HookMethodCollection::defaultPostCondition()->add(new HookMethod('someMethod', 0)), + ['assertPostConditions', 'someMethod'], + ], + [ + HookMethodCollection::defaultAfter()->add(new HookMethod('someMethod', 0)), + ['tearDown', 'someMethod'], + ], + [ + HookMethodCollection::defaultAfterClass()->add(new HookMethod('someMethod', 0)), + ['tearDownAfterClass', 'someMethod'], + ], + [ + HookMethodCollection::defaultBeforeClass() + ->add(new HookMethod('methodWithHighPriority', priority: 1)) + ->add(new HookMethod('methodWithVeryLowPriority', priority: -10)) + ->add(new HookMethod('methodWithLowPriority', priority: -1)) + ->add(new HookMethod('methodWithVeryHighPriority', priority: 10)) + ->add(new HookMethod('methodWithoutPriority', 0)), + [ + 'methodWithVeryHighPriority', + 'methodWithHighPriority', + 'methodWithoutPriority', + 'setUpBeforeClass', + 'methodWithLowPriority', + 'methodWithVeryLowPriority', + ], + ], + ]; + } + + #[DataProvider('provider')] + public function testIterator(HookMethodCollection $hookMethodsCollection, array $expected): void + { + $this->assertSame($expected, $hookMethodsCollection->methodNamesSortedByPriority()); + } +} diff --git a/tests/unit/Runner/Phpt/ParserTest.php b/tests/unit/Runner/Phpt/ParserTest.php new file mode 100644 index 00000000000..8abec6ddf28 --- /dev/null +++ b/tests/unit/Runner/Phpt/ParserTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Phpt; + +use function glob; +use function str_replace; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Parser::class)] +#[Small] +final class ParserTest extends TestCase +{ + /** + * @return non-empty-array + */ + public static function unsupportedSections(): array + { + $data = []; + + foreach (glob(__DIR__ . '/../../../_files/phpt/unsupported/*.phpt') as $file) { + $data[str_replace([__DIR__ . '/../../../_files/phpt/unsupported/', '.phpt'], '', $file)] = [$file]; + } + + return $data; + } + + /** + * @return non-empty-list + */ + public static function invalidFiles(): array + { + $data = []; + + foreach (glob(__DIR__ . '/../../../_files/phpt/invalid/*.phpt') as $file) { + $data[] = [$file]; + } + + return $data; + } + + /** + * @param non-empty-string $file + */ + #[DataProvider('unsupportedSections')] + #[TestDox('PHPT section --$_dataName-- is not supported')] + public function testRejectsUnsupportedSections(string $file): void + { + $parser = new Parser; + + $this->expectException(UnsupportedPhptSectionException::class); + + $parser->parse($file); + } + + /** + * @param non-empty-string $file + */ + #[DataProvider('invalidFiles')] + public function testRejectsInvalidPhptFile(string $file): void + { + $parser = new Parser; + + $this->expectException(InvalidPhptFileException::class); + + $parser->parse($file); + } +} diff --git a/tests/unit/Runner/ResultCache/DefaultResultCacheTest.php b/tests/unit/Runner/ResultCache/DefaultResultCacheTest.php index 61af997f59c..c2c4224b1d6 100644 --- a/tests/unit/Runner/ResultCache/DefaultResultCacheTest.php +++ b/tests/unit/Runner/ResultCache/DefaultResultCacheTest.php @@ -68,4 +68,32 @@ public function testCanPersistCacheToFile(): void unlink($cacheFile); } + + public function testCanBeMerged(): void + { + $cacheSourceOne = new DefaultResultCache; + $cacheSourceOne->setStatus('status.a', TestStatus::skipped()); + $cacheSourceOne->setStatus('status.b', TestStatus::incomplete()); + $cacheSourceOne->setTime('time.a', 1); + $cacheSourceOne->setTime('time.b', 2); + $cacheSourceTwo = new DefaultResultCache; + $cacheSourceTwo->setStatus('status.c', TestStatus::failure()); + $cacheSourceTwo->setTime('time.c', 4); + + $sum = new DefaultResultCache; + $sum->mergeWith($cacheSourceOne); + + $this->assertSame(TestStatus::skipped()->asString(), $sum->status('status.a')->asString()); + $this->assertSame(TestStatus::incomplete()->asString(), $sum->status('status.b')->asString()); + $this->assertNotSame(TestStatus::failure()->asString(), $sum->status('status.c')->asString()); + + $this->assertSame(1.0, $sum->time('time.a')); + $this->assertSame(2.0, $sum->time('time.b')); + $this->assertNotSame(4.0, $sum->time('time.c')); + + $sum->mergeWith($cacheSourceTwo); + + $this->assertSame(TestStatus::failure()->asString(), $sum->status('status.c')->asString()); + $this->assertSame(4.0, $sum->time('time.c')); + } } diff --git a/tests/unit/Runner/TestSuiteSorterTest.php b/tests/unit/Runner/TestSuiteSorterTest.php index b56a0835b12..609b9b680a1 100644 --- a/tests/unit/Runner/TestSuiteSorterTest.php +++ b/tests/unit/Runner/TestSuiteSorterTest.php @@ -18,20 +18,15 @@ use PHPUnit\Framework\TestSuite; use PHPUnit\Runner\ResultCache\DefaultResultCache; use PHPUnit\TestFixture\MultiDependencyTest; -use PHPUnit\TestFixture\TestWithDifferentSizes; use ReflectionClass; #[CoversClass(TestSuiteSorter::class)] #[Small] final class TestSuiteSorterTest extends TestCase { - /** - * Constants to improve clarity of @dataprovider. - */ - private const IGNORE_DEPENDENCIES = false; - - private const RESOLVE_DEPENDENCIES = true; - private const MULTIDEPENDENCYTEST_EXECUTION_ORDER = [ + private const bool IGNORE_DEPENDENCIES = false; + private const bool RESOLVE_DEPENDENCIES = true; + private const array MULTI_DEPENDENCY_TEST_EXECUTION_ORDER = [ MultiDependencyTest::class . '::testOne', MultiDependencyTest::class . '::testTwo', MultiDependencyTest::class . '::testThree', @@ -484,7 +479,7 @@ public function testBasicExecutionOrderOptions(int $order, bool $resolveDependen $sorter->reorderTestsInSuite($suite, $order, $resolveDependencies, TestSuiteSorter::ORDER_DEFAULT); - $this->assertSame(self::MULTIDEPENDENCYTEST_EXECUTION_ORDER, $sorter->getOriginalExecutionOrder()); + $this->assertSame(self::MULTI_DEPENDENCY_TEST_EXECUTION_ORDER, $sorter->getOriginalExecutionOrder()); $this->assertSame($expectedOrder, $sorter->getExecutionOrder()); } @@ -590,26 +585,4 @@ public function testSuiteSorterDefectsOptions(int $order, bool $resolveDependenc $this->assertSame($expected, $sorter->getExecutionOrder()); } - - public function testOrderBySize(): void - { - $suite = TestSuite::empty('test suite name'); - $suite->addTestSuite(new ReflectionClass(TestWithDifferentSizes::class)); - $sorter = new TestSuiteSorter; - - $sorter->reorderTestsInSuite($suite, TestSuiteSorter::ORDER_SIZE, true, TestSuiteSorter::ORDER_DEFAULT); - - $expectedOrder = [ - TestWithDifferentSizes::class . '::testDataProviderWithSizeSmall with data set #0', - TestWithDifferentSizes::class . '::testDataProviderWithSizeSmall with data set #1', - TestWithDifferentSizes::class . '::testDataProviderWithSizeMedium with data set #0', - TestWithDifferentSizes::class . '::testDataProviderWithSizeMedium with data set #1', - TestWithDifferentSizes::class . '::testWithSizeMedium', - TestWithDifferentSizes::class . '::testWithSizeLarge', - TestWithDifferentSizes::class . '::testWithSizeSmall', - TestWithDifferentSizes::class . '::testWithSizeUnknown', - ]; - - $this->assertSame($expectedOrder, $sorter->getExecutionOrder()); - } } diff --git a/tests/unit/TextUI/AbstractSouceFilterTestCase.php b/tests/unit/TextUI/AbstractSouceFilterTestCase.php new file mode 100644 index 00000000000..accc20b1a64 --- /dev/null +++ b/tests/unit/TextUI/AbstractSouceFilterTestCase.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use const DIRECTORY_SEPARATOR; +use function ltrim; +use function realpath; +use function str_replace; +use PHPUnit\Framework\TestCase; + +abstract class AbstractSouceFilterTestCase extends TestCase +{ + protected static function createSource( + ?FilterDirectoryCollection $includeDirectories = null, + ?FilterDirectoryCollection $excludeDirectories = null, + ?FileCollection $includeFiles = null, + ?FileCollection $excludeFiles = null, + ): Source { + return new Source( + null, + false, + $includeDirectories ?? FilterDirectoryCollection::fromArray([]), + $includeFiles ?? FileCollection::fromArray([]), + $excludeDirectories ?? FilterDirectoryCollection::fromArray([]), + $excludeFiles ?? FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + } + + protected static function fixturePath(?string $subPath = null): string + { + $path = realpath(__DIR__ . '/../..') . '/_files/source-filter'; + + if ($subPath !== null) { + $path = $path . '/' . ltrim($subPath, '/'); + } + + return str_replace('/', DIRECTORY_SEPARATOR, $path); + } +} diff --git a/tests/unit/TextUI/Command/Commands/VersionCheckCommandTest.php b/tests/unit/TextUI/Command/Commands/VersionCheckCommandTest.php new file mode 100644 index 00000000000..7b3936a7b62 --- /dev/null +++ b/tests/unit/TextUI/Command/Commands/VersionCheckCommandTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Command; + +use const PHP_EOL; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\MockObject\Stub; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\Http\Downloader; + +#[CoversClass(VersionCheckCommand::class)] +#[Small] +final class VersionCheckCommandTest extends TestCase +{ + /** + * @return non-empty-list + */ + public static function provider(): array + { + return [ + [ + 'You are using the latest version of PHPUnit.' . PHP_EOL, + Result::SUCCESS, + 10, + '10.5.0', + '10.5.0', + '10.5.0', + ], + [ + 'You are not using the latest version of PHPUnit.' . PHP_EOL . + 'The latest version compatible with PHPUnit 10.5.0 is PHPUnit 10.5.1.' . PHP_EOL . + 'The latest version is PHPUnit 10.5.1.' . PHP_EOL, + Result::FAILURE, + 10, + '10.5.0', + '10.5.1', + '10.5.1', + ], + [ + 'You are not using the latest version of PHPUnit.' . PHP_EOL . + 'The latest version compatible with PHPUnit 10.5.0 is PHPUnit 10.5.1.' . PHP_EOL . + 'The latest version is PHPUnit 11.0.0.' . PHP_EOL, + Result::FAILURE, + 10, + '10.5.0', + '11.0.0', + '10.5.1', + ], + ]; + } + + /** + * @param non-empty-string $expectedMessage + * @param non-negative-int $expectedShellExitCode + * @param positive-int $majorVersionNumber + * @param non-empty-string $versionId + * @param non-empty-string $latestVersion + * @param non-empty-string $latestCompatibleVersion + */ + #[DataProvider('provider')] + public function testChecksVersion(string $expectedMessage, int $expectedShellExitCode, int $majorVersionNumber, string $versionId, string $latestVersion, string $latestCompatibleVersion): void + { + $command = new VersionCheckCommand( + $this->downloader($latestVersion, $latestCompatibleVersion), + $majorVersionNumber, + $versionId, + ); + + $result = $command->execute(); + + $this->assertSame($expectedMessage, $result->output()); + $this->assertSame($expectedShellExitCode, $result->shellExitCode()); + } + + private function downloader(string $latestVersion, string $latestCompatibleVersion): Downloader&Stub + { + $downloader = $this->createStub(Downloader::class); + + $downloader + ->method('download') + ->willReturn($latestVersion, $latestCompatibleVersion); + + return $downloader; + } +} diff --git a/tests/unit/TextUI/Configuration/Cli/BuilderTest.php b/tests/unit/TextUI/Configuration/Cli/BuilderTest.php new file mode 100644 index 00000000000..0fbe7ca0f1c --- /dev/null +++ b/tests/unit/TextUI/Configuration/Cli/BuilderTest.php @@ -0,0 +1,2413 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\CliArguments; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\TestSuiteSorter; + +#[CoversClass(Builder::class)] +#[CoversClass(Configuration::class)] +#[Small] +#[TestDox('CLI Options Parser')] +final class BuilderTest extends TestCase +{ + #[TestDox('argument')] + public function testArguments(): void + { + $configuration = (new Builder)->fromParameters(['command', 'argument']); + + $this->assertSame(['argument'], $configuration->arguments()); + } + + #[TestDox('--colors')] + public function testColorsImplicitAuto(): void + { + $configuration = (new Builder)->fromParameters(['--colors']); + + $this->assertTrue($configuration->hasColors()); + $this->assertSame('auto', $configuration->colors()); + } + + #[TestDox('--colors=auto')] + public function testColorsExplicitAuto(): void + { + $configuration = (new Builder)->fromParameters(['--colors=auto']); + + $this->assertTrue($configuration->hasColors()); + $this->assertSame('auto', $configuration->colors()); + } + + #[TestDox('--colors=always')] + public function testColorsAlways(): void + { + $configuration = (new Builder)->fromParameters(['--colors=always']); + + $this->assertTrue($configuration->hasColors()); + $this->assertSame('always', $configuration->colors()); + } + + #[TestDox('--colors=never')] + public function testColorsNever(): void + { + $configuration = (new Builder)->fromParameters(['--colors=never']); + + $this->assertTrue($configuration->hasColors()); + $this->assertSame('never', $configuration->colors()); + } + + public function testColorsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasColors()); + + $this->expectException(Exception::class); + + $configuration->colors(); + } + + #[TestDox('--bootstrap script.php')] + public function testBootstrap(): void + { + $configuration = (new Builder)->fromParameters(['--bootstrap', 'script.php']); + + $this->assertTrue($configuration->hasBootstrap()); + $this->assertSame('script.php', $configuration->bootstrap()); + } + + public function testBootstrapMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasBootstrap()); + + $this->expectException(Exception::class); + + $configuration->bootstrap(); + } + + #[TestDox('--cache-directory directory')] + public function testCacheDirectory(): void + { + $configuration = (new Builder)->fromParameters(['--cache-directory', 'directory']); + + $this->assertTrue($configuration->hasCacheDirectory()); + $this->assertSame('directory', $configuration->cacheDirectory()); + } + + public function testCacheDirectoryMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCacheDirectory()); + + $this->expectException(Exception::class); + + $configuration->cacheDirectory(); + } + + #[TestDox('--cache-result')] + public function testCacheResult(): void + { + $configuration = (new Builder)->fromParameters(['--cache-result']); + + $this->assertTrue($configuration->hasCacheResult()); + $this->assertTrue($configuration->cacheResult()); + } + + #[TestDox('--do-not-cache-result')] + public function testDoNotCacheResult(): void + { + $configuration = (new Builder)->fromParameters(['--do-not-cache-result']); + + $this->assertTrue($configuration->hasCacheResult()); + $this->assertFalse($configuration->cacheResult()); + } + + public function testCacheResultMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCacheResult()); + + $this->expectException(Exception::class); + + $configuration->cacheResult(); + } + + #[TestDox('--columns ')] + public function testColumnsNumber(): void + { + $configuration = (new Builder)->fromParameters(['--columns', '100']); + + $this->assertTrue($configuration->hasColumns()); + $this->assertSame(100, $configuration->columns()); + } + + #[TestDox('--columns max')] + public function testColumnsMax(): void + { + $configuration = (new Builder)->fromParameters(['--columns', 'max']); + + $this->assertTrue($configuration->hasColumns()); + $this->assertSame('max', $configuration->columns()); + } + + public function testColumnsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasColumns()); + + $this->expectException(Exception::class); + + $configuration->columns(); + } + + #[TestDox('-c file')] + public function testConfigurationShort(): void + { + $configuration = (new Builder)->fromParameters(['-c', 'file']); + + $this->assertTrue($configuration->hasConfigurationFile()); + $this->assertSame('file', $configuration->configurationFile()); + } + + #[TestDox('--configuration file')] + public function testConfiguration(): void + { + $configuration = (new Builder)->fromParameters(['--configuration', 'file']); + + $this->assertTrue($configuration->hasConfigurationFile()); + $this->assertSame('file', $configuration->configurationFile()); + } + + public function testConfigurationMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasConfigurationFile()); + + $this->expectException(Exception::class); + + $configuration->configurationFile(); + } + + #[TestDox('--warm-coverage-cache')] + public function testWarmCoverageCache(): void + { + $configuration = (new Builder)->fromParameters(['--warm-coverage-cache']); + + $this->assertTrue($configuration->warmCoverageCache()); + } + + public function testWarmCoverageCacheMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->warmCoverageCache()); + } + + #[TestDox('--coverage-clover file')] + public function testCoverageClover(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-clover', 'file']); + + $this->assertTrue($configuration->hasCoverageClover()); + $this->assertSame('file', $configuration->coverageClover()); + } + + public function testCoverageCloverMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageClover()); + + $this->expectException(Exception::class); + + $configuration->coverageClover(); + } + + #[TestDox('--coverage-cobertura file')] + public function testCoverageCobertura(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-cobertura', 'file']); + + $this->assertTrue($configuration->hasCoverageCobertura()); + $this->assertSame('file', $configuration->coverageCobertura()); + } + + public function testCoverageCoberturaMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageCobertura()); + + $this->expectException(Exception::class); + + $configuration->coverageCobertura(); + } + + #[TestDox('--coverage-crap4j file')] + public function testCoverageCrap4j(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-crap4j', 'file']); + + $this->assertTrue($configuration->hasCoverageCrap4J()); + $this->assertSame('file', $configuration->coverageCrap4J()); + } + + public function testCoverageCrap4jMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageCrap4J()); + + $this->expectException(Exception::class); + + $configuration->coverageCrap4J(); + } + + #[TestDox('--coverage-html directory')] + public function testCoverageHtml(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-html', 'directory']); + + $this->assertTrue($configuration->hasCoverageHtml()); + $this->assertSame('directory', $configuration->coverageHtml()); + } + + public function testCoverageHtmlMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageHtml()); + + $this->expectException(Exception::class); + + $configuration->coverageHtml(); + } + + #[TestDox('--coverage-php file')] + public function testCoveragePhp(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-php', 'file']); + + $this->assertTrue($configuration->hasCoveragePhp()); + $this->assertSame('file', $configuration->coveragePhp()); + } + + public function testCoveragePhpMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoveragePhp()); + + $this->expectException(Exception::class); + + $configuration->coveragePhp(); + } + + #[TestDox('--coverage-text')] + public function testCoverageText(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-text']); + + $this->assertTrue($configuration->hasCoverageText()); + $this->assertSame('php://stdout', $configuration->coverageText()); + } + + #[TestDox('--coverage-text=file')] + public function testCoverageTextFile(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-text=file']); + + $this->assertTrue($configuration->hasCoverageText()); + $this->assertSame('file', $configuration->coverageText()); + } + + public function testCoverageTextMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageText()); + + $this->expectException(Exception::class); + + $configuration->coverageText(); + } + + #[TestDox('--only-summary-for-coverage-text')] + public function testOnlySummaryForCoverageText(): void + { + $configuration = (new Builder)->fromParameters(['--only-summary-for-coverage-text']); + + $this->assertTrue($configuration->hasCoverageTextShowOnlySummary()); + $this->assertTrue($configuration->coverageTextShowOnlySummary()); + } + + public function testOnlySummaryForCoverageTextMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageTextShowOnlySummary()); + + $this->expectException(Exception::class); + + $configuration->coverageTextShowOnlySummary(); + } + + #[TestDox('--show-uncovered-for-coverage-text')] + public function testShowUncoveredForCoverageText(): void + { + $configuration = (new Builder)->fromParameters(['--show-uncovered-for-coverage-text']); + + $this->assertTrue($configuration->hasCoverageTextShowUncoveredFiles()); + $this->assertTrue($configuration->coverageTextShowUncoveredFiles()); + } + + public function testShowUncoveredForCoverageTextMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageTextShowUncoveredFiles()); + + $this->expectException(Exception::class); + + $configuration->coverageTextShowUncoveredFiles(); + } + + #[TestDox('--coverage-xml directory')] + public function testCoverageXml(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-xml', 'directory']); + + $this->assertTrue($configuration->hasCoverageXml()); + $this->assertSame('directory', $configuration->coverageXml()); + } + + public function testCoverageXmlMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageXml()); + + $this->expectException(Exception::class); + + $configuration->coverageXml(); + } + + #[TestDox('--path-coverage')] + public function testPathCoverage(): void + { + $configuration = (new Builder)->fromParameters(['--path-coverage']); + + $this->assertTrue($configuration->hasPathCoverage()); + $this->assertTrue($configuration->pathCoverage()); + } + + public function testPathCoverageMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasPathCoverage()); + + $this->expectException(Exception::class); + + $configuration->pathCoverage(); + } + + #[TestDox('-d foo=bar')] + public function testIniSetting(): void + { + $configuration = (new Builder)->fromParameters(['-d', 'foo=bar']); + + $this->assertTrue($configuration->hasIniSettings()); + $this->assertSame(['foo' => 'bar'], $configuration->iniSettings()); + } + + #[TestDox('-d foo')] + public function testIniSetting2(): void + { + $configuration = (new Builder)->fromParameters(['-d', 'foo']); + + $this->assertTrue($configuration->hasIniSettings()); + $this->assertSame(['foo' => '1'], $configuration->iniSettings()); + } + + public function testIniSettingMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasIniSettings()); + + $this->expectException(Exception::class); + + $configuration->iniSettings(); + } + + #[TestDox('-h')] + public function testHelpShort(): void + { + $configuration = (new Builder)->fromParameters(['-h']); + + $this->assertTrue($configuration->help()); + } + + #[TestDox('--help')] + public function testHelp(): void + { + $configuration = (new Builder)->fromParameters(['--help']); + + $this->assertTrue($configuration->help()); + } + + public function testHelpMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->help()); + } + + #[TestDox('--filter string')] + public function testFilter(): void + { + $configuration = (new Builder)->fromParameters(['--filter', 'string']); + + $this->assertTrue($configuration->hasFilter()); + $this->assertSame('string', $configuration->filter()); + } + + public function testFilterMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFilter()); + + $this->expectException(Exception::class); + + $configuration->filter(); + } + + #[TestDox('--exclude-filter string')] + public function testExcludeFilter(): void + { + $configuration = (new Builder)->fromParameters(['--exclude-filter', 'string']); + + $this->assertTrue($configuration->hasExcludeFilter()); + $this->assertSame('string', $configuration->excludeFilter()); + } + + public function testExcludeFilterMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasExcludeFilter()); + + $this->expectException(Exception::class); + + $configuration->excludeFilter(); + } + + #[TestDox('--testsuite string')] + public function testTestSuite(): void + { + $configuration = (new Builder)->fromParameters(['--testsuite', 'string']); + + $this->assertTrue($configuration->hasTestSuite()); + $this->assertSame('string', $configuration->testSuite()); + } + + public function testTestSuiteMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestSuite()); + + $this->expectException(Exception::class); + + $configuration->testSuite(); + } + + #[TestDox('--exclude-testsuite string')] + public function testExcludeTestSuite(): void + { + $configuration = (new Builder)->fromParameters(['--exclude-testsuite', 'string']); + + $this->assertTrue($configuration->hasExcludedTestSuite()); + $this->assertSame('string', $configuration->excludedTestSuite()); + } + + public function testExcludeTestSuiteMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasExcludedTestSuite()); + + $this->expectException(Exception::class); + + $configuration->excludedTestSuite(); + } + + #[TestDox('--generate-baseline file')] + public function testGenerateBaseline(): void + { + $configuration = (new Builder)->fromParameters(['--generate-baseline', 'file']); + + $this->assertTrue($configuration->hasGenerateBaseline()); + $this->assertStringEndsWith('file', $configuration->generateBaseline()); + } + + #[TestDox('--generate-baseline /path/to/file')] + public function testGenerateBaselineWithPathToFile(): void + { + $configuration = (new Builder)->fromParameters(['--generate-baseline', '/path/to/file']); + + $this->assertTrue($configuration->hasGenerateBaseline()); + $this->assertSame('/path/to/file', $configuration->generateBaseline()); + } + + public function testGenerateBaselineMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasGenerateBaseline()); + + $this->expectException(Exception::class); + + $configuration->generateBaseline(); + } + + #[TestDox('--use-baseline file')] + public function testUseBaseline(): void + { + $configuration = (new Builder)->fromParameters(['--use-baseline', 'file']); + + $this->assertTrue($configuration->hasUseBaseline()); + $this->assertStringEndsWith('file', $configuration->useBaseline()); + } + + #[TestDox('--use-baseline /path/to/file')] + public function testUseBaselineWithPathToFile(): void + { + $configuration = (new Builder)->fromParameters(['--use-baseline', '/path/to/file']); + + $this->assertTrue($configuration->hasUseBaseline()); + $this->assertSame('/path/to/file', $configuration->useBaseline()); + } + + public function testUseBaselineMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasUseBaseline()); + + $this->expectException(Exception::class); + + $configuration->useBaseline(); + } + + #[TestDox('--ignore-baseline')] + public function testIgnoreBaseline(): void + { + $configuration = (new Builder)->fromParameters(['--ignore-baseline']); + + $this->assertTrue($configuration->ignoreBaseline()); + } + + #[TestDox('--generate-configuration')] + public function testGenerateConfiguration(): void + { + $configuration = (new Builder)->fromParameters(['--generate-configuration']); + + $this->assertTrue($configuration->generateConfiguration()); + } + + #[TestDox('--migrate-configuration')] + public function testMigrateConfiguration(): void + { + $configuration = (new Builder)->fromParameters(['--migrate-configuration']); + + $this->assertTrue($configuration->migrateConfiguration()); + } + + #[TestDox('--group string')] + public function testGroup(): void + { + $configuration = (new Builder)->fromParameters(['--group', 'string']); + + $this->assertTrue($configuration->hasGroups()); + $this->assertSame(['string'], $configuration->groups()); + } + + #[TestDox('--group string --group another-string')] + public function testGroups(): void + { + $configuration = (new Builder)->fromParameters(['--group', 'string', '--group', 'another-string']); + + $this->assertTrue($configuration->hasGroups()); + $this->assertSame(['string', 'another-string'], $configuration->groups()); + } + + public function testGroupMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasGroups()); + + $this->expectException(Exception::class); + + $configuration->groups(); + } + + #[TestDox('--exclude-group string')] + public function testExcludeGroup(): void + { + $configuration = (new Builder)->fromParameters(['--exclude-group', 'string']); + + $this->assertTrue($configuration->hasExcludeGroups()); + $this->assertSame(['string'], $configuration->excludeGroups()); + } + + #[TestDox('--exclude-group string --exclude-group another-string')] + public function testExcludeGroups(): void + { + $configuration = (new Builder)->fromParameters(['--exclude-group', 'string', '--exclude-group', 'another-string']); + + $this->assertTrue($configuration->hasExcludeGroups()); + $this->assertSame(['string', 'another-string'], $configuration->excludeGroups()); + } + + public function testExcludeGroupMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasGroups()); + + $this->expectException(Exception::class); + + $configuration->excludeGroups(); + } + + #[TestDox('--covers Foo\\Bar\\Baz')] + public function testCovers(): void + { + $configuration = (new Builder)->fromParameters(['--covers', 'Foo\\Bar\\Baz']); + + $this->assertTrue($configuration->hasTestsCovering()); + $this->assertSame(['foo\\bar\\baz'], $configuration->testsCovering()); + } + + public function testCoversMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestsCovering()); + + $this->expectException(Exception::class); + + $configuration->testsCovering(); + } + + #[TestDox('--uses Foo\\Bar\\Baz')] + public function testUses(): void + { + $configuration = (new Builder)->fromParameters(['--uses', 'Foo\\Bar\\Baz']); + + $this->assertTrue($configuration->hasTestsUsing()); + $this->assertSame(['foo\\bar\\baz'], $configuration->testsUsing()); + } + + public function testUsesMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestsUsing()); + + $this->expectException(Exception::class); + + $configuration->testsUsing(); + } + + #[TestDox('--requires-php-extension extension')] + public function testRequiresPhpExtension(): void + { + $configuration = (new Builder)->fromParameters(['--requires-php-extension', 'extension']); + + $this->assertTrue($configuration->hasTestsRequiringPhpExtension()); + $this->assertSame(['extension'], $configuration->testsRequiringPhpExtension()); + } + + public function testRequiresPhpExtensionMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestsRequiringPhpExtension()); + + $this->expectException(Exception::class); + + $configuration->testsRequiringPhpExtension(); + } + + #[TestDox('--test-suffix string')] + public function testTestSuffix(): void + { + $configuration = (new Builder)->fromParameters(['--test-suffix', 'string']); + + $this->assertTrue($configuration->hasTestSuffixes()); + $this->assertSame(['string'], $configuration->testSuffixes()); + } + + #[TestDox('--test-suffix string --test-suffix another-string')] + public function testTestSuffixes(): void + { + $configuration = (new Builder)->fromParameters(['--test-suffix', 'string', '--test-suffix', 'another-string']); + + $this->assertTrue($configuration->hasTestSuffixes()); + $this->assertSame(['string', 'another-string'], $configuration->testSuffixes()); + } + + public function testTestSuffixMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestSuffixes()); + + $this->expectException(Exception::class); + + $configuration->testSuffixes(); + } + + #[TestDox('--include-path string')] + public function testIncludePath(): void + { + $configuration = (new Builder)->fromParameters(['--include-path', 'string']); + + $this->assertTrue($configuration->hasIncludePath()); + $this->assertSame('string', $configuration->includePath()); + } + + public function testIncludePathMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasIncludePath()); + + $this->expectException(Exception::class); + + $configuration->includePath(); + } + + #[TestDox('--list-groups')] + public function testListGroups(): void + { + $configuration = (new Builder)->fromParameters(['--list-groups']); + + $this->assertTrue($configuration->listGroups()); + } + + #[TestDox('--list-suites')] + public function testListSuites(): void + { + $configuration = (new Builder)->fromParameters(['--list-suites']); + + $this->assertTrue($configuration->listSuites()); + } + + #[TestDox('--list-test-files')] + public function testListTestFiles(): void + { + $configuration = (new Builder)->fromParameters(['--list-test-files']); + + $this->assertTrue($configuration->listTestFiles()); + } + + #[TestDox('--list-tests')] + public function testListTests(): void + { + $configuration = (new Builder)->fromParameters(['--list-tests']); + + $this->assertTrue($configuration->listTests()); + } + + #[TestDox('--list-tests-xml file')] + public function testListTestsXml(): void + { + $configuration = (new Builder)->fromParameters(['--list-tests-xml', 'file']); + + $this->assertTrue($configuration->hasListTestsXml()); + $this->assertSame('file', $configuration->listTestsXml()); + } + + public function testListTestsXmlMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasListTestsXml()); + + $this->expectException(Exception::class); + + $configuration->listTestsXml(); + } + + #[TestDox('--log-events-text file')] + public function testEventsText(): void + { + $configuration = (new Builder)->fromParameters(['--log-events-text', 'file']); + + $this->assertTrue($configuration->hasLogEventsText()); + $this->assertStringEndsWith('file', $configuration->logEventsText()); + } + + #[TestDox('--log-events-text /invalid/path')] + public function testEventsTextInvalidPath(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('The path "/invalid/path" specified for the --log-events-text option could not be resolved'); + + (new Builder)->fromParameters(['--log-events-text', '/invalid/path']); + } + + public function testEventsTextMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasLogEventsText()); + + $this->expectException(Exception::class); + + $configuration->logEventsText(); + } + + #[TestDox('--log-events-verbose-text file')] + public function testEventsVerboseText(): void + { + $configuration = (new Builder)->fromParameters(['--log-events-verbose-text', 'file']); + + $this->assertTrue($configuration->hasLogEventsVerboseText()); + $this->assertStringEndsWith('file', $configuration->logEventsVerboseText()); + } + + #[TestDox('--log-events-verbose-text /invalid/path')] + public function testEventsVerboseTextInvalidPath(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('The path "/invalid/path" specified for the --log-events-verbose-text option could not be resolved'); + + (new Builder)->fromParameters(['--log-events-verbose-text', '/invalid/path']); + } + + public function testEventsVerboseTextMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasLogEventsVerboseText()); + + $this->expectException(Exception::class); + + $configuration->logEventsVerboseText(); + } + + #[TestDox('--log-junit file')] + public function testLogJunit(): void + { + $configuration = (new Builder)->fromParameters(['--log-junit', 'file']); + + $this->assertTrue($configuration->hasJunitLogfile()); + $this->assertSame('file', $configuration->junitLogfile()); + } + + public function testLogJunitMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasJunitLogfile()); + + $this->expectException(Exception::class); + + $configuration->junitLogfile(); + } + + #[TestDox('--log-teamcity file')] + public function testLogTeamcity(): void + { + $configuration = (new Builder)->fromParameters(['--log-teamcity', 'file']); + + $this->assertTrue($configuration->hasTeamcityLogfile()); + $this->assertSame('file', $configuration->teamcityLogfile()); + } + + public function testLogTeamcityMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTeamcityLogfile()); + + $this->expectException(Exception::class); + + $configuration->teamcityLogfile(); + } + + #[TestDox('--order-by default')] + public function testOrderByDefault(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'default']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_DEFAULT, $configuration->executionOrder()); + $this->assertTrue($configuration->hasExecutionOrderDefects()); + $this->assertSame(TestSuiteSorter::ORDER_DEFAULT, $configuration->executionOrderDefects()); + $this->assertTrue($configuration->resolveDependencies()); + } + + #[TestDox('--order-by defects')] + public function testOrderByDefects(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'defects']); + + $this->assertFalse($configuration->hasExecutionOrder()); + $this->assertTrue($configuration->hasExecutionOrderDefects()); + $this->assertSame(TestSuiteSorter::ORDER_DEFECTS_FIRST, $configuration->executionOrderDefects()); + $this->assertFalse($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by depends')] + public function testOrderByDepends(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'depends']); + + $this->assertFalse($configuration->hasExecutionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by duration')] + public function testOrderByDuration(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'duration']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_DURATION, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertFalse($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by random')] + public function testOrderByRandom(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'random']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_RANDOMIZED, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertFalse($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by reverse')] + public function testOrderByReverse(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'reverse']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_REVERSED, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertFalse($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by size')] + public function testOrderBySize(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'size']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_SIZE, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertFalse($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by depends,defects')] + public function testOrderByDependsDefects(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'depends,defects']); + + $this->assertFalse($configuration->hasExecutionOrder()); + $this->assertTrue($configuration->hasExecutionOrderDefects()); + $this->assertSame(TestSuiteSorter::ORDER_DEFECTS_FIRST, $configuration->executionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by depends,duration')] + public function testOrderByDependsDuration(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'depends,duration']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_DURATION, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by depends,random')] + public function testOrderByDependsRandom(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'depends,random']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_RANDOMIZED, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by depends,reverse')] + public function testOrderByDependsReverse(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'depends,reverse']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_REVERSED, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by depends,size')] + public function testOrderByDependsSize(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'depends,size']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_SIZE, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + } + + #[TestDox('--order-by no-depends')] + public function testOrderByNoDepends(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'no-depends']); + + $this->assertFalse($configuration->hasExecutionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--order-by no-depends,defects')] + public function testOrderByNoDependsDefects(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'no-depends,defects']); + + $this->assertFalse($configuration->hasExecutionOrder()); + $this->assertTrue($configuration->hasExecutionOrderDefects()); + $this->assertSame(TestSuiteSorter::ORDER_DEFECTS_FIRST, $configuration->executionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--order-by no-depends,duration')] + public function testOrderByNoDependsDuration(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'no-depends,duration']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_DURATION, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--order-by no-depends,random')] + public function testOrderByNoDependsRandom(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'no-depends,random']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_RANDOMIZED, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--order-by no-depends,reverse')] + public function testOrderByNoDependsReverse(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'no-depends,reverse']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_REVERSED, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--order-by no-depends,size')] + public function testOrderByNoDependsSize(): void + { + $configuration = (new Builder)->fromParameters(['--order-by', 'no-depends,size']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_SIZE, $configuration->executionOrder()); + $this->assertFalse($configuration->hasExecutionOrderDefects()); + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--order-by invalid')] + public function testOrderByInvalid(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('unrecognized --order-by option: invalid'); + + (new Builder)->fromParameters(['--order-by', 'invalid']); + } + + public function testExecutionOrderMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasExecutionOrder()); + + $this->expectException(Exception::class); + + $configuration->executionOrder(); + } + + public function testExecutionOrderDefectsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasExecutionOrderDefects()); + + $this->expectException(Exception::class); + + $configuration->executionOrderDefects(); + } + + public function testResolveDependenciesMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasResolveDependencies()); + + $this->expectException(Exception::class); + + $configuration->resolveDependencies(); + } + + #[TestDox('--process-isolation')] + public function testProcessIsolation(): void + { + $configuration = (new Builder)->fromParameters(['--process-isolation']); + + $this->assertTrue($configuration->hasProcessIsolation()); + $this->assertTrue($configuration->processIsolation()); + } + + public function testProcessIsolationMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasProcessIsolation()); + + $this->expectException(Exception::class); + + $configuration->processIsolation(); + } + + #[TestDox('--stderr')] + public function testStderr(): void + { + $configuration = (new Builder)->fromParameters(['--stderr']); + + $this->assertTrue($configuration->hasStderr()); + $this->assertTrue($configuration->stderr()); + } + + public function testStderrMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStderr()); + + $this->expectException(Exception::class); + + $configuration->stderr(); + } + + #[TestDox('--fail-on-all-issues')] + public function testFailOnAllIssues(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-all-issues']); + + $this->assertTrue($configuration->hasFailOnAllIssues()); + $this->assertTrue($configuration->failOnAllIssues()); + } + + public function testFailOnAllIssuesMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnAllIssues()); + + $this->expectException(Exception::class); + + $configuration->failOnAllIssues(); + } + + #[TestDox('--fail-on-deprecation')] + public function testFailOnDeprecation(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-deprecation']); + + $this->assertTrue($configuration->hasFailOnDeprecation()); + $this->assertTrue($configuration->failOnDeprecation()); + } + + public function testFailOnDeprecationMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnDeprecation()); + + $this->expectException(Exception::class); + + $configuration->failOnDeprecation(); + } + + #[TestDox('--fail-on-phpunit-deprecation')] + public function testFailOnPhpunitDeprecation(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-phpunit-deprecation']); + + $this->assertTrue($configuration->hasFailOnPhpunitDeprecation()); + $this->assertTrue($configuration->failOnPhpunitDeprecation()); + } + + public function testFailOnPhpunitDeprecationMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnPhpunitDeprecation()); + + $this->expectException(Exception::class); + + $configuration->failOnPhpunitDeprecation(); + } + + #[TestDox('--fail-on-phpunit-notice')] + public function testFailOnPhpunitNotice(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-phpunit-notice']); + + $this->assertTrue($configuration->hasFailOnPhpunitNotice()); + $this->assertTrue($configuration->failOnPhpunitNotice()); + } + + public function testFailOnPhpunitNoticeMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnPhpunitNotice()); + + $this->expectException(Exception::class); + + $configuration->failOnPhpunitNotice(); + } + + #[TestDox('--fail-on-empty-test-suite')] + public function testFailOnEmptyTestSuite(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-empty-test-suite']); + + $this->assertTrue($configuration->hasFailOnEmptyTestSuite()); + $this->assertTrue($configuration->failOnEmptyTestSuite()); + } + + public function testFailOnEmptyTestSuiteMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnEmptyTestSuite()); + + $this->expectException(Exception::class); + + $configuration->failOnEmptyTestSuite(); + } + + #[TestDox('--fail-on-incomplete')] + public function testFailOnIncomplete(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-incomplete']); + + $this->assertTrue($configuration->hasFailOnIncomplete()); + $this->assertTrue($configuration->failOnIncomplete()); + } + + public function testFailOnIncompleteMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnIncomplete()); + + $this->expectException(Exception::class); + + $configuration->failOnIncomplete(); + } + + #[TestDox('--fail-on-notice')] + public function testFailOnNotice(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-notice']); + + $this->assertTrue($configuration->hasFailOnNotice()); + $this->assertTrue($configuration->failOnNotice()); + } + + public function testFailOnNoticeMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnNotice()); + + $this->expectException(Exception::class); + + $configuration->failOnNotice(); + } + + #[TestDox('--fail-on-risky')] + public function testFailOnRisky(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-risky']); + + $this->assertTrue($configuration->hasFailOnRisky()); + $this->assertTrue($configuration->failOnRisky()); + } + + public function testFailOnRiskyMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnRisky()); + + $this->expectException(Exception::class); + + $configuration->failOnRisky(); + } + + #[TestDox('--fail-on-skipped')] + public function testFailOnSkipped(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-skipped']); + + $this->assertTrue($configuration->hasFailOnSkipped()); + $this->assertTrue($configuration->failOnSkipped()); + } + + public function testFailOnSkippedMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnSkipped()); + + $this->expectException(Exception::class); + + $configuration->failOnSkipped(); + } + + #[TestDox('--fail-on-warning')] + public function testFailOnWarning(): void + { + $configuration = (new Builder)->fromParameters(['--fail-on-warning']); + + $this->assertTrue($configuration->hasFailOnWarning()); + $this->assertTrue($configuration->failOnWarning()); + } + + public function testFailOnWarningMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasFailOnWarning()); + + $this->expectException(Exception::class); + + $configuration->failOnWarning(); + } + + #[TestDox('--stop-on-defect')] + public function testStopOnDefect(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-defect']); + + $this->assertTrue($configuration->hasStopOnDefect()); + $this->assertTrue($configuration->stopOnDefect()); + } + + public function testStopOnDefectMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnDefect()); + + $this->expectException(Exception::class); + + $configuration->stopOnDefect(); + } + + #[TestDox('--stop-on-deprecation')] + public function testStopOnDeprecation(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-deprecation']); + + $this->assertTrue($configuration->hasStopOnDeprecation()); + $this->assertTrue($configuration->stopOnDeprecation()); + + $this->expectException(Exception::class); + + $configuration->specificDeprecationToStopOn(); + } + + #[TestDox('--stop-on-deprecation=message')] + public function testStopOnDeprecationMessage(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-deprecation=message']); + + $this->assertTrue($configuration->hasStopOnDeprecation()); + $this->assertTrue($configuration->stopOnDeprecation()); + $this->assertTrue($configuration->hasSpecificDeprecationToStopOn()); + $this->assertSame('message', $configuration->specificDeprecationToStopOn()); + } + + public function testStopOnDeprecationMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnDeprecation()); + + $this->expectException(Exception::class); + + $configuration->stopOnDeprecation(); + } + + #[TestDox('--stop-on-error')] + public function testStopOnError(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-error']); + + $this->assertTrue($configuration->hasStopOnError()); + $this->assertTrue($configuration->stopOnError()); + } + + public function testStopOnErrorMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnError()); + + $this->expectException(Exception::class); + + $configuration->stopOnError(); + } + + #[TestDox('--stop-on-failure')] + public function testStopOnFailure(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-failure']); + + $this->assertTrue($configuration->hasStopOnFailure()); + $this->assertTrue($configuration->stopOnFailure()); + } + + public function testStopOnFailureMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnFailure()); + + $this->expectException(Exception::class); + + $configuration->stopOnFailure(); + } + + #[TestDox('--stop-on-incomplete')] + public function testStopOnIncomplete(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-incomplete']); + + $this->assertTrue($configuration->hasStopOnIncomplete()); + $this->assertTrue($configuration->stopOnIncomplete()); + } + + public function testStopOnIncompleteMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnIncomplete()); + + $this->expectException(Exception::class); + + $configuration->stopOnIncomplete(); + } + + #[TestDox('--stop-on-notice')] + public function testStopOnNotice(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-notice']); + + $this->assertTrue($configuration->hasStopOnNotice()); + $this->assertTrue($configuration->stopOnNotice()); + } + + public function testStopOnNoticeMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnNotice()); + + $this->expectException(Exception::class); + + $configuration->stopOnNotice(); + } + + #[TestDox('--stop-on-risky')] + public function testStopOnRisky(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-risky']); + + $this->assertTrue($configuration->hasStopOnRisky()); + $this->assertTrue($configuration->stopOnRisky()); + } + + public function testStopOnRiskyMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnRisky()); + + $this->expectException(Exception::class); + + $configuration->stopOnRisky(); + } + + #[TestDox('--stop-on-skipped')] + public function testStopOnSkipped(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-skipped']); + + $this->assertTrue($configuration->hasStopOnSkipped()); + $this->assertTrue($configuration->stopOnSkipped()); + } + + public function testStopOnSkippedMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnSkipped()); + + $this->expectException(Exception::class); + + $configuration->stopOnSkipped(); + } + + #[TestDox('--stop-on-warning')] + public function testStopOnWarning(): void + { + $configuration = (new Builder)->fromParameters(['--stop-on-warning']); + + $this->assertTrue($configuration->hasStopOnWarning()); + $this->assertTrue($configuration->stopOnWarning()); + } + + public function testStopOnWarningMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStopOnWarning()); + + $this->expectException(Exception::class); + + $configuration->stopOnWarning(); + } + + #[TestDox('--teamcity')] + public function testTeamcity(): void + { + $configuration = (new Builder)->fromParameters(['--teamcity']); + + $this->assertTrue($configuration->hasTeamCityPrinter()); + $this->assertTrue($configuration->teamCityPrinter()); + } + + public function testTeamcityMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTeamCityPrinter()); + + $this->expectException(Exception::class); + + $configuration->teamCityPrinter(); + } + + #[TestDox('--testdox')] + public function testTestDox(): void + { + $configuration = (new Builder)->fromParameters(['--testdox']); + + $this->assertTrue($configuration->hasTestDoxPrinter()); + $this->assertTrue($configuration->testdoxPrinter()); + } + + public function testTestDoxMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestDoxPrinter()); + + $this->expectException(Exception::class); + + $configuration->testdoxPrinter(); + } + + #[TestDox('--testdox-summary')] + public function testTestDoxPrinterSummary(): void + { + $configuration = (new Builder)->fromParameters(['--testdox-summary']); + + $this->assertTrue($configuration->hasTestDoxPrinterSummary()); + $this->assertTrue($configuration->testdoxPrinterSummary()); + } + + public function testTestDoxPrinterSummaryMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestDoxPrinterSummary()); + + $this->expectException(Exception::class); + + $configuration->testdoxPrinterSummary(); + } + + #[TestDox('--testdox-html file')] + public function testTestDoxHtml(): void + { + $configuration = (new Builder)->fromParameters(['--testdox-html', 'file']); + + $this->assertTrue($configuration->hasTestdoxHtmlFile()); + $this->assertSame('file', $configuration->testdoxHtmlFile()); + } + + public function testTestDoxHtmlMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestdoxHtmlFile()); + + $this->expectException(Exception::class); + + $configuration->testdoxHtmlFile(); + } + + #[TestDox('--testdox-text file')] + public function testTestDoxText(): void + { + $configuration = (new Builder)->fromParameters(['--testdox-text', 'file']); + + $this->assertTrue($configuration->hasTestdoxTextFile()); + $this->assertSame('file', $configuration->testdoxTextFile()); + } + + public function testTestDoxTextMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasTestdoxTextFile()); + + $this->expectException(Exception::class); + + $configuration->testdoxTextFile(); + } + + #[TestDox('--no-configuration')] + public function testNoConfiguration(): void + { + $configuration = (new Builder)->fromParameters(['--no-configuration']); + + $this->assertFalse($configuration->useDefaultConfiguration()); + } + + #[TestDox('--no-extensions')] + public function testNoExtensions(): void + { + $configuration = (new Builder)->fromParameters(['--no-extensions']); + + $this->assertTrue($configuration->hasNoExtensions()); + $this->assertTrue($configuration->noExtensions()); + } + + public function testNoExtensionsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasNoExtensions()); + + $this->expectException(Exception::class); + + $configuration->noExtensions(); + } + + #[TestDox('--no-coverage')] + public function testNoCoverage(): void + { + $configuration = (new Builder)->fromParameters(['--no-coverage']); + + $this->assertTrue($configuration->hasNoCoverage()); + $this->assertTrue($configuration->noCoverage()); + } + + public function testNoCoverageMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasNoCoverage()); + + $this->expectException(Exception::class); + + $configuration->noCoverage(); + } + + #[TestDox('--no-logging')] + public function testNoLogging(): void + { + $configuration = (new Builder)->fromParameters(['--no-logging']); + + $this->assertTrue($configuration->hasNoLogging()); + $this->assertTrue($configuration->noLogging()); + } + + public function testNoLoggingMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasNoLogging()); + + $this->expectException(Exception::class); + + $configuration->noLogging(); + } + + #[TestDox('--no-output')] + public function testNoOutput(): void + { + $configuration = (new Builder)->fromParameters(['--no-output']); + + $this->assertTrue($configuration->hasNoOutput()); + $this->assertTrue($configuration->noOutput()); + } + + public function testNoOutputMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasNoOutput()); + + $this->expectException(Exception::class); + + $configuration->noOutput(); + } + + #[TestDox('--no-progress')] + public function testNoProgress(): void + { + $configuration = (new Builder)->fromParameters(['--no-progress']); + + $this->assertTrue($configuration->hasNoProgress()); + $this->assertTrue($configuration->noProgress()); + } + + public function testNoProgressMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasNoProgress()); + + $this->expectException(Exception::class); + + $configuration->noProgress(); + } + + #[TestDox('--no-results')] + public function testNoResults(): void + { + $configuration = (new Builder)->fromParameters(['--no-results']); + + $this->assertTrue($configuration->hasNoResults()); + $this->assertTrue($configuration->noResults()); + } + + public function testNoResultsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasNoResults()); + + $this->expectException(Exception::class); + + $configuration->noResults(); + } + + #[TestDox('--globals-backup')] + public function testGlobalsBackup(): void + { + $configuration = (new Builder)->fromParameters(['--globals-backup']); + + $this->assertTrue($configuration->hasBackupGlobals()); + $this->assertTrue($configuration->backupGlobals()); + } + + public function testGlobalsBackupMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasBackupGlobals()); + + $this->expectException(Exception::class); + + $configuration->backupGlobals(); + } + + #[TestDox('--static-backup')] + public function testStaticBackup(): void + { + $configuration = (new Builder)->fromParameters(['--static-backup']); + + $this->assertTrue($configuration->hasBackupStaticProperties()); + $this->assertTrue($configuration->backupStaticProperties()); + } + + public function testStaticBackupMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasBackupStaticProperties()); + + $this->expectException(Exception::class); + + $configuration->backupStaticProperties(); + } + + #[TestDox('--atleast-version string')] + public function testAtLeastVersion(): void + { + $configuration = (new Builder)->fromParameters(['--atleast-version', 'string']); + + $this->assertTrue($configuration->hasAtLeastVersion()); + $this->assertSame('string', $configuration->atLeastVersion()); + } + + public function testAtLeastVersionMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasAtLeastVersion()); + + $this->expectException(Exception::class); + + $configuration->atLeastVersion(); + } + + #[TestDox('--version')] + public function testVersion(): void + { + $configuration = (new Builder)->fromParameters(['--version']); + + $this->assertTrue($configuration->version()); + } + + #[TestDox('--dont-report-useless-tests')] + public function testDontReportUselessTests(): void + { + $configuration = (new Builder)->fromParameters(['--dont-report-useless-tests']); + + $this->assertTrue($configuration->hasReportUselessTests()); + $this->assertFalse($configuration->reportUselessTests()); + } + + public function testDontReportUselessTestsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasReportUselessTests()); + + $this->expectException(Exception::class); + + $configuration->reportUselessTests(); + } + + #[TestDox('--strict-coverage')] + public function testStrictCoverage(): void + { + $configuration = (new Builder)->fromParameters(['--strict-coverage']); + + $this->assertTrue($configuration->hasStrictCoverage()); + $this->assertTrue($configuration->strictCoverage()); + } + + public function testStrictCoverageMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasStrictCoverage()); + + $this->expectException(Exception::class); + + $configuration->strictCoverage(); + } + + #[TestDox('--disable-coverage-ignore')] + public function testDisableCoverageIgnore(): void + { + $configuration = (new Builder)->fromParameters(['--disable-coverage-ignore']); + + $this->assertTrue($configuration->hasDisableCodeCoverageIgnore()); + $this->assertTrue($configuration->disableCodeCoverageIgnore()); + } + + public function testDisableCoverageIgnoreMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisableCodeCoverageIgnore()); + + $this->expectException(Exception::class); + + $configuration->disableCodeCoverageIgnore(); + } + + #[TestDox('--strict-global-state')] + public function testStrictGlobalState(): void + { + $configuration = (new Builder)->fromParameters(['--strict-global-state']); + + $this->assertTrue($configuration->hasBeStrictAboutChangesToGlobalState()); + $this->assertTrue($configuration->beStrictAboutChangesToGlobalState()); + } + + public function testStrictGlobalStateMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasBeStrictAboutChangesToGlobalState()); + + $this->expectException(Exception::class); + + $configuration->beStrictAboutChangesToGlobalState(); + } + + #[TestDox('--disallow-test-output')] + public function testDisallowTestOutput(): void + { + $configuration = (new Builder)->fromParameters(['--disallow-test-output']); + + $this->assertTrue($configuration->hasDisallowTestOutput()); + $this->assertTrue($configuration->disallowTestOutput()); + } + + public function testDisallowTestOutputMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisallowTestOutput()); + + $this->expectException(Exception::class); + + $configuration->disallowTestOutput(); + } + + #[TestDox('--display-all-issues')] + public function testDisplayAllIssues(): void + { + $configuration = (new Builder)->fromParameters(['--display-all-issues']); + + $this->assertTrue($configuration->hasDisplayDetailsOnAllIssues()); + $this->assertTrue($configuration->displayDetailsOnAllIssues()); + } + + public function testDisplayAllIssuesMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnAllIssues()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnAllIssues(); + } + + #[TestDox('--display-incomplete')] + public function testDisplayIncomplete(): void + { + $configuration = (new Builder)->fromParameters(['--display-incomplete']); + + $this->assertTrue($configuration->hasDisplayDetailsOnIncompleteTests()); + $this->assertTrue($configuration->displayDetailsOnIncompleteTests()); + } + + public function testDisplayIncompleteMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnIncompleteTests()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnIncompleteTests(); + } + + #[TestDox('--display-skipped')] + public function testDisplaySkipped(): void + { + $configuration = (new Builder)->fromParameters(['--display-skipped']); + + $this->assertTrue($configuration->hasDisplayDetailsOnSkippedTests()); + $this->assertTrue($configuration->displayDetailsOnSkippedTests()); + } + + public function testDisplaySkippedMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnSkippedTests()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnSkippedTests(); + } + + #[TestDox('--display-deprecations')] + public function testDisplayDeprecations(): void + { + $configuration = (new Builder)->fromParameters(['--display-deprecations']); + + $this->assertTrue($configuration->hasDisplayDetailsOnTestsThatTriggerDeprecations()); + $this->assertTrue($configuration->displayDetailsOnTestsThatTriggerDeprecations()); + } + + public function testDisplayDeprecationsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnTestsThatTriggerDeprecations()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnTestsThatTriggerDeprecations(); + } + + #[TestDox('--display-phpunit-deprecations')] + public function testDisplayPhpunitDeprecations(): void + { + $configuration = (new Builder)->fromParameters(['--display-phpunit-deprecations']); + + $this->assertTrue($configuration->hasDisplayDetailsOnPhpunitDeprecations()); + $this->assertTrue($configuration->displayDetailsOnPhpunitDeprecations()); + } + + public function testDisplayPhpunitDeprecationsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnPhpunitDeprecations()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnPhpunitDeprecations(); + } + + #[TestDox('--display-phpunit-notices')] + public function testDisplayPhpunitNotices(): void + { + $configuration = (new Builder)->fromParameters(['--display-phpunit-notices']); + + $this->assertTrue($configuration->hasDisplayDetailsOnPhpunitNotices()); + $this->assertTrue($configuration->displayDetailsOnPhpunitNotices()); + } + + public function testDisplayPhpunitNoticesMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnPhpunitNotices()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnPhpunitNotices(); + } + + #[TestDox('--display-errors')] + public function testDisplayErrors(): void + { + $configuration = (new Builder)->fromParameters(['--display-errors']); + + $this->assertTrue($configuration->hasDisplayDetailsOnTestsThatTriggerErrors()); + $this->assertTrue($configuration->displayDetailsOnTestsThatTriggerErrors()); + } + + public function testDisplayErrorsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnTestsThatTriggerErrors()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnTestsThatTriggerErrors(); + } + + #[TestDox('--display-notices')] + public function testDisplayNotices(): void + { + $configuration = (new Builder)->fromParameters(['--display-notices']); + + $this->assertTrue($configuration->hasDisplayDetailsOnTestsThatTriggerNotices()); + $this->assertTrue($configuration->displayDetailsOnTestsThatTriggerNotices()); + } + + public function testDisplayNoticesMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnTestsThatTriggerNotices()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnTestsThatTriggerNotices(); + } + + #[TestDox('--display-warnings')] + public function testDisplayWarnings(): void + { + $configuration = (new Builder)->fromParameters(['--display-warnings']); + + $this->assertTrue($configuration->hasDisplayDetailsOnTestsThatTriggerWarnings()); + $this->assertTrue($configuration->displayDetailsOnTestsThatTriggerWarnings()); + } + + public function testDisplayWarningsMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDisplayDetailsOnTestsThatTriggerWarnings()); + + $this->expectException(Exception::class); + + $configuration->displayDetailsOnTestsThatTriggerWarnings(); + } + + #[TestDox('--default-time-limit ')] + public function testDefaultTimeLimit(): void + { + $configuration = (new Builder)->fromParameters(['--default-time-limit', '10']); + + $this->assertTrue($configuration->hasDefaultTimeLimit()); + $this->assertSame(10, $configuration->defaultTimeLimit()); + } + + public function testDefaultTimeLimitMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasDefaultTimeLimit()); + + $this->expectException(Exception::class); + + $configuration->defaultTimeLimit(); + } + + #[TestDox('--enforce-time-limit')] + public function testEnforceTimeLimit(): void + { + $configuration = (new Builder)->fromParameters(['--enforce-time-limit']); + + $this->assertTrue($configuration->hasEnforceTimeLimit()); + $this->assertTrue($configuration->enforceTimeLimit()); + } + + public function testEnforceTimeLimitMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasEnforceTimeLimit()); + + $this->expectException(Exception::class); + + $configuration->enforceTimeLimit(); + } + + #[TestDox('--reverse-list')] + public function testReverseList(): void + { + $configuration = (new Builder)->fromParameters(['--reverse-list']); + + $this->assertTrue($configuration->hasReverseList()); + $this->assertTrue($configuration->reverseList()); + } + + public function testReverseListMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasReverseList()); + + $this->expectException(Exception::class); + + $configuration->reverseList(); + } + + #[TestDox('--check-version')] + public function testCheckVersion(): void + { + $configuration = (new Builder)->fromParameters(['--check-version']); + + $this->assertTrue($configuration->checkVersion()); + } + + #[TestDox('--coverage-filter directory')] + public function testCoverageFilterDirectory(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-filter', 'directory']); + + $this->assertTrue($configuration->hasCoverageFilter()); + $this->assertSame(['directory'], $configuration->coverageFilter()); + } + + #[TestDox('--coverage-filter directory --coverage-filter another-directory')] + public function testCoverageFilterDirectories(): void + { + $configuration = (new Builder)->fromParameters(['--coverage-filter', 'directory', '--coverage-filter', 'another-directory']); + + $this->assertTrue($configuration->hasCoverageFilter()); + $this->assertSame(['directory', 'another-directory'], $configuration->coverageFilter()); + } + + public function testCoverageFilterMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasCoverageFilter()); + + $this->expectException(Exception::class); + + $configuration->coverageFilter(); + } + + #[TestDox('--random-order')] + public function testRandomOrder(): void + { + $configuration = (new Builder)->fromParameters(['--random-order']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_RANDOMIZED, $configuration->executionOrder()); + } + + #[TestDox('--resolve-dependencies')] + public function testResolveDependencies(): void + { + $configuration = (new Builder)->fromParameters(['--resolve-dependencies']); + + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertTrue($configuration->resolveDependencies()); + } + + #[TestDox('--ignore-dependencies')] + public function testIgnoreDependencies(): void + { + $configuration = (new Builder)->fromParameters(['--ignore-dependencies']); + + $this->assertTrue($configuration->hasResolveDependencies()); + $this->assertFalse($configuration->resolveDependencies()); + } + + #[TestDox('--reverse-order')] + public function testReverseOrder(): void + { + $configuration = (new Builder)->fromParameters(['--reverse-order']); + + $this->assertTrue($configuration->hasExecutionOrder()); + $this->assertSame(TestSuiteSorter::ORDER_REVERSED, $configuration->executionOrder()); + } + + #[TestDox('--random-order-seed')] + public function testRandomOrderSeed(): void + { + $configuration = (new Builder)->fromParameters(['--random-order-seed', '1234']); + + $this->assertTrue($configuration->hasRandomOrderSeed()); + $this->assertSame(1234, $configuration->randomOrderSeed()); + } + + public function testRandomOrderSeedMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasRandomOrderSeed()); + + $this->expectException(Exception::class); + + $configuration->randomOrderSeed(); + } + + #[TestDox('--debug')] + public function testDebug(): void + { + $configuration = (new Builder)->fromParameters(['--debug']); + + $this->assertTrue($configuration->debug()); + } + + #[TestDox('--with-telemetry')] + public function testWithTelemetry(): void + { + $configuration = (new Builder)->fromParameters(['--with-telemetry']); + + $this->assertTrue($configuration->withTelemetry()); + } + + #[TestDox('--extension')] + public function testExtension(): void + { + $configuration = (new Builder)->fromParameters(['--extension', 'ExtensionClass']); + + $this->assertTrue($configuration->hasExtensions()); + $this->assertSame(['ExtensionClass'], $configuration->extensions()); + } + + public function testExtensionMayNotBeConfigured(): void + { + $configuration = (new Builder)->fromParameters([]); + + $this->assertFalse($configuration->hasExtensions()); + + $this->expectException(Exception::class); + + $configuration->extensions(); + } + + public function testInvalidOption(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Unknown option "--invalid-option"'); + + (new Builder)->fromParameters(['--invalid-option']); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/ConstantCollectionTest.php b/tests/unit/TextUI/Configuration/Value/ConstantCollectionTest.php new file mode 100644 index 00000000000..d01426fcf8e --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/ConstantCollectionTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ConstantCollection::class)] +#[CoversClass(ConstantCollectionIterator::class)] +#[UsesClass(Constant::class)] +#[Small] +final class ConstantCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = ConstantCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = ConstantCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = ConstantCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): Constant + { + return new Constant('name', 'value'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/ConstantTest.php b/tests/unit/TextUI/Configuration/Value/ConstantTest.php new file mode 100644 index 00000000000..e06dda4c6ba --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/ConstantTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Constant::class)] +#[Small] +final class ConstantTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('name', (new Constant('name', 'value'))->name()); + } + + public function testHasValue(): void + { + $this->assertSame('value', (new Constant('name', 'value'))->value()); + $this->assertSame(true, (new Constant('name', true))->value()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/DirectoryCollectionTest.php b/tests/unit/TextUI/Configuration/Value/DirectoryCollectionTest.php new file mode 100644 index 00000000000..6031909affd --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/DirectoryCollectionTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(DirectoryCollection::class)] +#[CoversClass(DirectoryCollectionIterator::class)] +#[UsesClass(Directory::class)] +#[Small] +final class DirectoryCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = DirectoryCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = DirectoryCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + $this->assertFalse($elements->isEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = DirectoryCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): Directory + { + return new Directory('path'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/DirectoryTest.php b/tests/unit/TextUI/Configuration/Value/DirectoryTest.php new file mode 100644 index 00000000000..830e51dbca7 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/DirectoryTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Directory::class)] +#[Small] +final class DirectoryTest extends TestCase +{ + public function testHasPath(): void + { + $path = 'path'; + + $this->assertSame($path, (new Directory($path))->path()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/ExtensionBootstrapCollectionTest.php b/tests/unit/TextUI/Configuration/Value/ExtensionBootstrapCollectionTest.php new file mode 100644 index 00000000000..c5aec047506 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/ExtensionBootstrapCollectionTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ExtensionBootstrapCollection::class)] +#[CoversClass(ExtensionBootstrapCollectionIterator::class)] +#[UsesClass(ExtensionBootstrap::class)] +#[Small] +final class ExtensionBootstrapCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = ExtensionBootstrapCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = ExtensionBootstrapCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): ExtensionBootstrap + { + return new ExtensionBootstrap('ClassName', []); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/ExtensionBootstrapTest.php b/tests/unit/TextUI/Configuration/Value/ExtensionBootstrapTest.php new file mode 100644 index 00000000000..7c8c1a45f85 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/ExtensionBootstrapTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ExtensionBootstrap::class)] +#[Small] +final class ExtensionBootstrapTest extends TestCase +{ + public function testHasClassName(): void + { + $className = 'ClassName'; + + $this->assertSame($className, (new ExtensionBootstrap($className, []))->className()); + } + + public function testHasParameters(): void + { + $parameters = ['foo' => 'bar']; + + $this->assertSame($parameters, (new ExtensionBootstrap('ClassName', $parameters))->parameters()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/FileCollectionTest.php b/tests/unit/TextUI/Configuration/Value/FileCollectionTest.php new file mode 100644 index 00000000000..e79ef89df39 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/FileCollectionTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(FileCollection::class)] +#[CoversClass(FileCollectionIterator::class)] +#[UsesClass(File::class)] +#[Small] +final class FileCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = FileCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = FileCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + $this->assertTrue($elements->notEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = FileCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): File + { + return new File('path'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/FileTest.php b/tests/unit/TextUI/Configuration/Value/FileTest.php new file mode 100644 index 00000000000..078dceccf02 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/FileTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(File::class)] +#[Small] +final class FileTest extends TestCase +{ + public function testHasPath(): void + { + $path = 'path'; + + $this->assertSame($path, (new File($path))->path()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/FilterDirectoryCollectionTest.php b/tests/unit/TextUI/Configuration/Value/FilterDirectoryCollectionTest.php new file mode 100644 index 00000000000..5f96b7958fb --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/FilterDirectoryCollectionTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(FilterDirectoryCollection::class)] +#[CoversClass(FilterDirectoryCollectionIterator::class)] +#[UsesClass(FilterDirectory::class)] +#[Small] +final class FilterDirectoryCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = FilterDirectoryCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = FilterDirectoryCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + $this->assertTrue($elements->notEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = FilterDirectoryCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): FilterDirectory + { + return new FilterDirectory('path', 'prefix', 'suffix'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/FilterDirectoryTest.php b/tests/unit/TextUI/Configuration/Value/FilterDirectoryTest.php new file mode 100644 index 00000000000..3294b1bb5fc --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/FilterDirectoryTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(FilterDirectory::class)] +#[Small] +final class FilterDirectoryTest extends TestCase +{ + public function testHasPath(): void + { + $path = 'path'; + + $this->assertSame($path, (new FilterDirectory($path, 'prefix', 'suffix'))->path()); + } + + public function testHasPrefix(): void + { + $prefix = 'prefix'; + + $this->assertSame($prefix, (new FilterDirectory('path', $prefix, 'suffix'))->prefix()); + } + + public function testHasSuffix(): void + { + $suffix = 'suffix'; + + $this->assertSame($suffix, (new FilterDirectory('path', 'prefix', $suffix))->suffix()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/GroupCollectionTest.php b/tests/unit/TextUI/Configuration/Value/GroupCollectionTest.php new file mode 100644 index 00000000000..32a229526ef --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/GroupCollectionTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(GroupCollection::class)] +#[CoversClass(GroupCollectionIterator::class)] +#[UsesClass(Group::class)] +#[Small] +final class GroupCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = GroupCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + $this->assertFalse($elements->isEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = GroupCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + public function testCanBeRepresentedAsArrayOfStrings(): void + { + $elements = GroupCollection::fromArray( + [ + new Group('foo'), + new Group('bar'), + ], + ); + + $this->assertSame(['foo', 'bar'], $elements->asArrayOfStrings()); + } + + private function element(): Group + { + return new Group('name'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/GroupTest.php b/tests/unit/TextUI/Configuration/Value/GroupTest.php new file mode 100644 index 00000000000..604140582b5 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/GroupTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Group::class)] +#[Small] +final class GroupTest extends TestCase +{ + public function testHasPath(): void + { + $name = 'name'; + + $this->assertSame($name, (new Group($name))->name()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/IniSettingCollectionTest.php b/tests/unit/TextUI/Configuration/Value/IniSettingCollectionTest.php new file mode 100644 index 00000000000..11c5f9a6fe8 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/IniSettingCollectionTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(IniSettingCollection::class)] +#[CoversClass(IniSettingCollectionIterator::class)] +#[UsesClass(IniSetting::class)] +#[Small] +final class IniSettingCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = IniSettingCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = IniSettingCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = IniSettingCollection::fromArray([$element]); + + foreach ($elements as $index => $_IniSetting) { + $this->assertSame(0, $index); + $this->assertSame($element, $_IniSetting); + } + } + + private function element(): IniSetting + { + return new IniSetting('name', 'value'); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/IniSettingTest.php b/tests/unit/TextUI/Configuration/Value/IniSettingTest.php new file mode 100644 index 00000000000..96099e1a143 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/IniSettingTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(IniSetting::class)] +#[Small] +final class IniSettingTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('name', (new IniSetting('name', 'value'))->name()); + } + + public function testHasValue(): void + { + $this->assertSame('value', (new IniSetting('name', 'value'))->value()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/PhpTest.php b/tests/unit/TextUI/Configuration/Value/PhpTest.php new file mode 100644 index 00000000000..db139ffedfd --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/PhpTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Php::class)] +#[Small] +final class PhpTest extends TestCase +{ + private readonly DirectoryCollection $includePaths; + private readonly IniSettingCollection $iniSettings; + private readonly ConstantCollection $constants; + private readonly VariableCollection $globalVariables; + private readonly VariableCollection $envVariables; + private readonly VariableCollection $postVariables; + private readonly VariableCollection $getVariables; + private readonly VariableCollection $cookieVariables; + private readonly VariableCollection $serverVariables; + private readonly VariableCollection $filesVariables; + private readonly VariableCollection $requestVariables; + private readonly Php $fixture; + + protected function setUp(): void + { + $this->includePaths = DirectoryCollection::fromArray([]); + $this->iniSettings = IniSettingCollection::fromArray([]); + $this->constants = ConstantCollection::fromArray([]); + $this->globalVariables = VariableCollection::fromArray([]); + $this->envVariables = VariableCollection::fromArray([]); + $this->postVariables = VariableCollection::fromArray([]); + $this->getVariables = VariableCollection::fromArray([]); + $this->cookieVariables = VariableCollection::fromArray([]); + $this->serverVariables = VariableCollection::fromArray([]); + $this->filesVariables = VariableCollection::fromArray([]); + $this->requestVariables = VariableCollection::fromArray([]); + + $this->fixture = new Php( + $this->includePaths, + $this->iniSettings, + $this->constants, + $this->globalVariables, + $this->envVariables, + $this->postVariables, + $this->getVariables, + $this->cookieVariables, + $this->serverVariables, + $this->filesVariables, + $this->requestVariables, + ); + } + + public function testHasIncludePaths(): void + { + $this->assertSame($this->includePaths, $this->fixture->includePaths()); + } + + public function testHasIniSettings(): void + { + $this->assertSame($this->iniSettings, $this->fixture->iniSettings()); + } + + public function testHasConstants(): void + { + $this->assertSame($this->constants, $this->fixture->constants()); + } + + public function testHasGlobalVariables(): void + { + $this->assertSame($this->globalVariables, $this->fixture->globalVariables()); + } + + public function testHasEnvVariables(): void + { + $this->assertSame($this->envVariables, $this->fixture->envVariables()); + } + + public function testHasPostVariables(): void + { + $this->assertSame($this->postVariables, $this->fixture->postVariables()); + } + + public function testHasGetVariables(): void + { + $this->assertSame($this->getVariables, $this->fixture->getVariables()); + } + + public function testHasCookieVariables(): void + { + $this->assertSame($this->cookieVariables, $this->fixture->cookieVariables()); + } + + public function testHasServerVariables(): void + { + $this->assertSame($this->serverVariables, $this->fixture->serverVariables()); + } + + public function testHasFilesVariables(): void + { + $this->assertSame($this->filesVariables, $this->fixture->filesVariables()); + } + + public function testHasRequestVariables(): void + { + $this->assertSame($this->requestVariables, $this->fixture->requestVariables()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/SourceTest.php b/tests/unit/TextUI/Configuration/Value/SourceTest.php index 1ec21c5eb71..cda89543505 100644 --- a/tests/unit/TextUI/Configuration/Value/SourceTest.php +++ b/tests/unit/TextUI/Configuration/Value/SourceTest.php @@ -44,6 +44,12 @@ public function testHasIncludeDirectories(): void false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ); @@ -70,6 +76,12 @@ public function testHasIncludeFiles(): void false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ); @@ -96,6 +108,12 @@ public function testHasExcludeDirectories(): void false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ); @@ -122,6 +140,12 @@ public function testHasExcludeFiles(): void false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ); @@ -148,11 +172,18 @@ public function testMayHaveBaseline(): void false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ); $this->assertSame($baseline, $source->baseline()); $this->assertTrue($source->hasBaseline()); + $this->assertTrue($source->useBaseline()); } public function testMayNotHaveBaseline(): void @@ -173,13 +204,628 @@ public function testMayNotHaveBaseline(): void false, false, false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, false, ); $this->assertFalse($source->hasBaseline()); + $this->assertFalse($source->useBaseline()); $this->expectException(NoBaselineException::class); $source->baseline(); } + + public function testRestrictionOfNoticesMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->restrictNotices()); + } + + public function testRestrictionOfNoticesMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + true, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->restrictNotices()); + } + + public function testRestrictionOfWarningsMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->restrictWarnings()); + } + + public function testRestrictionOfWarningsMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + true, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->restrictWarnings()); + } + + public function testIgnoringOfSuppressedDeprecationsMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfDeprecations()); + } + + public function testIgnoringOfSuppressedDeprecationsMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + true, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfDeprecations()); + } + + public function testIgnoringOfSuppressedPhpDeprecationsMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfPhpDeprecations()); + } + + public function testIgnoringOfSuppressedPhpDeprecationsMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + true, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfPhpDeprecations()); + } + + public function testIgnoringOfSuppressedErrorsMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfErrors()); + } + + public function testIgnoringOfSuppressedErrorsMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + true, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfErrors()); + } + + public function testIgnoringOfSuppressedNoticesMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfNotices()); + } + + public function testIgnoringOfSuppressedNoticesMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + true, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfNotices()); + } + + public function testIgnoringOfSuppressedPhpNoticesMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfPhpNotices()); + } + + public function testIgnoringOfSuppressedPhpNoticesMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + true, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfPhpNotices()); + } + + public function testIgnoringOfSuppressedWarningsMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfWarnings()); + } + + public function testIgnoringOfSuppressedWarningsMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + true, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfWarnings()); + } + + public function testIgnoringOfSuppressedPhpWarningsMayBeDisabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->ignoreSuppressionOfPhpWarnings()); + } + + public function testIgnoringOfSuppressedPhpWarningsMayBeEnabled(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + true, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->ignoreSuppressionOfPhpWarnings()); + } + + public function testMayBeEmpty(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertFalse($source->notEmpty()); + } + + public function testMayNotBeEmpty(): void + { + $source = new Source( + null, + false, + FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + 'path', + 'prefix', + 'suffix', + ), + ], + ), + FileCollection::fromArray([]), + FilterDirectoryCollection::fromArray([]), + FileCollection::fromArray([]), + false, + false, + false, + false, + false, + false, + false, + false, + false, + [ + 'functions' => [], + 'methods' => [], + ], + false, + false, + false, + ); + + $this->assertTrue($source->notEmpty()); + } } diff --git a/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php new file mode 100644 index 00000000000..ea1423c572b --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/TestDirectoryCollectionTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\VersionComparisonOperator; + +#[CoversClass(TestDirectoryCollection::class)] +#[CoversClass(TestDirectoryCollectionIterator::class)] +#[UsesClass(TestDirectory::class)] +#[Small] +final class TestDirectoryCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = TestDirectoryCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = TestDirectoryCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + $this->assertFalse($elements->isEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = TestDirectoryCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): TestDirectory + { + return new TestDirectory( + 'path', + 'prefix', + 'suffix', + '8.2.0', + new VersionComparisonOperator('>='), + [], + ); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php b/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php new file mode 100644 index 00000000000..040c5e316fd --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/TestDirectoryTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\VersionComparisonOperator; + +#[CoversClass(TestDirectory::class)] +#[Small] +final class TestDirectoryTest extends TestCase +{ + public function testHasPath(): void + { + $this->assertSame('path', $this->fixture()->path()); + } + + public function testHasPrefix(): void + { + $this->assertSame('prefix', $this->fixture()->prefix()); + } + + public function testHasSuffix(): void + { + $this->assertSame('suffix', $this->fixture()->suffix()); + } + + public function testHasPhpVersion(): void + { + $this->assertSame('8.2.0', $this->fixture()->phpVersion()); + } + + public function testHasPhpVersionOperator(): void + { + $this->assertSame('>=', $this->fixture()->phpVersionOperator()->asString()); + } + + public function testHasGroups(): void + { + $this->assertSame(['group'], $this->fixture()->groups()); + } + + private function fixture(): TestDirectory + { + return new TestDirectory( + 'path', + 'prefix', + 'suffix', + '8.2.0', + new VersionComparisonOperator('>='), + ['group'], + ); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php new file mode 100644 index 00000000000..7691b4cb2d2 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/TestFileCollectionTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\VersionComparisonOperator; + +#[CoversClass(TestFileCollection::class)] +#[CoversClass(TestFileCollectionIterator::class)] +#[UsesClass(TestFile::class)] +#[Small] +final class TestFileCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = TestFileCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = TestFileCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + $this->assertFalse($elements->isEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = TestFileCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): TestFile + { + return new TestFile( + 'path', + '8.2.0', + new VersionComparisonOperator('>='), + [], + ); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/TestFileTest.php b/tests/unit/TextUI/Configuration/Value/TestFileTest.php new file mode 100644 index 00000000000..63b2914f005 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/TestFileTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\VersionComparisonOperator; + +#[CoversClass(TestFile::class)] +#[Small] +final class TestFileTest extends TestCase +{ + public function testHasPath(): void + { + $this->assertSame('path', $this->fixture()->path()); + } + + public function testHasPhpVersion(): void + { + $this->assertSame('8.2.0', $this->fixture()->phpVersion()); + } + + public function testHasPhpVersionOperator(): void + { + $this->assertSame('>=', $this->fixture()->phpVersionOperator()->asString()); + } + + public function testHasGroups(): void + { + $this->assertSame(['group'], $this->fixture()->groups()); + } + + private function fixture(): TestFile + { + return new TestFile( + 'path', + '8.2.0', + new VersionComparisonOperator('>='), + ['group'], + ); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/TestSuiteCollectionTest.php b/tests/unit/TextUI/Configuration/Value/TestSuiteCollectionTest.php new file mode 100644 index 00000000000..e3dd3644c3c --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/TestSuiteCollectionTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestSuiteCollection::class)] +#[CoversClass(TestSuiteCollectionIterator::class)] +#[UsesClass(TestSuite::class)] +#[Small] +final class TestSuiteCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = TestSuiteCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = TestSuiteCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + $this->assertFalse($elements->isEmpty()); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = TestSuiteCollection::fromArray([$element]); + + foreach ($elements as $index => $_constant) { + $this->assertSame(0, $index); + $this->assertSame($element, $_constant); + } + } + + private function element(): TestSuite + { + return new TestSuite( + 'name', + TestDirectoryCollection::fromArray([]), + TestFileCollection::fromArray([]), + FileCollection::fromArray([]), + ); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/TestSuiteTest.php b/tests/unit/TextUI/Configuration/Value/TestSuiteTest.php new file mode 100644 index 00000000000..6c417fa8297 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/TestSuiteTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TestSuite::class)] +#[Small] +final class TestSuiteTest extends TestCase +{ + private readonly string $name; + private readonly TestDirectoryCollection $directories; + private readonly TestFileCollection $files; + private readonly FileCollection $excludedFiles; + private readonly TestSuite $fixture; + + protected function setUp(): void + { + $this->name = 'name'; + $this->directories = TestDirectoryCollection::fromArray([]); + $this->files = TestFileCollection::fromArray([]); + $this->excludedFiles = FileCollection::fromArray([]); + + $this->fixture = new TestSuite( + $this->name, + $this->directories, + $this->files, + $this->excludedFiles, + ); + } + + public function testHasName(): void + { + $this->assertSame($this->name, $this->fixture->name()); + } + + public function testDirectories(): void + { + $this->assertSame($this->directories, $this->fixture->directories()); + } + + public function testHasFiles(): void + { + $this->assertSame($this->files, $this->fixture->files()); + } + + public function testHasExcludedFiles(): void + { + $this->assertSame($this->excludedFiles, $this->fixture->exclude()); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/VariableCollectionTest.php b/tests/unit/TextUI/Configuration/Value/VariableCollectionTest.php new file mode 100644 index 00000000000..ad2f686e173 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/VariableCollectionTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(VariableCollection::class)] +#[CoversClass(VariableCollectionIterator::class)] +#[UsesClass(Variable::class)] +#[Small] +final class VariableCollectionTest extends TestCase +{ + public function testIsCreatedFromArray(): void + { + $element = $this->element(); + $elements = VariableCollection::fromArray([$element]); + + $this->assertSame([$element], $elements->asArray()); + } + + public function testIsCountable(): void + { + $element = $this->element(); + $elements = VariableCollection::fromArray([$element]); + + $this->assertCount(1, $elements); + } + + public function testIsIterable(): void + { + $element = $this->element(); + $elements = VariableCollection::fromArray([$element]); + + foreach ($elements as $index => $_Variable) { + $this->assertSame(0, $index); + $this->assertSame($element, $_Variable); + } + } + + private function element(): Variable + { + return new Variable('name', 'value', false); + } +} diff --git a/tests/unit/TextUI/Configuration/Value/VariableTest.php b/tests/unit/TextUI/Configuration/Value/VariableTest.php new file mode 100644 index 00000000000..1b0ea6f0e43 --- /dev/null +++ b/tests/unit/TextUI/Configuration/Value/VariableTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Configuration; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Variable::class)] +#[Small] +final class VariableTest extends TestCase +{ + public function testHasName(): void + { + $this->assertSame('name', (new Variable('name', 'value', false))->name()); + } + + public function testHasValue(): void + { + $this->assertSame('value', (new Variable('name', 'value', false))->value()); + } + + public function testValueCanBeForced(): void + { + $this->assertFalse((new Variable('name', 'value', false))->force()); + $this->assertTrue((new Variable('name', 'value', true))->force()); + } +} diff --git a/tests/unit/TextUI/Configuration/Xml/GeneratorTest.php b/tests/unit/TextUI/Configuration/Xml/GeneratorTest.php index be3f4447485..375596e5797 100644 --- a/tests/unit/TextUI/Configuration/Xml/GeneratorTest.php +++ b/tests/unit/TextUI/Configuration/Xml/GeneratorTest.php @@ -24,13 +24,15 @@ public function testGeneratesConfigurationCorrectly(): void $this->assertEquals( ' @@ -39,7 +41,7 @@ public function testGeneratesConfigurationCorrectly(): void - + src @@ -47,7 +49,7 @@ public function testGeneratesConfigurationCorrectly(): void ', $generator->generateDefaultConfiguration( - 'X.Y.Z', + 'https://schema.phpunit.de/X.Y/phpunit.xsd', 'vendor/autoload.php', 'tests', 'src', diff --git a/tests/unit/TextUI/Configuration/Xml/LoaderTest.php b/tests/unit/TextUI/Configuration/Xml/LoaderTest.php index 7ca282b1b6d..7c6c1e1891a 100644 --- a/tests/unit/TextUI/Configuration/Xml/LoaderTest.php +++ b/tests/unit/TextUI/Configuration/Xml/LoaderTest.php @@ -18,40 +18,40 @@ use function sys_get_temp_dir; use function uniqid; use function unlink; -use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversNamespace; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Medium; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TextUI\Configuration\Configuration; use SebastianBergmann\CodeCoverage\Report\Html\Colors; use SebastianBergmann\CodeCoverage\Report\Thresholds; -#[CoversClass(Loader::class)] +#[CoversNamespace('PHPUnit\TextUI\XmlConfiguration')] #[Medium] final class LoaderTest extends TestCase { public static function configurationRootOptionsProvider(): array { return [ - 'executionOrder default' => ['executionOrder', 'default', TestSuiteSorter::ORDER_DEFAULT], - 'executionOrder random' => ['executionOrder', 'random', TestSuiteSorter::ORDER_RANDOMIZED], - 'executionOrder reverse' => ['executionOrder', 'reverse', TestSuiteSorter::ORDER_REVERSED], - 'executionOrder size' => ['executionOrder', 'size', TestSuiteSorter::ORDER_SIZE], - 'cacheDirectory absolute path' => ['cacheDirectory', '/path/to/cache', '/path/to/cache'], - 'cacheResult=false' => ['cacheResult', 'false', false], - 'cacheResult=true' => ['cacheResult', 'true', true], - 'columns' => ['columns', 'max', 'max'], - 'stopOnFailure' => ['stopOnFailure', 'true', true], - 'stopOnWarning' => ['stopOnWarning', 'true', true], - 'stopOnIncomplete' => ['stopOnIncomplete', 'true', true], - 'stopOnRisky' => ['stopOnRisky', 'true', true], - 'stopOnSkipped' => ['stopOnSkipped', 'true', true], - 'failOnEmptyTestSuite' => ['failOnEmptyTestSuite', 'true', true], - 'failOnWarning' => ['failOnWarning', 'true', true], - 'failOnRisky' => ['failOnRisky', 'true', true], - 'processIsolation' => ['processIsolation', 'true', true], - 'reverseDefectList' => ['reverseDefectList', 'true', true], - 'registerMockObjectsFromTestArgumentsRecursively' => ['registerMockObjectsFromTestArgumentsRecursively', 'true', true], + 'executionOrder default' => ['executionOrder', 'default', TestSuiteSorter::ORDER_DEFAULT], + 'executionOrder random' => ['executionOrder', 'random', TestSuiteSorter::ORDER_RANDOMIZED], + 'executionOrder reverse' => ['executionOrder', 'reverse', TestSuiteSorter::ORDER_REVERSED], + 'executionOrder size' => ['executionOrder', 'size', TestSuiteSorter::ORDER_SIZE], + 'cacheDirectory absolute path' => ['cacheDirectory', '/path/to/cache', '/path/to/cache'], + 'cacheResult=false' => ['cacheResult', 'false', false], + 'cacheResult=true' => ['cacheResult', 'true', true], + 'columns' => ['columns', 'max', 'max'], + 'stopOnFailure' => ['stopOnFailure', 'true', true], + 'stopOnWarning' => ['stopOnWarning', 'true', true], + 'stopOnIncomplete' => ['stopOnIncomplete', 'true', true], + 'stopOnRisky' => ['stopOnRisky', 'true', true], + 'stopOnSkipped' => ['stopOnSkipped', 'true', true], + 'failOnEmptyTestSuite' => ['failOnEmptyTestSuite', 'true', true], + 'failOnWarning' => ['failOnWarning', 'true', true], + 'failOnRisky' => ['failOnRisky', 'true', true], + 'processIsolation' => ['processIsolation', 'true', true], + 'reverseDefectList' => ['reverseDefectList', 'true', true], ]; } @@ -67,28 +67,28 @@ public function testShouldReadColorsWhenTrueInConfigurationFile(): void { $phpunit = $this->configuration('configuration.colors.true.xml')->phpunit(); - $this->assertEquals(\PHPUnit\TextUI\Configuration\Configuration::COLOR_AUTO, $phpunit->colors()); + $this->assertEquals(Configuration::COLOR_AUTO, $phpunit->colors()); } public function testShouldReadColorsWhenFalseInConfigurationFile(): void { $phpunit = $this->configuration('configuration.colors.false.xml')->phpunit(); - $this->assertEquals(\PHPUnit\TextUI\Configuration\Configuration::COLOR_NEVER, $phpunit->colors()); + $this->assertEquals(Configuration::COLOR_NEVER, $phpunit->colors()); } public function testShouldReadColorsWhenEmptyInConfigurationFile(): void { $phpunit = $this->configuration('configuration.colors.empty.xml')->phpunit(); - $this->assertEquals(\PHPUnit\TextUI\Configuration\Configuration::COLOR_NEVER, $phpunit->colors()); + $this->assertEquals(Configuration::COLOR_NEVER, $phpunit->colors()); } public function testShouldReadColorsWhenInvalidInConfigurationFile(): void { $phpunit = $this->configuration('configuration.colors.invalid.xml')->phpunit(); - $this->assertEquals(\PHPUnit\TextUI\Configuration\Configuration::COLOR_NEVER, $phpunit->colors()); + $this->assertEquals(Configuration::COLOR_NEVER, $phpunit->colors()); } public function testInvalidConfigurationGeneratesValidationErrors(): void @@ -163,6 +163,22 @@ public function testSourceConfigurationIsReadCorrectly(): void $file = iterator_to_array($source->excludeFiles(), false)[0]; $this->assertSame('/path/to/file', $file->path()); + + $this->assertSame( + [ + 'functions' => [ + 'PHPUnit\TestFixture\DeprecationTrigger\trigger_deprecation', + ], + 'methods' => [ + 'PHPUnit\TestFixture\DeprecationTrigger\DeprecationTrigger::triggerDeprecation', + ], + ], + $source->deprecationTriggers(), + ); + + $this->assertTrue($source->ignoreSelfDeprecations()); + $this->assertTrue($source->ignoreDirectDeprecations()); + $this->assertTrue($source->ignoreIndirectDeprecations()); } public function testCodeCoverageConfigurationIsReadCorrectly(): void @@ -341,22 +357,24 @@ public function testPHPUnitConfigurationIsReadCorrectly(): void $this->assertTrue($phpunit->resolveDependencies()); $this->assertTrue($phpunit->controlGarbageCollector()); $this->assertSame(1000, $phpunit->numberOfTestsBeforeGarbageCollection()); + $this->assertSame(10, $phpunit->shortenArraysForExportThreshold()); } public function test_TestDox_configuration_is_parsed_correctly(): void { - $this->assertTrue( - $this->configuration('configuration_testdox.xml')->phpunit()->testdoxPrinter(), - ); + $configuration = $this->configuration('configuration_testdox.xml')->phpunit(); + + $this->assertTrue($configuration->testdoxPrinter()); + $this->assertTrue($configuration->testdoxPrinterSummary()); } public function testConfigurationForSingleTestSuiteCanBeLoaded(): void { - $testsuites = $this->configuration('configuration_testsuite.xml')->testSuite(); + $testSuites = $this->configuration('configuration_testsuite.xml')->testSuite(); - $this->assertCount(1, $testsuites); + $this->assertCount(1, $testSuites); - $first = $testsuites->asArray()[0]; + $first = $testSuites->asArray()[0]; $this->assertSame('first', $first->name()); $this->assertCount(1, $first->directories()); $this->assertSame(TEST_FILES_PATH . 'tests/first', $first->directories()->asArray()[0]->path()); @@ -370,11 +388,11 @@ public function testConfigurationForSingleTestSuiteCanBeLoaded(): void public function testConfigurationForMultipleTestSuitesCanBeLoaded(): void { - $testsuites = $this->configuration('configuration_testsuites.xml')->testSuite(); + $testSuites = $this->configuration('configuration_testsuites.xml')->testSuite(); - $this->assertCount(2, $testsuites); + $this->assertCount(2, $testSuites); - $first = $testsuites->asArray()[0]; + $first = $testSuites->asArray()[0]; $this->assertSame('first', $first->name()); $this->assertCount(1, $first->directories()); $this->assertSame(TEST_FILES_PATH . 'tests/first', $first->directories()->asArray()[0]->path()); @@ -384,8 +402,9 @@ public function testConfigurationForMultipleTestSuitesCanBeLoaded(): void $this->assertSame('>=', $first->directories()->asArray()[0]->phpVersionOperator()->asString()); $this->assertCount(0, $first->files()); $this->assertCount(0, $first->exclude()); + $this->assertSame(['foo'], $first->directories()->asArray()[0]->groups()); - $second = $testsuites->asArray()[1]; + $second = $testSuites->asArray()[1]; $this->assertSame('second', $second->name()); $this->assertSame(TEST_FILES_PATH . 'tests/second', $second->directories()->asArray()[0]->path()); $this->assertSame('test', $second->directories()->asArray()[0]->prefix()); @@ -398,6 +417,8 @@ public function testConfigurationForMultipleTestSuitesCanBeLoaded(): void $this->assertSame('!=', $second->files()->asArray()[0]->phpVersionOperator()->asString()); $this->assertCount(1, $second->exclude()); $this->assertSame(TEST_FILES_PATH . 'tests/second/_files', $second->exclude()->asArray()[0]->path()); + $this->assertSame(['bar'], $second->directories()->asArray()[0]->groups()); + $this->assertSame(['baz'], $second->files()->asArray()[0]->groups()); } private function configuration(string $filename): LoadedFromFileConfiguration diff --git a/tests/unit/TextUI/Configuration/Xml/MigratorTest.php b/tests/unit/TextUI/Configuration/Xml/MigratorTest.php index 8fbdb941482..221d2978a13 100644 --- a/tests/unit/TextUI/Configuration/Xml/MigratorTest.php +++ b/tests/unit/TextUI/Configuration/Xml/MigratorTest.php @@ -9,37 +9,44 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use PHPUnit\Util\Xml\Loader as XmlLoader; -#[CoversClass(Migrator::class)] final class MigratorTest extends TestCase { - #[TestDox('Can migrate PHPUnit 9.2 configuration')] - public function testCanMigratePhpUnit92Configuration(): void + public static function provider(): array { - $this->assertEquals( - (new XmlLoader)->loadFile(__DIR__ . '/../../../../_files/XmlConfigurationMigration/output-9.2.xml'), - (new XmlLoader)->load( - (new Migrator)->migrate( - __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-9.2.xml', - ), - ), - ); + return [ + 'PHPUnit 9.2' => [ + __DIR__ . '/../../../../_files/XmlConfigurationMigration/output-9.2.xml', + __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-9.2.xml', + ], + 'PHPUnit 9.5' => [ + __DIR__ . '/../../../../_files/XmlConfigurationMigration/output-9.5.xml', + __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-9.5.xml', + ], + 'Relative Path' => [ + __DIR__ . '/../../../../_files/XmlConfigurationMigration/output-relative-schema-path.xml', + __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-relative-schema-path.xml', + ], + 'Issue 5859' => [ + __DIR__ . '/../../../../_files/XmlConfigurationMigration/output-issue-5859.xml', + __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-issue-5859.xml', + ], + 'Issue 6087' => [ + __DIR__ . '/../../../../_files/XmlConfigurationMigration/output-issue-6087.xml', + __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-issue-6087.xml', + ], + ]; } - #[TestDox('Can migrate PHPUnit 9.5 configuration')] - public function testCanMigratePhpUnit95Configuration(): void + #[DataProvider('provider')] + public function testCanMigrateConfigurationFileThatValidatesAgainstPreviousSchema(string $output, string $input): void { $this->assertEquals( - (new XmlLoader)->loadFile(__DIR__ . '/../../../../_files/XmlConfigurationMigration/output-9.5.xml'), - (new XmlLoader)->load( - (new Migrator)->migrate( - __DIR__ . '/../../../../_files/XmlConfigurationMigration/input-9.5.xml', - ), - ), + (new XmlLoader)->loadFile($output), + (new XmlLoader)->load((new Migrator)->migrate($input)), ); } } diff --git a/tests/unit/TextUI/Configuration/Xml/SchemaFinderTest.php b/tests/unit/TextUI/Configuration/Xml/SchemaFinderTest.php index 576aad41e2e..6b4f703a19f 100644 --- a/tests/unit/TextUI/Configuration/Xml/SchemaFinderTest.php +++ b/tests/unit/TextUI/Configuration/Xml/SchemaFinderTest.php @@ -9,6 +9,7 @@ */ namespace PHPUnit\TextUI\XmlConfiguration; +use function count; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; @@ -18,12 +19,20 @@ #[Small] final class SchemaFinderTest extends TestCase { - public function testFindsExistingSchemaForComposerInstallation(): void + public function testListsAvailableSchemas(): void + { + $schemas = (new SchemaFinder)->available(); + + $this->assertSame((new Version)->series(), $schemas[0]); + $this->assertSame('8.5', $schemas[count($schemas) - 1]); + } + + public function testFindsExistingSchema(): void { $this->assertFileExists((new SchemaFinder)->find((new Version)->series())); } - public function testDoesNotFindNonExistentSchemaForComposerInstallation(): void + public function testDoesNotFindNonExistentSchema(): void { $this->expectException(CannotFindSchemaException::class); diff --git a/tests/unit/TextUI/Output/Default/DefaultPrinterTest.php b/tests/unit/TextUI/Output/Default/DefaultPrinterTest.php new file mode 100644 index 00000000000..d843dc379c9 --- /dev/null +++ b/tests/unit/TextUI/Output/Default/DefaultPrinterTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI\Output\Default; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Medium; +use PHPUnit\Framework\TestCase; +use PHPUnit\TextUI\CannotOpenSocketException; +use PHPUnit\TextUI\InvalidSocketException; +use PHPUnit\TextUI\Output\DefaultPrinter; + +#[CoversClass(DefaultPrinter::class)] +#[Medium] +final class DefaultPrinterTest extends TestCase +{ + public static function providePrinter(): array + { + $data = [ + 'standard output' => [DefaultPrinter::standardOutput()], + 'standard error' => [DefaultPrinter::standardError()], + ]; + + try { + $data['socket'] = [DefaultPrinter::from('socket://www.example.com:80')]; + } catch (CannotOpenSocketException $e) { + } + + return $data; + } + + #[DataProvider('providePrinter')] + public function testFlush(DefaultPrinter $printer): void + { + $printer->flush(); + $this->expectOutputString(''); + } + + public function testInvalidSocket(): void + { + $this->expectException(InvalidSocketException::class); + DefaultPrinter::from('socket://hostname:port:wrong'); + } +} diff --git a/tests/unit/TextUI/Output/Default/ResultPrinterTest.php b/tests/unit/TextUI/Output/Default/ResultPrinterTest.php index 34454e74372..135d1c707c7 100644 --- a/tests/unit/TextUI/Output/Default/ResultPrinterTest.php +++ b/tests/unit/TextUI/Output/Default/ResultPrinterTest.php @@ -29,10 +29,12 @@ use PHPUnit\Event\Test\MarkedIncomplete; use PHPUnit\Event\Test\PhpunitDeprecationTriggered; use PHPUnit\Event\Test\PhpunitErrorTriggered; +use PHPUnit\Event\Test\PhpunitNoticeTriggered; use PHPUnit\Event\Test\PhpunitWarningTriggered; use PHPUnit\Event\Test\Skipped as TestSkipped; use PHPUnit\Event\TestData\TestDataCollection; use PHPUnit\Event\TestRunner\DeprecationTriggered as TestRunnerDeprecationTriggered; +use PHPUnit\Event\TestRunner\NoticeTriggered as TestRunnerNoticeTriggered; use PHPUnit\Event\TestRunner\WarningTriggered as TestRunnerWarningTriggered; use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped; use PHPUnit\Framework\Attributes\CoversClass; @@ -53,7 +55,7 @@ final class ResultPrinterTest extends TestCase { /** - * @psalm-return array + * @return array */ public static function provider(): array { @@ -179,7 +181,7 @@ public static function provider(): array ), ], - 'successful test that triggers deprecation' => [ + 'successful test that triggers deprecation (do not display stack trace)' => [ __DIR__ . '/expectations/successful_test_with_deprecation.txt', self::createTestResult( deprecations: [ @@ -193,6 +195,22 @@ public static function provider(): array ), ], + 'successful test that triggers deprecation (display stack trace)' => [ + __DIR__ . '/expectations/successful_test_with_deprecation_with_stack_trace.txt', + self::createTestResult( + deprecations: [ + Issue::from( + 'Foo.php', + 1, + 'message', + self::testMethod(), + '/path/to/file.php:1234', + ), + ], + ), + true, + ], + 'successful test that triggers PHP deprecation' => [ __DIR__ . '/expectations/successful_test_with_php_deprecation.txt', self::createTestResult( @@ -329,6 +347,33 @@ public static function provider(): array ), ], + 'successful test and PHPUnit test runner notice' => [ + __DIR__ . '/expectations/successful_test_and_phpunit_test_runner_notice.txt', + self::createTestResult( + testRunnerTriggeredNoticeEvents: [ + new TestRunnerNoticeTriggered( + self::telemetryInfo(), + 'message', + ), + ], + ), + ], + + 'successful test that triggers PHPUnit notice' => [ + __DIR__ . '/expectations/successful_test_with_phpunit_notice.txt', + self::createTestResult( + testTriggeredPhpunitNoticeEvents: [ + 'Foo::testBar' => [ + new PhpunitNoticeTriggered( + self::telemetryInfo(), + self::testMethod(), + 'message', + ), + ], + ], + ), + ], + 'successful test that triggers PHPUnit warning' => [ __DIR__ . '/expectations/successful_test_with_phpunit_warning.txt', self::createTestResult( @@ -361,7 +406,7 @@ public static function provider(): array } #[DataProvider('provider')] - public function testPrintsExpectedOutputForTestResultObject(string $expectationFile, TestResult $result): void + public function testPrintsExpectedOutputForTestResultObject(string $expectationFile, TestResult $result, bool $stackTraceForDeprecations = false): void { $printer = $this->printer(); @@ -379,11 +424,11 @@ public function testPrintsExpectedOutputForTestResultObject(string $expectationF true, true, true, + true, false, ); - $resultPrinter->print($result); - $resultPrinter->flush(); + $resultPrinter->print($result, $stackTraceForDeprecations); $summaryPrinter = new SummaryPrinter( $printer, @@ -422,26 +467,28 @@ public function buffer(): string } /** - * @psalm-param list $testErroredEvents - * @psalm-param list $testFailedEvents - * @psalm-param array> $testConsideredRiskyEvents - * @psalm-param list $testSuiteSkippedEvents - * @psalm-param list $testSkippedEvents - * @psalm-param list $testMarkedIncompleteEvents - * @psalm-param list $deprecations - * @psalm-param list $phpDeprecations - * @psalm-param array> $testTriggeredPhpunitDeprecationEvents - * @psalm-param list $errors - * @psalm-param list $notices - * @psalm-param list $phpNotices - * @psalm-param list $warnings - * @psalm-param list $phpWarnings - * @psalm-param array> $testTriggeredPhpunitErrorEvents - * @psalm-param array> $testTriggeredPhpunitWarningEvents - * @psalm-param list $testRunnerTriggeredDeprecationEvents - * @psalm-param list $testRunnerTriggeredWarningEvents + * @param list $testErroredEvents + * @param list $testFailedEvents + * @param array> $testConsideredRiskyEvents + * @param list $testSuiteSkippedEvents + * @param list $testSkippedEvents + * @param list $testMarkedIncompleteEvents + * @param list $deprecations + * @param list $phpDeprecations + * @param array> $testTriggeredPhpunitDeprecationEvents + * @param list $errors + * @param list $notices + * @param list $phpNotices + * @param list $warnings + * @param list $phpWarnings + * @param array> $testTriggeredPhpunitErrorEvents + * @param array> $testTriggeredPhpunitNoticeEvents + * @param array> $testTriggeredPhpunitWarningEvents + * @param list $testRunnerTriggeredDeprecationEvents + * @param list $testRunnerTriggeredNoticeEvents + * @param list $testRunnerTriggeredWarningEvents */ - private static function createTestResult(int $numberOfTests = 1, int $numberOfTestsRun = 1, int $numberOfAssertions = 1, array $testErroredEvents = [], array $testFailedEvents = [], array $testConsideredRiskyEvents = [], array $testSuiteSkippedEvents = [], array $testSkippedEvents = [], array $testMarkedIncompleteEvents = [], array $deprecations = [], array $phpDeprecations = [], array $testTriggeredPhpunitDeprecationEvents = [], array $errors = [], array $notices = [], array $phpNotices = [], array $warnings = [], array $phpWarnings = [], array $testTriggeredPhpunitErrorEvents = [], array $testTriggeredPhpunitWarningEvents = [], array $testRunnerTriggeredDeprecationEvents = [], array $testRunnerTriggeredWarningEvents = [], int $numberOfIssuesIgnoredByBaseline = 0): TestResult + private static function createTestResult(int $numberOfTests = 1, int $numberOfTestsRun = 1, int $numberOfAssertions = 1, array $testErroredEvents = [], array $testFailedEvents = [], array $testConsideredRiskyEvents = [], array $testSuiteSkippedEvents = [], array $testSkippedEvents = [], array $testMarkedIncompleteEvents = [], array $deprecations = [], array $phpDeprecations = [], array $testTriggeredPhpunitDeprecationEvents = [], array $errors = [], array $notices = [], array $phpNotices = [], array $warnings = [], array $phpWarnings = [], array $testTriggeredPhpunitErrorEvents = [], array $testTriggeredPhpunitNoticeEvents = [], array $testTriggeredPhpunitWarningEvents = [], array $testRunnerTriggeredDeprecationEvents = [], array $testRunnerTriggeredNoticeEvents = [], array $testRunnerTriggeredWarningEvents = [], int $numberOfIssuesIgnoredByBaseline = 0): TestResult { return new TestResult( $numberOfTests, @@ -455,8 +502,10 @@ private static function createTestResult(int $numberOfTests = 1, int $numberOfTe $testMarkedIncompleteEvents, $testTriggeredPhpunitDeprecationEvents, $testTriggeredPhpunitErrorEvents, + $testTriggeredPhpunitNoticeEvents, $testTriggeredPhpunitWarningEvents, $testRunnerTriggeredDeprecationEvents, + $testRunnerTriggeredNoticeEvents, $testRunnerTriggeredWarningEvents, $errors, $deprecations, diff --git a/tests/unit/TextUI/Output/Default/expectations/successful_test_and_phpunit_test_runner_notice.txt b/tests/unit/TextUI/Output/Default/expectations/successful_test_and_phpunit_test_runner_notice.txt new file mode 100644 index 00000000000..8c482460ad3 --- /dev/null +++ b/tests/unit/TextUI/Output/Default/expectations/successful_test_and_phpunit_test_runner_notice.txt @@ -0,0 +1,6 @@ +There was 1 PHPUnit test runner notice: + +1) message + +OK, but there were issues! +Tests: 1, Assertions: 1, PHPUnit Notices: 1. diff --git a/tests/unit/TextUI/Output/Default/expectations/successful_test_with_deprecation_with_stack_trace.txt b/tests/unit/TextUI/Output/Default/expectations/successful_test_with_deprecation_with_stack_trace.txt new file mode 100644 index 00000000000..14eede871c4 --- /dev/null +++ b/tests/unit/TextUI/Output/Default/expectations/successful_test_with_deprecation_with_stack_trace.txt @@ -0,0 +1,14 @@ +1 test triggered 1 deprecation: + +1) Foo.php:1 +message + +/path/to/file.php:1234 + +Triggered by: + +* FooTest::testBar + FooTest.php:1 + +OK, but there were issues! +Tests: 1, Assertions: 1, Deprecations: 1. diff --git a/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_deprecation.txt b/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_deprecation.txt index 630b31dd304..f5f8711f8b1 100644 --- a/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_deprecation.txt +++ b/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_deprecation.txt @@ -12,4 +12,4 @@ message %s:%i OK, but there were issues! -Tests: 1, Assertions: 1, Deprecations: 2. +Tests: 1, Assertions: 1, PHPUnit Deprecations: 2. diff --git a/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_notice.txt b/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_notice.txt new file mode 100644 index 00000000000..e5e7f777b4d --- /dev/null +++ b/tests/unit/TextUI/Output/Default/expectations/successful_test_with_phpunit_notice.txt @@ -0,0 +1,9 @@ +1 test triggered 1 PHPUnit notice: + +1) FooTest::testBar +message + +FooTest.php:1 + +OK, but there were issues! +Tests: 1, Assertions: 1, PHPUnit Notices: 1. diff --git a/tests/unit/TextUI/SourceFilterTest.php b/tests/unit/TextUI/SourceFilterTest.php index e1e9711628e..fe41843333e 100644 --- a/tests/unit/TextUI/SourceFilterTest.php +++ b/tests/unit/TextUI/SourceFilterTest.php @@ -9,219 +9,422 @@ */ namespace PHPUnit\TextUI\Configuration; -use function realpath; +use function json_encode; +use function sprintf; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; #[CoversClass(SourceFilter::class)] #[Small] -final class SourceFilterTest extends TestCase +final class SourceFilterTest extends AbstractSouceFilterTestCase { public static function provider(): array { - $fixtureDirectory = realpath(__DIR__ . '/../../_files/source-filter'); - return [ 'file included using file' => [ - true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( - [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), - ], - ), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - ), + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource(includeFiles: FileCollection::fromArray( + [ + new File(self::fixturePath('/a/PrefixSuffix.php')), + ], + )), ], 'file included using file, but excluded using directory' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( - [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), - ], - ), - FilterDirectoryCollection::fromArray( + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeFiles: FileCollection::fromArray([ + new File(self::fixturePath('/a/PrefixSuffix.php')), + ]), + excludeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory . '/a', + self::fixturePath('/a'), '', '.php', ), ], ), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ], 'file included using file, but excluded using file' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + excludeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ], 'file included using directory' => [ - true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + ), + ], + 'file included using directory, but excluded using file' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + excludeFiles: FileCollection::fromArray( + [ + new File(self::fixturePath('/a/PrefixSuffix.php')), + ], + ), + ), + ], + 'file included using directory, but excluded using directory' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('/a'), '', '.php'), + ], + ), + ), + ], + 'file included using directory, but wrong suffix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', 'Foobar.php'), + ], + ), + ), + ], + 'file included using directory, but wrong prefix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), 'WrongPrefix', '.php'), + ], + ), + ), + ], + 'file included using directory, but not excluded by suffix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', '.php'), + ], + ), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), '', 'WrongSuffix.php'), + ], + ), + ), + ], + 'file included using directory, but not excluded by prefix' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), 'BadPrefix', '.php'), + ], + ), + ), + ], + 'directory wildcard does not include files at same level' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath(), 'a/*', '.php'), + ], + ), + ), + ], + 'directory wildcard with suffix does not match files' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/Pre*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard with suffix matches directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard with prefix matches directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('*a'), '', '.php'), + ], + ), + ), + ], + 'directory wildcards with prefix and suffix matches directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('*a/c*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard includes files' => [ + [ + self::fixturePath('a/c/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/*'), '', '.php'), + ], + ), + ), + ], + 'directory wildcard includes files in sub-directories' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/*'), '', '.php'), + ], + ), + ), + ], + 'wildcard includes all files' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('*'), '', '.php'), + ], + ), + ), + ], + 'globstar includes files at globstar\'s level' => [ + [ + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/**'), '', '.php'), + ], + ), + ), + ], + 'globstar includes files at globstar\'s level (2)' => [ + [ + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/c/**'), '', '.php'), + ], + ), + ), + ], + 'globstar includes all files' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath('**'), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ], - 'file included using directory, but excluded using file' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + 'globstar with any single char prefix includes sibling files' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath('a/c/Z**'), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + ), + ], + 'globstar with any more than a single char prefix does not include sibling files' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/c/d/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new FilterDirectory( + self::fixturePath('a/c/ZZ**'), + '', + '.php', + ), ], ), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ], - 'file included using directory, but excluded using directory' => [ - false, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php', - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + 'globstar includes zero directories' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath('**/a'), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray( + ), + ], + 'globstar with trailing directory' => [ + [ + self::fixturePath('a/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory . '/a', + self::fixturePath('/a/**/d'), '', '.php', ), ], ), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, + ), + ], + 'question mark' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('a/?/d'), '', '.php'), + ], + ), + ), + ], + 'multiple question marks' => [ + [ + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixSuffix.php') => false, + self::fixturePath('a/c/PrefixSuffix.php') => false, + self::fixturePath('a/PrefixSuffix.php') => false, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory(self::fixturePath('?/?/d'), '', '.php'), + ], + ), ), ], ]; } #[DataProvider('provider')] - public function testDeterminesWhetherFileIsIncluded(bool $expected, string $file, Source $source): void + public function testDeterminesWhetherFileIsIncluded(array $expectations, Source $source): void { - $this->assertSame($expected, (new SourceFilter)->includes($source, $file)); + foreach ($expectations as $file => $shouldInclude) { + $this->assertFileExists($file); + $this->assertSame( + $shouldInclude, + (new SourceFilter((new SourceMapper)->map($source)))->includes($file), + sprintf('expected match to return %s for: %s', json_encode($shouldInclude), $file), + ); + } } } diff --git a/tests/unit/TextUI/SourceMapperTest.php b/tests/unit/TextUI/SourceMapperTest.php index 28ddbaec18f..c4cb7387f92 100644 --- a/tests/unit/TextUI/SourceMapperTest.php +++ b/tests/unit/TextUI/SourceMapperTest.php @@ -9,132 +9,84 @@ */ namespace PHPUnit\TextUI\Configuration; -use const DIRECTORY_SEPARATOR; -use function realpath; +use const PHP_OS_FAMILY; use Generator; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; #[CoversClass(SourceMapper::class)] #[Small] -final class SourceMapperTest extends TestCase +final class SourceMapperTest extends AbstractSouceFilterTestCase { public static function provider(): Generator { - $fixtureDirectory = realpath(__DIR__ . '/../../_files/source-filter'); - yield 'file included using file' => [ [ - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, + self::fixturePath('a/PrefixSuffix.php') => true, ], - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( - [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), - ], - ), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, + self::createSource( + includeFiles: FileCollection::fromArray([ + new File(self::fixturePath('a/PrefixSuffix.php')), + ]), ), ]; yield 'file included using file, but excluded using directory' => [ [ ], - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + self::createSource( + includeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - FilterDirectoryCollection::fromArray( + excludeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory . '/a', + self::fixturePath('/a'), '', '.php', ), ], ), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ]; yield 'file included using file, but excluded using file' => [ [ ], - new Source( - null, - false, - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + self::createSource( + includeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + excludeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ]; - $fileHiddenOnUnix = $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . '.hidden' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php'; + $fileHiddenOnUnix = self::fixturePath('a/c/.hidden/PrefixSuffix.php'); $expectedFiles = [ - $fileHiddenOnUnix => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'Prefix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'Suffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'd' . DIRECTORY_SEPARATOR . 'Prefix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'd' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'd' . DIRECTORY_SEPARATOR . 'Suffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'e' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'e' . DIRECTORY_SEPARATOR . 'g' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'f' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'f' . DIRECTORY_SEPARATOR . 'h' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, + $fileHiddenOnUnix => true, + self::fixturePath('a/PrefixSuffix.php') => true, + self::fixturePath('a/c/Prefix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/c/Suffix.php') => true, + self::fixturePath('a/c/d/Prefix.php') => true, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/d/Suffix.php') => true, + self::fixturePath('b/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + self::fixturePath('b/e/g/PrefixSuffix.php') => true, + self::fixturePath('b/f/PrefixSuffix.php') => true, + self::fixturePath('b/f/h/PrefixSuffix.php') => true, ]; if (PHP_OS_FAMILY !== 'Windows') { @@ -143,47 +95,33 @@ public static function provider(): Generator yield 'file included using directory' => [ $expectedFiles, - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath(), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ]; $expectedFiles = [ - $fileHiddenOnUnix => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'Prefix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'Suffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'd' . DIRECTORY_SEPARATOR . 'Prefix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'd' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR . 'c' . DIRECTORY_SEPARATOR . 'd' . DIRECTORY_SEPARATOR . 'Suffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'e' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'e' . DIRECTORY_SEPARATOR . 'g' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'f' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'f' . DIRECTORY_SEPARATOR . 'h' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, + $fileHiddenOnUnix => true, + self::fixturePath('a/c/Prefix.php') => true, + self::fixturePath('a/c/PrefixSuffix.php') => true, + self::fixturePath('a/c/Suffix.php') => true, + self::fixturePath('a/c/d/Prefix.php') => true, + self::fixturePath('a/c/d/PrefixSuffix.php') => true, + self::fixturePath('a/c/d/Suffix.php') => true, + self::fixturePath('b/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + self::fixturePath('b/e/g/PrefixSuffix.php') => true, + self::fixturePath('b/f/PrefixSuffix.php') => true, + self::fixturePath('b/f/h/PrefixSuffix.php') => true, ]; if (PHP_OS_FAMILY !== 'Windows') { @@ -192,79 +130,216 @@ public static function provider(): Generator yield 'file included using directory, but excluded using file' => [ $expectedFiles, - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath(), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray([]), - FileCollection::fromArray( + excludeFiles: FileCollection::fromArray( [ - new File($fixtureDirectory . '/a/PrefixSuffix.php'), + new File(self::fixturePath('/a/PrefixSuffix.php')), ], ), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ]; yield 'file included using directory, but excluded using directory' => [ [ - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'e' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'e' . DIRECTORY_SEPARATOR . 'g' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'f' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, - $fixtureDirectory . DIRECTORY_SEPARATOR . 'b' . DIRECTORY_SEPARATOR . 'f' . DIRECTORY_SEPARATOR . 'h' . DIRECTORY_SEPARATOR . 'PrefixSuffix.php' => true, + self::fixturePath('b/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixSuffix.php') => true, + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + self::fixturePath('b/e/g/PrefixSuffix.php') => true, + self::fixturePath('b/f/PrefixSuffix.php') => true, + self::fixturePath('b/f/h/PrefixSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + self::fixturePath(), + '', + '.php', + ), + ], + ), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + self::fixturePath('/a'), + '', + '.php', + ), + ], + ), + ), + ]; + + yield 'files included using directory and prefix' => [ + [ + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + path: self::fixturePath(), + prefix: 'PrefixExample', + suffix: '.php', + ), + ], + ), + ), + ]; + + yield 'files included using directory and suffix' => [ + [ + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + path: self::fixturePath(), + prefix: '', + suffix: 'ExampleSuffix.php', + ), + ], + ), + ), + ]; + + yield 'files excluded using directory and prefix' => [ + [ + self::fixturePath('a/c/Suffix.php') => true, + self::fixturePath('a/c/d/Suffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + self::fixturePath(), + '', + '.php', + ), + ], + ), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + path: self::fixturePath(), + prefix: 'Prefix', + suffix: '.php', + ), + ], + ), + ), + ]; + + yield 'files excluded using directory and suffix' => [ + [ + self::fixturePath('a/c/Prefix.php') => true, + self::fixturePath('a/c/d/Prefix.php') => true, ], - new Source( - null, - false, - FilterDirectoryCollection::fromArray( + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory, + self::fixturePath(), '', '.php', ), ], ), - FileCollection::fromArray([]), - FilterDirectoryCollection::fromArray( + excludeDirectories: FilterDirectoryCollection::fromArray( [ new FilterDirectory( - $fixtureDirectory . '/a', + path: self::fixturePath(), + prefix: '', + suffix: 'Suffix.php', + ), + ], + ), + ), + ]; + + yield 'files included using same directory and different suffixes' => [ + [ + self::fixturePath('a/c/Prefix.php') => true, + self::fixturePath('a/c/d/Prefix.php') => true, + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + self::fixturePath(), + '', + 'ExampleSuffix.php', + ), + new FilterDirectory( + self::fixturePath(), '', + 'Prefix.php', + ), + ], + ), + ), + ]; + + yield 'files included using same directory and different prefixes' => [ + [ + self::fixturePath('a/c/Suffix.php') => true, + self::fixturePath('a/c/d/Suffix.php') => true, + self::fixturePath('b/e/PrefixExampleSuffix.php') => true, + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + self::fixturePath(), + 'Suffix', + '.php', + ), + new FilterDirectory( + self::fixturePath(), + 'PrefixExample', + '.php', + ), + ], + ), + ), + ]; + + yield 'files excluded using same directory and different prefixes' => [ + [ + ], + self::createSource( + includeDirectories: FilterDirectoryCollection::fromArray([ + new FilterDirectory( + self::fixturePath(), + '', + '.php', + ), + ]), + excludeDirectories: FilterDirectoryCollection::fromArray( + [ + new FilterDirectory( + self::fixturePath(), + 'Prefix', + '.php', + ), + new FilterDirectory( + self::fixturePath(), + 'Suffix', '.php', ), ], ), - FileCollection::fromArray([]), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, ), ]; } diff --git a/tests/unit/Util/ClonerTest.php b/tests/unit/Util/ClonerTest.php deleted file mode 100644 index c712b03ee9b..00000000000 --- a/tests/unit/Util/ClonerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Small; -use PHPUnit\Framework\TestCase; -use RuntimeException; -use stdClass; - -#[CoversClass(Cloner::class)] -#[Small] -final class ClonerTest extends TestCase -{ - public function testReturnsCloneWhenCloningWorks(): void - { - $object = new stdClass; - - $object->key = 'value'; - - $clone = (new Cloner)->clone($object); - - $this->assertNotSame($object, $clone); - $this->assertEquals($object, $clone); - } - - public function testReturnsOriginalWhenCloningDoesNotWork(): void - { - $object = new class - { - public function __clone(): void - { - throw new RuntimeException; - } - }; - - $this->assertSame($object, (new Cloner)->clone($object)); - } -} diff --git a/tests/unit/Util/ColorTest.php b/tests/unit/Util/ColorTest.php index f37887bd71c..57700918e9d 100644 --- a/tests/unit/Util/ColorTest.php +++ b/tests/unit/Util/ColorTest.php @@ -10,6 +10,8 @@ namespace PHPUnit\Util; use const DIRECTORY_SEPARATOR; +use const PHP_EOL; +use function str_repeat; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Small; @@ -72,6 +74,41 @@ public static function colorizePathProvider(): array ]; } + public static function colorizeTextBoxProvider(): array + { + return [ + 'fitting text' => [ + 40, // simulate 40 char wide terminal + 'this is fine' . PHP_EOL . + PHP_EOL . + 'all lines fit nicely' . PHP_EOL . + 'bottom text', + Color::colorize('red', 'this is fine ') . PHP_EOL . + Color::colorize('red', ' ') . PHP_EOL . + Color::colorize('red', 'all lines fit nicely') . PHP_EOL . + Color::colorize('red', 'bottom text '), + ], + 'oversize text' => [ + 20, // simulate 20 char wide terminal + 'this is also fine' . PHP_EOL . + PHP_EOL . + 'the very long lines do not stretch the whole textbox' . PHP_EOL . + 'anymore', + Color::colorize('red', 'this is also fine ') . PHP_EOL . + Color::colorize('red', ' ') . PHP_EOL . + Color::colorize('red', 'the very long lines do not stretch the whole textbox') . PHP_EOL . + Color::colorize('red', 'anymore '), + ], + 'default terminal width cap' => [ + 80, // simulate (default) 80 char wide terminal + str_repeat('.123456789', 8) . PHP_EOL . + 'this is a shorter line', + Color::colorize('red', str_repeat('.123456789', 8)) . PHP_EOL . + Color::colorize('red', 'this is a shorter line '), + ], + ]; + } + public static function whitespacedStringProvider(): array { return [ @@ -119,6 +156,13 @@ public function testColorizePath(?string $prevPath, string $path, bool $colorize $this->assertSame($expected, Color::colorizePath($path, $prevPath, $colorizeFilename)); } + #[TestDox('Colorize an autosizing text box')] + #[DataProvider('colorizeTextBoxProvider')] + public function testColorizeTextBox(int $columns, string $buffer, string $expected): void + { + $this->assertSame($expected, Color::colorizeTextBox('red', $buffer, $columns)); + } + #[TestDox('dim($m) and colorize(\'dim\',$m) return different ANSI codes')] public function testDimAndColorizeDimAreDifferent(): void { diff --git a/tests/unit/Util/ExcludeListTest.php b/tests/unit/Util/ExcludeListTest.php index 16c150bf49a..31eff0d0d75 100644 --- a/tests/unit/Util/ExcludeListTest.php +++ b/tests/unit/Util/ExcludeListTest.php @@ -11,12 +11,14 @@ use function realpath; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\TestCase; #[CoversClass(ExcludeList::class)] #[CoversClass(InvalidDirectoryException::class)] #[Small] +#[RunTestsInSeparateProcesses] final class ExcludeListTest extends TestCase { public function testIsInitialized(): void diff --git a/tests/unit/Util/FilesystemTest.php b/tests/unit/Util/FilesystemTest.php new file mode 100644 index 00000000000..2543bc693c0 --- /dev/null +++ b/tests/unit/Util/FilesystemTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use const DIRECTORY_SEPARATOR; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Filesystem::class)] +#[Small] +final class FilesystemTest extends TestCase +{ + public function testCanResolveStreamOrFile(): void + { + $this->assertSame('php://stdout', Filesystem::resolveStreamOrFile('php://stdout')); + $this->assertSame('socket://hostname:port', Filesystem::resolveStreamOrFile('socket://hostname:port')); + $this->assertSame(__FILE__, Filesystem::resolveStreamOrFile(__FILE__)); + $this->assertSame(__DIR__ . DIRECTORY_SEPARATOR . 'does-not-exist', Filesystem::resolveStreamOrFile(__DIR__ . '/does-not-exist')); + $this->assertFalse(Filesystem::resolveStreamOrFile(__DIR__ . '/does-not-exist/does-not-exist')); + } +} diff --git a/tests/unit/Util/FilterTest.php b/tests/unit/Util/FilterTest.php new file mode 100644 index 00000000000..01056ba7623 --- /dev/null +++ b/tests/unit/Util/FilterTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Test::class)] +#[Small] +final class FilterTest extends TestCase +{ + public function testUnwrapThrowableUsesPreviousValues(): void + { + $first = new Exception('first', 123, null); + $second = new Exception('second', 345, $first); + + $this->assertSame(Filter::stackTraceFromThrowableAsString($second), Filter::stackTraceFromThrowableAsString($first)); + } +} diff --git a/tests/unit/Util/PHP/DefaultJobRunnerTest.php b/tests/unit/Util/PHP/DefaultJobRunnerTest.php new file mode 100644 index 00000000000..6c3bdf48254 --- /dev/null +++ b/tests/unit/Util/PHP/DefaultJobRunnerTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use Generator; +use PHPUnit\Event\Emitter; +use PHPUnit\Event\Facade; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\ChildProcessResultProcessor; +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\CodeCoverage; +use PHPUnit\TestRunner\TestResult\PassedTests; + +#[CoversClass(DefaultJobRunner::class)] +#[UsesClass(Job::class)] +#[UsesClass(Result::class)] +#[Small] +final class DefaultJobRunnerTest extends TestCase +{ + public static function provider(): Generator + { + yield 'output to stdout' => [ + new Result('test', ''), + new Job( + <<<'EOT' + [ + new Result('', 'test'), + new Job( + <<<'EOT' + [ + new Result('test-stdout', 'test-stderr'), + new Job( + <<<'EOT' + [ + new Result('test', ''), + new Job( + <<<'EOT' + [ + new Result('test', ''), + new Job( + <<<'EOT' + 'test'], + ), + ]; + + yield 'arguments' => [ + new Result('test', ''), + new Job( + <<<'EOT' + [ + new Result('test', ''), + new Job( + <<<'EOT' +createStub(Emitter::class), + new PassedTests, + new CodeCoverage, + ), + ); + + $result = $jobRunner->run($job); + + $this->assertSame($expected->stdout(), $result->stdout()); + $this->assertSame($expected->stderr(), $result->stderr()); + } +} diff --git a/tests/unit/Util/PHP/JobTest.php b/tests/unit/Util/PHP/JobTest.php new file mode 100644 index 00000000000..e2b9a6fd1a4 --- /dev/null +++ b/tests/unit/Util/PHP/JobTest.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Job::class)] +#[Small] +final class JobTest extends TestCase +{ + public function testHasCode(): void + { + $code = 'the-code'; + + $job = new Job( + $code, + [], + [], + [], + null, + false, + ); + + $this->assertSame($code, $job->code()); + + $this->assertFalse($job->hasEnvironmentVariables()); + $this->assertFalse($job->hasInput()); + $this->assertFalse($job->redirectErrors()); + } + + public function testMayHavePhpSettings(): void + { + $phpSettings = ['foo' => 'bar']; + + $job = new Job( + 'the-code', + $phpSettings, + [], + [], + null, + false, + ); + + $this->assertSame($phpSettings, $job->phpSettings()); + + $this->assertFalse($job->hasEnvironmentVariables()); + $this->assertFalse($job->hasInput()); + $this->assertFalse($job->redirectErrors()); + } + + public function testMayHaveEnvironmentVariables(): void + { + $environmentVariables = ['foo' => 'bar']; + + $job = new Job( + 'the-code', + [], + $environmentVariables, + [], + null, + false, + ); + + $this->assertTrue($job->hasEnvironmentVariables()); + $this->assertSame($environmentVariables, $job->environmentVariables()); + + $this->assertFalse($job->hasInput()); + $this->assertFalse($job->redirectErrors()); + } + + public function testMayHaveArguments(): void + { + $arguments = ['foo', 'bar']; + + $job = new Job( + 'the-code', + [], + [], + $arguments, + null, + false, + ); + + $this->assertTrue($job->hasArguments()); + $this->assertSame($arguments, $job->arguments()); + + $this->assertFalse($job->hasEnvironmentVariables()); + $this->assertFalse($job->hasInput()); + $this->assertFalse($job->redirectErrors()); + } + + public function testMayHaveInput(): void + { + $input = 'the-input'; + + $job = new Job( + 'the-code', + [], + [], + [], + $input, + false, + ); + + $this->assertTrue($job->hasInput()); + $this->assertSame($input, $job->input()); + + $this->assertFalse($job->hasEnvironmentVariables()); + $this->assertFalse($job->redirectErrors()); + } + + public function testMayNotHaveInput(): void + { + $job = new Job( + 'the-code', + [], + [], + [], + null, + false, + ); + + $this->assertFalse($job->hasInput()); + + $this->expectException(PhpProcessException::class); + + $job->input(); + } + + public function testMayRedirectErrors(): void + { + $job = new Job( + 'the-code', + [], + [], + [], + null, + true, + ); + + $this->assertTrue($job->redirectErrors()); + } +} diff --git a/tests/unit/Util/PHP/ResultTest.php b/tests/unit/Util/PHP/ResultTest.php new file mode 100644 index 00000000000..c8597f2fbd7 --- /dev/null +++ b/tests/unit/Util/PHP/ResultTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Result::class)] +#[Small] +final class ResultTest extends TestCase +{ + public function testHasOutputFromStdout(): void + { + $this->assertSame('stdout', $this->fixture()->stdout()); + } + + public function testHasOutputFromStderr(): void + { + $this->assertSame('stderr', $this->fixture()->stderr()); + } + + private function fixture(): Result + { + return new Result('stdout', 'stderr'); + } +} diff --git a/tests/unit/Util/ReflectionTest.php b/tests/unit/Util/ReflectionTest.php index 5deeaa0dfaa..1efbdbee769 100644 --- a/tests/unit/Util/ReflectionTest.php +++ b/tests/unit/Util/ReflectionTest.php @@ -25,7 +25,7 @@ public function testFindsSourceLocationForMethod(): void $this->assertSame( [ 'file' => realpath(__DIR__ . '/../../_files/BankAccountTest.php'), - 'line' => 30, + 'line' => 22, ], Reflection::sourceLocationFor(BankAccountTest::class, 'testBalanceIsInitiallyZero'), ); @@ -44,7 +44,7 @@ public function testReturnsUnknownSourceLocationForMethodThatDoesNotExist(): voi public function testFindsPublicMethodsInTestClass(): void { - $methods = Reflection::publicMethodsInTestClass(new ReflectionClass(BankAccountTest::class)); + $methods = Reflection::publicMethodsDeclaredDirectlyInTestClass(new ReflectionClass(BankAccountTest::class)); $this->assertCount(3, $methods); $this->assertSame('testBalanceIsInitiallyZero', $methods[0]->getName()); @@ -54,12 +54,11 @@ public function testFindsPublicMethodsInTestClass(): void public function testFindsMethodsInTestClass(): void { - $methods = Reflection::methodsInTestClass(new ReflectionClass(BankAccountTest::class)); + $methods = Reflection::methodsDeclaredDirectlyInTestClass(new ReflectionClass(BankAccountTest::class)); - $this->assertCount(4, $methods); - $this->assertSame('setUp', $methods[0]->getName()); - $this->assertSame('testBalanceIsInitiallyZero', $methods[1]->getName()); - $this->assertSame('testBalanceCannotBecomeNegative', $methods[2]->getName()); - $this->assertSame('testBalanceCannotBecomeNegative2', $methods[3]->getName()); + $this->assertCount(3, $methods); + $this->assertSame('testBalanceIsInitiallyZero', $methods[0]->getName()); + $this->assertSame('testBalanceCannotBecomeNegative', $methods[1]->getName()); + $this->assertSame('testBalanceCannotBecomeNegative2', $methods[2]->getName()); } } diff --git a/tests/unit/Util/TestTest.php b/tests/unit/Util/TestTest.php index b3533fe96c0..ad78c9f238e 100644 --- a/tests/unit/Util/TestTest.php +++ b/tests/unit/Util/TestTest.php @@ -25,9 +25,8 @@ public static function provider(): array return [ [true, new ReflectionMethod(TestCaseTest::class, 'testOne')], [true, new ReflectionMethod(TestCaseTest::class, 'two')], - [true, new ReflectionMethod(TestCaseTest::class, 'three')], + [false, new ReflectionMethod(TestCaseTest::class, 'three')], [false, new ReflectionMethod(TestCaseTest::class, 'four')], - [false, new ReflectionMethod(TestCaseTest::class, 'five')], ]; } diff --git a/tests/unit/Util/VersionComparisonOperatorTest.php b/tests/unit/Util/VersionComparisonOperatorTest.php index a78b2963a2f..b52ab48ffd8 100644 --- a/tests/unit/Util/VersionComparisonOperatorTest.php +++ b/tests/unit/Util/VersionComparisonOperatorTest.php @@ -21,7 +21,7 @@ final class VersionComparisonOperatorTest extends TestCase { /** - * @psalm-return non-empty-list> + * @return non-empty-list> */ public static function validValues(): array { diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json new file mode 100644 index 00000000000..d7de2347d80 --- /dev/null +++ b/tools/.phpstan/composer.json @@ -0,0 +1,14 @@ +{ + "require-dev": { + "phpstan/phpstan": "^2.1.15", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-strict-rules": "^2.0.4", + "tomasvotruba/type-coverage": "^2.0.2", + "ergebnis/phpstan-rules": "^2.8.0" + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + } + } +} diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock new file mode 100644 index 00000000000..c317353c48a --- /dev/null +++ b/tools/.phpstan/composer.lock @@ -0,0 +1,388 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "fd579d4386eb40e4efe0de4d7062544a", + "packages": [], + "packages-dev": [ + { + "name": "ergebnis/phpstan-rules", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/phpstan-rules.git", + "reference": "30e790621fbad05573ef9cd355279fff5122e080" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/30e790621fbad05573ef9cd355279fff5122e080", + "reference": "30e790621fbad05573ef9cd355279fff5122e080", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "phpstan/phpstan": "^2.0.0" + }, + "require-dev": { + "doctrine/orm": "^2.20.0 || ^3.3.0", + "ergebnis/composer-normalize": "^2.45.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.43.0", + "ergebnis/phpunit-slow-test-detector": "^2.18.0", + "nette/di": "^3.1.10", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.4", + "phpstan/phpstan-strict-rules": "^2.0.3", + "phpunit/phpunit": "^9.6.21", + "psr/container": "^2.0.2", + "symfony/finder": "^5.4.45", + "symfony/process": "^5.4.47" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com/" + } + ], + "description": "Provides rules for phpstan/phpstan.", + "homepage": "https://github.com/ergebnis/phpstan-rules", + "keywords": [ + "PHPStan", + "phpstan-rules" + ], + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/phpstan-rules" + }, + "time": "2025-02-18T11:20:05+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.6", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "ce708655043c7050eb050df361c5e313cf708309" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309", + "reference": "ce708655043c7050eb050df361c5e313cf708309", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com/" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org/", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.6" + }, + "time": "2025-03-30T21:06:30+00:00" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "time": "2024-09-04T20:21:43+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.15", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "402d11c1aa40ae2e1c3a512e6a4edb957527b20b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/402d11c1aa40ae2e1c3a512e6a4edb957527b20b", + "reference": "402d11c1aa40ae2e1c3a512e6a4edb957527b20b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-05-14T11:16:08+00:00" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4" + }, + "time": "2025-03-18T11:42:40+00:00" + }, + { + "name": "tomasvotruba/type-coverage", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/TomasVotruba/type-coverage.git", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c", + "shasum": "" + }, + "require": { + "nette/utils": "^3.2 || ^4.0", + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "config/extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "TomasVotruba\\TypeCoverage\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Measure type coverage of your project", + "keywords": [ + "phpstan-extension", + "static analysis" + ], + "support": { + "issues": "https://github.com/TomasVotruba/type-coverage/issues", + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2" + }, + "funding": [ + { + "url": "https://www.paypal.me/rectorphp", + "type": "custom" + }, + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2025-01-07T00:10:26+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/tools/.phpstan/vendor/autoload.php b/tools/.phpstan/vendor/autoload.php new file mode 100644 index 00000000000..94a663ef036 --- /dev/null +++ b/tools/.phpstan/vendor/autoload.php @@ -0,0 +1,22 @@ +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan'); + } +} + +return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan'; diff --git a/tools/.phpstan/vendor/bin/phpstan.phar b/tools/.phpstan/vendor/bin/phpstan.phar new file mode 100755 index 00000000000..fecf96f69d9 --- /dev/null +++ b/tools/.phpstan/vendor/bin/phpstan.phar @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'); + } +} + +return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'; diff --git a/tools/.phpstan/vendor/composer/ClassLoader.php b/tools/.phpstan/vendor/composer/ClassLoader.php new file mode 100644 index 00000000000..7824d8f7eaf --- /dev/null +++ b/tools/.phpstan/vendor/composer/ClassLoader.php @@ -0,0 +1,579 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/tools/.phpstan/vendor/composer/InstalledVersions.php b/tools/.phpstan/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000000..2052022fd8e --- /dev/null +++ b/tools/.phpstan/vendor/composer/InstalledVersions.php @@ -0,0 +1,396 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool + */ + private static $installedIsLocalDir; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + $copiedLocalDir = false; + + if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; + } + } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/tools/.phpstan/vendor/composer/LICENSE b/tools/.phpstan/vendor/composer/LICENSE new file mode 100644 index 00000000000..f27399a042d --- /dev/null +++ b/tools/.phpstan/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/tools/.phpstan/vendor/composer/autoload_classmap.php b/tools/.phpstan/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000000..c66a950246a --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_classmap.php @@ -0,0 +1,60 @@ + $vendorDir . '/composer/InstalledVersions.php', + 'Nette\\ArgumentOutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\DeprecatedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\DirectoryNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\FileNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\HtmlStringable' => $vendorDir . '/nette/utils/src/HtmlStringable.php', + 'Nette\\IOException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidArgumentException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidStateException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Iterators\\CachingIterator' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php', + 'Nette\\Iterators\\Mapper' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php', + 'Nette\\Localization\\ITranslator' => $vendorDir . '/nette/utils/src/compatibility.php', + 'Nette\\Localization\\Translator' => $vendorDir . '/nette/utils/src/Translator.php', + 'Nette\\MemberAccessException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\NotImplementedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\NotSupportedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\OutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\ShouldNotHappenException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php', + 'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php', + 'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Utils\\ArrayHash' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php', + 'Nette\\Utils\\ArrayList' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php', + 'Nette\\Utils\\Arrays' => $vendorDir . '/nette/utils/src/Utils/Arrays.php', + 'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php', + 'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php', + 'Nette\\Utils\\FileInfo' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php', + 'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php', + 'Nette\\Utils\\Finder' => $vendorDir . '/nette/utils/src/Utils/Finder.php', + 'Nette\\Utils\\Floats' => $vendorDir . '/nette/utils/src/Utils/Floats.php', + 'Nette\\Utils\\Helpers' => $vendorDir . '/nette/utils/src/Utils/Helpers.php', + 'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php', + 'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/compatibility.php', + 'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php', + 'Nette\\Utils\\ImageColor' => $vendorDir . '/nette/utils/src/Utils/ImageColor.php', + 'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ImageType' => $vendorDir . '/nette/utils/src/Utils/ImageType.php', + 'Nette\\Utils\\Iterables' => $vendorDir . '/nette/utils/src/Utils/Iterables.php', + 'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php', + 'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php', + 'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php', + 'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php', + 'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php', + 'Nette\\Utils\\ReflectionMethod' => $vendorDir . '/nette/utils/src/Utils/ReflectionMethod.php', + 'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php', + 'Nette\\Utils\\Type' => $vendorDir . '/nette/utils/src/Utils/Type.php', + 'Nette\\Utils\\UnknownImageFileException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Validators' => $vendorDir . '/nette/utils/src/Utils/Validators.php', +); diff --git a/tools/.phpstan/vendor/composer/autoload_files.php b/tools/.phpstan/vendor/composer/autoload_files.php new file mode 100644 index 00000000000..b62e293ba24 --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/phpstan/phpstan/bootstrap.php', +); diff --git a/tools/.phpstan/vendor/composer/autoload_namespaces.php b/tools/.phpstan/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000000..15a2ff3ad6d --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/tomasvotruba/type-coverage/src'), + 'PHPStan\\ExtensionInstaller\\' => array($vendorDir . '/phpstan/extension-installer/src'), + 'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-strict-rules/src'), + 'Ergebnis\\PHPStan\\Rules\\' => array($vendorDir . '/ergebnis/phpstan-rules/src'), +); diff --git a/tools/.phpstan/vendor/composer/autoload_real.php b/tools/.phpstan/vendor/composer/autoload_real.php new file mode 100644 index 00000000000..c91f94e3d39 --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_real.php @@ -0,0 +1,48 @@ +register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/tools/.phpstan/vendor/composer/autoload_static.php b/tools/.phpstan/vendor/composer/autoload_static.php new file mode 100644 index 00000000000..17adb2b10a6 --- /dev/null +++ b/tools/.phpstan/vendor/composer/autoload_static.php @@ -0,0 +1,111 @@ + __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'T' => + array ( + 'TomasVotruba\\TypeCoverage\\' => 26, + ), + 'P' => + array ( + 'PHPStan\\ExtensionInstaller\\' => 27, + 'PHPStan\\' => 8, + ), + 'E' => + array ( + 'Ergebnis\\PHPStan\\Rules\\' => 23, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'TomasVotruba\\TypeCoverage\\' => + array ( + 0 => __DIR__ . '/..' . '/tomasvotruba/type-coverage/src', + ), + 'PHPStan\\ExtensionInstaller\\' => + array ( + 0 => __DIR__ . '/..' . '/phpstan/extension-installer/src', + ), + 'PHPStan\\' => + array ( + 0 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src', + ), + 'Ergebnis\\PHPStan\\Rules\\' => + array ( + 0 => __DIR__ . '/..' . '/ergebnis/phpstan-rules/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Nette\\ArgumentOutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\DeprecatedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\DirectoryNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\FileNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\HtmlStringable' => __DIR__ . '/..' . '/nette/utils/src/HtmlStringable.php', + 'Nette\\IOException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidArgumentException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidStateException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Iterators\\CachingIterator' => __DIR__ . '/..' . '/nette/utils/src/Iterators/CachingIterator.php', + 'Nette\\Iterators\\Mapper' => __DIR__ . '/..' . '/nette/utils/src/Iterators/Mapper.php', + 'Nette\\Localization\\ITranslator' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php', + 'Nette\\Localization\\Translator' => __DIR__ . '/..' . '/nette/utils/src/Translator.php', + 'Nette\\MemberAccessException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\NotImplementedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\NotSupportedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\OutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\ShouldNotHappenException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php', + 'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php', + 'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Utils\\ArrayHash' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayHash.php', + 'Nette\\Utils\\ArrayList' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayList.php', + 'Nette\\Utils\\Arrays' => __DIR__ . '/..' . '/nette/utils/src/Utils/Arrays.php', + 'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php', + 'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php', + 'Nette\\Utils\\FileInfo' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileInfo.php', + 'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php', + 'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/utils/src/Utils/Finder.php', + 'Nette\\Utils\\Floats' => __DIR__ . '/..' . '/nette/utils/src/Utils/Floats.php', + 'Nette\\Utils\\Helpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/Helpers.php', + 'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php', + 'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php', + 'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php', + 'Nette\\Utils\\ImageColor' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageColor.php', + 'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ImageType' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageType.php', + 'Nette\\Utils\\Iterables' => __DIR__ . '/..' . '/nette/utils/src/Utils/Iterables.php', + 'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php', + 'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php', + 'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php', + 'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php', + 'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php', + 'Nette\\Utils\\ReflectionMethod' => __DIR__ . '/..' . '/nette/utils/src/Utils/ReflectionMethod.php', + 'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php', + 'Nette\\Utils\\Type' => __DIR__ . '/..' . '/nette/utils/src/Utils/Type.php', + 'Nette\\Utils\\UnknownImageFileException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Validators' => __DIR__ . '/..' . '/nette/utils/src/Utils/Validators.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json new file mode 100644 index 00000000000..2fe6d2edc85 --- /dev/null +++ b/tools/.phpstan/vendor/composer/installed.json @@ -0,0 +1,400 @@ +{ + "packages": [ + { + "name": "ergebnis/phpstan-rules", + "version": "2.8.0", + "version_normalized": "2.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/phpstan-rules.git", + "reference": "30e790621fbad05573ef9cd355279fff5122e080" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/30e790621fbad05573ef9cd355279fff5122e080", + "reference": "30e790621fbad05573ef9cd355279fff5122e080", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "phpstan/phpstan": "^2.0.0" + }, + "require-dev": { + "doctrine/orm": "^2.20.0 || ^3.3.0", + "ergebnis/composer-normalize": "^2.45.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.43.0", + "ergebnis/phpunit-slow-test-detector": "^2.18.0", + "nette/di": "^3.1.10", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.4", + "phpstan/phpstan-strict-rules": "^2.0.3", + "phpunit/phpunit": "^9.6.21", + "psr/container": "^2.0.2", + "symfony/finder": "^5.4.45", + "symfony/process": "^5.4.47" + }, + "time": "2025-02-18T11:20:05+00:00", + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com/" + } + ], + "description": "Provides rules for phpstan/phpstan.", + "homepage": "https://github.com/ergebnis/phpstan-rules", + "keywords": [ + "PHPStan", + "phpstan-rules" + ], + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/phpstan-rules" + }, + "install-path": "../ergebnis/phpstan-rules" + }, + { + "name": "nette/utils", + "version": "v4.0.6", + "version_normalized": "4.0.6.0", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "ce708655043c7050eb050df361c5e313cf708309" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309", + "reference": "ce708655043c7050eb050df361c5e313cf708309", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "time": "2025-03-30T21:06:30+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com/" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org/", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.6" + }, + "install-path": "../nette/utils" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "version_normalized": "1.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "time": "2024-09-04T20:21:43+00:00", + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "install-path": "../phpstan/extension-installer" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.15", + "version_normalized": "2.1.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "402d11c1aa40ae2e1c3a512e6a4edb957527b20b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/402d11c1aa40ae2e1c3a512e6a4edb957527b20b", + "reference": "402d11c1aa40ae2e1c3a512e6a4edb957527b20b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "time": "2025-05-14T11:16:08+00:00", + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "install-path": "../phpstan/phpstan" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "2.0.4", + "version_normalized": "2.0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "time": "2025-03-18T11:42:40+00:00", + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4" + }, + "install-path": "../phpstan/phpstan-strict-rules" + }, + { + "name": "tomasvotruba/type-coverage", + "version": "2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/TomasVotruba/type-coverage.git", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c", + "reference": "d033429580f2c18bda538fa44f2939236a990e0c", + "shasum": "" + }, + "require": { + "nette/utils": "^3.2 || ^4.0", + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "time": "2025-01-07T00:10:26+00:00", + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "config/extension.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "TomasVotruba\\TypeCoverage\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Measure type coverage of your project", + "keywords": [ + "phpstan-extension", + "static analysis" + ], + "support": { + "issues": "https://github.com/TomasVotruba/type-coverage/issues", + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2" + }, + "funding": [ + { + "url": "https://www.paypal.me/rectorphp", + "type": "custom" + }, + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "install-path": "../tomasvotruba/type-coverage" + } + ], + "dev": true, + "dev-package-names": [ + "ergebnis/phpstan-rules", + "nette/utils", + "phpstan/extension-installer", + "phpstan/phpstan", + "phpstan/phpstan-strict-rules", + "tomasvotruba/type-coverage" + ] +} diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php new file mode 100644 index 00000000000..159e368728f --- /dev/null +++ b/tools/.phpstan/vendor/composer/installed.php @@ -0,0 +1,77 @@ + array( + 'name' => '__root__', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'a5331acb9a076a2750308fa6b3cbe97c8727e737', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'a5331acb9a076a2750308fa6b3cbe97c8727e737', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'ergebnis/phpstan-rules' => array( + 'pretty_version' => '2.8.0', + 'version' => '2.8.0.0', + 'reference' => '30e790621fbad05573ef9cd355279fff5122e080', + 'type' => 'phpstan-extension', + 'install_path' => __DIR__ . '/../ergebnis/phpstan-rules', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'nette/utils' => array( + 'pretty_version' => 'v4.0.6', + 'version' => '4.0.6.0', + 'reference' => 'ce708655043c7050eb050df361c5e313cf708309', + 'type' => 'library', + 'install_path' => __DIR__ . '/../nette/utils', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpstan/extension-installer' => array( + 'pretty_version' => '1.4.3', + 'version' => '1.4.3.0', + 'reference' => '85e90b3942d06b2326fba0403ec24fe912372936', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/../phpstan/extension-installer', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpstan/phpstan' => array( + 'pretty_version' => '2.1.15', + 'version' => '2.1.15.0', + 'reference' => '402d11c1aa40ae2e1c3a512e6a4edb957527b20b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phpstan/phpstan', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpstan/phpstan-strict-rules' => array( + 'pretty_version' => '2.0.4', + 'version' => '2.0.4.0', + 'reference' => '3e139cbe67fafa3588e1dbe27ca50f31fdb6236a', + 'type' => 'phpstan-extension', + 'install_path' => __DIR__ . '/../phpstan/phpstan-strict-rules', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'tomasvotruba/type-coverage' => array( + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', + 'reference' => 'd033429580f2c18bda538fa44f2939236a990e0c', + 'type' => 'phpstan-extension', + 'install_path' => __DIR__ . '/../tomasvotruba/type-coverage', + 'aliases' => array(), + 'dev_requirement' => true, + ), + ), +); diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md new file mode 100644 index 00000000000..8b8d5ce6af5 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md @@ -0,0 +1,637 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +For a full diff see [`2.8.0...main`][2.8.0...main]. + +## [`2.8.0`][2.8.0] + +For a full diff see [`2.7.0...2.8.0`][2.7.0...2.8.0]. + +### Added + +- Added `allRules` parameter to allow disabling and enabling all rules ([#913]), by [@localheinz] +- Added `Expressions\NoAssignByReferenceRule`, which reports an error when a variable is assigned by reference ([#914]), by [@localheinz] + +## [`2.7.0`][2.7.0] + +For a full diff see [`2.6.1...2.7.0`][2.6.1...2.7.0]. + +### Added + +- Added `Closures\NoParameterPassedByReferenceRule`, `Functions\NoParameterPassedByReferenceRule`, `Methods\NoParameterPassedByReferenceRule`, which report an error when a closure, a function, or a method has a parameter that is passed by reference ([#911]), by [@localheinz] +- Added `Functions\NoReturnByReferenceRule` and `Methods\NoReturnByReferenceRule`, which report an error when a function or a method returns by reference ([#912]), by [@localheinz] + +## [`2.6.1`][2.6.1] + +For a full diff see [`2.6.0...2.6.1`][2.6.0...2.6.1]. + +### Fixed + +- Adjusted `Methods\NoParameterWithNullableTypeDeclarationRule` to use the appropriate error identifier ([#902]), by [@manuelkiessling] + +## [`2.6.0`][2.6.0] + +For a full diff see [`2.5.2...2.6.0`][2.5.2...2.6.0]. + +### Added + +- Added support for `phpstan/phpstan:^2.0.0` ([#873]), by [@localheinz] + +## [`2.5.2`][2.5.2] + +For a full diff see [`2.5.1...2.5.2`][2.5.1...2.5.2]. + +### Fixed + +- Adjusted `Closures\NoNullableReturnTypeDeclarationRule`, `Closures\NoParameterWithNullableTypeDeclarationRule`, `Functions\NoNullableReturnTypeDeclarationRule`, `Functions\NoParameterWithNullableTypeDeclarationRule`, `Methods\NoNullableReturnTypeDeclarationRule`, `Methods\NoParameterWithNullableTypeDeclarationRule` to detect cases where `null` is referenced with incorrect case or relative to the root namespace ([#897]), by [@localheinz] + +## [`2.5.1`][2.5.1] + +For a full diff see [`2.5.0...2.5.1`][2.5.0...2.5.1]. + +### Fixed + +- Returned rule with error identifier ([#882]), by [@localheinz] +- Adjusted `Methods\FinalInAbstractClassRule` to ignore Doctrine embeddables and entities ([#396]), by [@localheinz] +- Adjusted `Expressions\NoCompactRule` to detect usages of `compact()` with incorrect case ([#889]), by [@localheinz] +- Adjusted `Methods\PrivateInFinalClassRule` to use more appropriate message when detecting a `protected` method in an anonymous class ([#890]), by [@localheinz] +- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods from traits ([#891]), by [@localheinz] +- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods with `phpunit/phpunit` attributes requiring methods to be `protected` ([#863]), by [@cosmastech] +- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods with `phpunit/phpunit` annotations requiring methods to be `protected` ([#895]), by [@cosmastech] + +## [`2.5.0`][2.5.0] + +For a full diff see [`2.4.0...2.5.0`][2.4.0...2.5.0]. + +### Added + +- Added rule error identifiers ([#875]), by [@localheinz] +- Added support for PHP 8.0 ([#877]), by [@localheinz] +- Added support for PHP 7.4 ([#880]), by [@localheinz] + +### Changed + +- Removed dependency on `nikic/php-parser` ([#878]), by [@localheinz] + +## [`2.4.0`][2.4.0] + +For a full diff see [`2.3.0...2.4.0`][2.3.0...2.4.0]. + +### Added + +- Added support for PHP 8.4 ([#872]), by [@localheinz] + +## [`2.3.0`][2.3.0] + +For a full diff see [`2.2.0...2.3.0`][2.2.0...2.3.0]. + +### Changed + +- Allowed installation on PHP 8.4 ([#862]), by [@localheinz] + +## [`2.2.0`][2.2.0] + +For a full diff see [`2.1.0...2.2.0`][2.1.0...2.2.0]. + +### Changed + +- Allowed installation of `nikic/php-parser:^5.0.0` ([#735]), by [@localheinz] + +## [`2.1.0`][2.1.0] + +For a full diff see [`2.0.0...2.1.0`][2.0.0...2.1.0]. + +### Changed + +- Dropped support for PHP 8.0 ([#567]), by [@localheinz] +- Added support for PHP 8.3 ([#604]), by [@nunomaduro] + +## [`2.0.0`][2.0.0] + +For a full diff see [`1.0.0...2.0.0`][1.0.0...2.0.0]. + +### Added + +- Added `methodsAllowedToUseContainerTypeDeclarations` parameter to allow configuring a list of method names that are allowed to have container parameter type declarations ([#541), by [@localheinz] +- Allowed disabling rules ([#542), by [@localheinz] +- Added support for nullable union types ([#543), by [@localheinz] + +### Changed + +- Dropped support for PHP 7.2 ([#496]), by [@localheinz] +- Dropped support for PHP 7.3 ([#498]), by [@localheinz] +- Dropped support for PHP 7.4 ([#499]), by [@localheinz] +- Added support for PHP 8.2 ([#540]), by [@localheinz] + +### Removed + +- Removed `Expressions\NoEmptyRule` ([#525]), by [@enumag] + +## [`1.0.0`][1.0.0] + +For a full diff see [`0.15.3...1.0.0`][0.15.3...1.0.0]. + +### Changed + +- Added support for `phpstan/phpstan:^1.0.0` and dropped support for non-stable versions of `phpstan/phpstan` ([#381]), by [@rpkamp] + +### Fixed + +- Adjusted `Classes\FinalRule` to not report an error when a non-final class has a `Doctrinbe\ORM\Mapping\Entity` attribute ([#395]), by [@localheinz] + +## [`0.15.3`][0.15.3] + +For a full diff see [`0.15.2...0.15.3`][0.15.2...0.15.3]. + +### Changed + +- Allow installation with PHP 8.0 ([#294]), by [@localheinz] + +## [`0.15.2`][0.15.2] + +For a full diff see [`0.15.1...0.15.2`][0.15.1...0.15.2]. + +### Changed + +- Dropped support for PHP 7.1 ([#259]), by [@localheinz] + +## [`0.15.1`][0.15.1] + +For a full diff see [`0.15.0...0.15.1`][0.15.0...0.15.1]. + +### Changed + +- Adjusted `Methods\FinalInAbstractClass` rule to allow non-`final` `public` constructors in abstract classes ([#248]), by [@Slamdunk] + +## [`0.15.0`][0.15.0] + +For a full diff see [`0.14.4...0.15.0`][0.14.4...0.15.0]. + +### Added + +- Added `Classes\PHPUnit\Framework\TestCaseWithSuffixRule`, which reports an error when a concrete class extending `PHPUnit\Framework\TestCase` does not have a `Test` suffix ([#225]), by [@localheinz] + +## [`0.14.4`][0.14.4] + +For a full diff see [`0.14.3...0.14.4`][0.14.3...0.14.4]. + +### Fixed + +- Ignored classes with `@ORM\Mapping\Entity` annotations in `FinalRule` ([#202]), by [@localheinz] + +## [`0.14.3`][0.14.3] + +For a full diff see [`0.14.2...0.14.3`][0.14.2...0.14.3]. + +### Fixed + +- Ignored first line in `DeclareStrictTypesRule` when it is a shebang ([#186]), by [@Great-Antique] + +## [`0.14.2`][0.14.2] + +For a full diff see [`0.14.1...0.14.2`][0.14.1...0.14.2]. + +### Fixed + +- Brought back support for PHP 7.1 ([#166]), by [@localheinz] + +## [`0.14.1`][0.14.1] + +For a full diff see [`0.14.0...0.14.1`][0.14.0...0.14.1]. + +### Fixed + +- Removed an inappropriate `replace` configuration from `composer.json` ([#161]), by [@localheinz] + +## [`0.14.0`][0.14.0] + +For a full diff see [`0.13.0...0.14.0`][0.13.0...0.14.0]. + +### Changed + +- Allowed installation of `phpstan/phpstan:~0.12.0` ([#147]), by [@localheinz] +- Renamed vendor namespace `Localheinz` to `Ergebnis` after move to [@ergebnis] ([#157]), by [@localheinz] + + Run + + ```sh + composer remove localheinz/phpstan-rules + ``` + + and + + ```sh + composer require ergebnis/phpstan-rules + ``` + + to update. + + Run + + ```sh + find . -type f -exec sed -i '.bak' 's/Localheinz\\PHPStan/Ergebnis\\PHPStan/g' {} \; + ``` + + to replace occurrences of `Localheinz\PHPStan` with `Ergebnis\PHPStan`. + + Run + + ```sh + find -type f -name '*.bak' -delete + ``` + + to delete backup files created in the previous step. + +- Moved parameters into `ergebnis` section to prevent conflicts with global parameters ([#158]), by [@localheinz] + + Where previously `phpstan.neon` looked like the following + + ```neon + parameters: + allowAbstractClasses: true + classesAllowedToBeExtended: [] + classesNotRequiredToBeAbstractOrFinal: [] + interfacesImplementedByContainers: + - Psr\Container\ContainerInterface + ``` + + these parameters now need to be moved into an `ergebnis` section: + + ```diff + parameters: + - allowAbstractClasses: true + - classesAllowedToBeExtended: [] + - classesNotRequiredToBeAbstractOrFinal: [] + - interfacesImplementedByContainers: + - - Psr\Container\ContainerInterface + + ergebnis: + + allowAbstractClasses: true + + classesAllowedToBeExtended: [] + + classesNotRequiredToBeAbstractOrFinal: [] + + interfacesImplementedByContainers: + + - Psr\Container\ContainerInterface + ``` + +### Fixed + +- Dropped support for PHP 7.1 ([#141]), by [@localheinz] + +## [`0.13.0`][0.13.0] + +For a full diff see [`0.12.2...0.13.0`][0.12.2...0.13.0]. + +### Added + +- Added `Methods\PrivateInFinalClassRule` which reports an error when a method in a `final` class is `protected` when it could be `private` ([#126]), by [@localheinz] + +## [`0.12.2`][0.12.2] + +For a full diff see [`0.12.1...0.12.2`][0.12.1...0.12.2]. + +### Fixed + +- Started ignoring interfaces from analysis by `Methods\FinalInAbstractClassRule` to avoid inappropriate errors ([#132]), by [@localheinz] + +## [`0.12.1`][0.12.1] + +For a full diff see [`0.12.0...0.12.1`][0.12.0...0.12.1]. + +### Fixed + +- Started resolving class name in type declaration before attempting to analyze it in the `Methods\NoParameterWithContainerTypeDeclarationRule` to avoid errors where class `self` is not found ([#128]), by [@localheinz] + +## [`0.12.0`][0.12.0] + +For a full diff see [`0.11.0...0.12.0`][0.11.0...0.12.0]. + +### Added + +- Added `Methods\NoParameterWithContainerTypeDeclarationRule`, which reports an error when a method has a type declaration that corresponds to a known dependency injection container or service locator ([#122]), by [@localheinz] +- Added `Methods\FinalInAbstractClassRule`, which reports an error when a concrete `public` or `protected` method in an `abstract` class is not `final` ([#123]), by [@localheinz] + +## [`0.11.0`][0.11.0] + +For a full diff see [`0.10.0...0.11.0`][0.10.0...0.11.0]. + +### Added + +- Added `Files\DeclareStrictTypesRule`, which reports an error when a PHP file does not have a `declare(strict_types=1)` declaration ([#79] +- Added `Expressions\NoEmptyRule`, which reports an error when the language construct `empty()` is used ([#110]), by [@localheinz] +- Added `Expressions\NoEvalRule`, which reports an error when the language construct `eval()` is used ([#112]), by [@localheinz] +- Added `Expressions\NoErrorSuppressionRule`, which reports an error when `@` is used to suppress errors ([#113]), by [@localheinz] +- Added `Expressions\NoCompactRule`, which reports an error when the function `compact()` is used ([#116]), by [@localheinz] +- Added `Statements\NoSwitchRule`, which reports an error when the statement `switch()` is used ([#117]), by [@localheinz] + +### Changed + +- Require at least `nikic/php-parser:^4.2.3` ([#102]), by [@localheinz] +- Require at least `phpstan/phpstan:~0.11.15` ([#103]), by [@localheinz] + +## [`0.10.0`][0.10.0] + +For a full diff see [`0.9.1...0.10.0`][0.9.1...0.10.0]. + +### Changed + +- Require at least `phpstan/phpstan:~0.11.7` ([#91]), by [@localheinz] + +### Fixed + +- Added missing `parametersSchema` configuration to `rules.neon`, which is required for use with `bleedingEdge.neon`, see [`phpstan/phpstan@54a125d`](https://github.com/phpstan/phpstan/commit/54a125df47fa097b792cb9a3e70c49f753f66b85) ([#93]), by [@localheinz] +* +## [`0.9.1`][0.9.1] + +For a full diff see [`0.9.0...0.9.1`][0.9.0...0.9.1]. + +### Changed + +- Allow usage with [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer) ([#89]), by [@localheinz] + +## [`0.9.0`][0.9.0] + +For a full diff see [`0.8.1...0.9.0`][0.8.1...0.9.0]. + +### Changed + +- Adjusted `Classes\FinalRule` to ignore Doctrine entities when they are annotated ([#84]), by [@localheinz] + +## [`0.8.1`][0.8.1] + +For a full diff see [`0.8.0...0.8.1`][0.8.0...0.8.1]. + +### Fixed + +- Actually enable `Expressions\NoIssetRule` ([#83]), by [@localheinz] + +## [`0.8.0`][0.8.0] + +For a full diff see [`0.7.1...0.8.0`][0.7.1...0.8.0]. + +### Added + +- Added `Expressions\NoIssetRule`, which reports an error when the language construct `isset()` is used ([#81]), by [@localheinz] + +## [`0.7.1`][0.7.1] + +For a full diff see [`0.7.0...0.7.1`][0.7.0...0.7.1]. + +### Changed + +- Configured `Classes\NoExtendsRule` to allow a set of default classes to be extended ([#73]), by [@localheinz] + +## [`0.7.0`][0.7.0] + +For a full diff see [`0.6.0...0.7.0`][0.6.0...0.7.0]. + +### Added + +- Added `Classes\NoExtendsRule`, which reports an error when a class extends a class that is not allowed to be extended ([#68]), by [@localheinz] + +## [`0.6.0`][0.6.0] + +For a full diff see [`0.5.0...0.6.0`][0.5.0...0.6.0]. + +### Added + +- Allow installation with `phpstan/phpstan:~0.11.0` ([#65]), by [@localheinz] + +## [`0.5.0`][0.5.0] + +For a full diff see [`0.4.0...0.5.0`][0.4.0...0.5.0]. + +### Added + +- Added `Methods\NoConstructorParameterWithDefaultValueRule`, which reports an error when a constructor of an anonymous class or a class has a parameter with a default value ([#45]), by [@localheinz] +- Added parameters `$allowAbstractClasses` and `$classesNotRequiredToBeAbstractOrFinal` to allow configuration of `Classes`FinalRule` ([#51]), by [@localheinz] + +### Removed + +- Removed `Classes\AbstractOrFinalRule` after merging behaviour into `Classes\FinalRule` ([#51]), by [@localheinz] +- Removed default values from constructor of `Classes\FinalRule` ([#53]), by [@localheinz] + +## [`0.4.0`][0.4.0] + +For a full diff see [`0.3.0...0.4.0`][0.3.0...0.4.0] + +### Changed + +- Removed double-quotes from error messages to be more consistent with error messages generated by `phpstan/phstan` ([#39]), by [@localheinz] +- Modified wording and placement of method, function, and parameter names in error messages to be more consistent with error messages generated by `phpstan/phstan` ([#42]), by [@localheinz] + +## [`0.3.0`][0.3.0] + +For a full diff see [`0.2.0...0.3.0`][0.2.0...0.3.0] + +### Added + +- Added `Functions\NoNullableReturnTypeDeclarationRule`, which reports an error when a function has a nullable return type declaration, and `Methods\NoNullableReturnTypeDeclarationRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a nullable return type declaration ([#16]), by [@localheinz] +- Added `Closures\NoParameterWithNullDefaultValueRule`, which reports an error when a closure has a parameter with `null` as default value ([#26]), by [@localheinz] +- Added `Closures\NoNullableReturnTypeDeclarationRule`, which reports an error when a closure has a nullable return type declaration ([#29]), by [@localheinz] +- Added `Functions\NoParameterWithNullDefaultValueRule`, which reports an error when a function has a parameter with `null` as default value ([#31]), by [@localheinz] +- Added `Methods\NoParameterWithNullDefaultValueRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with `null` as default value ([#32]), by [@localheinz] +- Added `Closures\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a closure has a parameter with a nullable type declaration ([#33]), by [@localheinz] +- Added `Functions\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a function has a parameter with a nullable type declaration ([#34]), by [@localheinz] +- Added `Methods\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with a nullable type declaration ([#35]), by [@localheinz] +- Extracted `rules.neon`, so you can easily enable all rules by including it in your `phpstan.neon` ([#37]), by [@localheinz] + +## [`0.2.0`][0.2.0] + +For a full diff see [`0.1.0...0.2.0`][0.1.0...0.2.0] + +### Added + +- Added `Classes\FinalRule`, which reports an error when a non-anonymous class is not `final`, ([#4]), by [@localheinz] + +### Changed + +- Added an `$excludeClassNames` argument to the constructors of `Classes\AbstractOrFinalRule` and `Classes\FinalRule` to allow whitelisting of classes, ([#11]), by [@localheinz] + +## [`0.1.0`][0.1.0] + +For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0]. + +### Added + +- Added `Classes\AbstractOrFinalRule`, which reports an error when a non-anonymous class is neither `abstract` nor `final`, ([#1]), by [@localheinz] + +[0.1.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.1.0 +[0.2.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.2.0 +[0.3.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.3.0 +[0.4.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.4.0 +[0.5.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.5.0 +[0.6.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.6.0 +[0.7.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.7.0 +[0.7.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.7.1 +[0.8.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.8.0 +[0.8.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.8.1 +[0.9.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.9.0 +[0.9.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.9.1 +[0.10.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.10.0 +[0.11.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.11.0 +[0.12.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.0 +[0.12.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.1 +[0.12.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.2 +[0.13.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.13.0 +[0.14.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.0 +[0.14.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.1 +[0.14.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.2 +[0.14.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.3 +[0.14.4]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.4 +[0.15.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.0 +[0.15.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.1 +[0.15.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.2 +[0.15.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.3 +[1.0.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/1.0.0 +[2.0.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.0.0 +[2.1.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.1.0 +[2.2.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.2.0 +[2.3.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.3.0 +[2.4.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.4.0 +[2.5.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.0 +[2.5.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.1 +[2.5.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.2 +[2.6.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.6.0 +[2.6.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.6.1 +[2.7.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.7.0 +[2.8.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.8.0 + +[362c7ea...0.1.0]: https://github.com/ergebnis/phpstan-rules/compare/362c7ea...0.1.0 +[0.1.0...0.2.0]: https://github.com/ergebnis/phpstan-rules/compare/0.1.0...0.2.0 +[0.2.0...0.3.0]: https://github.com/ergebnis/phpstan-rules/compare/0.2.0...0.3.0 +[0.3.0...0.4.0]: https://github.com/ergebnis/phpstan-rules/compare/0.3.0...0.4.0 +[0.4.0...0.5.0]: https://github.com/ergebnis/phpstan-rules/compare/0.4.0...0.5.0 +[0.5.0...0.6.0]: https://github.com/ergebnis/phpstan-rules/compare/0.5.0...0.6.0 +[0.6.0...0.7.0]: https://github.com/ergebnis/phpstan-rules/compare/0.6.0...0.7.0 +[0.7.0...0.7.1]: https://github.com/ergebnis/phpstan-rules/compare/0.7.0...0.7.1 +[0.7.1...0.8.0]: https://github.com/ergebnis/phpstan-rules/compare/0.7.1...0.8.0 +[0.8.0...0.8.1]: https://github.com/ergebnis/phpstan-rules/compare/0.8.0...0.8.1 +[0.8.1...0.9.0]: https://github.com/ergebnis/phpstan-rules/compare/0.8.1...0.9.0 +[0.9.0...0.9.1]: https://github.com/ergebnis/phpstan-rules/compare/0.9.0...0.9.1 +[0.9.1...0.10.0]: https://github.com/ergebnis/phpstan-rules/compare/0.9.1...0.10.0 +[0.10.0...0.11.0]: https://github.com/ergebnis/phpstan-rules/compare/0.10.0...0.11.0 +[0.11.0...0.12.0]: https://github.com/ergebnis/phpstan-rules/compare/0.11.0...0.12.0 +[0.12.0...0.12.1]: https://github.com/ergebnis/phpstan-rules/compare/0.12.0...0.12.1 +[0.12.1...0.12.2]: https://github.com/ergebnis/phpstan-rules/compare/0.12.1...0.12.2 +[0.12.2...0.13.0]: https://github.com/ergebnis/phpstan-rules/compare/0.12.2...0.13.0 +[0.13.0...0.14.0]: https://github.com/ergebnis/phpstan-rules/compare/0.13.0...0.14.0 +[0.14.0...0.14.1]: https://github.com/ergebnis/phpstan-rules/compare/0.14.0...0.14.1 +[0.14.1...0.14.2]: https://github.com/ergebnis/phpstan-rules/compare/0.14.1...0.14.2 +[0.14.2...0.14.3]: https://github.com/ergebnis/phpstan-rules/compare/0.14.2...0.14.3 +[0.14.3...0.14.4]: https://github.com/ergebnis/phpstan-rules/compare/0.14.3...0.14.4 +[0.14.4...0.15.0]: https://github.com/ergebnis/phpstan-rules/compare/0.14.4...0.15.0 +[0.15.0...0.15.1]: https://github.com/ergebnis/phpstan-rules/compare/0.15.0...0.15.1 +[0.15.1...0.15.2]: https://github.com/ergebnis/phpstan-rules/compare/0.15.1...0.15.2 +[0.15.2...0.15.3]: https://github.com/ergebnis/phpstan-rules/compare/0.15.2...0.15.3 +[0.15.3...1.0.0]: https://github.com/ergebnis/phpstan-rules/compare/0.15.3...1.0.0 +[1.0.0...2.0.0]: https://github.com/ergebnis/phpstan-rules/compare/1.0.0...2.0.0 +[2.0.0...2.1.0]: https://github.com/ergebnis/phpstan-rules/compare/2.0.0...2.1.0 +[2.1.0...2.2.0]: https://github.com/ergebnis/phpstan-rules/compare/2.1.0...2.2.0 +[2.2.0...2.3.0]: https://github.com/ergebnis/phpstan-rules/compare/2.2.0...2.3.0 +[2.3.0...2.4.0]: https://github.com/ergebnis/phpstan-rules/compare/2.3.0...2.4.0 +[2.4.0...2.5.0]: https://github.com/ergebnis/phpstan-rules/compare/2.4.0...2.5.0 +[2.5.0...2.5.1]: https://github.com/ergebnis/phpstan-rules/compare/2.5.0...2.5.1 +[2.5.1...2.5.2]: https://github.com/ergebnis/phpstan-rules/compare/2.5.1...2.5.2 +[2.5.2...2.6.0]: https://github.com/ergebnis/phpstan-rules/compare/2.5.2...2.6.0 +[2.6.0...2.6.1]: https://github.com/ergebnis/phpstan-rules/compare/2.6.0...2.6.1 +[2.6.1...2.7.0]: https://github.com/ergebnis/phpstan-rules/compare/2.6.1...2.7.0 +[2.7.0...2.8.0]: https://github.com/ergebnis/phpstan-rules/compare/2.7.0...2.8.0 +[2.8.0...main]: https://github.com/ergebnis/phpstan-rules/compare/2.8.0...main + +[#1]: https://github.com/ergebnis/phpstan-rules/pull/1 +[#4]: https://github.com/ergebnis/phpstan-rules/pull/4 +[#11]: https://github.com/ergebnis/phpstan-rules/pull/11 +[#16]: https://github.com/ergebnis/phpstan-rules/pull/16 +[#26]: https://github.com/ergebnis/phpstan-rules/pull/26 +[#29]: https://github.com/ergebnis/phpstan-rules/pull/29 +[#31]: https://github.com/ergebnis/phpstan-rules/pull/31 +[#32]: https://github.com/ergebnis/phpstan-rules/pull/32 +[#33]: https://github.com/ergebnis/phpstan-rules/pull/33 +[#34]: https://github.com/ergebnis/phpstan-rules/pull/34 +[#35]: https://github.com/ergebnis/phpstan-rules/pull/35 +[#37]: https://github.com/ergebnis/phpstan-rules/pull/37 +[#39]: https://github.com/ergebnis/phpstan-rules/pull/39 +[#42]: https://github.com/ergebnis/phpstan-rules/pull/42 +[#45]: https://github.com/ergebnis/phpstan-rules/pull/45 +[#51]: https://github.com/ergebnis/phpstan-rules/pull/51 +[#53]: https://github.com/ergebnis/phpstan-rules/pull/53 +[#65]: https://github.com/ergebnis/phpstan-rules/pull/65 +[#68]: https://github.com/ergebnis/phpstan-rules/pull/68 +[#73]: https://github.com/ergebnis/phpstan-rules/pull/73 +[#79]: https://github.com/ergebnis/phpstan-rules/pull/79 +[#81]: https://github.com/ergebnis/phpstan-rules/pull/81 +[#83]: https://github.com/ergebnis/phpstan-rules/pull/83 +[#84]: https://github.com/ergebnis/phpstan-rules/pull/84 +[#89]: https://github.com/ergebnis/phpstan-rules/pull/89 +[#91]: https://github.com/ergebnis/phpstan-rules/pull/91 +[#93]: https://github.com/ergebnis/phpstan-rules/pull/93 +[#102]: https://github.com/ergebnis/phpstan-rules/pull/102 +[#103]: https://github.com/ergebnis/phpstan-rules/pull/103 +[#110]: https://github.com/ergebnis/phpstan-rules/pull/110 +[#112]: https://github.com/ergebnis/phpstan-rules/pull/112 +[#113]: https://github.com/ergebnis/phpstan-rules/pull/113 +[#116]: https://github.com/ergebnis/phpstan-rules/pull/116 +[#117]: https://github.com/ergebnis/phpstan-rules/pull/117 +[#122]: https://github.com/ergebnis/phpstan-rules/pull/122 +[#123]: https://github.com/ergebnis/phpstan-rules/pull/123 +[#126]: https://github.com/ergebnis/phpstan-rules/pull/126 +[#128]: https://github.com/ergebnis/phpstan-rules/pull/128 +[#132]: https://github.com/ergebnis/phpstan-rules/pull/132 +[#141]: https://github.com/ergebnis/phpstan-rules/pull/141 +[#147]: https://github.com/ergebnis/phpstan-rules/pull/147 +[#157]: https://github.com/ergebnis/phpstan-rules/pull/157 +[#158]: https://github.com/ergebnis/phpstan-rules/pull/158 +[#161]: https://github.com/ergebnis/phpstan-rules/pull/161 +[#166]: https://github.com/ergebnis/phpstan-rules/pull/166 +[#186]: https://github.com/ergebnis/phpstan-rules/pull/186 +[#202]: https://github.com/ergebnis/phpstan-rules/pull/202 +[#225]: https://github.com/ergebnis/phpstan-rules/pull/225 +[#248]: https://github.com/ergebnis/phpstan-rules/pull/248 +[#259]: https://github.com/ergebnis/phpstan-rules/pull/259 +[#294]: https://github.com/ergebnis/phpstan-rules/pull/294 +[#381]: https://github.com/ergebnis/phpstan-rules/pull/381 +[#395]: https://github.com/ergebnis/phpstan-rules/pull/395 +[#396]: https://github.com/ergebnis/phpstan-rules/pull/396 +[#496]: https://github.com/ergebnis/phpstan-rules/pull/496 +[#498]: https://github.com/ergebnis/phpstan-rules/pull/498 +[#499]: https://github.com/ergebnis/phpstan-rules/pull/498 +[#525]: https://github.com/ergebnis/phpstan-rules/pull/525 +[#540]: https://github.com/ergebnis/phpstan-rules/pull/540 +[#541]: https://github.com/ergebnis/phpstan-rules/pull/541 +[#542]: https://github.com/ergebnis/phpstan-rules/pull/542 +[#543]: https://github.com/ergebnis/phpstan-rules/pull/543 +[#567]: https://github.com/ergebnis/phpstan-rules/pull/567 +[#735]: https://github.com/ergebnis/phpstan-rules/pull/735 +[#862]: https://github.com/ergebnis/phpstan-rules/pull/862 +[#863]: https://github.com/ergebnis/phpstan-rules/pull/863 +[#872]: https://github.com/ergebnis/phpstan-rules/pull/872 +[#873]: https://github.com/ergebnis/phpstan-rules/pull/873 +[#875]: https://github.com/ergebnis/phpstan-rules/pull/875 +[#877]: https://github.com/ergebnis/phpstan-rules/pull/877 +[#878]: https://github.com/ergebnis/phpstan-rules/pull/878 +[#880]: https://github.com/ergebnis/phpstan-rules/pull/880 +[#882]: https://github.com/ergebnis/phpstan-rules/pull/882 +[#889]: https://github.com/ergebnis/phpstan-rules/pull/889 +[#890]: https://github.com/ergebnis/phpstan-rules/pull/890 +[#891]: https://github.com/ergebnis/phpstan-rules/pull/891 +[#895]: https://github.com/ergebnis/phpstan-rules/pull/895 +[#897]: https://github.com/ergebnis/phpstan-rules/pull/897 +[#902]: https://github.com/ergebnis/phpstan-rules/pull/902 +[#911]: https://github.com/ergebnis/phpstan-rules/pull/911 +[#912]: https://github.com/ergebnis/phpstan-rules/pull/912 +[#913]: https://github.com/ergebnis/phpstan-rules/pull/913 +[#914]: https://github.com/ergebnis/phpstan-rules/pull/914 + +[@cosmastech]: https://github.com/cosmastech +[@enumag]: https://github.com/enumag +[@ergebnis]: https://github.com/ergebnis +[@Great-Antique]: https://github.com/Great-Antique +[@localheinz]: https://github.com/localheinz +[@manuelkiessling]: https://github.com/manuelkiessling +[@nunomaduro]: https://github.com/nunomaduro +[@rpkamp]: https://github.com/rpkamp +[@Slamdunk]: https://github.com/Slamdunk diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md new file mode 100644 index 00000000000..130e719a290 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md @@ -0,0 +1,16 @@ +# The MIT License (MIT) + +Copyright (c) 2018-2025 Andreas Möller + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the _Software_), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED **AS IS**, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md new file mode 100644 index 00000000000..8296743cd8b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md @@ -0,0 +1,683 @@ +# phpstan-rules + +[![Integrate](https://github.com/ergebnis/phpstan-rules/workflows/Integrate/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) +[![Merge](https://github.com/ergebnis/phpstan-rules/workflows/Merge/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) +[![Release](https://github.com/ergebnis/phpstan-rules/workflows/Release/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) +[![Renew](https://github.com/ergebnis/phpstan-rules/workflows/Renew/badge.svg)](https://github.com/ergebnis/phpstan-rules/actions) + +[![Code Coverage](https://codecov.io/gh/ergebnis/phpstan-rules/branch/main/graph/badge.svg)](https://codecov.io/gh/ergebnis/phpstan-rules) + +[![Latest Stable Version](https://poser.pugx.org/ergebnis/phpstan-rules/v/stable)](https://packagist.org/packages/ergebnis/phpstan-rules) +[![Total Downloads](https://poser.pugx.org/ergebnis/phpstan-rules/downloads)](https://packagist.org/packages/ergebnis/phpstan-rules) +[![Monthly Downloads](http://poser.pugx.org/ergebnis/phpstan-rules/d/monthly)](https://packagist.org/packages/ergebnis/phpstan-rules) + +This project provides a [`composer`](https://getcomposer.org) package with rules for [`phpstan/phpstan`](https://github.com/phpstan/phpstan). + +## Installation + +Run + +```sh +composer require --dev ergebnis/phpstan-rules +``` + +## Usage + +All of the [rules](https://github.com/ergebnis/phpstan-rules#rules) provided (and used) by this library are included in [`rules.neon`](rules.neon). + +When you are using [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer), `rules.neon` will be automatically included. + +Otherwise you need to include `rules.neon` in your `phpstan.neon`: + +```neon +includes: + - vendor/ergebnis/phpstan-rules/rules.neon +``` + +:bulb: You probably want to use these rules on top of the rules provided by: + +- [`phpstan/phpstan`](https://github.com/phpstan/phpstan) +- [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules) +- [`phpstan/phpstan-strict-rules`](https://github.com/phpstan/phpstan-strict-rules) + +## Rules + +This package provides the following rules for use with [`phpstan/phpstan`](https://github.com/phpstan/phpstan): + +- [`Ergebnis\PHPStan\Rules\Classes\FinalRule`](https://github.com/ergebnis/phpstan-rules#classesfinalrule) +- [`Ergebnis\PHPStan\Rules\Classes\NoExtendsRule`](https://github.com/ergebnis/phpstan-rules#classesnoextendsrule) +- [`Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule`](https://github.com/ergebnis/phpstan-rules#classesphpunitframeworktestcasewithsuffixrule) +- [`Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnonullablereturntypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterpassedbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnullabletypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoassignbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoCompactRule`](https://github.com/ergebnis/phpstan-rules#expressionsnocompactrule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoerrorsuppressionrule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoEvalRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoevalrule) +- [`Ergebnis\PHPStan\Rules\Expressions\NoIssetRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoissetrule) +- [`Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule`](https://github.com/ergebnis/phpstan-rules#filesdeclarestricttypesrule) +- [`Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnonullablereturntypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterpassedbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnullabletypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoreturnbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule`](https://github.com/ergebnis/phpstan-rules#methodsfinalinabstractclassrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoconstructorparameterwithdefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnonullablereturntypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterpassedbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithcontainertypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnullabletypedeclarationrule) +- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnulldefaultvaluerule) +- [`Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoreturnbyreferencerule) +- [`Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule`](https://github.com/ergebnis/phpstan-rules#methodsprivateinfinalclassrule) +- [`Ergebnis\PHPStan\Rules\Statements\NoSwitchRule`](https://github.com/ergebnis/phpstan-rules#statementsnoswitchrule) + +### Classes + +#### `Classes\FinalRule` + +This rule reports an error when a non-anonymous class is not `final`. + +:bulb: This rule ignores classes that + +- use `@Entity`, `@ORM\Entity`, or `@ORM\Mapping\Entity` annotations +- use `Doctrine\ORM\Mapping\Entity` attributes + +on the class level. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + final: + enabled: false +``` + +##### Disallowing `abstract` classes + +By default, this rule allows to declare `abstract` classes. + +You can set the `allowAbstractClasses` parameter to `false` to disallow abstract classes. + +```neon +parameters: + ergebnis: + final: + allowAbstractClasses: false +``` + +##### Excluding classes from inspection + +You can set the `classesNotRequiredToBeAbstractOrFinal` parameter to a list of class names that you want to exclude from inspection. + +```neon +parameters: + ergebnis: + final: + classesNotRequiredToBeAbstractOrFinal: + - Foo\Bar\NeitherAbstractNorFinal + - Bar\Baz\NeitherAbstractNorFinal +``` + +#### `Classes\NoExtendsRule` + +This rule reports an error when a class extends another class. + +##### Defaults + +By default, this rule allows the following classes to be extended: + +- [`PHPUnit\Framework\TestCase`](https://github.com/sebastianbergmann/phpunit/blob/7.5.2/src/Framework/TestCase.php) + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noExtends: + enabled: false +``` + +##### Allowing classes to be extended + +You can set the `classesAllowedToBeExtended` parameter to a list of class names that you want to allow to be extended. + +```neon +parameters: + ergebnis: + noExtends: + classesAllowedToBeExtended: + - Ergebnis\PHPStan\Rules\Test\Integration\AbstractTestCase + - Ergebnis\PHPStan\Rules\Test\Integration\AbstractTestCase +``` + +#### `Classes\PHPUnit\Framework\TestCaseWithSuffixRule` + +This rule reports an error when a concrete class is a sub-class of `PHPUnit\Framework\TestCase` but does not have a `Test` suffix. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + testCaseWithSuffix: + enabled: false +``` + +### Closures + +#### `Closures\NoNullableReturnTypeDeclarationRule` + +This rule reports an error when a closure uses a nullable return type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNullableReturnTypeDeclaration: + enabled: false +``` + +#### `Closures\NoParameterPassedByReferenceRule` + +This rule reports an error when a closure has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterPassedByReference: + enabled: false +``` + +#### `Closures\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a closure has a parameter with a nullable type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullableTypeDeclaration: + enabled: false +``` + +#### `Closures\NoParameterWithNullDefaultValueRule` + +This rule reports an error when a closure has a parameter with `null` as default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullDefaultValue: + enabled: false +``` + +### Expressions + +#### `Expressions\NoAssignByReferenceRule` + +This rule reports an error when [a variable is assigned by reference](https://www.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noAssignByReference: + enabled: false +``` + +#### `Expressions\NoCompactRule` + +This rule reports an error when the function [`compact()`](https://www.php.net/compact) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noCompact: + enabled: false +``` + +#### `Expressions\NoEvalRule` + +This rule reports an error when the language construct [`eval()`](https://www.php.net/eval) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noEval: + enabled: false +``` + +#### `Expressions\NoErrorSuppressionRule` + +This rule reports an error when [`@`](https://www.php.net/manual/en/language.operators.errorcontrol.php) is used to suppress errors. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noErrorSuppression: + enabled: false +``` + +#### `Expressions\NoIssetRule` + +This rule reports an error when the language construct [`isset()`](https://www.php.net/isset) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noIsset: + enabled: false +``` + +### Files + +#### `Files\DeclareStrictTypesRule` + +This rule reports an error when a non-empty file does not contain a `declare(strict_types=1)` declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + declareStrictTypes: + enabled: false +``` + +### Functions + +#### `Functions\NoNullableReturnTypeDeclarationRule` + +This rule reports an error when a function uses a nullable return type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNullableReturnTypeDeclaration: + enabled: false +``` + +#### `Functions\NoParameterPassedByReferenceRule` + +This rule reports an error when a function has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterPassedByReference: + enabled: false +``` + +#### `Functions\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a function has a parameter with a nullable type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullableTypeDeclaration: + enabled: false +``` + +#### `Functions\NoParameterWithNullDefaultValueRule` + +This rule reports an error when a function has a parameter with `null` as default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullDefaultValue: + enabled: false +``` + +#### `Functions\NoReturnByReferenceRule` + +This rule reports an error when a function [returns by reference](https://www.php.net/manual/en/language.references.return.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noReturnByReference: + enabled: false +``` + +### Methods + +#### `Methods\FinalInAbstractClassRule` + +This rule reports an error when a concrete `public` or `protected `method in an `abstract` class is not `final`. + +:bulb: This rule ignores + +- Doctrine embeddables +- Doctrine entities + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + finalInAbstractClass: + enabled: false +``` + +#### `Methods\NoConstructorParameterWithDefaultValueRule` + +This rule reports an error when a constructor declared in + +- an anonymous class +- a class + +has a default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noConstructorParameterWithDefaultValue: + enabled: false +``` + +#### `Methods\NoParameterPassedByReferenceRule` + +This rule reports an error when a method has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterPassedByReference: + enabled: false +``` + +#### `Methods\NoNullableReturnTypeDeclarationRule` + +This rule reports an error when a method declared in + +- an anonymous class +- a class +- an interface + +uses a nullable return type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noNullableReturnTypeDeclaration: + enabled: false +``` + +#### `Methods\NoParameterWithContainerTypeDeclarationRule` + +This rule reports an error when a method has a type declaration for a known dependency injection container or service locator. + +##### Defaults + +By default, this rule disallows the use of type declarations indicating an implementation of + +- [`Psr\Container\ContainerInterface`](https://github.com/php-fig/container/blob/1.0.0/src/ContainerInterface.php) + +is expected to be injected into a method. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithContainerTypeDeclaration: + enabled: false +``` + +##### Configuring container interfaces + +You can set the `interfacesImplementedByContainers` parameter to a list of interface names of additional containers and service locators. + +```neon +parameters: + ergebnis: + noParameterWithContainerTypeDeclaration: + interfacesImplementedByContainers: + - Fancy\DependencyInjection\ContainerInterface + - Other\ServiceLocatorInterface +``` + +##### Configuring methods allowed to use parameters with container type declarations + +You can set the `methodsAllowedToUseContainerTypeDeclarations` parameter to a list of method names that are allowed to use parameters with container type declarations. + +```neon +parameters: + ergebnis: + noParameterWithContainerTypeDeclaration: + methodsAllowedToUseContainerTypeDeclarations: + - loadExtension +``` + +#### `Methods\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a method declared in + +- an anonymous class +- a class +- an interface + +has a parameter with a nullable type declaration. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullableTypeDeclaration: + enabled: false +``` + +#### `Methods\NoParameterWithNullDefaultValueRule` + +This rule reports an error when a method declared in + +- an anonymous class +- a class +- an interface + +has a parameter with `null` as default value. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noParameterWithNullDefaultValue: + enabled: false +``` + +#### `Functions\NoReturnByReferenceRule` + +This rule reports an error when a method [returns by reference](https://www.php.net/manual/en/language.references.return.php). + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noReturnByReference: + enabled: false +``` + +#### `Methods\PrivateInFinalClassRule` + +This rule reports an error when a method in a `final` class is `protected` but could be `private`. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + privateInFinalClass: + enabled: false +``` + +### Statements + +#### `Statements\NoSwitchRule` + +This rule reports an error when the statement [`switch()`](https://www.php.net/manual/control-structures.switch.php) is used. + +##### Disabling the rule + +You can set the `enabled` parameter to `false` to disable this rule. + +```neon +parameters: + ergebnis: + noSwitch: + enabled: false +``` + +## Disabling all rules + +You can disable all rules using the `allRules` configuration parameter: + +```neon +parameters: + ergebnis: + allRules: false +``` + +## Enabling rules one-by-one + +If you have disabled all rules using the `allRules` configuration parameter, you can re-enable individual rules with their corresponding configuration parameters: + +```neon +parameters: + ergebnis: + allRules: false + privateInFinalClass: + enabled: true +``` + +## Changelog + +The maintainers of this project record notable changes to this project in a [changelog](CHANGELOG.md). + +## Contributing + +The maintainers of this project suggest following the [contribution guide](.github/CONTRIBUTING.md). + +## Code of Conduct + +The maintainers of this project ask contributors to follow the [code of conduct](https://github.com/ergebnis/.github/blob/main/CODE_OF_CONDUCT.md). + +## General Support Policy + +The maintainers of this project provide limited support. + +You can support the maintenance of this project by [sponsoring @localheinz](https://github.com/sponsors/localheinz) or [requesting an invoice for services related to this project](mailto:am@localheinz.com?subject=ergebnis/phpstan-rules:%20Requesting%20invoice%20for%20services). + +## PHP Version Support Policy + +This project supports PHP versions with [active and security support](https://www.php.net/supported-versions.php). + +The maintainers of this project add support for a PHP version following its initial release and drop support for a PHP version when it has reached the end of security support. + +## Security Policy + +This project has a [security policy](.github/SECURITY.md). + +## License + +This project uses the [MIT license](LICENSE.md). + +## Credits + +The method [`FinalRule::isWhitelistedClass()`](src/Classes/FinalRule.php) is inspired by the work on [`FinalClassFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalClassFixer.php) and [`FinalInternalClassFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalInternalClassFixer.php), contributed by [Dariusz Rumiński](https://github.com/keradus), [Filippo Tessarotto](https://github.com/Slamdunk), and [Spacepossum](https://github.com/SpacePossum) for [`friendsofphp/php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) (originally licensed under MIT). + +## Social + +Follow [@localheinz](https://twitter.com/intent/follow?screen_name=localheinz) and [@ergebnis](https://twitter.com/intent/follow?screen_name=ergebnis) on Twitter. diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json new file mode 100644 index 00000000000..89d68711b50 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json @@ -0,0 +1,76 @@ +{ + "name": "ergebnis/phpstan-rules", + "description": "Provides rules for phpstan/phpstan.", + "license": "MIT", + "type": "phpstan-extension", + "keywords": [ + "phpstan", + "phpstan-rules" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com/" + } + ], + "homepage": "https://github.com/ergebnis/phpstan-rules", + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "source": "https://github.com/ergebnis/phpstan-rules", + "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md" + }, + "require": { + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "ext-mbstring": "*", + "phpstan/phpstan": "^2.0.0" + }, + "require-dev": { + "doctrine/orm": "^2.20.0 || ^3.3.0", + "ergebnis/composer-normalize": "^2.45.0", + "ergebnis/license": "^2.6.0", + "ergebnis/php-cs-fixer-config": "^6.43.0", + "ergebnis/phpunit-slow-test-detector": "^2.18.0", + "nette/di": "^3.1.10", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.4", + "phpstan/phpstan-strict-rules": "^2.0.3", + "phpunit/phpunit": "^9.6.21", + "psr/container": "^2.0.2", + "symfony/finder": "^5.4.45", + "symfony/process": "^5.4.47" + }, + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\Test\\": "test/" + } + }, + "config": { + "allow-plugins": { + "ergebnis/composer-normalize": true, + "infection/extension-installer": true, + "phpstan/extension-installer": true + }, + "audit": { + "abandoned": "report" + }, + "platform": { + "php": "7.4.33" + }, + "preferred-install": "dist", + "sort-packages": true + }, + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon b/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon new file mode 100644 index 00000000000..a81a7e83f7c --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon @@ -0,0 +1,263 @@ +conditionalTags: + Ergebnis\PHPStan\Rules\Classes\FinalRule: + phpstan.rules.rule: %ergebnis.final.enabled% + Ergebnis\PHPStan\Rules\Classes\NoExtendsRule: + phpstan.rules.rule: %ergebnis.noExtends.enabled% + Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule: + phpstan.rules.rule: %ergebnis.testCaseWithSuffix.enabled% + Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule: + phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% + Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule: + phpstan.rules.rule: %ergebnis.noAssignByReference.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoCompactRule: + phpstan.rules.rule: %ergebnis.noCompact.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule: + phpstan.rules.rule: %ergebnis.noErrorSuppression.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoEvalRule: + phpstan.rules.rule: %ergebnis.noEval.enabled% + Ergebnis\PHPStan\Rules\Expressions\NoIssetRule: + phpstan.rules.rule: %ergebnis.noIsset.enabled% + Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule: + phpstan.rules.rule: %ergebnis.declareStrictTypes.enabled% + Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule: + phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% + Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled% + Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule: + phpstan.rules.rule: %ergebnis.noReturnByReference.enabled% + Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule: + phpstan.rules.rule: %ergebnis.finalInAbstractClass.enabled% + Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule: + phpstan.rules.rule: %ergebnis.noConstructorParameterWithDefaultValue.enabled% + Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule: + phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithContainerTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled% + Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule: + phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled% + Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule: + phpstan.rules.rule: %ergebnis.noReturnByReference.enabled% + Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule: + phpstan.rules.rule: %ergebnis.privateInFinalClass.enabled% + Ergebnis\PHPStan\Rules\Statements\NoSwitchRule: + phpstan.rules.rule: %ergebnis.noSwitch.enabled% + +parameters: + ergebnis: + allRules: true + declareStrictTypes: + enabled: %ergebnis.allRules% + final: + allowAbstractClasses: true + classesNotRequiredToBeAbstractOrFinal: [] + enabled: %ergebnis.allRules% + finalInAbstractClass: + enabled: %ergebnis.allRules% + noAssignByReference: + enabled: %ergebnis.allRules% + noCompact: + enabled: %ergebnis.allRules% + noConstructorParameterWithDefaultValue: + enabled: %ergebnis.allRules% + noErrorSuppression: + enabled: %ergebnis.allRules% + noEval: + enabled: %ergebnis.allRules% + noExtends: + classesAllowedToBeExtended: [] + enabled: %ergebnis.allRules% + noIsset: + enabled: %ergebnis.allRules% + noNullableReturnTypeDeclaration: + enabled: %ergebnis.allRules% + noParameterPassedByReference: + enabled: %ergebnis.allRules% + noParameterWithContainerTypeDeclaration: + enabled: %ergebnis.allRules% + interfacesImplementedByContainers: + - Psr\Container\ContainerInterface + methodsAllowedToUseContainerTypeDeclarations: [] + noParameterWithNullableTypeDeclaration: + enabled: %ergebnis.allRules% + noParameterWithNullDefaultValue: + enabled: %ergebnis.allRules% + noReturnByReference: + enabled: %ergebnis.allRules% + noSwitch: + enabled: %ergebnis.allRules% + privateInFinalClass: + enabled: %ergebnis.allRules% + testCaseWithSuffix: + enabled: %ergebnis.allRules% + +parametersSchema: + ergebnis: structure([ + allRules: bool() + declareStrictTypes: structure([ + enabled: bool(), + ]) + final: structure([ + allowAbstractClasses: bool() + classesNotRequiredToBeAbstractOrFinal: listOf(string()) + enabled: bool(), + ]) + finalInAbstractClass: structure([ + enabled: bool(), + ]) + noAssignByReference: structure([ + enabled: bool(), + ]) + noCompact: structure([ + enabled: bool(), + ]) + noConstructorParameterWithDefaultValue: structure([ + enabled: bool(), + ]) + noErrorSuppression: structure([ + enabled: bool(), + ]) + noExtends: structure([ + classesAllowedToBeExtended: listOf(string()) + enabled: bool(), + ]) + noEval: structure([ + enabled: bool(), + ]) + noIsset: structure([ + enabled: bool(), + ]) + noNullableReturnTypeDeclaration: structure([ + enabled: bool(), + ]) + noParameterPassedByReference: structure([ + enabled: bool(), + ]) + noParameterWithContainerTypeDeclaration: structure([ + enabled: bool(), + interfacesImplementedByContainers: listOf(string()) + methodsAllowedToUseContainerTypeDeclarations: listOf(string()) + ]) + noParameterWithNullableTypeDeclaration: structure([ + enabled: bool(), + ]) + noParameterWithNullDefaultValue: structure([ + enabled: bool(), + ]) + noReturnByReference: structure([ + enabled: bool(), + ]) + noSwitch: structure([ + enabled: bool(), + ]) + privateInFinalClass: structure([ + enabled: bool(), + ]) + testCaseWithSuffix: structure([ + enabled: bool(), + ]) + ]) + +services: + - + class: Ergebnis\PHPStan\Rules\Analyzer + + - + class: Ergebnis\PHPStan\Rules\Classes\FinalRule + arguments: + allowAbstractClasses: %ergebnis.final.allowAbstractClasses% + classesNotRequiredToBeAbstractOrFinal: %ergebnis.final.classesNotRequiredToBeAbstractOrFinal% + + - + class: Ergebnis\PHPStan\Rules\Classes\NoExtendsRule + arguments: + classesAllowedToBeExtended: %ergebnis.noExtends.classesAllowedToBeExtended% + + - + class: Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule + + - + class: Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoCompactRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoEvalRule + + - + class: Ergebnis\PHPStan\Rules\Expressions\NoIssetRule + + - + class: Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule + + - + class: Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule + arguments: + interfacesImplementedByContainers: %ergebnis.noParameterWithContainerTypeDeclaration.interfacesImplementedByContainers% + methodsAllowedToUseContainerTypeDeclarations: %ergebnis.noParameterWithContainerTypeDeclaration.methodsAllowedToUseContainerTypeDeclarations% + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule + + - + class: Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule + + - + class: Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule + + - + class: Ergebnis\PHPStan\Rules\Statements\NoSwitchRule diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php new file mode 100644 index 00000000000..938cab76203 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php @@ -0,0 +1,68 @@ +default instanceof Node\Expr\ConstFetch) { + return false; + } + + return 'null' === $parameter->default->name->toLowerString(); + } + + /** + * @param null|Node\ComplexType|Node\Identifier|Node\Name $typeDeclaration + */ + public function isNullableTypeDeclaration($typeDeclaration): bool + { + if ($typeDeclaration instanceof Node\NullableType) { + return true; + } + + if ($typeDeclaration instanceof Node\UnionType) { + foreach ($typeDeclaration->types as $type) { + if ( + $type instanceof Node\Identifier + && 'null' === $type->toLowerString() + ) { + return true; + } + + if ( + $type instanceof Node\Name\FullyQualified + && $type->hasAttribute('originalName') + ) { + $originalName = $type->getAttribute('originalName'); + + if ( + $originalName instanceof Node\Name + && 'null' === $originalName->toLowerString() + ) { + return true; + } + } + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php new file mode 100644 index 00000000000..937bf0ec293 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php @@ -0,0 +1,162 @@ + + */ +final class FinalRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $whitelistedAnnotations = [ + 'Entity', + 'ORM\Entity', + 'ORM\Mapping\Entity', + ]; + + /** + * @var list + */ + private static array $whitelistedAttributes = [ + ORM\Mapping\Entity::class, + ]; + private bool $allowAbstractClasses; + + /** + * @var list + */ + private array $classesNotRequiredToBeAbstractOrFinal; + private string $errorMessageTemplate = 'Class %s is not final.'; + + /** + * @param list $classesNotRequiredToBeAbstractOrFinal + */ + public function __construct( + bool $allowAbstractClasses, + array $classesNotRequiredToBeAbstractOrFinal + ) { + $this->allowAbstractClasses = $allowAbstractClasses; + $this->classesNotRequiredToBeAbstractOrFinal = \array_map(static function (string $classNotRequiredToBeAbstractOrFinal): string { + return $classNotRequiredToBeAbstractOrFinal; + }, $classesNotRequiredToBeAbstractOrFinal); + + if ($allowAbstractClasses) { + $this->errorMessageTemplate = 'Class %s is neither abstract nor final.'; + } + } + + public function getNodeType(): string + { + return Node\Stmt\Class_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!isset($node->namespacedName)) { + return []; + } + + if (\in_array($node->namespacedName->toString(), $this->classesNotRequiredToBeAbstractOrFinal, true)) { + return []; + } + + if ( + $this->allowAbstractClasses + && $node->isAbstract() + ) { + return []; + } + + if ($node->isFinal()) { + return []; + } + + if ($this->hasWhitelistedAnnotation($node)) { + return []; + } + + if ($this->hasWhitelistedAttribute($node)) { + return []; + } + + $message = \sprintf( + $this->errorMessageTemplate, + $node->namespacedName->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::final()->toString()) + ->build(), + ]; + } + + /** + * This method is inspired by the work on PhpCsFixer\Fixer\ClassNotation\FinalClassFixer and + * PhpCsFixer\Fixer\ClassNotation\FinalInternalClassFixer contributed by Dariusz Rumiński, Filippo Tessarotto, and + * Spacepossum for friendsofphp/php-cs-fixer. + * + * @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalClassFixer.php + * @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalInternalClassFixer.php + * @see https://github.com/keradus + * @see https://github.com/SpacePossum + * @see https://github.com/Slamdunk + */ + private function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool + { + $docComment = $node->getDocComment(); + + if (!$docComment instanceof Comment\Doc) { + return false; + } + + $reformattedComment = $docComment->getReformattedText(); + + if (\is_int(\preg_match_all('/@(\S+)(?=\s|$)/', $reformattedComment, $matches))) { + foreach ($matches[1] as $annotation) { + foreach (self::$whitelistedAnnotations as $whitelistedAnnotation) { + if (0 === \mb_strpos($annotation, $whitelistedAnnotation)) { + return true; + } + } + } + } + + return false; + } + + private function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool + { + foreach ($node->attrGroups as $attributeGroup) { + foreach ($attributeGroup->attrs as $attribute) { + if (\in_array($attribute->name->toString(), self::$whitelistedAttributes, true)) { + return true; + } + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php new file mode 100644 index 00000000000..93ecf7c6f81 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php @@ -0,0 +1,98 @@ + + */ +final class NoExtendsRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $defaultClassesAllowedToBeExtended = [ + 'PHPUnit\\Framework\\TestCase', + ]; + + /** + * @var list + */ + private array $classesAllowedToBeExtended; + + /** + * @param list $classesAllowedToBeExtended + */ + public function __construct(array $classesAllowedToBeExtended) + { + $this->classesAllowedToBeExtended = \array_values(\array_unique(\array_merge( + self::$defaultClassesAllowedToBeExtended, + \array_map(static function (string $classAllowedToBeExtended): string { + /** @var class-string $classAllowedToBeExtended */ + return $classAllowedToBeExtended; + }, $classesAllowedToBeExtended), + ))); + } + + public function getNodeType(): string + { + return Node\Stmt\Class_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$node->extends instanceof Node\Name) { + return []; + } + + $extendedClassName = $node->extends->toString(); + + if (\in_array($extendedClassName, $this->classesAllowedToBeExtended, true)) { + return []; + } + + if (!isset($node->namespacedName)) { + $message = \sprintf( + 'Anonymous class is not allowed to extend "%s".', + $extendedClassName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noExtends()->toString()) + ->build(), + ]; + } + + $extendingClassName = $node->namespacedName->toString(); + + $message = \sprintf( + 'Class "%s" is not allowed to extend "%s".', + $extendingClassName, + $extendedClassName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noExtends()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php new file mode 100644 index 00000000000..76e96c5681b --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php @@ -0,0 +1,95 @@ + + */ +final class TestCaseWithSuffixRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $phpunitTestCaseClassNames = [ + 'PHPUnit\Framework\TestCase', + ]; + private Reflection\ReflectionProvider $reflectionProvider; + + public function __construct(Reflection\ReflectionProvider $reflectionProvider) + { + $this->reflectionProvider = $reflectionProvider; + } + + public function getNodeType(): string + { + return Node\Stmt\Class_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if ($node->isAbstract()) { + return []; + } + + if (!$node->extends instanceof Node\Name) { + return []; + } + + if (!isset($node->namespacedName)) { + return []; + } + + $fullyQualifiedClassName = $node->namespacedName->toString(); + + $classReflection = $this->reflectionProvider->getClass($fullyQualifiedClassName); + + $extendedPhpunitTestCaseClassName = ''; + + foreach (self::$phpunitTestCaseClassNames as $phpunitTestCaseClassName) { + if ($classReflection->isSubclassOf($phpunitTestCaseClassName)) { + $extendedPhpunitTestCaseClassName = $phpunitTestCaseClassName; + + break; + } + } + + if ('' === $extendedPhpunitTestCaseClassName) { + return []; + } + + if (1 === \preg_match('/Test$/', $fullyQualifiedClassName)) { + return []; + } + + $message = \sprintf( + 'Class %s extends %s, is concrete, but does not have a Test suffix.', + $fullyQualifiedClassName, + $extendedPhpunitTestCaseClassName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::testCaseWithSuffix()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php new file mode 100644 index 00000000000..64351579137 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php @@ -0,0 +1,53 @@ + + */ +final class NoNullableReturnTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) { + return []; + } + + return [ + Rules\RuleErrorBuilder::message('Closure has a nullable return type declaration.') + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php new file mode 100644 index 00000000000..2c47cb742f8 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php @@ -0,0 +1,64 @@ + + */ +final class NoParameterPassedByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return $parameter->byRef; + })); + + if (0 === \count($parametersPassedByReference)) { + return []; + } + + return \array_map(static function (Node\Param $parameterPassedByReference): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Closure has parameter $%s that is passed by reference.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString()) + ->build(); + }, $parametersPassedByReference); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php new file mode 100644 index 00000000000..94bd1463542 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php @@ -0,0 +1,72 @@ + + */ +final class NoParameterWithNullDefaultValueRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->hasNullDefaultValue($parameter); + })); + + if (0 === \count($parametersWithNullDefaultValue)) { + return []; + } + + return \array_map(static function (Node\Param $parameterWithNullDefaultValue): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Closure has parameter $%s with null as default value.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 00000000000..3cff6ee6dc5 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,72 @@ + + */ +final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->isNullableTypeDeclaration($parameter->type); + })); + + if (0 === \count($parametersWithNullableTypeDeclaration)) { + return []; + } + + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Closure has parameter $%s with a nullable type declaration.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php new file mode 100644 index 00000000000..3fcbb101be0 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php @@ -0,0 +1,130 @@ +value = $value; + } + + public static function declareStrictTypes(): self + { + return new self('declareStrictTypes'); + } + + public static function final(): self + { + return new self('final'); + } + + public static function finalInAbstractClass(): self + { + return new self('finalInAbstractClass'); + } + + public static function noCompact(): self + { + return new self('noCompact'); + } + + public static function noConstructorParameterWithDefaultValue(): self + { + return new self('noConstructorParameterWithDefaultValue'); + } + + public static function noAssignByReference(): self + { + return new self('noAssignByReference'); + } + + public static function noErrorSuppression(): self + { + return new self('noErrorSuppression'); + } + + public static function noEval(): self + { + return new self('noEval'); + } + + public static function noExtends(): self + { + return new self('noExtends'); + } + + public static function noIsset(): self + { + return new self('noIsset'); + } + + public static function noParameterPassedByReference(): self + { + return new self('noParameterPassedByReference'); + } + + public static function noParameterWithContainerTypeDeclaration(): self + { + return new self('noParameterWithContainerTypeDeclaration'); + } + + public static function noParameterWithNullDefaultValue(): self + { + return new self('noParameterWithNullDefaultValue'); + } + + public static function noParameterWithNullableTypeDeclaration(): self + { + return new self('noParameterWithNullableTypeDeclaration'); + } + + public static function noNullableReturnTypeDeclaration(): self + { + return new self('noNullableReturnTypeDeclaration'); + } + + public static function noReturnByReference(): self + { + return new self('noReturnByReference'); + } + + public static function noSwitch(): self + { + return new self('noSwitch'); + } + + public static function privateInFinalClass(): self + { + return new self('privateInFinalClass'); + } + + public static function testCaseWithSuffix(): self + { + return new self('testCaseWithSuffix'); + } + + public function toString(): string + { + return \sprintf( + 'ergebnis.%s', + $this->value, + ); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php new file mode 100644 index 00000000000..ad8cc1ef996 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php @@ -0,0 +1,41 @@ + + */ +final class NoAssignByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\AssignRef::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Assign by reference should not be used.') + ->identifier(ErrorIdentifier::noAssignByReference()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php new file mode 100644 index 00000000000..cd12ffea348 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php @@ -0,0 +1,49 @@ + + */ +final class NoCompactRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\FuncCall::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$node->name instanceof Node\Name) { + return []; + } + + if ('compact' !== \mb_strtolower($scope->resolveName($node->name))) { + return []; + } + + return [ + Rules\RuleErrorBuilder::message('Function compact() should not be used.') + ->identifier(ErrorIdentifier::noCompact()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php new file mode 100644 index 00000000000..49c0939c892 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php @@ -0,0 +1,41 @@ + + */ +final class NoErrorSuppressionRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\ErrorSuppress::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Error suppression via "@" should not be used.') + ->identifier(ErrorIdentifier::noErrorSuppression()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php new file mode 100644 index 00000000000..b54205787fc --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php @@ -0,0 +1,41 @@ + + */ +final class NoEvalRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\Eval_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Language construct eval() should not be used.') + ->identifier(ErrorIdentifier::noEval()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php new file mode 100644 index 00000000000..f1443d66742 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php @@ -0,0 +1,41 @@ + + */ +final class NoIssetRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Expr\Isset_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Language construct isset() should not be used.') + ->identifier(ErrorIdentifier::noIsset()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php new file mode 100644 index 00000000000..5f9ec1153c9 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php @@ -0,0 +1,70 @@ + + */ +final class DeclareStrictTypesRule implements Rules\Rule +{ + public function getNodeType(): string + { + return FileNode::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + $nodes = $node->getNodes(); + + if (0 === \count($nodes)) { + return []; + } + + $firstNode = \array_shift($nodes); + + if ( + $firstNode instanceof Node\Stmt\InlineHTML + && 2 === $firstNode->getEndLine() + && 0 === \mb_strpos($firstNode->value, '#!') + ) { + $firstNode = \array_shift($nodes); + } + + if ($firstNode instanceof Node\Stmt\Declare_) { + foreach ($firstNode->declares as $declare) { + if ( + 'strict_types' === $declare->key->toLowerString() + && $declare->value instanceof Node\Scalar\LNumber + && 1 === $declare->value->value + ) { + return []; + } + } + } + + return [ + Rules\RuleErrorBuilder::message('File is missing a "declare(strict_types=1)" declaration.') + ->identifier(ErrorIdentifier::declareStrictTypes()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php new file mode 100644 index 00000000000..06da4f724e8 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php @@ -0,0 +1,62 @@ + + */ +final class NoNullableReturnTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!isset($node->namespacedName)) { + return []; + } + + if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) { + return []; + } + + $message = \sprintf( + 'Function %s() has a nullable return type declaration.', + $node->namespacedName->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php new file mode 100644 index 00000000000..e461aa13900 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php @@ -0,0 +1,67 @@ + + */ +final class NoParameterPassedByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return $parameter->byRef; + })); + + if (0 === \count($parametersPassedByReference)) { + return []; + } + + $functionName = $node->namespacedName; + + return \array_map(static function (Node\Param $parameterPassedByReference) use ($functionName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Function %s() has parameter $%s that is passed by reference.', + $functionName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersPassedByReference); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php new file mode 100644 index 00000000000..fe82a4e3d33 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php @@ -0,0 +1,75 @@ + + */ +final class NoParameterWithNullDefaultValueRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->hasNullDefaultValue($parameter); + })); + + if (0 === \count($parametersWithNullDefaultValue)) { + return []; + } + + $functionName = $node->namespacedName; + + return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($functionName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Function %s() has parameter $%s with null as default value.', + $functionName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 00000000000..90275530b6f --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,75 @@ + + */ +final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->isNullableTypeDeclaration($parameter->type); + })); + + if (0 === \count($parametersWithNullableTypeDeclaration)) { + return []; + } + + $functionName = $node->namespacedName; + + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($functionName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Function %s() has parameter $%s with a nullable type declaration.', + $functionName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php new file mode 100644 index 00000000000..0efb7008b71 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php @@ -0,0 +1,50 @@ + + */ +final class NoReturnByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\Function_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (false === $node->byRef) { + return []; + } + + $message = \sprintf( + 'Function %s() returns by reference.', + $node->namespacedName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noReturnByReference()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php new file mode 100644 index 00000000000..89fc226fae5 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php @@ -0,0 +1,123 @@ + + */ +final class FinalInAbstractClassRule implements Rules\Rule +{ + private const DOCTRINE_ATTRIBUTE_NAMES = [ + 'Doctrine\\ORM\\Mapping\\Embeddable', + 'Doctrine\\ORM\\Mapping\\Entity', + ]; + private const DOCTRINE_TAG_NAMES = [ + '@ORM\\Mapping\\Embeddable', + '@ORM\\Embeddable', + '@Embeddable', + '@ORM\\Mapping\\Entity', + '@ORM\\Entity', + '@Entity', + ]; + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + /** @var Reflection\ClassReflection $containingClass */ + $containingClass = $scope->getClassReflection(); + + if (self::isDoctrineEntity($containingClass)) { + return []; + } + + if (!$containingClass->isAbstract()) { + return []; + } + + if ($containingClass->isInterface()) { + return []; + } + + if ($node->isAbstract()) { + return []; + } + + if ($node->isFinal()) { + return []; + } + + if ($node->isPrivate()) { + return []; + } + + if ('__construct' === $node->name->name) { + return []; + } + + $message = \sprintf( + 'Method %s::%s() is not final, but since the containing class is abstract, it should be.', + $containingClass->getName(), + $node->name->toString(), + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::finalInAbstractClass()->toString()) + ->build(), + ]; + } + + private static function isDoctrineEntity(Reflection\ClassReflection $containingClass): bool + { + $attributes = $containingClass->getNativeReflection()->getAttributes(); + + foreach ($attributes as $attribute) { + if (\in_array($attribute->getName(), self::DOCTRINE_ATTRIBUTE_NAMES, true)) { + return true; + } + } + + $resolvedPhpDocBlock = $containingClass->getResolvedPhpDoc(); + + if ($resolvedPhpDocBlock instanceof PhpDoc\ResolvedPhpDocBlock) { + foreach ($resolvedPhpDocBlock->getPhpDocNodes() as $phpDocNode) { + foreach ($phpDocNode->children as $child) { + if (!$child instanceof PhpDocParser\Ast\PhpDoc\PhpDocTagNode) { + continue; + } + + if (\in_array($child->name, self::DOCTRINE_TAG_NAMES, true)) { + return true; + } + } + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php new file mode 100644 index 00000000000..e8b7e34a3f7 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php @@ -0,0 +1,99 @@ + + */ +final class NoConstructorParameterWithDefaultValueRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if ('__construct' !== $node->name->toLowerString()) { + return []; + } + + if (0 === \count($node->params)) { + return []; + } + + $parametersWithDefaultValue = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return self::hasDefaultValue($parameter); + })); + + if (0 === \count($parametersWithDefaultValue)) { + return []; + } + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterWithDefaultValue): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Constructor in anonymous class has parameter $%s with default value.', + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noConstructorParameterWithDefaultValue()->toString()) + ->build(); + }, $parametersWithDefaultValue); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterWithDefaultValue) use ($className): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Constructor in %s has parameter $%s with default value.', + $className, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noConstructorParameterWithDefaultValue()->toString()) + ->build(); + }, $parametersWithDefaultValue); + } + + private static function hasDefaultValue(Node\Param $parameter): bool + { + return null !== $parameter->default; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php new file mode 100644 index 00000000000..e0e84c94719 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php @@ -0,0 +1,76 @@ + + */ +final class NoNullableReturnTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) { + return []; + } + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class has a nullable return type declaration.', + $node->name->name, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } + + $message = \sprintf( + 'Method %s::%s() has a nullable return type declaration.', + $classReflection->getName(), + $node->name->name, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php new file mode 100644 index 00000000000..4c90f696de0 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php @@ -0,0 +1,94 @@ + + */ +final class NoParameterPassedByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersExplicitlyPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool { + return $parameter->byRef; + })); + + if (0 === \count($parametersExplicitlyPassedByReference)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterExplicitlyPassedByReference) use ($methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterExplicitlyPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s() in anonymous class has parameter $%s that is passed by reference.', + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString()) + ->build(); + }, $parametersExplicitlyPassedByReference); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterExplicitlyPassedByReference) use ($className, $methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterExplicitlyPassedByReference->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s::%s() has parameter $%s that is passed by reference.', + $className, + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString()) + ->build(); + }, $parametersExplicitlyPassedByReference); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php new file mode 100644 index 00000000000..f64a8312c39 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php @@ -0,0 +1,179 @@ + + */ +final class NoParameterWithContainerTypeDeclarationRule implements Rules\Rule +{ + private Reflection\ReflectionProvider $reflectionProvider; + + /** + * @var list + */ + private array $interfacesImplementedByContainers; + + /** + * @var list + */ + private array $methodsAllowedToUseContainerTypeDeclarations; + + /** + * @param list $interfacesImplementedByContainers + * @param list $methodsAllowedToUseContainerTypeDeclarations + */ + public function __construct( + Reflection\ReflectionProvider $reflectionProvider, + array $interfacesImplementedByContainers, + array $methodsAllowedToUseContainerTypeDeclarations + ) { + $this->reflectionProvider = $reflectionProvider; + $this->interfacesImplementedByContainers = \array_values(\array_filter( + \array_map(static function (string $interfaceImplementedByContainers): string { + return $interfaceImplementedByContainers; + }, $interfacesImplementedByContainers), + static function (string $interfaceImplementedByContainer): bool { + return \interface_exists($interfaceImplementedByContainer); + }, + )); + $this->methodsAllowedToUseContainerTypeDeclarations = $methodsAllowedToUseContainerTypeDeclarations; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($this->interfacesImplementedByContainers)) { + return []; + } + + if (0 === \count($node->params)) { + return []; + } + + $methodName = $node->name->toString(); + + if (\in_array($methodName, $this->methodsAllowedToUseContainerTypeDeclarations, true)) { + return []; + } + + /** @var Reflection\ClassReflection $containingClass */ + $containingClass = $scope->getClassReflection(); + + return \array_values(\array_reduce( + $node->params, + function (array $errors, Node\Param $node) use ($scope, $containingClass, $methodName): array { + $type = $node->type; + + if (!$type instanceof Node\Name) { + return $errors; + } + + /** @var Node\Expr\Variable $variable */ + $variable = $node->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $classUsedInTypeDeclaration = $this->reflectionProvider->getClass($scope->resolveName($type)); + + if ($classUsedInTypeDeclaration->isInterface()) { + foreach ($this->interfacesImplementedByContainers as $interfaceImplementedByContainer) { + if ($classUsedInTypeDeclaration->getName() === $interfaceImplementedByContainer) { + $errors[] = self::createError( + $containingClass, + $methodName, + $parameterName, + $classUsedInTypeDeclaration, + ); + + return $errors; + } + + if ($classUsedInTypeDeclaration->getNativeReflection()->isSubclassOf($interfaceImplementedByContainer)) { + $errors[] = self::createError( + $containingClass, + $methodName, + $parameterName, + $classUsedInTypeDeclaration, + ); + + return $errors; + } + } + } + + foreach ($this->interfacesImplementedByContainers as $interfaceImplementedByContainer) { + if ($classUsedInTypeDeclaration->getNativeReflection()->implementsInterface($interfaceImplementedByContainer)) { + $errors[] = self::createError( + $containingClass, + $methodName, + $parameterName, + $classUsedInTypeDeclaration, + ); + + return $errors; + } + } + + return $errors; + }, + [], + )); + } + + private static function createError( + Reflection\ClassReflection $classReflection, + string $methodName, + string $parameterName, + Reflection\ClassReflection $classUsedInTypeDeclaration + ): Rules\RuleError { + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class has a parameter $%s with a type declaration of %s, but containers should not be injected.', + $methodName, + $parameterName, + $classUsedInTypeDeclaration->getName(), + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithContainerTypeDeclaration()->toString()) + ->build(); + } + + $message = \sprintf( + 'Method %s::%s() has a parameter $%s with a type declaration of %s, but containers should not be injected.', + $classReflection->getName(), + $methodName, + $parameterName, + $classUsedInTypeDeclaration->getName(), + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithContainerTypeDeclaration()->toString()) + ->build(); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php new file mode 100644 index 00000000000..9888974c695 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php @@ -0,0 +1,102 @@ + + */ +final class NoParameterWithNullDefaultValueRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->hasNullDefaultValue($parameter); + })); + + if (0 === \count($parametersWithNullDefaultValue)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s() in anonymous class has parameter $%s with null as default value.', + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($className, $methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullDefaultValue->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s::%s() has parameter $%s with null as default value.', + $className, + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString()) + ->build(); + }, $parametersWithNullDefaultValue); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 00000000000..632cd7bea27 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,102 @@ + + */ +final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule +{ + private Analyzer $analyzer; + + public function __construct(Analyzer $analyzer) + { + $this->analyzer = $analyzer; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (0 === \count($node->params)) { + return []; + } + + $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool { + return $this->analyzer->isNullableTypeDeclaration($parameter->type); + })); + + if (0 === \count($parametersWithNullableTypeDeclaration)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s() in anonymous class has parameter $%s with a nullable type declaration.', + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($className, $methodName): Rules\RuleError { + /** @var Node\Expr\Variable $variable */ + $variable = $parameterWithNullableTypeDeclaration->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + $message = \sprintf( + 'Method %s::%s() has parameter $%s with a nullable type declaration.', + $className, + $methodName, + $parameterName, + ); + + return Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString()) + ->build(); + }, $parametersWithNullableTypeDeclaration); + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php new file mode 100644 index 00000000000..a1bf87ff604 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php @@ -0,0 +1,72 @@ + + */ +final class NoReturnByReferenceRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + if (false === $node->byRef) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class returns by reference.', + $methodName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noReturnByReference()->toString()) + ->build(), + ]; + } + + $className = $classReflection->getName(); + + $message = \sprintf( + 'Method %s::%s() returns by reference.', + $className, + $methodName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::noReturnByReference()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php new file mode 100644 index 00000000000..9aa2c3cc286 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php @@ -0,0 +1,198 @@ + + */ +final class PrivateInFinalClassRule implements Rules\Rule +{ + /** + * @var list + */ + private static array $whitelistedAnnotations = [ + '@after', + '@before', + '@postCondition', + '@preCondition', + ]; + + /** + * @var list + */ + private static array $whitelistedAttributes = [ + Framework\Attributes\After::class, + Framework\Attributes\Before::class, + Framework\Attributes\PostCondition::class, + Framework\Attributes\PreCondition::class, + ]; + private Type\FileTypeMapper $fileTypeMapper; + + public function __construct(Type\FileTypeMapper $fileTypeMapper) + { + $this->fileTypeMapper = $fileTypeMapper; + } + + public function getNodeType(): string + { + return Node\Stmt\ClassMethod::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + /** @var Reflection\ClassReflection $containingClass */ + $containingClass = $scope->getClassReflection(); + + if (!$containingClass->isFinal()) { + return []; + } + + if ($node->isPublic()) { + return []; + } + + if ($node->isPrivate()) { + return []; + } + + if ($this->hasWhitelistedAnnotation($node, $containingClass)) { + return []; + } + + if (self::hasWhitelistedAttribute($node)) { + return []; + } + + $methodName = $node->name->toString(); + + if (self::isDeclaredByParentClass($containingClass, $methodName)) { + return []; + } + + if (self::isDeclaredByTrait($containingClass, $methodName)) { + return []; + } + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + $message = \sprintf( + 'Method %s() in anonymous class is protected, but since the containing class is final, it can be private.', + $node->name->name, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::privateInFinalClass()->toString()) + ->build(), + ]; + } + + $message = \sprintf( + 'Method %s::%s() is protected, but since the containing class is final, it can be private.', + $containingClass->getName(), + $methodName, + ); + + return [ + Rules\RuleErrorBuilder::message($message) + ->identifier(ErrorIdentifier::privateInFinalClass()->toString()) + ->build(), + ]; + } + + private function hasWhitelistedAnnotation( + Node\Stmt\ClassMethod $node, + Reflection\ClassReflection $containingClass + ): bool { + $docComment = $node->getDocComment(); + + if (!$docComment instanceof Comment\Doc) { + return false; + } + + $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc( + null, + $containingClass->getName(), + null, + null, + $docComment->getText(), + ); + + foreach ($resolvedPhpDoc->getPhpDocNodes() as $phpDocNode) { + foreach ($phpDocNode->getTags() as $tag) { + if (\in_array($tag->name, self::$whitelistedAnnotations, true)) { + return true; + } + } + } + + return false; + } + + private static function hasWhitelistedAttribute(Node\Stmt\ClassMethod $node): bool + { + foreach ($node->attrGroups as $attributeGroup) { + foreach ($attributeGroup->attrs as $attribute) { + if (\in_array($attribute->name->toString(), self::$whitelistedAttributes, true)) { + return true; + } + } + } + + return false; + } + + private static function isDeclaredByParentClass( + Reflection\ClassReflection $containingClass, + string $methodName + ): bool { + $parentClass = $containingClass->getNativeReflection()->getParentClass(); + + if (!$parentClass instanceof \ReflectionClass) { + return false; + } + + if (!$parentClass->hasMethod($methodName)) { + return false; + } + + return true; + } + + private static function isDeclaredByTrait( + Reflection\ClassReflection $containingClass, + string $methodName + ): bool { + foreach ($containingClass->getTraits() as $trait) { + if ($trait->hasMethod($methodName)) { + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php new file mode 100644 index 00000000000..127e5eed3e0 --- /dev/null +++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php @@ -0,0 +1,41 @@ + + */ +final class NoSwitchRule implements Rules\Rule +{ + public function getNodeType(): string + { + return Node\Stmt\Switch_::class; + } + + public function processNode( + Node $node, + Analyser\Scope $scope + ): array { + return [ + Rules\RuleErrorBuilder::message('Control structures using switch should not be used.') + ->identifier(ErrorIdentifier::noSwitch()->toString()) + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php b/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php new file mode 100644 index 00000000000..25851af66c0 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php @@ -0,0 +1,13 @@ + +✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks
+✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …
+✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories
+✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers
+✅ [Helper Functions](https://doc.nette.org/utils/helpers)
+✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML
+✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images
+✅ [Iterables](https://doc.nette.org/utils/iterables)
+✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding
+✅ [Generating Random Strings](https://doc.nette.org/utils/random)
+✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math
+✅ [PHP Reflection](https://doc.nette.org/utils/reflection)
+✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions
+✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements
+✅ [Type](https://doc.nette.org/utils/type) - PHP data type
+✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs
+ +  + +Installation +------------ + +The recommended way to install is via Composer: + +``` +composer require nette/utils +``` + +Nette Utils 4.0 is compatible with PHP 8.0 to 8.4. + +  + +[Support Me](https://github.com/sponsors/dg) +-------------------------------------------- + +Do you like Nette Utils? Are you looking forward to the new features? + +[![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg) + +Thank you! diff --git a/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php b/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php new file mode 100644 index 00000000000..d749d4ee8cd --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php @@ -0,0 +1,22 @@ +counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0); + } + + + /** + * Is the current element the last one? + */ + public function isLast(?int $gridWidth = null): bool + { + return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0); + } + + + /** + * Is the iterator empty? + */ + public function isEmpty(): bool + { + return $this->counter === 0; + } + + + /** + * Is the counter odd? + */ + public function isOdd(): bool + { + return $this->counter % 2 === 1; + } + + + /** + * Is the counter even? + */ + public function isEven(): bool + { + return $this->counter % 2 === 0; + } + + + /** + * Returns the counter. + */ + public function getCounter(): int + { + return $this->counter; + } + + + /** + * Returns the count of elements. + */ + public function count(): int + { + $inner = $this->getInnerIterator(); + if ($inner instanceof \Countable) { + return $inner->count(); + + } else { + throw new Nette\NotSupportedException('Iterator is not countable.'); + } + } + + + /** + * Forwards to the next element. + */ + public function next(): void + { + parent::next(); + if (parent::valid()) { + $this->counter++; + } + } + + + /** + * Rewinds the Iterator. + */ + public function rewind(): void + { + parent::rewind(); + $this->counter = parent::valid() ? 1 : 0; + } + + + /** + * Returns the next key. + */ + public function getNextKey(): mixed + { + return $this->getInnerIterator()->key(); + } + + + /** + * Returns the next element. + */ + public function getNextValue(): mixed + { + return $this->getInnerIterator()->current(); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php b/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php new file mode 100644 index 00000000000..284da29da4c --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php @@ -0,0 +1,33 @@ +callback = $callback; + } + + + public function current(): mixed + { + return ($this->callback)(parent::current(), parent::key()); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/SmartObject.php b/tools/.phpstan/vendor/nette/utils/src/SmartObject.php new file mode 100644 index 00000000000..3b2203f1f74 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/SmartObject.php @@ -0,0 +1,140 @@ +$name ?? null; + if (is_iterable($handlers)) { + foreach ($handlers as $handler) { + $handler(...$args); + } + } elseif ($handlers !== null) { + throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . get_debug_type($handlers) . ' given.'); + } + + return null; + } + + ObjectHelpers::strictCall($class, $name); + } + + + /** + * @throws MemberAccessException + */ + public static function __callStatic(string $name, array $args) + { + ObjectHelpers::strictStaticCall(static::class, $name); + } + + + /** + * @return mixed + * @throws MemberAccessException if the property is not defined. + */ + public function &__get(string $name) + { + $class = static::class; + + if ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property getter + if (!($prop & 0b0001)) { + throw new MemberAccessException("Cannot read a write-only property $class::\$$name."); + } + + $m = ($prop & 0b0010 ? 'get' : 'is') . ucfirst($name); + if ($prop & 0b10000) { + $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call() + $loc = isset($trace['file'], $trace['line']) + ? " in $trace[file] on line $trace[line]" + : ''; + trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED); + } + + if ($prop & 0b0100) { // return by reference + return $this->$m(); + } else { + $val = $this->$m(); + return $val; + } + } else { + ObjectHelpers::strictGet($class, $name); + } + } + + + /** + * @throws MemberAccessException if the property is not defined or is read-only + */ + public function __set(string $name, mixed $value): void + { + $class = static::class; + + if (ObjectHelpers::hasProperty($class, $name)) { // unsetted property + $this->$name = $value; + + } elseif ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property setter + if (!($prop & 0b1000)) { + throw new MemberAccessException("Cannot write to a read-only property $class::\$$name."); + } + + $m = 'set' . ucfirst($name); + if ($prop & 0b10000) { + $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call() + $loc = isset($trace['file'], $trace['line']) + ? " in $trace[file] on line $trace[line]" + : ''; + trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED); + } + + $this->$m($value); + + } else { + ObjectHelpers::strictSet($class, $name); + } + } + + + /** + * @throws MemberAccessException + */ + public function __unset(string $name): void + { + $class = static::class; + if (!ObjectHelpers::hasProperty($class, $name)) { + throw new MemberAccessException("Cannot unset the property $class::\$$name."); + } + } + + + public function __isset(string $name): bool + { + return isset(ObjectHelpers::getMagicProperties(static::class)[$name]); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/StaticClass.php b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php new file mode 100644 index 00000000000..b1d84862eed --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php @@ -0,0 +1,34 @@ + + * @implements \ArrayAccess + */ +class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate +{ + /** + * Transforms array to ArrayHash. + * @param array $array + */ + public static function from(array $array, bool $recursive = true): static + { + $obj = new static; + foreach ($array as $key => $value) { + $obj->$key = $recursive && is_array($value) + ? static::from($value) + : $value; + } + + return $obj; + } + + + /** + * Returns an iterator over all items. + * @return \Iterator + */ + public function &getIterator(): \Iterator + { + foreach ((array) $this as $key => $foo) { + yield $key => $this->$key; + } + } + + + /** + * Returns items count. + */ + public function count(): int + { + return count((array) $this); + } + + + /** + * Replaces or appends a item. + * @param array-key $key + * @param T $value + */ + public function offsetSet($key, $value): void + { + if (!is_scalar($key)) { // prevents null + throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', get_debug_type($key))); + } + + $this->$key = $value; + } + + + /** + * Returns a item. + * @param array-key $key + * @return T + */ + #[\ReturnTypeWillChange] + public function offsetGet($key) + { + return $this->$key; + } + + + /** + * Determines whether a item exists. + * @param array-key $key + */ + public function offsetExists($key): bool + { + return isset($this->$key); + } + + + /** + * Removes the element from this list. + * @param array-key $key + */ + public function offsetUnset($key): void + { + unset($this->$key); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php new file mode 100644 index 00000000000..a402f9bf6c6 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php @@ -0,0 +1,136 @@ + + * @implements \ArrayAccess + */ +class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate +{ + use Nette\SmartObject; + + private array $list = []; + + + /** + * Transforms array to ArrayList. + * @param list $array + */ + public static function from(array $array): static + { + if (!Arrays::isList($array)) { + throw new Nette\InvalidArgumentException('Array is not valid list.'); + } + + $obj = new static; + $obj->list = $array; + return $obj; + } + + + /** + * Returns an iterator over all items. + * @return \Iterator + */ + public function &getIterator(): \Iterator + { + foreach ($this->list as &$item) { + yield $item; + } + } + + + /** + * Returns items count. + */ + public function count(): int + { + return count($this->list); + } + + + /** + * Replaces or appends a item. + * @param int|null $index + * @param T $value + * @throws Nette\OutOfRangeException + */ + public function offsetSet($index, $value): void + { + if ($index === null) { + $this->list[] = $value; + + } elseif (!is_int($index) || $index < 0 || $index >= count($this->list)) { + throw new Nette\OutOfRangeException('Offset invalid or out of range'); + + } else { + $this->list[$index] = $value; + } + } + + + /** + * Returns a item. + * @param int $index + * @return T + * @throws Nette\OutOfRangeException + */ + public function offsetGet($index): mixed + { + if (!is_int($index) || $index < 0 || $index >= count($this->list)) { + throw new Nette\OutOfRangeException('Offset invalid or out of range'); + } + + return $this->list[$index]; + } + + + /** + * Determines whether a item exists. + * @param int $index + */ + public function offsetExists($index): bool + { + return is_int($index) && $index >= 0 && $index < count($this->list); + } + + + /** + * Removes the element at the specified position in this list. + * @param int $index + * @throws Nette\OutOfRangeException + */ + public function offsetUnset($index): void + { + if (!is_int($index) || $index < 0 || $index >= count($this->list)) { + throw new Nette\OutOfRangeException('Offset invalid or out of range'); + } + + array_splice($this->list, $index, 1); + } + + + /** + * Prepends a item. + * @param T $value + */ + public function prepend(mixed $value): void + { + $first = array_slice($this->list, 0, 1); + $this->offsetSet(0, $value); + array_splice($this->list, 1, 0, $first); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php new file mode 100644 index 00000000000..bf09d8a2165 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php @@ -0,0 +1,554 @@ + $array + * @param array-key|array-key[] $key + * @param ?T $default + * @return ?T + * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided + */ + public static function get(array $array, string|int|array $key, mixed $default = null): mixed + { + foreach (is_array($key) ? $key : [$key] as $k) { + if (is_array($array) && array_key_exists($k, $array)) { + $array = $array[$k]; + } else { + if (func_num_args() < 3) { + throw new Nette\InvalidArgumentException("Missing item '$k'."); + } + + return $default; + } + } + + return $array; + } + + + /** + * Returns reference to array item. If the index does not exist, new one is created with value null. + * @template T + * @param array $array + * @param array-key|array-key[] $key + * @return ?T + * @throws Nette\InvalidArgumentException if traversed item is not an array + */ + public static function &getRef(array &$array, string|int|array $key): mixed + { + foreach (is_array($key) ? $key : [$key] as $k) { + if (is_array($array) || $array === null) { + $array = &$array[$k]; + } else { + throw new Nette\InvalidArgumentException('Traversed item is not an array.'); + } + } + + return $array; + } + + + /** + * Recursively merges two fields. It is useful, for example, for merging tree structures. It behaves as + * the + operator for array, ie. it adds a key/value pair from the second array to the first one and retains + * the value from the first array in the case of a key collision. + * @template T1 + * @template T2 + * @param array $array1 + * @param array $array2 + * @return array + */ + public static function mergeTree(array $array1, array $array2): array + { + $res = $array1 + $array2; + foreach (array_intersect_key($array1, $array2) as $k => $v) { + if (is_array($v) && is_array($array2[$k])) { + $res[$k] = self::mergeTree($v, $array2[$k]); + } + } + + return $res; + } + + + /** + * Returns zero-indexed position of given array key. Returns null if key is not found. + */ + public static function getKeyOffset(array $array, string|int $key): ?int + { + return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), strict: true)); + } + + + /** + * @deprecated use getKeyOffset() + */ + public static function searchKey(array $array, $key): ?int + { + return self::getKeyOffset($array, $key); + } + + + /** + * Tests an array for the presence of value. + */ + public static function contains(array $array, mixed $value): bool + { + return in_array($value, $array, true); + } + + + /** + * Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?V + */ + public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed + { + $key = self::firstKey($array, $predicate); + return $key === null + ? ($else ? $else() : null) + : $array[$key]; + } + + + /** + * Returns the last item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?V + */ + public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed + { + $key = self::lastKey($array, $predicate); + return $key === null + ? ($else ? $else() : null) + : $array[$key]; + } + + + /** + * Returns the key of first item (matching the specified predicate if given) or null if there is no such item. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?K + */ + public static function firstKey(array $array, ?callable $predicate = null): int|string|null + { + if (!$predicate) { + return array_key_first($array); + } + foreach ($array as $k => $v) { + if ($predicate($v, $k, $array)) { + return $k; + } + } + return null; + } + + + /** + * Returns the key of last item (matching the specified predicate if given) or null if there is no such item. + * @template K of int|string + * @template V + * @param array $array + * @param ?callable(V, K, array): bool $predicate + * @return ?K + */ + public static function lastKey(array $array, ?callable $predicate = null): int|string|null + { + return $predicate + ? self::firstKey(array_reverse($array, preserve_keys: true), $predicate) + : array_key_last($array); + } + + + /** + * Inserts the contents of the $inserted array into the $array immediately after the $key. + * If $key is null (or does not exist), it is inserted at the beginning. + */ + public static function insertBefore(array &$array, string|int|null $key, array $inserted): void + { + $offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key); + $array = array_slice($array, 0, $offset, preserve_keys: true) + + $inserted + + array_slice($array, $offset, count($array), preserve_keys: true); + } + + + /** + * Inserts the contents of the $inserted array into the $array before the $key. + * If $key is null (or does not exist), it is inserted at the end. + */ + public static function insertAfter(array &$array, string|int|null $key, array $inserted): void + { + if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) { + $offset = count($array) - 1; + } + + $array = array_slice($array, 0, $offset + 1, preserve_keys: true) + + $inserted + + array_slice($array, $offset + 1, count($array), preserve_keys: true); + } + + + /** + * Renames key in array. + */ + public static function renameKey(array &$array, string|int $oldKey, string|int $newKey): bool + { + $offset = self::getKeyOffset($array, $oldKey); + if ($offset === null) { + return false; + } + + $val = &$array[$oldKey]; + $keys = array_keys($array); + $keys[$offset] = $newKey; + $array = array_combine($keys, $array); + $array[$newKey] = &$val; + return true; + } + + + /** + * Returns only those array items, which matches a regular expression $pattern. + * @param string[] $array + * @return string[] + */ + public static function grep( + array $array, + #[Language('RegExp')] + string $pattern, + bool|int $invert = false, + ): array + { + $flags = $invert ? PREG_GREP_INVERT : 0; + return Strings::pcre('preg_grep', [$pattern, $array, $flags]); + } + + + /** + * Transforms multidimensional array to flat array. + */ + public static function flatten(array $array, bool $preserveKeys = false): array + { + $res = []; + $cb = $preserveKeys + ? function ($v, $k) use (&$res): void { $res[$k] = $v; } + : function ($v) use (&$res): void { $res[] = $v; }; + array_walk_recursive($array, $cb); + return $res; + } + + + /** + * Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list. + * @return ($value is list ? true : false) + */ + public static function isList(mixed $value): bool + { + return is_array($value) && ( + PHP_VERSION_ID < 80100 + ? !$value || array_keys($value) === range(0, count($value) - 1) + : array_is_list($value) + ); + } + + + /** + * Reformats table to associative tree. Path looks like 'field|field[]field->field=field'. + * @param string|string[] $path + */ + public static function associate(array $array, $path): array|\stdClass + { + $parts = is_array($path) + ? $path + : preg_split('#(\[\]|->|=|\|)#', $path, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + if (!$parts || $parts === ['->'] || $parts[0] === '=' || $parts[0] === '|') { + throw new Nette\InvalidArgumentException("Invalid path '$path'."); + } + + $res = $parts[0] === '->' ? new \stdClass : []; + + foreach ($array as $rowOrig) { + $row = (array) $rowOrig; + $x = &$res; + + for ($i = 0; $i < count($parts); $i++) { + $part = $parts[$i]; + if ($part === '[]') { + $x = &$x[]; + + } elseif ($part === '=') { + if (isset($parts[++$i])) { + $x = $row[$parts[$i]]; + $row = null; + } + } elseif ($part === '->') { + if (isset($parts[++$i])) { + if ($x === null) { + $x = new \stdClass; + } + + $x = &$x->{$row[$parts[$i]]}; + } else { + $row = is_object($rowOrig) ? $rowOrig : (object) $row; + } + } elseif ($part !== '|') { + $x = &$x[(string) $row[$part]]; + } + } + + if ($x === null) { + $x = $row; + } + } + + return $res; + } + + + /** + * Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling. + */ + public static function normalize(array $array, mixed $filling = null): array + { + $res = []; + foreach ($array as $k => $v) { + $res[is_int($k) ? $v : $k] = is_int($k) ? $filling : $v; + } + + return $res; + } + + + /** + * Returns and removes the value of an item from an array. If it does not exist, it throws an exception, + * or returns $default, if provided. + * @template T + * @param array $array + * @param ?T $default + * @return ?T + * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided + */ + public static function pick(array &$array, string|int $key, mixed $default = null): mixed + { + if (array_key_exists($key, $array)) { + $value = $array[$key]; + unset($array[$key]); + return $value; + + } elseif (func_num_args() < 3) { + throw new Nette\InvalidArgumentException("Missing item '$key'."); + + } else { + return $default; + } + } + + + /** + * Tests whether at least one element in the array passes the test implemented by the provided function. + * @template K of int|string + * @template V + * @param array $array + * @param callable(V, K, array): bool $predicate + */ + public static function some(iterable $array, callable $predicate): bool + { + foreach ($array as $k => $v) { + if ($predicate($v, $k, $array)) { + return true; + } + } + + return false; + } + + + /** + * Tests whether all elements in the array pass the test implemented by the provided function. + * @template K of int|string + * @template V + * @param array $array + * @param callable(V, K, array): bool $predicate + */ + public static function every(iterable $array, callable $predicate): bool + { + foreach ($array as $k => $v) { + if (!$predicate($v, $k, $array)) { + return false; + } + } + + return true; + } + + + /** + * Returns a new array containing all key-value pairs matching the given $predicate. + * @template K of int|string + * @template V + * @param array $array + * @param callable(V, K, array): bool $predicate + * @return array + */ + public static function filter(array $array, callable $predicate): array + { + $res = []; + foreach ($array as $k => $v) { + if ($predicate($v, $k, $array)) { + $res[$k] = $v; + } + } + return $res; + } + + + /** + * Returns an array containing the original keys and results of applying the given transform function to each element. + * @template K of int|string + * @template V + * @template R + * @param array $array + * @param callable(V, K, array): R $transformer + * @return array + */ + public static function map(iterable $array, callable $transformer): array + { + $res = []; + foreach ($array as $k => $v) { + $res[$k] = $transformer($v, $k, $array); + } + + return $res; + } + + + /** + * Returns an array containing new keys and values generated by applying the given transform function to each element. + * If the function returns null, the element is skipped. + * @template K of int|string + * @template V + * @template ResK of int|string + * @template ResV + * @param array $array + * @param callable(V, K, array): ?array{ResK, ResV} $transformer + * @return array + */ + public static function mapWithKeys(array $array, callable $transformer): array + { + $res = []; + foreach ($array as $k => $v) { + $pair = $transformer($v, $k, $array); + if ($pair) { + $res[$pair[0]] = $pair[1]; + } + } + + return $res; + } + + + /** + * Invokes all callbacks and returns array of results. + * @param callable[] $callbacks + */ + public static function invoke(iterable $callbacks, ...$args): array + { + $res = []; + foreach ($callbacks as $k => $cb) { + $res[$k] = $cb(...$args); + } + + return $res; + } + + + /** + * Invokes method on every object in an array and returns array of results. + * @param object[] $objects + */ + public static function invokeMethod(iterable $objects, string $method, ...$args): array + { + $res = []; + foreach ($objects as $k => $obj) { + $res[$k] = $obj->$method(...$args); + } + + return $res; + } + + + /** + * Copies the elements of the $array array to the $object object and then returns it. + * @template T of object + * @param T $object + * @return T + */ + public static function toObject(iterable $array, object $object): object + { + foreach ($array as $k => $v) { + $object->$k = $v; + } + + return $object; + } + + + /** + * Converts value to array key. + */ + public static function toKey(mixed $value): int|string + { + return key([$value => null]); + } + + + /** + * Returns copy of the $array where every item is converted to string + * and prefixed by $prefix and suffixed by $suffix. + * @param string[] $array + * @return string[] + */ + public static function wrap(array $array, string $prefix = '', string $suffix = ''): array + { + $res = []; + foreach ($array as $k => $v) { + $res[$k] = $prefix . $v . $suffix; + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php new file mode 100644 index 00000000000..1777428fdf4 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php @@ -0,0 +1,137 @@ +getClosureScopeClass()?->name; + if (str_ends_with($r->name, '}')) { + return $closure; + + } elseif (($obj = $r->getClosureThis()) && $obj::class === $class) { + return [$obj, $r->name]; + + } elseif ($class) { + return [$class, $r->name]; + + } else { + return $r->name; + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php new file mode 100644 index 00000000000..6ad65205b9d --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php @@ -0,0 +1,140 @@ +format('Y-m-d H:i:s.u'), $time->getTimezone()); + + } elseif (is_numeric($time)) { + if ($time <= self::YEAR) { + $time += time(); + } + + return (new static)->setTimestamp((int) $time); + + } else { // textual or null + return new static((string) $time); + } + } + + + /** + * Creates DateTime object. + * @throws Nette\InvalidArgumentException if the date and time are not valid. + */ + public static function fromParts( + int $year, + int $month, + int $day, + int $hour = 0, + int $minute = 0, + float $second = 0.0, + ): static + { + $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second); + if ( + !checkdate($month, $day, $year) + || $hour < 0 + || $hour > 23 + || $minute < 0 + || $minute > 59 + || $second < 0 + || $second >= 60 + ) { + throw new Nette\InvalidArgumentException("Invalid date '$s'"); + } + + return new static($s); + } + + + /** + * Returns new DateTime object formatted according to the specified format. + */ + public static function createFromFormat( + string $format, + string $time, + string|\DateTimeZone|null $timezone = null, + ): static|false + { + if ($timezone === null) { + $timezone = new \DateTimeZone(date_default_timezone_get()); + + } elseif (is_string($timezone)) { + $timezone = new \DateTimeZone($timezone); + } + + $date = parent::createFromFormat($format, $time, $timezone); + return $date ? static::from($date) : false; + } + + + /** + * Returns JSON representation in ISO 8601 (used by JavaScript). + */ + public function jsonSerialize(): string + { + return $this->format('c'); + } + + + /** + * Returns the date and time in the format 'Y-m-d H:i:s'. + */ + public function __toString(): string + { + return $this->format('Y-m-d H:i:s'); + } + + + /** + * You'd better use: (clone $dt)->modify(...) + */ + public function modifyClone(string $modify = ''): static + { + $dolly = clone $this; + return $modify ? $dolly->modify($modify) : $dolly; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php new file mode 100644 index 00000000000..fb92d119174 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php @@ -0,0 +1,69 @@ +setInfoClass(static::class); + $this->relativePath = $relativePath; + } + + + /** + * Returns the relative directory path. + */ + public function getRelativePath(): string + { + return $this->relativePath; + } + + + /** + * Returns the relative path including file name. + */ + public function getRelativePathname(): string + { + return ($this->relativePath === '' ? '' : $this->relativePath . DIRECTORY_SEPARATOR) + . $this->getBasename(); + } + + + /** + * Returns the contents of the file. + * @throws Nette\IOException + */ + public function read(): string + { + return FileSystem::read($this->getPathname()); + } + + + /** + * Writes the contents to the file. + * @throws Nette\IOException + */ + public function write(string $content): void + { + FileSystem::write($this->getPathname(), $content); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php new file mode 100644 index 00000000000..6328fb856fb --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php @@ -0,0 +1,339 @@ +getPathname()); + } + + foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($origin, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item) { + if ($item->isDir()) { + static::createDir($target . '/' . $iterator->getSubPathName()); + } else { + static::copy($item->getPathname(), $target . '/' . $iterator->getSubPathName()); + } + } + } else { + static::createDir(dirname($target)); + if (@stream_copy_to_stream(static::open($origin, 'rb'), static::open($target, 'wb')) === false) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to copy file '%s' to '%s'. %s", + self::normalizePath($origin), + self::normalizePath($target), + Helpers::getLastError(), + )); + } + } + } + + + /** + * Opens file and returns resource. + * @return resource + * @throws Nette\IOException on error occurred + */ + public static function open(string $path, string $mode) + { + $f = @fopen($path, $mode); // @ is escalated to exception + if (!$f) { + throw new Nette\IOException(sprintf( + "Unable to open file '%s'. %s", + self::normalizePath($path), + Helpers::getLastError(), + )); + } + return $f; + } + + + /** + * Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first. + * @throws Nette\IOException on error occurred + */ + public static function delete(string $path): void + { + if (is_file($path) || is_link($path)) { + $func = DIRECTORY_SEPARATOR === '\\' && is_dir($path) ? 'rmdir' : 'unlink'; + if (!@$func($path)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to delete '%s'. %s", + self::normalizePath($path), + Helpers::getLastError(), + )); + } + } elseif (is_dir($path)) { + foreach (new \FilesystemIterator($path) as $item) { + static::delete($item->getPathname()); + } + + if (!@rmdir($path)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to delete directory '%s'. %s", + self::normalizePath($path), + Helpers::getLastError(), + )); + } + } + } + + + /** + * Renames or moves a file or a directory. Overwrites existing files and directories by default. + * @throws Nette\IOException on error occurred + * @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists + */ + public static function rename(string $origin, string $target, bool $overwrite = true): void + { + if (!$overwrite && file_exists($target)) { + throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target))); + + } elseif (!file_exists($origin)) { + throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin))); + + } else { + static::createDir(dirname($target)); + if (realpath($origin) !== realpath($target)) { + static::delete($target); + } + + if (!@rename($origin, $target)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to rename file or directory '%s' to '%s'. %s", + self::normalizePath($origin), + self::normalizePath($target), + Helpers::getLastError(), + )); + } + } + } + + + /** + * Reads the content of a file. + * @throws Nette\IOException on error occurred + */ + public static function read(string $file): string + { + $content = @file_get_contents($file); // @ is escalated to exception + if ($content === false) { + throw new Nette\IOException(sprintf( + "Unable to read file '%s'. %s", + self::normalizePath($file), + Helpers::getLastError(), + )); + } + + return $content; + } + + + /** + * Reads the file content line by line. Because it reads continuously as we iterate over the lines, + * it is possible to read files larger than the available memory. + * @return \Generator + * @throws Nette\IOException on error occurred + */ + public static function readLines(string $file, bool $stripNewLines = true): \Generator + { + return (function ($f) use ($file, $stripNewLines) { + $counter = 0; + do { + $line = Callback::invokeSafe('fgets', [$f], fn($error) => throw new Nette\IOException(sprintf( + "Unable to read file '%s'. %s", + self::normalizePath($file), + $error, + ))); + if ($line === false) { + fclose($f); + break; + } + if ($stripNewLines) { + $line = rtrim($line, "\r\n"); + } + + yield $counter++ => $line; + + } while (true); + })(static::open($file, 'r')); + } + + + /** + * Writes the string to a file. + * @throws Nette\IOException on error occurred + */ + public static function write(string $file, string $content, ?int $mode = 0666): void + { + static::createDir(dirname($file)); + if (@file_put_contents($file, $content) === false) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to write file '%s'. %s", + self::normalizePath($file), + Helpers::getLastError(), + )); + } + + if ($mode !== null && !@chmod($file, $mode)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to chmod file '%s' to mode %s. %s", + self::normalizePath($file), + decoct($mode), + Helpers::getLastError(), + )); + } + } + + + /** + * Sets file permissions to `$fileMode` or directory permissions to `$dirMode`. + * Recursively traverses and sets permissions on the entire contents of the directory as well. + * @throws Nette\IOException on error occurred + */ + public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void + { + if (is_file($path)) { + if (!@chmod($path, $fileMode)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to chmod file '%s' to mode %s. %s", + self::normalizePath($path), + decoct($fileMode), + Helpers::getLastError(), + )); + } + } elseif (is_dir($path)) { + foreach (new \FilesystemIterator($path) as $item) { + static::makeWritable($item->getPathname(), $dirMode, $fileMode); + } + + if (!@chmod($path, $dirMode)) { // @ is escalated to exception + throw new Nette\IOException(sprintf( + "Unable to chmod directory '%s' to mode %s. %s", + self::normalizePath($path), + decoct($dirMode), + Helpers::getLastError(), + )); + } + } else { + throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($path))); + } + } + + + /** + * Determines if the path is absolute. + */ + public static function isAbsolute(string $path): bool + { + return (bool) preg_match('#([a-z]:)?[/\\\]|[a-z][a-z0-9+.-]*://#Ai', $path); + } + + + /** + * Normalizes `..` and `.` and directory separators in path. + */ + public static function normalizePath(string $path): string + { + $parts = $path === '' ? [] : preg_split('~[/\\\]+~', $path); + $res = []; + foreach ($parts as $part) { + if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') { + array_pop($res); + } elseif ($part !== '.') { + $res[] = $part; + } + } + + return $res === [''] + ? DIRECTORY_SEPARATOR + : implode(DIRECTORY_SEPARATOR, $res); + } + + + /** + * Joins all segments of the path and normalizes the result. + */ + public static function joinPaths(string ...$paths): string + { + return self::normalizePath(implode('/', $paths)); + } + + + /** + * Resolves a path against a base path. If the path is absolute, returns it directly, if it's relative, joins it with the base path. + */ + public static function resolvePath(string $basePath, string $path): string + { + return match (true) { + self::isAbsolute($path) => self::platformSlashes($path), + $path === '' => self::platformSlashes($basePath), + default => self::joinPaths($basePath, $path), + }; + } + + + /** + * Converts backslashes to slashes. + */ + public static function unixSlashes(string $path): string + { + return strtr($path, '\\', '/'); + } + + + /** + * Converts slashes to platform-specific directory separators. + */ + public static function platformSlashes(string $path): string + { + return DIRECTORY_SEPARATOR === '/' + ? strtr($path, '\\', '/') + : str_replace(':\\\\', '://', strtr($path, '/', '\\')); // protocol:// + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php new file mode 100644 index 00000000000..91617dadcac --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php @@ -0,0 +1,510 @@ +size('> 10kB') + * ->from('.') + * ->exclude('temp'); + * + * @implements \IteratorAggregate + */ +class Finder implements \IteratorAggregate +{ + use Nette\SmartObject; + + /** @var array */ + private array $find = []; + + /** @var string[] */ + private array $in = []; + + /** @var \Closure[] */ + private array $filters = []; + + /** @var \Closure[] */ + private array $descentFilters = []; + + /** @var array */ + private array $appends = []; + private bool $childFirst = false; + + /** @var ?callable */ + private $sort; + private int $maxDepth = -1; + private bool $ignoreUnreadableDirs = true; + + + /** + * Begins search for files and directories matching mask. + */ + public static function find(string|array $masks = ['*']): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + return (new static)->addMask($masks, 'dir')->addMask($masks, 'file'); + } + + + /** + * Begins search for files matching mask. + */ + public static function findFiles(string|array $masks = ['*']): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + return (new static)->addMask($masks, 'file'); + } + + + /** + * Begins search for directories matching mask. + */ + public static function findDirectories(string|array $masks = ['*']): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + return (new static)->addMask($masks, 'dir'); + } + + + /** + * Finds files matching the specified masks. + */ + public function files(string|array $masks = ['*']): static + { + return $this->addMask((array) $masks, 'file'); + } + + + /** + * Finds directories matching the specified masks. + */ + public function directories(string|array $masks = ['*']): static + { + return $this->addMask((array) $masks, 'dir'); + } + + + private function addMask(array $masks, string $mode): static + { + foreach ($masks as $mask) { + $mask = FileSystem::unixSlashes($mask); + if ($mode === 'dir') { + $mask = rtrim($mask, '/'); + } + if ($mask === '' || ($mode === 'file' && str_ends_with($mask, '/'))) { + throw new Nette\InvalidArgumentException("Invalid mask '$mask'"); + } + if (str_starts_with($mask, '**/')) { + $mask = substr($mask, 3); + } + $this->find[] = [$mask, $mode]; + } + return $this; + } + + + /** + * Searches in the given directories. Wildcards are allowed. + */ + public function in(string|array $paths): static + { + $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic + $this->addLocation($paths, ''); + return $this; + } + + + /** + * Searches recursively from the given directories. Wildcards are allowed. + */ + public function from(string|array $paths): static + { + $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic + $this->addLocation($paths, '/**'); + return $this; + } + + + private function addLocation(array $paths, string $ext): void + { + foreach ($paths as $path) { + if ($path === '') { + throw new Nette\InvalidArgumentException("Invalid directory '$path'"); + } + $path = rtrim(FileSystem::unixSlashes($path), '/'); + $this->in[] = $path . $ext; + } + } + + + /** + * Lists directory's contents before the directory itself. By default, this is disabled. + */ + public function childFirst(bool $state = true): static + { + $this->childFirst = $state; + return $this; + } + + + /** + * Ignores unreadable directories. By default, this is enabled. + */ + public function ignoreUnreadableDirs(bool $state = true): static + { + $this->ignoreUnreadableDirs = $state; + return $this; + } + + + /** + * Set a compare function for sorting directory entries. The function will be called to sort entries from the same directory. + * @param callable(FileInfo, FileInfo): int $callback + */ + public function sortBy(callable $callback): static + { + $this->sort = $callback; + return $this; + } + + + /** + * Sorts files in each directory naturally by name. + */ + public function sortByName(): static + { + $this->sort = fn(FileInfo $a, FileInfo $b): int => strnatcmp($a->getBasename(), $b->getBasename()); + return $this; + } + + + /** + * Adds the specified paths or appends a new finder that returns. + */ + public function append(string|array|null $paths = null): static + { + if ($paths === null) { + return $this->appends[] = new static; + } + + $this->appends = array_merge($this->appends, (array) $paths); + return $this; + } + + + /********************* filtering ****************d*g**/ + + + /** + * Skips entries that matches the given masks relative to the ones defined with the in() or from() methods. + */ + public function exclude(string|array $masks): static + { + $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic + foreach ($masks as $mask) { + $mask = FileSystem::unixSlashes($mask); + if (!preg_match('~^/?(\*\*/)?(.+)(/\*\*|/\*|/|)$~D', $mask, $m)) { + throw new Nette\InvalidArgumentException("Invalid mask '$mask'"); + } + $end = $m[3]; + $re = $this->buildPattern($m[2]); + $filter = fn(FileInfo $file): bool => ($end && !$file->isDir()) + || !preg_match($re, FileSystem::unixSlashes($file->getRelativePathname())); + + $this->descentFilter($filter); + if ($end !== '/*') { + $this->filter($filter); + } + } + + return $this; + } + + + /** + * Yields only entries which satisfy the given filter. + * @param callable(FileInfo): bool $callback + */ + public function filter(callable $callback): static + { + $this->filters[] = \Closure::fromCallable($callback); + return $this; + } + + + /** + * It descends only to directories that match the specified filter. + * @param callable(FileInfo): bool $callback + */ + public function descentFilter(callable $callback): static + { + $this->descentFilters[] = \Closure::fromCallable($callback); + return $this; + } + + + /** + * Sets the maximum depth of entries. + */ + public function limitDepth(?int $depth): static + { + $this->maxDepth = $depth ?? -1; + return $this; + } + + + /** + * Restricts the search by size. $operator accepts "[operator] [size] [unit]" example: >=10kB + */ + public function size(string $operator, ?int $size = null): static + { + if (func_num_args() === 1) { // in $operator is predicate + if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?((?:\d*\.)?\d+)\s*(K|M|G|)B?$#Di', $operator, $matches)) { + throw new Nette\InvalidArgumentException('Invalid size predicate format.'); + } + + [, $operator, $size, $unit] = $matches; + $units = ['' => 1, 'k' => 1e3, 'm' => 1e6, 'g' => 1e9]; + $size *= $units[strtolower($unit)]; + $operator = $operator ?: '='; + } + + return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getSize(), $operator, $size)); + } + + + /** + * Restricts the search by modified time. $operator accepts "[operator] [date]" example: >1978-01-23 + */ + public function date(string $operator, string|int|\DateTimeInterface|null $date = null): static + { + if (func_num_args() === 1) { // in $operator is predicate + if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?(.+)$#Di', $operator, $matches)) { + throw new Nette\InvalidArgumentException('Invalid date predicate format.'); + } + + [, $operator, $date] = $matches; + $operator = $operator ?: '='; + } + + $date = DateTime::from($date)->format('U'); + return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getMTime(), $operator, $date)); + } + + + /********************* iterator generator ****************d*g**/ + + + /** + * Returns an array with all found files and directories. + * @return list + */ + public function collect(): array + { + return iterator_to_array($this->getIterator(), preserve_keys: false); + } + + + /** @return \Generator */ + public function getIterator(): \Generator + { + $plan = $this->buildPlan(); + foreach ($plan as $dir => $searches) { + yield from $this->traverseDir($dir, $searches); + } + + foreach ($this->appends as $item) { + if ($item instanceof self) { + yield from $item->getIterator(); + } else { + $item = FileSystem::platformSlashes($item); + yield $item => new FileInfo($item); + } + } + } + + + /** + * @param array $searches + * @param string[] $subdirs + * @return \Generator + */ + private function traverseDir(string $dir, array $searches, array $subdirs = []): \Generator + { + if ($this->maxDepth >= 0 && count($subdirs) > $this->maxDepth) { + return; + } elseif (!is_dir($dir)) { + throw new Nette\InvalidStateException(sprintf("Directory '%s' does not exist.", rtrim($dir, '/\\'))); + } + + try { + $pathNames = new \FilesystemIterator($dir, \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::UNIX_PATHS); + } catch (\UnexpectedValueException $e) { + if ($this->ignoreUnreadableDirs) { + return; + } else { + throw new Nette\InvalidStateException($e->getMessage()); + } + } + + $files = $this->convertToFiles($pathNames, implode('/', $subdirs), FileSystem::isAbsolute($dir)); + + if ($this->sort) { + $files = iterator_to_array($files); + usort($files, $this->sort); + } + + foreach ($files as $file) { + $pathName = $file->getPathname(); + $cache = $subSearch = []; + + if ($file->isDir()) { + foreach ($searches as $search) { + if ($search->recursive && $this->proveFilters($this->descentFilters, $file, $cache)) { + $subSearch[] = $search; + } + } + } + + if ($this->childFirst && $subSearch) { + yield from $this->traverseDir($pathName, $subSearch, array_merge($subdirs, [$file->getBasename()])); + } + + $relativePathname = FileSystem::unixSlashes($file->getRelativePathname()); + foreach ($searches as $search) { + if ( + $file->{'is' . $search->mode}() + && preg_match($search->pattern, $relativePathname) + && $this->proveFilters($this->filters, $file, $cache) + ) { + yield $pathName => $file; + break; + } + } + + if (!$this->childFirst && $subSearch) { + yield from $this->traverseDir($pathName, $subSearch, array_merge($subdirs, [$file->getBasename()])); + } + } + } + + + private function convertToFiles(iterable $pathNames, string $relativePath, bool $absolute): \Generator + { + foreach ($pathNames as $pathName) { + if (!$absolute) { + $pathName = preg_replace('~\.?/~A', '', $pathName); + } + $pathName = FileSystem::platformSlashes($pathName); + yield new FileInfo($pathName, $relativePath); + } + } + + + private function proveFilters(array $filters, FileInfo $file, array &$cache): bool + { + foreach ($filters as $filter) { + $res = &$cache[spl_object_id($filter)]; + $res ??= $filter($file); + if (!$res) { + return false; + } + } + + return true; + } + + + /** @return array> */ + private function buildPlan(): array + { + $plan = $dirCache = []; + foreach ($this->find as [$mask, $mode]) { + $splits = []; + if (FileSystem::isAbsolute($mask)) { + if ($this->in) { + throw new Nette\InvalidStateException("You cannot combine the absolute path in the mask '$mask' and the directory to search '{$this->in[0]}'."); + } + $splits[] = self::splitRecursivePart($mask); + } else { + foreach ($this->in ?: ['.'] as $in) { + $in = strtr($in, ['[' => '[[]', ']' => '[]]']); // in path, do not treat [ and ] as a pattern by glob() + $splits[] = self::splitRecursivePart($in . '/' . $mask); + } + } + + foreach ($splits as [$base, $rest, $recursive]) { + $base = $base === '' ? '.' : $base; + $dirs = $dirCache[$base] ??= strpbrk($base, '*?[') + ? glob($base, GLOB_NOSORT | GLOB_ONLYDIR | GLOB_NOESCAPE) + : [strtr($base, ['[[]' => '[', '[]]' => ']'])]; // unescape [ and ] + + if (!$dirs) { + throw new Nette\InvalidStateException(sprintf("Directory '%s' does not exist.", rtrim($base, '/\\'))); + } + + $search = (object) ['pattern' => $this->buildPattern($rest), 'mode' => $mode, 'recursive' => $recursive]; + foreach ($dirs as $dir) { + $plan[$dir][] = $search; + } + } + } + + return $plan; + } + + + /** + * Since glob() does not know ** wildcard, we divide the path into a part for glob and a part for manual traversal. + */ + private static function splitRecursivePart(string $path): array + { + $a = strrpos($path, '/'); + $parts = preg_split('~(?<=^|/)\*\*($|/)~', substr($path, 0, $a + 1), 2); + return isset($parts[1]) + ? [$parts[0], $parts[1] . substr($path, $a + 1), true] + : [$parts[0], substr($path, $a + 1), false]; + } + + + /** + * Converts wildcards to regular expression. + */ + private function buildPattern(string $mask): string + { + if ($mask === '*') { + return '##'; + } elseif (str_starts_with($mask, './')) { + $anchor = '^'; + $mask = substr($mask, 2); + } else { + $anchor = '(?:^|/)'; + } + + $pattern = strtr( + preg_quote($mask, '#'), + [ + '\*\*/' => '(.+/)?', + '\*' => '[^/]*', + '\?' => '[^/]', + '\[\!' => '[^', + '\[' => '[', + '\]' => ']', + '\-' => '-', + ], + ); + return '#' . $anchor . $pattern . '$#D' . (Helpers::IsWindows ? 'i' : ''); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php new file mode 100644 index 00000000000..cc2781d7113 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php @@ -0,0 +1,107 @@ + $b it returns 1 + * @throws \LogicException if one of parameters is NAN + */ + public static function compare(float $a, float $b): int + { + if (is_nan($a) || is_nan($b)) { + throw new \LogicException('Trying to compare NAN'); + + } elseif (!is_finite($a) && !is_finite($b) && $a === $b) { + return 0; + } + + $diff = abs($a - $b); + if (($diff < self::Epsilon || ($diff / max(abs($a), abs($b)) < self::Epsilon))) { + return 0; + } + + return $a < $b ? -1 : 1; + } + + + /** + * Returns true if $a = $b + * @throws \LogicException if one of parameters is NAN + */ + public static function areEqual(float $a, float $b): bool + { + return self::compare($a, $b) === 0; + } + + + /** + * Returns true if $a < $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isLessThan(float $a, float $b): bool + { + return self::compare($a, $b) < 0; + } + + + /** + * Returns true if $a <= $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isLessThanOrEqualTo(float $a, float $b): bool + { + return self::compare($a, $b) <= 0; + } + + + /** + * Returns true if $a > $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isGreaterThan(float $a, float $b): bool + { + return self::compare($a, $b) > 0; + } + + + /** + * Returns true if $a >= $b + * @throws \LogicException if one of parameters is NAN + */ + public static function isGreaterThanOrEqualTo(float $a, float $b): bool + { + return self::compare($a, $b) >= 0; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php new file mode 100644 index 00000000000..21efb2ac715 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php @@ -0,0 +1,107 @@ + $max) { + throw new Nette\InvalidArgumentException("Minimum ($min) is not less than maximum ($max)."); + } + + return min(max($value, $min), $max); + } + + + /** + * Looks for a string from possibilities that is most similar to value, but not the same (for 8-bit encoding). + * @param string[] $possibilities + */ + public static function getSuggestion(array $possibilities, string $value): ?string + { + $best = null; + $min = (strlen($value) / 4 + 1) * 10 + .1; + foreach (array_unique($possibilities) as $item) { + if ($item !== $value && ($len = levenshtein($item, $value, 10, 11, 10)) < $min) { + $min = $len; + $best = $item; + } + } + + return $best; + } + + + /** + * Compares two values in the same way that PHP does. Recognizes operators: >, >=, <, <=, =, ==, ===, !=, !==, <> + */ + public static function compare(mixed $left, string $operator, mixed $right): bool + { + return match ($operator) { + '>' => $left > $right, + '>=' => $left >= $right, + '<' => $left < $right, + '<=' => $left <= $right, + '=', '==' => $left == $right, + '===' => $left === $right, + '!=', '<>' => $left != $right, + '!==' => $left !== $right, + default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"), + }; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php new file mode 100644 index 00000000000..fc0e3ef2a9a --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php @@ -0,0 +1,839 @@ + element's attributes */ + public $attrs = []; + + /** void elements */ + public static $emptyElements = [ + 'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1, + 'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1, + 'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1, + ]; + + /** @var array nodes */ + protected $children = []; + + /** element's name */ + private string $name = ''; + + private bool $isEmpty = false; + + + /** + * Constructs new HTML element. + * @param array|string $attrs element's attributes or plain text content + */ + public static function el(?string $name = null, array|string|null $attrs = null): static + { + $el = new static; + $parts = explode(' ', (string) $name, 2); + $el->setName($parts[0]); + + if (is_array($attrs)) { + $el->attrs = $attrs; + + } elseif ($attrs !== null) { + $el->setText($attrs); + } + + if (isset($parts[1])) { + foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\2|\s))?#i') as $m) { + $el->attrs[$m[1]] = $m[3] ?? true; + } + } + + return $el; + } + + + /** + * Returns an object representing HTML text. + */ + public static function fromHtml(string $html): static + { + return (new static)->setHtml($html); + } + + + /** + * Returns an object representing plain text. + */ + public static function fromText(string $text): static + { + return (new static)->setText($text); + } + + + /** + * Converts to HTML. + */ + final public function toHtml(): string + { + return $this->render(); + } + + + /** + * Converts to plain text. + */ + final public function toText(): string + { + return $this->getText(); + } + + + /** + * Converts given HTML code to plain text. + */ + public static function htmlToText(string $html): string + { + return html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + + + /** + * Changes element's name. + */ + final public function setName(string $name, ?bool $isEmpty = null): static + { + $this->name = $name; + $this->isEmpty = $isEmpty ?? isset(static::$emptyElements[$name]); + return $this; + } + + + /** + * Returns element's name. + */ + final public function getName(): string + { + return $this->name; + } + + + /** + * Is element empty? + */ + final public function isEmpty(): bool + { + return $this->isEmpty; + } + + + /** + * Sets multiple attributes. + */ + public function addAttributes(array $attrs): static + { + $this->attrs = array_merge($this->attrs, $attrs); + return $this; + } + + + /** + * Appends value to element's attribute. + */ + public function appendAttribute(string $name, mixed $value, mixed $option = true): static + { + if (is_array($value)) { + $prev = isset($this->attrs[$name]) ? (array) $this->attrs[$name] : []; + $this->attrs[$name] = $value + $prev; + + } elseif ((string) $value === '') { + $tmp = &$this->attrs[$name]; // appending empty value? -> ignore, but ensure it exists + + } elseif (!isset($this->attrs[$name]) || is_array($this->attrs[$name])) { // needs array + $this->attrs[$name][$value] = $option; + + } else { + $this->attrs[$name] = [$this->attrs[$name] => true, $value => $option]; + } + + return $this; + } + + + /** + * Sets element's attribute. + */ + public function setAttribute(string $name, mixed $value): static + { + $this->attrs[$name] = $value; + return $this; + } + + + /** + * Returns element's attribute. + */ + public function getAttribute(string $name): mixed + { + return $this->attrs[$name] ?? null; + } + + + /** + * Unsets element's attribute. + */ + public function removeAttribute(string $name): static + { + unset($this->attrs[$name]); + return $this; + } + + + /** + * Unsets element's attributes. + */ + public function removeAttributes(array $attributes): static + { + foreach ($attributes as $name) { + unset($this->attrs[$name]); + } + + return $this; + } + + + /** + * Overloaded setter for element's attribute. + */ + final public function __set(string $name, mixed $value): void + { + $this->attrs[$name] = $value; + } + + + /** + * Overloaded getter for element's attribute. + */ + final public function &__get(string $name): mixed + { + return $this->attrs[$name]; + } + + + /** + * Overloaded tester for element's attribute. + */ + final public function __isset(string $name): bool + { + return isset($this->attrs[$name]); + } + + + /** + * Overloaded unsetter for element's attribute. + */ + final public function __unset(string $name): void + { + unset($this->attrs[$name]); + } + + + /** + * Overloaded setter for element's attribute. + */ + final public function __call(string $m, array $args): mixed + { + $p = substr($m, 0, 3); + if ($p === 'get' || $p === 'set' || $p === 'add') { + $m = substr($m, 3); + $m[0] = $m[0] | "\x20"; + if ($p === 'get') { + return $this->attrs[$m] ?? null; + + } elseif ($p === 'add') { + $args[] = true; + } + } + + if (count($args) === 0) { // invalid + + } elseif (count($args) === 1) { // set + $this->attrs[$m] = $args[0]; + + } else { // add + $this->appendAttribute($m, $args[0], $args[1]); + } + + return $this; + } + + + /** + * Special setter for element's attribute. + */ + final public function href(string $path, array $query = []): static + { + if ($query) { + $query = http_build_query($query, '', '&'); + if ($query !== '') { + $path .= '?' . $query; + } + } + + $this->attrs['href'] = $path; + return $this; + } + + + /** + * Setter for data-* attributes. Booleans are converted to 'true' resp. 'false'. + */ + public function data(string $name, mixed $value = null): static + { + if (func_num_args() === 1) { + $this->attrs['data'] = $name; + } else { + $this->attrs["data-$name"] = is_bool($value) + ? json_encode($value) + : $value; + } + + return $this; + } + + + /** + * Sets element's HTML content. + */ + final public function setHtml(mixed $html): static + { + $this->children = [(string) $html]; + return $this; + } + + + /** + * Returns element's HTML content. + */ + final public function getHtml(): string + { + return implode('', $this->children); + } + + + /** + * Sets element's textual content. + */ + final public function setText(mixed $text): static + { + if (!$text instanceof HtmlStringable) { + $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); + } + + $this->children = [(string) $text]; + return $this; + } + + + /** + * Returns element's textual content. + */ + final public function getText(): string + { + return self::htmlToText($this->getHtml()); + } + + + /** + * Adds new element's child. + */ + final public function addHtml(mixed $child): static + { + return $this->insert(null, $child); + } + + + /** + * Appends plain-text string to element content. + */ + public function addText(mixed $text): static + { + if (!$text instanceof HtmlStringable) { + $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); + } + + return $this->insert(null, $text); + } + + + /** + * Creates and adds a new Html child. + */ + final public function create(string $name, array|string|null $attrs = null): static + { + $this->insert(null, $child = static::el($name, $attrs)); + return $child; + } + + + /** + * Inserts child node. + */ + public function insert(?int $index, HtmlStringable|string $child, bool $replace = false): static + { + $child = $child instanceof self ? $child : (string) $child; + if ($index === null) { // append + $this->children[] = $child; + + } else { // insert or replace + array_splice($this->children, $index, $replace ? 1 : 0, [$child]); + } + + return $this; + } + + + /** + * Inserts (replaces) child node (\ArrayAccess implementation). + * @param int|null $index position or null for appending + * @param Html|string $child Html node or raw HTML string + */ + final public function offsetSet($index, $child): void + { + $this->insert($index, $child, replace: true); + } + + + /** + * Returns child node (\ArrayAccess implementation). + * @param int $index + */ + final public function offsetGet($index): HtmlStringable|string + { + return $this->children[$index]; + } + + + /** + * Exists child node? (\ArrayAccess implementation). + * @param int $index + */ + final public function offsetExists($index): bool + { + return isset($this->children[$index]); + } + + + /** + * Removes child node (\ArrayAccess implementation). + * @param int $index + */ + public function offsetUnset($index): void + { + if (isset($this->children[$index])) { + array_splice($this->children, $index, 1); + } + } + + + /** + * Returns children count. + */ + final public function count(): int + { + return count($this->children); + } + + + /** + * Removes all children. + */ + public function removeChildren(): void + { + $this->children = []; + } + + + /** + * Iterates over elements. + * @return \ArrayIterator + */ + final public function getIterator(): \ArrayIterator + { + return new \ArrayIterator($this->children); + } + + + /** + * Returns all children. + */ + final public function getChildren(): array + { + return $this->children; + } + + + /** + * Renders element's start tag, content and end tag. + */ + final public function render(?int $indent = null): string + { + $s = $this->startTag(); + + if (!$this->isEmpty) { + // add content + if ($indent !== null) { + $indent++; + } + + foreach ($this->children as $child) { + if ($child instanceof self) { + $s .= $child->render($indent); + } else { + $s .= $child; + } + } + + // add end tag + $s .= $this->endTag(); + } + + if ($indent !== null) { + return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2)); + } + + return $s; + } + + + final public function __toString(): string + { + return $this->render(); + } + + + /** + * Returns element's start tag. + */ + final public function startTag(): string + { + return $this->name + ? '<' . $this->name . $this->attributes() . '>' + : ''; + } + + + /** + * Returns element's end tag. + */ + final public function endTag(): string + { + return $this->name && !$this->isEmpty ? 'name . '>' : ''; + } + + + /** + * Returns element's attributes. + * @internal + */ + final public function attributes(): string + { + if (!is_array($this->attrs)) { + return ''; + } + + $s = ''; + $attrs = $this->attrs; + foreach ($attrs as $key => $value) { + if ($value === null || $value === false) { + continue; + + } elseif ($value === true) { + $s .= ' ' . $key; + + continue; + + } elseif (is_array($value)) { + if (strncmp($key, 'data-', 5) === 0) { + $value = Json::encode($value); + + } else { + $tmp = null; + foreach ($value as $k => $v) { + if ($v != null) { // intentionally ==, skip nulls & empty string + // composite 'style' vs. 'others' + $tmp[] = $v === true + ? $k + : (is_string($k) ? $k . ':' . $v : $v); + } + } + + if ($tmp === null) { + continue; + } + + $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp); + } + } elseif (is_float($value)) { + $value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); + + } else { + $value = (string) $value; + } + + $q = str_contains($value, '"') ? "'" : '"'; + $s .= ' ' . $key . '=' . $q + . str_replace( + ['&', $q, '<'], + ['&', $q === '"' ? '"' : ''', '<'], + $value, + ) + . (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '') + . $q; + } + + $s = str_replace('@', '@', $s); + return $s; + } + + + /** + * Clones all children too. + */ + public function __clone() + { + foreach ($this->children as $key => $value) { + if (is_object($value)) { + $this->children[$key] = clone $value; + } + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php new file mode 100644 index 00000000000..19ce7b4f492 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php @@ -0,0 +1,816 @@ + + * $image = Image::fromFile('nette.jpg'); + * $image->resize(150, 100); + * $image->sharpen(); + * $image->send(); + * + * + * @method Image affine(array $affine, ?array $clip = null) + * @method void alphaBlending(bool $enable) + * @method void antialias(bool $enable) + * @method void arc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color) + * @method int colorAllocate(int $red, int $green, int $blue) + * @method int colorAllocateAlpha(int $red, int $green, int $blue, int $alpha) + * @method int colorAt(int $x, int $y) + * @method int colorClosest(int $red, int $green, int $blue) + * @method int colorClosestAlpha(int $red, int $green, int $blue, int $alpha) + * @method int colorClosestHWB(int $red, int $green, int $blue) + * @method void colorDeallocate(int $color) + * @method int colorExact(int $red, int $green, int $blue) + * @method int colorExactAlpha(int $red, int $green, int $blue, int $alpha) + * @method void colorMatch(Image $image2) + * @method int colorResolve(int $red, int $green, int $blue) + * @method int colorResolveAlpha(int $red, int $green, int $blue, int $alpha) + * @method void colorSet(int $index, int $red, int $green, int $blue, int $alpha = 0) + * @method array colorsForIndex(int $color) + * @method int colorsTotal() + * @method int colorTransparent(?int $color = null) + * @method void convolution(array $matrix, float $div, float $offset) + * @method void copy(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH) + * @method void copyMerge(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct) + * @method void copyMergeGray(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct) + * @method void copyResampled(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH) + * @method void copyResized(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH) + * @method Image cropAuto(int $mode = IMG_CROP_DEFAULT, float $threshold = .5, ?ImageColor $color = null) + * @method void ellipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color) + * @method void fill(int $x, int $y, ImageColor $color) + * @method void filledArc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color, int $style) + * @method void filledEllipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color) + * @method void filledPolygon(array $points, ImageColor $color) + * @method void filledRectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color) + * @method void fillToBorder(int $x, int $y, ImageColor $borderColor, ImageColor $color) + * @method void filter(int $filter, ...$args) + * @method void flip(int $mode) + * @method array ftText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontFile, string $text, array $options = []) + * @method void gammaCorrect(float $inputgamma, float $outputgamma) + * @method array getClip() + * @method int getInterpolation() + * @method int interlace(?bool $enable = null) + * @method bool isTrueColor() + * @method void layerEffect(int $effect) + * @method void line(int $x1, int $y1, int $x2, int $y2, ImageColor $color) + * @method void openPolygon(array $points, ImageColor $color) + * @method void paletteCopy(Image $source) + * @method void paletteToTrueColor() + * @method void polygon(array $points, ImageColor $color) + * @method void rectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color) + * @method mixed resolution(?int $resolutionX = null, ?int $resolutionY = null) + * @method Image rotate(float $angle, ImageColor $backgroundColor) + * @method void saveAlpha(bool $enable) + * @method Image scale(int $newWidth, int $newHeight = -1, int $mode = IMG_BILINEAR_FIXED) + * @method void setBrush(Image $brush) + * @method void setClip(int $x1, int $y1, int $x2, int $y2) + * @method void setInterpolation(int $method = IMG_BILINEAR_FIXED) + * @method void setPixel(int $x, int $y, ImageColor $color) + * @method void setStyle(array $style) + * @method void setThickness(int $thickness) + * @method void setTile(Image $tile) + * @method void trueColorToPalette(bool $dither, int $ncolors) + * @method array ttfText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontfile, string $text, array $options = []) + * @property-read positive-int $width + * @property-read positive-int $height + * @property-read \GdImage $imageResource + */ +class Image +{ + use Nette\SmartObject; + + /** Prevent from getting resized to a bigger size than the original */ + public const ShrinkOnly = 0b0001; + + /** Resizes to a specified width and height without keeping aspect ratio */ + public const Stretch = 0b0010; + + /** Resizes to fit into a specified width and height and preserves aspect ratio */ + public const OrSmaller = 0b0000; + + /** Resizes while bounding the smaller dimension to the specified width or height and preserves aspect ratio */ + public const OrBigger = 0b0100; + + /** Resizes to the smallest possible size to completely cover specified width and height and reserves aspect ratio */ + public const Cover = 0b1000; + + /** @deprecated use Image::ShrinkOnly */ + public const SHRINK_ONLY = self::ShrinkOnly; + + /** @deprecated use Image::Stretch */ + public const STRETCH = self::Stretch; + + /** @deprecated use Image::OrSmaller */ + public const FIT = self::OrSmaller; + + /** @deprecated use Image::OrBigger */ + public const FILL = self::OrBigger; + + /** @deprecated use Image::Cover */ + public const EXACT = self::Cover; + + /** @deprecated use Image::EmptyGIF */ + public const EMPTY_GIF = self::EmptyGIF; + + /** image types */ + public const + JPEG = ImageType::JPEG, + PNG = ImageType::PNG, + GIF = ImageType::GIF, + WEBP = ImageType::WEBP, + AVIF = ImageType::AVIF, + BMP = ImageType::BMP; + + public const EmptyGIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;"; + + private const Formats = [ImageType::JPEG => 'jpeg', ImageType::PNG => 'png', ImageType::GIF => 'gif', ImageType::WEBP => 'webp', ImageType::AVIF => 'avif', ImageType::BMP => 'bmp']; + + private \GdImage $image; + + + /** + * Returns RGB color (0..255) and transparency (0..127). + * @deprecated use ImageColor::rgb() + */ + public static function rgb(int $red, int $green, int $blue, int $transparency = 0): array + { + return [ + 'red' => max(0, min(255, $red)), + 'green' => max(0, min(255, $green)), + 'blue' => max(0, min(255, $blue)), + 'alpha' => max(0, min(127, $transparency)), + ]; + } + + + /** + * Reads an image from a file and returns its type in $type. + * @throws Nette\NotSupportedException if gd extension is not loaded + * @throws UnknownImageFileException if file not found or file type is not known + */ + public static function fromFile(string $file, ?int &$type = null): static + { + self::ensureExtension(); + $type = self::detectTypeFromFile($file); + if (!$type) { + throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found."); + } + + return self::invokeSafe('imagecreatefrom' . self::Formats[$type], $file, "Unable to open file '$file'.", __METHOD__); + } + + + /** + * Reads an image from a string and returns its type in $type. + * @throws Nette\NotSupportedException if gd extension is not loaded + * @throws ImageException + */ + public static function fromString(string $s, ?int &$type = null): static + { + self::ensureExtension(); + $type = self::detectTypeFromString($s); + if (!$type) { + throw new UnknownImageFileException('Unknown type of image.'); + } + + return self::invokeSafe('imagecreatefromstring', $s, 'Unable to open image from string.', __METHOD__); + } + + + private static function invokeSafe(string $func, string $arg, string $message, string $callee): static + { + $errors = []; + $res = Callback::invokeSafe($func, [$arg], function (string $message) use (&$errors): void { + $errors[] = $message; + }); + + if (!$res) { + throw new ImageException($message . ' Errors: ' . implode(', ', $errors)); + } elseif ($errors) { + trigger_error($callee . '(): ' . implode(', ', $errors), E_USER_WARNING); + } + + return new static($res); + } + + + /** + * Creates a new true color image of the given dimensions. The default color is black. + * @param positive-int $width + * @param positive-int $height + * @throws Nette\NotSupportedException if gd extension is not loaded + */ + public static function fromBlank(int $width, int $height, ImageColor|array|null $color = null): static + { + self::ensureExtension(); + if ($width < 1 || $height < 1) { + throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.'); + } + + $image = new static(imagecreatetruecolor($width, $height)); + if ($color) { + $image->alphablending(false); + $image->filledrectangle(0, 0, $width - 1, $height - 1, $color); + $image->alphablending(true); + } + + return $image; + } + + + /** + * Returns the type of image from file. + * @return ImageType::*|null + */ + public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int + { + [$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error + return isset(self::Formats[$type]) ? $type : null; + } + + + /** + * Returns the type of image from string. + * @return ImageType::*|null + */ + public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int + { + [$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error + return isset(self::Formats[$type]) ? $type : null; + } + + + /** + * Returns the file extension for the given image type. + * @param ImageType::* $type + * @return value-of + */ + public static function typeToExtension(int $type): string + { + if (!isset(self::Formats[$type])) { + throw new Nette\InvalidArgumentException("Unsupported image type '$type'."); + } + + return self::Formats[$type]; + } + + + /** + * Returns the image type for given file extension. + * @return ImageType::* + */ + public static function extensionToType(string $extension): int + { + $extensions = array_flip(self::Formats) + ['jpg' => ImageType::JPEG]; + $extension = strtolower($extension); + if (!isset($extensions[$extension])) { + throw new Nette\InvalidArgumentException("Unsupported file extension '$extension'."); + } + + return $extensions[$extension]; + } + + + /** + * Returns the mime type for the given image type. + * @param ImageType::* $type + */ + public static function typeToMimeType(int $type): string + { + return 'image/' . self::typeToExtension($type); + } + + + /** + * @param ImageType::* $type + */ + public static function isTypeSupported(int $type): bool + { + self::ensureExtension(); + return (bool) (imagetypes() & match ($type) { + ImageType::JPEG => IMG_JPG, + ImageType::PNG => IMG_PNG, + ImageType::GIF => IMG_GIF, + ImageType::WEBP => IMG_WEBP, + ImageType::AVIF => 256, // IMG_AVIF, + ImageType::BMP => IMG_BMP, + default => 0, + }); + } + + + /** @return ImageType[] */ + public static function getSupportedTypes(): array + { + self::ensureExtension(); + $flag = imagetypes(); + return array_filter([ + $flag & IMG_GIF ? ImageType::GIF : null, + $flag & IMG_JPG ? ImageType::JPEG : null, + $flag & IMG_PNG ? ImageType::PNG : null, + $flag & IMG_WEBP ? ImageType::WEBP : null, + $flag & 256 ? ImageType::AVIF : null, // IMG_AVIF + $flag & IMG_BMP ? ImageType::BMP : null, + ]); + } + + + /** + * Wraps GD image. + */ + public function __construct(\GdImage $image) + { + $this->setImageResource($image); + imagesavealpha($image, true); + } + + + /** + * Returns image width. + * @return positive-int + */ + public function getWidth(): int + { + return imagesx($this->image); + } + + + /** + * Returns image height. + * @return positive-int + */ + public function getHeight(): int + { + return imagesy($this->image); + } + + + /** + * Sets image resource. + */ + protected function setImageResource(\GdImage $image): static + { + $this->image = $image; + return $this; + } + + + /** + * Returns image GD resource. + */ + public function getImageResource(): \GdImage + { + return $this->image; + } + + + /** + * Scales an image. Width and height accept pixels or percent. + * @param int-mask-of $mode + */ + public function resize(int|string|null $width, int|string|null $height, int $mode = self::OrSmaller): static + { + if ($mode & self::Cover) { + return $this->resize($width, $height, self::OrBigger)->crop('50%', '50%', $width, $height); + } + + [$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $mode); + + if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize + $newImage = static::fromBlank($newWidth, $newHeight, ImageColor::rgb(0, 0, 0, 0))->getImageResource(); + imagecopyresampled( + $newImage, + $this->image, + 0, + 0, + 0, + 0, + $newWidth, + $newHeight, + $this->getWidth(), + $this->getHeight(), + ); + $this->image = $newImage; + } + + if ($width < 0 || $height < 0) { + imageflip($this->image, $width < 0 ? ($height < 0 ? IMG_FLIP_BOTH : IMG_FLIP_HORIZONTAL) : IMG_FLIP_VERTICAL); + } + + return $this; + } + + + /** + * Calculates dimensions of resized image. Width and height accept pixels or percent. + * @param int-mask-of $mode + */ + public static function calculateSize( + int $srcWidth, + int $srcHeight, + $newWidth, + $newHeight, + int $mode = self::OrSmaller, + ): array + { + if ($newWidth === null) { + } elseif (self::isPercent($newWidth)) { + $newWidth = (int) round($srcWidth / 100 * abs($newWidth)); + $percents = true; + } else { + $newWidth = abs($newWidth); + } + + if ($newHeight === null) { + } elseif (self::isPercent($newHeight)) { + $newHeight = (int) round($srcHeight / 100 * abs($newHeight)); + $mode |= empty($percents) ? 0 : self::Stretch; + } else { + $newHeight = abs($newHeight); + } + + if ($mode & self::Stretch) { // non-proportional + if (!$newWidth || !$newHeight) { + throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.'); + } + + if ($mode & self::ShrinkOnly) { + $newWidth = min($srcWidth, $newWidth); + $newHeight = min($srcHeight, $newHeight); + } + } else { // proportional + if (!$newWidth && !$newHeight) { + throw new Nette\InvalidArgumentException('At least width or height must be specified.'); + } + + $scale = []; + if ($newWidth > 0) { // fit width + $scale[] = $newWidth / $srcWidth; + } + + if ($newHeight > 0) { // fit height + $scale[] = $newHeight / $srcHeight; + } + + if ($mode & self::OrBigger) { + $scale = [max($scale)]; + } + + if ($mode & self::ShrinkOnly) { + $scale[] = 1; + } + + $scale = min($scale); + $newWidth = (int) round($srcWidth * $scale); + $newHeight = (int) round($srcHeight * $scale); + } + + return [max($newWidth, 1), max($newHeight, 1)]; + } + + + /** + * Crops image. Arguments accepts pixels or percent. + */ + public function crop(int|string $left, int|string $top, int|string $width, int|string $height): static + { + [$r['x'], $r['y'], $r['width'], $r['height']] + = static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height); + if (gd_info()['GD Version'] === 'bundled (2.1.0 compatible)') { + $this->image = imagecrop($this->image, $r); + imagesavealpha($this->image, true); + } else { + $newImage = static::fromBlank($r['width'], $r['height'], ImageColor::rgb(0, 0, 0, 0))->getImageResource(); + imagecopy($newImage, $this->image, 0, 0, $r['x'], $r['y'], $r['width'], $r['height']); + $this->image = $newImage; + } + + return $this; + } + + + /** + * Calculates dimensions of cutout in image. Arguments accepts pixels or percent. + */ + public static function calculateCutout( + int $srcWidth, + int $srcHeight, + int|string $left, + int|string $top, + int|string $newWidth, + int|string $newHeight, + ): array + { + if (self::isPercent($newWidth)) { + $newWidth = (int) round($srcWidth / 100 * $newWidth); + } + + if (self::isPercent($newHeight)) { + $newHeight = (int) round($srcHeight / 100 * $newHeight); + } + + if (self::isPercent($left)) { + $left = (int) round(($srcWidth - $newWidth) / 100 * $left); + } + + if (self::isPercent($top)) { + $top = (int) round(($srcHeight - $newHeight) / 100 * $top); + } + + if ($left < 0) { + $newWidth += $left; + $left = 0; + } + + if ($top < 0) { + $newHeight += $top; + $top = 0; + } + + $newWidth = min($newWidth, $srcWidth - $left); + $newHeight = min($newHeight, $srcHeight - $top); + return [$left, $top, $newWidth, $newHeight]; + } + + + /** + * Sharpens image a little bit. + */ + public function sharpen(): static + { + imageconvolution($this->image, [ // my magic numbers ;) + [-1, -1, -1], + [-1, 24, -1], + [-1, -1, -1], + ], 16, 0); + return $this; + } + + + /** + * Puts another image into this image. Left and top accepts pixels or percent. + * @param int<0, 100> $opacity 0..100 + */ + public function place(self $image, int|string $left = 0, int|string $top = 0, int $opacity = 100): static + { + $opacity = max(0, min(100, $opacity)); + if ($opacity === 0) { + return $this; + } + + $width = $image->getWidth(); + $height = $image->getHeight(); + + if (self::isPercent($left)) { + $left = (int) round(($this->getWidth() - $width) / 100 * $left); + } + + if (self::isPercent($top)) { + $top = (int) round(($this->getHeight() - $height) / 100 * $top); + } + + $output = $input = $image->image; + if ($opacity < 100) { + $tbl = []; + for ($i = 0; $i < 128; $i++) { + $tbl[$i] = round(127 - (127 - $i) * $opacity / 100); + } + + $output = imagecreatetruecolor($width, $height); + imagealphablending($output, false); + if (!$image->isTrueColor()) { + $input = $output; + imagefilledrectangle($output, 0, 0, $width, $height, imagecolorallocatealpha($output, 0, 0, 0, 127)); + imagecopy($output, $image->image, 0, 0, 0, 0, $width, $height); + } + + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $c = \imagecolorat($input, $x, $y); + $c = ($c & 0xFFFFFF) + ($tbl[$c >> 24] << 24); + \imagesetpixel($output, $x, $y, $c); + } + } + + imagealphablending($output, true); + } + + imagecopy( + $this->image, + $output, + $left, + $top, + 0, + 0, + $width, + $height, + ); + return $this; + } + + + /** + * Calculates the bounding box for a TrueType text. Returns keys left, top, width and height. + */ + public static function calculateTextBox( + string $text, + string $fontFile, + float $size, + float $angle = 0, + array $options = [], + ): array + { + self::ensureExtension(); + $box = imagettfbbox($size, $angle, $fontFile, $text, $options); + return [ + 'left' => $minX = min([$box[0], $box[2], $box[4], $box[6]]), + 'top' => $minY = min([$box[1], $box[3], $box[5], $box[7]]), + 'width' => max([$box[0], $box[2], $box[4], $box[6]]) - $minX + 1, + 'height' => max([$box[1], $box[3], $box[5], $box[7]]) - $minY + 1, + ]; + } + + + /** + * Draw a rectangle. + */ + public function rectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void + { + if ($width !== 0 && $height !== 0) { + $this->rectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color); + } + } + + + /** + * Draw a filled rectangle. + */ + public function filledRectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void + { + if ($width !== 0 && $height !== 0) { + $this->filledRectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color); + } + } + + + /** + * Saves image to the file. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). + * @param ImageType::*|null $type + * @throws ImageException + */ + public function save(string $file, ?int $quality = null, ?int $type = null): void + { + $type ??= self::extensionToType(pathinfo($file, PATHINFO_EXTENSION)); + $this->output($type, $quality, $file); + } + + + /** + * Outputs image to string. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). + * @param ImageType::* $type + */ + public function toString(int $type = ImageType::JPEG, ?int $quality = null): string + { + return Helpers::capture(function () use ($type, $quality): void { + $this->output($type, $quality); + }); + } + + + /** + * Outputs image to string. + */ + public function __toString(): string + { + return $this->toString(); + } + + + /** + * Outputs image to browser. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). + * @param ImageType::* $type + * @throws ImageException + */ + public function send(int $type = ImageType::JPEG, ?int $quality = null): void + { + header('Content-Type: ' . self::typeToMimeType($type)); + $this->output($type, $quality); + } + + + /** + * Outputs image to browser or file. + * @param ImageType::* $type + * @throws ImageException + */ + private function output(int $type, ?int $quality, ?string $file = null): void + { + [$defQuality, $min, $max] = match ($type) { + ImageType::JPEG => [85, 0, 100], + ImageType::PNG => [9, 0, 9], + ImageType::GIF => [null, null, null], + ImageType::WEBP => [80, 0, 100], + ImageType::AVIF => [30, 0, 100], + ImageType::BMP => [null, null, null], + default => throw new Nette\InvalidArgumentException("Unsupported image type '$type'."), + }; + + $args = [$this->image, $file]; + if ($defQuality !== null) { + $args[] = $quality === null ? $defQuality : max($min, min($max, $quality)); + } + + Callback::invokeSafe('image' . self::Formats[$type], $args, function (string $message) use ($file): void { + if ($file !== null) { + @unlink($file); + } + throw new ImageException($message); + }); + } + + + /** + * Call to undefined method. + * @throws Nette\MemberAccessException + */ + public function __call(string $name, array $args): mixed + { + $function = 'image' . $name; + if (!function_exists($function)) { + ObjectHelpers::strictCall(static::class, $name); + } + + foreach ($args as $key => $value) { + if ($value instanceof self) { + $args[$key] = $value->getImageResource(); + + } elseif ($value instanceof ImageColor || (is_array($value) && isset($value['red']))) { + $args[$key] = $this->resolveColor($value); + } + } + + $res = $function($this->image, ...$args); + return $res instanceof \GdImage + ? $this->setImageResource($res) + : $res; + } + + + public function __clone() + { + ob_start(function () {}); + imagepng($this->image, null, 0); + $this->setImageResource(imagecreatefromstring(ob_get_clean())); + } + + + private static function isPercent(int|string &$num): bool + { + if (is_string($num) && str_ends_with($num, '%')) { + $num = (float) substr($num, 0, -1); + return true; + } elseif (is_int($num) || $num === (string) (int) $num) { + $num = (int) $num; + return false; + } + + throw new Nette\InvalidArgumentException("Expected dimension in int|string, '$num' given."); + } + + + /** + * Prevents serialization. + */ + public function __sleep(): array + { + throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.'); + } + + + public function resolveColor(ImageColor|array $color): int + { + $color = $color instanceof ImageColor ? $color->toRGBA() : array_values($color); + return imagecolorallocatealpha($this->image, ...$color) ?: imagecolorresolvealpha($this->image, ...$color); + } + + + private static function ensureExtension(): void + { + if (!extension_loaded('gd')) { + throw new Nette\NotSupportedException('PHP extension GD is not loaded.'); + } + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php new file mode 100644 index 00000000000..013adbd6e0f --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php @@ -0,0 +1,75 @@ +red = max(0, min(255, $red)); + $this->green = max(0, min(255, $green)); + $this->blue = max(0, min(255, $blue)); + $this->opacity = max(0, min(1, $opacity)); + } + + + public function toRGBA(): array + { + return [ + max(0, min(255, $this->red)), + max(0, min(255, $this->green)), + max(0, min(255, $this->blue)), + max(0, min(127, (int) round(127 - $this->opacity * 127))), + ]; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php new file mode 100644 index 00000000000..3092c8f02d5 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php @@ -0,0 +1,25 @@ + $v) { + if ($k === $key) { + return true; + } + } + return false; + } + + + /** + * Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K + * @template V + * @param iterable $iterable + * @param ?callable(V, K, iterable): bool $predicate + * @return ?V + */ + public static function first(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed + { + foreach ($iterable as $k => $v) { + if (!$predicate || $predicate($v, $k, $iterable)) { + return $v; + } + } + return $else ? $else() : null; + } + + + /** + * Returns the key of first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null. + * @template K + * @template V + * @param iterable $iterable + * @param ?callable(V, K, iterable): bool $predicate + * @return ?K + */ + public static function firstKey(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed + { + foreach ($iterable as $k => $v) { + if (!$predicate || $predicate($v, $k, $iterable)) { + return $k; + } + } + return $else ? $else() : null; + } + + + /** + * Tests whether at least one element in the iterator passes the test implemented by the provided function. + * @template K + * @template V + * @param iterable $iterable + * @param callable(V, K, iterable): bool $predicate + */ + public static function some(iterable $iterable, callable $predicate): bool + { + foreach ($iterable as $k => $v) { + if ($predicate($v, $k, $iterable)) { + return true; + } + } + return false; + } + + + /** + * Tests whether all elements in the iterator pass the test implemented by the provided function. + * @template K + * @template V + * @param iterable $iterable + * @param callable(V, K, iterable): bool $predicate + */ + public static function every(iterable $iterable, callable $predicate): bool + { + foreach ($iterable as $k => $v) { + if (!$predicate($v, $k, $iterable)) { + return false; + } + } + return true; + } + + + /** + * Iterator that filters elements according to a given $predicate. Maintains original keys. + * @template K + * @template V + * @param iterable $iterable + * @param callable(V, K, iterable): bool $predicate + * @return \Generator + */ + public static function filter(iterable $iterable, callable $predicate): \Generator + { + foreach ($iterable as $k => $v) { + if ($predicate($v, $k, $iterable)) { + yield $k => $v; + } + } + } + + + /** + * Iterator that transforms values by calling $transformer. Maintains original keys. + * @template K + * @template V + * @template R + * @param iterable $iterable + * @param callable(V, K, iterable): R $transformer + * @return \Generator + */ + public static function map(iterable $iterable, callable $transformer): \Generator + { + foreach ($iterable as $k => $v) { + yield $k => $transformer($v, $k, $iterable); + } + } + + + /** + * Iterator that transforms keys and values by calling $transformer. If it returns null, the element is skipped. + * @template K + * @template V + * @template ResV + * @template ResK + * @param iterable $iterable + * @param callable(V, K, iterable): ?array{ResV, ResK} $transformer + * @return \Generator + */ + public static function mapWithKeys(iterable $iterable, callable $transformer): \Generator + { + foreach ($iterable as $k => $v) { + $pair = $transformer($v, $k, $iterable); + if ($pair) { + yield $pair[0] => $pair[1]; + } + } + } + + + /** + * Wraps around iterator and caches its keys and values during iteration. + * This allows the data to be re-iterated multiple times. + * @template K + * @template V + * @param iterable $iterable + * @return \IteratorAggregate + */ + public static function memoize(iterable $iterable): iterable + { + return new class (self::toIterator($iterable)) implements \IteratorAggregate { + public function __construct( + private \Iterator $iterator, + private array $cache = [], + ) { + } + + + public function getIterator(): \Generator + { + if (!$this->cache) { + $this->iterator->rewind(); + } + $i = 0; + while (true) { + if (isset($this->cache[$i])) { + [$k, $v] = $this->cache[$i]; + } elseif ($this->iterator->valid()) { + $k = $this->iterator->key(); + $v = $this->iterator->current(); + $this->iterator->next(); + $this->cache[$i] = [$k, $v]; + } else { + break; + } + yield $k => $v; + $i++; + } + } + }; + } + + + /** + * Creates an iterator from anything that is iterable. + * @template K + * @template V + * @param iterable $iterable + * @return \Iterator + */ + public static function toIterator(iterable $iterable): \Iterator + { + return match (true) { + $iterable instanceof \Iterator => $iterable, + $iterable instanceof \IteratorAggregate => self::toIterator($iterable->getIterator()), + is_array($iterable) => new \ArrayIterator($iterable), + default => throw new Nette\ShouldNotHappenException, + }; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php new file mode 100644 index 00000000000..b87917b2af7 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php @@ -0,0 +1,84 @@ +getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()), + self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'), + ), $name); + throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.')); + } + + + /** + * @return never + * @throws MemberAccessException + */ + public static function strictSet(string $class, string $name): void + { + $rc = new \ReflectionClass($class); + $hint = self::getSuggestion(array_merge( + array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()), + self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'), + ), $name); + throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.')); + } + + + /** + * @return never + * @throws MemberAccessException + */ + public static function strictCall(string $class, string $method, array $additionalMethods = []): void + { + $trace = debug_backtrace(0, 3); // suppose this method is called from __call() + $context = ($trace[1]['function'] ?? null) === '__call' + ? ($trace[2]['class'] ?? null) + : null; + + if ($context && is_a($class, $context, true) && method_exists($context, $method)) { // called parent::$method() + $class = get_parent_class($context); + } + + if (method_exists($class, $method)) { // insufficient visibility + $rm = new \ReflectionMethod($class, $method); + $visibility = $rm->isPrivate() + ? 'private ' + : ($rm->isProtected() ? 'protected ' : ''); + throw new MemberAccessException("Call to {$visibility}method $class::$method() from " . ($context ? "scope $context." : 'global scope.')); + + } else { + $hint = self::getSuggestion(array_merge( + get_class_methods($class), + self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:static[ \t]+)?(?:\S+[ \t]+)??(\w+)\(~m'), + $additionalMethods, + ), $method); + throw new MemberAccessException("Call to undefined method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.')); + } + } + + + /** + * @return never + * @throws MemberAccessException + */ + public static function strictStaticCall(string $class, string $method): void + { + $trace = debug_backtrace(0, 3); // suppose this method is called from __callStatic() + $context = ($trace[1]['function'] ?? null) === '__callStatic' + ? ($trace[2]['class'] ?? null) + : null; + + if ($context && is_a($class, $context, true) && method_exists($context, $method)) { // called parent::$method() + $class = get_parent_class($context); + } + + if (method_exists($class, $method)) { // insufficient visibility + $rm = new \ReflectionMethod($class, $method); + $visibility = $rm->isPrivate() + ? 'private ' + : ($rm->isProtected() ? 'protected ' : ''); + throw new MemberAccessException("Call to {$visibility}method $class::$method() from " . ($context ? "scope $context." : 'global scope.')); + + } else { + $hint = self::getSuggestion( + array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), fn($m) => $m->isStatic()), + $method, + ); + throw new MemberAccessException("Call to undefined static method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.')); + } + } + + + /** + * Returns array of magic properties defined by annotation @property. + * @return array of [name => bit mask] + * @internal + */ + public static function getMagicProperties(string $class): array + { + static $cache; + $props = &$cache[$class]; + if ($props !== null) { + return $props; + } + + $rc = new \ReflectionClass($class); + preg_match_all( + '~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx', + (string) $rc->getDocComment(), + $matches, + PREG_SET_ORDER, + ); + + $props = []; + foreach ($matches as [, $type, $name]) { + $uname = ucfirst($name); + $write = $type !== '-read' + && $rc->hasMethod($nm = 'set' . $uname) + && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic(); + $read = $type !== '-write' + && ($rc->hasMethod($nm = 'get' . $uname) || $rc->hasMethod($nm = 'is' . $uname)) + && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic(); + + if ($read || $write) { + $props[$name] = $read << 0 | ($nm[0] === 'g') << 1 | $rm->returnsReference() << 2 | $write << 3 | ($type === '-deprecated') << 4; + } + } + + foreach ($rc->getTraits() as $trait) { + $props += self::getMagicProperties($trait->name); + } + + if ($parent = get_parent_class($class)) { + $props += self::getMagicProperties($parent); + } + + return $props; + } + + + /** + * Finds the best suggestion (for 8-bit encoding). + * @param (\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionClass|\ReflectionProperty|string)[] $possibilities + * @internal + */ + public static function getSuggestion(array $possibilities, string $value): ?string + { + $norm = preg_replace($re = '#^(get|set|has|is|add)(?=[A-Z])#', '+', $value); + $best = null; + $min = (strlen($value) / 4 + 1) * 10 + .1; + foreach (array_unique($possibilities, SORT_REGULAR) as $item) { + $item = $item instanceof \Reflector ? $item->name : $item; + if ($item !== $value && ( + ($len = levenshtein($item, $value, 10, 11, 10)) < $min + || ($len = levenshtein(preg_replace($re, '*', $item), $norm, 10, 11, 10)) < $min + )) { + $min = $len; + $best = $item; + } + } + + return $best; + } + + + private static function parseFullDoc(\ReflectionClass $rc, string $pattern): array + { + do { + $doc[] = $rc->getDocComment(); + $traits = $rc->getTraits(); + while ($trait = array_pop($traits)) { + $doc[] = $trait->getDocComment(); + $traits += $trait->getTraits(); + } + } while ($rc = $rc->getParentClass()); + + return preg_match_all($pattern, implode('', $doc), $m) ? $m[1] : []; + } + + + /** + * Checks if the public non-static property exists. + * Returns 'event' if the property exists and has event like name + * @internal + */ + public static function hasProperty(string $class, string $name): bool|string + { + static $cache; + $prop = &$cache[$class][$name]; + if ($prop === null) { + $prop = false; + try { + $rp = new \ReflectionProperty($class, $name); + if ($rp->isPublic() && !$rp->isStatic()) { + $prop = $name >= 'onA' && $name < 'on_' ? 'event' : true; + } + } catch (\ReflectionException $e) { + } + } + + return $prop; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Paginator.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Paginator.php new file mode 100644 index 00000000000..aa4812c0a3a --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Paginator.php @@ -0,0 +1,245 @@ + $firstItemOnPage + * @property-read int<0,max> $lastItemOnPage + * @property int $base + * @property-read bool $first + * @property-read bool $last + * @property-read int<0,max>|null $pageCount + * @property positive-int $itemsPerPage + * @property int<0,max>|null $itemCount + * @property-read int<0,max> $offset + * @property-read int<0,max>|null $countdownOffset + * @property-read int<0,max> $length + */ +class Paginator +{ + use Nette\SmartObject; + + private int $base = 1; + + /** @var positive-int */ + private int $itemsPerPage = 1; + + private int $page = 1; + + /** @var int<0, max>|null */ + private ?int $itemCount = null; + + + /** + * Sets current page number. + */ + public function setPage(int $page): static + { + $this->page = $page; + return $this; + } + + + /** + * Returns current page number. + */ + public function getPage(): int + { + return $this->base + $this->getPageIndex(); + } + + + /** + * Returns first page number. + */ + public function getFirstPage(): int + { + return $this->base; + } + + + /** + * Returns last page number. + */ + public function getLastPage(): ?int + { + return $this->itemCount === null + ? null + : $this->base + max(0, $this->getPageCount() - 1); + } + + + /** + * Returns the sequence number of the first element on the page + * @return int<0, max> + */ + public function getFirstItemOnPage(): int + { + return $this->itemCount !== 0 + ? $this->offset + 1 + : 0; + } + + + /** + * Returns the sequence number of the last element on the page + * @return int<0, max> + */ + public function getLastItemOnPage(): int + { + return $this->offset + $this->length; + } + + + /** + * Sets first page (base) number. + */ + public function setBase(int $base): static + { + $this->base = $base; + return $this; + } + + + /** + * Returns first page (base) number. + */ + public function getBase(): int + { + return $this->base; + } + + + /** + * Returns zero-based page number. + * @return int<0, max> + */ + protected function getPageIndex(): int + { + $index = max(0, $this->page - $this->base); + return $this->itemCount === null + ? $index + : min($index, max(0, $this->getPageCount() - 1)); + } + + + /** + * Is the current page the first one? + */ + public function isFirst(): bool + { + return $this->getPageIndex() === 0; + } + + + /** + * Is the current page the last one? + */ + public function isLast(): bool + { + return $this->itemCount === null + ? false + : $this->getPageIndex() >= $this->getPageCount() - 1; + } + + + /** + * Returns the total number of pages. + * @return int<0, max>|null + */ + public function getPageCount(): ?int + { + return $this->itemCount === null + ? null + : (int) ceil($this->itemCount / $this->itemsPerPage); + } + + + /** + * Sets the number of items to display on a single page. + */ + public function setItemsPerPage(int $itemsPerPage): static + { + $this->itemsPerPage = max(1, $itemsPerPage); + return $this; + } + + + /** + * Returns the number of items to display on a single page. + * @return positive-int + */ + public function getItemsPerPage(): int + { + return $this->itemsPerPage; + } + + + /** + * Sets the total number of items. + */ + public function setItemCount(?int $itemCount = null): static + { + $this->itemCount = $itemCount === null ? null : max(0, $itemCount); + return $this; + } + + + /** + * Returns the total number of items. + * @return int<0, max>|null + */ + public function getItemCount(): ?int + { + return $this->itemCount; + } + + + /** + * Returns the absolute index of the first item on current page. + * @return int<0, max> + */ + public function getOffset(): int + { + return $this->getPageIndex() * $this->itemsPerPage; + } + + + /** + * Returns the absolute index of the first item on current page in countdown paging. + * @return int<0, max>|null + */ + public function getCountdownOffset(): ?int + { + return $this->itemCount === null + ? null + : max(0, $this->itemCount - ($this->getPageIndex() + 1) * $this->itemsPerPage); + } + + + /** + * Returns the number of items on current page. + * @return int<0, max> + */ + public function getLength(): int + { + return $this->itemCount === null + ? $this->itemsPerPage + : min($this->itemsPerPage, $this->itemCount - $this->getPageIndex() * $this->itemsPerPage); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php new file mode 100644 index 00000000000..b14fbd55ed0 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php @@ -0,0 +1,52 @@ + implode('', range($m[0][0], $m[0][2])), + $charlist, + ); + $charlist = count_chars($charlist, mode: 3); + $chLen = strlen($charlist); + + if ($length < 1) { + throw new Nette\InvalidArgumentException('Length must be greater than zero.'); + } elseif ($chLen < 2) { + throw new Nette\InvalidArgumentException('Character list must contain at least two chars.'); + } elseif (PHP_VERSION_ID >= 80300) { + return (new Randomizer)->getBytesFromString($charlist, $length); + } + + $res = ''; + for ($i = 0; $i < $length; $i++) { + $res .= $charlist[random_int(0, $chLen - 1)]; + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php new file mode 100644 index 00000000000..2d864df9c21 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php @@ -0,0 +1,322 @@ +isDefaultValueConstant()) { + $const = $orig = $param->getDefaultValueConstantName(); + $pair = explode('::', $const); + if (isset($pair[1])) { + $pair[0] = Type::resolve($pair[0], $param); + try { + $rcc = new \ReflectionClassConstant($pair[0], $pair[1]); + } catch (\ReflectionException $e) { + $name = self::toString($param); + throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name.", 0, $e); + } + + return $rcc->getValue(); + + } elseif (!defined($const)) { + $const = substr((string) strrchr($const, '\\'), 1); + if (!defined($const)) { + $name = self::toString($param); + throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name."); + } + } + + return constant($const); + } + + return $param->getDefaultValue(); + } + + + /** + * Returns a reflection of a class or trait that contains a declaration of given property. Property can also be declared in the trait. + */ + public static function getPropertyDeclaringClass(\ReflectionProperty $prop): \ReflectionClass + { + foreach ($prop->getDeclaringClass()->getTraits() as $trait) { + if ($trait->hasProperty($prop->name) + // doc-comment guessing as workaround for insufficient PHP reflection + && $trait->getProperty($prop->name)->getDocComment() === $prop->getDocComment() + ) { + return self::getPropertyDeclaringClass($trait->getProperty($prop->name)); + } + } + + return $prop->getDeclaringClass(); + } + + + /** + * Returns a reflection of a method that contains a declaration of $method. + * Usually, each method is its own declaration, but the body of the method can also be in the trait and under a different name. + */ + public static function getMethodDeclaringMethod(\ReflectionMethod $method): \ReflectionMethod + { + // file & line guessing as workaround for insufficient PHP reflection + $decl = $method->getDeclaringClass(); + if ($decl->getFileName() === $method->getFileName() + && $decl->getStartLine() <= $method->getStartLine() + && $decl->getEndLine() >= $method->getEndLine() + ) { + return $method; + } + + $hash = [$method->getFileName(), $method->getStartLine(), $method->getEndLine()]; + if (($alias = $decl->getTraitAliases()[$method->name] ?? null) + && ($m = new \ReflectionMethod(...explode('::', $alias, 2))) + && $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] + ) { + return self::getMethodDeclaringMethod($m); + } + + foreach ($decl->getTraits() as $trait) { + if ($trait->hasMethod($method->name) + && ($m = $trait->getMethod($method->name)) + && $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] + ) { + return self::getMethodDeclaringMethod($m); + } + } + + return $method; + } + + + /** + * Finds out if reflection has access to PHPdoc comments. Comments may not be available due to the opcode cache. + */ + public static function areCommentsAvailable(): bool + { + static $res; + return $res ?? $res = (bool) (new \ReflectionMethod(self::class, __FUNCTION__))->getDocComment(); + } + + + public static function toString(\Reflector $ref): string + { + if ($ref instanceof \ReflectionClass) { + return $ref->name; + } elseif ($ref instanceof \ReflectionMethod) { + return $ref->getDeclaringClass()->name . '::' . $ref->name . '()'; + } elseif ($ref instanceof \ReflectionFunction) { + return PHP_VERSION_ID >= 80200 && $ref->isAnonymous() + ? '{closure}()' + : $ref->name . '()'; + } elseif ($ref instanceof \ReflectionProperty) { + return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name; + } elseif ($ref instanceof \ReflectionParameter) { + return '$' . $ref->name . ' in ' . self::toString($ref->getDeclaringFunction()); + } else { + throw new Nette\InvalidArgumentException; + } + } + + + /** + * Expands the name of the class to full name in the given context of given class. + * Thus, it returns how the PHP parser would understand $name if it were written in the body of the class $context. + * @throws Nette\InvalidArgumentException + */ + public static function expandClassName(string $name, \ReflectionClass $context): string + { + $lower = strtolower($name); + if (empty($name)) { + throw new Nette\InvalidArgumentException('Class name must not be empty.'); + + } elseif (Validators::isBuiltinType($lower)) { + return $lower; + + } elseif ($lower === 'self' || $lower === 'static') { + return $context->name; + + } elseif ($lower === 'parent') { + return $context->getParentClass() + ? $context->getParentClass()->name + : 'parent'; + + } elseif ($name[0] === '\\') { // fully qualified name + return ltrim($name, '\\'); + } + + $uses = self::getUseStatements($context); + $parts = explode('\\', $name, 2); + if (isset($uses[$parts[0]])) { + $parts[0] = $uses[$parts[0]]; + return implode('\\', $parts); + + } elseif ($context->inNamespace()) { + return $context->getNamespaceName() . '\\' . $name; + + } else { + return $name; + } + } + + + /** @return array of [alias => class] */ + public static function getUseStatements(\ReflectionClass $class): array + { + if ($class->isAnonymous()) { + throw new Nette\NotImplementedException('Anonymous classes are not supported.'); + } + + static $cache = []; + if (!isset($cache[$name = $class->name])) { + if ($class->isInternal()) { + $cache[$name] = []; + } else { + $code = file_get_contents($class->getFileName()); + $cache = self::parseUseStatements($code, $name) + $cache; + } + } + + return $cache[$name]; + } + + + /** + * Parses PHP code to [class => [alias => class, ...]] + */ + private static function parseUseStatements(string $code, ?string $forClass = null): array + { + try { + $tokens = \PhpToken::tokenize($code, TOKEN_PARSE); + } catch (\ParseError $e) { + trigger_error($e->getMessage(), E_USER_NOTICE); + $tokens = []; + } + + $namespace = $class = null; + $classLevel = $level = 0; + $res = $uses = []; + + $nameTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED]; + + while ($token = current($tokens)) { + next($tokens); + switch ($token->id) { + case T_NAMESPACE: + $namespace = ltrim(self::fetch($tokens, $nameTokens) . '\\', '\\'); + $uses = []; + break; + + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + case PHP_VERSION_ID < 80100 + ? T_CLASS + : T_ENUM: + if ($name = self::fetch($tokens, T_STRING)) { + $class = $namespace . $name; + $classLevel = $level + 1; + $res[$class] = $uses; + if ($class === $forClass) { + return $res; + } + } + + break; + + case T_USE: + while (!$class && ($name = self::fetch($tokens, $nameTokens))) { + $name = ltrim($name, '\\'); + if (self::fetch($tokens, '{')) { + while ($suffix = self::fetch($tokens, $nameTokens)) { + if (self::fetch($tokens, T_AS)) { + $uses[self::fetch($tokens, T_STRING)] = $name . $suffix; + } else { + $tmp = explode('\\', $suffix); + $uses[end($tmp)] = $name . $suffix; + } + + if (!self::fetch($tokens, ',')) { + break; + } + } + } elseif (self::fetch($tokens, T_AS)) { + $uses[self::fetch($tokens, T_STRING)] = $name; + + } else { + $tmp = explode('\\', $name); + $uses[end($tmp)] = $name; + } + + if (!self::fetch($tokens, ',')) { + break; + } + } + + break; + + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case ord('{'): + $level++; + break; + + case ord('}'): + if ($level === $classLevel) { + $class = $classLevel = 0; + } + + $level--; + } + } + + return $res; + } + + + private static function fetch(array &$tokens, string|int|array $take): ?string + { + $res = null; + while ($token = current($tokens)) { + if ($token->is($take)) { + $res .= $token->text; + } elseif (!$token->is([T_DOC_COMMENT, T_WHITESPACE, T_COMMENT])) { + break; + } + + next($tokens); + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php new file mode 100644 index 00000000000..b003fcbd179 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php @@ -0,0 +1,36 @@ +originalClass = new \ReflectionClass($objectOrMethod); + } + + + public function getOriginalClass(): \ReflectionClass + { + return $this->originalClass; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php new file mode 100644 index 00000000000..79fa46bb0db --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php @@ -0,0 +1,727 @@ += 0xD800 && $code <= 0xDFFF) || $code > 0x10FFFF) { + throw new Nette\InvalidArgumentException('Code point must be in range 0x0 to 0xD7FF or 0xE000 to 0x10FFFF.'); + } elseif (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); + } + + return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code)); + } + + + /** + * Returns a code point of specific character in UTF-8 (number in range 0x0000..D7FF or 0xE000..10FFFF). + */ + public static function ord(string $c): int + { + if (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); + } + + $tmp = iconv('UTF-8', 'UTF-32BE//IGNORE', $c); + if (!$tmp) { + throw new Nette\InvalidArgumentException('Invalid UTF-8 character "' . ($c === '' ? '' : '\x' . strtoupper(bin2hex($c))) . '".'); + } + + return unpack('N', $tmp)[1]; + } + + + /** + * @deprecated use str_starts_with() + */ + public static function startsWith(string $haystack, string $needle): bool + { + return str_starts_with($haystack, $needle); + } + + + /** + * @deprecated use str_ends_with() + */ + public static function endsWith(string $haystack, string $needle): bool + { + return str_ends_with($haystack, $needle); + } + + + /** + * @deprecated use str_contains() + */ + public static function contains(string $haystack, string $needle): bool + { + return str_contains($haystack, $needle); + } + + + /** + * Returns a part of UTF-8 string specified by starting position and length. If start is negative, + * the returned string will start at the start'th character from the end of string. + */ + public static function substring(string $s, int $start, ?int $length = null): string + { + if (function_exists('mb_substr')) { + return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster + } elseif (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires extension ICONV or MBSTRING, neither is loaded.'); + } elseif ($length === null) { + $length = self::length($s); + } elseif ($start < 0 && $length < 0) { + $start += self::length($s); // unifies iconv_substr behavior with mb_substr + } + + return iconv_substr($s, $start, $length, 'UTF-8'); + } + + + /** + * Removes control characters, normalizes line breaks to `\n`, removes leading and trailing blank lines, + * trims end spaces on lines, normalizes UTF-8 to the normal form of NFC. + */ + public static function normalize(string $s): string + { + // convert to compressed normal form (NFC) + if (class_exists('Normalizer', false) && ($n = \Normalizer::normalize($s, \Normalizer::FORM_C)) !== false) { + $s = $n; + } + + $s = self::unixNewLines($s); + + // remove control characters; leave \t + \n + $s = self::pcre('preg_replace', ['#[\x00-\x08\x0B-\x1F\x7F-\x9F]+#u', '', $s]); + + // right trim + $s = self::pcre('preg_replace', ['#[\t ]+$#m', '', $s]); + + // leading and trailing blank lines + $s = trim($s, "\n"); + + return $s; + } + + + /** @deprecated use Strings::unixNewLines() */ + public static function normalizeNewLines(string $s): string + { + return self::unixNewLines($s); + } + + + /** + * Converts line endings to \n used on Unix-like systems. + * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator. + */ + public static function unixNewLines(string $s): string + { + return preg_replace("~\r\n?|\u{2028}|\u{2029}~", "\n", $s); + } + + + /** + * Converts line endings to platform-specific, i.e. \r\n on Windows and \n elsewhere. + * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator. + */ + public static function platformNewLines(string $s): string + { + return preg_replace("~\r\n?|\n|\u{2028}|\u{2029}~", PHP_EOL, $s); + } + + + /** + * Converts UTF-8 string to ASCII, ie removes diacritics etc. + */ + public static function toAscii(string $s): string + { + $iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null; + static $transliterator = null; + if ($transliterator === null) { + if (class_exists('Transliterator', false)) { + $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII'); + } else { + trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE); + $transliterator = false; + } + } + + // remove control characters and check UTF-8 validity + $s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]); + + // transliteration (by Transliterator and iconv) is not optimal, replace some characters directly + $s = strtr($s, ["\u{201E}" => '"', "\u{201C}" => '"', "\u{201D}" => '"', "\u{201A}" => "'", "\u{2018}" => "'", "\u{2019}" => "'", "\u{B0}" => '^', "\u{42F}" => 'Ya', "\u{44F}" => 'ya', "\u{42E}" => 'Yu', "\u{44E}" => 'yu', "\u{c4}" => 'Ae', "\u{d6}" => 'Oe', "\u{dc}" => 'Ue', "\u{1e9e}" => 'Ss', "\u{e4}" => 'ae', "\u{f6}" => 'oe', "\u{fc}" => 'ue', "\u{df}" => 'ss']); // „ “ ” ‚ ‘ ’ ° Я я Ю ю Ä Ö Ü ẞ ä ö ü ß + if ($iconv !== 'libiconv') { + $s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔ + } + + if ($transliterator) { + $s = $transliterator->transliterate($s); + // use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ + if ($iconv === 'glibc') { + $s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates + $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); + $s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters + } elseif ($iconv === 'libiconv') { + $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); + } else { // null or 'unknown' (#216) + $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars + } + } elseif ($iconv === 'glibc' || $iconv === 'libiconv') { + // temporarily hide these characters to distinguish them from the garbage that iconv creates + $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06"); + if ($iconv === 'glibc') { + // glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved + $s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); + $s = strtr( + $s, + "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7", + 'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.', + ); + $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); + } else { + $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); + } + + // remove garbage that iconv creates during transliteration (eg Ý -> Y') + $s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s); + // restore temporarily hidden characters + $s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?'); + } else { + $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars + } + + return $s; + } + + + /** + * Modifies the UTF-8 string to the form used in the URL, ie removes diacritics and replaces all characters + * except letters of the English alphabet and numbers with a hyphens. + */ + public static function webalize(string $s, ?string $charlist = null, bool $lower = true): string + { + $s = self::toAscii($s); + if ($lower) { + $s = strtolower($s); + } + + $s = self::pcre('preg_replace', ['#[^a-z0-9' . ($charlist !== null ? preg_quote($charlist, '#') : '') . ']+#i', '-', $s]); + $s = trim($s, '-'); + return $s; + } + + + /** + * Truncates a UTF-8 string to given maximal length, while trying not to split whole words. Only if the string is truncated, + * an ellipsis (or something else set with third argument) is appended to the string. + */ + public static function truncate(string $s, int $maxLen, string $append = "\u{2026}"): string + { + if (self::length($s) > $maxLen) { + $maxLen -= self::length($append); + if ($maxLen < 1) { + return $append; + + } elseif ($matches = self::match($s, '#^.{1,' . $maxLen . '}(?=[\s\x00-/:-@\[-`{-~])#us')) { + return $matches[0] . $append; + + } else { + return self::substring($s, 0, $maxLen) . $append; + } + } + + return $s; + } + + + /** + * Indents a multiline text from the left. Second argument sets how many indentation chars should be used, + * while the indent itself is the third argument (*tab* by default). + */ + public static function indent(string $s, int $level = 1, string $chars = "\t"): string + { + if ($level > 0) { + $s = self::replace($s, '#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level)); + } + + return $s; + } + + + /** + * Converts all characters of UTF-8 string to lower case. + */ + public static function lower(string $s): string + { + return mb_strtolower($s, 'UTF-8'); + } + + + /** + * Converts the first character of a UTF-8 string to lower case and leaves the other characters unchanged. + */ + public static function firstLower(string $s): string + { + return self::lower(self::substring($s, 0, 1)) . self::substring($s, 1); + } + + + /** + * Converts all characters of a UTF-8 string to upper case. + */ + public static function upper(string $s): string + { + return mb_strtoupper($s, 'UTF-8'); + } + + + /** + * Converts the first character of a UTF-8 string to upper case and leaves the other characters unchanged. + */ + public static function firstUpper(string $s): string + { + return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1); + } + + + /** + * Converts the first character of every word of a UTF-8 string to upper case and the others to lower case. + */ + public static function capitalize(string $s): string + { + return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8'); + } + + + /** + * Compares two UTF-8 strings or their parts, without taking character case into account. If length is null, whole strings are compared, + * if it is negative, the corresponding number of characters from the end of the strings is compared, + * otherwise the appropriate number of characters from the beginning is compared. + */ + public static function compare(string $left, string $right, ?int $length = null): bool + { + if (class_exists('Normalizer', false)) { + $left = \Normalizer::normalize($left, \Normalizer::FORM_D); // form NFD is faster + $right = \Normalizer::normalize($right, \Normalizer::FORM_D); // form NFD is faster + } + + if ($length < 0) { + $left = self::substring($left, $length, -$length); + $right = self::substring($right, $length, -$length); + } elseif ($length !== null) { + $left = self::substring($left, 0, $length); + $right = self::substring($right, 0, $length); + } + + return self::lower($left) === self::lower($right); + } + + + /** + * Finds the common prefix of strings or returns empty string if the prefix was not found. + * @param string[] $strings + */ + public static function findPrefix(array $strings): string + { + $first = array_shift($strings); + for ($i = 0; $i < strlen($first); $i++) { + foreach ($strings as $s) { + if (!isset($s[$i]) || $first[$i] !== $s[$i]) { + while ($i && $first[$i - 1] >= "\x80" && $first[$i] >= "\x80" && $first[$i] < "\xC0") { + $i--; + } + + return substr($first, 0, $i); + } + } + } + + return $first; + } + + + /** + * Returns number of characters (not bytes) in UTF-8 string. + * That is the number of Unicode code points which may differ from the number of graphemes. + */ + public static function length(string $s): int + { + return match (true) { + extension_loaded('mbstring') => mb_strlen($s, 'UTF-8'), + extension_loaded('iconv') => iconv_strlen($s, 'UTF-8'), + default => strlen(@utf8_decode($s)), // deprecated + }; + } + + + /** + * Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string. + */ + public static function trim(string $s, string $charlist = self::TrimCharacters): string + { + $charlist = preg_quote($charlist, '#'); + return self::replace($s, '#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', ''); + } + + + /** + * Pads a UTF-8 string to given length by prepending the $pad string to the beginning. + * @param non-empty-string $pad + */ + public static function padLeft(string $s, int $length, string $pad = ' '): string + { + $length = max(0, $length - self::length($s)); + $padLen = self::length($pad); + return str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen) . $s; + } + + + /** + * Pads UTF-8 string to given length by appending the $pad string to the end. + * @param non-empty-string $pad + */ + public static function padRight(string $s, int $length, string $pad = ' '): string + { + $length = max(0, $length - self::length($s)); + $padLen = self::length($pad); + return $s . str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen); + } + + + /** + * Reverses UTF-8 string. + */ + public static function reverse(string $s): string + { + if (!extension_loaded('iconv')) { + throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); + } + + return iconv('UTF-32LE', 'UTF-8', strrev(iconv('UTF-8', 'UTF-32BE', $s))); + } + + + /** + * Returns part of $haystack before $nth occurence of $needle or returns null if the needle was not found. + * Negative value means searching from the end. + */ + public static function before(string $haystack, string $needle, int $nth = 1): ?string + { + $pos = self::pos($haystack, $needle, $nth); + return $pos === null + ? null + : substr($haystack, 0, $pos); + } + + + /** + * Returns part of $haystack after $nth occurence of $needle or returns null if the needle was not found. + * Negative value means searching from the end. + */ + public static function after(string $haystack, string $needle, int $nth = 1): ?string + { + $pos = self::pos($haystack, $needle, $nth); + return $pos === null + ? null + : substr($haystack, $pos + strlen($needle)); + } + + + /** + * Returns position in characters of $nth occurence of $needle in $haystack or null if the $needle was not found. + * Negative value of `$nth` means searching from the end. + */ + public static function indexOf(string $haystack, string $needle, int $nth = 1): ?int + { + $pos = self::pos($haystack, $needle, $nth); + return $pos === null + ? null + : self::length(substr($haystack, 0, $pos)); + } + + + /** + * Returns position in characters of $nth occurence of $needle in $haystack or null if the needle was not found. + */ + private static function pos(string $haystack, string $needle, int $nth = 1): ?int + { + if (!$nth) { + return null; + } elseif ($nth > 0) { + if ($needle === '') { + return 0; + } + + $pos = 0; + while (($pos = strpos($haystack, $needle, $pos)) !== false && --$nth) { + $pos++; + } + } else { + $len = strlen($haystack); + if ($needle === '') { + return $len; + } elseif ($len === 0) { + return null; + } + + $pos = $len - 1; + while (($pos = strrpos($haystack, $needle, $pos - $len)) !== false && ++$nth) { + $pos--; + } + } + + return Helpers::falseToNull($pos); + } + + + /** + * Divides the string into arrays according to the regular expression. Expressions in parentheses will be captured and returned as well. + */ + public static function split( + string $subject, + #[Language('RegExp')] + string $pattern, + bool|int $captureOffset = false, + bool $skipEmpty = false, + int $limit = -1, + bool $utf8 = false, + ): array + { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); + + $pattern .= $utf8 ? 'u' : ''; + $m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]); + return $utf8 && $captureOffset + ? self::bytesToChars($subject, [$m])[0] + : $m; + } + + + /** + * Searches the string for the part matching the regular expression and returns + * an array with the found expression and individual subexpressions, or `null`. + */ + public static function match( + string $subject, + #[Language('RegExp')] + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + bool $utf8 = false, + ): ?array + { + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); + + if ($utf8) { + $offset = strlen(self::substring($subject, 0, $offset)); + $pattern .= 'u'; + } + + if ($offset > strlen($subject)) { + return null; + } elseif (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) { + return null; + } elseif ($utf8 && $captureOffset) { + return self::bytesToChars($subject, [$m])[0]; + } else { + return $m; + } + } + + + /** + * Searches the string for all occurrences matching the regular expression and + * returns an array of arrays containing the found expression and each subexpression. + * @return ($lazy is true ? \Generator : array[]) + */ + public static function matchAll( + string $subject, + #[Language('RegExp')] + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + bool $patternOrder = false, + bool $utf8 = false, + bool $lazy = false, + ): array|\Generator + { + if ($utf8) { + $offset = strlen(self::substring($subject, 0, $offset)); + $pattern .= 'u'; + } + + if ($lazy) { + $flags = PREG_OFFSET_CAPTURE | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); + return (function () use ($utf8, $captureOffset, $flags, $subject, $pattern, $offset) { + $counter = 0; + while ( + $offset <= strlen($subject) - ($counter ? 1 : 0) + && self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset]) + ) { + $offset = $m[0][1] + max(1, strlen($m[0][0])); + if (!$captureOffset) { + $m = array_map(fn($item) => $item[0], $m); + } elseif ($utf8) { + $m = self::bytesToChars($subject, [$m])[0]; + } + yield $counter++ => $m; + } + })(); + } + + if ($offset > strlen($subject)) { + return []; + } + + $flags = is_int($captureOffset) // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0); + + self::pcre('preg_match_all', [ + $pattern, $subject, &$m, + ($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER), + $offset, + ]); + return $utf8 && $captureOffset + ? self::bytesToChars($subject, $m) + : $m; + } + + + /** + * Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`. + */ + public static function replace( + string $subject, + #[Language('RegExp')] + string|array $pattern, + string|callable $replacement = '', + int $limit = -1, + bool $captureOffset = false, + bool $unmatchedAsNull = false, + bool $utf8 = false, + ): string + { + if (is_object($replacement) || is_array($replacement)) { + if (!is_callable($replacement, false, $textual)) { + throw new Nette\InvalidStateException("Callback '$textual' is not callable."); + } + + $flags = ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); + if ($utf8) { + $pattern .= 'u'; + if ($captureOffset) { + $replacement = fn($m) => $replacement(self::bytesToChars($subject, [$m])[0]); + } + } + + return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]); + + } elseif (is_array($pattern) && is_string(key($pattern))) { + $replacement = array_values($pattern); + $pattern = array_keys($pattern); + } + + if ($utf8) { + $pattern = array_map(fn($item) => $item . 'u', (array) $pattern); + } + + return self::pcre('preg_replace', [$pattern, $replacement, $subject, $limit]); + } + + + private static function bytesToChars(string $s, array $groups): array + { + $lastBytes = $lastChars = 0; + foreach ($groups as &$matches) { + foreach ($matches as &$match) { + if ($match[1] > $lastBytes) { + $lastChars += self::length(substr($s, $lastBytes, $match[1] - $lastBytes)); + } elseif ($match[1] < $lastBytes) { + $lastChars -= self::length(substr($s, $match[1], $lastBytes - $match[1])); + } + + $lastBytes = $match[1]; + $match[1] = $lastChars; + } + } + + return $groups; + } + + + /** @internal */ + public static function pcre(string $func, array $args) + { + $res = Callback::invokeSafe($func, $args, function (string $message) use ($args): void { + // compile-time error, not detectable by preg_last_error + throw new RegexpException($message . ' in pattern: ' . implode(' or ', (array) $args[0])); + }); + + if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars + && ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true)) + ) { + throw new RegexpException(preg_last_error_msg() + . ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code); + } + + return $res; + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php new file mode 100644 index 00000000000..3444a8f17de --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php @@ -0,0 +1,267 @@ + */ + private array $types; + private bool $simple; + private string $kind; // | & + + + /** + * Creates a Type object based on reflection. Resolves self, static and parent to the actual class name. + * If the subject has no type, it returns null. + */ + public static function fromReflection( + \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection, + ): ?self + { + $type = $reflection instanceof \ReflectionFunctionAbstract + ? $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null) + : $reflection->getType(); + + return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null; + } + + + private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject): self|string + { + if ($type instanceof \ReflectionNamedType) { + $name = self::resolve($type->getName(), $of); + return $asObject + ? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name]) + : $name; + + } elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { + return new self( + array_map(fn($t) => self::fromReflectionType($t, $of, asObject: false), $type->getTypes()), + $type instanceof \ReflectionUnionType ? '|' : '&', + ); + + } else { + throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of)); + } + } + + + /** + * Creates the Type object according to the text notation. + */ + public static function fromString(string $type): self + { + if (!Validators::isTypeDeclaration($type)) { + throw new Nette\InvalidArgumentException("Invalid type '$type'."); + } + + if ($type[0] === '?') { + return new self([substr($type, 1), 'null']); + } + + $unions = []; + foreach (explode('|', $type) as $part) { + $part = explode('&', trim($part, '()')); + $unions[] = count($part) === 1 ? $part[0] : new self($part, '&'); + } + + return count($unions) === 1 && $unions[0] instanceof self + ? $unions[0] + : new self($unions); + } + + + /** + * Resolves 'self', 'static' and 'parent' to the actual class name. + */ + public static function resolve( + string $type, + \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of, + ): string + { + $lower = strtolower($type); + if ($of instanceof \ReflectionFunction) { + return $type; + } elseif ($lower === 'self') { + return $of->getDeclaringClass()->name; + } elseif ($lower === 'static') { + return ($of instanceof ReflectionMethod ? $of->getOriginalClass() : $of->getDeclaringClass())->name; + } elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) { + return $of->getDeclaringClass()->getParentClass()->name; + } else { + return $type; + } + } + + + private function __construct(array $types, string $kind = '|') + { + $o = array_search('null', $types, strict: true); + if ($o !== false) { // null as last + array_splice($types, $o, 1); + $types[] = 'null'; + } + + $this->types = $types; + $this->simple = is_string($types[0]) && ($types[1] ?? 'null') === 'null'; + $this->kind = count($types) > 1 ? $kind : ''; + } + + + public function __toString(): string + { + $multi = count($this->types) > 1; + if ($this->simple) { + return ($multi ? '?' : '') . $this->types[0]; + } + + $res = []; + foreach ($this->types as $type) { + $res[] = $type instanceof self && $multi ? "($type)" : $type; + } + return implode($this->kind, $res); + } + + + /** + * Returns the array of subtypes that make up the compound type as strings. + * @return array + */ + public function getNames(): array + { + return array_map(fn($t) => $t instanceof self ? $t->getNames() : $t, $this->types); + } + + + /** + * Returns the array of subtypes that make up the compound type as Type objects: + * @return self[] + */ + public function getTypes(): array + { + return array_map(fn($t) => $t instanceof self ? $t : new self([$t]), $this->types); + } + + + /** + * Returns the type name for simple types, otherwise null. + */ + public function getSingleName(): ?string + { + return $this->simple + ? $this->types[0] + : null; + } + + + /** + * Returns true whether it is a union type. + */ + public function isUnion(): bool + { + return $this->kind === '|'; + } + + + /** + * Returns true whether it is an intersection type. + */ + public function isIntersection(): bool + { + return $this->kind === '&'; + } + + + /** + * Returns true whether it is a simple type. Single nullable types are also considered to be simple types. + */ + public function isSimple(): bool + { + return $this->simple; + } + + + /** @deprecated use isSimple() */ + public function isSingle(): bool + { + return $this->simple; + } + + + /** + * Returns true whether the type is both a simple and a PHP built-in type. + */ + public function isBuiltin(): bool + { + return $this->simple && Validators::isBuiltinType($this->types[0]); + } + + + /** + * Returns true whether the type is both a simple and a class name. + */ + public function isClass(): bool + { + return $this->simple && !Validators::isBuiltinType($this->types[0]); + } + + + /** + * Determines if type is special class name self/parent/static. + */ + public function isClassKeyword(): bool + { + return $this->simple && Validators::isClassKeyword($this->types[0]); + } + + + /** + * Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter. + */ + public function allows(string $subtype): bool + { + if ($this->types === ['mixed']) { + return true; + } + + $subtype = self::fromString($subtype); + return $subtype->isUnion() + ? Arrays::every($subtype->types, fn($t) => $this->allows2($t instanceof self ? $t->types : [$t])) + : $this->allows2($subtype->types); + } + + + private function allows2(array $subtypes): bool + { + return $this->isUnion() + ? Arrays::some($this->types, fn($t) => $this->allows3($t instanceof self ? $t->types : [$t], $subtypes)) + : $this->allows3($this->types, $subtypes); + } + + + private function allows3(array $types, array $subtypes): bool + { + return Arrays::every( + $types, + fn($type) => Arrays::some( + $subtypes, + fn($subtype) => Validators::isBuiltinType($type) + ? strcasecmp($type, $subtype) === 0 + : is_a($subtype, $type, allow_string: true), + ), + ); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php new file mode 100644 index 00000000000..61ccf091ad1 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php @@ -0,0 +1,416 @@ + 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1, + 'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1, + 'never' => 1, 'true' => 1, + ]; + + /** @var array */ + protected static $validators = [ + // PHP types + 'array' => 'is_array', + 'bool' => 'is_bool', + 'boolean' => 'is_bool', + 'float' => 'is_float', + 'int' => 'is_int', + 'integer' => 'is_int', + 'null' => 'is_null', + 'object' => 'is_object', + 'resource' => 'is_resource', + 'scalar' => 'is_scalar', + 'string' => 'is_string', + + // pseudo-types + 'callable' => [self::class, 'isCallable'], + 'iterable' => 'is_iterable', + 'list' => [Arrays::class, 'isList'], + 'mixed' => [self::class, 'isMixed'], + 'none' => [self::class, 'isNone'], + 'number' => [self::class, 'isNumber'], + 'numeric' => [self::class, 'isNumeric'], + 'numericint' => [self::class, 'isNumericInt'], + + // string patterns + 'alnum' => 'ctype_alnum', + 'alpha' => 'ctype_alpha', + 'digit' => 'ctype_digit', + 'lower' => 'ctype_lower', + 'pattern' => null, + 'space' => 'ctype_space', + 'unicode' => [self::class, 'isUnicode'], + 'upper' => 'ctype_upper', + 'xdigit' => 'ctype_xdigit', + + // syntax validation + 'email' => [self::class, 'isEmail'], + 'identifier' => [self::class, 'isPhpIdentifier'], + 'uri' => [self::class, 'isUri'], + 'url' => [self::class, 'isUrl'], + + // environment validation + 'class' => 'class_exists', + 'interface' => 'interface_exists', + 'directory' => 'is_dir', + 'file' => 'is_file', + 'type' => [self::class, 'isType'], + ]; + + /** @var array */ + protected static $counters = [ + 'string' => 'strlen', + 'unicode' => [Strings::class, 'length'], + 'array' => 'count', + 'list' => 'count', + 'alnum' => 'strlen', + 'alpha' => 'strlen', + 'digit' => 'strlen', + 'lower' => 'strlen', + 'space' => 'strlen', + 'upper' => 'strlen', + 'xdigit' => 'strlen', + ]; + + + /** + * Verifies that the value is of expected types separated by pipe. + * @throws AssertionException + */ + public static function assert(mixed $value, string $expected, string $label = 'variable'): void + { + if (!static::is($value, $expected)) { + $expected = str_replace(['|', ':'], [' or ', ' in range '], $expected); + $translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null']; + $type = $translate[gettype($value)] ?? gettype($value); + if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) { + $type .= ' ' . var_export($value, return: true); + } elseif (is_object($value)) { + $type .= ' ' . $value::class; + } + + throw new AssertionException("The $label expects to be $expected, $type given."); + } + } + + + /** + * Verifies that element $key in array is of expected types separated by pipe. + * @param mixed[] $array + * @throws AssertionException + */ + public static function assertField( + array $array, + $key, + ?string $expected = null, + string $label = "item '%' in array", + ): void + { + if (!array_key_exists($key, $array)) { + throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.'); + + } elseif ($expected) { + static::assert($array[$key], $expected, str_replace('%', $key, $label)); + } + } + + + /** + * Verifies that the value is of expected types separated by pipe. + */ + public static function is(mixed $value, string $expected): bool + { + foreach (explode('|', $expected) as $item) { + if (str_ends_with($item, '[]')) { + if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) { + return true; + } + + continue; + } elseif (str_starts_with($item, '?')) { + $item = substr($item, 1); + if ($value === null) { + return true; + } + } + + [$type] = $item = explode(':', $item, 2); + if (isset(static::$validators[$type])) { + try { + if (!static::$validators[$type]($value)) { + continue; + } + } catch (\TypeError $e) { + continue; + } + } elseif ($type === 'pattern') { + if (Strings::match($value, '|^' . ($item[1] ?? '') . '$|D')) { + return true; + } + + continue; + } elseif (!$value instanceof $type) { + continue; + } + + if (isset($item[1])) { + $length = $value; + if (isset(static::$counters[$type])) { + $length = static::$counters[$type]($value); + } + + $range = explode('..', $item[1]); + if (!isset($range[1])) { + $range[1] = $range[0]; + } + + if (($range[0] !== '' && $length < $range[0]) || ($range[1] !== '' && $length > $range[1])) { + continue; + } + } + + return true; + } + + return false; + } + + + /** + * Finds whether all values are of expected types separated by pipe. + * @param mixed[] $values + */ + public static function everyIs(iterable $values, string $expected): bool + { + foreach ($values as $value) { + if (!static::is($value, $expected)) { + return false; + } + } + + return true; + } + + + /** + * Checks if the value is an integer or a float. + * @return ($value is int|float ? true : false) + */ + public static function isNumber(mixed $value): bool + { + return is_int($value) || is_float($value); + } + + + /** + * Checks if the value is an integer or a integer written in a string. + * @return ($value is non-empty-string ? bool : ($value is int ? true : false)) + */ + public static function isNumericInt(mixed $value): bool + { + return is_int($value) || (is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value)); + } + + + /** + * Checks if the value is a number or a number written in a string. + * @return ($value is non-empty-string ? bool : ($value is int|float ? true : false)) + */ + public static function isNumeric(mixed $value): bool + { + return is_float($value) || is_int($value) || (is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value)); + } + + + /** + * Checks if the value is a syntactically correct callback. + */ + public static function isCallable(mixed $value): bool + { + return $value && is_callable($value, syntax_only: true); + } + + + /** + * Checks if the value is a valid UTF-8 string. + */ + public static function isUnicode(mixed $value): bool + { + return is_string($value) && preg_match('##u', $value); + } + + + /** + * Checks if the value is 0, '', false or null. + * @return ($value is 0|''|false|null ? true : false) + */ + public static function isNone(mixed $value): bool + { + return $value == null; // intentionally == + } + + + /** @internal */ + public static function isMixed(): bool + { + return true; + } + + + /** + * Checks if a variable is a zero-based integer indexed array. + * @deprecated use Nette\Utils\Arrays::isList + * @return ($value is list ? true : false) + */ + public static function isList(mixed $value): bool + { + return Arrays::isList($value); + } + + + /** + * Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null). + * Numbers, strings and DateTime objects can be compared. + */ + public static function isInRange(mixed $value, array $range): bool + { + if ($value === null || !(isset($range[0]) || isset($range[1]))) { + return false; + } + + $limit = $range[0] ?? $range[1]; + if (is_string($limit)) { + $value = (string) $value; + } elseif ($limit instanceof \DateTimeInterface) { + if (!$value instanceof \DateTimeInterface) { + return false; + } + } elseif (is_numeric($value)) { + $value *= 1; + } else { + return false; + } + + return (!isset($range[0]) || ($value >= $range[0])) && (!isset($range[1]) || ($value <= $range[1])); + } + + + /** + * Checks if the value is a valid email address. It does not verify that the domain actually exists, only the syntax is verified. + */ + public static function isEmail(string $value): bool + { + $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part + $alpha = "a-z\x80-\xFF"; // superset of IDN + return (bool) preg_match(<< \\? (? [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) | + (? (?&type) (& (?&type))+ ) | + (? (?&type) | \( (?&intersection) \) ) (\| (?&upart))+ + )$~xAD + XX, $type); + } +} diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/exceptions.php b/tools/.phpstan/vendor/nette/utils/src/Utils/exceptions.php new file mode 100644 index 00000000000..30805ea3c48 --- /dev/null +++ b/tools/.phpstan/vendor/nette/utils/src/Utils/exceptions.php @@ -0,0 +1,50 @@ + + array ( + 'install_path' => '/Users/sb/Work/OpenSource/phpunit/tools/.phpstan/vendor/ergebnis/phpstan-rules', + 'relative_install_path' => '../../../ergebnis/phpstan-rules', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'rules.neon', + ), + ), + 'version' => '2.8.0', + 'phpstanVersionConstraint' => '>=2.0.0.0-dev, <3.0.0.0-dev', + ), + 'phpstan/phpstan-strict-rules' => + array ( + 'install_path' => '/Users/sb/Work/OpenSource/phpunit/tools/.phpstan/vendor/phpstan/phpstan-strict-rules', + 'relative_install_path' => '../../phpstan-strict-rules', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'rules.neon', + ), + ), + 'version' => '2.0.4', + 'phpstanVersionConstraint' => '>=2.0.4.0-dev, <3.0.0.0-dev', + ), + 'tomasvotruba/type-coverage' => + array ( + 'install_path' => '/Users/sb/Work/OpenSource/phpunit/tools/.phpstan/vendor/tomasvotruba/type-coverage', + 'relative_install_path' => '../../../tomasvotruba/type-coverage', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'config/extension.neon', + ), + ), + 'version' => '2.0.2', + 'phpstanVersionConstraint' => '>=2.0.0.0-dev, <3.0.0.0-dev', + ), +); + + public const NOT_INSTALLED = array ( +); + + /** @var string|null */ + public const PHPSTAN_VERSION_CONSTRAINT = '>=2.0.4.0-dev, <3.0.0.0-dev'; + + private function __construct() + { + } + +} diff --git a/tools/.phpstan/vendor/phpstan/extension-installer/src/Plugin.php b/tools/.phpstan/vendor/phpstan/extension-installer/src/Plugin.php new file mode 100644 index 00000000000..ec757351f91 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/extension-installer/src/Plugin.php @@ -0,0 +1,228 @@ + + */ + public static function getSubscribedEvents(): array + { + return [ + ScriptEvents::POST_INSTALL_CMD => 'process', + ScriptEvents::POST_UPDATE_CMD => 'process', + ]; + } + + public function process(Event $event): void + { + $io = $event->getIO(); + + if (!file_exists(__DIR__)) { + $io->write('phpstan/extension-installer: Package not found (probably scheduled for removal); extensions installation skipped.'); + return; + } + + $composer = $event->getComposer(); + $installationManager = $composer->getInstallationManager(); + + $generatedConfigFilePath = __DIR__ . '/GeneratedConfig.php'; + $oldGeneratedConfigFileHash = null; + if (is_file($generatedConfigFilePath)) { + $oldGeneratedConfigFileHash = md5_file($generatedConfigFilePath); + } + $notInstalledPackages = []; + $installedPackages = []; + $ignoredPackages = []; + + $data = []; + $fs = new Filesystem(); + $ignore = []; + + $packageExtra = $composer->getPackage()->getExtra(); + + if (isset($packageExtra['phpstan/extension-installer']['ignore'])) { + $ignore = $packageExtra['phpstan/extension-installer']['ignore']; + } + + $phpstanVersionConstraints = []; + + foreach ($composer->getRepositoryManager()->getLocalRepository()->getPackages() as $package) { + if ( + $package->getType() !== 'phpstan-extension' + && !isset($package->getExtra()['phpstan']) + ) { + if ( + strpos($package->getName(), 'phpstan') !== false + && !in_array($package->getName(), [ + 'phpstan/phpstan', + 'phpstan/phpstan-shim', + 'phpstan/phpdoc-parser', + 'phpstan/extension-installer', + ], true) + ) { + $notInstalledPackages[$package->getName()] = $package->getFullPrettyVersion(); + } + continue; + } + + if (in_array($package->getName(), $ignore, true)) { + $ignoredPackages[] = $package->getName(); + continue; + } + + $installPath = $installationManager->getInstallPath($package); + if ($installPath === null) { + continue; + } + + $absoluteInstallPath = $fs->isAbsolutePath($installPath) + ? $installPath + : getcwd() . DIRECTORY_SEPARATOR . $installPath; + + $packageRequires = $package->getRequires(); + $phpstanConstraint = null; + if (array_key_exists('phpstan/phpstan', $packageRequires)) { + $phpstanConstraint = $packageRequires['phpstan/phpstan']->getConstraint(); + if ($phpstanConstraint->getLowerBound()->isZero()) { + continue; + } + if ($phpstanConstraint->getUpperBound()->isPositiveInfinity()) { + continue; + } + $phpstanVersionConstraints[] = $phpstanConstraint; + } + + $data[$package->getName()] = [ + 'install_path' => $absoluteInstallPath, + 'relative_install_path' => $fs->findShortestPath(dirname($generatedConfigFilePath), $absoluteInstallPath, true), + 'extra' => $package->getExtra()['phpstan'] ?? null, + 'version' => $package->getFullPrettyVersion(), + 'phpstanVersionConstraint' => $phpstanConstraint !== null ? $this->constraintIntoString($phpstanConstraint) : null, + ]; + + $installedPackages[$package->getName()] = true; + } + + $phpstanVersionConstraint = null; + if (count($phpstanVersionConstraints) > 0 && class_exists(Intervals::class)) { + if (count($phpstanVersionConstraints) === 1) { + $multiConstraint = $phpstanVersionConstraints[0]; + } else { + $multiConstraint = new MultiConstraint($phpstanVersionConstraints); + } + $phpstanVersionConstraint = $this->constraintIntoString(Intervals::compactConstraint($multiConstraint)); + } + + ksort($data); + ksort($installedPackages); + ksort($notInstalledPackages); + sort($ignoredPackages); + + $generatedConfigFileContents = sprintf(self::$generatedFileTemplate, var_export($data, true), var_export($notInstalledPackages, true), var_export($phpstanVersionConstraint, true)); + file_put_contents($generatedConfigFilePath, $generatedConfigFileContents); + $io->write('phpstan/extension-installer: Extensions installed'); + + if ($oldGeneratedConfigFileHash === md5($generatedConfigFileContents)) { + return; + } + + foreach (array_keys($installedPackages) as $name) { + $io->write(sprintf('> %s: installed', $name)); + } + + foreach (array_keys($notInstalledPackages) as $name) { + $io->write(sprintf('> %s: not supported', $name)); + } + + foreach ($ignoredPackages as $name) { + $io->write(sprintf('> %s: ignored', $name)); + } + } + + private function constraintIntoString(ConstraintInterface $constraint): string + { + return sprintf( + '%s%s, %s%s', + $constraint->getLowerBound()->isInclusive() ? '>=' : '>', + $constraint->getLowerBound()->getVersion(), + $constraint->getUpperBound()->isInclusive() ? '<=' : '<', + $constraint->getUpperBound()->getVersion() + ); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/.editorconfig b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/.editorconfig new file mode 100644 index 00000000000..5d66bc427b8 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +[*.{php,phpt}] +indent_style = tab +indent_size = 4 + +[*.xml] +indent_style = tab +indent_size = 4 + +[*.neon] +indent_style = tab +indent_size = 4 + +[*.{yaml,yml}] +indent_style = space +indent_size = 2 + +[composer.json] +indent_style = tab +indent_size = 4 diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/LICENSE b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/LICENSE new file mode 100644 index 00000000000..52fba1e23bb --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2016 Ondřej Mirtes +Copyright (c) 2025 PHPStan s.r.o. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md new file mode 100644 index 00000000000..88b7e96ff21 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md @@ -0,0 +1,110 @@ +# Extra strict and opinionated rules for PHPStan + +[![Build](https://github.com/phpstan/phpstan-strict-rules/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-strict-rules/actions) +[![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-strict-rules/v/stable)](https://packagist.org/packages/phpstan/phpstan-strict-rules) +[![License](https://poser.pugx.org/phpstan/phpstan-strict-rules/license)](https://packagist.org/packages/phpstan/phpstan-strict-rules) + +[PHPStan](https://phpstan.org/) focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming: + +* Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `||`. +* Require booleans in `while` and `do while` loop conditions. +* Require numeric operands or arrays in `+` and numeric operands in `-`/`*`/`/`/`**`/`%`. +* Require numeric operand in `$var++`, `$var--`, `++$var`and `--$var`. +* These functions contain a `$strict` parameter for better type safety, it must be set to `true`: + * `in_array` (3rd parameter) + * `array_search` (3rd parameter) + * `array_keys` (3rd parameter; only if the 2nd parameter `$search_value` is provided) + * `base64_decode` (2nd parameter) +* Variables assigned in `while` loop condition and `for` loop initial assignment cannot be used after the loop. +* Variables set in foreach that's always looped thanks to non-empty arrays cannot be used after the loop. +* Types in `switch` condition and `case` value must match. PHP compares them loosely by default and that can lead to unexpected results. +* Check that statically declared methods are called statically. +* Disallow `empty()` - it's a very loose comparison (see [manual](https://php.net/empty)), it's recommended to use more strict one. +* Disallow short ternary operator (`?:`) - implies weak comparison, it's recommended to use null coalesce operator (`??`) or ternary operator with strict condition. +* Disallow variable variables (`$$foo`, `$this->$method()` etc.) +* Disallow overwriting variables with foreach key and value variables +* Always true `instanceof`, type-checking `is_*` functions and strict comparisons `===`/`!==`. These checks can be turned off by setting `checkAlwaysTrueInstanceof`/`checkAlwaysTrueCheckTypeFunctionCall`/`checkAlwaysTrueStrictComparison` to false. +* Correct case for referenced and called function names. +* Correct case for inherited and implemented method names. +* Contravariance for parameter types and covariance for return types in inherited methods (also known as Liskov substitution principle - LSP) +* Check LSP even for static methods +* Require calling parent constructor +* Disallow usage of backtick operator (`` $ls = `ls -la` ``) +* Closure should use `$this` directly instead of using `$this` variable indirectly + +Additional rules are coming in subsequent releases! + + +## Installation + +To use this extension, require it in [Composer](https://getcomposer.org/): + +``` +composer require --dev phpstan/phpstan-strict-rules +``` + +If you also install [phpstan/extension-installer](https://github.com/phpstan/extension-installer) then you're all set! + +
+ Manual installation + +If you don't want to use `phpstan/extension-installer`, include rules.neon in your project's PHPStan config: + +``` +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon +``` +
+ +## Disabling rules + +You can disable rules using configuration parameters: + +```neon +parameters: + strictRules: + disallowedLooseComparison: false + booleansInConditions: false + booleansInLoopConditions: false + uselessCast: false + requireParentConstructorCall: false + disallowedBacktick: false + disallowedEmpty: false + disallowedImplicitArrayCreation: false + disallowedShortTernary: false + overwriteVariablesWithLoop: false + closureUsesThis: false + matchingInheritedMethodNames: false + numericOperandsInArithmeticOperators: false + strictFunctionCalls: false + dynamicCallOnStaticMethod: false + switchConditionsMatchingType: false + noVariableVariables: false + strictArrayFilter: false + illegalConstructorMethodCall: false +``` + +Aside from introducing new custom rules, phpstan-strict-rules also [change the default values of some configuration parameters](https://github.com/phpstan/phpstan-strict-rules/blob/1.6.x/rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis). + +## Enabling rules one-by-one + +If you don't want to start using all the available strict rules at once but only one or two, you can! + +You can disable all rules from the included `rules.neon` with: + +```neon +parameters: + strictRules: + allRules: false +``` + +Then you can re-enable individual rules with configuration parameters: + +```neon +parameters: + strictRules: + allRules: false + booleansInConditions: true +``` + +Even with `strictRules.allRules` set to `false`, part of this package is still in effect. That's because phpstan-strict-rules also [change the default values of some configuration parameters](https://github.com/phpstan/phpstan-strict-rules/blob/1.6.x/rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis). diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json new file mode 100644 index 00000000000..2bbc44d6976 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json @@ -0,0 +1,43 @@ +{ + "name": "phpstan/phpstan-strict-rules", + "type": "phpstan-extension", + "description": "Extra strict and opinionated rules for PHPStan", + "license": [ + "MIT" + ], + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "config": { + "platform": { + "php": "7.4.6" + }, + "sort-packages": true + }, + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "autoload-dev": { + "classmap": [ + "tests/" + ] + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon new file mode 100644 index 00000000000..dd20b8d17ee --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon @@ -0,0 +1,291 @@ +parameters: + strictRulesInstalled: true + polluteScopeWithLoopInitialAssignments: false + polluteScopeWithAlwaysIterableForeach: false + polluteScopeWithBlock: false + checkDynamicProperties: true + checkExplicitMixedMissingReturn: true + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true + reportMaybesInMethodSignatures: true + reportStaticMethodSignatures: true + reportMaybesInPropertyPhpDocTypes: true + reportWrongPhpDocTypeInVarTag: true + strictRules: + allRules: true + disallowedLooseComparison: %strictRules.allRules% + booleansInConditions: %strictRules.allRules% + booleansInLoopConditions: [%strictRules.allRules%, %featureToggles.bleedingEdge%] + uselessCast: %strictRules.allRules% + requireParentConstructorCall: %strictRules.allRules% + disallowedBacktick: %strictRules.allRules% + disallowedEmpty: %strictRules.allRules% + disallowedImplicitArrayCreation: %strictRules.allRules% + disallowedShortTernary: %strictRules.allRules% + overwriteVariablesWithLoop: %strictRules.allRules% + closureUsesThis: %strictRules.allRules% + matchingInheritedMethodNames: %strictRules.allRules% + numericOperandsInArithmeticOperators: %strictRules.allRules% + strictFunctionCalls: %strictRules.allRules% + dynamicCallOnStaticMethod: %strictRules.allRules% + switchConditionsMatchingType: %strictRules.allRules% + noVariableVariables: %strictRules.allRules% + strictArrayFilter: %strictRules.allRules% + illegalConstructorMethodCall: %strictRules.allRules% + +parametersSchema: + strictRules: structure([ + allRules: anyOf(bool(), arrayOf(bool())), + disallowedLooseComparison: anyOf(bool(), arrayOf(bool())), + booleansInConditions: anyOf(bool(), arrayOf(bool())) + booleansInLoopConditions: anyOf(bool(), arrayOf(bool())) + uselessCast: anyOf(bool(), arrayOf(bool())) + requireParentConstructorCall: anyOf(bool(), arrayOf(bool())) + disallowedBacktick: anyOf(bool(), arrayOf(bool())) + disallowedEmpty: anyOf(bool(), arrayOf(bool())) + disallowedImplicitArrayCreation: anyOf(bool(), arrayOf(bool())) + disallowedShortTernary: anyOf(bool(), arrayOf(bool())) + overwriteVariablesWithLoop: anyOf(bool(), arrayOf(bool())) + closureUsesThis: anyOf(bool(), arrayOf(bool())) + matchingInheritedMethodNames: anyOf(bool(), arrayOf(bool())) + numericOperandsInArithmeticOperators: anyOf(bool(), arrayOf(bool())) + strictFunctionCalls: anyOf(bool(), arrayOf(bool())) + dynamicCallOnStaticMethod: anyOf(bool(), arrayOf(bool())) + switchConditionsMatchingType: anyOf(bool(), arrayOf(bool())) + noVariableVariables: anyOf(bool(), arrayOf(bool())) + strictArrayFilter: anyOf(bool(), arrayOf(bool())) + illegalConstructorMethodCall: anyOf(bool(), arrayOf(bool())) + ]) + +conditionalTags: + PHPStan\Rules\DisallowedConstructs\DisallowedLooseComparisonRule: + phpstan.rules.rule: %strictRules.disallowedLooseComparison% + PHPStan\Rules\BooleansInConditions\BooleanInBooleanAndRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInDoWhileConditionRule: + phpstan.rules.rule: %strictRules.booleansInLoopConditions% + PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule: + phpstan.rules.rule: %strictRules.booleansInConditions% + PHPStan\Rules\BooleansInConditions\BooleanInWhileConditionRule: + phpstan.rules.rule: %strictRules.booleansInLoopConditions% + PHPStan\Rules\Cast\UselessCastRule: + phpstan.rules.rule: %strictRules.uselessCast% + PHPStan\Rules\Classes\RequireParentConstructCallRule: + phpstan.rules.rule: %strictRules.requireParentConstructorCall% + PHPStan\Rules\DisallowedConstructs\DisallowedBacktickRule: + phpstan.rules.rule: %strictRules.disallowedBacktick% + PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule: + phpstan.rules.rule: %strictRules.disallowedEmpty% + PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule: + phpstan.rules.rule: %strictRules.disallowedImplicitArrayCreation% + PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule: + phpstan.rules.rule: %strictRules.disallowedShortTernary% + PHPStan\Rules\ForeachLoop\OverwriteVariablesWithForeachRule: + phpstan.rules.rule: %strictRules.overwriteVariablesWithLoop% + PHPStan\Rules\ForLoop\OverwriteVariablesWithForLoopInitRule: + phpstan.rules.rule: %strictRules.overwriteVariablesWithLoop% + PHPStan\Rules\Functions\ArrayFilterStrictRule: + phpstan.rules.rule: %strictRules.strictArrayFilter% + PHPStan\Rules\Functions\ClosureUsesThisRule: + phpstan.rules.rule: %strictRules.closureUsesThis% + PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule: + phpstan.rules.rule: %strictRules.matchingInheritedMethodNames% + PHPStan\Rules\Operators\OperandInArithmeticPostDecrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticPostIncrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticPreDecrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticExponentiationRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticModuloRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticMultiplicationRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\Operators\OperandsInArithmeticSubtractionRule: + phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators% + PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule: + phpstan.rules.rule: %strictRules.dynamicCallOnStaticMethod% + PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsCallableRule: + phpstan.rules.rule: %strictRules.dynamicCallOnStaticMethod% + PHPStan\Rules\StrictCalls\StrictFunctionCallsRule: + phpstan.rules.rule: %strictRules.strictFunctionCalls% + PHPStan\Rules\SwitchConditions\MatchingTypeInSwitchCaseConditionRule: + phpstan.rules.rule: %strictRules.switchConditionsMatchingType% + PHPStan\Rules\VariableVariables\VariableMethodCallRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableMethodCallableRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableStaticMethodCallRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableStaticMethodCallableRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableStaticPropertyFetchRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariableVariablesRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\VariableVariables\VariablePropertyFetchRule: + phpstan.rules.rule: %strictRules.noVariableVariables% + PHPStan\Rules\Methods\IllegalConstructorMethodCallRule: + phpstan.rules.rule: %strictRules.illegalConstructorMethodCall% + PHPStan\Rules\Methods\IllegalConstructorStaticCallRule: + phpstan.rules.rule: %strictRules.illegalConstructorMethodCall% + +services: + - + class: PHPStan\Rules\BooleansInConditions\BooleanRuleHelper + + - + class: PHPStan\Rules\Operators\OperatorRuleHelper + + - + class: PHPStan\Rules\VariableVariables\VariablePropertyFetchRule + arguments: + universalObjectCratesClasses: %universalObjectCratesClasses% + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedLooseComparisonRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanAndRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInDoWhileConditionRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule + + - + class: PHPStan\Rules\BooleansInConditions\BooleanInWhileConditionRule + + - + class: PHPStan\Rules\Cast\UselessCastRule + arguments: + treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain% + treatPhpDocTypesAsCertainTip: %tips.treatPhpDocTypesAsCertain% + + - + class: PHPStan\Rules\Classes\RequireParentConstructCallRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedBacktickRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule + + - + class: PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule + + - + class: PHPStan\Rules\ForeachLoop\OverwriteVariablesWithForeachRule + + - + class: PHPStan\Rules\ForLoop\OverwriteVariablesWithForLoopInitRule + + - + class: PHPStan\Rules\Functions\ArrayFilterStrictRule + arguments: + treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain% + checkNullables: %checkNullables% + treatPhpDocTypesAsCertainTip: %tips.treatPhpDocTypesAsCertain% + + - + class: PHPStan\Rules\Functions\ClosureUsesThisRule + + - + class: PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule + + - + class: PHPStan\Rules\Methods\IllegalConstructorMethodCallRule + + - + class: PHPStan\Rules\Methods\IllegalConstructorStaticCallRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPostDecrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPostIncrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPreDecrementRule + + - + class: PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticExponentiationRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticModuloRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticMultiplicationRule + + - + class: PHPStan\Rules\Operators\OperandsInArithmeticSubtractionRule + + - + class: PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule + + - + class: PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsCallableRule + + - + class: PHPStan\Rules\StrictCalls\StrictFunctionCallsRule + + - + class: PHPStan\Rules\SwitchConditions\MatchingTypeInSwitchCaseConditionRule + + - + class: PHPStan\Rules\VariableVariables\VariableMethodCallRule + + - + class: PHPStan\Rules\VariableVariables\VariableMethodCallableRule + + - + class: PHPStan\Rules\VariableVariables\VariableStaticMethodCallRule + + - + class: PHPStan\Rules\VariableVariables\VariableStaticMethodCallableRule + + - + class: PHPStan\Rules\VariableVariables\VariableStaticPropertyFetchRule + + - + class: PHPStan\Rules\VariableVariables\VariableVariablesRule diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanAndRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanAndRule.php new file mode 100644 index 00000000000..eed19aeac0e --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanAndRule.php @@ -0,0 +1,59 @@ + + */ +class BooleanInBooleanAndRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return BooleanAndNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $originalNode = $node->getOriginalNode(); + $messages = []; + $nodeText = $originalNode->getOperatorSigil(); + $identifierType = $originalNode instanceof Node\Expr\BinaryOp\BooleanAnd ? 'booleanAnd' : 'logicalAnd'; + if (!$this->helper->passesAsBoolean($scope, $originalNode->left)) { + $leftType = $scope->getType($originalNode->left); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the left side.', + $nodeText, + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.leftNotBoolean', $identifierType))->build(); + } + + $rightScope = $node->getRightScope(); + if (!$this->helper->passesAsBoolean($rightScope, $originalNode->right)) { + $rightType = $rightScope->getType($originalNode->right); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the right side.', + $nodeText, + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.rightNotBoolean', $identifierType))->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanNotRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanNotRule.php new file mode 100644 index 00000000000..5187cf57bdf --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanNotRule.php @@ -0,0 +1,47 @@ + + */ +class BooleanInBooleanNotRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return BooleanNot::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->expr)) { + return []; + } + + $expressionType = $scope->getType($node->expr); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a negated boolean, %s given.', + $expressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('booleanNot.exprNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanOrRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanOrRule.php new file mode 100644 index 00000000000..cb06a34162d --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInBooleanOrRule.php @@ -0,0 +1,59 @@ + + */ +class BooleanInBooleanOrRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return BooleanOrNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $originalNode = $node->getOriginalNode(); + $messages = []; + $nodeText = $originalNode->getOperatorSigil(); + $identifierType = $originalNode instanceof Node\Expr\BinaryOp\BooleanOr ? 'booleanOr' : 'logicalOr'; + if (!$this->helper->passesAsBoolean($scope, $originalNode->left)) { + $leftType = $scope->getType($originalNode->left); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the left side.', + $nodeText, + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.leftNotBoolean', $identifierType))->build(); + } + + $rightScope = $node->getRightScope(); + if (!$this->helper->passesAsBoolean($rightScope, $originalNode->right)) { + $rightType = $rightScope->getType($originalNode->right); + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in %s, %s given on the right side.', + $nodeText, + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.rightNotBoolean', $identifierType))->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInDoWhileConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInDoWhileConditionRule.php new file mode 100644 index 00000000000..d0db29629c0 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInDoWhileConditionRule.php @@ -0,0 +1,46 @@ + + */ +class BooleanInDoWhileConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Node\Stmt\Do_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a do-while condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('doWhile.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInElseIfConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInElseIfConditionRule.php new file mode 100644 index 00000000000..550e9857da3 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInElseIfConditionRule.php @@ -0,0 +1,47 @@ + + */ +class BooleanInElseIfConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return ElseIf_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in an elseif condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('elseif.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php new file mode 100644 index 00000000000..5c08894b4eb --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInIfConditionRule.php @@ -0,0 +1,47 @@ + + */ +class BooleanInIfConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return If_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in an if condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('if.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInTernaryOperatorRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInTernaryOperatorRule.php new file mode 100644 index 00000000000..4fe855a5ce0 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInTernaryOperatorRule.php @@ -0,0 +1,51 @@ + + */ +class BooleanInTernaryOperatorRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Ternary::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->if === null) { + return []; // elvis ?: + } + + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a ternary operator condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('ternary.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInWhileConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInWhileConditionRule.php new file mode 100644 index 00000000000..2f1661a63fa --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanInWhileConditionRule.php @@ -0,0 +1,46 @@ + + */ +class BooleanInWhileConditionRule implements Rule +{ + + private BooleanRuleHelper $helper; + + public function __construct(BooleanRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Node\Stmt\While_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($this->helper->passesAsBoolean($scope, $node->cond)) { + return []; + } + + $conditionExpressionType = $scope->getType($node->cond); + + return [ + RuleErrorBuilder::message(sprintf( + 'Only booleans are allowed in a while condition, %s given.', + $conditionExpressionType->describe(VerbosityLevel::typeOnly()), + ))->identifier('while.condNotBoolean')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php new file mode 100644 index 00000000000..4ecba329926 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/BooleansInConditions/BooleanRuleHelper.php @@ -0,0 +1,42 @@ +ruleLevelHelper = $ruleLevelHelper; + } + + public function passesAsBoolean(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return !$type->isExplicitMixed(); + } + $typeToCheck = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $expr, + '', + static fn (Type $type): bool => $type->isBoolean()->yes(), + ); + $foundType = $typeToCheck->getType(); + if ($foundType instanceof ErrorType) { + return true; + } + + return $foundType->isBoolean()->yes(); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Cast/UselessCastRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Cast/UselessCastRule.php new file mode 100644 index 00000000000..ca8f2268926 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Cast/UselessCastRule.php @@ -0,0 +1,81 @@ + + */ +class UselessCastRule implements Rule +{ + + private bool $treatPhpDocTypesAsCertain; + + private bool $treatPhpDocTypesAsCertainTip; + + public function __construct( + bool $treatPhpDocTypesAsCertain, + bool $treatPhpDocTypesAsCertainTip + ) + { + $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain; + $this->treatPhpDocTypesAsCertainTip = $treatPhpDocTypesAsCertainTip; + } + + public function getNodeType(): string + { + return Cast::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $castType = $scope->getType($node); + if ($castType instanceof ErrorType) { + return []; + } + $castType = $castType->generalize(GeneralizePrecision::lessSpecific()); + + if ($this->treatPhpDocTypesAsCertain) { + $expressionType = $scope->getType($node->expr); + } else { + $expressionType = $scope->getNativeType($node->expr); + } + if ($castType->isSuperTypeOf($expressionType)->yes()) { + $addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node, $castType): RuleErrorBuilder { + if (!$this->treatPhpDocTypesAsCertain) { + return $ruleErrorBuilder; + } + + $expressionTypeWithoutPhpDoc = $scope->getNativeType($node->expr); + if ($castType->isSuperTypeOf($expressionTypeWithoutPhpDoc)->yes()) { + return $ruleErrorBuilder; + } + + if (!$this->treatPhpDocTypesAsCertainTip) { + return $ruleErrorBuilder; + } + + return $ruleErrorBuilder->treatPhpDocTypesAsCertainTip(); + }; + return [ + $addTip(RuleErrorBuilder::message(sprintf( + 'Casting to %s something that\'s already %s.', + $castType->describe(VerbosityLevel::typeOnly()), + $expressionType->describe(VerbosityLevel::typeOnly()), + )))->identifier('cast.useless')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php new file mode 100644 index 00000000000..77595810a10 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php @@ -0,0 +1,142 @@ + + */ +class RequireParentConstructCallRule implements Rule +{ + + public function getNodeType(): string + { + return ClassMethod::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$scope->isInClass()) { + throw new ShouldNotHappenException(); + } + + if ($scope->isInTrait()) { + return []; + } + + if ($node->name->name !== '__construct') { + return []; + } + + if ($node->isAbstract()) { + return []; + } + + $classReflection = $scope->getClassReflection()->getNativeReflection(); + if ($classReflection->isInterface() || $classReflection->isAnonymous()) { + return []; + } + + if ($this->callsParentConstruct($node)) { + return []; + } + + $parentClass = $this->getParentConstructorClass($classReflection); + if ($parentClass !== false) { + return [ + RuleErrorBuilder::message(sprintf( + '%s::__construct() does not call parent constructor from %s.', + $classReflection->getName(), + $parentClass->getName(), + ))->identifier('constructor.missingParentCall')->build(), + ]; + } + + return []; + } + + private function callsParentConstruct(Node $parserNode): bool + { + if (!property_exists($parserNode, 'stmts')) { + return false; + } + + foreach ($parserNode->stmts as $statement) { + if ($statement instanceof Node\Stmt\Expression) { + $statement = $statement->expr; + } + + $statement = $this->ignoreErrorSuppression($statement); + if ($statement instanceof StaticCall) { + if ( + $statement->class instanceof Name + && ((string) $statement->class === 'parent') + && $statement->name instanceof Node\Identifier + && $statement->name->name === '__construct' + ) { + return true; + } + } else { + if ($this->callsParentConstruct($statement)) { + return true; + } + } + } + + return false; + } + + /** + * @param ReflectionClass|ReflectionEnum $classReflection + * @return ReflectionClass|false + */ + private function getParentConstructorClass($classReflection) + { + while ($classReflection->getParentClass() !== false) { + $constructor = $classReflection->getParentClass()->hasMethod('__construct') ? $classReflection->getParentClass()->getMethod('__construct') : null; + $constructorWithClassName = $classReflection->getParentClass()->hasMethod($classReflection->getParentClass()->getName()) ? $classReflection->getParentClass()->getMethod($classReflection->getParentClass()->getName()) : null; + if ( + ( + $constructor !== null + && $constructor->getDeclaringClass()->getName() === $classReflection->getParentClass()->getName() + && !$constructor->isAbstract() + && !$constructor->isPrivate() + && !$constructor->isDeprecated() + ) || ( + $constructorWithClassName !== null + && $constructorWithClassName->getDeclaringClass()->getName() === $classReflection->getParentClass()->getName() + && !$constructorWithClassName->isAbstract() + ) + ) { + return $classReflection->getParentClass(); + } + + $classReflection = $classReflection->getParentClass(); + } + + return false; + } + + private function ignoreErrorSuppression(Node $statement): Node + { + if ($statement instanceof Node\Expr\ErrorSuppress) { + + return $statement->expr; + } + + return $statement; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedBacktickRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedBacktickRule.php new file mode 100644 index 00000000000..76e401ceef5 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedBacktickRule.php @@ -0,0 +1,31 @@ + + */ +class DisallowedBacktickRule implements Rule +{ + + public function getNodeType(): string + { + return ShellExec::class; + } + + public function processNode(Node $node, Scope $scope): array + { + return [ + RuleErrorBuilder::message('Backtick operator is not allowed. Use shell_exec() instead.') + ->identifier('backtick.notAllowed') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedEmptyRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedEmptyRule.php new file mode 100644 index 00000000000..d19f5ea20d2 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedEmptyRule.php @@ -0,0 +1,31 @@ + + */ +class DisallowedEmptyRule implements Rule +{ + + public function getNodeType(): string + { + return Empty_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + return [ + RuleErrorBuilder::message('Construct empty() is not allowed. Use more strict comparison.') + ->identifier('empty.notAllowed') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedImplicitArrayCreationRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedImplicitArrayCreationRule.php new file mode 100644 index 00000000000..cee777ce991 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedImplicitArrayCreationRule.php @@ -0,0 +1,65 @@ + + */ +class DisallowedImplicitArrayCreationRule implements Rule +{ + + public function getNodeType(): string + { + return Assign::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->var instanceof ArrayDimFetch) { + return []; + } + + $node = $node->var; + while ($node instanceof ArrayDimFetch) { + $node = $node->var; + } + + if (!$node instanceof Variable) { + return []; + } + + if (!is_string($node->name)) { + return []; + } + + $certainty = $scope->hasVariableType($node->name); + if ($certainty->no()) { + return [ + RuleErrorBuilder::message(sprintf('Implicit array creation is not allowed - variable $%s does not exist.', $node->name)) + ->identifier('variable.implicitArray') + ->build(), + ]; + } + + if ($certainty->maybe()) { + return [ + RuleErrorBuilder::message(sprintf('Implicit array creation is not allowed - variable $%s might not exist.', $node->name)) + ->identifier('variable.implicitArray') + ->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedLooseComparisonRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedLooseComparisonRule.php new file mode 100644 index 00000000000..70b8514f1b8 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedLooseComparisonRule.php @@ -0,0 +1,48 @@ + + */ +class DisallowedLooseComparisonRule implements Rule +{ + + public function getNodeType(): string + { + return BinaryOp::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof Equal) { + return [ + RuleErrorBuilder::message( + 'Loose comparison via "==" is not allowed.', + )->tip('Use strict comparison via "===" instead.') + ->identifier('equal.notAllowed') + ->build(), + ]; + } + if ($node instanceof NotEqual) { + return [ + RuleErrorBuilder::message( + 'Loose comparison via "!=" is not allowed.', + )->tip('Use strict comparison via "!==" instead.') + ->identifier('notEqual.notAllowed') + ->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedShortTernaryRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedShortTernaryRule.php new file mode 100644 index 00000000000..fac42790d15 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/DisallowedConstructs/DisallowedShortTernaryRule.php @@ -0,0 +1,35 @@ + + */ +class DisallowedShortTernaryRule implements Rule +{ + + public function getNodeType(): string + { + return Ternary::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->if !== null) { + return []; + } + + return [ + RuleErrorBuilder::message('Short ternary operator is not allowed. Use null coalesce operator if applicable or consider using long ternary.') + ->identifier('ternary.shortNotAllowed') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForLoop/OverwriteVariablesWithForLoopInitRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForLoop/OverwriteVariablesWithForLoopInitRule.php new file mode 100644 index 00000000000..f710474e2f9 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForLoop/OverwriteVariablesWithForLoopInitRule.php @@ -0,0 +1,77 @@ + + */ +class OverwriteVariablesWithForLoopInitRule implements Rule +{ + + public function getNodeType(): string + { + return For_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $errors = []; + foreach ($node->init as $expr) { + if (!($expr instanceof Assign)) { + continue; + } + + foreach ($this->checkValueVar($scope, $expr->var) as $error) { + $errors[] = $error; + } + } + + return $errors; + } + + /** + * @return list + */ + private function checkValueVar(Scope $scope, Expr $expr): array + { + $errors = []; + if ( + $expr instanceof Node\Expr\Variable + && is_string($expr->name) + && $scope->hasVariableType($expr->name)->yes() + ) { + $errors[] = RuleErrorBuilder::message(sprintf('For loop initial assignment overwrites variable $%s.', $expr->name)) + ->identifier('for.variableOverwrite') + ->build(); + } + + if ( + $expr instanceof Node\Expr\List_ + || $expr instanceof Node\Expr\Array_ + ) { + foreach ($expr->items as $item) { + if ($item === null) { + continue; + } + + foreach ($this->checkValueVar($scope, $item->value) as $error) { + $errors[] = $error; + } + } + } + + return $errors; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForeachLoop/OverwriteVariablesWithForeachRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForeachLoop/OverwriteVariablesWithForeachRule.php new file mode 100644 index 00000000000..0cf620c37ee --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/ForeachLoop/OverwriteVariablesWithForeachRule.php @@ -0,0 +1,80 @@ + + */ +class OverwriteVariablesWithForeachRule implements Rule +{ + + public function getNodeType(): string + { + return Foreach_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $errors = []; + if ( + $node->keyVar instanceof Node\Expr\Variable + && is_string($node->keyVar->name) + && $scope->hasVariableType($node->keyVar->name)->yes() + ) { + $errors[] = RuleErrorBuilder::message(sprintf('Foreach overwrites $%s with its key variable.', $node->keyVar->name)) + ->identifier('foreach.keyOverwrite') + ->build(); + } + + foreach ($this->checkValueVar($scope, $node->valueVar) as $error) { + $errors[] = $error; + } + + return $errors; + } + + /** + * @return list + */ + private function checkValueVar(Scope $scope, Expr $expr): array + { + $errors = []; + if ( + $expr instanceof Node\Expr\Variable + && is_string($expr->name) + && $scope->hasVariableType($expr->name)->yes() + ) { + $errors[] = RuleErrorBuilder::message(sprintf('Foreach overwrites $%s with its value variable.', $expr->name)) + ->identifier('foreach.valueOverwrite') + ->build(); + } + + if ( + $expr instanceof Node\Expr\List_ + || $expr instanceof Node\Expr\Array_ + ) { + foreach ($expr->items as $item) { + if ($item === null) { + continue; + } + + foreach ($this->checkValueVar($scope, $item->value) as $error) { + $errors[] = $error; + } + } + } + + return $errors; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ArrayFilterStrictRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ArrayFilterStrictRule.php new file mode 100644 index 00000000000..6760c7d563e --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ArrayFilterStrictRule.php @@ -0,0 +1,162 @@ + + */ +class ArrayFilterStrictRule implements Rule +{ + + private ReflectionProvider $reflectionProvider; + + private bool $treatPhpDocTypesAsCertain; + + private bool $checkNullables; + + private bool $treatPhpDocTypesAsCertainTip; + + public function __construct( + ReflectionProvider $reflectionProvider, + bool $treatPhpDocTypesAsCertain, + bool $checkNullables, + bool $treatPhpDocTypesAsCertainTip + ) + { + $this->reflectionProvider = $reflectionProvider; + $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain; + $this->checkNullables = $checkNullables; + $this->treatPhpDocTypesAsCertainTip = $treatPhpDocTypesAsCertainTip; + } + + public function getNodeType(): string + { + return FuncCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Name) { + return []; + } + + if (!$this->reflectionProvider->hasFunction($node->name, $scope)) { + return []; + } + + $functionReflection = $this->reflectionProvider->getFunction($node->name, $scope); + + if ($functionReflection->getName() !== 'array_filter') { + return []; + } + + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( + $scope, + $node->getArgs(), + $functionReflection->getVariants(), + $functionReflection->getNamedArgumentsVariants(), + ); + + $normalizedFuncCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); + + if ($normalizedFuncCall === null) { + return []; + } + + $args = $normalizedFuncCall->getArgs(); + if (count($args) === 0) { + return []; + } + + if (count($args) === 1) { + $arrayType = $scope->getType($args[0]->value); + $itemType = $arrayType->getIterableValueType(); + if ($itemType instanceof UnionType) { + $hasTruthy = false; + $hasFalsey = false; + foreach ($itemType->getTypes() as $innerType) { + $booleanType = $innerType->toBoolean(); + if ($booleanType->isTrue()->yes()) { + $hasTruthy = true; + continue; + } + if ($booleanType->isFalse()->yes()) { + $hasFalsey = true; + continue; + } + + $hasTruthy = false; + $hasFalsey = false; + break; + } + + if ($hasTruthy && $hasFalsey) { + return []; + } + } elseif ($itemType->isBoolean()->yes()) { + return []; + } elseif ($itemType->isArray()->yes()) { + return []; + } + + return [ + RuleErrorBuilder::message('Call to function array_filter() requires parameter #2 to be passed to avoid loose comparison semantics.') + ->identifier('arrayFilter.strict') + ->build(), + ]; + } + + $nativeCallbackType = $scope->getNativeType($args[1]->value); + + if ($this->treatPhpDocTypesAsCertain) { + $callbackType = $scope->getType($args[1]->value); + } else { + $callbackType = $nativeCallbackType; + } + + if ($this->isCallbackTypeNull($callbackType)) { + $message = 'Parameter #2 of array_filter() cannot be null to avoid loose comparison semantics (%s given).'; + $errorBuilder = RuleErrorBuilder::message(sprintf( + $message, + $callbackType->describe(VerbosityLevel::typeOnly()), + ))->identifier('arrayFilter.strict'); + + if ($this->treatPhpDocTypesAsCertainTip && !$this->isCallbackTypeNull($nativeCallbackType) && $this->treatPhpDocTypesAsCertain) { + $errorBuilder->treatPhpDocTypesAsCertainTip(); + } + + return [$errorBuilder->build()]; + } + + return []; + } + + private function isCallbackTypeNull(Type $callbackType): bool + { + if ($callbackType->isNull()->yes()) { + return true; + } + + if ($callbackType->isNull()->no()) { + return false; + } + + return $this->checkNullables; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ClosureUsesThisRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ClosureUsesThisRule.php new file mode 100644 index 00000000000..4f41d26b428 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Functions/ClosureUsesThisRule.php @@ -0,0 +1,52 @@ + + */ +class ClosureUsesThisRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\Closure::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->static) { + return []; + } + + if ($scope->isInClosureBind()) { + return []; + } + + $messages = []; + foreach ($node->uses as $closureUse) { + $varType = $scope->getType($closureUse->var); + if (!is_string($closureUse->var->name)) { + continue; + } + if (!$varType instanceof ThisType) { + continue; + } + + $messages[] = RuleErrorBuilder::message(sprintf('Anonymous function uses $this assigned to variable $%s. Use $this directly in the function body.', $closureUse->var->name)) + ->line($closureUse->getStartLine()) + ->identifier('closure.useThis') + ->build(); + } + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorMethodCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorMethodCallRule.php new file mode 100644 index 00000000000..1dba6ed6d24 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorMethodCallRule.php @@ -0,0 +1,34 @@ + + */ +final class IllegalConstructorMethodCallRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== '__construct') { + return []; + } + + return [ + RuleErrorBuilder::message('Call to __construct() on an existing object is not allowed.') + ->identifier('constructor.call') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorStaticCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorStaticCallRule.php new file mode 100644 index 00000000000..fa747d6a2b5 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/IllegalConstructorStaticCallRule.php @@ -0,0 +1,92 @@ + + */ +final class IllegalConstructorStaticCallRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== '__construct') { + return []; + } + + if ($this->isCollectCallingConstructor($node, $scope)) { + return []; + } + + return [ + RuleErrorBuilder::message('Static call to __construct() is only allowed on a parent class in the constructor.') + ->identifier('constructor.call') + ->build(), + ]; + } + + private function isCollectCallingConstructor(Node\Expr\StaticCall $node, Scope $scope): bool + { + // __construct should be called from inside constructor + if ($scope->getFunction() === null) { + return false; + } + + if ($scope->getFunction()->getName() !== '__construct') { + if (!$this->isInRenamedTraitConstructor($scope)) { + return false; + } + } + + if (!$scope->isInClass()) { + return false; + } + + if (!$node->class instanceof Node\Name) { + return false; + } + + $parentClasses = array_map(static fn (string $name) => strtolower($name), $scope->getClassReflection()->getParentClassesNames()); + + return in_array(strtolower($scope->resolveName($node->class)), $parentClasses, true); + } + + private function isInRenamedTraitConstructor(Scope $scope): bool + { + if (!$scope->isInClass()) { + return false; + } + + if (!$scope->isInTrait()) { + return false; + } + + if ($scope->getFunction() === null) { + return false; + } + + $traitAliases = $scope->getClassReflection()->getNativeReflection()->getTraitAliases(); + $functionName = $scope->getFunction()->getName(); + if (!array_key_exists($functionName, $traitAliases)) { + return false; + } + + return $traitAliases[$functionName] === sprintf('%s::%s', $scope->getTraitReflection()->getName(), '__construct'); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/WrongCaseOfInheritedMethodRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/WrongCaseOfInheritedMethodRule.php new file mode 100644 index 00000000000..5f800e50284 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Methods/WrongCaseOfInheritedMethodRule.php @@ -0,0 +1,86 @@ + + */ +class WrongCaseOfInheritedMethodRule implements Rule +{ + + public function getNodeType(): string + { + return InClassMethodNode::class; + } + + public function processNode( + Node $node, + Scope $scope + ): array + { + $methodReflection = $node->getMethodReflection(); + $declaringClass = $methodReflection->getDeclaringClass(); + + $messages = []; + if ($declaringClass->getParentClass() !== null) { + $parentMessage = $this->findMethod( + $declaringClass, + $declaringClass->getParentClass(), + $methodReflection->getName(), + ); + if ($parentMessage !== null) { + $messages[] = $parentMessage; + } + } + + foreach ($declaringClass->getInterfaces() as $interface) { + $interfaceMessage = $this->findMethod( + $declaringClass, + $interface, + $methodReflection->getName(), + ); + if ($interfaceMessage === null) { + continue; + } + + $messages[] = $interfaceMessage; + } + + return $messages; + } + + private function findMethod( + ClassReflection $declaringClass, + ClassReflection $classReflection, + string $methodName + ): ?IdentifierRuleError + { + if (!$classReflection->hasNativeMethod($methodName)) { + return null; + } + + $parentMethod = $classReflection->getNativeMethod($methodName); + if ($parentMethod->getName() === $methodName) { + return null; + } + + return RuleErrorBuilder::message(sprintf( + 'Method %s::%s() does not match %s method name: %s::%s().', + $declaringClass->getDisplayName(), + $methodName, + $classReflection->isInterface() ? 'interface' : 'parent', + $classReflection->getDisplayName(), + $parentMethod->getName(), + ))->identifier('method.nameCase')->build(); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticIncrementOrDecrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticIncrementOrDecrementRule.php new file mode 100644 index 00000000000..4e87a885845 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticIncrementOrDecrementRule.php @@ -0,0 +1,61 @@ + + */ +abstract class OperandInArithmeticIncrementOrDecrementRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + /** + * @param TNodeType $node + */ + public function processNode(Node $node, Scope $scope): array + { + $messages = []; + $varType = $scope->getType($node->var); + + if ( + ($node instanceof PreInc || $node instanceof PostInc) + && !$this->helper->isValidForIncrement($scope, $node->var) + || ($node instanceof PreDec || $node instanceof PostDec) + && !$this->helper->isValidForDecrement($scope, $node->var) + ) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in %s, %s given.', + $this->describeOperation(), + $varType->describe(VerbosityLevel::typeOnly()), + ))->identifier(sprintf('%s.nonNumeric', $this->getIdentifier()))->build(); + } + + return $messages; + } + + abstract protected function describeOperation(): string; + + /** + * @return 'preInc'|'postInc'|'preDec'|'postDec' + */ + abstract protected function getIdentifier(): string; + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostDecrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostDecrementRule.php new file mode 100644 index 00000000000..d0e08099f6b --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostDecrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPostDecrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PostDec::class; + } + + protected function describeOperation(): string + { + return 'post-decrement'; + } + + protected function getIdentifier(): string + { + return 'postDec'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostIncrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostIncrementRule.php new file mode 100644 index 00000000000..400d82889a5 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPostIncrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPostIncrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PostInc::class; + } + + protected function describeOperation(): string + { + return 'post-increment'; + } + + protected function getIdentifier(): string + { + return 'postInc'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreDecrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreDecrementRule.php new file mode 100644 index 00000000000..9d583560077 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreDecrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPreDecrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PreDec::class; + } + + protected function describeOperation(): string + { + return 'pre-decrement'; + } + + protected function getIdentifier(): string + { + return 'preDec'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreIncrementRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreIncrementRule.php new file mode 100644 index 00000000000..d5d81f2a51b --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandInArithmeticPreIncrementRule.php @@ -0,0 +1,28 @@ + + */ +class OperandInArithmeticPreIncrementRule extends OperandInArithmeticIncrementOrDecrementRule +{ + + public function getNodeType(): string + { + return PreInc::class; + } + + protected function describeOperation(): string + { + return 'pre-increment'; + } + + protected function getIdentifier(): string + { + return 'preInc'; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticAdditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticAdditionRule.php new file mode 100644 index 00000000000..80de1463baa --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticAdditionRule.php @@ -0,0 +1,69 @@ + + */ +class OperandsInArithmeticAdditionRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpPlus) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpPlus) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $leftType = $scope->getType($left); + $rightType = $scope->getType($right); + if (count($leftType->getArrays()) > 0 && count($rightType->getArrays()) > 0) { + return []; + } + + $messages = []; + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in +, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('plus.leftNonNumeric')->build(); + } + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in +, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('plus.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticDivisionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticDivisionRule.php new file mode 100644 index 00000000000..e95b3d624d5 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticDivisionRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticDivisionRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpDiv) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpDiv) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in /, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('div.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in /, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('div.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticExponentiationRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticExponentiationRule.php new file mode 100644 index 00000000000..1992b84a7b0 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticExponentiationRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticExponentiationRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpPow) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpPow) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in **, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('pow.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in **, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('pow.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticModuloRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticModuloRule.php new file mode 100644 index 00000000000..5b5f3c32679 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticModuloRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticModuloRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpMod) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpMod) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in %%, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mod.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in %%, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mod.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php new file mode 100644 index 00000000000..353df4c67d7 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticMultiplicationRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpMul) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpMul) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in *, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mul.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in *, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('mul.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticSubtractionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticSubtractionRule.php new file mode 100644 index 00000000000..5559d60fe08 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperandsInArithmeticSubtractionRule.php @@ -0,0 +1,65 @@ + + */ +class OperandsInArithmeticSubtractionRule implements Rule +{ + + private OperatorRuleHelper $helper; + + public function __construct(OperatorRuleHelper $helper) + { + $this->helper = $helper; + } + + public function getNodeType(): string + { + return Expr::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof BinaryOpMinus) { + $left = $node->left; + $right = $node->right; + } elseif ($node instanceof AssignOpMinus) { + $left = $node->var; + $right = $node->expr; + } else { + return []; + } + + $messages = []; + $leftType = $scope->getType($left); + if (!$this->helper->isValidForArithmeticOperation($scope, $left)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in -, %s given on the left side.', + $leftType->describe(VerbosityLevel::typeOnly()), + ))->identifier('minus.leftNonNumeric')->build(); + } + + $rightType = $scope->getType($right); + if (!$this->helper->isValidForArithmeticOperation($scope, $right)) { + $messages[] = RuleErrorBuilder::message(sprintf( + 'Only numeric types are allowed in -, %s given on the right side.', + $rightType->describe(VerbosityLevel::typeOnly()), + ))->identifier('minus.rightNonNumeric')->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperatorRuleHelper.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperatorRuleHelper.php new file mode 100644 index 00000000000..6de54cab150 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Operators/OperatorRuleHelper.php @@ -0,0 +1,92 @@ +ruleLevelHelper = $ruleLevelHelper; + } + + public function isValidForArithmeticOperation(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return true; + } + + // already reported by PHPStan core + if ($type->toNumber() instanceof ErrorType) { + return true; + } + + return $this->isSubtypeOfNumber($scope, $expr); + } + + public function isValidForIncrement(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return true; + } + + if ($type->isString()->yes()) { + // Because `$a = 'a'; $a++;` is valid + return true; + } + + return $this->isSubtypeOfNumber($scope, $expr); + } + + public function isValidForDecrement(Scope $scope, Expr $expr): bool + { + $type = $scope->getType($expr); + if ($type instanceof MixedType) { + return true; + } + + return $this->isSubtypeOfNumber($scope, $expr); + } + + private function isSubtypeOfNumber(Scope $scope, Expr $expr): bool + { + $acceptedType = new UnionType([new IntegerType(), new FloatType(), new IntersectionType([new StringType(), new AccessoryNumericStringType()])]); + + $type = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $expr, + '', + static fn (Type $type): bool => $acceptedType->isSuperTypeOf($type)->yes(), + )->getType(); + + if ($type instanceof ErrorType) { + return true; + } + + $isSuperType = $acceptedType->isSuperTypeOf($type); + if ($type instanceof BenevolentUnionType) { + return !$isSuperType->no(); + } + + return $isSuperType->yes(); + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php new file mode 100644 index 00000000000..492aa604c42 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php @@ -0,0 +1,65 @@ + + */ +class DynamicCallOnStaticMethodsCallableRule implements Rule +{ + + private RuleLevelHelper $ruleLevelHelper; + + public function __construct(RuleLevelHelper $ruleLevelHelper) + { + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return MethodCallableNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->getName() instanceof Node\Identifier) { + return []; + } + + $name = $node->getName()->name; + $type = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->getVar(), + '', + static fn (Type $type): bool => $type->canCallMethods()->yes() && $type->hasMethod($name)->yes(), + )->getType(); + + if ($type instanceof ErrorType || !$type->canCallMethods()->yes() || !$type->hasMethod($name)->yes()) { + return []; + } + + $methodReflection = $type->getMethod($name, $scope); + if ($methodReflection->isStatic()) { + return [ + RuleErrorBuilder::message(sprintf( + 'Dynamic call to static method %s::%s().', + $methodReflection->getDeclaringClass()->getDisplayName(), + $methodReflection->getName(), + ))->identifier('staticMethod.dynamicCall')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsRule.php new file mode 100644 index 00000000000..c0ae18b34cc --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/DynamicCallOnStaticMethodsRule.php @@ -0,0 +1,76 @@ + + */ +class DynamicCallOnStaticMethodsRule implements Rule +{ + + private RuleLevelHelper $ruleLevelHelper; + + public function __construct(RuleLevelHelper $ruleLevelHelper) + { + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier) { + return []; + } + + $name = $node->name->name; + $type = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->var, + '', + static fn (Type $type): bool => $type->canCallMethods()->yes() && $type->hasMethod($name)->yes(), + )->getType(); + + if ($type instanceof ErrorType || !$type->canCallMethods()->yes() || !$type->hasMethod($name)->yes()) { + return []; + } + + $methodReflection = $type->getMethod($name, $scope); + if ($methodReflection->isStatic()) { + $prototype = $methodReflection->getPrototype(); + if (in_array($prototype->getDeclaringClass()->getName(), [ + TypeInferenceTestCase::class, + PHPStanTestCase::class, + ], true)) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Dynamic call to static method %s::%s().', + $methodReflection->getDeclaringClass()->getDisplayName(), + $methodReflection->getName(), + ))->identifier('staticMethod.dynamicCall')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/StrictFunctionCallsRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/StrictFunctionCallsRule.php new file mode 100644 index 00000000000..f959fc91306 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/StrictCalls/StrictFunctionCallsRule.php @@ -0,0 +1,96 @@ + + */ +class StrictFunctionCallsRule implements Rule +{ + + /** @var int[] */ + private array $functionArguments = [ + 'in_array' => 2, + 'array_search' => 2, + 'base64_decode' => 1, + 'array_keys' => 2, + ]; + + private ReflectionProvider $reflectionProvider; + + public function __construct(ReflectionProvider $reflectionProvider) + { + $this->reflectionProvider = $reflectionProvider; + } + + public function getNodeType(): string + { + return FuncCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Name) { + return []; + } + + if (!$this->reflectionProvider->hasFunction($node->name, $scope)) { + return []; + } + + $function = $this->reflectionProvider->getFunction($node->name, $scope); + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $function->getVariants()); + $node = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); + if ($node === null) { + return []; + } + $functionName = strtolower($function->getName()); + if (!array_key_exists($functionName, $this->functionArguments)) { + return []; + } + + if ($functionName === 'array_keys' && !array_key_exists(1, $node->getArgs())) { + return []; + } + + $argumentPosition = $this->functionArguments[$functionName]; + if (!array_key_exists($argumentPosition, $node->getArgs())) { + return [ + RuleErrorBuilder::message(sprintf( + 'Call to function %s() requires parameter #%d to be set.', + $functionName, + $argumentPosition + 1, + ))->identifier('function.strict')->build(), + ]; + } + + $argumentType = $scope->getType($node->getArgs()[$argumentPosition]->value); + $trueType = new ConstantBooleanType(true); + if (!$trueType->isSuperTypeOf($argumentType)->yes()) { + return [ + RuleErrorBuilder::message(sprintf( + 'Call to function %s() requires parameter #%d to be true.', + $functionName, + $argumentPosition + 1, + ))->identifier('function.strict')->build(), + ]; + } + + return []; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/SwitchConditions/MatchingTypeInSwitchCaseConditionRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/SwitchConditions/MatchingTypeInSwitchCaseConditionRule.php new file mode 100644 index 00000000000..ba7c92f3a0a --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/SwitchConditions/MatchingTypeInSwitchCaseConditionRule.php @@ -0,0 +1,60 @@ + + */ +class MatchingTypeInSwitchCaseConditionRule implements Rule +{ + + private Printer $printer; + + public function __construct(Printer $printer) + { + $this->printer = $printer; + } + + public function getNodeType(): string + { + return Switch_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $messages = []; + $conditionType = $scope->getType($node->cond); + foreach ($node->cases as $case) { + if ($case->cond === null) { + continue; + } + + $caseType = $scope->getType($case->cond); + if (!$conditionType->isSuperTypeOf($caseType)->no()) { + continue; + } + + $messages[] = RuleErrorBuilder::message(sprintf( + 'Switch condition type (%s) does not match case condition %s (%s).', + $conditionType->describe(VerbosityLevel::value()), + $this->printer->prettyPrintExpr($case->cond), + $caseType->describe(VerbosityLevel::typeOnly()), + )) + ->line($case->getStartLine()) + ->identifier('switch.type') + ->build(); + } + + return $messages; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallRule.php new file mode 100644 index 00000000000..d55fc7894d0 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallRule.php @@ -0,0 +1,38 @@ + + */ +class VariableMethodCallRule implements Rule +{ + + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable method call on %s.', + $scope->getType($node->var)->describe(VerbosityLevel::typeOnly()), + ))->identifier('method.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallableRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallableRule.php new file mode 100644 index 00000000000..dd891a9e8c5 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableMethodCallableRule.php @@ -0,0 +1,38 @@ + + */ +class VariableMethodCallableRule implements Rule +{ + + public function getNodeType(): string + { + return MethodCallableNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->getName() instanceof Node\Identifier) { + return []; + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable method call on %s.', + $scope->getType($node->getVar())->describe(VerbosityLevel::typeOnly()), + ))->identifier('method.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariablePropertyFetchRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariablePropertyFetchRule.php new file mode 100644 index 00000000000..760bff697e8 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariablePropertyFetchRule.php @@ -0,0 +1,94 @@ + + */ +class VariablePropertyFetchRule implements Rule +{ + + private ReflectionProvider $reflectionProvider; + + /** @var string[] */ + private array $universalObjectCratesClasses; + + /** + * @param string[] $universalObjectCratesClasses + */ + public function __construct(ReflectionProvider $reflectionProvider, array $universalObjectCratesClasses) + { + $this->reflectionProvider = $reflectionProvider; + $this->universalObjectCratesClasses = $universalObjectCratesClasses; + } + + public function getNodeType(): string + { + return PropertyFetch::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + $fetchedOnType = $scope->getType($node->var); + foreach ($fetchedOnType->getObjectClassNames() as $referencedClass) { + if (!$this->reflectionProvider->hasClass($referencedClass)) { + continue; + } + + $classReflection = $this->reflectionProvider->getClass($referencedClass); + if ( + $this->isUniversalObjectCrate($classReflection) + || $this->isSimpleXMLElement($classReflection) + ) { + return []; + } + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable property access on %s.', + $fetchedOnType->describe(VerbosityLevel::typeOnly()), + ))->identifier('property.dynamicName')->build(), + ]; + } + + private function isSimpleXMLElement( + ClassReflection $classReflection + ): bool + { + return $classReflection->is(SimpleXMLElement::class); + } + + private function isUniversalObjectCrate( + ClassReflection $classReflection + ): bool + { + foreach ($this->universalObjectCratesClasses as $className) { + if (!$this->reflectionProvider->hasClass($className)) { + continue; + } + + if ($classReflection->is($className)) { + return true; + } + } + + return false; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallRule.php new file mode 100644 index 00000000000..963f01d09a2 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallRule.php @@ -0,0 +1,44 @@ + + */ +class VariableStaticMethodCallRule implements Rule +{ + + public function getNodeType(): string + { + return StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + if ($node->class instanceof Node\Name) { + $methodCalledOn = $scope->resolveName($node->class); + } else { + $methodCalledOn = $scope->getType($node->class)->describe(VerbosityLevel::typeOnly()); + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable static method call on %s.', + $methodCalledOn, + ))->identifier('staticMethod.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallableRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallableRule.php new file mode 100644 index 00000000000..2cfebaca86e --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticMethodCallableRule.php @@ -0,0 +1,44 @@ + + */ +class VariableStaticMethodCallableRule implements Rule +{ + + public function getNodeType(): string + { + return StaticMethodCallableNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->getName() instanceof Node\Identifier) { + return []; + } + + if ($node->getClass() instanceof Node\Name) { + $methodCalledOn = $scope->resolveName($node->getClass()); + } else { + $methodCalledOn = $scope->getType($node->getClass())->describe(VerbosityLevel::typeOnly()); + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable static method call on %s.', + $methodCalledOn, + ))->identifier('staticMethod.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticPropertyFetchRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticPropertyFetchRule.php new file mode 100644 index 00000000000..bc4759928b3 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableStaticPropertyFetchRule.php @@ -0,0 +1,44 @@ + + */ +class VariableStaticPropertyFetchRule implements Rule +{ + + public function getNodeType(): string + { + return StaticPropertyFetch::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node->name instanceof Node\Identifier) { + return []; + } + + if ($node->class instanceof Node\Name) { + $propertyAccessedOn = $scope->resolveName($node->class); + } else { + $propertyAccessedOn = $scope->getType($node->class)->describe(VerbosityLevel::typeOnly()); + } + + return [ + RuleErrorBuilder::message(sprintf( + 'Variable static property access on %s.', + $propertyAccessedOn, + ))->identifier('staticProperty.dynamicName')->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableVariablesRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableVariablesRule.php new file mode 100644 index 00000000000..f78e4ef422f --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/VariableVariables/VariableVariablesRule.php @@ -0,0 +1,36 @@ + + */ +class VariableVariablesRule implements Rule +{ + + public function getNodeType(): string + { + return Variable::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (is_string($node->name)) { + return []; + } + + return [ + RuleErrorBuilder::message('Variable variables are not allowed.') + ->identifier('variable.dynamicName') + ->build(), + ]; + } + +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan/LICENSE b/tools/.phpstan/vendor/phpstan/phpstan/LICENSE new file mode 100644 index 00000000000..e5f34e607a1 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2016 Ondřej Mirtes +Copyright (c) 2025 PHPStan s.r.o. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/.phpstan/vendor/phpstan/phpstan/README.md b/tools/.phpstan/vendor/phpstan/phpstan/README.md new file mode 100644 index 00000000000..abae67ecf9c --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/README.md @@ -0,0 +1,108 @@ +

PHPStan - PHP Static Analysis Tool

+ +

+ PHPStan +

+ +

+ Build Status + Latest Stable Version + Total Downloads + License + PHPStan Enabled +

+ +------ + +PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs +even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code +can be checked before you run the actual line. + +**[Read more about PHPStan »](https://phpstan.org/)** + +**[Try out PHPStan on the on-line playground! »](https://phpstan.org/try)** + +## Sponsors + +TheCodingMachine +    +Private Packagist +
+CDN77 +    +Blackfire.io +
+iO +    +Fame Helsinki +
+ShipMonk +    +Togetter +
+RightCapital +    +ContentKing +
+ZOL +    +EdgeNext +
+Shopware +    +Craft CMS +
+Worksome +    +campoint AG +
+Crisp.nl +    +Inviqa +
+Shoptet +    +Route4Me: Route Optimizer and Route Planner Software +
+Belsimpel +    +TicketSwap + + +[**You can now sponsor my open-source work on PHPStan through GitHub Sponsors.**](https://github.com/sponsors/ondrejmirtes) + +Does GitHub already have your 💳? Do you use PHPStan to find 🐛 before they reach production? [Send a couple of 💸 a month my way too.](https://github.com/sponsors/ondrejmirtes) Thank you! + +One-time donations [through Revolut.me](https://revolut.me/ondrejmirtes) are also accepted. To request an invoice, [contact me](mailto:ondrej@mirtes.cz) through e-mail. + +## Documentation + +All the documentation lives on the [phpstan.org website](https://phpstan.org/): + +* [Getting Started & User Guide](https://phpstan.org/user-guide/getting-started) +* [Config Reference](https://phpstan.org/config-reference) +* [PHPDocs Basics](https://phpstan.org/writing-php-code/phpdocs-basics) & [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) +* [Extension Library](https://phpstan.org/user-guide/extension-library) +* [Developing Extensions](https://phpstan.org/developing-extensions/extension-types) +* [API Reference](https://apiref.phpstan.org/) + +## PHPStan Pro + +PHPStan Pro is a paid add-on on top of open-source PHPStan Static Analysis Tool with these premium features: + +* Web UI for browsing found errors, you can click and open your editor of choice on the offending line. +* Continuous analysis (watch mode): scans changed files in the background, refreshes the UI automatically. + +Try it on PHPStan 0.12.45 or later by running it with the `--pro` option. You can create an account either by following the on-screen instructions, or by visiting [account.phpstan.com](https://account.phpstan.com/). + +After 30-day free trial period it costs 7 EUR for individuals monthly, 70 EUR for teams (up to 25 members). By paying for PHPStan Pro, you're supporting the development of open-source PHPStan. + +You can read more about it on [PHPStan's website](https://phpstan.org/blog/introducing-phpstan-pro). + +## Code of Conduct + +This project adheres to a [Contributor Code of Conduct](https://github.com/phpstan/phpstan/blob/master/CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code. + +## Contributing + +Any contributions are welcome. PHPStan's source code open to pull requests lives at [`phpstan/phpstan-src`](https://github.com/phpstan/phpstan-src). diff --git a/tools/.phpstan/vendor/phpstan/phpstan/UPGRADING.md b/tools/.phpstan/vendor/phpstan/phpstan/UPGRADING.md new file mode 100644 index 00000000000..52294909653 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/UPGRADING.md @@ -0,0 +1,338 @@ +Upgrading from PHPStan 1.x to 2.0 +================================= + +## PHP version requirements + +PHPStan now requires PHP 7.4 or newer to run. + +## Upgrading guide for end users + +The best way to get ready for upgrade to PHPStan 2.0 is to update to the **latest PHPStan 1.12 release** +and enable [**Bleeding Edge**](https://phpstan.org/blog/what-is-bleeding-edge). This will enable the new rules and behaviours that 2.0 turns on for all users. + +Also make sure to install and enable [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules). + +Once you get to a green build with no deprecations showed on latest PHPStan 1.12.x with Bleeding Edge enabled, you can update all your related PHPStan dependencies to 2.0 in `composer.json`: + +```json +"require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-doctrine": "^2.0", + "phpstan/phpstan-nette": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpstan/phpstan-webmozart-assert": "^2.0", + ... +} +``` + +Don't forget to update [3rd party PHPStan extensions](https://phpstan.org/user-guide/extension-library) as well. + +After changing your `composer.json`, run `composer update 'phpstan/*' -W`. + +It's up to you whether you go through the new reported errors or if you just put them all to the [baseline](https://phpstan.org/user-guide/baseline) ;) Everyone who's on PHPStan 1.12 should be able to upgrade to PHPStan 2.0. + +### Noteworthy changes to code analysis + +* [**Enhancements in handling parameters passed by reference**](https://phpstan.org/blog/enhancements-in-handling-parameters-passed-by-reference) +* [**Validate inline PHPDoc `@var` tag type**](https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector#validate-inline-phpdoc-%40var-tag-type) +* [**List type enforced**](https://phpstan.org/blog/phpstan-1-9-0-with-phpdoc-asserts-list-type#list-type) +* **Always `true` conditions always reported**: previously reported only with phpstan-strict-rules, this is now always reported. + +### Removed option `checkMissingIterableValueType` + +It's strongly recommended to add the missing array typehints. + +If you want to continue ignoring missing typehints from arrays, add `missingType.iterableValue` error identifier to your `ignoreErrors`: + +```neon +parameters: + ignoreErrors: + - + identifier: missingType.iterableValue +``` + +### Removed option `checkGenericClassInNonGenericObjectType` + +It's strongly recommended to add the missing generic typehints. + +If you want to continue ignoring missing typehints from generics, add `missingType.generics` error identifier to your `ignoreErrors`: + +```neon +parameters: + ignoreErrors: + - + identifier: missingType.generics +``` + +### Removed `checkAlwaysTrue*` options + +These options have been removed because PHPStan now always behaves as if these were set to `true`: + +* `checkAlwaysTrueCheckTypeFunctionCall` +* `checkAlwaysTrueInstanceof` +* `checkAlwaysTrueStrictComparison` +* `checkAlwaysTrueLooseComparison` + +### Removed option `excludes_analyse` + +It has been replaced with [`excludePaths`](https://phpstan.org/user-guide/ignoring-errors#excluding-whole-files). + +### Paths in `excludePaths` and `ignoreErrors` have to be a valid file path or a fnmatch pattern + +If you are excluding a file path that might not exist but you still want to have it in `excludePaths`, append `(?)`: + +```neon +parameters: + excludePaths: + - tests/*/data/* + - src/broken + - node_modules (?) # optional path, might not exist +``` + +If you have the same situation in `ignoreErrors` (ignoring an error in a path that might not exist), use `reportUnmatchedIgnoredErrors: false`. + +```neon +parameters: + reportUnmatchedIgnoredErrors: false +``` + +Appending `(?)` in `ignoreErrors` is not supported. + +### Changes in 1st party PHPStan extensions + +* [phpstan-doctrine](https://github.com/phpstan/phpstan-doctrine) + * Removed config parameter `searchOtherMethodsForQueryBuilderBeginning` (extension now behaves as when this was set to `true`) + * Removed config parameter `queryBuilderFastAlgorithm` (extension now behaves as when this was set to `false`) +* [phpstan-symfony](https://github.com/phpstan/phpstan-symfony) + * Removed legacy options with `_` in the name + * `container_xml_path` -> use `containerXmlPath` + * `constant_hassers` -> use `constantHassers` + * `console_application_loader` -> use `consoleApplicationLoader` + +### Minor backward compatibility breaks + +* Removed unused config parameter `cache.nodesByFileCountMax` +* Removed unused config parameter `memoryLimitFile` +* Removed unused feature toggle `disableRuntimeReflectionProvider` +* Removed unused config parameter `staticReflectionClassNamePatterns` +* Remove `fixerTmpDir` config parameter, use `pro.tmpDir` instead +* Remove `tempResultCachePath` config parameter, use `resultCachePath` instead +* `additionalConfigFiles` config parameter must be a list + +## Upgrading guide for extension developers + +> [!NOTE] +> Please switch to PHPStan 2.0 in a new major version of your extension. It's not feasible to try to support both PHPStan 1.x and PHPStan 2.x with the same extension code. +> +> You can definitely get closer to supporting PHPStan 2.0 without increasing major version by solving reported deprecations and other issues by analysing your extension code with PHPStan & phpstan-deprecation-rules & Bleeding Edge, but the final leap and solving backward incompatibilities should be done by requiring `"phpstan/phpstan": "^2.0"` in your `composer.json`, and releasing a new major version. + +### PHPStan now uses nikic/php-parser v5 + +See [UPGRADING](https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md) guide for PHP-Parser. + +The most notable change is how `throw` statement is represented. Previously, `throw` statements like `throw $e;` were represented using the `Stmt\Throw_` class, while uses inside other expressions (such as `$x ?? throw $e`) used the `Expr\Throw_` class. + +Now, `throw $e;` is represented as a `Stmt\Expression` that contains an `Expr\Throw_`. The +`Stmt\Throw_` class has been removed. + +### PHPStan now uses phpstan/phpdoc-parser v2 + +See [UPGRADING](https://github.com/phpstan/phpdoc-parser/blob/2.0.x/UPGRADING.md) guide for phpstan/phpdoc-parser. + +### Returning plain strings as errors no longer supported, use RuleErrorBuilder + +Identifiers are also required in custom rules. + +Learn more: [Using RuleErrorBuilder to enrich reported errors in custom rules](https://phpstan.org/blog/using-rule-error-builder) + +**Before**: + +```php +return ['My error']; +``` + +**After**: + +```php +return [ + RuleErrorBuilder::message('My error') + ->identifier('my.error') + ->build(), +]; +``` + +### Deprecate various `instanceof *Type` in favour of new methods on `Type` interface + +Learn more: [Why Is instanceof *Type Wrong and Getting Deprecated?](https://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting-deprecated) + +### Removed deprecated `ParametersAcceptorSelector::selectSingle()` + +Use [`ParametersAcceptorSelector::selectFromArgs()`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ParametersAcceptorSelector.html#_selectFromArgs) instead. It should be used in most places where `selectSingle()` was previously used, like dynamic return type extensions. + +**Before**: + +```php +$defaultReturnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(); +``` + +**After**: + +```php +$defaultReturnType = ParametersAcceptorSelector::selectFromArgs( + $scope, + $functionCall->getArgs(), + $functionReflection->getVariants() +)->getReturnType(); +``` + +If you're analysing function or method body itself and you're using one of the following methods, ask for `getParameters()` and `getReturnType()` directly on the reflection object: + +* [InClassMethodNode::getMethodReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.InClassMethodNode.html) +* [InFunctionNode::getFunctionReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.InFunctionNode.html) +* [FunctionReturnStatementsNode::getFunctionReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.FunctionReturnStatementsNode.html) +* [MethodReturnStatementsNode::getMethodReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.MethodReturnStatementsNode.html) +* [Scope::getFunction()](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.Scope.html#_getFunction) + +**Before**: + +```php +$function = $node->getFunctionReflection(); +$returnType = ParametersAcceptorSelector::selectSingle($function->getVariants())->getReturnType(); +``` + +**After**: + +```php +$returnType = $node->getFunctionReflection()->getReturnType(); +``` + +### Changed `TypeSpecifier::create()` and `SpecifiedTypes` constructor parameters + +[`PHPStan\Analyser\TypeSpecifier::create()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.TypeSpecifier.html#_create) now accepts (all parameters are required): + +* `Expr $expr` +* `Type $type` +* `TypeSpecifierContext $context` +* `Scope $scope` + +If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by this method), call `setAlwaysOverwriteTypes()` and `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::create()`). These methods return a new object (SpecifiedTypes is immutable). + +[`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) constructor now accepts: + +* `array $sureTypes` +* `array $sureNotTypes` + +If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by the constructor), call `setAlwaysOverwriteTypes()` and `setRootExpr()`. These methods return a new object (SpecifiedTypes is immutable). + +### `ConstantArrayType` no longer extends `ArrayType` + +`Type::getArrays()` now returns `list`. + +Using `$type instanceof ArrayType` is [being deprecated anyway](https://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting-deprecated) so the impact of this change should be minimal. + +### Changed `TypeSpecifier::specifyTypesInCondition()` + +This method no longer accepts `Expr $rootExpr`. If you want to change it, call `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::specifyTypesInCondition()`). `setRootExpr()` method returns a new object (SpecifiedTypes is immutable). + +### Node attributes `parent`, `previous`, `next` are no longer available + +Learn more: https://phpstan.org/blog/preprocessing-ast-for-custom-rules + +### Removed config parameter `scopeClass` + +As a replacement you can implement [`PHPStan\Type\ExpressionTypeResolverExtension`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.ExpressionTypeResolverExtension.html) interface instead and register it as a service. + +### Removed `PHPStan\Broker\Broker` + +Use [`PHPStan\Reflection\ReflectionProvider`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ReflectionProvider.html) instead. + +`BrokerAwareExtension` was also removed. Ask for `ReflectionProvider` in the extension constructor instead. + +Instead of `PHPStanTestCase::createBroker()`, call `PHPStanTestCase::createReflectionProvider()`. + +### List type is enabled for everyone + +Removed static methods from `AccessoryArrayListType` class: + +* `isListTypeEnabled()` +* `setListTypeEnabled()` +* `intersectWith()` + +Instead of `AccessoryArrayListType::intersectWith($type)`, do `TypeCombinator::intersect($type, new AccessoryArrayListType())`. + +### Minor backward compatibility breaks + +* Classes that were previously `@final` were made `final` +* Parameter `$callableParameters` of [`MutatingScope::enterAnonymousFunction()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterAnonymousFunction) and [`enterArrowFunction()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterArrowFunction) made required +* Parameter `StatementContext $context` of [`NodeScopeResolver::processStmtNodes()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.NodeScopeResolver.html#_processStmtNodes) made required +* ClassPropertiesNode - remove `$extensions` parameter from [`getUninitializedProperties()`](https://apiref.phpstan.org/2.0.x/PHPStan.Node.ClassPropertiesNode.html#_getUninitializedProperties) +* `Type::getSmallerType()`, `Type::getSmallerOrEqualType()`, `Type::getGreaterType()`, `Type::getGreaterOrEqualType()`, `Type::isSmallerThan()`, `Type::isSmallerThanOrEqual()` now require [`PhpVersion`](https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html) as argument. +* `CompoundType::isGreaterThan()`, `CompoundType::isGreaterThanOrEqual()` now require [`PhpVersion`](https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html) as argument. +* Removed `ReflectionProvider::supportsAnonymousClasses()` (all reflection providers support anonymous classes) +* Remove `ArrayType::generalizeKeys()` +* Remove `ArrayType::count()`, use `Type::getArraySize()` instead +* Remove `ArrayType::castToArrayKeyType()`, `Type::toArrayKey()` instead +* Remove `UnionType::pickTypes()`, use `pickFromTypes()` instead +* Remove `RegexArrayShapeMatcher::matchType()`, use `matchExpr()` instead +* Remove unused `PHPStanTestCase::$useStaticReflectionProvider` +* Remove `PHPStanTestCase::getReflectors()`, use `getReflector()` instead +* Remove `ClassReflection::getFileNameWithPhpDocs()`, use `getFileName()` instead +* Remove `AnalysisResult::getInternalErrors()`, use `getInternalErrorObjects()` instead +* Remove `ConstantReflection::getValue()`, use `getValueExpr()` instead. To get `Type` from `Expr`, use `Scope::getType()` or `InitializerExprTypeResolver::getType()` +* Remove `PropertyTag::getType()`, use `getReadableType()` / `getWritableType()` instead +* Remove `GenericTypeVariableResolver`, use [`Type::getTemplateType()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getTemplateType) instead +* Rename `Type::isClassStringType()` to `Type::isClassString()` +* Remove `Scope::isSpecified()`, use `hasExpressionType()` instead +* Remove `ConstantArrayType::isEmpty()`, use `isIterableAtLeastOnce()->no()` instead +* Remove `ConstantArrayType::getNextAutoIndex()` +* Removed methods from `ConstantArrayType` - `getFirst*Type` and `getLast*Type` + * Use `getFirstIterable*Type` and `getLastIterable*Type` instead +* Remove `ConstantArrayType::generalizeToArray()` +* Remove `ConstantArrayType::findTypeAndMethodName()`, use `findTypeAndMethodNames()` instead +* Remove `ConstantArrayType::removeLast()`, use [`Type::popArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_popArray) instead +* Remove `ConstantArrayType::removeFirst()`, use [`Type::shiftArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_shiftArray) instead +* Remove `ConstantArrayType::reverse()`, use [`Type::reverseArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_reverseArray) instead +* Remove `ConstantArrayType::chunk()`, use [`Type::chunkArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_chunkArray) instead +* Remove `ConstantArrayType::slice()`, use [`Type::sliceArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_sliceArray) instead +* Made `TypeUtils` thinner by removing methods: + * Remove `TypeUtils::getArrays()` and `getAnyArrays()`, use [`Type::getArrays()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getArrays) instead + * Remove `TypeUtils::getConstantArrays()` and `getOldConstantArrays()`, use [`Type::getConstantArrays()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantArrays) instead + * Remove `TypeUtils::getConstantStrings()`, use [`Type::getConstantStrings()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantStrings) instead + * Remove `TypeUtils::getConstantTypes()` and `getAnyConstantTypes()`, use [`Type::isConstantValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue) or [`Type::generalize()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize) + * Remove `TypeUtils::generalizeType()`, use [`Type::generalize()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize) instead + * Remove `TypeUtils::getDirectClassNames()`, use [`Type::getObjectClassNames()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getObjectClassNames) instead + * Remove `TypeUtils::getConstantScalars()`, use [`Type::isConstantScalarValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantScalarValue) or [`Type::getConstantScalarTypes()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantScalarTypes) instead + * Remove `TypeUtils::getEnumCaseObjects()`, use [`Type::getEnumCases()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getEnumCases) instead + * Remove `TypeUtils::containsCallable()`, use [`Type::isCallable()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isCallable) instead +* Removed `Scope::doNotTreatPhpDocTypesAsCertain()`, use `getNativeType()` instead +* Parameter `$isList` in `ConstantArrayType` constructor can only be `TrinaryLogic`, no longer `bool` +* Parameter `$nextAutoIndexes` in `ConstantArrayType` constructor can only be `non-empty-list`, no longer `int` +* Remove `ConstantType` interface, use [`Type::isConstantValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue) instead +* `acceptsNamedArguments()` in `FunctionReflection`, `ExtendedMethodReflection` and `CallableParametersAcceptor` interfaces returns `TrinaryLogic` instead of `bool` +* Remove `FunctionReflection::isFinal()` +* [`Type::getProperty()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getProperty) now returns [`ExtendedPropertyReflection`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ExtendedPropertyReflection.html) +* Remove `__set_state()` on objects that should not be serialized in cache +* Parameter `$selfClass` of [`TypehintHelper::decideTypeFromReflection()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.TypehintHelper.html#_decideTypeFromReflection) no longer accepts `string` +* `LevelsTestCase::dataTopics()` data provider made static +* `PHPStan\Node\Printer\Printer` no longer autowired as `PhpParser\PrettyPrinter\Standard`, use `PHPStan\Node\Printer\Printer` in the typehint +* Remove `Type::acceptsWithReason()`, `Type:accepts()` return type changed from `TrinaryLogic` to [`AcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +* Remove `CompoundType::isAcceptedWithReasonBy()`, `CompoundType::isAcceptedBy()` return type changed from `TrinaryLogic` to [`AcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +Remove `Type::isSuperTypeOfWithReason()`, `Type:isSuperTypeOf()` return type changed from `TrinaryLogic` to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* Remove `CompoundType::isSubTypeOfWithReasonBy()`, `CompoundType::isSubTypeOf()` return type changed from `TrinaryLogic` to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* Remove `TemplateType::isValidVarianceWithReason()`, changed `TemplateType::isValidVariance()` return type to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* `RuleLevelHelper::accepts()` return type changed from `bool` to [`RuleLevelHelperAcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +* Changes around `ClassConstantReflection` + * Class `ClassConstantReflection` removed from BC promise, renamed to `RealClassConstantReflection` + * Interface `ConstantReflection` renamed to `ClassConstantReflection` + * Added more methods around PHPDoc types and native types to the (new) `ClassConstantReflection` + * Interface `GlobalConstantReflection` renamed to `ConstantReflection` +* Renamed interfaces and classes from `*WithPhpDocs` to `Extended*` + * `ParametersAcceptorWithPhpDocs` -> `ExtendedParametersAcceptor` + * `ParameterReflectionWithPhpDocs` -> `ExtendedParameterReflection` + * `FunctionVariantWithPhpDocs` -> `ExtendedFunctionVariant` +* `ClassPropertyNode::getNativeType()` return type changed from AST node to `Type|null` +* Class `PHPStan\Node\ClassMethod` (accessible from `ClassMethodsNode`) is no longer an AST node + * Call `PHPStan\Node\ClassMethod::getNode()` to access the original AST node diff --git a/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php b/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php new file mode 100644 index 00000000000..a5d341bfdb3 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php @@ -0,0 +1,114 @@ +loadClass($class); + + return; + } + if (strpos($class, 'PHPStan\\') !== 0 || strpos($class, 'PHPStan\\PhpDocParser\\') === 0) { + return; + } + + if (!in_array('phar', stream_get_wrappers(), true)) { + throw new \Exception('Phar wrapper is not registered. Please review your php.ini settings.'); + } + + if (!self::$polyfillsLoaded) { + self::$polyfillsLoaded = true; + + if ( + PHP_VERSION_ID < 80000 + && empty($GLOBALS['__composer_autoload_files']['a4a119a56e50fbb293281d9a48007e0e']) + && !class_exists(\Symfony\Polyfill\Php80\Php80::class, false) + ) { + $GLOBALS['__composer_autoload_files']['a4a119a56e50fbb293281d9a48007e0e'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php80/Php80.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php80/bootstrap.php'; + } + + if ( + empty($GLOBALS['__composer_autoload_files']['0e6d7bf4a5811bfa5cf40c5ccd6fae6a']) + && !class_exists(\Symfony\Polyfill\Mbstring\Mbstring::class, false) + ) { + $GLOBALS['__composer_autoload_files']['0e6d7bf4a5811bfa5cf40c5ccd6fae6a'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-mbstring/Mbstring.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-mbstring/bootstrap.php'; + } + + if ( + empty($GLOBALS['__composer_autoload_files']['e69f7f6ee287b969198c3c9d6777bd38']) + && !class_exists(\Symfony\Polyfill\Intl\Normalizer\Normalizer::class, false) + ) { + $GLOBALS['__composer_autoload_files']['e69f7f6ee287b969198c3c9d6777bd38'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-normalizer/Normalizer.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-normalizer/bootstrap.php'; + } + + if ( + !extension_loaded('intl') + && empty($GLOBALS['__composer_autoload_files']['8825ede83f2f289127722d4e842cf7e8']) + && !class_exists(\Symfony\Polyfill\Intl\Grapheme\Grapheme::class, false) + ) { + $GLOBALS['__composer_autoload_files']['8825ede83f2f289127722d4e842cf7e8'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-grapheme/Grapheme.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-grapheme/bootstrap.php'; + } + + if ( + PHP_VERSION_ID < 80100 + && empty ($GLOBALS['__composer_autoload_files']['23c18046f52bef3eea034657bafda50f']) + && !class_exists(\Symfony\Polyfill\Php81\Php81::class, false) + ) { + $GLOBALS['__composer_autoload_files']['23c18046f52bef3eea034657bafda50f'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/Php81.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/bootstrap.php'; + } + } + + $filename = str_replace('\\', DIRECTORY_SEPARATOR, $class); + if (strpos($class, 'PHPStan\\BetterReflection\\') === 0) { + $filename = substr($filename, strlen('PHPStan\\BetterReflection\\')); + $filepath = 'phar://' . __DIR__ . '/phpstan.phar/vendor/ondrejmirtes/better-reflection/src/' . $filename . '.php'; + } else { + $filename = substr($filename, strlen('PHPStan\\')); + $filepath = 'phar://' . __DIR__ . '/phpstan.phar/src/' . $filename . '.php'; + } + + if (!file_exists($filepath)) { + return; + } + + require $filepath; + } +} + +spl_autoload_register([PharAutoloader::class, 'loadClass']); diff --git a/tools/.phpstan/vendor/phpstan/phpstan/composer.json b/tools/.phpstan/vendor/phpstan/phpstan/composer.json new file mode 100644 index 00000000000..dc62c19ce08 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/composer.json @@ -0,0 +1,26 @@ +{ + "name": "phpstan/phpstan", + "description": "PHPStan - PHP Static Analysis Tool", + "license": ["MIT"], + "keywords": ["dev", "static analysis"], + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "autoload": { + "files": ["bootstrap.php"] + }, + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "forum": "https://github.com/phpstan/phpstan/discussions", + "source": "https://github.com/phpstan/phpstan-src", + "docs": "https://phpstan.org/user-guide/getting-started", + "security": "https://github.com/phpstan/phpstan/security/policy" + } +} diff --git a/tools/.phpstan/vendor/phpstan/phpstan/conf/bleedingEdge.neon b/tools/.phpstan/vendor/phpstan/phpstan/conf/bleedingEdge.neon new file mode 100644 index 00000000000..01fee972d82 --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/conf/bleedingEdge.neon @@ -0,0 +1,2 @@ +includes: + - phar://phpstan.phar/conf/bleedingEdge.neon diff --git a/tools/.phpstan/vendor/phpstan/phpstan/phpstan b/tools/.phpstan/vendor/phpstan/phpstan/phpstan new file mode 100755 index 00000000000..7a08ef4850b --- /dev/null +++ b/tools/.phpstan/vendor/phpstan/phpstan/phpstan @@ -0,0 +1,8 @@ +#!/usr/bin/env php + + +
+ AI abilities sea level rising... as way to rise type coverage for class elements +
+ +
+ +PHPStan uses type declarations to determine the type of variables, properties and other expression. Sometimes it's hard to see what PHPStan errors are the important ones among thousands of others. + +Instead of fixing all PHPStan errors at once, we can start with minimal require type coverage. + +
+ +What is the type coverage you ask? We have 4 type possible declarations in total here: + +```php +final class ConferenceFactory +{ + const SPEAKER_TAG = 'speaker'; + + private $talkFactory; + + public function createConference(array $data) + { + $talks = $this->talkFactory->create($data); + + return new Conference($talks); + } +} +``` + +*Note: Class constant types require PHP 8.3 to run.* + +The param type is defined. But the property, return and constant types are missing. + +* 1 out of 4 = 25 % coverage + +Our code quality is only at one-quarter of its potential. Let's get to 100 %! + +```diff + final class ConferenceFactory + { +- public const SPEAKER_TAG = 'speaker'; ++ public const string SPEAKER_TAG = 'speaker'; + +- private $talkFactory; ++ private TalkFactory $talkFactory; + +- public function createConference(array $data) ++ public function createConference(array $data): Conference + { + $talks = $this->talkFactory->create($data); + + return new Conference($talks); + } + } +``` + +This technique is very simple to start even on legacy project. Also, you're now aware exactly how high coverage your project has. + +
+ +## Install + +```bash +composer require tomasvotruba/type-coverage --dev +``` + +The package is available on PHP 7.2+ version in tagged releases. + +
+ +## Usage + +With [PHPStan extension installer](https://github.com/phpstan/extension-installer), everything is ready to run. + +Enable each item on their own: + +```yaml +# phpstan.neon +parameters: + type_coverage: + return: 50 + param: 35.5 + property: 70 + constant: 85 +``` + +
+ +## Measure Strict Declares coverage + +Once you've reached 100 % type coverage, make sure [your code is strict and uses types](https://tomasvotruba.com/blog/how-adding-type-declarations-makes-your-code-dangerous): + +```php + + +## Full Paths only + +If you run PHPStan only on some subpaths that are different from your setup in `phpstan.neon`, e.g.: + +```bash +vendor/bin/phpstan analyze src/Controller +``` + +This package could show false positives, as classes in the `src/Controller` could be slightly less typed. This would be spamming whole PHPStan output and make hard to see any other errors you look for. + +That's why this package only triggers if there are full paths, e.g.: + +```bash +vendor/bin/phpstan +```` + +
+ +Happy coding! diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json new file mode 100644 index 00000000000..e32de7ffba1 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json @@ -0,0 +1,24 @@ +{ + "name": "tomasvotruba/type-coverage", + "type": "phpstan-extension", + "description": "Measure type coverage of your project", + "license": "MIT", + "keywords": ["static analysis", "phpstan-extension"], + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0", + "nette/utils": "^3.2 || ^4.0" + }, + "autoload": { + "psr-4": { + "TomasVotruba\\TypeCoverage\\": "src" + } + }, + "extra": { + "phpstan": { + "includes": [ + "config/extension.neon" + ] + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/config/extension.neon b/tools/.phpstan/vendor/tomasvotruba/type-coverage/config/extension.neon new file mode 100644 index 00000000000..b2a58b699c2 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/config/extension.neon @@ -0,0 +1,79 @@ +parametersSchema: + # see https://doc.nette.org/en/schema for configuration + type_coverage: structure([ + declare: anyOf(float(), int()) + # type declarations + return_type: anyOf(float(), int()) + param_type: anyOf(float(), int()) + property_type: anyOf(float(), int()) + constant_type: anyOf(float(), int()) + print_suggestions: bool() + # aliases to avoid typos + return: anyOf(schema(float(), nullable()), schema(int(), nullable())) + param: anyOf(schema(float(), nullable()), schema(int(), nullable())) + property: anyOf(schema(float(), nullable()), schema(int(), nullable())) + constant: anyOf(schema(float(), nullable()), schema(int(), nullable())) + + # measure + measure: bool() + ]) + +# default parameters +parameters: + type_coverage: + declare: 0 + # type declarations + return_type: 99 + param_type: 99 + property_type: 99 + constant_type: 99 + # default, yet deprecated + print_suggestions: true + # aliases + return: null + param: null + property: null + constant: null + + measure: false + +services: + - TomasVotruba\TypeCoverage\Formatter\TypeCoverageFormatter + - TomasVotruba\TypeCoverage\CollectorDataNormalizer + + - + factory: TomasVotruba\TypeCoverage\Configuration + arguments: + - %type_coverage% + + # collectors + - + class: TomasVotruba\TypeCoverage\Collectors\ReturnTypeDeclarationCollector + tags: + - phpstan.collector + + - + class: TomasVotruba\TypeCoverage\Collectors\ParamTypeDeclarationCollector + tags: + - phpstan.collector + + - + class: TomasVotruba\TypeCoverage\Collectors\PropertyTypeDeclarationCollector + tags: + - phpstan.collector + - + class: TomasVotruba\TypeCoverage\Collectors\ConstantTypeDeclarationCollector + tags: + - phpstan.collector + + - + class: TomasVotruba\TypeCoverage\Collectors\DeclareCollector + tags: + - phpstan.collector + +rules: + - TomasVotruba\TypeCoverage\Rules\ParamTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\ReturnTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\PropertyTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\ConstantTypeCoverageRule + - TomasVotruba\TypeCoverage\Rules\DeclareCoverageRule diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg b/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg new file mode 100644 index 00000000000..cd219abe8b3 Binary files /dev/null and b/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg differ diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php new file mode 100644 index 00000000000..224fbf73e06 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php @@ -0,0 +1,18 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->withPhpSets() + ->withPreparedSets(deadCode: true, codeQuality: true, codingStyle: true, typeDeclarations: true, privatization: true, naming: true) + ->withImportNames(removeUnusedImports: true) + ->withSkip([ + '*/Fixture/*', + '*/Source/*', + ]); diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php new file mode 100644 index 00000000000..e05c09d15fc --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/CollectorDataNormalizer.php @@ -0,0 +1,36 @@ +}>> $collectorDataByPath + */ + public function normalize(array $collectorDataByPath): TypeCountAndMissingTypes + { + $totalCount = 0; + $missingCount = 0; + + $missingTypeLinesByFilePath = []; + + foreach ($collectorDataByPath as $filePath => $typeCoverageData) { + foreach ($typeCoverageData as $nestedData) { + $totalCount += $nestedData[0]; + + $missingCount += count($nestedData[1]); + + $missingTypeLinesByFilePath[$filePath] = array_merge( + $missingTypeLinesByFilePath[$filePath] ?? [], + $nestedData[1] + ); + } + } + + return new TypeCountAndMissingTypes($totalCount, $missingCount, $missingTypeLinesByFilePath); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php new file mode 100644 index 00000000000..4296b613518 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php @@ -0,0 +1,77 @@ + + */ + public function getNodeType(): string + { + return ClassConstantsNode::class; + } + + /** + * @param ClassConstantsNode $node + * @return mixed[] + */ + public function processNode(Node $node, Scope $scope): array + { + // enable only on PHP 8.3+ + if (PHP_VERSION_ID < 80300) { + return [0, []]; + } + + $constantCount = count($node->getConstants()); + + $missingTypeLines = []; + + foreach ($node->getConstants() as $classConst) { + // blocked by parent type + if ($this->isGuardedByParentClassConstant($scope, $classConst)) { + continue; + } + + // already typed + if ($classConst->type instanceof Node) { + continue; + } + + // give useful context + $missingTypeLines[] = $classConst->getLine(); + } + + return [$constantCount, $missingTypeLines]; + } + + private function isGuardedByParentClassConstant(Scope $scope, ClassConst $classConst): bool + { + $constName = $classConst->consts[0]->name->toString(); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + foreach ($classReflection->getParents() as $parentClassReflection) { + if ($parentClassReflection->hasConstant($constName)) { + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/DeclareCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/DeclareCollector.php new file mode 100644 index 00000000000..ad677eeede1 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/DeclareCollector.php @@ -0,0 +1,51 @@ +getNodes() as $node) { + if (! $node instanceof Declare_) { + continue; + } + + foreach ($node->declares as $declare) { + if ( + $declare->key->name !== 'strict_types' + ) { + continue; + } + + if ( + ! $declare->value instanceof LNumber + || $declare->value->value !== 1 + ) { + return false; + } + + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php new file mode 100644 index 00000000000..a9d4a494380 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ParamTypeDeclarationCollector.php @@ -0,0 +1,72 @@ +shouldSkipFunctionLike($node)) { + return null; + } + + $missingTypeLines = []; + $paramCount = count($node->getParams()); + + foreach ($node->getParams() as $param) { + if ($param->variadic) { + // skip variadic + --$paramCount; + continue; + } + + if ($param->type === null) { + $missingTypeLines[] = $param->getLine(); + } + } + + return [$paramCount, $missingTypeLines]; + } + + private function shouldSkipFunctionLike(FunctionLike $functionLike): bool + { + // nothing to analyse + if ($functionLike->getParams() === []) { + return true; + } + + return $this->hasFunctionLikeCallableParam($functionLike); + } + + private function hasFunctionLikeCallableParam(FunctionLike $functionLike): bool + { + // skip callable, can be anythings + $docComment = $functionLike->getDocComment(); + if (! $docComment instanceof Doc) { + return false; + } + + $docCommentText = $docComment->getText(); + return strpos($docCommentText, '@param callable') !== false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php new file mode 100644 index 00000000000..893a60ba4a6 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php @@ -0,0 +1,93 @@ + + */ + public function getNodeType(): string + { + return InClassNode::class; + } + + /** + * @param InClassNode $node + * @return mixed[] + */ + public function processNode(Node $node, Scope $scope): array + { + // return typed properties/all properties + $classLike = $node->getOriginalNode(); + + $propertyCount = count($classLike->getProperties()); + + $missingTypeLines = []; + + foreach ($classLike->getProperties() as $property) { + // blocked by parent type + if ($this->isGuardedByParentClassProperty($scope, $property)) { + continue; + } + + // already typed + if ($property->type instanceof Node) { + continue; + } + + if ($this->isPropertyDocTyped($property)) { + continue; + } + + // give useful context + $missingTypeLines[] = $property->getLine(); + } + + return [$propertyCount, $missingTypeLines]; + } + + private function isPropertyDocTyped(Property $property): bool + { + $docComment = $property->getDocComment(); + if (! $docComment instanceof Doc) { + return false; + } + + $docCommentText = $docComment->getText(); + + // skip as unable to type + return strpos($docCommentText, 'callable') !== false || strpos($docCommentText, 'resource') !== false; + } + + private function isGuardedByParentClassProperty(Scope $scope, Property $property): bool + { + $propertyName = $property->props[0]->name->toString(); + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + foreach ($classReflection->getParents() as $parentClassReflection) { + if ($parentClassReflection->hasProperty($propertyName)) { + return true; + } + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php new file mode 100644 index 00000000000..f6f63b8419e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ReturnTypeDeclarationCollector.php @@ -0,0 +1,48 @@ +isMagic()) { + return null; + } + + if ($scope->isInTrait()) { + $originalMethodName = $node->getAttribute('originalTraitMethodName'); + if ($originalMethodName === '__construct') { + return null; + } + } + + $missingTypeLines = []; + + if (! $node->returnType instanceof Node) { + $missingTypeLines[] = $node->getLine(); + } + + return [1, $missingTypeLines]; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php new file mode 100644 index 00000000000..f2fd6f2b982 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php @@ -0,0 +1,76 @@ + + * @readonly + */ + private array $parameters; + + /** + * @param array $parameters + */ + public function __construct(array $parameters) + { + $this->parameters = $parameters; + } + + /** + * @return float|int + */ + public function getRequiredPropertyTypeLevel() + { + return $this->parameters['property'] ?? $this->parameters['property_type']; + } + + public function isConstantTypeCoverageEnabled(): bool + { + if (PHP_VERSION_ID < 80300) { + return false; + } + + return $this->getRequiredConstantTypeLevel() > 0; + } + + /** + * @return float|int + */ + public function getRequiredConstantTypeLevel() + { + return $this->parameters['constant'] ?? $this->parameters['constant_type']; + } + + /** + * @return float|int + */ + public function getRequiredParamTypeLevel() + { + return $this->parameters['param'] ?? $this->parameters['param_type']; + } + + /** + * @return float|int + */ + public function getRequiredReturnTypeLevel() + { + return $this->parameters['return'] ?? $this->parameters['return_type']; + } + + /** + * @return float|int + */ + public function getRequiredDeclareLevel() + { + return $this->parameters['declare']; + } + + public function showOnlyMeasure(): bool + { + return $this->parameters['measure']; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php new file mode 100644 index 00000000000..7b4cec24f92 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php @@ -0,0 +1,58 @@ +getParameter('analysedPaths'); + $analysedPathsFromConfig = $originalContainer->getParameter('analysedPathsFromConfig'); + + self::$areFullPathsAnalysed = $analysedPathsFromConfig === $analysedPaths; + + return self::$areFullPathsAnalysed; + } + + private static function getPrivateProperty(object $object, string $propertyName): object + { + $reflectionProperty = new ReflectionProperty($object, $propertyName); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Formatter/TypeCoverageFormatter.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Formatter/TypeCoverageFormatter.php new file mode 100644 index 00000000000..04d7c3946aa --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Formatter/TypeCoverageFormatter.php @@ -0,0 +1,56 @@ +getTotalCount() === 0) { + return []; + } + + $typeCoveragePercentage = $typeCountAndMissingTypes->getCoveragePercentage(); + + // has the code met the minimal sea level of types? + if ($typeCoveragePercentage >= $minimalLevel) { + return []; + } + + $ruleErrors = []; + + foreach ($typeCountAndMissingTypes->getMissingTypeLinesByFilePath() as $filePath => $lines) { + $errorMessage = sprintf( + $message, + $typeCountAndMissingTypes->getTotalCount(), + $typeCountAndMissingTypes->getFilledCount(), + $typeCoveragePercentage, + $minimalLevel + ); + + foreach ($lines as $line) { + $ruleErrors[] = RuleErrorBuilder::message($errorMessage) + ->identifier($identifier) + ->file($filePath) + ->line($line) + ->build(); + } + } + + return $ruleErrors; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php new file mode 100644 index 00000000000..b53da098c88 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php @@ -0,0 +1,101 @@ + + */ +final class ConstantTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible constant types, only %d - %.1f %% actually have it. Add more constant types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.constantTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $constantTypeDeclarationCollector = $node->get(ConstantTypeDeclarationCollector::class); + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($constantTypeDeclarationCollector); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Class constant type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + if (! $this->configuration->isConstantTypeCoverageEnabled()) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredConstantTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php new file mode 100644 index 00000000000..314c0b59785 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/DeclareCoverageRule.php @@ -0,0 +1,116 @@ + + */ +final class DeclareCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible declare(strict_types=1), only %d - %.1f %% actually have it. Add more declares to get over %s %%'; + + /** + * @readonly + */ + private Configuration $configuration; + + public function __construct(Configuration $configuration) + { + $this->configuration = $configuration; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $requiredDeclareLevel = $this->configuration->getRequiredDeclareLevel(); + + $declareCollector = $node->get(DeclareCollector::class); + $totalPossibleDeclares = count($declareCollector); + + $coveredDeclares = 0; + $notCoveredDeclareFilePaths = []; + + foreach ($declareCollector as $fileName => $data) { + // has declares + if ($data === [true]) { + ++$coveredDeclares; + } else { + $notCoveredDeclareFilePaths[] = $fileName; + } + } + + $declareCoverage = ($coveredDeclares / $totalPossibleDeclares) * 100; + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Strict declares coverage is %.1f %% out of %d possible', + $declareCoverage, + $totalPossibleDeclares + ); + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + // not enabled + if ($requiredDeclareLevel === 0) { + return []; + } + + // nothing to handle + if ($totalPossibleDeclares === 0) { + return []; + } + + // we meet the limit, all good + if ($declareCoverage >= $requiredDeclareLevel) { + return []; + } + + $ruleErrors = []; + foreach ($notCoveredDeclareFilePaths as $notCoveredDeclareFilePath) { + $errorMessage = sprintf( + self::ERROR_MESSAGE, + $totalPossibleDeclares, + $coveredDeclares, + $declareCoverage, + $requiredDeclareLevel, + ); + + $ruleErrors[] = RuleErrorBuilder::message($errorMessage)->file($notCoveredDeclareFilePath)->build(); + } + + return $ruleErrors; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php new file mode 100644 index 00000000000..ed300eea6ae --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php @@ -0,0 +1,105 @@ + + */ +final class ParamTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible param types, only %d - %.1f %% actually have it. Add more param types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.paramTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $paramTypeDeclarationCollector = $node->get(ParamTypeDeclarationCollector::class); + + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($paramTypeDeclarationCollector); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Param type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + + $ruleError = RuleErrorBuilder::message($errorMessage) + ->build(); + + return [$ruleError]; + } + + if ($this->configuration->getRequiredParamTypeLevel() === 0) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredParamTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php new file mode 100644 index 00000000000..27dcec2cff4 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php @@ -0,0 +1,101 @@ + + */ +final class PropertyTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible property types, only %d - %.1f %% actually have it. Add more property types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.propertyTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $propertyTypeDeclarationCollector = $node->get(PropertyTypeDeclarationCollector::class); + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($propertyTypeDeclarationCollector); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Property type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + if ($this->configuration->getRequiredPropertyTypeLevel() === 0) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredPropertyTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php new file mode 100644 index 00000000000..c7e7fbaa674 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php @@ -0,0 +1,100 @@ + + */ +final class ReturnTypeCoverageRule implements Rule +{ + /** + * @var string + */ + public const ERROR_MESSAGE = 'Out of %d possible return types, only %d - %.1f %% actually have it. Add more return types to get over %s %%'; + + /** + * @var string + */ + private const IDENTIFIER = 'typeCoverage.returnTypeCoverage'; + + /** + * @readonly + */ + private TypeCoverageFormatter $typeCoverageFormatter; + + /** + * @readonly + */ + private Configuration $configuration; + + /** + * @readonly + */ + private CollectorDataNormalizer $collectorDataNormalizer; + + public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer) + { + $this->typeCoverageFormatter = $typeCoverageFormatter; + $this->configuration = $configuration; + $this->collectorDataNormalizer = $collectorDataNormalizer; + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + // if only subpaths are analysed, skip as data will be false positive + if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) { + return []; + } + + $returnSeaLevelDataByFilePath = $node->get(ReturnTypeDeclarationCollector::class); + $typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($returnSeaLevelDataByFilePath); + + if ($this->configuration->showOnlyMeasure()) { + $errorMessage = sprintf( + 'Return type coverage is %.1f %% out of %d possible', + $typeCountAndMissingTypes->getCoveragePercentage(), + $typeCountAndMissingTypes->getTotalCount() + ); + return [RuleErrorBuilder::message($errorMessage)->build()]; + } + + if ($this->configuration->getRequiredReturnTypeLevel() === 0) { + return []; + } + + return $this->typeCoverageFormatter->formatErrors( + self::ERROR_MESSAGE, + self::IDENTIFIER, + $this->configuration->getRequiredReturnTypeLevel(), + $typeCountAndMissingTypes + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/ValueObject/TypeCountAndMissingTypes.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/ValueObject/TypeCountAndMissingTypes.php new file mode 100644 index 00000000000..c539839de58 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/ValueObject/TypeCountAndMissingTypes.php @@ -0,0 +1,75 @@ + + * @readonly + */ + private array $missingTypeLinesByFilePath; + + /** + * @param array $missingTypeLinesByFilePath + */ + public function __construct(int $totalCount, int $missingCount, array $missingTypeLinesByFilePath) + { + $this->totalCount = $totalCount; + $this->missingCount = $missingCount; + $this->missingTypeLinesByFilePath = $missingTypeLinesByFilePath; + } + + public function getTotalCount(): int + { + return $this->totalCount; + } + + public function getFilledCount(): int + { + return $this->totalCount - $this->missingCount; + } + + /** + * @return array + */ + public function getMissingTypeLinesByFilePath(): array + { + return $this->missingTypeLinesByFilePath; + } + + public function getCoveragePercentage(): float + { + if ($this->totalCount === 0) { + return 100.0; + } + + $relative = 100 * ($this->getTypedCount() / $this->totalCount); + + // round down with one decimal, to make error message clear that required value is not reached yet + return floor($relative * 10) / 10; + } + + private function getTypedCount(): int + { + $missingCount = 0; + + foreach ($this->missingTypeLinesByFilePath as $missingTypeLines) { + $missingCount += count($missingTypeLines); + } + + return $this->totalCount - $missingCount; + } +} diff --git a/tools/composer b/tools/composer index e766506542d..bb6ba648b02 100755 Binary files a/tools/composer and b/tools/composer differ diff --git a/tools/phive b/tools/phive index b01fe088b27..da56a9d34e0 100755 --- a/tools/phive +++ b/tools/phive @@ -85,6 +85,7 @@ spl_autoload_register( 'phario\\manifest\\manifestloader' => '/vendor/phar-io/manifest/src/ManifestLoader.php', 'phario\\manifest\\manifestloaderexception' => '/vendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php', 'phario\\manifest\\manifestserializer' => '/vendor/phar-io/manifest/src/ManifestSerializer.php', + 'phario\\manifest\\noemailaddressexception' => '/vendor/phar-io/manifest/src/exceptions/NoEmailAddressException.php', 'phario\\manifest\\phpelement' => '/vendor/phar-io/manifest/src/xml/PhpElement.php', 'phario\\manifest\\phpextensionrequirement' => '/vendor/phar-io/manifest/src/values/PhpExtensionRequirement.php', 'phario\\manifest\\phpversionrequirement' => '/vendor/phar-io/manifest/src/values/PhpVersionRequirement.php', @@ -351,772 +352,753 @@ spl_autoload_register( Phar::mapPhar('phive.phar'); -$rc = (new Factory(new Cli\Request($_SERVER['argv']), new StaticPhiveVersion('0.15.2')))->getRunner()->run(); +$rc = (new Factory(new Cli\Request($_SERVER['argv']), new StaticPhiveVersion('0.16.0')))->getRunner()->run(); exit($rc); __HALT_COMPILER(); ?> -X< -phive.phar,vendor/phar-io/version/src/BuildMetaData.php����b���H�/vendor/phar-io/version/src/PreReleaseSuffix.php^���bw.�o �&vendor/phar-io/version/src/Version.php+���bBfr붴6vendor/phar-io/version/src/VersionConstraintParser.php����b�13�,vendor/phar-io/version/src/VersionNumber.php����bqJ}�r�Dvendor/phar-io/version/src/constraints/AbstractVersionConstraint.php����bsoSMy�Dvendor/phar-io/version/src/constraints/AndVersionConstraintGroup.php����b�/j-Ҵ?vendor/phar-io/version/src/constraints/AnyVersionConstraint.php@���bN�[ �Avendor/phar-io/version/src/constraints/ExactVersionConstraint.php����b�:B���Pvendor/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php����b���^��Cvendor/phar-io/version/src/constraints/OrVersionConstraintGroup.php����b����شQvendor/phar-io/version/src/constraints/SpecificMajorAndMinorVersionConstraint.php����b�L�K�Ivendor/phar-io/version/src/constraints/SpecificMajorVersionConstraint.php����b� ��<vendor/phar-io/version/src/constraints/VersionConstraint.php����b2���W�3vendor/phar-io/version/src/exceptions/Exception.php����b��G��Jvendor/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php����bv�����Avendor/phar-io/version/src/exceptions/InvalidVersionException.php����bu|���Bvendor/phar-io/version/src/exceptions/NoBuildMetaDataException.php����bq�����Evendor/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php����bsXbpԴOvendor/phar-io/version/src/exceptions/UnsupportedVersionConstraintException.php����b(�Gd�+vendor/phar-io/filesystem/src/Directory.php����b���4vendor/phar-io/filesystem/src/DirectoryException.php����b�=v�"�+vendor/phar-io/filesystem/src/Exception.phpf���b]�$��&vendor/phar-io/filesystem/src/File.phpf���b�C�+r�*vendor/phar-io/filesystem/src/Filename.php� ���b�Vs�)�3vendor/phar-io/filesystem/src/FilenameException.phpm���bafo���2vendor/phar-io/filesystem/src/LastModifiedDate.php����bT���[�(vendor/phar-io/executor/src/Executor.php���b�#�j~�1vendor/phar-io/executor/src/ExecutorException.php����bf�vendor/phar-io/manifest/src/exceptions/InvalidUrlException.php����b1O�6�Dvendor/phar-io/manifest/src/exceptions/ManifestDocumentException.php����brYT��Kvendor/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php����b��*��Jvendor/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php����bv\;/�Cvendor/phar-io/manifest/src/exceptions/ManifestElementException.php����bo��m}�Bvendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php����bl�G#�2vendor/phar-io/manifest/src/values/Application.php����b*Dh ��6vendor/phar-io/manifest/src/values/ApplicationName.phpP���b ��-vendor/phar-io/manifest/src/values/Author.php����b��8hZ�7vendor/phar-io/manifest/src/values/AuthorCollection.php����b���3�?vendor/phar-io/manifest/src/values/AuthorCollectionIterator.php���b��#�д7vendor/phar-io/manifest/src/values/BundledComponent.php*���b�J���b�:��[�,vendor/phar-io/manifest/src/values/Email.php9���b��q�"�0vendor/phar-io/manifest/src/values/Extension.phpx���b���TK�.vendor/phar-io/manifest/src/values/Library.php����b)����.vendor/phar-io/manifest/src/values/License.php����bxl��>�/vendor/phar-io/manifest/src/values/Manifest.php -���b�-����>vendor/phar-io/manifest/src/values/PhpExtensionRequirement.php����bo7 hD�<vendor/phar-io/manifest/src/values/PhpVersionRequirement.php���b|��d!�2vendor/phar-io/manifest/src/values/Requirement.php����bg{���<vendor/phar-io/manifest/src/values/RequirementCollection.php����b���蜴Dvendor/phar-io/manifest/src/values/RequirementCollectionIterator.phpL���b��#!0�+vendor/phar-io/manifest/src/values/Type.php����b��4BJ�*vendor/phar-io/manifest/src/values/Url.phpp���b�ڤ:��1vendor/phar-io/manifest/src/xml/AuthorElement.phpa���bS�z�Y�;vendor/phar-io/manifest/src/xml/AuthorElementCollection.php5���bJ�ʶ��2vendor/phar-io/manifest/src/xml/BundlesElement.php\���b\��p��4vendor/phar-io/manifest/src/xml/ComponentElement.phph���bV�*�>vendor/phar-io/manifest/src/xml/ComponentElementCollection.php>���bK�>_�3vendor/phar-io/manifest/src/xml/ContainsElement.phpk���b�4�6�4vendor/phar-io/manifest/src/xml/CopyrightElement.php����bs����5vendor/phar-io/manifest/src/xml/ElementCollection.php���b�$��۴.vendor/phar-io/manifest/src/xml/ExtElement.php����bBq��ߴ8vendor/phar-io/manifest/src/xml/ExtElementCollection.php,���bI֏i�4vendor/phar-io/manifest/src/xml/ExtensionElement.phpl���bUs�x�2vendor/phar-io/manifest/src/xml/LicenseElement.php^���bQ��9�4vendor/phar-io/manifest/src/xml/ManifestDocument.php� ���b�/����3vendor/phar-io/manifest/src/xml/ManifestElement.php����b��dq�.vendor/phar-io/manifest/src/xml/PhpElement.php���b�Ȃb�3vendor/phar-io/manifest/src/xml/RequiresElement.php-���bR&� '�/src/commands/composer/ComposerCommandConfig.php���b��w���)src/commands/composer/ComposerContext.php����b�x���)src/commands/composer/ComposerService.php ���b-�D�)src/commands/composer/ComposerCommand.php����b��wnt�src/commands/help/help.md� -���b�$4w�!src/commands/help/HelpCommand.php����b�G:�=�-src/commands/install/InstallCommandConfig.php����b��ż(�6src/commands/install/InstallCommandConfigException.php����b3��=�'src/commands/install/InstallContext.phpN���b�|S���'src/commands/install/InstallCommand.phpH -���bQg��!src/commands/list/ListCommand.php����bA�?I�#src/commands/purge/PurgeCommand.phpt���b�����#src/commands/purge/PurgeContext.php����bf��P�+src/commands/remove/RemoveCommandConfig.php���b�RiXg�%src/commands/remove/RemoveContext.php���bP'���%src/commands/remove/RemoveCommand.php ���b3�R{&�#src/commands/reset/ResetCommand.php1���by��=�)src/commands/reset/ResetCommandConfig.php���b�oAsI�#src/commands/reset/ResetContext.php����b�E�Ŵ!src/commands/skel/SkelCommand.phpl ���b�'2{�'src/commands/skel/SkelCommandConfig.php���b�� -�!src/commands/skel/SkelContext.php2���bZ�_MʹCsrc/commands/update-repository-list/UpdateRepositoryListCommand.php����b��2d��+src/commands/update/UpdateCommandConfig.php ���b�4�ƀ�%src/commands/update/UpdateContext.php����b}�f�%src/commands/update/UpdateCommand.php���b�9]M1�'src/commands/version/VersionCommand.php ���bP���%src/commands/status/StatusCommand.php� ���b�BH�7�+src/commands/status/StatusCommandConfig.phpJ -���b@���*�%src/commands/status/StatusContext.php����bt�&��-src/commands/selfupdate/SelfupdateCommand.php����b�L�'src/commands/default/DefaultCommand.php-���b����-src/commands/default/DefaultCommandConfig.php����bw�ę��'src/commands/migrate/MigrateCommand.php����b�W���-src/commands/migrate/MigrateCommandConfig.phpu���bx~�$p�'src/commands/migrate/MigrateContext.php���bOX'�e�(src/commands/outdated/OutdatedConfig.php:���b��ʲe�1src/commands/outdated/OutdatedConfigException.php����b�-4��)src/commands/outdated/OutdatedContext.php4���b�#>��)src/commands/outdated/OutdatedCommand.phpU���bU�-ƴsrc/commands/CommandLocator.phpr -���b�����)src/services/checksum/ChecksumService.php8���b 2w��+src/services/key/gpg/GnupgKeyDownloader.phpt -���b��ݴ)src/services/key/gpg/GnupgKeyImporter.php����b���~�(src/services/key/gpg/PublicKeyReader.php����b3�ӊ�"src/services/key/KeyDownloader.php����b!�}.e�$src/services/key/KeyImportResult.phpb���b�Z7Ŵ src/services/key/KeyImporter.php����b �j�src/services/key/KeyService.php� ���b�1 �y�src/services/key/PublicKey.php����bts�׎�&src/services/key/TrustedCollection.php����b�%P;�*src/services/phar/CompatibilityService.php� ���b�^c���*src/services/phar/PharInstallerFactory.php����b��� �*src/services/phar/PharInstallerLocator.php-���b��V��!src/services/phar/PharService.php����b�s���*src/services/phar/UnixoidPharInstaller.php7���b�n0ޱ�*src/services/phar/WindowsPharInstaller.php����b��G��%src/services/phar/ReleaseSelector.php� ���b�Λ�$src/services/phar/InstallService.phpv ���b����$src/services/phar/PharDownloader.phpV���b��^��#src/services/phar/PharInstaller.php� ���b5�JԴ$src/services/phar/RemovalService.phpv���b0Afi�<src/services/resolver/strategy/AbstractResolvingStrategy.php ���b.ז� �>src/services/resolver/strategy/LocalFirstResolvingStrategy.php]���buM;B�?src/services/resolver/strategy/RemoteFirstResolvingStrategy.php^���br��ٴ4src/services/resolver/strategy/ResolvingStrategy.php����b.W$���-src/services/resolver/GithubAliasResolver.phpm ���bE�i��-src/services/resolver/GitlabAliasResolver.phpG���b��v㵴,src/services/resolver/LocalAliasResolver.php���b��_���-src/services/resolver/PharIoAliasResolver.php� ���bH�D�X�/src/services/resolver/RequestedPharResolver.php4���bX�R/�6src/services/resolver/RequestedPharResolverFactory.php4���b1�?L�6src/services/resolver/RequestedPharResolverService.phpW���b�z��N�=src/services/resolver/RequestedPharResolverServiceBuilder.php����b�+��&�7src/services/resolver/AbstractRequestedPharResolver.php0���b82�Q�+src/services/resolver/DirectUrlResolver.php����b7�k��5src/services/signature/gpg/GnupgSignatureVerifier.php8���b�e '8�6src/services/signature/gpg/GnupgVerificationResult.php����boʖ��,src/services/signature/SignatureVerifier.php����b=P���-src/services/signature/VerificationResult.php4���bJO���(src/services/migration/FileMigration.php����bt ;Y�0src/services/migration/HomePhiveXmlMigration.php>���b�?겴0src/services/migration/InternalFileMigration.phpY���bm���@�$src/services/migration/Migration.php����b�X'mY�+src/services/migration/MigrationFactory.php����b�� � �+src/services/migration/MigrationService.php����b��V:�3src/services/migration/ProjectPhiveXmlMigration.php����b<9̴,src/services/migration/UserFileMigration.php����bd�p�0src/services/migration/HomePharsXmlMigration.phpm���b��d�:�%src/shared/cli/input/ConsoleInput.php����b�(�b�src/shared/cli/input/Input.php����b*)D�U�.src/shared/cli/output/ColoredConsoleOutput.php\���b=�}��'src/shared/cli/output/ConsoleOutput.php� -���b�T:|�&src/shared/cli/output/ConsoleTable.php����br�d#;� src/shared/cli/output/Output.php����b_���J�'src/shared/cli/output/OutputFactory.php����b[Q�@�'src/shared/cli/output/OutputLocator.phph���b���%�src/shared/cli/Command.php����bǯG�!src/shared/cli/CommandLocator.php����b9�\�ôsrc/shared/cli/Options.php����b�/TI�src/shared/cli/error.txtR���b���'�*src/shared/cli/CommandOptionsException.php����b=��@�#src/shared/cli/ContextException.php����b1�U�src/shared/cli/Request.php���b|^F��#src/shared/cli/RequestException.php���bGM�ꪴ"src/shared/cli/RunnerException.php����bm;g��src/shared/cli/Context.phpz���b��4nN�src/shared/cli/Runner.phpU���bH���l�*src/shared/cli/CommandLocatorException.php����b(�t��!src/shared/cli/GeneralContext.phpC ���b&���M�)src/shared/config/CompositeAuthConfig.php����b�1z���)src/shared/config/LocalPhiveXmlConfig.php����b��q�ٴ/src/shared/config/PhiveXmlConfigFileLocator.php)���b[QwŴ$src/shared/config/PhiveXmlConfig.php�#���bhK�l4�#src/shared/config/AuthXmlConfig.php ���b�S�঴ src/shared/config/AuthConfig.php����b0d���.src/shared/config/AuthXmlConfigFileLocator.phpx���bk6�ôsrc/shared/config/Config.php����b�hs�G�*src/shared/config/GlobalPhiveXmlConfig.php@���ba�fK[�+src/shared/config/EnvironmentAuthConfig.php ���b�v#*�&src/shared/download/FileDownloader.phpk ���bG�8�-src/shared/environment/WindowsEnvironment.php����b��� |�&src/shared/environment/Environment.php=���b D�p��-src/shared/environment/EnvironmentLocator.php����b�����-src/shared/environment/UnixoidEnvironment.php -���b��y�c�&src/shared/exceptions/GitException.php����b�=��-src/shared/exceptions/CurlConfigException.php����b�?c��'src/shared/exceptions/CurlException.php����bα���1src/shared/exceptions/DownloadFailedException.php����b˫)$�(src/shared/exceptions/ErrorException.php����bI��W�#src/shared/exceptions/Exception.php����b �<���+src/shared/exceptions/ExecutorException.php����b0��2src/shared/exceptions/FileNotWritableException.php����b�g )�5src/shared/exceptions/GnupgKeyDownloaderException.php����b��m8�%src/shared/exceptions/IOException.php����b �˸˴5src/shared/exceptions/InstallationFailedException.php����bu�.src/shared/exceptions/InvalidHashException.php����b`o��5src/shared/exceptions/LinkCreationFailedException.php����bH@!g�,src/shared/exceptions/MigrationException.php����b�g�3src/shared/exceptions/MigrationsFailedException.php����bsshg�3src/shared/exceptions/NoGPGBinaryFoundException.php����b� G?�+src/shared/exceptions/NotFoundException.php����b�A�)�'src/shared/exceptions/PharException.php����b 2eh״0src/shared/exceptions/PharInstallerException.php����b�B��/src/shared/exceptions/PharRegistryException.php����b�Qz-�,src/shared/exceptions/PublicKeyException.php����b���L�*src/shared/exceptions/ReleaseException.php����b|���*src/shared/exceptions/ResolveException.php����b�Db�.src/shared/exceptions/SourcesListException.php����b+z�/�?src/shared/exceptions/UnsupportedVersionConstraintException.php����b��R�5src/shared/exceptions/VerificationFailedException.php����bV��&�-src/shared/exceptions/InvalidXmlException.php����b��}�1src/shared/exceptions/FeatureMissingException.php����b����'src/shared/exceptions/AuthException.php����bZ -P��)src/shared/exceptions/ConfigException.php����b�.�´.src/shared/exceptions/EnvironmentException.php����b��bb� src/shared/hash/sha/Sha1Hash.phpL���b���|��"src/shared/hash/sha/Sha256Hash.phpR���b�$�U�"src/shared/hash/sha/Sha384Hash.phpR���b�-G���"src/shared/hash/sha/Sha512Hash.phpS���b��Cc��src/shared/hash/BaseHash.phpN���b���qQ�src/shared/hash/Hash.php���bB.�� �7src/shared/http/authentication/BearerAuthentication.php����b4��'I�6src/shared/http/authentication/TokenAuthentication.php����b3E��6src/shared/http/authentication/BasicAuthentication.php����b��B-��%src/shared/http/CurlConfigBuilder.php ���bb �2�src/shared/http/ETag.php3���bSM;{�+src/shared/http/FileStorageCacheBackend.php����b�؞(�src/shared/http/HttpClient.php����b3h���!src/shared/http/HttpException.php����b fd�_�&src/shared/http/HttpProgressUpdate.php����b.li�� src/shared/http/HttpResponse.php����b~���)src/shared/http/HttpResponseException.php����bmȑA�'src/shared/http/LocalSslCertificate.php����b�����src/shared/http/RateLimit.php)���b�z���&src/shared/http/RetryingHttpClient.php����br,Pϴ*src/shared/http/RingdownCurlHttpClient.php ���bs^?���src/shared/http/CurlConfig.phpH���b�����src/shared/http/Curl.phpu -���bpBi��"src/shared/http/Authentication.php����b�O� �'src/shared/http/HttpProgressHandler.php����b� Y�_� src/shared/http/CacheBackend.phpL���bS�4�"src/shared/http/CurlHttpClient.php����b) ���(src/shared/http/HttpProgressRenderer.php� -���b���&}�"src/shared/phar/ConfiguredPhar.php1 ���b -/K�R�+src/shared/phar/ConfiguredPharException.php����b14@ý�!src/shared/phar/InstalledPhar.php@���b͑䛴src/shared/phar/Phar.php����b��}�src/shared/phar/PharAlias.phpw���bo�|��"src/shared/phar/PharIdentifier.php����bl��I�src/shared/phar/PharUrl.php����b`�\��src/shared/phar/Release.php���b6�E�ȴ%src/shared/phar/ReleaseCollection.phpH���b�cG�$src/shared/phar/SupportedRelease.php����bR���&src/shared/phar/UnsupportedRelease.php���b�!�=��src/shared/phar/UsedPhar.phpu���b���Ѕ�!src/shared/phar/RequestedPhar.php� -���b���כ�*src/shared/repository/GithubRepository.php -���b����)src/shared/repository/LocalRepository.php����b���*src/shared/repository/SourceRepository.php����b0�d�д'src/shared/repository/UrlRepository.php���bU� �*src/shared/repository/PharIoRepository.php) -���btH�*src/shared/repository/GitlabRepository.phpJ -���b��Zr�1src/shared/sources/LocalSourcesListFileLoader.phpD���b�Ig�1�2src/shared/sources/RemoteSourcesListFileLoader.php���b�7���src/shared/sources/Source.php]���b�z���,src/shared/sources/SourcesListFileLoader.php����b9�>K�"src/shared/sources/SourcesList.php� ���b �x�~�+src/shared/version/GitAwarePhiveVersion.php����b)��BǴ#src/shared/version/PhiveVersion.php����b��L++�)src/shared/version/StaticPhiveVersion.phpa���b^V�kشsrc/shared/JsonData.phpM���b�讛 �src/shared/XmlFile.phpl ���b?#���&src/shared/FileDownloaderException.php����b���f�src/shared/GnuPG.phpY���b����src/shared/Url.phpm -���b�� �ôsrc/shared/PharRegistry.php&���bg���� src/shared/executor/Executor.php���b['&�/�&src/shared/executor/ExecutorResult.php#���b�m;�P�src/shared/ComposerAlias.php^���bM�����src/shared/Git.phpV���b�5<�Ӵ%src/shared/TargetDirectoryLocator.php����b{H>Of�$src/GithubAliasResolverException.php����b��Q��src/PhiveContext.php����b�4��k�src/Factory.php�F���b� -���|�src/autoload.php�S���b" "w|��conf/pgp-keyservers.phpY���bI+��p�conf/pharBat.template%���b'����conf/phive.skeleton.xml����bW��conf/auth.skeleton.xmld���bO�&�ٴ conf/auth.xsd����b���:%�}Q�j1��W����Icz���q��@[9̬v��%u4� %����%uh�ˢ�7o�{;��-�Lu�FV�)� >'�xT��;�`m�~#�@X�]�|~>'��� -�6C�dO�p��%��>o��yKg���X��21LS�����=qA�٢���[)ԣ�/�A:�uvL�Ol7���5䓆���[�}��Y���^��X��Cb�P`� Z[�i�P {+��h)d6��Z*�+�[J;-hRU�rJ��ֵ�I��wU���h�2�~S� -��P;t�&�ܘM��)`�2��\6R�_�CU���.���^�<=�]�� V?����$�?Q�����R}�}�pV�&�O�70�͎C������TMo�@��Wl%*�QB�襪rh�(��b���b�v�@U�w��`�������7o��a&"A6p&�*-�@��o�L��; ہ,�1���/ U�&c��7J�' ��S0��0>�?�O�x�4� M�&{�3���L ��`������s2%�W�(s͉ѡ�"›Nt�`��(肤��[aﭨh��Q2D�]b+�s�{&I��dK��V#z�]l���@Ӭ���l��=����0��- �:��A���Ǜ��ߎ��W��3[�8 a��Y���2 4����x�T_OG��� -&���Œ ��9K���RoTv���3���e�������sU�]&�i,(�f�Coq�N]$��w �|�XB���zʜ�h���J��ڊ���i����VXѴ�NK����A2��s��cuҖn�۽��iގ c�sj�t&pD�ޔa�o|�锧��LbW��F�5�(��ˢ�1��٧��$a G��g��^̋��s�f��,7�'���3����y7�� v�m�|�t�]�%�U�O�O�b�wl3��$pxwP_a���[A gk��r�t�w�z�_��HZ ��.�6���-;�67�KS��vkj���EQ�g=����I�Κ����W�/�X[S�6~ϯ;ٵ 8�Dž���l�L�2egJ(��J�֑]IP���G��X�%!��b��w�#�7H� -IaN\!9 �|N�@g��;mu�[h}�R��4"�&�K����x��pAc�9%���Af�.�a�|>�?wB�?D�d�����_IJ8���9�D�$"���f̒M�JT�9�HN �Q��8y�t2��E4 L@l���!G��)y��~������R��2�S,�#(� -�Q*I���� -�!��DA�m1<#"�������@�3zi!x����|�9R��DYP�N�XԎ!|�p��n3)�e����t6(m3�w�_�B�kU,��� �N� ʷ�xL�ʪ�@����F�"��X⒉Q�ª�O�T��Uj��� `N�j���"����zڪ�~��rb�%�ሆ��{j�VV���[�}_e����)���m�.�����h�2� ���띠R-��1ru�vA���YV�"B�ɂD Z�!N׹��R���e$�F�N6��z8�)g�&�����/VY�j��8��f/�p��6��z��ɱk �;ُsh��!��/�m]/���C�:��W=� -�հ�+q�e�cO�N�(S�fF�0`�Xb�Q9�� ����},r�KS� �qTK���4�=�S 7���1���We�d���U�5���/���� �0�L�Yy���}��� V _{k�,I؃�8P�u�Z��Xb*~��u�ٺ�z���^�m�~s��V�MN{;9]��g���oޝ� � -����n�hն�o�V<�*����6&��vZ�������6�7u�Ù�i��Ssęۍɀr�n(��O��{�!�DŽ 6������lS�w[�z�(�,^����s��>��Ȝ�cd�A�|5^��g��K�_���r^0�l=,�b3s~�����o�˦�,W4˓�v2���:J`�g�z~w��a z�����x��ԥ����K�㢋9��+��<�ay������ s����9YC;��Q3k��,(�* YS� -ė�Z!�MP@���W�'�G�4Q�d� �M�A k -DZ9W;�fa�6��j;��"A�&(����-To��T_]S���˴%���>���L�����5����^F�������x? �=Cb8�5�k5\[%W�Z��ّ����f{6k���d�A��8J�x�Y�-�Ey��@E���Ã�� ����������f��›���>�v��:V���&1�e1W�:\opY?�5�s�I�_���G.Mi�pP�X,�8�"0}Kf �� 3I�n��ee7�v��νJ)?T����V]o�0}ﯸHEMJ�n�c]7��!���K�!7�m �clgcb�s��YҴ�$�b���8�{��� %�GL�����dn%j����i֠ �!�0���)�.B����GT�ǢM��Q�w�T �_P G��OƋ�v��-��iÙ�ט��#��8��0�.|�j:cB,�M7�h���X� &I��[ŧ�&���B1�ՌJ����5Ǜ��훳��AߺJ���an�'��&�/Ć��# -�RЩ 6C-� z^��ZC�~ �Ǹ0Lib�{ ��4��J�OL�� �N�������(-�̨��2V�@���-���WL�~͢��j"Y -��p<ږ�v�-h�5\x��„E��i�(4�P�����w��ꕊ�y 9-��J��xR�����F�j�]�h�z׽����gN�p��t{�o��H�Ae܊0�dlOK"�L�FfEUͩy���El ��~ȥ�k5NM��Cȸ��� ?���VQ{ɣ�%*fbU%���Ֆ�h�� H�Z�)��?Z�'�b����hޱ���;��T�:��f��8���Y/�J$.��4��� ���1+�.��Li�9nk�A΂5N�s�׃�-�� -�E�J�C=A�ҿ���墐��U���|S�W�6sI�|�����A8��0�R(�(d6�F��`#��`�WI1�V����2���]xM%�����dLy��/�Xj�K����M�Qrm��"yT���\��&,�����9��~��U����U��уeM:+�)�o5I�b3��5�{��˪�ׂ�C�[8� ��|�m�U��o���I���03�}g� �n����*�[�Yr�!o���O�UM��0��+�Ҋ8�]�P�U�K�n�T�K��L�U�D�C����k��!i��������@Z�����W�h���ˆ�l�׸3�_�4f�����vΕy �9��S��|&-��١s8�l[�)����*���:(�ݵ`SO� -rŨ�^ ܱSu��'�4ƌCO�n�Xwָ�Y-ar��K���,3q�ty��������� @�,��`<��R�ɍ��w LJTu֩f�5�%�4�\h5qŕ��yg-yg��5�Mۍ��_MRu3�Ґ�n�F�f�^s=�q"�{7H^�5qޒ��=��jl��Ai�riVr���Ru��(}k�r|y%@�PG�� -�|���|�4������]4J��ks�oҤ�z���hA}t)ǘY�F7n�j����J��F�@��N��y�}�Ak1���+搃mҘ^k;qR(���S��j�^y$F#S��;��К]��>��v~����B��❮��(�>�g�t��~�>���&���=�c��B�}�+�U�ȍ�V��. ��h�9�~ٞ�W]_�3���#�7*$0ϧ��,QL�΅w$�-2�i {�ң��Q@{�u \L�^���q����E5ۗ`��v��ý��O�5$����󵟶(u���ۉ��cG�PG��iø���f�4kk9g8�ʶ�ܿ�lM'X�P�ƳVL&�;T���B�we*��x�jX�\dsV�����걑�.��O����� W��������hc ���� `+�C�����+sp�!�zk�mR�j#1 ��W��C� �n�ݴ����,d�4%c�#��PJ�}�aBI��I��==��ٷ=4� -M��w�ַ�����U5�U0�?�O����E!n�w��__H��|a���)���$�`�V/�C}���9��Ƥ)��"K�D�t -�%�v�|���@GQ@[�M\���o[��w�ɖ�M��l�� ��v��û_Ow�ϫ�B5l�-*�1A�K>uVj`ﵵ���bG&�P�`^1v�z��q@WU��1�S��S���w�K�Y�w�#����W`g>��r��o�ri��w�g�����C&����,vewX��@��NF��w�Q�����룹}�S���Z�j�L�\� i�R�@�Q�mR�n�0 ��+8 '��d]ڢ� - À�tE@�t,T� �N ��Q���ix�D>>�O\~��rR=���V��}M>ç�"�M���R(�!��F�� -�Y��w�����"4Uc���ڠ}!��(�Uv|_�t9��0�F ߨ!�pt��%r��S�5�M�֞`�9B��. ��P��{�)��`�"d[8_!K�S�B(���]��������m�j'�v �Q��a�a�����\�I�����bE�F� Z$��\ټs�8+��-�����0�\Yt+~�� ��&Xm��;��sl!bj���#����,}t�)c��G�:K����}���"�UQFX�_���v��FN���#������~�8��i e-��}�!ø�<�(j��r8[�z�B��f��j�)���0ھ^�sȜ3��dQU 陊����a����x��ʾ�;͉�o�@h1�����.��q�C�mPMK�@�ﯘ�д���j?�X*��$�fq3v'-E�ߝ���u/��v��{oz_�5�9 �D 6�9����3�ki#��]c�������B��G�׶&Y� ���D��ֳ�\�r�†R�b�� -0�g`���׎�6.(l+d��۰�����HIP4�A��C��R9g3�&��BQ�C�E�����4��^��7����XJ�c�ܶ���P{+��h�7!#]�S��0VkT�w@c4�a·ZzVJ�,`+�Qk\��_��M��g�r]�3�b� ����ѿ��{׍�'�4�ABC�x4�RbܨE�&JO�K�ޠw�8�omQ]o�0 |���C���H��5N�tV`d�S����X�" "ݴ��G�v�Ӌ$��;M�B���b��%�Z��K �>���I'�3 ������_�;�pO��w�%h^p�ܢ{�( S��u5�' ��`I�t��z�0�p�#���s��5:w�흑��#HG���B��K4�N]���XM���5��}*�Z{2�y�}��]�\.R�W�ҡ���z�6F:}�4���&j(Ep�9\��ǀ.�LSf��3�2o�Ӧh�= ���J��!�f�+���P{�NTy�!�=���PyoZZG^ F��r>[�(�Tsn�럿�M ��!�{c�$�����R���1L>�p�g�;ɱ�v'I��nR��`(�rPz�m�m�m�Ao1���+���E$�W�$�m�������U����Ш��x� ������7o=��T �� z��Z� -0��]6d0�U�l�!�o���m�G�����I>hgoE��}�ý��o�`�����mI�!,���-|�H&�P� ��Х����Fk/��jN������&�5�^o+�%���n���{���Ӵo�}}z\|[.�U;1WȰ��N������JN$��W$�JJ�2�5��v�]�I�!�gO��W��~�'�Y�N���@m�/�-������_�F��w�$�To��I�����E{ C2�*��j�����-�����Dix��0�;��U�������&����K������ٽ#N�����Ս��G�]�5��9s牣�̶tu�N��a:�^Ǻ�>:���r���?�_5�c�f�uR�n�0 ��+x����&Y����6�C/]�2 S$���C�}�봱��"�|��{��c]�P�2Ȕ��Z�U��������t��~V�C� ��5rW -���z ��� �Eh�2�bKpm��&(�e~�_t9�{����PC  ,e$r��!�x�Ak������C���P�޳^W�`�"�E�-o0��"��VӮ��zws���6�j� -��C��?y����d� �V$DE ��� �%�7h�$���ȍ��� ��55П@��p�ǰ -o`�7Y���[dxx|�DL�z��`�^��I�d��G��ʥ<(n ��k譑�E󀦡A�9�S������<�U�vX���� -i�eȌ�^��S�v��j�l��Y��U��e߅���zH�N�ܦ6�|�i������ wΜ�%?�PU��a��sBy*&.]J�k:�v�/lÒ��B��������� �%OGɇ��R�o�0~�_q�xH-�+�:��:Mb��$tq.ě�-��&���CP�M�_b�}��G<��)����A+^�3x_�e�aC����6��\ _ O�� ����\�檀e���O -a�r^���mE�����-|��L㩰I伡k�=����¶Vs��>��ԭ1�� z�0���hE6� [��E�#"��NӾ��������1��s� {�P�O�2U���HG҈� �����8����Qj��e��#�<)]k��?\X��Y[zЃ�B��2�/&[EX����o�� d��CX�0@* ���w��m�������R���J�z�:�Vq�D�NR�� ��F��#�i����n�<0˓����)EO�� e{3�&�K<��n'��}T�Z�{Pn덦��1�`w�(�3gJu ��{3�w?*/�}�4/��lv!��jZ�� j4��t���K�d�hvFt���mR�n�0 ��+x�����I����0 COZ�cm�$Ht�b迏R�i���G��g-�}�!e0P9h�;~�a��b>-` -�;�Ն@��k�[����x����җZKU�M���/ -a�r�ԧ�eC�l���-|��,� ؈%r����[ -��}�;Xͩu4��������S���mF+�Q��� =�؞��`M�|�������}��s� G���O=05p��IE҈n�D������S�(�y@WE!)�[OJ�Z}��.��;g��e��L��pS'D���?ȚO��9`��n�|�d�I��E1�C-�KV��a�S�kP������IHڢyD3�,S�pT�Z&�7by�xMp~�%Ӛ�x.֙E��R*=�ߘr�7�b9� ��˦Z@�ye%�_��zO��,���n� -V�ՙ���s�mP�JA ��W��a[�ū�Z[*D��'A�����lf�ɶ�����`q.�����dz�Y���,�Y��}� 7p5�����^��v�@��I ���`Z��7J�)��v�� ����d����*���5���!�#u�`�O�L-Q��ΉsJ��ϸ;�G!�4u�=���m� -���Y��:�Em_�BŶ�v����b��^�R���A�f�\���`�ъ^#�.Y�A�'ƖrD��hb�c�T��#����8|��RM�w��;�m�w��c\l��5�!x�� �Z��PZ>D�m~]�AK1���slKi�ܪ�R� "�xd6�m�i&�n���M�E�)�ɗ�7o~�lC�#� ;-�rL����35)�ֺ ���L������o����0)\Ezw���d�cы�'�nư��8 �H1��y�(�(&O���x��.�.8��o��� ����ӑ�� -`0��������G)��P���>��wO����fU�:m,z�`\��� �Nl�)m�ر�bd�V0U������fJu�gk9��x*�!n+�:hJ5�A(������V?E�1 �0��=��F��89t+ -N9��4��G�*�ۭ�����2 ������#P���ع�*�A�̵_�'�ƥt�m�*���)���V<�i���<Ķ�� %(�;�, Ŕv����E�1 �0��=��F����C7'����a���]�R�ݭP������B!���ZM�^�(]�tl�@���v螨�n�[?�����7�Ob� L�J���{,`ۋT$�OJ;�nq?E˻ -A F�>O�RK�U��B��Z�0���%L"����v�~��?��tl�{ -�������@U -L%���k��Z��nj���N9��r������Q��R*�QP�x�/��E˻ -�@F�~�bJ-��XY�HP� -�e�,�e�aE|��K�q��� ӄ���&1�h_�҅N������_�����h��sn{T�Q{� ^���sl���5�f(�ҰS̜�QLiǟ��]�AkA ���+rT�gmkK�RJm{J�ͺ���a&[���ެ(��!/o�/o|��#u�F��K����Mw� {���%;{F_�k�q�W�{���v�.�y�+�S�N}��Z�+�j�b�}�*brpDS� z!+�n!9Yԫ�5�7����ܐ{�B(x�Rف�IE�{>�"������ON��Qz?������{�U -=��t�- 4ʀ�$�se�X:@�@���jw4�����t�TЀ) w��G.�@F�����O-��J)zye�Dgn��LX7(���lՉ暷�E"�;['�0vX�Y��=$O(��H����'�#Z�|fi�$h�d��)p�k>�@I����sz0�s�P��EP�+^�V���x34$e*h�p��[�����vo��&a4\#����Y��� �*b`*��K[���j��ď� ����L �DՏ��M��㏐ -R���H�J���Qq��/��uH ��R�t�@U! A"y�ʈ��-��ũ��X�v#Xސ�Ɍ -�!��Zߥ>G���bw)x�U���,���k��1�/���A��d�"AJ<����L�RP���ʹ�����$׵ޕ��Ou�ډ�6ݵa�ERЌ�T�f+u8tz�(3,���֢�F���2��&�!��a}q-���?AU.R`I��W�b�>:�#�J���f�ǵ��1%�;��ǂ�8\h�>jQ[�!���k -Vӣ�*�}U�����b�t�b��vX���`�H�h���q�9 6ͽ�Ĥ?4EF��_���u�u�)�-Mm�k䎓��[Vq� ����kU4f5�҇�V �H��*t�~D�Sa���w�-�� �Ք�76�lzn�Z[ -��T��j���ʨHVK -~�l��=x�[�Ie �~�;-)Y����2�ޞ �E�t�ߗ�W��syjjt��R-�Ŷ���6�ˤyóU���0���PQ'{٧���ӛ?���^�]�\��_�� -ԭ�)"�Փ�n���/4fe^���=zpn��SQ�̍ -v�'�Kb˵�ڮ�ۊ��Ttb��zJ�ߓ],�X[p������,�"3dtc���P -����Qks��%����4���*$�}�.�fa�G��rڼ�}T���H�I;�a�+��7Od�M��u�����t��� ~�V�k�k�l9D���V1�?�HsT��iد�E�qx��8�����i��5�s�q��m�= 1 ཿ"��~l�8�7����4p�^[�(w�����f �˛,V����-4b)�,C&�%Lƕ��#� ��-M:�>�a`��2Fc̰�PR�=R�"P/�ws7t��<��M����.9z�U�b]� -�VK���?�v�}��x��-s��a�E�� -@@��<�9:#'�M�*m�/[�ig$������jd^�7�����m�K�\s�������RL]�0^j�K���r{z��#2NC\���nz��P=k�0��+n� ���5n��B�[!k���%8��N�P��+��ܴ���{�[[[�@�H�ܓӊ*:Y��;Q2#��T��t/�۳npw�ǒ�H�� ->�8ˢ��N�~��P,��u���0��rBH����ZChh@���h�`��@U�{dE|T%��i�"�MfF���M�����4�f����ۮH� �Kq���z��>�)�C�]eU�M�G�b#�����yV�N���\��2��U���@���� ���n�8���\�����wo�uڴu��4h�؋&0i�H���E�}gt�([��آ�ߐ�ȿ��I�"S��7V�Ю�6Ö��|"y&�!����+u�I�p�5�����h����Ʉ�3?=efēO�t^�Z�(�&��ba����mP%C��ڷ����n{�|\�ad7 ��~B����.�h ���b(�m6Ak��9��G�b�˦?jHS����V dž�+��^B� �{��8��U�]p�-طAc/p����ij��Y�`�#u �?J����\>�n�M���}��"w9��E�:ڿ�d�G��E/���p1$Fk ]�_+:�)��\���[�Ө�;�J���D�n8{�^�T^�U�w'H��X!�@��i#�2k!��#H��_!�d��h5�Ѳx�3vW9��|�X��ܝ����� W�,��P2W�ۀ�b�j0Ejq�; ��6���w�Iv��j���d���Zi��[�9����U�U�q��̠��-E���N �1V�N"T��x3��w����cd�?��'ßp8��r����=tL���vу\�%us:���N�7��?g1������m)�G�P��i�.�ҿ��W�;��*˪�� VJ���Q�/*���:[l�8��UaW/X���(�ۏ���\i����x�k�~����=`�����c\O���.��#�m���s�I�C��-�X���7�gyy}ɨ�� -=�i��}zX�����������������U������/�(PHIM�I,J�(.)�L.�/�,H-V�U0Դ��K�M-.HLNU�H,�̏q��I �,.I͵��j+.V �ԹV$��d��)�V���+ D��j�uQ�j�@}�WLAH"-�W���VZZ�}º�4 { �����1�h�,;�sΜ�>Te9r�,&����ѶBwp�N"���q���ٹY΄ď�#T�(�c�� s�jrQ̟!�D�c46/ �q�,,Cm!Ε���$��Q�VV��� o�<~��WRp(j�I Yƍ�JkNI�8��2B ����P�uXB�.�I�Қ���o0���c��ދsĨ���Fp�WU"4y�o:�dX������x�-����όU���3��s���W�������� �, -7�]�x��L��� T�T�!h� a�j�8��H��'N�_U�7��]�L_��wne���o���;��w�/�S�N�0��+ԪI�@�RB�\\wK,%��y�߉�$�iq��$Z�x';��s��D�I3�p�3s���ȝU�$Iu�KQ��Y,�WϪ�`D�2�=��f�i��)\hfX -F\�%���Ñ5B>����!��d�:#�q�H����~j�*���� -%k(�_ -8�e�z_#� :^�$g�C���,�}vv2L�)d��(��pp������;�5�ir5�8�H9E%~o���C��4�bf�٪�/�b�@R��v�z1<��� ��E�=�+>h��Um���9���ܨJ�ڇ ���b����Cw��H����1 Q�j!͖-�| ����JW3��>�������.>AA -� �\m��6r����J�g+9[$%��sI���:Sޗ�j�N�r��(Hb�"x|����=� �H������n���_��>�l+�7��"���;բef��^U�(ښ��xs'��窿���W�`E۲�M#�5,��-���W �����k��hx'�������ȭ��߷���@,�U�����E=�?3��?�+�n��h����<�̳�՛J&p ��ꡒO�y�I<ȣh�BV���}W=�ز�ߔE�ĉ�r/��;� o��� ��xi�o���I �y#:�QH��x�7�B� � �&�i��A�m���"rXЅ����~O���A4����J^U�c� U���=�R�.�7Gz���@�‘�n�ǩ�8Hr�i�o��������X��������X7#6�Vl���7p��eHܦ�-����r�J�Qti�4N��Twx�6?��'�j�/�lƦ�ʖ -�z�ВI�v���ٍ<~�u� c$�>U�T:1b@Q}�����'� (~�>q�̦�5���0rL<x�vD��o�n$��/ �g�o�o4�7��]�s"�$u_�_����ϵ���{/3��D�[C ���q�����v�҇���ɝ� �:�n�g��� ��o�?؊No�<�:+E��6,`q��QT�[ ����������y�;� �E�$��1û�'��GY�ע�u�v�(��- �����F�4QX���F���0C�תo��aϳ�{��^x��wuqN�Y~�Ee# � -0��b�{����l _k?��|o_��lZӱ��k�P��M�q*#�j��I��>���Ҳޝh�@8�4ܾ��]�0_��T��_A.�Ұz���b+4=P'�]��� �ܓ�4,ki�8�5�cHGr�c] -�s����f4?�eO�u�` T���Գ뻒��N=z�ն${!���ˎuM_���C[��P���eQ���� `�!�& ;���w ^WQ��Ư�՛t��Q��p��j���a�m�s�����C��G�������*�_� �D�@�˜��$�����~�5�F��z�?�����3C�>1�Z6���站��� ���o�ߙ��ֳ�!-Z��=Ym8�y߷2d�X���(�ʦ���{�ܒ 0\�S� �$"�׃�D�W�uV�� �V��O�}�U.J�����b����3�fQ��A���.���'�OIf3K�����>7��ß��h�l -�S�B����i��0���M��eߺ��2�h��<�V�c868�F��wɋfw��@K�Xn��Q�; � ��DfQ������f $��Yw>=��I�0����H���U�ϟ��;k �:�#���h����V<D� h"������� [D�ϊ5�.�?w�wJ�$@��h���3�5�F8EUt|���xx���R�?�!#�{Z������b�d�M)7 �b���q��F�f����8d����G��E9��Qb쌬�~n��#EX ���YO{� _��'�v����Ɨ<״�y��ׄd�y�i~;�e��-u�HY����TC:z�T�R˝NN��` �H�j�l_< -��l¡C�+�e�� -9L6�68k[2M��Xnu_S��t��Ө����b�c��+��Y�`�Ld�-����zp�g��̊Lɔ?�V#���� ���=�"��Ώ�w��� ���J�`JU� -�INNw���1���Z��w�%��AXW���-�7 $�ՙ��jIQ��� ���\i�L��/�S8e]�<4���`z�R��� ���@}Bu�x���!�bc�G�����36I��6���5��P��P% � ���W�m����6��(�ʁ7�V_�����o���/�В�/�HL9w��E��{��-ں�'�Iw�ۧ�K�F��X�M[�-&�pű� ���LT�3߂:��|�&����+�Ƽ���}�e�}�;M��,Y�o�l��h��l��d���ؠ�*Z�ce� �փ5�$}4�\����\,��ıe��I�ߋ=�O�c���!�_j�G�(�����)ԣ�ͱ��p���ʨ/�Zb�X�հ3��8v3�F��&��F��V���(v�"�i[�}�Ձ�J��&<�����826I�5�I�M�$�r˞^��;�J �i���'����y�#.�PĉK�e�yw8��M~~Sʡ�0����в�R��E ;�M��w�+���8qs�B���I�ݾ����*�?Љ����1�~y����.���!��$��tn/�v?/_^i{�1�ƕ�s+����Sk�3^^��/}P �H؋�����kS�x#��zpl*�d�)�[��Ħ؀ep�n/��O�j 7��OpY�`y��7q��x�������ol -_: c��x��'ZR��vw�˙j>�y.j���g���T -1s�n� �+;��@�2� -���E��I\H�S��H/��26t��,1e�ת���U�1�(*6�:���E�:�vq����!��G W������F��ûN% %1��qbS�@�8$�h�U2�燁��T\���ک*aܴ�<��Չs��u�6<�������[�"�'��REQ�}dž�-�ek�ad�]��tu�gn��NY�Ĥ{7v=$�dMLη��U��� �Y�4D���_���\2�CTy�걀�^5yS�yժ�C�S�sD����~ђ'1��G��N��U�p���$q��Ċ۸ML�Fq.�n�� �$&=3�g���~n�%ILv=~�NtN�)1�֗���"q�I��e2X\H�����W]�N�$��X(��%v���F>�&�?�����"�C��a�U�BO†�\9Po@����š�M����S6�¥�{�n?�����-�#����i�v��cf��b��6�Hh��87��� C��� �W�ܤz�P^]MI`z��g\J�ZF�b|�S�!�MF '�'�R���0�����O�I� � $����<�����������>�`�ɓ ���0��^��KXS3�0A�_��������� ��|M�� o�6��KU(�@]�,MMP=�?D���5Yi�1V�����M��pN�5� 6�҅ݿ�zU��z A�̧!������2��l���oh'3��'ͫ�ᒸ�Z���d���"ҥ�����vEIB�t�K��"Fݟ�HMh=���ǚpZm���?��[љ,����S��������&p�����g�������є��� �RO�pZ8jH�ޑ����.~�&|ˆ����t&E�!XG��4%� ���&�S��;�6�D�:�^�I��D����hC5Z�2&&P�JS"S %�E���ѡPG�y�j�@�Fw`��mX�E�*ԜK3k�g7T�������2?�����{��x[�T����QU����|p%�N��V���l]��L?��4��kx{ �;��Q ����*�o��G���\@�'�c( x}���1��7�LFD��j-ٌ�1�B�#���ޥ���8� ��pV -�#���vHP�(�GO��\P<����M����p ��Yh�~�UnGs����J�<���~�r�\�X��TE�؎�B�jӷd۴T\��eqt�T��Яd`ٝ�;�Z0��� pS���T5�|``n$K�6�?�~[����� �|†���O�>%�oZ���L��.�5ɥ��B4(q\����ļ#k�<t��A��=/��f�#,-�R���5y����Z4*��z��G<�C��F=����83<�=u�o��0�{�� j�\����Y�~��/̱Kɷ�� aI�*�e�5����)����~Q"~�7��K�MC�����̸�`��7�]ffN��,b�F0YE�:G3L Mĭ=�W�����1E//��N/���]F/(zu�$hl�^B�]Fg_F�(:yA:3 -I�T�� Ҍ�8��Qy&/�3�M^hD%�� ш�t1E�������G�so|1����X�}�O?s�Ue�i8���w=5 M��F^���t�|:߀�cO��}^g`w���[=���*���(���ĝ*J�cb[k$�4�^5~)k ��C ��0�Q��]'�5Nc�M��-�*O�>6]��^fY�qLc0d�'V��h���F�l{5�E��K��ۢ}`�Q5[���&��H�d3b�oT%��8C�%&R$�mD��� �v���;�[�fD�Z�7J�=M� C����gsxX4*�U������P�'��Qx�r�|pX�L�r���� Ii�ک�ߕh�ޕm?��@FVƣ��_v�� .�����#_���n�SS�����qp��S �i�vB �SKk����Í�p�W�L�pPey�- P �T�or�"�kFx�������t?�W/��v��1P�F�)�{AuN;��xS���xi�v�kr8��|R��D�E��%��d �`X����`S�~���*�@cbZk�L��7��+����4�5C� .΢��� ��!���Qȯ�<����Ŏ]�x�]�������Q迾~m���P^f��z8z���d��z���}���x���/�(PHIM�I,J�(.)�L.�/�,H-V�U0Դ��K�M-.HLNU�H,�̏q�+ p����(.Vp�HN-(���SH�(I�K)V�AUs�ruUmo�0��_q�Z�A@�} ��Q��QT:��2En0$Rp,�)����s� �������{��~+BK�T2�����` -��3�8�0%h�`R9M� O瓑e��:�X��DV���C��^i��~F�x�� `>MD����O"��D G��O6"QL�w�q�� ���i��]Y��R�@�@�aY�+��N%� �\�S��b-�?"N��J��z�� ��Pj6�⮄� *��^�+E��-�X�<��W>I�-ր�q�`P� Fjpӄ4�n �3V &���T���0ٰj�` -,֍�� -��J��8�K�$���l��6�<�"�ؓ��������چa+����j���㌎�M2&m�-^B:��4�2��d�Ht����󋿘>�g�NQ�rI�S�3��ߡ�4��Y��R���?��ӓ�$�Ü��s�+ۈ���Q�U3S�3/���v�Ҋ�9��a4��"Yb�������j�B?��;�<��͝e�UH\����q+�W��@��c9�6��i�dln�( �ن��x�ǹ�����Lg���~�[��!�l�v�����nIO2���xCX@<�Ip�"���Ke��x�{m��}�jl��B�{k[�Y:�%��Ǵ�0+���a�&�g d]o9 �$�K����j�b��C"���q�n�i|��U]=�[]v�J�)���`Vƀ�M�{��_"��ݮ���dQ�4]���[���Z���]��Z��B=�>��ͫj����G>�+ �jP����YmS9�ί�U�56��6�B0���Q� T���r�3��b�"i�.��Z�7͌�v�rSIaOw�������*B.q<�H� F1�p4B���^�}�#�����.�������^��W7�ĉE��g�_ �=Q���z�q��'ה��M � -l���mE9��Ċm��p�0 ��EbE�2��%���u�ԧ� ����݄GT�x3F�kxk��� -]����.��Q������Lt a-� ���t?F�6�B�yću� J��/��n�`�FÂ����@s��P�A$�� -�P���,b-@M�1�, -9�lw��9R�B��!x V��3f(K�䓄��g ��<{e�<#5�U�-_�� ?j�ʲ[ZF�O*{U �3�/n^�� -]@���P�=�E8*̳�����<.y :�5����Xɓ��6���> ������H~�3W�qj�����(�r�+�%%w%������\��"������~$� QF ;߈��%���5ۘ*�JCZ�G>�m'FX��� ���"T�e+3¾��Q��j�s�i�K�},�X�t3r1�Ƚ'i9�.Pk1���X8��5:<������o��Oxk��� �#��&rg [|I�k�'%Kx[�|1 P�]�r�0q-� (;�hg���N]d�"�ɹ��%�i��[�hf����M7b�)�`N0sV&|��ޞi�raú��l �P��u/w�@�.r��Nj-��� ��W^�W�&�}��6���6����3N� j)g�R��a'�{�� -�� �q���Ɯ$�P�8�9^��sD��I�R�lj�-�8�շP��E�0@���I�ۚݠRA�"�����fW��Yc�1��r�������f�X� D ->'P؈���i�(��0��|���Pܔ�M%�F�I�̇� }�A���G�^!w��b�s��b�Q��ө^}�h��jY�AB!���[������c= IN�Ǝ`g�M�#̡����t��[�����A�Q�ڂY�&��!%�F��]uE�@[$hkIJ�\�\v;YN�R�t�-eUY�/?�]?��Zm�Q�� ����/>��(�A�����������������÷�w������Q����wb�OQ������������Q��z=XD�I��փګ\�9T~�Wc�=�$�FX'��\�֌�y�0�A��� �����&.;������&� O��t�(�=�H������eh��0_5S�j5���cX�Q�=�o/"v��5�t'돶�rD�gu�b��]Yc�C -���ߊ�O�?��D .���K1�H�P�(�'–>��#�^�����k@H�� ~�s7_���s�Dݹlj��ԴP*��eS�^���I*W�Q��$t.��c���۠�?z�k� �T�c�����z(�+}O���^T��g�Q��u6:�*�<4VB/bUaL#+�&��ʗ8m�|鋢e�n;���d<�|jCSrM���h���y�0l�*����R�,th��s�V�SbA��� ��D/t�l집W0��vD�O���� f�6�-!�H�9�A:��QAl���y�r@4�/D��Φ+'�€[EnS��U��9�Yןvc���|���c.y�{�쉤*Y�&�z��?B{��0�{���س�Y]����[y��g(~j%���r~��]�V�M��_uc�]� ���,��[��<�_��&�Ua�z��ɽ.݇:����0ǰ��WCE-�ƹP��0�C��ML�}�}��KL���n�RW�3psa,&m�}��W��B�i2K�7�T�S��}�Hu��G��Ky!�����#B+!">�v�T���Ð-�Β�sȷ/d~_?D�� ���h��Y�a�-F�V�������G]m8iSČ��q?�.7����k��b�qxY�N��a�áިus�qoj�_0'��[�Zd��W����f���|�H��.�����X��)N����$���`!��6�d,��9_?�7�ٳaa��� -GxN=*(�z���i{����JIz�6�V�=��J"���&��%�w�ꎶn����j\�]oZ��f���Ym�N6 [�׍a�[s�O�`���Q+��s���H@��9������埋�,f����������b#�ͬ���{AI� }i�J�em++>�^6<?2l���5�t�܎e�W�F�����Oѱ�[S�^�ѳ -C -bK&:Ct��K���F�sv������f�1�:2Mr��D4�7 ��Xm��_�,�r<�H��2#��S�JjrըAY�>LaS~�u�s��j���N��&���F�̰��h�B|�*���:2�x$d[�� � �sE�7���}��AC���#Q�pIy�ˠ�ڑ��t�.S��\�b%aK~o+3|� OA�3@f��J�>�����`F�! y�O3Y%l��� ��櫙A�dԑ��rD���V��/���duP���gg�YEU�I�:ִ��5�:��c,g�1�m���n�6��_�"N�����\4@Sk��-G�dJ#��A��!E�&�u3 {�,��xݖ-) �)�LH^���؂ �����Ջ yA>�� ۪��rI�-Y��_7�7�U[�%*�,������)�����������Ŝ|� �����8�� �M[CLx�nG�h;VIEj x�p"K ۮ�I޴���+%�� u���6|Geհ9AEa��[6 m��k�pT�)� ��������m���6!���#��c�6hG�,f �����Yi�_=�?z_�� �h�p�K'�tqri(V5(X��HPI�%��.��ψͬ���U@�w9`��"�&�r9��U\#�X�o6mPb��MNJ�e�k�����b���Id�aX�C���@= -��m1�>b��`j�5h6�'YM�E,4ť���D%p~��Q�/�O��K�ōF�0��V��y�6��ͦ���!��jN2켙/q2(jH��Y0�����=���n��3]3c�+�b�3QyV��2�=���=���N�FY5ի��y��t�e�ՆS��=9�,-�J\�q}��#�-<��O�A�On`˭�\1��N�0ѵm��:��gq2'��[ ƔYw1�^�b�-���R/�S�ɲ�jt����M]C>���"?PS9F�6�g2�E6S ��5����� Z{F����bɒ��'-��� o�j�Q��#tu�Wp�}�A��0�lP��~�Ne���3/S�pO�u3)���Ӆ~g�mц�����#���i�ĩi�rK�cbۍO pL繬'��rw]�&<^=���$Ԟl�jK���L�T�Bq30Nð���I�܈�� �,X�h�����y��S�9���)0>$����G��l�8���9�9�+�?MkpÈ���0VF��#���bς���c�Q�$�/�N�$���3Jѝ㓡��5h��/өQ�X3��2[��75������lَ�:�j��Y+y ��x�) �����œ�n�0 ��~ -2�.���YֵK��P ;���ْ �K���>*��4��]��`K4I}�IO߻�AA�FOY`�卣��m~��O8�/�P�@�=�-�Bm�o����1�f*��\�h���S����?� -� aAK ���'j��4�sa"�j:v� �jИ#��h������A��5(�6^�*4�Z� R�)�o��5C��Pl�5��q7ח�ϋyL�-�+dXc�BG��-Sk͕|9�m�"���(�81�Pp(�#�ΒDtzÍ�B*���,�. �x�"��6W��m�Y� 2�+��>O�!.���S\���2#���j2|�Α���t֠˞��u�?��q�8�E����GPȪ�l���m�P~DǕ�k8dۉ��y u�(�e�Fר�N�b���$.��lE|) �~�3���잯�+����5'P]h�:�0�S=%�}:n�������u�Ŗe��'��=���J�UBԣ�e7A�����'�X�o�6�_q,v�{�8���@R M� h����1Q��H*i6��Q�$�ˎ�`z�%��w�cIV �L���侾�O *�����`�7�=�YqK"�o¤�x �����^1����>Z�É�!�Qj������~�G��S�3�a�U�pL6a���$�3�Dd�tK���� ��F�i��Ysi�~�Q���\L�c91�H���Q>��m[�7�-����։�0��}�hcV$qfl耴[�M�KX3���X�"(�kîN�Y�u���N5��z��,w �$I�ydud��Yg[�0|�_&`�����l�̩�ѷ�Fk}nf�XB �,��T��F���ȱ�nOrD�2�� -�p�4�o��,�#��ؙ��I_f�e@�*Q5|�B"�~P[~�1�/$3Hx�Ya.�ML:�A��z�Xa!ȅS������Rg1 th�j�Lk儙 �F�Б���5y��n� p��P�.�~�t5��<�޶�ekNq�w�0�;Vwi4VJ �$�)�N-ybS��%�@��e_��ߤ<�j��}.�w��%a�x�unּqO}if�`(�,���e�/s���7�V�v�EsaTw7�Ieؔ�E��nS�ުrjê�q�!�TCٵ�Q>t-u$�A��W�Nʆ���9|��Y]�Vd��4��/�t &H��|b-��/��1Wd_'��fR�"ŵ�w���i���*�_��L�0�Ss�����ak��ve�Z�t4 -�9� \��m[01��+���_��Kj�Z궆E�^�?`��qJ����|>�w���W7/�Z�J�����dXino �`�8M^s�;E�)��gJ�5��@V�t�^�� ��j�� �Z�~�l;�-���F�|�4�")���g�֎E���]��JC1��y�Y����n�^8bAE�˂L�9=��IH����&�*t��?����}h�#��D��]N���M��xkl��:��������o��mMIF,lO�a�`�?(J�)�x���#CwCX��Xdx��"L�E���G����E�+�c+�]��G������)�]#�l�YM��\�آX�Cȃ0k{K�s��jY�����|�4(p���������&g��wQSd�X0V�-��Y�rh�T�9%��IJ�Α.KTGM�|��BllV�Gg�<���� ?�ϟ����7]P�jB1��+��"J��V-� -m)�Q(��}&4&a��O)��&R/����ݙ��c� i�L�,�|�9Q�{���t�`;�2�����b y�o\KY&�X�=�%����E,�X�Es�'�ư��8 �B1��XO��[��p�n�]pR���#�X���tLgv+���w�B._�6���0�"��v�_�^7O�������,z�`\ �� �Nl��8r�XS2T#���G� v��L)�����IS��NB�d��,��"��~�]QMk�0 ��W�ؖ��]�mmG� -[t��Pb�1slc)�`�O ��b�����<{�UC��D�d ��K$�G�N��(�|V����@�I ��Qaڄ�;z[˝[��"y��C�MIf��<��;COc�Q�,=�RC f�s�D!:�%.)j����x+-���:$���l��"�K��J�g �[�2��?BŎ�N]���e�ݭ�Q��R�� �m�!'+��h�T� -j#�dk∊�$4�2͙6��ΚE�j�s�՞չ��@g!o�=1���\�V�-�p2��\����+��m�K��o�]�OkA ���)rT�J��V-� -m)أP�N� �� ��(��E�4���˛<�:���c��Z�Ҿ�I����b4(`_5+T� �1� ->kL˰~G���2ر���$s��C�&����R�9zŠ6��(�J-%��E�fO��[pNi۠� � -[�� ��VT��P�xL�� Px.I4_!UH B^�Y�1�Oso����j�}u:�j4أ��.�Mk�`�V�N�CC�Jʋu� -��4b�nE�Y��C�n� �š��:�SX�۳�m��d���p���]�OkA ���)rT�gm�K��loB�;Yg�lf�d�C�w�(ž���2yy��h#*=&�&W귞" ��]R� ��:��y��FL -���i6oȮ"�Q[�W�a��`�(��s=�^둡�!�i��^��S� -��B���v52w؆�����sH���j��2�Sr;��l���X�\�T���Cȋ0k{G�����i��^�_�OV� -0� h�(88����Ф��"Cmケ&���NB���9������J~y,)�6��Jl6��<횚Xo����V����_�E�= �0����ut��EA� �H_i���+T��n���qluT'�ؙ������D�}�3L9��#�k�n,� �&�u2�?8�8g�_��T��8d0��x��L�:�m�>� �SMo�0 ��W�Тq�&�5M��]�p��a@S�L��lɐ�|���8M�6�.��G��})�����qd��9mJtp �ë`� � �2� U9ߥ�&�����f� �J�Q��ۑ!|��&�Zr0��Ļw?�q1����+�0r;Ä9�)s<ޠ]B�#l�yhC��X� !���)7V-2�ȕD�� �[RF�� �-����v��q�S�-S&V�A��@qE��JQ��Ù�J�B z ��R��H�� ��P�����Zc���;;�7#�5EF$J/�k��g �&ԉ��ޤ -f���Ɨ� �]�,�=�����yoi�R�Y��u��^�T�������I͂T���7A�� /�(�ʷ�5ȆQ�hxbZ֙���a�شcæM��.LJn^ݣ�������u�?J������?����Vp�c�XSMi���Ź�N�4�<�ܚ�*t����O^��n�^�y��X�i@��O{e]��ox����<�n}}�2�TYݞ�?'�@�t�C�|?�&�S��x �E˱ -�@E�~�bJ-���ha� �'Y��;H��XH�s�dzJ=��;�?}Q��o�p�)G�}�z+]˒^0oBX'3�å�)C�eU�����f��F�cO[IYG�� ���E�= �@ �����::����A��W���"�� -"]����F�yJ;�*9��28�|�7���-e�uL�<����GC����� ݒa!�2������!��b��y�oZ�E˱ -�@ �=O�QG�*N��P(���rᒡ"��.����x��8#-ұ��%�/����T*�$���k�� ������&��&X���5��yܨT[P���>�]P�J1|���ǶH���j[�XP� {w{�`��dc-�w�hA�/agg3�3�m@͕�ȃ$�T�)�� ��N�ɨ���$4�2� ��[Kq�?^ș�����q���KK$�H�yy��5�]�KJb��3G����'���%q�q�#�.���'�>BZF��E��!�m+ WÚ�]�+\���xw"ž ��������}�}՟,- ��P�.�2 ��iu�q$�c�*Ts��p��H����E�9��Eꤗ��:a��㷀Vȥ�տ�z�I�������Ȓ�����<���S]o�0 |����q���^�ѥR,�V �Ч#ӱPG�$9n1俏r�6q�N/�����@����@J�@K��V -��φ��Kw%�z�;�2Y�נ��3������OT2#����U��G����>Y�W)]�aN+t^���T���;&쉴)� �%�ޠR-l��������s��, -�<[��=�J�����)T���Ԫ,�\�J���o���4P�#�=T� �!�U�)�J��_8�K+��R -$�� 9�\k%4�"��9�1����� ���Iz=�l�BPQ�@���-z����u�\1O�D ��Rh�m��q�]û u8���5�_Zz�B�wu�9lC���� �����kɸ;8XU��K��9?�n���"n'��8���Ň�07��Sk�|�{�Iz�j��� �O ci�d������J�y�ߴ'�ϭ�@�j��6�&�> 2�����p�W�,�콅���; ?��jN�z2$x1�2�Tۄ��^���h���`��Nڏ6fׄ����R�N1��W��n�z-! T��DQ�pD�&�٬Սmٳ���;�l�J}�<��7O u��L�����^�C��8�F� pW��m�|�k����:[Q�3*67\FGpՠ�C��Q����}V�dsZbb�~RK�iW��'�C����;��βB{?|� ��i�����f@WBc �$[���5��n"�R�X�vs7��g��Ru+s� [LPZ h�2���\KG�H���D�$�`�9\S -(���γLrN .[���crF�L7A��J��� )�L�Agk��;$i���v)�J�薰X�D�5��B��z.zOz�t�Ӊ�@>C���Mi������0�;ݼ��[�U,���}�eW�KC�ɱ|��49�����Y:��꾀�O���o��3�o��G�ū\�9��j/���/}R�n�0 ��+x�! ��.i�4��0��5�@۴-L� �N ��Q�]4�6] ���=>y����)��4 �&��rj(� ��.��,���L��X�6������¬3�V`�N�)l��Yt��%� -��N��uN�sx��t�ZbX���VO�Kc�qY�s#l�Dho��g���h���7'6e%�.k2rA�p���x7B� �s_v��o��꼲T(p�����PG#�v4��[�H�r�,�5��6Jh�$�s�i����R=���DMN��o}�SKs����,�J�� �Y�f�> �L�OQ766���s#����{m��k,��<�t�|���H�y8ݞ/*��=M1�E_)C?n C�D�q��!�/��=l�V2�5�%0HM0K΁ ͚��@_���|�)�,��*�Z[ZZm�}������T���F�F�lh��*XY���qĐؐ -U�#���^B�E�9��I����92��H�QԶ]��y�l�Z�S��^� �%r{��1K�F�v�Bp��FT���rmB�YX�C�o�T�����L&&x�7)���juZsy��4�mG�������"���2����L+뫲� ��V�仾�-�� �4��$�t}�����C7�D묐�}O�(�I����%�7�Z[{�G�=���==ݒ<�Q�n1��W̡�$ -��Ҵ�TET� -��'�h��Zl��x��B�w��n��/��<���y�6�jr -M��w���H �����Z�*����'h|G`wDQ |nQ���OȾ���,؉��;a�u���D,���v�/k���#m1�G��I`����hK����]��\C�q�-� �G���`Bh��������ۻ�ǻBuZY[T8b�ڗ��Y�����&G -Y�PM%�EŸ��z����*�s��$��p��>!%Xg�;�o�>&V�^���l� -�+B��P .��q�����Β�v�Z.�l6.��d��^��>?� ��[9%�W7X����h޿,�2~�����L�o��* -i�U�T���6��/�������Oo�0 ���<��i�]��MtX�m�ݚb�-�&KE'�~�Q�3lv��bXz|��I���j�P�i�m!�e�P�[x7�f�Q#�V���� � |��Wa���P�&���{��C��X"�P���������(�(=|��f�1W& -���pA\��}G�z+Iz��"ش�A�=۲@o�ق|�)�&p�b��6B��Z��>���_���ad�P`��M子���JO4�Z.HJL2�5�u���4�4�a�z��,C�O^��9* �Zqj݋�^�� �ư^ 1J���d*Q~f�k2�|���|~I,IҰݦ���#Iw���TU�k:�?��1��g�b����LZW)��^/���Ou��y�����$-�7�:)&oB�$݊8PFd���'�K�Qu{�V���@z����%��?��|ϩ��grV��{}���;���y�~�RK��0��W�a�}�^��R�VEj�J�۲B�3!�:c˞@Q���cU7YX_"�?�x���J�V�#�y�;�>���m6f0�_��P� _����g���-�+2F�`��z�!̬�' a�d?-���'Wp���l�Wl1�8S��[�g֍"�`[2��_\���Z���Y� �J�F#EIA� �b�� -DH�lcp���m1��q�����V [�4���e,ak���#�6h�S��T��+�u��2�9F��TZ,��x�E��,�b `���Y��� d��C�nT��<<&���l#\H/8 q�w�$3���-�;���6a�Ҏ��Vs~:�+��C��.RדFt��'k�T��&���m�*���8S��_&=C�Q��y -��<� ԣ�R��8?��'�%��oޒ:ˣ�D-qu�;Iܳ��Q|<+I�������H���R�j1��W�!۸1�&N��$Đ�Bz,��v�+*Kb4� %�ޑ�[����"4���'�?�6BM�!�( [#ky���.�W�lR���6Ac��Y 4�E^�o��ۆ��+�`Gf �� ���Ē`�z^l��yM7Sx� &���21�S_X�& -��xG�ݡ�l�V -���%h�s`B|a�m���!�ԅo�Pl�S�A��������jy����P-K�LP��& �p��jG�H!�!TS�`Vy�Q���ABWU�9��^��~T�k6��b� �Y�����c_� �ݣ�����{O��!����c�hA#�o�� ^�e#�w��Q�_�=۸S]�YI��M���sR8���p����~���-ɛ�4_��夀I2����Qw� -u��_����OmQK��0��W�a ��z-d �f�HlU�'�hp&Ī�#?�����q(�֗(�o��g������BK��V -_�׎��1�&�Q#X��A#;�L?Z���<�� 9���ME � �_d����|{����a +ڢ�5|�@f�<��'2��[�#�����-}����4A)�{�r�z@]������1v�^=B�$��e����*"Uٷ��j �O5�o���p&XA,TS�`�hܓ�g7 M��{v�=J�3�`~@ �[�"AwV��Q��f? [N����֡��Ѽ�O���l ��.�������^�t/�d�؁����/�����t�^8�>������oXߒ !/l�<� -V�����J@6�n��<ي;L#j ��r�.~V/�e�u�.��yQ.3��T���Ƿ�A����U{�oA]�5��O��S��SKo�@��W�!� ��^ !$���ڨR��R �����n������:श �/�wf��ؓ+S((�h)uފ�/��!��7NF���P -+! �n�z�+�R����?�+r��co����*���Y�`��>��MpO:/P�G -da�3�D�Hj6ސ]oP�FoP��ֽ�ڂ/ VAJȵy�b]z@U�9)�.�J� z����l+hW�}��/�����K��C��e�S;�K�pN�#% -7� �Y#�q�W�~%��p�Q�k��(T��U9�_��p (>xk�;�F�>̶h���\��cUQ]�+�� �������[��qUC���Wy��2�ZB�Ӧ�� -�m���c�����W��e�Zc�:�đ�nSk� /i�}+���|��C�)�V&���1��s%\�]E�Lk�F�Mҿ����y�A���N�g@�>O�����L�7�G���O�^�)= O�J���h��i�B� ���O�]�AOA ���+zb$^1IИ���tw��ġ3�tDb��v \�e�7�L_��!u � - ����K��2���p��##Xw>C��� E!��ޡ,��+�o)뵁=;���(L0��$�a��Ϫs���|P�Y=2�P!�i> 3�D1��$�-2_�������sЎ�-!@�^��S@n ��8��F٢��W`�дO����i����:��*�0C��������kg7G�Ej�A ���rB�.�8g9� +_ ��W�� k �X�R�I��u���>���-T1�ח�aP)49�wp��RMk1�ﯘC�qbzm��MIi �4��kg���$F#�P��;��i�T�E�7�]^�>CK. Ӥ{'ky�T���^6�Y3���������/=�]����ȅ ;qSxϑ�&`�N,�����x�h�j���"#|�J �r����xC��a�'��t4�11HO������~� `l!xG�h��%ޡ��B����ðw�����֨��ң� �� -�T�^z}�:J��H�Z� -M���:;i�i��R�~��=�� V{d0��5 -g�{�3���ߠO��U#,׍&�����^���:��"�|�����Ι�:�2����ɣ��=�6����A�'ӷ�t�t��r|-�?*ukLf�_4� �4?�VQO�0~ϯ8$�*��:hWZ1 ��i��er� ��ڙ픡���s�����KT����>�{�� -H0Ι‰6����<�a����,��g\C�s�L�)|˘����2�S�攀;��p��*g�7*����e��>Mp�FL�|�\�fbI5�,rt�+T�&��-7Z�Y*0BZ�9IJxV�13�D9�Qh�B�Rm��R�@��m9>Uq7��ۻ+KU�l2f��iH�(* &��MF+$�����%h%8 ۠.�9 -�A����4�o��H��54h���� �[��(����[�`3��B�-3�6-��bj�v�����i�X��nT�� 9�= ��;�)�� -��y���I�Q�eX�"�1Y�M!�a�ȁ6\E�����{x����.c3qU�d[a%Ýv�X�_�����CM���α��Z�da�w��8ລ�5���=��w�q�>,�o'�+J7S�N�@�:�������#������Aܫ�ДJtE�����m,5Fד�����U~��o�8��Ė�k�D��:��s"�M�?!c�^���*݁�6��}V�� �1�2?�U' '��)������_��k��|O՝��r��=�u�~bx��i��k�)L��z�t��N�)�u�ʪk��}nZ���ּ1v�&��3֫�-�h^��㸑�:���r�I�7&}���b���o��ݘ��?mR�n�0 ��+x�! ���Y�vȰ]1,=���ٔFQɊ��>*�� �.���<��5��FI�;]�S�������N*��c�4�#�;�(�~�(w��wd�P� k,�#7�a���7�&������}Q��9,i�I=2|�L�4s�D!vt�xK����7���z0�5hK����M��\C�q�-� ң���`Bh���n?w�e�\���ڢ�Ծ��J5켶V�8R��Ȅj*L+ƞRD�N��*�9%��⯚��I��|o^�W�c��;���[(&xS� -�oQ �h`4�}!�m]K�]�V+�f��с�mf|)第����f�1yiyy_�rO<_.�h�4 ��>�T�u�AO1���+��!�(Q�%�DU��B�z�M��Y��c3'B��q�@�M}�z�y����>�6BM�C�QR�N�)�|�W�Ixl}��w6G���]�r~�@� %=3��#7�/�W�M0C[ϗ��YM���@KL��e��}an�(Ď�+�U��lf����%hrׁ �Y��U@���8�-� ң���`Bh��������뛟7����ڢ�Ծ��J5l���cq��ő �T"�V�=��V;H輪rz��"I����[w�\0{�� ���{z�^�'V�._ �-����t2��]��BD�kT����-����Ǯ����"��h�u�f��R�II��倱��~9�r�Ŋt >:r�wu!���5��{�^]�MOA ���+zB �A F5F<����eg;c�+!���,� ��o�~�����TTdS��Î�2\��p�##x��@PbB5� �x�M|BᆲM -س�jK�U@�$� s,�bw�'5݌aK;��(�@)��YX��(�@���tߢ�� [��p�4]P�tT�{�W$�|!M�����,¢}3N}�����vݏ:�l ���ޠ]gTÁ͗J�#�N+*�j�-�:��r¢]84s��H���J_+�$?���}��n�0 ��~ -zH��A�Kڥ Z,�6 koM1�6m �%��C�}ToNTC�ϟ)�>5U9e�A�d�S� ����4���Se��A�|���~��)(ȥ -�v� ���E��X�P��_�t3�GJ1�A��%�Y�se"�X:��5:w�m��(=Z�F� -6 7qAi+���H�]G�-g��r�+�$k - j�dC�$�=�?�wk�jr���RA��JcV �:���VK!F�|[�L% -���LF#����v�/� f6먽������/�����C����1�=����GX{��sg�p�7���GUӝ�5�h�� -�$-�c�Crr��$��@y��=����#�S�,.:����j�>c:|�Uy�W��g߻����G|�d��5� �R�n�0��+����<�^�u����(�������E�&��R�Q�߳4�V~�������H�M�@��*�<0�3^5�=��]g�~}�Y���r7�|�kE?����0�6�=�H��*����J��b��*qt�X��F9�� �a[�'��}� �|����Fg8A7>y��h-h߬��k�J�F� ���<-�.@���Z���ܗ��ݷ��D�^�kŰTJ�*"c Kõt$��#i�S�̩�FIm/��,��C��+�:��֢NF&��X���8J�ӿ�?���0nu�h�6dZ�g��Dtg�8>�7>�dA��� ����%��9�ʹw�~Ԝ�����N�,�t9�Eh~9�#whC.�^�}>�pi\���A�My���� �VY��)��B���0�>:Ώ�t����LJ�5��<:�Z����_ή��=�ǓB����oI���RMo1�ﯘ@�k�-$�j$UJ�S�jvw6k�ؖg6U�������؞�y�'�B���b�.K4���u ��7�F� ��P��X�F_���������J Sm���,:�k��Ea��ާy{�*���)G��PC&��ʉ|�tZxM�q�Ν�6�H*���#HMP5�B��:��Z] ��XU���%��n�5�dh�y��������F��(�B��$��F����Z3j�&��JJ�2�K�;qh�e ��)��o���Ke��s!�sa���@Whr,J�P��H"C��FP��f���]Z�����<(o��ٿ��#�u�^���n��@�o�Q$�\��:���+{��tRZ��<�]�{�~���߇i`�ˡB?��TC� Y��|��ޝPó������,+�L�ng�F��~j��so�]�V_��9� mR�n�0��+��C@l�^ t�[��ĮV��� ɄX5v4v`����4�)���o�y��[UV�S��)v�U�S�^�� |퍢a?�>�J�P�@� -ك-�w����O4� �o��qփ����;������6��,i��+4��jb�0Od+M��������F�@m Gu�w}�;�[�w��⵼�'N���O�b5N_g��q���/ϋL&(P;�V��|�+6Gg� ����OK1���)� ��ū���T,h*����v��ɒL�E�ݝ����\B&��{�2�m� -�-zj�&�w�5��:�l�͠ /� PK�{�^���\�_��'dSR����m��z&�Y��`�z��O�~A��h�A 2��(���5�̼sX�U�� ���'�&�@�ѡ[Df�Z�ۿ#�x -F�L@���:)����� -evy��gL�BJ�l��u�MO1���+�`�B�U��D� ML�β��i�Ί��ߝ"ˁĹ4�>�����W4V�#S��=F����(�2��[e"��"��U`p%�V*,���"Sb䁀�͋.<B�YE�8�X�}�i���>�q�"E�� �61���y��� öVDlC�zZ��� -�l����}0ۊA�k -�(*�t�Vl�A)�}�랗��j�H����R ;A�dЦa԰3\ɋ�] -�A�ÌT��+�]84�2�9F�5�-ƅ���IGh�6����o6����"� [乫�#ANd̻�p��;k��Q�������Oq�T^O���X�f��H�;Eۡ�=WȗI�!;d���OK1���)� �-�����U*������v��IHf[��ݝh���\�L~�{�2����� -���(��*;O��7�����6*c t�\O5��{yD6E9S0�yуI`���FA" Q��U�?+it Za� ��R�a� -c�D�[:o(�d>`[6�н�;@j��� -�w��k��)��v�� �q|*�Z��~�{��N�iz�e�Q`�J�Z�B%l��z�qD׆�T���y��P�����Y�9����ej�� �]����?2��ە�~�H�aM2W��w ���\�� G���Hɉ�=/Ѷ�'w��� ���zI!����˿*��e�MO�0 ���>ph���+c�b �qDBi��%.cB��8����b�yb�}��kTX�1 �uɟ�s�n�I2$0��F��A��g�5�5ʯ�Nj"]c����M� �G~6G�=l�F"t+��y�� ���ԕ+of�r֤�A����9�q�%B�- -7uO��`�:��6#K�K ��hд�`i��?jGp�OI�2�\$�s -%jnϡN���!@߱��0�4'�O!���:���FYMU���i 3��R4[W��l���I*�p���(�^��'h+j6����k����O��<�b�S�?�X��8�����D[�����k?w�ʺ�;P�_]��ج���h1��dꝣ-y{E�@�V���=7�Q�w:�iSc]�'���wp������Q�����n�nb���]�A��Zi��M�W� �TQO�0~ϯ�iL$���u���J�������\��X�vd_Z���9III�����}�}w��K���̅�БU��t_����1'�zp�)��������+vb�K�U���9��2��V#��Bߢ%C����v��i�q!)���ha�g� M�c7��r%���Z�Om|7(CH�<i�{���� �J�v\�N�] RF�����n�{����z졪�)� QޠEI��FQ��Ù�Jd��'�+t�೎C� (�����q�+�|����2ĂI�$��8M���9J�Ԫ���B+�����^����PLg^�V�!h&��Θ��5�i/�0Ny�&�(l"{�k5�4���Bj�/j��u� {��d � �g,y� u�C���4��TZ}Ã�G��(z��*��_W�,�eC��J4�q���J��|t�DJ��=�7�����.ҋ8k���#-��_��!�Ҕ\\�Z��`q�t�z�m��@��"��G�%Ҩv��:����+����V��#��|���*ⷎBf���k�6���J!|SE������_���n�_�Y�����?����� �n�;.OS�;n�]Q�ۇ�!�߹�>�l��"�ED�`4xr����j����Z��W��]�Qk�@ ���)�0��S�:�SGDŽM�= Fڦ��kZ�ҩ ��rb_�ˑ���7l�r�,:��8�ɷ[��w�Y4E0���x(�%гE'��^��4_oȦ /gCX9&X[�r�a��/�>��ÎR�b��:r0�}a���i-]�kre��Wl�Fz��8���謅�i�Δ�r�d�^��q5�ix :��kh~��yJ��$�:�, -��Cn�Ai'���H�7j�o:���)X0�k�-j�ʡY���Cr��RM,@!�=�L_��@��R���p�BI������./\G�9�� �v��J.j?�v���~i�O�)�e�OO�0 ���>ph'�ĕ�161 �8"�4u׈ԩ�mB��8��D����{�训[(Q[�1 ��޴���a2�%Ѓ����E��U��U�Z+?w�ϊL���F6��{B�ZE��9�H�}R��Ǘ��B6��;�0 -��DvB�Z<�藍":c;2����Ug-h�n�Y� �J�F#qA��b���!%�/���wO�Y���㨝e��J(M ��KX�E�8��Fy��� !�`h���&���k�-6HYl�x��%�@(�G�+Y��Ÿ�1Et 5�dL�����@��w��Ytv>��_Pw��$wO�5rw�_�W�uRN��"���_�]� ��QKA���S�C��)���F�E��������:w��f~�fM �^ڗef;�� o����2��Z^���M� y���� �w���2 -c �n� �&S�գ���1��ˆ:��ה�0iZt���BHe%�����1�o�>�� -M�h����f<���Fm�Y�YPA ��s -z�?$Յhi�*6��y�/�� �s�$�5Ԃh@%X�@��ґQzy��15�jug��m�K[�U"υ�8��%ԅ�����x��Y����p^C^g�;�mtSIM��`o<���{�P�m+������vCd3�ԍΦ��E=���M�Zm�� m�J�T -� �;��m��AS�fR_���}I}ɸ�n�D�J=��˴o�a֡H�ęS�>ͨ�0vv�����6tdW�ʞ��0�������T\��eO�:ʾݮ ���c���V]ށ��2�k�HGJ�����[��yԬ���9�p�84�(Vt��i�N?��%���u�zV����_�9}{�^L�֒'��*Ig��-¤=�6�x�g�U�k�e�r �^X�=�C����͵��o׵�W��r@�3��] -��@�5���xz�����F:k���E�g��X�~��:�&�`��O��MZ<���n�E�?�-��>k(��DB�ù���5k��9��TQO�0~ϯ8$P��4����: �IE����\k������;�IIS�@b��|w���.w�)Os�1L�o�摽�� ��4za׃.ܦ�@�}s�-���L_��&y���)���QZ"L��P[g����܏q܃9.���I��j83��8���'����[Hn]hE���`S��"��5_���A���W�D�Y�d����̻�����SW�|�M��3s'Т�Ê۔nH� -!��$=�249#_K�������T`���yF�׼��NuJ�= ˋ��������z6�.uRks3Ü��\����*?<�t����]8`�A��- k��,�!�ĚxI!#'�ߗк���(RgMg�N��quC4�K�� -@+���s��D{a+�0Ql�R.��� �:>C���]��q�̶��Im�sfS�V ��-���掌���ΐT�&�7�:[H82 ����H�e82�ދ�%�W�Z/*bbF�{���'n����4�B˽�M�+��Ш˔�x�vd�]�J�6�jLIc6S�ϣ�7��-[�2��oP$�A9�]��1���'��k D �ib���6�3����t�^ȸ1�C�E��y�RʯQ���vy�����R�Y�&咶�h4���"�o��~����?-�R�Wd_(%O}��{�Wb81k�O����jA ���:�iB���&6. ����S�hw���c�0��1%�^��u�M�.��~���f�1��3բٷ�S��>���Eu~R� |w^����΄Y!���a��?�"��D� ,l�N�:3�< ���3����.O�����0��pe�(�@����j��G��^ �7�)fPG�!@�6��S@� ��Xl -�c^��ȧ`Bh�O�ݽ/7���ݲ�ڍ�6(����fP�`���[��!�dB��W�k��V;��EUٞE������ -��ĝ�Ȍ��X��1�6 ��+�H�)�}���P��j��ȤCfxS|��4�Z���1 TO�/O�a �T���P���)JM��:�/�]=�a���<�~I1z���b{�����x|g(Ϸ��{g����]RQO�@ ~߯� A���� $J��hbn[�.޺����# #}i��������B��Qc�Ng��{���&F�n]x-��B�V9���u�ܲ~{V� ��b��Y��F�:�0RROҶ��8��S�Y+�6�`�[`"���/�St�J]p��'��.���j�wz[2(��� ��TԮR�k�,R�}i��=-g��fFO�R1씇\�҆1���R:b����,�1X0�HU��¡a���� ~6ڡ����r-��"��M*��ʂ`�"��'Nne���"�Cn�Q���q�_�eެ�&��W�<��L�?�!:D�uSMo1�ﯘ�JD@��4��HQ�z@B�;�N��e�BQ��������^l�<�{o<;��rJ-<6y%iA;���K��t[ ��5WVJ#��'�+x��;����u;��9AC6�7�Z�w�` �_.��V#c�`���sGJI� -��V�_.��v�i�U~� )u�nb6�[S�!H�t8!^�_#%>[)x����O�N�?٬0M�S��qf�^�����'�Bo)��.����/��@��q����zN���3C�����0�{�N�uނ5w͚�#�UP�N1��+��@<��nPI�!�#�������L$�w��f2�Λ7���2X�2 ����G9e�ۛ��LGF��@��֌\ 5�v�@�;E����+0������1`�".s�~������1lh�R'!�ࠏ���Ï�5�TMo�0 ��Wp@��A�`�%��e�֡(�e�% -Ŧcm��I��`��X��Q��N[�(��Dr��H �1ʘD_i�#��7��`� �t�{�$��ri�RG��{�0۷4��`x��:T�$�����o�8q��z6o����e~jL Ϡw�)�A�'��6/��t�Pd���ăpo����Ej�U�k��6�u��� �o��C|�����$�����������?��S-�i���d� -D���;�c�LMi��%�ٻ��-�:�T.>1��W�Zo�ۣR/�~`���U �]��PUݶ{�5y�!��g�ג-��Yf�$��H�f`�)˰�o� t�ȭ�hN����X͚���]� *�e -�c��g\��\�>y�uU�O�0�_q�:�T-h� -��� �΅XKl�vh��ƭ�?��}��rv�K 9�L�3��'��ў�N��qcx(��BTtkf��J��1��ĕ�oF<�R��w#~TL�E�,�1z_���8����b� &�6h���%E�JW�����تq����2�J���*��^���Xe�L͜Prd������.���,���.1W2/�B.����"\IJڪ�p$GyH�$��F���Jf걫�i�4��%�� V!]Ad*��T�Z+����G��ena!�cU���蜌�p�2�Q[�i)?����x�ˆ��O��x\Hݸ=E᩽�nVT�m�OOTy*G�]� �3H�J�B<��=� #�J��o��;���a��s����1߈{��j#�:"�:"�tq��l���ȨA����fq5B�{��m��0�#��|P8?�ŁJ�7l�����~�������f3h�ȣ�F��gt?��B|��֎��nl�u��]�4��M2^Bz8����� q6��E� ���4ˀ�qăf�D闸0�54u�]z�c �Z�8�DƦsf�4������/!�� �ǯ���� ӭ��^�#0�ǭ�y]3hl�eU=���io��*/5rQ~��1��gҷ��+�tIM@���{x�{Ѱ1���i-n��p6X`�Z�����X��H{[Z���]e%G���0�a�I�F�=��V�n�6��W̡E���N���0P�il E ���0�4�2�H��l/��������A�ꐵ��Ǚ7�Z,�WAu�\���F�3]wƭ������~�]�+�ӵ��س��f�X.ߧ@*�G����V���|K��#)��]O)�P�=���-�j�U��G���*�x=AYW �u�C�#�" ^[�7��84*r�f�zG�6w�8�!z��(�u�f(d?n�ō$���8�`k�G%��}���� ��T�k/��籊d�\Kg�^�2;�n�&m黋���ݞ����Cσ �V���_f���p�)^�hJ� H%h���)&����m�7�>-�D3� �� tE�������I��B� l�S��a��%���'����)9n�2�RQ��9I�@G2�pGD"̦(� �$� �2<��A�$��>㮡#&AV6�b�嬍��o+J ��Epu3Ф�bj�z�M�\�"S�H�n@i�`4�T>l0�$2��y�����t�DjH&^��o�����������﮹�%,Ԃ\”�m9� �G���.d>�qDS����p�$Y��N �RO#�}?��}�\���ƚ���y�@�I��o���)}�k}�Ly?p�v}!� ��рH�]�wE�26�s�O����H�����bAWX�F4��ZX�T-����W�m�[Q�ْ0�`���Fa1'*w�%1< -� ��� �~*� B��*0��M���/���R�ҵ�+kYV���Q�F���e�{:�� '����wm���e �obI �U�m�_�U�*���{M�@-��&9]0��}}� ��rF���G��t��T/�N�/��0���G��1��1�Z�}� ���j��.�� -G��6�/r~;&�Lu����Z�[&�8��S�l�7��5����F���m~��K�V,�qi�KƬ�b�h�u -Z������ X�љ��j����et�Q&����b��i�󈽘B��%"�Uh�h���s~)���5g���j�.�7�'t0�ϟ��T�/+�3�X���G�ݔ&ՊU�$�y���ej� -v�����������yTPӿ߾u����2�-+Et��Q�C%<�����3��R�s��P�14���nFf��njq����5����G�� ���l�i���<;�R\ W<䑗T)5����+G�i]ztDU���[�q�"��,��J��8E Z�bs /�[��Q��65׳�*��)�(�-5hޝtz��v�F۾��g�� �Fˠ�*U9��?���G��Y�-�� ��.t�> �85�* ����-؇_�Y������Ѓ�����X���qA_�[���U-˩����yon��!�fV(�2.�O�溡��l�0�{bi&��p����o�"���I��xF�+�$�D5KI���c{c¬E�1SӪ]u=< ����V��b�z_9��V�9�<���ЎSow���LE$S�S߼�0�\�K�*�o�?�r�b����F��>7�G���ύEPKkA ���QE�jk�X*�"�ބ2;�u��N�I����Y[�\B�d�=��%���q�����$��l8/��F���7d�e�8����e�}Z�e��T����$8]�J��#��;+Ɩ ; -;�w��k��1F�z�����jKG+��� ���؃�u{��tr���:�j�Z1�U�B����\KF���E��Z>K�Za�J�7�����'I�/�Geqq���f܆%�����")3���eYD0����� ?�E�}�㛥���H��ф�2�v�������,�3���rC�6;0�Yʊ*�4��� <�|5��MHH�}� -O��9�<��w"�?Z��_ڒa �ȱ����)�����T���q�G���ʸ�2��[��+�H�AqaKe��� -3Ǒ�s�%?�V�n�0 ��+8��H[�ڴ]�l�Ű�� -�fba��Ir�`迏�-ے�l�$�H��=��Ň"+ �$g -#mO̳��/�dz�����!�� ��?J��E�7=e>v��]R<���ܦ������w�ɕk!w�H�^�Sx7� -����&Md��l��x��z�ǗGG�BS*�m���\m���ԧ�eQHE�v!5�Ǩ?έ�o��u�t�9�o��&���O�[�[�Ϝ���p��o_sT�F�s]��渓�OL��Ҷ[�4}�N�h�$x�$X�G��F�+�� -�%�3�Bl�j�a -�=ݽv&��?���| Q�7c���w��Rsb�r���w�@�^I�U��ZZ^'�T�j�0}�W %v�e���l���J��>�bOjQY2��4������,l�bkn��I�y�C��`Cc5���r4���$���K� �@�oδ��甗8"��X����kj!�#���`� �50e�_l��(��V�e�r&�7�ajÂ���@`2�XI"�-����Rl��+� ���Q�*wJg�r%@��J��:��ay���w���l�,왁��#&��6%5mT�c$��iyH�����S�ԺVd�q�d�)����$�1�H՗*�O�� %)�|ݘ?�h��}X�Lê�6.ӡ:_�y�,BϜ|�/� 3����?�͋�4U�}B^lI�S�� M��+b���V�6�G�H ���0�����8�m�w�ݮς���6;������c:&at ��I�l u'8����p���G����-��+��Lة�����>��5����6�Y�Z"��u`�˛T{ V��h�9���Z�D�k�i��d��)� -�k�>9ݕW��(Q�%����.FWڨ;C�?�ةŴf�yF�s��Y�B؄=��'z �<}�8�U�&+Ϸ -��Sێ�0}�W��:���Z.�E[�ڢ��i%�$b5�-_BQſ�8�%,<�B"�93��d�Q2LKn�YgD�����Ǔh܏�? a!%�kn�օ�qD�X*}0bW8`i ��D�\r���0�t_$�}��|L�u�K�� LmX�"T��.3H�$a�w��3�e��/Kʷ�]��%�2W��N(9j�)V �7u�V�ǧ�ch�sw��2aO\��^��2d�*oR$���<�$��jN�u��J=7�D��A�L3Xm�t9�i���ڛ.UU��"]JҲ�m�_�q�������e���Զ��gP++h4�ɛ���wڻ�"Մ��'4����N\>u��s��C:xEt����TLvZH/ s8�T�����M�]D���t��)�:�Z��CM��<-��h�w�~IzkY0iY �=��:��"��B��cONI��ޢ��8�F�Q#x-��L�|[�L/�j��!"1v�T^�r�N#|)����<�����6��V����� kt0�GÂ��%�@:i4��d���*��,�Dѥ��=sՙq� e���`[�p��==&���2�j�Q!v�C�� S�)*�â���DJ��'�z+��R�h�mG�QT�����W{OX��*���9���=���4���bډљ��w��h�F8HJ��n�0��:�B�Ru��Y֫p9R��d����t1�+f�-w��e��͆����%ŧ���_C�3d�НAh���]���cz�q�tȸ�-$�]V��g��'�?'|R���I��/�����P��F�3;�λۈ��_�W:��=��3�d vX�W�7�ey��BM��[�UP�NA }߯�` �Wo�[4j�71��vى�̤����vHh2����r:g��Ѓ!|���%O�7cʀ\�S��8&����l��*6��\)�p�0��h8g�}����NF0�ӆ3w���s]. �,ALDJA���J� o�#,�$!��F'���՚��LVh�������K�?�d3ms|W2h�n���܀�g�Q�[����-����˷�<�]=ñ! ,m =^�����dl!��b5��lS���Jd�޻z�.�r��2��`ݮ��a_ -�����f+���w�p2�(n�b�E�~p�b�]����#ږ�U����'*m�ͯ=�b���KR� '#,6��w(��A�Z�`]#����^��{��ٓT��cOg�G|���ÞA����HH�Bxr# w��[��_����Y�{j�� ń� �'��j5)�`�ު����{�mK��켝��TMo�0 ��W�@Q�A�`ץ�ڦ-`(�v��(6 �%C��C��(%�?��tHl�|���՗"+ �X0�����ve����hLG��{� �\ �����sƷxI��PE��&���j�p'�|Gm \1z�Y�� ����kf,g�b��L�!F� -�L&+I�֥U�>* 6CHK!h���Ђ�( q���9�\�1P9F�-���m�xxz}p��0�1 �@�;&��mF;$ڨR�H��Z�4�,GS0 -?gL/՛wd�q�d�:�ӚU'���4h*�W��%����� �5��f�t'g�d�7����o�E8�}x�Kt�^p�4U')Ek��� �\+騝�a�7�o)�eB�L�w�E���5~�V���� =�����#�#�����U�et�4��=(����s�mL�{&u�E���q���-z���J7��O�v�ݰ��c����3lO�����`?[�1A�7�К�J;M�>���[�\�!���*� �#,�������qu �X��F9��=,�TX�"��b�j�މ��gOq$�� �Ehzk�?��ik4�(Z]�Sl���SR;<{_�����m��q��*Bm� k8n�#���I�Փ�y�T�1()?����Ӑ�"�$��F��w�h*�k��dș�f�>(�Қ��! � 0���b�+m��Z��B_�31�t*�n'���^s~ t�Y#��*��Y�4��5Iz~��Uqc����T�� `B������#oh�w�$��q^� -޽b�(�@���qe�mD���8�����"ۋHE�N��R6'u��_UP�N1��+�(�@<��nPI�!�� �Y�Xڦ3 �;��C3�7��ޛ�]v,� ]�_ˇ3���`j&CCxw���@k�"�X9����;F���O'pU�D����0�P����ǖnG��-�x��L-�q����#F u�jl�J*||L�4m:��;v�5EV��Ie��S��C����/�j�^t�N�ġ���-�p��t��9��&�}䉉�#Ψ��aY���"ScZ��6U�'�T0T��EYzqfx#&9����e���5�V�n�8}�WL��������&u\������4��J�@Rv�E�}�e��>�I<�3����|)��3�0�F�ج͡@}�Wx=���<�\Ög��`ʀ��S�w8!������[j �C�W�k��OTF� ��w���$�i+�0m8��%*�ѵ��,2���D�lS��o�M*0)¶�2���mt�c����T93\��#ێ������Ϳ�涔̤���iH��za{nR���Z�*Fj��#_�Q��O)S ��� Jmq��vv��oh�vF_���o��P\�m�j�Za�Q���� >�y^�ɼ�#�FMVm0wZ�-�C�:���4�� Ah ����d�[^yN$�"�����`������S͔�[�f��4ȎP§ؙ�;y� *m���*{7�7u/Q�}7��)�ɝ�ܐ�N|����el���~�N ����\� ��+܂ E��*��'+��jp��Kۈ6]�÷T n�-\ߝ�GZ���궵y�p9�;ފ�����e�1���W����E0�C�,L}�7��8��ָa�q&�+Vh`jO�9)B��F`2�TI���2�#|P\��WB����w ����U�ʔ�q%G@p�j{�u3�m������P�1W05��q�ra5w��i�*�"e'��@��fT~*�Y��&�IT�뒩gCh%)`ka�C�PeI:J�| ����0�3 �_���ği���!ܤ�wG��)�Yn{�2;�/����;tc��PHg�IB D�����pt��aFd��Ƈu;;�u��?�_�_#R{�Z���`<�Z���^��~ܣ� wFa����h�UF���� -f��p@���;L[tK���͊y�.�3�!�;�\1�.3�k��S ���6����R Ωvr� G� -� ��=?c�m�]���$Y�~$I+0�~��y���-B�����i\�t ��oUPQO1 ~�_�kQ�+-��b1mH��:Mn�G"B�>�j��98�Z���؟W�/Б��4��� -�Ň��9;i�~� ЇH`wAV�=���L�FW�:���0ss�ĉ�s��H�+��j7�].`C; ���bX�\�D�K�*L��l�ݠ���̠��b4~�^�18Jb��>�j�i��h?�}�]_�\ׯFc�Qa�]��^��>�7�LKؑ5�&�gM�'���{�ۼ7�l�A���:��WJ��f����l�"�y� ;�:�c)�k��pVru�~H���һ���G������xx����'���hm/�Z���g�8�pP���a�N�^�ϗ��}PMk1�ﯘC�&��5v\�&�B�� F��]Q�Fh�또����K)��eWoF�k�5� j�"����i�N��K5-&�F�R[��u� *&�<նñ��ƒ�)ڟu�RW�-z��N�_�L�}��c��k��^q���l1Œ`!���ü����ؾM�"���F8���|P���j�,^��b��% B��,�w�����>S��R���|�BG�j�Hh�6j!3D�^5�A �T��@۾�iQ��'�]:�]Rӈ;K����J����J��Zt/;`�۠���(@�d4�E�"�L|Ge41z?����2<�Θ6� �Tp�ۅSR��.��R��Yc�Ҟ�B*0Bޔ%���ֺ�) -MZE.U� �b�h����4=�|un�\`�`����%f��MA;���J���6�H� -u�h�k��'y�22��F[]"�̤�)���-/��j� V��s�)FAk��3fp*����J��9�E@���>�>0Ӓ�~�-��R�^����^Zr�5 �����҃��uo;t����J�L�n��A3V73��:9��TZ�w���ا['��� #н�ۢx�D�P�[�=[���Zt��~�����S���qX�uܖX������J��4J���O��h����;�}0X ���89׼ݬ�q1�T���3�P��ҝ�yS�0��n���rVr:�α_�ؓ$����zW/�}��D:Ux;� -sT_��-�L�r+��[�'����%�X.Q��f����Y�bm���s�)�ϐ;�1s���#^�T~t˴���kK>�q� ����Q$YZ@��Enj��o�;����۷�I�t�n.B}Aޟ��u�Qk�@ ���)��$-{]Ү]趲��>5�(g�}�zwHr�0��'gq!� �O���JW�!�i"���� -����:;���wA��H`����=�v�N�<�\v�Na�pʼn�}���X�h��f�O���mP4`�O��RF��&�\" `j��d�mz�,��2�v����c����Q�5��O�!��sh�s���ޗ���׻�᩽1�Pa�M�����m��*fZrώ�Q3Z>�>�4��C����FU�˱�^Ű�H��ʬ�/5�6.��A�� -�Qj�a�]�E�䆹|��` Z��)oӷ2�2��d����L�s��Wa��6�h��Q�y�$����/�c�Ӆ�ϳ�1$�G�?t�c ���u[��?��K�R�}T]O�0}ϯ�<$U핲��V !Da�r���Zj{�SVM��]�;N!/I��=�ؗ�t�!E�3��uFp����/�,:�D0��LX؈�ޚj����}�B���A��6�&g�7g���պ�?Kq>���u�I������&B�s��d -\Il]8el��2�2�M���o���\p��f�ev� %�@����ʼ����~u�K����9xcRa�^�›pyhi� -Ñ����d;����!cf�^JDfQDXZ �:ej��C�M�CIp,r�Ҙ�E�s>��՞�a�Bɍ��V>F�'���< 2��:��+4{��$�0��hie��#Z���|TJ�b�B%�v����=�_ҳ�'�Gy�v�y�X�Ľ�]x}%L����� ��1(O��q�f�uq��5��� �i��Wb�.]F$$�&��K�t^�1�ף��¬�R>+�F��ߧj3�(�Z���#=m�B;����8��9�8���i:��x����݀3'@G���}��!ME���*g���TMU���)��f�Al�������}����S����t鹧{+N�q:@80&e�F�s�R2Bfw�� -����:�/n�ZYA"8t����0�?D�V��*�R��7�����i�F��m��P6�*e�eJ���4q�z;������ҋ�cB�Q����(p���{�Vle��6+iN�{�5PMK1��xǶ�.^m��R� R�x*H6;��IH��E�ߝ��\�̼��X=x�ѐ4"�$��e�LO��f�,�Y�ޕ�h�!��EHp-vJ�h�댨��}T 9�c��'#���ܯ�_4t?Ǟj�/�S�*��5+"� e �m �eau�\����. )B����=���d#k�� �H��9����I�y����6o�M>5KJ$�ED��58�xæ��$&jF�eaEG� �[wYg#>(D��\�eU�c YΣ2�0� -p��f�l��,�M�L��89=�r�%�cI���P�Y+ -�������� ]�|���� �2�Bo-�����VKo�8��W ��9�\��n�F� �A��%Z[�ʤ@Rv�m����$��-��H��O#^�I�" ���F�м�}�zr�g'8�ǘkX���)S� -�c��S:�3��_��Їk%�H����h�d��ZV����qɴ�L�{�P���W��4A�LDJA�-3#�.�� -L��ʒ�Ϋ�V;�! -M���Tf��;F�-�]nw{3{{7k]兙��1 �E,�`�ML'T��� -�EU�g�6�SF��������y��F�ۿlX:>$߲$C�9 -e&LG�7iB�;R�-)�B�&��%|1�B��2�YQ����a&�3��X��Wܠ�&����h����Ֆ����̤��uۣT�-3�2�;6�\[(�=��9��ңV|ݳ�}��M�-�� �� �ڧ���H�=�������W��=�VA���JN9q~���G��t��i,Rr״�ÞqK5�� u� �Ο;g2�������|V��r�,��������`8vǺ5M�v&:�Ei@T�-ʦy~..wU9.�ä�j�/f�j���'L#vQ������'���mR��V�@AKR�ۊvٯ��Y{�}�j�Qy[�5���!N�@����Ur'�|-h�(|G5�".�]�k���;2���ٹ_I��S�6p�.׌�6�Ã�iaP@�\��u�/�U[k�0~��8�@��6��rY�в��ʲ�AS�b+��bInF���d%�/��^�(���O��>Y)M8�4TZ�D/���j�!�~}��1+�)�oA������� T��(���3 a���)\s���R+�����H�$�]���3-���� .��H�B"r�,���%�tFaUr��]vc�YBs�X����c�pe/��Z�/wӛ�ٍ e ���J�LU�h -�Lg����(eB1Q�+y�dCUAP��y'斑a�������d�ؐb�)�rM;5��e�T%��uCV*!���łf[��� Κ��O9�O�f��}-L$u�;��T��A�B��*ߺ�m�$�@ �q���&�T.�Wl ����p�B$��.���� -�^���K8s�a�˂���.hӱ����D~�ks��׃�9�����b��Z�D�>���q^O\GPK!Aྞi��Ɂ+�� ���4ƲID�����_�1}۳Wy#�:��U��>i���M����5ՆF����`+�9���;���i���OR]ʼ����!ǁW���<5H�~Lq������;+KY��j�2�j��Q�5U�x<��M�S����[�*Mز�X���q��(�2Ѻ����!�s ��]�<��<�BN�g�inF��,�Y�N&�h�+��6lX�צ��^ }�g�5����s����TW/G�W�к�j�z$I�M� �����gC3�� -��έV�ӳ��>S'A�/���*�.���O�������d�ϭ��'.�u;���0^�~ѻ�����g����>�R�Y_u���|�U��3t>�NfYs�$��� -s����P�K#A ��E�����W[���7+��fw�g�$k���ٶB�$�e�dz�������J}�u"9�1�G�c�j� �N� -���ֽѡѹcӚ]�* �\p ������>_��ÊN�0�%�: pM1Le��#��Sn �1��e��e+�32hKPw��S��ޕļ�:�+��a ���hտ���]��_����E� -TN6ZT��ik� -��L�ڍ|T|%Ih�C�|�F&E��>��y��E���F��j]�q�+j'[�P����_��R�}�](�XАކ� -�)�22�z�"�v���1@�999�|0�'�h|&���<���� 7渶�� �w==mL��ϟ�ߋ��Wmo�6��_�),v�}( -4���s�$E� Z:[DdR#);Ɛ��#%Y"%�)��-�^x��s����,�H QJ%JK��e�F��g���9&_�Ȓ�@�?�R�$7 �� ���Dd;�V�&A�KɁ|J)�9��|���Ob�,�Ҍrr 9Hr��� �D��Q$��$[�ZHUnx%$� �e��(�v7�)��+��/�\S�tGqm�`k���&ӿ�ƕML'T�-U$f�� b�e:A &�D.#���*���kP�囄ʙ�����z�2q���L�d-�3o ���L�,�s�+p�<��$�z���|X0Xg�IOl�1Jo5�)��b��~�S�ooMf��H����|W�NG�(�ԩX�#H,����2�b�a"�kS%�ƒ�M�d���j��^��uz|L.6TZ��-O��%�#�E�2G|����˔Qu J��+��=�͔o���2�Z�Y:�@��\J�i�����s��hY ��܂%9b �J�J���.��*5/�{X�<ҁU0����`��u��E�&���� ��UX���2�=V#D�U�G�ׇc7�B4jsͳ�H�Xu��3md_I���-Ϥ c�I+ϴ��8ר攧]��i�\2 O݌�G�� d#X�DZ¿9����C���ms�����جZ��)Ő�����-ݮ�*x��^-,[ņcl���IpuV�:"�(Cy��P���PB&Cwу,�eq���gMW��(N�؆Q6���s�����.�;�@�/��,Fؒ���߇�Z�}���K�*�[���j��� ���,E�5�<0��������W��6�f{ �[�4�p �����7�w$��n���Mټf���}����I�K��e���j�F�xAdE?ue�q\�0��7r`7MS�}A�h��@��DTG ���(vF<}� ��r/k*��A�r���j���:�H���ڊ� J���Չ[���f�"-�z���#��)�A9}� ;ƪ<�p?3�H1y�D�}|�T@uG�t��0_a��kĈ�G!�D'Rly�R�K�o��쫡d^uԺl�W�:۫��8��d�Ks�7�j���`����#�)���P8?���l ��_?���I��`J�I)�?���X�7�V�ZlZ}�N����]�>��}R�n�0��+���d81z�u��H�� ���M�"�I��ܠ�w)���$�E�����P�O�2P ��b�����A���-��,���LV�x������6ψ7 ��:PF9F�Ex���X ֯��ʡ��D6A��c1�c@�z|;&���~�4�����(!M&?�� �4��f�-���r��?V<}c�y�c�FcN�c�mPMk1�ﯘC ^���Z;��mJ� ��0���HT�ČvM(���^���"i����ջdtd<2�$�3y�_��zY-���u��z'� ���F�Q�t41��{�f���>x ?��� -��i��MG�+x�%; �bX�Tب#��Si ��X;��r��%����K�w�����G�av1\�ҡ�FG��ܗ�����]�:�3�Q�srԢ�.[E4�ā �P7E^Tw$ ��`����a#˪�]��G�q𹉻��jb��3��*г��a3"C��ӷT,J�,Xb7b&�0ޝ��q* �f����2l��u;�<;':��UN�$�^���o����)��X�y��o��џ�3�ÿ*�k>M]�G��zR{�~�U�O�0~�_q���T-�WJKY�4����=Q����XK��vZ���}��&)h~I}��������U�b3��6��ټ������9:0���k����)S��#��S:� ��*�� x��J |�����h�d�����i��,qʹ�L�7�P��� sb�2��:!R�uf��e¯R��6Y�y��z�<@����H�0å�1��8��﷋��卅ʅ���3 !�E. a�MD'$Z�LH��J�#X�:ed�������"�ɴ�%�t��0�Inm;�1_-��2���m$uAk����\�$�zxB�T�"�0�u�u6�|�T'��7|kiZ'Ⱓ3�y� ��?3�f� ss7��E�vT�^X�qh�/U}NUA%Xl�%yi*�A�lJ�?[Sw��>?� �W�p��#�c3��v�VV��jէ,H~�S*����8�fM�v\���k�l����[���v{p�� _0���� �I�x� xo��3���0�i�oK�i� �M}\�x�E�[��;AH��8�N|%AХl���-�#p��O-��䗃T-��Ȃ�4���-n�]��ڃ�3QB_q�X猈�{'�IӘc�Nz��Xc77JI�_� &V��"�M��},���H��3}�X��{��#j��-���V�<����Cg(Zwh<�+n�_�W��Kz�Ŗ�ؙ�&S�o���ͫ��u����[�C����sOB��N4Dl��FJ��4�u��O�4��9\��]�������mPKo�0 ��W��v�6�uI�lF�أ@v,�2 S$A�C��h'� -l�H�G~�>$��%�1Sɒ���<'�wղX� ��O�:� �N�b���Vᡣ��9��(Ms ��1��, +������-��aK �8 ��zʰ⩰QG���0�`bPcM/1�E�s� ���W|R��3X��.������vtt�>��߷��L, -���u|֢NN�"�c� �P;E^� ��h1?ħq#ˢ�]2�7��(T��A]�1tn���,�s�1C��ӏ4X�r�RvG�����-�S�o4�Ff(�n��Q����-�,�����̆7뿜p�ڷ�^�/�6����sY��&F��5��9�K~����wy���U5ɼUP�JA }߯�[/H���^K���͊�����83$��"���� - ���\NƧ�IPQ鑩+ʮ�w]'���ި� ��K�j� �O� -����-i`�̘Ĵf�h�e�9\x �*0F���xP��<�E���Ʋ�l#��S&� -�l�y��e;�22hCP��[~7=��+)���ȟ�.��vh���jSww3�>4�x7��@�~bR�K�e�{�`T��`�B%�φ��G�2/V��̺`Az�*<�J�wȌ�mE6&m9��?��#��J�O�����F���)~�RKk�@��WL!�$1�֎��84�$��C������zw��1%���z��))d/���{I�����cfT��8�W|�H���d4L`��&(�A��W���P�^I;N̝?�)�l��E�l���� &J�uw��qzK\+b�,|� -L�+�D:o0��9dΊ�u�.PKx�p�PT�H�c��FghI��…�b�����N���v;_�-�6ƥb�+�\SÅ9�5���䪐���Qb��+)?�*ܺ�:�q�Tt,���@���5�Ȁ�M��Jh��z��䌆C��T���O�>���{>�l��k��f�Wk�,Q�,�a����E�q�G����9��.�m�{ر��oR;|t�Q:�k�L2 W�� _NKE����U�+>��� u�E��ǥ HߵL}Q}��p��|��S_�j�T,��cA��^�%!�����^��)��dgf���e�d�o�C.���f8��� -F�����6�{�N��Ț5֩4(>�1�:T���t���^�^��nյ�88!����EN^�2Z�S[ ���+O�� qF�7�:���U�ޒ�Z��[�)���:�]��P� ��7����������Q�n�0 ��+x�Z���]��݊ k��4E!�t�V�4�j��Q� �e% X�#�ޣg�|���(�Q`�?��c(N����(�#����6���\7�~�c�S���-�e�0���,����)y�����Ƴ �b�ke�F$��!q.��y�����rV�������p��Dc�S��� Zm�h�X;;�$��q�����_���H�:c�*�� -P���֚[A�tp�*�z�|�Y�����oZEWn�md�e1�s�_�")3+�a����בk%\}䠭��K>�bN<�*��?Q�����ϳK��|��t���)9�p(�r�GΧ]�k�c!�X%�{�%�w����k #�������}"E����R��=����IXn�{e��=v�Rei��e�|�+�$����į�_�mO�8�{��bj� -l�N@�17&���N��I\�[j�l�P�����ys�P��򥍟�����%�I�bA��ԝZfD��{����DӄJ4�)A�a�����.�6�5�!ϖ�>$ -�F���D(��1���޷c2� ��RQ��g���eu�4",�(���<�3������������s�5c� 6{����䟜H�R�tI��"�� ��Ȥ�~"H.�V����R���.���2���ݝ��G*p�9 F� nY8�l���Qr�2�Qz~�F峡+aklK������^I/OC۩�z��7A�cu� �|�m��~�4�sw�����]��4�=R*Vra1� ��R�C�U��a�3,�D?t��N���xA��ZD0�cc�xTU8U-��D�5^z�u{�bF����vU��Z�B�e�j�n����{�����hv����m�k�Rn#����dB��'ד�E ���j�6tk���K -��}��x�A`%G�qT� ]��x�B��)��`�ڃ%�������O"���д= �J��� NS뗃��&��9 s��(A�&�L%���@�o�q_���4��z����v�"���rN{�j��6�VUr�m+��1��a���&'uC�em�V�'�[�����/G����^c ��<��C^?�ƛE7<:z'<��z�F. �r�3~â/�z���)���+��Ko��mֽM�Q�H�%IbK��v)��Vŀ5�2tU�b@��)�kI���bP�~Ƒ�k�� Liɸ ���B/c)��(�1���H�(Mx����J�Z4ډ��Z��ޮkĶ�E���۠���? �Cə6����̗.�A*Qeֶ�����l]8�N!v�����f�%N�f���,E��w���,��������'w�jJӕ�L�p��r���!;�[��x|��^��ˎ~g[���$/�?�TMo1�ﯘUD�H��BiQ��*=V�����b�]� AU�{�f�%TŗY���{3��r�%��:o%��~k����ä�I���tP���aփ.�A�5^�q@L��Z�$<�� ߬B�^2�D���'Y���q܅f�y��c�F�qLHjSbS9p�HXVym]Mx�-x�PTeI� {@���r�Uڮ��Zu��1�%nb܏�t�s1 �ba^0� �nDž9l�tBE;]Y�D�7%��V� #��`v�ǎ ��rA����?Rs�����R�b�ܑ���y\�OJ#a*�/]�Z�]Sq�7Z�N'Z*a��s�f����g�&0՘^��ʨ=k��ئ!Z�l�S�aׅ�Z�9�dZ�5mX-A�i�v{�0=�@ӳ�;�$���G����v��C�`� N��q_��HP��)�*�F��c�x��*��Yd����7�������_>�/a~W 7���%���-�ğc��ҿ&�#���[S���)����_Ji�2F[Nj���%MΗ�E_YՔv9�?d^��.�%��V�n�6}�WL�,$vܾ:���4i�mSc��������H�^�ȿw��X�����E�ۙ3á.~���3a0����=�C�v�St>����Sia-3�ͅq�װL��H�W:?�I�q�B�� ���Y��|����\��W�:)�b�.l�qI�P��"�@�[N[��\��.���ut��d��V��f+��j �N��N����vwu}�pͮ|b.��B"m �K������01R��Ny:Rb�6��L���_<#�Qa��92��H���~�15z/Vk��¯��7���ޫL����[�EE�u��V^�(C��:X���9���N3���< ߬7==�˝0p�\~�IrȨ� #w�!����o�$����g<<�١�}r�����0���}��τr��>�����G+�K���#��Je�f�)�ӓ'�� [��X�0F����q;�*è��n������!��x Ɋm�{�e(�3?R�S*1�J5���2��q[Z�[�FЉH��Ř3o��iRلe5<�$�YkSRZ/�|���F�+�yP��/ ؠ �}9����oM_lQ�8 J��pV�iԾV�ċf�8�pH?M�#��t��e��̡��ʎS��?YPFa�h��i�0`��M�5�c4ڰ��g3>�Q4Y�*9 ; F��D�`a�c��!�Y�@,��ǡ)8� 2n�.�P��/�T�p�̫}aP6T9N���$1�A���J��>��I٥�po��}��q�X�FL4hd#����LF#X�Ŷ�}���#�W�0�E1�n�������1l6�1rN�����W���Y/���Wխ?1��f�W�|9�:d�ϴ�y������r F>���s!�4�t���b��J�$�3���31��%�U�N�@ }�W��R� �#��]n�v��� -B��mF$3�̤�Zu�}=�5i�yi�9�9����y�b3��6��լSԣ� �8�Ljk���~S� �9L"��>[ĥL׊/"^��w%~�L��2�}_̪�~��.<��iÙ�����3].H�4F &B� a��H�K���D�,��b��(4is�f�]�p�lK���������õ �'f"f`�4�\\Š��N(i-3 �U�G�u��<�����yE����3���K�̰YLvk.�7D�����+�0���쫥��tIZ�$�Q��p�YH���8@���.�L���&�V����/I -t"K�hM�_I�F�[������NwC�l�~�)C�@� �+���� M݆��֧��|:�z�K��:�@�j�x{�qʡ6�EËtx؅��v -�ns�@No̓T*��> U -u+���kۘQ��㗲��G�CM�i�H-�,��+�M]�r_�Z��Js�f��`�KOo����U��2�9x��f��>�n$)�AsS���=��V�s�%<�]i6��Hn&B�?HԙSyQQ���,UB:��+�D嘺 ey~�ۼj)mX��p8D *�ɶu�F��圛!>�z�-!�z~C�B�)����WI��64ػ���c*[�m�t{+�s/\�7���>~{��-QS��),%���>�M����خ`ϥݠM8�/҅�w���`P�;H�?�F�Nt�K�>�1hR�����f�_�L$�v8�B򻰋����|��P���j�WO9A1���N񴷷1r�$� z�����i�m���5P�N1��+���xT1�!�#����҆�6�,���ߝ]�^&3�u�{�|�&���a�Q�dK��6R��/�|�`_�f��#�11� -v�^h&p�X��&{4 �r O�<;�'J�a�ү���iz�ž -�l��Ք`���JQ��:"��P/Ɗ�C�W�א� AU;'��ޱ�-�g�꫐��6�)�:���R���خ7��M���̠m��" �e#��ΡN%��"ϕ�3�2�L�p�/�P�z�Tu�;�/��.���? -�ź�bܗ�5�WB|���[=��]O� ���}P�j1}߯�B �ƍ��؛:5 5-%�y �v�+"KB٘��h�M���13g�e&?\�F���<�W���s���8 2�]�4J#��'� ܴj�g���j��5�{���J� ��4֯)k��t�{��n��|v�wq���`� -��P��5l�<���F/���>�(3b�� nߴ���}w�q��-C�߸����t�!j�Y�F�L7³=J,���B8�6�&?f��p=��.V��Obd - �%����<�=�{�C(���`��')�׋nc�*�����ym,�?���"J�!��9T��y���VH����,�ۧ�/�����H�g�~ɒ���;F�A���?EP�N�0 ��+|�MhW6`0 1���8"!�u�hY9� -�︅�\"������]� **=2���+�S�D��j<7��� �[��v�@��,k�Zw��ҝbS�noF��9���@�����r�en���Umo�8 ��_��bIz�um�n]�w؊5�})P�6 U$C���������8�h�$Ň��W��4��j��'{,�\�]���&�2.�`ڂ��>�{\��yܪ��6�&|��`��5p��|7�e��9<`̌�L�,QÕi^�"T�@�L��(I���*mꄟ��#d�do�;o�����L��\�9�u���9|���ۻ�w�*_�͙�3�rS����d���*u��(mJ�$ۡ)��ϙ^�G��e��ᒉ�L(����x9|m -ͥ�ț�7����=A���8?��=���I�P,%�(��R��Y���}��8t�+��Ӂ��4�V��o�-J;�R��T�ZN�s9�ѥ!~o�X�qf+�:�(cjjG��M��Ll��3�i�˼��2ҧ���k�t>=�jF�����xo�����k7Q�� \�:9vo�C����Ĺ�Լ���v��Bϯn׉[]�ֶǝ_���=8�۫��V��9�u���b?������M)l�4 -��=�����^]գ��A� -w�u�Qx�^5[��-ڞO�'�́�ƨ����As�?H�(28���+)�}���&/<9 O�(�4"�H5�e�l��=W��=jC���^��|��|�g�K8�~䕺QDBZ#�3lq�=<�pǎ1zQ�1*��i� i_�W`HRMͧ�z�Zhw������*�Q(�5�Ɠd�&�%�Q�4![�f��b9$��!} �ǿn�����ж�k�6����Q�i4=�E�W0ݨj*�����i�tX�|8��P��#K��f��-o�l���Qԇ�����w�dC�?���#��A;��e��I'�,���F�jܮt�KM�{�ן,c�0�o�i�]5 ���0��S�N :)��"N{���}�R�W�0u�|��l5���@N+�8e\��Y��݀�T[Ƅ��K��T�o�0���℘����O�vl��Ц ��2� -��B�%Nd;T���9?HT�_���߽ww��ke�3��Ғ�z�������,�S��t�FO���@S��1u�;�Jp�:����ܢ^�{Sף�*Q�R4�?���y��onn���er}~�ho����@F�5sl���9������^b��j�̢����.9������RT��!��T�+�M��;Y/:���(ݝ����c�&��v[«&��譄�a�ݏ�A��f�]���4������g��uPKo�0 ��W�Ѓ� v]��X�a�޺��-&�J%;0���Q~���"���=���k�*�Ly����;Oa��Xf�Y3��u��6�z���Z�t%��9߱>�򪀏l >�O�1� -�-�����n��5Z�L 1���؊#r�PZ��b�l��0 -�8�Xcd>�'��� ^���3F�����j:�{_���o�׉�k�p�J�A��u�e"��k�"RS�Ef�Gij���_d�eMH�l����#2c'}�qp�M�3�ȏ,��l��{�H"i�Y� .��۫k�X;�)%�*��#�x��J4`���r3p��zD.{��yk ���}��_��9�1Sl�-p�P��Žʋ���s��P//����{�F��l����5� �Vmo�F ��_�.,���c^�d��zh��: ua�%*:L>iw';���>��dK�������H���H޼�� B &�WZ�@/�.Cu���ڻ<�������'�ΘԐF�� ��m$�l'�K��p/¯ �� -n}߭��Q��!�qŔ�L��Q* w��,A#L�����r�J� ��J�1B�' �K�F:� -EXE��5�<C u�h�[{���a�8�U�13 [� 䪰�!l���CN�4����t��l�*cD~���� �k�˕�J�����ߟf�ϓ�|��x�dN�������|�Ů��\�S��B7h��)"�o��,İ���,!�T�I.tTP�����*M�f�R� �ɹ�5�v[�?�|K�Fn���Tdy�)�M��K�6,�����'ZeݛEE���:�*Y�����������s��0 �i�uC��l�F�Q]��p"�EMԾ��i�n�,� -~���|E�}L�ry�Fj�`v�!��LE_� 1�=z���8膷�ZqL�r�HC/�����4����?Y��8f��P�f�:��)�;Za`�>R$�0 ���ӔM �52�*���xc=pO�E��>��n�2Af5��kde��E�(���}Z^]��NV{0�a�ZY��Vq/�6��$�������C�o{�K�c���,�!�e���w���A��4ȲC8�2�Qk�7��~g O5��b�Y��� �t]��r\ �Sf��#6�Õ�����v6?V��o*?�� �weZ� /��-�����$q��P���K�G�밑i��.C8]�*�'oP��{z&�1,Z�S�=��"u�l[�Qq�+�ܬ5��v&&��J���#�Y�~>����P��7��ޟ�z#9QH����݆����=���de[@:��WR�3�ԫu�߆��s�A�T �)xX�����Z�r -k� -�0�$X���bGh]�q��PZ�ƶM��;�KB��5�{�8]��g����A[3�\k5�������>R�łƒ���J8蠸�K{�P�,$���{�Np�Y z�?R"7Y�Yz��k����� ��'X��R�wQ��bӑnE@����Do@����VH�k ��Vt͖���L#�͆cd� �����t�1 -�⹊�̖=�+�Cۣ�3GڌC�7���<�ݿ`z�|�8g���+D��S�O�u���l���cy�-Y��0/�pQ����D^í�! �q ���砺 �% �Ln�|�?9)N���5� }R�n�0��+��"R '�vܤA�ZnoZZ�Dh���E��KYr�ʃH�cfv����XY1'���>�ۏ�4���� -~hC�� {P��7��f�גN>��h��*�>:�/V�W�L0Sr�[���%�p���r�[�0�!p'��������N��[��z�'�5B�Z+��=U[S�#�����w%������{^<<~[=&�n0֊a�jCG.�aoXKF�&�� -��F�ɜ�"%�Vq�u�L�����U�(D㸙�=l�q��h��$:Vb�v�/`�0D�ә7o��+k�2|ct5�8�;Y!z�* q�L��� A�J�{�Q�w�]�'� [��Era2� o9����>ƅ�������/��ϸ | `/���H:�+Z��n���M�`�rѠ -��Ws�4��W���� �� -'�-�)i0�b���i���B3��p����T��j�a%y�9���E�0L�x��.�����O�M�����(?k��C� -��Ў�&�����9��6���[0�j�ۇ/aׂ���5W�\��Q�`0���̻:�����I���z�;ξ����IrZ˩s�?E�A������!xFW? ce�}���?^�� -���Q)�-�[+n���?�F��k�ք�`�L���� -NK�e�Ȱ�Z��*��� -�w�_u�A'�H5JG �z[��VQO�0~ϯ�I�&U �+�]��1��! -� r�Kc-ؑ��C��}�4q�&E�i�پ;�w������,� �0e -}mͣy�P��C��A��!�)�Sd 7 _� ���Tfϊ/~�G%>�L�De4�3�O���$�qf8g�p&� ��\W� E�2K��RP`��H�K��R�I��|��Il5��)�� Di o�@��`�+*�����ҿ���*�.�6� � +Tp��� e��,�*�RPb��H���wR��VUQ��G��OQh�U���0åH��1��r|vv���o?,n�+W�ə�g�!㺎�l��d��1g�^~q���*m��� �T\�Յ�m�(�b� nܣuԑF����[�5�s)(-F��)Y�ڻ����;�̠z̙�C�~�X�(��\���O߳�:Τ�?$ =̈�Z�=���X����}��OO�f�TM�?7���ba���-�'e#&o=3�^;&�������=���b7�R�`��g?̯����rSc���f7e��(5��jI�߳��)uͭR9�z�k��a����j�Mi�Е��Â���vk��ґ�L�v�ؙ<��yC�ބ�� �:z�= _X?��AN���9��l��6c���_����0��+ZTe)�`X{��{H��UH'Of���k�4���v�hϥ��TeF������Y�ʶ?�u��gWk4����%��פ����1���d �� ��;%7M{"ߞ��"�� -�D�L8;�)<�\��'�?cRC���������U�$���p��@��0�R+�`��rU���l K\1�9p�9J�P��%E�i��Q&"SA��r�JU^x�J�1�:O�W�턇(�*֩�2�S1r��l�qo�~���/�s��&�c�a�D\wa{�c�P�*�e�tQT�|6l�*ct|3y�~����2q��� *�\���=-����Ai�گdB�+eſ�{��,"H��>g��p�c�|#��%����9)PF�n�1�0T�yn���1|R�v�rG(u�m�on�ǍA�б��ox�uv�p�cע�(�WT���G*?9�CX�q:Ƶ��U�Fۇ�ǡ�l!�J٪�ʚ���g2�\ ��Byf^$����3r�)S�.�E�]%��Kb���j�TT�t��"�5#G�o!f�f�ڭر�G7Lž�Yoͣ�o�̳,�ԋ�X4�P_Fm-\�M���L -Ev�sT������Q�8k��b��6+94V)_�c3U�p��㬌fgj}�-���5+ow%Un=�n�m�� -J��� H��X�� Z~G O�$�'�&�����Z��nE��u-tF��a�Yҡ��;U��L�1�M�L�x��4�S�<���)֕C��G��d��2CT�K {�I���Bv]j�O�I��BtZ' -�ϱ�7w��#���dl�̈���Ħ,P&�c�(iS�]j���;�g<�x�)���E�/�pǹ�sl��.!d:�!0Q�X�}���/�e��o�D)饱�QP�H/��H�~|����M?��!~�W�����rG:��tL�RB���O]�ϧg�h$��c`R2څOf޸c��MYT��T���AZ���������w,�GF�X��(��8D�T)�J ��&�:/��jc��oX^��=��2ܣ�gt���iI �)jK�V�iyos��dF�^��]���֣�WX�rw�N�w5���ΓD�a�9F_űz��pz5/�in��n���z��IcS:���rF�M��]XM�W�G�����Ƣܑ?�y=���)�\u�T�������ѫ��_vhԻ�y��Vmo7 ��_�^� �d�P �S/�����@�KC��}Be� ��E���{�5��/�)�����_�8�C� 2�����擫� ��߱����n� |��/I�5�:9���0�ߌB�]r���p���f]�_F���#��u�+��)���� E�:����B�(�u괱��;m���TJ��޽�!*K���6;�V3 s���ٽ���ퟏ��T�����[���}a�b�h�S"9�J�W�whNǟbn��S���d�Z� -�g P�5���0��{F1� y�.�ΑE�Bc�Yń[���#���E�w��h�>�!���U���o��� ��5喇��rks]E�%�_&@���s��sK)��J]�:�k/"4{��:;&{� ;�L%��/���c��\���%u�V+�Ŗ��5��"6��z:\,r����—a�"��|&`���Y�i��zU�t�����u��ZD ��o�` n��ht��'D����O4�&��»޲f �|�Fb��?�z9Z�/[t�(� 6�rx(��H̲4e��L�`#��J��I�R���au0����n��PB��X��U���]l�ͦB=��S�Qu��� �Κ�`�]{?�3†�?�Z�%�A�H3���vTB꧊�d$� -F�l�H ĠK�jw��+V�Z׽0��~(5�X�pxO����*Q�;E��܀�y��]���G�PWj�D��F -�ĭUG�4���;�8��~y�6h%��^~��_F D��!���p���@A�6����������Q=�Aa��ͦI.�G�e�y?�G��CE�k��|���[o��ꘈ��W����ѥo���z�46�sTʦ4> -l����� l�Sex״�x��ӣz`��z^� -�%-Ll@g���CVu�\�C�h�Z�~� ����1Z)ptt8��+��?ֽ�[��vkop����f�}S�F�SS���Ѧ��v;#�r�\�< �J�(=�ş��c��S[X��{�B'5�2�@���#NN��e�/mS[o�0~ϯ8��H��\Ɔ�Viڪ1�/H�$'��`G�q���wp �$>��b{�**�0-��ؒ�)m�P��~J�ѨA~�B.K�V��� -Y�Ӿb����/A�&��(���PА����|�� g}X�VX�B�7th`bC`ΌPW%�B*�T+&�u��=>hT �,9�}u)ST���\�� �Ux��X-q��}\,��~T#� -A�2i�X��^R�m�3)2P$�"%vh+��B�G�nG��o���:X�]��[��|Ӳ^�r��Q刻�$��_�ӵ(Whj���x�z=����R��h�CE���UFւ:x΍�ڎ �:t�n�n���*���f���'.�� ���ɧQ ���x�V5L�H^�gC�3;�}���x�0C'?�%�Pk������$K*E���Rezo[Z�r�a<�S|�n��̄]�=R/qC�����Y�IG ��Ո��8y�6g0�I��/�c\�e�l�qw!����D>���}�w� g�\;� �}�I>������ArFݔ՚v��p����TM��0��ẂC�P�]�BWK�RZ��+�P 9�@�:vj;�����q>X� *z�\��{�Ϟ��Y)r� ������ -�7o��`��_2aa-$� f�54��{�'rxT3�s�ߣ�3U����V� �[ҕIɼ/�1 ,c@9B+��0��t6�4i����mQKO�0 ��W���E<�u���@Bm���xkD������[w�������Nn�K�c��%9#������qvy��)�U�a�<��kLq��k�B��>ֻ�֕@n -��@p�1|R� j}[����,�D�����b�� &5V6�1�T��{���;C��kXŴAq1��ҡ�ZG�~�����e��Q���B�-2X�{-��uR�DCsl�!�C��,���Fm�V���{�q��-�aA�(4w�eA}��z) ��;�o�`f�����>uSjML�C�Ȩ�|A_ �z��˔��Z�'��(F�FgT=��� ��@sD����u>Ek������tM2W�1�򢯞�A?��-d��o�5PMO1��W���*�(�Hb a=��nw�6��Ng7���,���3o�}ty�l� -�ׄ��� p�0�^�j>Q0�7�2��#ȝ41�vֵ8��X�ԑ;X����G��'gXj�W�P�*��B����t�gl�`���JaL�AС�+����"[���^�A����`��5ԑ��] S:-������]o^�MOu�V3�t���Vprl��96dP��!�\}Ĝ��wV�6��d�� �T��s�� ��I3:�Q '5�������ш�h�_ f��/�HR+��*_�h���W��V]o�6}���)\X�8yMb7Y��)�6p:�a Z����$GRv������d}�� b��<����7*Va�0�����va�����79��1|���O�bڂ\�C̷xB���F�g�ױ� µ�$L����d�~�,�O"�����X���5\�b��T :C`"�P -"�L��&��l��J��� tg���!�b%��Y.��:Fk[�;�������[w�w���Ž��ɰ0��1��F�:D� -�'=�6h�凘�{��+r�����C|R ����������J���a �J���:�����dK2�E�^�1 ��N�Y�ao�O��Ֆi�#�ʝH$�h���m��e���m|�� �������uX;�s�U7 G�>﷚�h��Ԫ�}H����J��R�łr�ICT�+H�� - �GU�l� 鞾˃�~�m�J~uZ��a���θ�ڽ~��]gQj�@�����ѧ�)�8�s�7���~��3�S/Vep�F�jQp���#ee�,}�i{7]�8�Xk�9�x��#�XË^�aV��c��`��V,1��Q�5e�w%2dI^h��=2� q�(;�ۧ��U�����G0��v�[�B}� ]3tOޝ�A���:d- #�c"#��E -«���`��yX�����J������ZL�Ϟ��y�D;���>u_��S_i�'���μe�b�/�JMu���U����N�b�t5o�'����3: �f���k��я(�F�^(�����#�&YJ[�5גf_�� �p�o۵bj��ob�.���x���ϥ{����������0i������u�%�璋@3��p8��F��̲���[� }^P7�FWյB�)ۡ)�t���{[eX��(��8r���涓��>�s�J���v����&��}��}Ⱥ�i3`���.�Z�t� �>�gp�����F�i�ے��k8"/Lw#�[�ߔtߔ�3mOw�fPt��O4�*���/INK�,LըVص$iV��F��\�f��C��=�>*$�ú�{�μ8m'vyW�O�Y����^b[��1����A��o0�h%�z��TQo�0~ϯ8�VK�v��u-c�!4M+<����<;��v���%i�~I�;���ww>}S��)a1&ge��ݺB��L�Q:�`�KIPH���JX���R.�����Tk+��8K��j��J�h�����|�?�q:��9)4|�-�������zG:��h&6����^ �D(j�ؾE��Jf��������C�p�ϖW�ާ�˛٥s�p��� 氒�d 'M��2P�M9��x@�ߖ�^�oA�q��y��#>V�o���TY�]��le�#p5��tJ�ϕt�dԒe�G�:'8��������������t0����p��3+�����ܽ�),�C�3�k=g!�r���*0f��x/�^�������M�09���O�#�MVq'U���69�Y(����e�b! �_t/���� m�� �e��Vo�;��a��=���JC;��{§���@�`���f�`z'��pS�8��Qz��d2�B(��G�u�9��L&T�H.�8� ��oKw%����c�U�U�ߙ�;���]+t�Q�q�9�3-���k���nt�U����}�25���C�ϵW[k�_�/Vŝ,�y��J�*:I�E`s���TT2]�N\J���Sj1��8����R$[-C ��…�cƟQjg������ ��<��)1�1E g��8'F(��!0�/8��ZH��t��H㘾эu��q� !WLG��1�[G��~���뻇ke�!Ӱa -�He�0�M�C�BI+�J)PP�|��l�*a�}2y+�ۊ�:T4Kq�J�kTD -X��������  1*�Զ�4���Ŧv(��^�L�h䁂�9�d�����5�;�_�ED�|�$p���������$2Z3����SD��Wq��*M ������q��;-j�s�$�S����7m�ٌZOy��vڹ������n^�����qnX,�̣aXG2�M����=�uĩ� �����BO�Q(�F p��qȔ ǭff�D�J^p�r{G�4�9��ҵ����{�u� �[١�1jR%��ꨎn������A��)�?���YU�^�g��:$�/>&�]�wER� &PN’z$��o�o���$ I�a�S�'=�dm��;{�f�/J�+��ckg�_�6"�^�{^�z���}Bd�%��0����F��լ��؈�p��be~e��i�P5k7d�|Mz�[3V��D�܂�6]1���M�W}�1�z�#�D��� -����v�<�ê�7d��y��Y��7�ꊛ=���U/��V�XH5� -�B�( ڬ�~J�T���rG�z�{��fY�LsTz�����=�l��o��,aK�7�ӓ�o�KE���?m�MK�@���s����Vm-���ԣ �db����$����ٴ������;�{_{(�0*`Jt��|�H�׃i2&0��ZT� ��U`p�j��Xʱc��)跚!-���(��� fJ�y����F��\kea� �Q���F���ʖP8+�� �@�G�k��1F�z�6�@K���\8(�Ύ@pJr��c7��Y���UDuƸV GEPj:ka G͵T�4�&(Beoy�Xu@�JһZ��{�.2M�h݊�3-(�� E�n�Ȁx�hŭ�}��q�Ԗ1T���#~A�J@�or1)�m� �+�f�*�7� �#-�<ɪ��y����*B��2�:]N�����Mo�@���s�`#�kC() M$TE��*�e=��.���������Aـ��������|*�R���3����h�?$wQ�A���B&$� f� ^s��[J���.vF�r1O��(�ϒ�_h����Ѳ��Mq؃9.�u�)x� l�B�/�R�Zزt��}É6�r�����Mw_-Ge�Ueڬ�Z����6��ӗ����������9�2 ��u/La+\N2mui8R���܏[�-�_sf^�j"wQD��f��DK2>;C��̈́q2��?���va�a�0��xaĆ9�NV�I� -�K�F���`���m�]�(4�$���x 7�}�t}�-��BW�z����@<�&a�4� n�YJ�ױ�p�Gd]i(Z�P�C]�"آwZs��fH|��*Y��ɡ>�����r�( ��`��YY�zv�K����U���L��pΣa����z����ߵ@��Z; n�v!}��(�� �ry6�@�KP��dA��m��Zm�w���l+�K�Wo���/h�V�v9����*N�3PsͰ�VP�@�.ޢ��SKo�0 ��W�@�:A�`���lA��"�q���t-̑4�vl��\��;���H���!yv�r)&�"�=�N�'�����4��"��\{�t� �N���>�^K9 V֝H?� q2�Od>��Bb3%���_����-�g� |� f�=X�"���eRH�a��-����%�!+�B�-{@:A�E��,�kk�@�)9�4�o����vF��8W �!��� S8hΥ"��-)A!J[˓Ȩ=z���>WtkԉL�H��6��D/cBu����y�'Y����"0��ooC -PG�R�0�4y�����N��؏E�\ \�A0�Nj�鹿�I��IB��Ҵ������~������ ������� V�2r��z����p��Ѐ/<�9~Q{>������ 5�n:��l+�l뗵Ag���}zOL��=���ݤ�>&肊�r��F�n v(�1)Y��V��7UB.�tnb�h��r����}QKo�0 ��W�Ѓ4 v]�Z�X��(� �L��\�#)�A��>*��`ʋ�����ߺ�� -mksrV�r萗_�y6�d0���c�]��ggH ��Ը���&nBw �kr[�w�׭񿐄aa��.��U��K�`iX���#,x��C�b�+����2J ��4ul[��i�u=�W_z5₿�3����w�7����Du &�����I +�;i���9D��B�y�y����C��帑y��.��Gd�I�g���H�^�]G�V�=��d�����<����E}•��R#jroS6�nu?j:Z�G��I1H��HI����3����?{���'���6Bji� -<|_?�zn�h����j��I^��X�� -����G�b�P*B���j�?�?�RM��@ ��W�@� �E���B�"U+=V���!���t�E���zBB��s�/~�Ϟ|,��s�0$v:��|.���q0�Ї��&Hu� ��r 6�M�+|��G,mqv��1�q g>���@�%�|�Ə ��ý"�����`Bmb.��9z (�@l�ۗl5�����2ϥ޲{t�c4$ZMj�Q��f�NI��x����^��w+ߪ�3�pR�� &pҜIE�&[��(iGF� -%�M���~�AI^��=3P��T�j/�U� ^�� ���D:�e�d�J��GQ|D#��� -@ި߇y����}�H�P�Q)F�|a�S�ʽ����,��~��k�E��:i4���. g�5L��5��Bv����A�e�M:�P��l������ b�E�8���'�X�p�c��z�e��z�؝���:���7L��J�ƿZ 9�[�as(��Җy�rk \���i��w�0�Q7��׉܋N5�0����!��h|���?�\:�]�p֮�f�v����R�n�@}�WL%�؈��Z.%���B4o��bxUg��CQ�Ϭ������/�ݙ3�~���3�0$v:�%�s���h����{� �:C�=W����!�[�I�wLm�wz�2�q�� |ɔ��� �JΓU}�%8��W�X+wX��!�Q�6��7�2 �ֈ�U��QExkp��.�L�5���t��D�Y[��X[���n�qW��Oof�?�4Ʃb�)�DӁ �iN�"��.F!Jj����'�\��C�ܽ�Q&2ɒ�j�1?�l�d��X�ߌ&!�^ ��y�� -$!��cןd��m�l��;�|�i4�9})wz���KBZ��]L�f����%�^�U�+V�$ib�,��ְ�ޠ�*�~�|>���se��9x����gA@�5��'X���1��E�����~8�Eb<��2�rȅ3�bv���HxA78B*ѥ��e~�%���:�������2�F��~Z4�LT��X���-L�#�p��u �;�Iok��$�-�/7�0�\��WM ����a�+#G�z��y��_!8 :����U[O�0~�8�*���뀎�* ���N{�&䦧�E�D�Ӯ����ٹ4����9>����$�)G���ed�*gs�.<�w蘾%��L�L�� m)��(� >E�e �|�e�X -��>j��)ꉵ5t!������)�{4�0V -E��`M�޸#��]" 5�(S 6)l�Mx�i� ӬHS�kt��ʈ�W5��\X������B�ҟ�� �����fai) M�)�xJKiD �d��@�Z�YG�9�\`{�}���w:���#�4�gǞ����2V���;k9���G��*��nT���pp�:>�p�1.��M��ܓs������?�t���@E��k����1�n%��j�z;����Pu(/&��P�W������"�A �z횭"a��}]g�I���e�n'cs�e���u#�<M6���l9-��iӬ*�젤�;��w���F�ܣz����Hh-Vल��F�5�Cs��[�l�Զ�[�j��V]�\֪��<�m�L��!V��?9��MU��/͝�na��a1P��;��Q�g� -M՘mKm�Y��kí ���(���ݧBUm��^�z�>��^�P�9kB�(��/��?���*�0�A���_Jj��^�����7�Mtݾ��mf������ߟg����\ܓ~us^�fD�Ϥ��)��=~���%����і=ͿU�\�g�� ���WX?-��_�T]O�0}ϯ�HhM+cOSKY+�b��:�eT���4��]�)���:i!���_��~�s��}�H �1�ah,��N��@�?n���N��J����dA'p��9�3�u�$9O-�Q�H!|Ȅ�E�Nf���O`�3a� ->�C���0#�E�>��!Ҋ�͜�dV������2���}v&#T���DS.������R⢪�<:~}�j0� - a ����Ҧᡍv!�둏%r4����T�H�T����KE�XNoq9�;nmzUx�J�����.Ge�w>����]*w}y3$�4fNj���1��b�I&2�&���ed΃��6D�:�t`P -���Q�nA�a�l~V����X٧��S>��E6��ǜ�fX�}����jTn� ��g�Я[Չ���h/X$�l�4j�ZGj��V�Tߚ���J酺¥�i��� ���炖� ����ڰ���?;!ً��(Bc�����>#������0���.a���v��9ߏ:Xa�iM�X����]V���2)��4d����>N{�T -���!�i�Fv��~ٔ��ۮmغ�k!�h@i[�YB*�ꗓď�wt�a����5PMK�@��W��C[B�W[�ZZ,�+��l&��tw��4�;I�^�}o�}��1Tr252M��5�-]�x;]&�Y3��l���zd_����+�Ol|�ؖ���L��s��D,V��u6��9=�p� �Xt�B 1���5�PS?�r0�i�����p��"(��V~t�kk�E�� -�g�] -*��],�ÿ��f�v��RC1�P����W/ʡ�R)���oؐ�c�E��L1�‡ -yᅥ�,��:!.z�hK��0}��j͟�&Ӕޙ>\z���J�Q݈%�0q�I�������)ItF���&fh�N��e��&���N�0 ��} -�i�ĕ L��1� ���FdI丫&Ļ�*�"���۟=� -u��U���d4��1`�8β�(�<�&Bi,��A�/�6���K�d��a��pM��*����J�E�Ǔ/ǰ�\E6��-6H0�}b!D��d� -�� Xް��3p� �F(kE�'�5]VWz�+6ލA�)� �]��f�zحR�n1�C�"&~��Zõ(�t� i�AE��4sj�1(I?֊6����,ˌc�2 /H�4�cx��X�� �&L�w:iP!����I�`x��U��7��[�[��c����?���)î�c���S'<+"O��V�}f_�Sۊ�0}�WLaY�����ҴK�.���>�,ObQY2#��P��;r�-ݶz�4�s�K��e^B�R ��yR����D�|�̣�$� |͕����-y�{x�U���w�<�:�b��;2�0ߑ�����:mϷ����T8���X!�µ�5+B[j � L��Vޒ��[�#�+�9߲�j�$�Z��R!��f - '8V+<6}�6���� �s��(dʝ�0���9gxhg+��DY;�,2�@W -?��ƑyU� �3���<�6�p�H�CH�s2�ԁ�� -�Z�a����x�&Xׂ�� -2B�$U �p�� �i������ U)��n���v�9������ �� �E\XW���չ�c=�n ��EB�?�^�����H��:B_��ެ�������W=a��#�2"K����dE?ZmU6 S{�[�C�n��s�G~v�e�$� ��| `�2ۂ�&��H�t���4~�Y�����/�z��9uc��/ݭ�/�!Ǻz���9� �RMo�0 ��W�P v���5m�ni�؆�a��,3�0Y2$*�1���R�4�>}�E�����m�tP���a��)Ik�;��o����<�s��(���#�xl���� ��N� A. x� �{-�wt��J����� -�cXa)<)a�:��C����4�B�i +Y� �j6Ak��c�V�g�fc]+HY3~Npl�p�p���ϫ��TF� � ���^X�NQ��mp�Q5H�Ȍh�w�Ï�pK�-M�2˂?����U� ��!\���l� �՟T�S�'BSyXBg����� ��B�"Y��)�^�v��/�٨�d:�"��aC��i�1�f2��"�[�P��������Q�Lj����A�R�%�Z'�yq,楊�s�[%m�W�y1��Z}��!g���__a��Sݾ� ������Ѣ�ƴќ��u��{^���!��a/1����^�p�xb 8R�� g��4�B�cx��1ɭU� ��̻@l)���d�PT��9��_L�86�������ʏ����UPKO�0��W̡�,�!�eK��J} -ܐ��=�V�v43����wȪ�%�7���,?aO.!S+���O�$����QG��@�}d���C�[:�qeܔa��%(�nל �$̿�U`�v�������cXS��3��H K��+KDeHT��ك+قu��wû ��S���^�):�bYs_x�K>�Cö�vӻ�������*5Ӏ -;�Q޼��.j����2�#3�s�&�d@��PO �4nU��� L *��%r�K�>2HCP��k~b�� -I��:r��bX��C��r�������fh�����4r����F3:t�=R";�\[Jj��A��oّUQ� �����v����)�|*�6XgPTf��'�QO*��5�����F�.T�@�Lh��j jN����?�(�������3��"i6 U�^�"�@J��hb�!Z��L�z�urP'sV�q��e����s��1W]��3O�4d��o�$����q�'�������a�gL䩏�%��� �Ϩ�*]zR��e8+����A;�w� �nj8�ڰ��n������w��Q�n�0��+搃d(1z���5�@[H�����Ŗ&�!e�(��j���P��o��2��s������d؄�C?{�M��(�|���Ri�:Al �J��Ǒ���DjWHe�� |��|C -���m�)p��n�J�� ������ L����%��Y�P!���<��#[+��sVSZڋ��Ɂ�c��濏�����(� �p -�[/,�B�.�mM٨�+�#��`x] Z��f#�$�]z�Ԏ�;!9� �' ��F�8�g��;R���\��Z=����o|�I+��-���a���q�Z���� �3�V$�Н���z�qa�K�3J�+_gC�8|ڶ|�Aj2?��ҍ�̾�0P}��A$Ng;�������=�7�{=H����笿��T�4��h����ƚ�W��d򋵞���{}M~�TMO�@��WL%���zŐB!Q�J�D{�(ڬ�xUgך]'D%����ۄB{�^l�̼���'���e.C�HI7v����(��z�-SR�#����p����{\�bA�!s��I#|̅���,��>��߇ ��'�:%4|� Nl}qƊ�9zG:i4 ��ΐ�G��ei��l�ٽw�$j�Zujh*�2zN��L�|���b��v�V��L8� ��k.L`�\�Nښ�$2QR�����-_�d��̏UE� (�ץ�gA$c�pHq�$M��s���k�@+��H3N~���zp6�8���J,��P�� ������u\QN� �x�e��J��-�:6bV�Ύ��`PY�E��˗i��%�H9�ֆ�1̌Jڨ��z' ww_)��[�BfvY�6j�E;���-��2��z0����F�݅�Q�!��0z�Ғyw��n��K��T��(�D.��m��E�ZV� ��9����A�x6�V��G�k�:>���ͤ� <0��$�`��W���]㴾[�v��ףےYa�'��v�u��bjf�>VL�nَ�v�ѫI�>�s���n;�*�j OEn�M�k�������>ǮV��L�ɹ<�_u���j�ti��{ -G�e��w ��ܳ�E�B��/�Z�v�_q�i�����m͑��+� ~�T�n�0��+�`@R��:���qPMa�-ڃ��F��xA�ϐ����.g}��Hw�ꪆ3�4&�j�ٵ=�h޾Io��M7�� -.謙�� -XT|�Cr���������$Kὖ�Ϩ��;F��Mw�8��7�X�$|D��Lg�'D�j�>��!S��m�Uڴ �[!N�w�}��JCXe���Y�d�#ێ�>�}�Og��3_*���grn�^�Þۊ�D��u�6�`��a���`��:-�`��_��4��u�} ~����V��vVguf��i� ����Z����������J���V��n'T��uK=�%ˎ}8[���)�_��� ��P�{pg�m��ž�iƴ6�s6i� &Y�3�R�K��`����7�k�N�D�mS�n�0 ��+x�;HR�ڴi��� -l]�t��,ӱPE$:YP��G)q�`�%1����H]^��A�R �y �$���a��ZL��aCxnT�Zi�u����� _nj�u[�� A. �� �w-�z -p)����'�F��RR��l��e�7���A� -�5,�l���'���A�[���c��ZI4������ e�����Z�&�~>��w�Tj�A�*v\X�FQ�7�t����DU��yf� -�~j��/ɑi��!�2227�1sz�+C�.��߳��6���*% ��G� ���/���7���� -p}π��p7k�a��˃q-E���ׂ�T�2O -�%�wT����3}+)?V�AFЉ�3�K!����M���Ytq��Bt��{^\@i���{����xR�`��Pơ�.�b?��W�+��,;��G5���*b�!VW�^[U�m� -b�l�~!��q��Ⱦ���^�@��F�� �Z�ږ����7D���I�:_F��uQ��ǣj����x��7J��q&�<�����a����x&�"��D0����u��)<���{�yW�8f}j~��*�H����S����T�n�0 }�W�E��Ac�M�u�-� E�{�*6m �%���C�}�;iz5(")���Hǟ���%C�H&��-+��O�8����g!-dR!�Z r`2�.�G����Z�� a��_�����c���Y��8�-΄uRh�� �mk8aDh*�>�N!1���jgȮ ^W d�R�o��h%Ԗ���P)�4z�N�m.qќ�vuz���ܧjs�p�RiW�0��t{�ikjJ� �m�q�E��l�.]�? #� �mg��ܷK�l��#�d[ ��Df��̩A -�d�%j�mc��_����\��< �H΅C8P��d9�h8a��mT���cNuҔ��c��_'.<5:�9$�1$X���tU���6h8���.�$a��[��p��{�o|�P��l'S�j'����O<� :zE��̌Q[X ]M�-T��u�px{k���D�ީ����R2�p� 7�)>p6���ۂ� eqCǺl3���k��?B��X�,r�x��`��b��(W�@:�a��B�29B�ڐw����VƆ;��\�'�3`8�f ����v�_8W٣8�I�+߂�õߋ`o2���:��e�:ѿ>��j���v��5�#�͊��ڄd�b�ψΟ��ݬ�d&I Ł�D��w(#S�}w;�� �[,�e�{[�A΍L���McU���?0�.>/?�i� -=��c��TMo�@��+��#�"�z���m�*�*'�s���Z�`V�]��ص���.uo���|�y3���eVB�q��(��ٗ��Éw>�` �א���%Sd -����7��+�� q��@��3���p��{�n����`�k� g�B��f�e��5&�� b��H���_��!�U���6���y�BW�JU0å���1¶w�����v����\a&cvLC�u� �q��*Z�J�H����sO�u�~Ș��g���M�'�Wik-�����b‴����`\��Rl?��)�.&�F��*�O�Ԛ�"��q.��/H�u�~{�9�a�e -�+ca*�”c� �H:x20NY��Ua��Љ� K����{��3�!V C�}96�M�jM���]�����M�0o�FGB��nDHUCsF���W�S M�rV�������;ʕ��\n��F48�m0���9�LY��K�`xQ�=�{����4�YOk{������m!�����:-�����S�ܺ���dJ�@�R}��@u�+F�5���c�`$��ڮ�l��Î�#��A�wM:��)n���-F�J�Y��O'��D�����:M�^r��KB��<�@���M���_��?}9ƴ6���ܴ���(�Y���2{��k&x��MNd'��KTOa8�.#U���r�J��Q;����}�vl���5P�j1��+��AE�^���b�P�`�B�ݝ5�1 ��"����m�e��&�ޛ�c� �=2 ����S����n47���1|X��u�@kB�-�;�T�nbӕ�� - ���˙�/���S� �D�S&#�$`��*m򀟔# S!h���Q�*�J���J�@��&���?����]˹ʈ٘Y�27�X��ۘv��Q��E�FE�1����6f���YF�W�7+��8mi,��lu��UI���\2Le��p���5!�Ӛ��ĉ�Q�1<�>�u�r��.�(����gC��C_J%���$��ǔ�h �|�=I:�4�xZ���]+ �.m��as�ַ��M��3i��վ�ԁ��{����:�O�&�֥73�|�,���@ngd`P ��ˎ�= �.�E�[�F���D$4�a�p�� ���4��C� ��}ouV�s�}wj�<3h -So��\W�揨�� _�C�NQ�N~V�r�� �ɚ��v ţN>/��L�ٶ�ٕ�g����z�@p{}�W���&��x� �����k�~��^�����������{Dn���<$--v��o�/�U�O�7j�cj:_�f:��~3e�^��U����r@ZZ+�`�n��\����d�t����˴i'�Q��+,��~��T.��s�&�F������ f(��.�FaD=b�0� �_��A�;���U��qXB�e�Ź�aLup 9�Քh��� ���c��_����<���'�T+(��� �fk+i��/͛� ���1��|su-� [���M�+Ϣޖ��oi벢2}���� -��VS�tS�u����oJ��6��Uݨ�RR˃|I�H�K�_�S�O�0~�_qBL$])��'JYYach0����M���kG>����;�IH�TM�K�����ݝO>ei �JX �Y��{ΐ�~p� �w�$�I���LXf7�\b��>bd�g+穃0���j��J�G���D�}8��O�p�SAN - �0G 'T��M���� �F3�i��c���\)�W�}��1jb�zf�B8itN�m)qU�]]�.�o/�r0�q�����������+ �#����N�KaAX+�=�"�ʥp�)�-�[�5+b��C^2�YO&��.� T�Q����������^Ix����"I~�U�L�cX���,��O�1���!cQqj���e�c�����u!��km��B�����K�� N+��^��Q�B�� �ؠR1G72�0�����V0� `���Kc�۾��k�:x�~��J*�쩖0,b���E�k綻P�o�W[��:>�ɹ>Zt��U��l�%���l ��s�I�Kc�qĭ�<�� Ǜć��6 -���{���Yu���k��@�f������:����n�0 ��} -v�@���!�!� �]���uۈ�D�K������Mj.���'�GW8H1)a�3鄿������'�x������An���f�.�G�s�N�󂡗�� �s���=L�ij��G)> a��򬕁W��`�a&�кC!(�Bb��+��_,YU��o���R'h�x5���bm������>so���}��΋q�j�!��2 S�5�����(A�6+�#��y](Z���Ev�RO�HF�B�bW1�D �U���& ��&͸�o>?���%X��*���N'��KY�%þ��O��Z��ĝ�����uf�ds��wW�s���}jk�v8\������MOA ���+z��h�~�$F�O$fv�����J���]�`ԋ��I?�}�vx��%�1vD�����r~�d���c�V> ؛+� -��k7#J;��J�St�#�Mp�Y�������xу�NԻ�X#�P�+#BJ�Ap��������pB Z!���po��/0����Ʃ���sV{��m��MG��Ÿ�j��)l�@��� K�z��cK �\����O��6(�Yy^9�Ҳ��r� ��"0�5�:q����-�T�Fh�h�`�:�(�s��8�6�3�E����^� F�9B��� -|�������l1~��Of�oƒV�=��-c��?����=��RM��0��W�!;$zm�iZ�� K���dy�*��F��w�ح�,����7�>�>��A�R �y �$���0ܿ+��b����*@�4����]�:�c8M�֝�:��,�7��0?�S���zS �]����i�$��^Mc�Q��fL'��)<]�����m���V�D�Z���pR�2¡��^" �C�Ef���޵�?ڗ�E^J��Yë��t�[sDC �C���\�'+'��Y��)l:�{�AH�I0�ΫN¤����%+���02���o�A���o�a�`=��$e��{�G� ���q@�Q�l0�?� *k5Lش����!|w5��>�ȇj ���!:g��)���^��O�#Eo^嘯�a�X�5��~�mC�ߔ}�1�/�%�d�5�AK1���slKi�j�V��� �Mf�`6 �lk������ɼo��K.�E�u�I�L������\MWj9S0�7G:�r'�b�����v41�3}8����}^�O�\`��޴c��x;�=��0�O8`�u��aLk#�`�� `��1���ǘ�B7x/��^�= EXCs��b���Ӣ O�wϻf����Q���i��.`��e���G�.q�%Ȏ+/U�=��E~u:����#���J) -���f��b}+���V�;� -��F�&�k8F�+��~]Q�j�0 ��+tء �a׵ۺ���(t��p�6slc+��ؿOI�K}�{�{�^>�Bee�I�h}�9`z��.D� ��S���\�����6'�3�3 -��5�DM�%:�W+�7FJ��ܯʱ�W�4�=�2��ޱ��4V�}��A� -�wl�l��t|�H#ԭ����=��.�WW��H2�̀�I�� vý��X����!iI���I-��3���ɷQ! Uc�\8�` -��;-���9�,�0�0�=X��a[[�$��_!�O�eCe���tC\�( ��+-jhK�ǩ��18"]� �p�.��a\���UQO�0~ϯ�I�H� �WZXYŴJ ��R!7�6�;��4��wv��&�i~ic����w_�ɷ"- �8c -Cm�ͣy.P_|���Q#��r k�!�o������o�m�LϊoRa<�+%�gL�Ae4L=OW��i����iÙ��X����7��e�� &�� b��H�=�����2��F���Qh�*�R��p)"�r���w.��|v}smK��L� 옆�� - �q�� 5�e�b$��n�,,G]0ھK������,�� (��&b�L)������x�$��0�=](.̚��P����nk�@�l4�閩��IJ�r��ɥ�e��Ė�����X�;��Ų��Ԧ�Q�n��%Ŧ��W�0ز�D� �+B�#4UaYӊ|����'�!�5�����>~I|����| ބK����Q�Jʬ�ДJ���_�E�)��N[�*����<'��]?�����&�#9��*��)娷H�5��<��F��v�]�$����^�]�|�ɍ�J��-���>�<���y���Fޗqz��/�nK��Tޜ�ѱ�$���mԫ�|�a���\��Q R 4��oH�I�����v���-ڢ.|"��A'4Y>�?���tO:� -�v� z�y3�����-^�V8:{���B�!�{|���]�}�n�5݇� j��͵nma�[�V��̴��pC�qaKZ��`��&�����b붾k��3j(6[�q�UѺ���]P1N�@ ��CA�>=��K|Y+��b{��q�n,{��̼T��(d��*���\Me"3� ��J����� ��h���X�nT}ܡ?�-��Aj,u�����`Yz�QőS�������71G�� �����Q(l�v�`qo6�ž��<�6����%z��,� ���4�)���Է^�Ä��Ӕ�$�Q�x�5�T:���M���]P�j�0��+�����s��BJ� ��@��u$*KB�΃��ڭ�/bg�;���M6A����� ��%!-gù�����:��yy�� ���uG��*V1]�;X���}^��L��2��<)�f ;,4����� �;I�1yl��C & V43�>� l��{�{�V��@�5T1ך] c�uZ���S���f�~٭�U�al5�I��~����c+�M��Ũ�O���k���Z�7q�5�_y7W����l0�Y�������x�?d�P\��j ^c����RSȥm7���J&v��ډ�0��/� �W[O�F~ϯ8�RŁKB�ET]��ji�����>�-�؝'����3ߝ˪��Z�$�9����ی�~N��c&ГJD�zP/)���p�;9���F�Q�@�) -�9| -�%Ӵ��L�-B�?���#|�D�$�1z��������gL��q�3p&� b�I�6��N�f�J�t��P!�<�c��w��q�#�ĕ���T����e�+�������ݕ�2�T���� �v/ `��fH�L2�#m�Oz�=�L  -���|5�zG�^/�����,�jR�����7G���7�����L��?^ ��}L�mG�>�_J�z@���\,�&{є�(��d -���b9iX��]�H�»}L�к�!)[Ɖw�i6����(�$;�g��C������a�:�%5� )3o�y�N[���r�G�Z�u7Q���%�(>+��g8.t��W�N5�m2yC�ް�ɐ!�Z�5[��f�Q���H�����) �M����V��B*���� -�\��e���O�ᤅ��)M7l@�M�l��Nk�^�+Zp:!np65����& -wa"TK��H_ݏ�S �%Ȉjˬ�l]c�^,�'� X���sݎ���e�u�E<�v��g&�=6;�[ _�A Wd�������UՌ�2�+9�_�T �*�[t�*�k'UH[/c�h_T�7w�� -E�� ���z�������P(��u���>7zJ��/���vm�1̒$��(ݲ�j�on���l���um"�L����"�y��yٹ5����ɭ�vǥ7��khs ?���`5j�k"�����Q`���kQ� +u ‹�?YL%�I�^y��.����'��3KM�:*O�������n�e�-h�Z��Vշ�%���elw^��t��蚃���)����Ge�O�W��%��uvdL?��t+�Y����嗴�3 �e��7����v����n�M�:^_�{���0����7��f$݄Z%���W���D�pwu�ko�Mn���+5 |N��_��A�� �}:F�>g�G�(�Ay�/����خ��cs�t�z��w�t��/[D��Iw��P��9� -�/�<*dVRiK*�L�!� -����>�V�J��!��������mPMO1�ﯘ�A�� -�$F��'��β����)1�wgA���43��}tx�K�"�GS�+�R�o �^;�6<Ն�2A�"��`^� vn�Ѭj�������ro�`�d���� ,�Qn1a�!�#I�>Xl�����; V$��� o|��d��'��mM��$��|\+6�u@��6��ww���~1m�ŸV [E� �P��p-��&�b�b�O�{�Sk��d=�U����G�kY��l�+14�2���#�'$�w�N�/>2�R!5�����p��D�Jkt 9�E�s�6��p����?ҋ� �$&J�.�g�]P]K1|ϯ�Ƕ��[�zT,��cA�ɞ 撐���W= >�������6����L.���VN���j8Sӑ��Z�P;O o�\ ְ�nO�;E�)�w[`��p�����A�0�Q�Ů�'�nư�rq��Z�0�XH"��S' t lז����!f(��n��w���i -,YCs���0Y������V��y��V�+ ��8��"W�0r4�6k#ӟ޼7O���I�`�YLA��'=x�?���jQ�~)�Ն�O�!�k�J��r�t��?ݩ31��` ���R� r�m��Tҧ%\S�1i���2��)�eE伡�h%gYX�� q$��$E�fc�>��n���Zm�B�I;����i����ŏ�2jX,)L��R�I�뤸�KG�� &��ʫ�bO�#���Kw38rS��*m��kO� �V��� �c�Va Жm3��g�8g�R�u��ӗ��N��7��q�����.dG���f׼�#Xp��C� -Xa< ->‹B�O���vɐ=wP��}���+&���>|?˿�𱋮�̧,�ct�3L�[���p�F�#"?�<�A�Y�B�ӄ ��h�E�s�; -�8� k�H��ga�����.�kAw1��GW}~�P�-}��>EAH%/�uȖ�i��9FA��^'�W��>_.}2Hn�EnzQx��d�cǔ�����3�C��q:�����l4�������P�-�x΅�,f�Y�g٭7��_V&4$\ -���3/�6 N���քާ�h�r ���#p̨WMTaz��C�����1��`xL<��X��c/�p�'uX��#LLxJ˜-�SF"\�*���<���߅$�W8wGʹ���8-ū"l��b�eCJ���0��-�{��]�������`�u�g���I)��&���.I��g0�@u�M�8�L�����f3H4㓞7��������=4���t��ty~�>�'W��0����N����j0:���-&����h���덇���0�o��O&� �h�_v'�a��c �pp6�NAn�;��s�?���?_ ��5+��z���t0�����+���'P7W+�d�Cq�UE�N�;�a���C�j��4c5�D�Ⱥgk$w�ǫ4Z�H��U� �=ôNB义nFx�nUK��E{������cX67�[ (#*���{�*��i�-JHM���%nn��4���m���+�Q�9҂����� -n�Ҫ�Z����������Ҥrg�x�4 ǭ;�<6��d����4�1�}��@�枆,Ɣ��a�+Y -��+܂������ ��2E��Q -K�GΚ/�-�gQ���N��t�� 4К�٠���w��]����\0�>UO�丿@�3卆�����[��E��^��E�_%�󔉊z�$��f����� �n�o�)e��򲚼�*/������z�B��(���OQ5.t�1�E�w.[=�h�7�.�蠁��!�*x��ʰ�>P%fI�e�ˀb�b��]�Ǿ�_'��X���5��%a��t� Ժj�^�'��9����x�矊��ť���u���eƁ�錧"Zޯ,`}q ��wn �ݘL� s�S��0M�+$&���3W�W:�E���� 9�Q �%h���DŽ�5DS�I�7��S|�c@�ㄭ�/��N�s��z�,�ȷh_sjX�?���4_k���X�n��Ʈ���� ڡ�N�Xn g;_%�SE�'f據-��q�5�yQRv��y;M(:w�(������A�L�--�����l���0� a^��i�ֈ�2>i��c}�(��o��6Q��Ma�~jIUdڡw�Л�xbT��V�(�oy���g����e�j�FC -�������r��#0ngCݒ���� �L����W��E��<[�D:�f��r�fP� {�F떷�B��z�,^S�[^�d�ө>\Ш1�v*Ȏ���R-���� -�� -�wu��m!M#�A.](��J Kìmd��B��oHޒ�}�SjAߪ-��s��ooCM�=��6�瞂 �o=��i���q����R�{�ԣ�sF s�@U4�-���v�l��Q������oIF�>��� ����>��O�(�0?��$��5��W���7�.npBsԲWd�j����� b��r -)�\�|n�k�j����fg�i4n�Y�X������3�&�[SVN-���7�y�.ؘ�ЯR��7<UK�1!:�K�ʪd�X�fs_�����%�u���ҧ]r��ط�4vDjD+I�j��h7�wzZ.1���R����v�i ۿ�bwS��s�),�S����R�?=�,��GC��7y-DjѧVk����r_f9S]��m�(Y�p�s&_��|)��B�t�9û?���,a6W���VQo�6~���^%��ю�dF�+�b��-�-"2���c�ߑ�,Y��f�^l�w�}��x���y�C�q���(���樧���wqփ3�+���iÙ�߰@����&F(� �!0�@,[F*��I&EXYF����x�BW��j� �" -�hn����}x?���tkC��L� <2 �%&��MJ+��������*勞`k�9��)S���ȗY�'�^�-7[t`J����s|��zҵ���8m-q1w��i�+.̲=kT���ق�������+ -T,����d��I�5 -ڙj��^�8;�� S�Gnj���'� 3}Y�OJ�X��5�|NE�El�������W�� h��b���NZM�G����aY�G�Qa -�!�%�4�zgO�+�� tR�U�F2�w�Z�Rh -%Zh'#�LX��(І\H�ƣ -��Q)�)�ј�#��dO��Na�.���(��� -��V��%�d��ZH)�:P?�Q�1�ipȃ/!,OyXZ���F��f?�ĝ�6�0Щ��ۍ`]h ��:_Q��z��6��S���.D�~��ە���p_�W����K$����b}M�P�u�\{M���x�)c�OWMO��x6���Ҥj���/V6�ܭ:��N#P��{0׎��;e�m>@W�;1�p5��A&�*��YA �"Vn���-��Ͷ�`�PH�;^��s��d�V�4�˻Y.�r�}�e>�C������J��I���a�]��Z���b�G�(�⃔E�^]��k��Z�l�R����`�z5n��V=v�:#�۾/3��TN����4����,N!,=�%׷���umfW�QY�ퟷm{���ቺ~[z�y��No���Μ�6:Dn�7��k= v�,,���k'�A���7 b��D�촷�Æ��f����w�!���b�^p���T"�ZGGC�I�LJ���<�ڠ�ѳ�����R���0��+�@���E���ET]�Z��{k+4$l5�#{E��qH �U�c}�=ofޛgO?���� �=;������I4F0�W�=�:#��@�`SxVzKw���-�NoC?��3�24?ɱ�)�}�n�w �F�Bk����*���7��("[dM�5"l]�u�&�l�"H�,�aٙ��x�jR�rdm��Jl�iW�}}\,�^��U5+dء�D�#%�Ӭ���-]LB�4#�#�9�%���=��#�(*}�e�� �p�p*a��{q*/��L%��5�ހ�E^NF�jEG k<�|���|��(���=<�^��j���d�w�+꼺e���W+q_,)c�s��j�a��S�� -��B�!z�gC� :�ެ��f�����t���3a����yҖ��ްt*Eg�v��_�h�/K�rĥ3��p=H�aҩ?D�����������WL�ы�/��7N�Qǥ���G�c��t)2�Z��ޠg�a6�v���"wQz5�fM� ��5�>��vS��ӓq�φĥ�S�e�����TMo1�ﯘJQ�E|��h����)��2�Y֍׶l/E���l ��ϼ��ٳ��&7�"��b�ܯ�ޠ�~N��q?�>�΅�LH�5�z�,r�����6{+6���'��*���3Z�`�h�n��gx�5s^0?�D ��Ij#1$S)p�Hغ�ں��N[�9BVJI�-{Ȗ��r�Ue�� �@p�b[�����~~���6@U���y�1�p5��>�j���r$��my)V�3�‹��{������Bw���w��o(�2۹��?��k��M8~МQ���q��[f�N�!l��2�p��0��K�U[a�*P�� -<���ͥX�*�)ϫtn -L�&�vŃ��Z� r��>�Rw�m��A���O��ú�g���?I�q�����IV�t�j� -۷��۠�'_����N��`v_�u� gT���/rO�0)�g'ME׌?�;VO�> ��.,�h����,<ָg��{)d/i�%,�A�j"�z�.N���Hv w ?�h8�Y���ĽM�0�Xdž^� �հ���u)S��yX#}Q2�h�h�¸����`QO����ؗ�Π{���7��;��K���R�I I�:ޢ��n�6��_�Y-v���M�,M�]4Y7 Z�ca�����`Ϳ��"��HYn -�az��˹���]�,М�)�$b�&1��Oa�oF�������e��"I ��S���\&k��ʼnӼx�����(���K���� ���~<��_���]�f<��HJB�[V-E$/R""��Q�g@ج�9eᇜ"�$hQ�)�W���4�Iƀ�l���I���ð�Nȃ��������L����%��34O��E��!�K��Y^Ҙ�y��� �+� - ˗KL��R"���d��,�Abw�N����`�y�?w�4��B����׳��H���Р^�cR�;�US�عzb����O(�AhO��`����P@�߮��<>0�{�)� �L���E1����U -'�=�g0@������5�v B�bX_cN�n,����s#��a�P+g`&����by��yT�d1� ��H?�„&G58�x��:��ŭ ���������K�?��l�ƾW"x>:��8NS"��nb���g�wI���.�_Gh�'s[��� (��{�/`'i�'Y�H�`��8' ?� F�t%�B� ���t.Y� ;���%1N#j��B*JxI�ic�ټ=#���l��V?���W;}4������~�u������U�6�1��+4�u���EC�R)��4 -��%�����j�`�vg�{�%"X���U���� � |ԏ�8�F%Ԉ�A���V�1�Oz1r�q[.D��0�0���z��������m���k鱖���W�ٽ��4��Բ�C(��}P)}*��z��OU��i@!��k�� -�A���Z�����*$jCo��Q�A�d���ɑ�� %�X:��<��K̗�ߍm���!�aS����gpN��[�������qM��!�����e�νq���y�*��e{Mwœ�^�l�w} �����H������ �pȏt] /?�|F?1��-�*�$C��FW�����!�V\3 -ԙ�%|L�`�JhmN\G(*�Y��Rبۇ�����D�ﴂ:�u����KM5:@'G�:m�-y�L�W�-���m7r�?�R�c ƛۚ]��ԍ�e��x��%8^����B���}�$��D`uu��q���� N]�Mf7����$�MY�mGp�}��gSf�4�$�����"�[��o����C��i�)�oKm��`@ڊx�y2�+�� ��h��kLA0uE���+��*~y���g^��յ�JW�ꖦ}��v��SV�6�����&��A�b�U�(E�� -��ژ���ѥ�S��-�Zz��>-���H��*�e�7�N$m�BI8YE?{'  b�!ș8M�p��-���Բ�t�:Z�^ �(H6�eq�F s�Q �jc����i�-#��8cm��g�Ukـ\�Q aӮ����9��@v�0�I�T�j �i�j�]�4�m:�v� ��`8]M�� J��8�g K ������wV S�p���~\�SAs�M�fc5�������@�\��� -l���a;�����w� ������ h�.�U3�@V�����{�6_��Y��4EU�אn�$���ѯ��|eU ��)o�?U�|Sn�YZ��f�ٸ��"b�8��J0���KP�%e�}� A0�dT>����V4�;dU�~�o�hQ�}���j�%[��W��6S�j,�8���=�M���drD2�x���5K���]5�o�VSV�c���x�GD�S�ނ� =~�7�;?l��(a��G�����“��$e�F���P�S��P�շ��kN�|���]ti��%�1�P{z H�^7�ʭ�ƹɹO ���Y�7F+�H�[}���8���P�,�y�/�VMo�8��WL�4�'�^���$�bt�������FQ�Ԓ�ݠ�ߡ>,J�-�T] ��7o�|�W��I�)Sh�xh��s�z���қ�yp� ���~s� ��� ڶ�2V|�� \+�p�2���p���j����p9�ϸf�p&�w,P��nV�e��5&"� b��H�k��� �E��~��Z�}��Pe`&avLC�u� #�q���e�B$GQ��,C�3Z~H������\z^�-/ZϠsŅ�/{�F�RW�����]� -Cg)Z�ua����V��o�gy�_mԫ�<�ovv�-S@�l D̮��-3'_�u����_ -��$&NEH_�5P�&A[�OO{Ӡ��8�Ԥ�wbu;_�;�p���o=a��XyXf�,I���Hf����R�����z���J�l��L̫#�����;��_��l�"5�b�����*�ZA�)4����e( a� ,�}':G�I��U ��bn�;*�͘�}GC�浤 ��.��AP�tX{a?I`]�3 ����v�u�}�~j����6���K�ra��̂�.N%���5��|;+������v�r�e)��%�����(#S�C�Dm�"� �AX,���C�MS�MA�(�k9\Xӊ�0��J� -# �RM ��r�<L�����2�%4J��_��&N�q������Ÿwv���0�I?=vN -���W��oJ�q�F�1�^z <���J�����;2i������J�:T��<�A����ܑ�<��z(���8=혿������V!t˭ExQ=;Z�)�srd[?�$b�~�aN/��T�a�C��S���sf@��'�h6�`�Kn����5�T��opJ��T�N�ui!����R0���8e4?O�|��b w�2c9��j85!pN�P�=��!S����*mZ�k�����փ�G ��4�UJW�r%�@t�b��&��fy����S5�ْY�297{-�a�mI+T�QNgHBy(yIV���W%�7���<��y ]���X��_�Bj�1p�l��K% ����*cT"�D@c6���i���ךo�EdM�؎�Wrõ�J�.k���������,Մۄڥ�^��|_��lP�\f�tWk*h-��j-}����w|��0�Y�|��݇����Jo�P���~\��ߗ8UJ�`-T�D��Nv<�����h���V' �����)�����J���bz��}����/f��?(�)˞I��5��J?q���5�+�����_�xX����f� zF�!�>������>�wMa_�C�jE�i{���XmO�8��_1Z!5E}ٻ/+Q`a�� q\��� !7q��N_t���I�4���Z{�<�L��N��q - 9Q4�F��<�eJ��o�~�w܀c�LÄq -�7%ʀ��0f3��mk1��R�il [p��o��g���S��/���nD���@�DF�Ќ*8���*�2���R��qf���T`b -��s�/٭5g!���T 1L�6 ���sw��vpu�pe��c&&�DC�t�E#�3�:�e�B�DQ�r�!HBuJpyu+]D��F��.Zf������*r�R��1��QL�/}'�� -CՌ��ʈ%�6I2CƼ`)�\�_Kmh���)b��� �G��ƾ��q��㟩Ղz�'Z��6�� ��w| 3��J̘�"���ٽT���#���׎�ޑ��5�� �q﬐��P��1#�}z�t�8g� ��}�ʫR�+�RdE�z��c�LOg��`��G6�:��� P�t� �� �}����,�R:�B�Oo�Zs����syHs�W;��F&t�kA�V_*Z�к_����sЌ����GL��W���P�ñ�י�W ���z�a��ɔp�ո��B�9ܥ�q��(���G6�Г��>ܝB� �������f7�ښ9;�x2�'�O:�u��PY�*Q,�r��i�E=h���Hx)t;�.DŽ_f&~'-A��(�J���C F��=^C�'�N���n���^�����L-��y���8��t7�1逄��ڸ7o����b��$����?R=31�8O&I���y߻u�?��sד�3ͦp�<{���:���o%�E۷\.�̫�o��C*�z�~���d�z\Y�O�HJ���� 1�"ֈ��ͱ��5��s ���7&�^�LDW����0�����džd��4�w'��ɁL�6J�sBb��Jϼ��m������ɔb���^���mϷ��y�l���3|-��|���X:�F�RI��bv��?��rJ����+5�����$�9G�A9˕���/�W�K��?]RMK1�ﯘ����ū��QZ-���� ���&�&Kf�Z���duE�K2o>�{C&W�m�"�1р%9#��o�/φ�xT���c��'л�$kXY�N�������X���M -��%a����e�V4=�5���0�=��`�=p��(6�r!`��Ġ��Vb�_�EL ��n��|Ϟ��3X��:�-���t*��h��=,g���<�ꌉE�2T������5ͱM����-���[�^YL���m�(Z��:{�g�m��-Z��f�;K�]����b���C(T �gz��LVV��dc�!y��39X���LG�8<�^��|I��A�������4����u)+;Џ�������UQo�0~�WܪN ��� --+ �V�h������I.��ؑ�@����s��l�����������$N �@0����vj7 ��O�n�ݨA&17q�@τi *�����Ea��d��<��u�k�0L.P[��/g��V��&<��˙�+LQù�m\#T�@&C�$b��*m�_�#D��uwh�����H�%�\�&P9F{+��,��z8?�\�L����53r��!���)B��Ju��(�In�$[�Im��L_���D��Zj/����f��7S|�Ҧ[�Dsi�����*2!F/&C�c`$W\+�Di����JF||I�ܞ����Т�+foi,܌�~������.�)C�屄��5�����������`��\mS��!����`������g���F.WLe�1y��!��%� = l:���4�~�R1���w���w�+DI{�����3�J0d')�"r��Z2.��)% -�x����~o�Au:'�E�ni���1a����� -����zD�;Ӝ���j����s�$�|ٖ&{l��z{��ǯ<~HsT���h�7�r�:�\�[7v�C̸���^������� -X�e�,�b�G�5���G-8�j���#&�����/g�CİWS�n���5<��(�e�M ��[�*wJ���q��jV�)+�� �A�w0(|��}[�o�K�!F,�m����O��=ʅTkY1�+ٺ�}���U]O�0}ﯸHEMP�M�S���@b��6 ��mc-�#�i�X����4M�1-P�~�{��}~��)D��10V n_�2E����N�ZpO�00 �O���&��9���y T��b[xߴD�N����8g����'^ta�cf�`n1C �f�qE�P� :G`2�$gViS�Ql�0ɒ�����;�!�r��Y�d(�������� FC��7fcfa� D��0���1Y�i�2͑ -E�O[��Ф��c���/��Y���KrWL����3�[8�P���X���B�n�]-d�XD���Z@���\͙�[k�A"PZWޙ(��Y�v\��A�c�f�75�0��I���I�{1ۥ��Rĥ٘�4��B�"3n� -� -�nT�"�v��ڎ��M��-6}�|�_6�V���<�:��V k�oq _9�}�v����� �u�L'aϏly��qtV{t���MK��&�if�tڗ�/� ��вia����XA� j:��顿ͫ��su��� �pfy ~�%MЮ��Wv��$y��6�����ƶ�}�g��)r��ۧ�G@������`@2�_Q���m�D�@c���]/�,�<�;UF6l� %�~t�l��IЎ�Q�% �ؽ�*!Œ2Ω��Z�1�N� V����C���#�;]��� fFtc�)������ӻc�\���Z8��}���k��6��s�4��)ԭ�U��7�6���t������Ih���܆�2���\���{��e����۟nUyRÒ�1�H�\Á�b�O�;�����J*D(�����j��R�H6o�[C$�=��7�?��tT���0�8�λh���h�� �T�O�0����&!�V�վ��!��FA���*7�` ǎl�?4��wN�hAZ�X�;����ß/ �D2��uF$n������� �7\XȄD��`Ɓ����v)�+"],�x�¤ 'F!�J���8 �������n�G0�)�N0,���]�I�B�/�RH�"a��ick�sm�q�����kv_-E�ʒV�i�3'�:�c� �W�~�x4�=T՘����YH�]qa -s�8e�i�K� ��{�b9ڂQ��33���#� (�ץ� ���<�r� �����T�������iĤ� �f��9�I�xi��g�`B�,�2���Y wB�znc5F��\8T��f�o��:�j%� ��(���4�7��ف�qӡ�j-����������6n����#�f�� ���_�� ���(l��!?��.�6}=���k��hNd~��l{�nT���SY�[R�i���O[�e��|=���F���*�Q�?@v$+>��-�B��i� ���+J�?�?����h{���;�#���{>���N��Pd�G�i[}����0��`����hG��jr�q��H�9�t�u��J=n�� vq��5�K��XmS�8��_�v��ar��}���% ��Bio(�*�&�`[>I��u�﷒_ⷄ$_H�}y��h�˻�c?&�`K%����A��:��9$�>�d� �7�B>%�>��nk�!�W��|El�K>��i@�GJ�w�L��G��1L�T�F���N�8-Hh��Gl�(.d��� �| �$p?����B$k4�"���G�ŵ9����|1]�Gڔ9��" *��d� <�`��<��py������ c���>���Ƞ�I����"������凛�ջ����겼<��x��6]�&���*]9��r`��dm��I�jkU�x0�I��d!��#p$��@���C����7��6����XUm���z�ն�/��g��E]�E̙5�����*N�Q_�-&�R�ږ��Դ���`���>���S�}�soH�`�t!֢�^��3��x%�?>2.2t�I@M�v�IF]E��IIFќ �)��!����9)4��j,�B'H� b����? R�8� ��Gr�+���N�dJ]tB�ρ��o� k���<XRy��|��E`2�~+L�5U�-�0�[jJM4#n�����SF'@"��棝�ζ^I�JZ�=��3��mw{5rۅ�:����_��j���f�6%v�A^���x鏉�����a�2��PI���]�4�ˠ0�T?�g�4�6��~�#z��7{�m�����ch��ܿ~�VZЯ%K�<���6������>� � ��zjF肩u�`��i�����[纐���/�F��՗�U=a �Vg�FW������Is|�<��+ú��G @{�Xu�ײiA��ۣ�C/�r�n}^�#� n��UҶ.�.W����՛���X�o�~Z��-_�X:Ɣ��l��c�'����뽪�gU�#�e����ۈ��������X� -{��(j�!]�XZ�Z�ˀ�h��=�y�� -�~Pv�9�t�5}>{�E��ET�.u�b��ܕ쵼�g@Q�0)��n&6U�r�p�a�9�w*v�uyc�3h����^F~�S�u����A�?��~��7؄"�� Զ��,�pO'F��Fc�d��Uv�e?�rB����o���A`��Ҿ��r��p�=�E��hϾ�ɺ��b��ږ������陼�� /XK5�����V�϶U%;Ք����;��v�X>|B��� noX����V:�<7��,�ǩ�pU��ki�T��l+/6E����\�H�h�����i�� �t�`�Єs�C�u�x���mJ�� LjV�/h����`8����K?� �!�ct�-�n���d�R�q�����}�.X�$1 �h����~���ۍ��iC[�f{.�iԶ�X}���M �n��z;����X7_��nz`���GG3�s�����2�����V�u� �`C������Fb�?d�N��}Z��j��O���RKk�@�ﯘC!R0IKo~$i��B�%�BYI#k�jvٙ�k��{GND�O�˲3�|��深��`�m‚%�Z~�>"/>�3s}i��w��uA�h�@h�s[���q�ܦ(�>%B��-��$ s���j�_5x3�5V��Y�o�1���Ɲ:�=@��@H�UYB�W�/!�tm�^�����Fb�JmH�hJg��u�;�=���@u &��Y���6�s��DCsȩFj��׆l����:�V���Ef�d|Q=(��I�1b�� r�3���3Ò�.��!�V��_Z1W��mo�r�=~2���� -��6�= �����j(�B��xqQ��O��>�%, �x^=^�'C%���5a�'Z�i�B��J�Շ�z.�G�Ɯ1� �'���`��VMo�6��W�$N�����4��m4�A�m����QD,M -$e�h��w�[��l���� g޼�H���,� F.���:#���M�v��`ԋ�zp�����@�3t��X� �}�Dg#�R!�oF!\J���q��_,���χ0��N0W���3[?� D�3�>���kE����V ?h.EHr))^w��RpT���D�%sB�!P9F�V�ź?�'ӏ��/U �R�`�,�–�0��p)Ehh�sÑ���QO�%ڌ��۔�k��`d�����R�wf �̿�f��Tڎ�a|F^>�d� �T�&���c��X��!�m���߅AN|l���)u��f �&B1bO2k��Z�S�F�%*G���B��?���Ŋ�~��'�2#V�!�� -�:  ���X=��Y����\��պ,_��`��w�%F/gy���L�ppځ �e��F�"�-�$��gӻ��w��Q ?��}�K����|N$�9wa�(�-��ᎁ�ȃF{�-����Y�ZZu�Wߛ���.�&o�J���K�j�iXh-����0 �>�L�aݺ�e�YY��s�6��v���WeD�xB�7���1�H |W�ݟ�ɺ� -,�ȇA��G )ՠ-_������9)h�T��q�B�ʇ����Rڰ����Y���h� Ɵr����M���V � -+��'����-j�{�~zw���]ܵT.������)�̥��:$��'65*V�2z�6�n���R�? �>C��Ҷ��O!ܞ�T��hk��Z�r���СJ�r�n]"�����G8�/o�_���B��_�u�D�5�Ԝ�h!T�9=�v�Ho�f����oV�چ��j� ��x\�6^a�������2/�CP�q��Ƿ,(LP�;f�#З@��~���� ��@����Q����[A祧�nB� �Q�U���˜f{�mR���S�`��"܌��a���Z���7EP]K1|�_��m)->�j�T-��cA�ɞ 撐���w�z�)��dgf���e�d�o�CΙ��f8��� -F�� -eM���\�{~P�߻g{�vf�3����2X2 X�7�!�L|7�U�Q#xw����@ߌE հu�H�;�*�K�N``��,��)`��" s�yq�牥�1��,#�PK�� MD)ꄀтIQ�ZI�� �Squ��{��Pd��T��t*v�t��{ݬ�o�u��ZL -���z��" '/N-ͩ-������UĆ8��[�e��׋̪Jo� �����P����e�� �h���^�~W?�/EP�j1|�W죊(}ֶZ�T(E��BY��&4��l�SJ��{��Oag&;3;L6�!�1ӀKv�|�K"����t�`��1T�ț0��;�D�N���ݧ-0�CX�@��1|Q. s�yq�牡�1��\x��2̹��b�� ��;6%f�7�� �T������;M�%k�b���� �P������u�Z���ݪk�b�@� ��h]��Hi�M�$F���٠�7볦�E: -��p�\-�j -r���~�/]P]K1|ϯ�Ƕ�ϭZ-'D -�� i�g�wI����w�z>������⮄]c G,��ɹ �\��f610���� �[, �6!�J�^���L�=���)!<46} �������)lqoY�M��,x��si��M\Nl�I&�5|��i��{u&֬���Z�9MA�Y�����U����U�b���2��?^��%(��9w�P��Pyf�m��Ux,���r��1zKf��2U'��OxL�a���Fk1���O�e�EPMk1��W�QE�����X*�"�Pf��&4&!�u���މ��Sx��f���&0�=f�N�;_�ۛ�B�' -&p��@�<�� 3Clag݉f"W�:�Kv�a�������I� ,Q�������`a���� �2+iD1y�F�`@� Ś�c.�O1[���^�!�����t m�Gd�d -wr�_��lכ�����.�z,`\��"�c+�,]b�5I�V���G* ��Y���v��B�N�6�_� L����YS����L��|�o�mPMk1��W�QE*�����R��`�B�fg���$df����w�]zi.!��{o��F��4:�89��|�H��p�&##x���v ��Q'�P�κ#� ��/�}X���S�ύ����`��,��]�c�c������&�S,%��`����%X�qH�k� �"�]�߻gu� z��������ӂ���^7�b�/�k1��� *G7/���� -#�)tɠU}��E�Z���i׍̔�]AqF�g�1�<3�����V���9�{�K��ؕ�$w'�m�u������ӥ�Y��L}�EP]k1|�_��*��Y�ڊR��`�/B���5��$d�����w���)��dgf��&��x�4`��ȇ\���pVLG���:��y}f�X�κ#M��˘.�}Z���S��ea��΋��'=�aO%�8 �B-e�s,4��� -L �l%f�7\� b ��{�{�N흡��5�17(.�1�:T���t���Y���U��ZL, -���r��E��Xe�4�6R���<-6� �Y̛x�^dVzKfX��m���KO����e: ���p�\�� -z��]��EP�N1|�W�#"�TQ�Ɛ�#�Y�=��k�n����=��S�3ӝ���%�����i�%;]>�)�\�j2R0��*� �M� � -���J�N������!��@��1|Q. 3�y���+C�c���8 �B e�q�%��: �kJ��o�3KP5� ߻wj�4�������b��C�����b��Yv��Ŋ�-2�^d�u� -#�96Y����D�� -���Wq{��T)�%3<�&�_����#�ˣ���: -�a{�\-k -r� ��~�/EP]K1|�_��m)->�j�T,��cA�ɞ 撐���w�z�)��dgf���e�d�o�CΙ��f8��� -F������c� U3�X���C����n�(�&�nC�<�� �@�ag݉f*w�uL��>����S��eaX���q�3CS��Yx��2,y Vڈb����*-vl%f��� b ��{Շ���]E��k�cnP\ S�q���ѹ���]�o���/&��`�e�����.ͱ�i�V��J�,�m<�Y�ޒ�����7���T�:t -��p�\� -z��]��EP]k1|�_��*��Y�ڊE��> -eM���\�{~P�߻g{�vf�3����2X2 X�7�!�L|7�U�Q#xw����@ߌE հu�H�;�2�K�N``��T"�s��EE�����Kc��Y�5�� gj:R0��*� �M� � -�֝h"�vc�5��-0�CX�@��1|Q. s�~q�����1��\x��2̹��b��.:vhJ��O�3KP5�˼go���X��*���a r;9:w�^7���nݞ����`�q���+V&b�c�5 ��-OU��8��[�y�]"3�$Kfxs�ܑ�/�R[] -ð�A�e5��~�� mQ�N�0 ��+|�N����`��@�G��K�5�$��nL��iW�T~~~�ϝ^�*@A�F�A�F�� g��Y6f0���F(mM�߀,�Kx��N���>�خ+�����U��X"LQ����O -:�3�0�E���4��\7"jJD@W��N[5�9� o<�TeS�������\�]]���z7�C�6���������y���`R��#6v^T��J� }ÆԨ�#�3��*�T!����"gY֨���PH�Z�mc���v�x��X�0�>�\3𙁾�p� 2 3�eB� -�Q�ʨ~�6+ͭ�p�UX.�h��12����|/��Q�v|�5`v�� ����d�[-��_���I�x�|�����$ �����W� EPMkB1��W�QE����U� -E{ʚ�kB������}�Oag&;3;}N6�!�1S�Kv�|�k"~|�O�x�`��1T�ț0��;�H�V���ݧ-��}xɁ`�1|Q. S�yv�摡�!��\x��2L�f��b�� -��;6%f�7\� �T���w��;M�%k�b���� �P�������f�����[�b����?/2pv� -#�96Y����X�� -���7�p��D)�%3l�z�������M0ˋ�Ԇ� -��p�\-k -r�;��~�/EP]k1|ϯ�G��Y�ڊR��`��&{M�]�{~P�߻g{�vf�3����38�5��`�C.���~85������P��@ߌE U���HwJw�Eʗ>����D���a����C?�9zÎ�0�+�T`�=0�D�rM�0:�)j�C+���*OP�u�|�ީ�`)�f�U* JHq �;:]�����n٭��'dp�����)�WFKsj�%5r}剉�gTx뱬��z��1zKf�$Y�6���R�B���c�ߠ�h���^�~�� EP]K1|�_��m)�[�Z*D -�� �d�sI����wz�)��dgf�e�d�o�C.���v<�f� -&���1�:T���x���^�޷�nե�88"��|�" G/N-ͩ-������UĆ8���e�v��̪Jo�|# �@eu2��$@'�hv7�7�������?�o�EP]k1|ϯ�GQ��m�b�P��> -%&{&4�������]{�vf�3����2X4Ao�����n8Sӑ�|8OP�� oօ!U�u���[�*�k�G�00CX���t���s-�����c��A{�,0�XH"L9`+-�%ء�T���9`�P5!߻��� F���J���S��ӂ�<������m�nWu��i��&������ٳFJSj�A1�}婊�F�Z��e���EfJ�-�:������b0�A/���o��%\�Q.u�Տ�EP]K1|ϯ�Ƕ��[�ZN,��cArɞ 撐���w�z�)��dgf���e�h�.8".���3���x��xw���Aެ Cj`��gB��u���?�Ȍ�D�Ǡ�&Xj�W�0�,�Ma��&�:�3vX`I��D�r�^:Z0)J���T���)`��t!?���� F���I���S���ӂ</�^7��mW��.��i��&������ѳFJS�A1�C幊�E�Z��e����,��[���%� ����ܧ<1FK��B��d-F9��V?�EP]K1|ϯ�Ƕ��[�Z*D��� �d�sI����wz�)��dgf���e�h�.8".��_2���x��xw���Aެ C�a��gBw�uʗ�?�Ȍ�D�Ǡ�&Xj�W�0�,�Ma��&�:�3�X`I��D�r�N:Z0)J���T���)`�P�!?�w�� F���N���S���ӂ=��/���u��V���i��&������ɳFJSj�A1�C幊�A�Z���e��EJ�-�� {���`�2��%8\!�H��� -~�� EP]K1|�_��m)->�j�T,��cA�ɞ 撐���w�z�)��dgf���e�d�o�CΙ��f8��� -F���j�T,���&{&�KBv���ݽ�ѧ�3�����g��� Xh�R��9g�ۛᬚ�*��� ��f,�����(�)�)���t3�� �/*�0G��~�X�Ö���1�3�T`�=��D�r�N-�5ؾ�T���)GP�!(߻w�� E֬�N�A�)�AסbO�˿��r��]u�.�ġ���?/�p���Ҝ�bH�l_yZEl�3*�qX�iw�Ȭ���̰� ޾7au2��@'�hvW�7���w����O� u�Ao�0 ���<�]�]Ӵ�V�X��(� ����I ����>ʱۢh}D>��|��[�T�d��w��c"��Z^�qc�S{��o�L� -q������Yqӑ��V��s ��`�K�s��b=�']���Q�c����\���&���B�P���[�Yz�MdК`�6��zV7�Q�5l"�P} �`vh���C�������a���ŴF� -T^N,��൶�--�eG����E�IB+��ȷ�K�(Z-�����e+7��2��">l_�@OJ�������1,�Ȁ�x��\M���g���������|±m��Y��^�[�k�̒ ��V/�щ=��~����\�\��|3� �?���fo �S����%����~�W$�-ޑ���?EP]k1|ϯ�GQ��m�b�P�`��&{M�]�{~P�߻g{�vf�3����38�5��`�C.���n83ӑ����P��@ߌE U���H�;�*�K �^``��,����EE�����Gc��YFx�� -̹��R��F6E vh%�7|N�Tm]+߻w�:X��Yc�J�R��CŎ�N�����m��V]��G�2��^���+��9�Œ����Dl�3*��X6i����%3,[�볥�: Eǰ�A��P E�� �6?�EP]k1|ϯ�GQ��mm�R��> -eM�L�]�{~P�߻g{�vf�3;���38�5��`�S.���n83ӑ�|��P��@ߌE U���H�;�2�K /0�Cx*����EE��b��Gc��YFx�� -̹��R��F6E �o%�7|I�Tm]+߻w�:X��Yc�J�R��CŎ�N�o���}��V]�'dp�����)�WF���Kj����&bC�Q�DzN�k#3c�Kf�$V�:[�]��Pt ���PԎn��1�EPMKA �ϯȱ-��U�ełH� ��d��ݙa���7[]=�������]�� �XJ��&�L|s=�����^}`�CC�5cH5l|8Еҽb��w/0�c�/����AE��r?�W�n���=���DX�,5��P/�l�l�I*�k�� -�'���Q~p��M�Y��:�%�8]��/s��U����U��ģ�\�/rp �ѣ9uŒ��䙉�gTx㱬���1�Kf��!�[�R�,�>�I(:��?Z�֫t��4_�m�o�1��律��EoO�V�z'�R,}�?G�Κн$d�z��w�$�jK�&d2���d��)�Fx�)x-�CxvH�G�qV�Ui�Z7�;�����5_nj�u�^oT�\�����F����D�y����~� -ׂ�آ� u�)�u �D�i ���`=~��B�ۦ���{�n�DC�jj�E�� ��Ƕw���r6���G�d,(`'*M�^X�N�7l�l�%r���\fF<"9��{%���HgYK����� 5�;����V� ^�u̘Z���Z�e��A�i���>��'��|Y�,q%(ha`�-zS�0Z�`La*��0ت �ӡ�'�!(��m����FK4Ĭ���^m�XNpl�q��>/��/�Y�JƂ���Ҵ�luP|æɶ^"7�:�ef�=��U�/�4�Q������A R��C�������6�f�Z��AK%^�����C@Sܰ�xʀ�kWl(�Yy�c�S���֬�B���wpV���z��C��y��{�Z��B�1}���,��B����,�F4����.�r�=��e��������;�FF�X]����g�����÷bX�|�z��(uN�����c2�?��0�����O7�?��~YL�T2����]/,a���6M���Q�Z.2#����~n�� ����ed� J��rW� &�� � KT��U "X(qq�f�:��MIp�^R�9^�Y�i��f�Q��OxrhBzX��3�;w��Iy\C� a]�;�Pj�� �O��m�����)�`����Y�Z�Q~�(�E�}N���6���=��s5��u���k��T]A��8üS���jp%���w۳BG�T�=)�+1&�������;/)�tc�CAʃ��zpq�6=�Q~xP��Dz��m�o�1��律�W��zP�V�z'�R<�����5�{I�d�����ItՖ�M�d��3���SJ���S�Z����> ����eЃ{� *]#��`+�Sz��|3��={�Vrمonka~�c��ɪ=_���K\ -Z�c���&L���A��5 �j��th��z -�j����1�� 1���A[���hܦ�/����r����D�� (5�{a [߰i���ȍ��r������ �#Md�e E.#cgP�����~`0��9�M�X��F��Z�R��ë9�>4%�-{I�� x�fŦ���'9�?�ɡ �a�.���}g�qy �7`x<�u�G�C�u�*�?�wYڊ^o�ЃIP�n f#j]F�ٓDI9��ۀ2�����x���i#�n�.�Pu��� �N�����;1�~� ��w��ĩ�{VW�L.�Ǚ F�Ս�)���"��I~tT����}R�n�0 ��+x����6i�����P �NZf*a��R���迗r�%��b�||����/�n�!e�� ������R���f�t\�~j`c,��[�~�tt.팸�� �'�T|eGpm��&��(�e=��ZL`E5�h��%b����G�[K�Pމ�:E�� ��3DM�I�JP�hk� ^�����nB�R� �����7�?V���_,j���� {-j`g���,|bE"� +O �[ --J�A#���>�YQ��}9��Ac�kzNh���vT$��Z<� �V n�IZ���y��1,;dȖ�S��M��`������Z�>ʮ���%��t������Vb���4Y���f�a� -W�hn��[ê,����Q�)&v�����W����\�O!���Ӟ�]�HLNf�G��?wӲ���p��)��^B�M3+ފwu��N1��}�9pX�xT�` 1��&f�;KK�v�b|w��k��K;3��|�w�i�"�0R�)Z��ҹ!����t�`/�2����`Lj�{����b�s�{���C���������(���IE�c�Q��,zXSK��'BD�q�������m -�>��ԭsR�g���< ��C<`���Aơ䎖N]��f�zޭ��X2��� ���.��d������FM���-O��q���������L)��:��>�i�R���i��:�� .���}���@_��50�Z��i��C�u�"˺�?B�h�q�G� ������ ��ԗ�]Q�n1 ��+|�!�Ж���TUHp�T�������BQſ׻���D��x���%U -���Y�Y��s�����Q�@���P:O�gB�%�+w��ҍbәݾ���8�=�ob�0A���{?,�y�a�ި&�I�SMD1yj������9� _#�Te���w�F흥�5k(#P\ �q���ѩ���Z,?6�fT��T(p� ��W/*��RF�αfKjT�W��� ^Wȫ�پ�ؘ��Ps�9��V� ζi�~�B���k@+q��}Y�{��~I��M���I�b��t�F�qK\���]QMk1��W����k��V,J�X(cv��dIf�R��ݺ��%�{/��$���UP� ���%y+_r�(?�&f<40���J��0 ��Οh�t�X���� ��� ^�d����]׏ -z�� �0�G�7�)�4w�LQ�5B@.�F�`�Zb�7�ט@AY��|�ި���Y�r��G����<��{���c�lF���C�3f(|���^�2�t�u��FE���0)W���aZ���E&Ɣ�QC���@<�Ւ��6 з��?���l�^�l[bO����������DR'�^�ӛ���\�/]R�K�@�_1BS)����w~�q�HAd��4�mg���U���ٴ�;�!af~���d�3w��1֢�>�KF9�6=��+8��.�!"�;;VH-,���s�U�/֝B��p��p�EV����|5����WN48���#�B�ƹ9”# 8j�'2c�^�^�Wb���c���^�1x$1��&�8 �f`t�zO�ýۛ����B5��)l�@d�� l�v6�Вz�hB���"�A������M�3l䴪z)��e�Px���ܵaș��D��I���! ~p -��H����kvr��t jM���r�ܦu���n7Ç�5�ҝ�X��|z��ݳ�s��������� �ar2��'�� �g����_B����9)��ߏk�����kg��+�dX�ddz��uRMo�@��W�!QlA�6@IU#U*QO��b��f�ڝ�����Y��Ő��;�fޛ�ї�� ôcKF���C�v�)y���z�VH �,�[ C�sXr������Fn -�8M��(��R�?h��H�{�nޏN��ĵ�$��o����6�)+B]�� T�V,l�H{&�� P�����x��ѥLQY֪rm���V}�r�};��:���l�c9���ƨ{a!��ą�%᦭v&E&ʚ���[��`���U��'�E�z]*��`+#���[ 3gʙV�ܼ8Yf���(�a�ӝ00W;i�ڢ"O�c\c'���S'�f���z�n^��ώ����4�:'Un��m�[�x7<0�R���/�]h��]�S���� &AE�qglH᱗�w�-+ԅæ=�x���__�|6�N[�_] ��Q��r���P� �-0۽}�_!nt;�l���q�aJ0�:�S$����W�8�Hk_�� -���[� -aF�=�I���H�t�} ���n��n]����j��f�� -ӛ ��m�Mk�@���s���Z?j+� -�z,�d31K�ݰ)���F�R�^�}g����cS7P�Ph)u�J���ܐ��g�d�'�î�*��n�z0lj�҈˱ci�����C*2x���Y��$�L�ߋ��J�aK:/Q�+�0u��`GdE�P� �fcE�ƺ��c��UP����ح� �ث��=��F�q�Z+��ͽ�����*��`�F'tPJw�E%����¡� V/*���D�\�,oj�k����$I�/����$�g��h�Bd�C��rce��`Т -q�C� 8��:������m�ڟ1ng]��;f?�X���E��`i�л��Y���?�vI��TQk�@ ~��� `;$M�4Xڬk���(�to���J|�rg������α�M���`c���'�ο�Y)% E������d/��a0�Ѕ�LXXI������L�����X�#���(��QW�g���r^���4����h�@7T��s[.��\�?�RH�bb��ic+�km�e�BJ����� )�\�B�:�U8�m-蹌�u;��M'>UY����3ZH��bQ -��e�᢭.LB ��%�+�9��>Cs�KE�APX�K%��>� 7�E�rv��� �ޚ���%��z��לj���V�?�����0��Z(�l�%�1�� -�'b5ĊK_y`h���3�v�r�v}iޓ�FG��P*�p�sxOv� c�E�}�:(f����q{ -C�?,��N�����Ny����,\4���0� �gd�6:���7�k-�bїv����ƙ�i�c��%�m��l�M��1� -�EiiO�b�8�$��A��Tep�����A����������0�x;tMY�x�e��6k��h��-h�G9 -�9�`�(.cj�OM.�t&x���k�=e��"1�G����rVj�To˵��j;�\�Z��9���~�y?H�6E�)���h�%x����橵i%�Ʀ�6�&؉s`.���Z��A؃pƯݵ�G�12�@���cui�&vH���~��MG���/l |x��������PMO1�ﯘ �U@Q���"x#1�������tB����սy��63o�}tvM�� -�L�$l y�k�4�L��0�!�MPYG�oDl�=�X�fb����<�'xt�?�%� �^�]=.�n;�1�Ek��a���BQ���A@_B��k �~����vN�N��v� �ԫ��Pl�#P:���ҥ�{�,W/�UC��LP���E%\�E4t -5�Bey�y3��\��k_4��H�c���V{04!�[� -�6>�J�α��\û�!��D�1}P�*��z|��v [�!K�+j�Œ{b�Q.�:#`r`s��v��ʿ����x���Q�>�s�`)�\�(!�1�8T��x���^,���nԥ�x8"� ��E�A�*Z�s[-i��+OL�=qA�7�:�^.25�ՠ��R��m�a%R�X��Pr��O�e��T���0��+氇� {-�KwK�+U*ͭ2ɄX�ږ�@i���qHJ����D��y�y�у�$�L�o��]حB3� �^��?2n �9=�d -�����I���̂�I �ǜ�_�����dY�o�����X�l���G0��0&��F\��#IN���%h��V8U,�F��,t��q[�yu��%Ɂ��TKl!"U\��2�jݸ��pO����[�f7�9L��uԄ��ΰFjl�ւ:�M������tt+���|,�doZ�-�hx�b�6��%s�����q<�����*�.��(�EG�Z�u����%wT�:C�g�(S�7Iy -��1�aМ熂�~��ލ��7㷇5<�%��� :gM]���}<�DZ~�o����T�n�0}�W�J� -�N��J:֊�J�J'U&�!ւm� m���CLB�2U�_��9�{��7�*�1ʘƖ��G�ݮ��K�6��Ё��Hx�@��i 2���/��A����B+j�w-�3&~��������e�w]���˙�模o�ƀ�T: 0C$��VjS ->J 6EH�,�s����P�*���r)�@t���Ž秇�x2tTEa6e��@��F cXr�� md�#$�ؗ|6G�m��L?�E"�A@Y#k�+���;��U����4�̱�m���Y�s]��e��m\����S�x OM��T� �ؾZ6;�~��g>��"ڟ��T>��)y�l����C�m9�[W]_�N]](�N ,ĻP�Wr�a�ƒr��f���t�j��{7�^XϬ, �����[wP-��>7�<�ИV�+L��j5i��{n�ps}}�t,���E��a�͇�3���]KO��s�q\�<����6�ʰlޣ�E8s���9?� ��7o'#*8����5�7wȃM�\�����*A�mP�F1�� �@�����ևޭB}�J�.�rë(C��q>͋v5�eI���8�Oz�ȱ�V�o�x��N6�b����1T�t�/5�g�S)|�C{_�u�EP]K1|�_��m)->�j�TZ)�ǂl�=�%!�����^��)��dgv���e�d�o�].���f8��� -F�����6�{�N��Ț5֩4(>�1�:T���t���Y�^v�n��0q(pB��׋,��8e�hNm1�F�?yZEl�3*�uX6imdVU�%3�E� -�fY� �.�Y(Z��?� �PԦ�������[k�@���+��ɸ1}��ԭIH ����1���RiW�lL��.�(vC�"�{v�7s��/eVBJI��B�V%�$w�9��aCx͔��� �]�e0+x�Ԇ�e�+��Y���$��V|�Q�&��(߳e�}����D� -5�v��^���~ ��RMo1�ﯘC�hh��P�4U��*J"�R ���^۲gAQ����’��/+��ߗw��WJ* �E \�B�<ū�q6d0�NJ#,���cpK��xM�z���?^U����%�b��� &��Y��/K��r��h�;5`��L��ЖP8���F\�;�o.�T��=���pA6�W�t�Fag��t��5Ӧ�w;����p���`R��#��ZT†�� ] -R���<�,�=����0w��F�Y֨�Wz��u��&͵��^On�f�?Y�F���԰$�4�׊� ������-��!��ɉ��: ��ڟ�j�T,Z�6��K�[C��‹���ʖ��)a�.R��-C;���;���U�#�*tL�i�|:ˊ�-�����qH�`�1x��su��������vo0wΜ�)�?�T�Io�h���m�W}>g�UQo�6~�����R���&��K&k�W2d�X�#�QY�H*nP��(K'9�����2���;~�]�V$�4J ����E2��_̋��Q� H��5K)� \B��E����V+/8�K$� �<��{J�o�K��W���ML'CX�["$#�hI9\�f� -ѼH��b�� �ݖ2��G�A&�e��~]�S�L �l�� �,φ����3����e�K[��� ��%b&v�h [&���E^�b��I���� ��EB���*r��B��"DJiq�Z+8���Xv!���?��n&ea��f�o�m�Gp��=��M&$pel!1c�\�8O``���Z�j>u�з����.|��O��|�x�- ����\׶t�ɉ���+ߵ������C�4�o_@��!���_�熁���z+=��S �\�f���k������'�>u|�/5䩞ɍ����F��3�c'ܙn��8n8�]Ǟ*�WԂ���=�(^�4�wC��Q��ji�D��q.����Π���s� -C|�JY�]�Z� ������ð*/#ih>j��N��;���5kݫ�W�q4i���V�h� ��B� ;;H���~<�JBIl�x -���C�r}*�w �D�y����B����g����g 4��` T���X�2_�xG���#�H�ɞ��L��r�P����g|��ٳY�~����c����GM���&S%�Q�bc�hQ�ӧf�[�q����z�MU� ���ϟ^��}:�`m��0��� �&[�$u���u�1�9Ʀ�WI�� �����v�D ��ū u�:��^��>�j|Ɖ�*�s2� �p�5A5C&<�"P;�Zw����V[O+7~ϯ����P_��%p��J�}��I�������D(���߂T�~I֞�73ߌ}y��L����Y�]���g��wv҃x��%�7bʀ\�S�7xJ�Vb,�������] �_C&�Fe4\2���ȿO} `�L��a� -.u�qC�PF!ZA`�O -��t��7���8 �<�n�C�ЄU,�Z3å�c���M�����|bM%����2 >ש/�a�M@'�����y�g=�֨#F�OSS��d�׋�����ev?Y�_�o����������q�|Q��}�/��6h�{�Xx6`J��b�j��':�K�8�d,�{�Ћ��BO*�q�#ŅY���Fv;����6��cl�����>ZÌ��/�b��Θ�DP�k����i��~�z@���n6LU�(����l�A8�틆��2�b�Wm�d����m �<��p�Җ�B-� Q� -^�3�(� ʕ\,�*��3N%�,�Awt�C˰���֑��p�*��"M���U|C 5m���;���B8"��<3Š�Y��:�=O�5#�K�j%\�&V"��?��K�(B���f�YL��_B���;k�KY� Um;�X� �?��u��0��@jc���� G���l�q+S�ri�3�h�!�p��2>V -.�,;�Q�J-� G�,��4*���E�fJ��h���)�X'�N�Y�l~��cL�g��c αvm����JXv�k`m�� -��gV���.gu�&LQ��p���ŗ��\`8�z{j���|69�+�a�k;{𘱅���1K1t9�J�D)�������X2���sJ��8&����Q�mdg�U`�k���6d�}�UR����x�d��U֞�8L��]�������5����c��lebG��qhN�w������~����j�f��-]Dx� ��Zx?qM��h:��P{o�~O/ bm��vU�j�vt�W;0y����B˭)5- (y8�NV*X'� xR�h�>�[DJ.ҪN���b~;�ߪ��#\�?�Z�-�wS�U@;^�d|ɲ��_Ӯ(V�oz�ҵo��+IT<6�ʴ&eq-ԟv����Hn���:M��5IG*��(&m�� ��ϣ>�۟�/��C#��p��[Gz�8�������I�+]U��_5q�x���Vms�F��_���#�*v�i�&~��k�ȂA�I��h�pLлC�'��qB���|��{��Wv���,� �~L5�`�/��!���M�s�ׁ=�ˆ�,�)�oF��tN-�>^K��f,�� ��P���+e�������}?�'}��[�ED��9epī�S���YL%H��&h�m.R�K��)R��q���v��#�&mMf)�FD�&}@:�g���r��i�'��*!pG8_��E"�t��9�)* -*�: �FyF�� �9�tr.� `^�#��=[�� /-��;� �Ñ=�\�v���hd����c��DzΦ���P���HCt���g�:�ݥ�ڞmڣ���_Z/�&��|:�ݻ�w�=w0��[Z�'���O����'Z�jz���LC<�l��FO8��o�NO/<�)ݞ�/K -c�a��>L�=� o\t%4h��E��a���9�9��4�Es���>{{p� ��d-&sH3)Mb�Xz�h�,@ G�v��+�8��h@��b��3k@��dl���l0G�&����R���'��&��h�({��o�8��M�{��� b��������4!L��$��5�o����N�ba�/�2(uz�U(��H^���T ��<�Up*�����}P��V�|�n}�ayݥ,(�{`�F��5U�Ƭ�G>� �n^+*^����W]l+�� -��7����.GSW�}��p���-&$4Edh�`ǯ_Z�+�kU��ɜ� �i�ѻ�S���E���Z!K��ݺ�i�����L�_�ݰ4�ѹ�}-�/JC�u�5Y3�����Ԙo��9N�(���P�.�i�2�65ի����%��RQ�l�lZ�Մ�6Mc��҄f����f^o�Q��PڙHv�jԂ\v�խeccH���'�K_�\_; -䯯���![@�>��ö@b��z���T�ΰ���w����V�'���^Ǭُ������ׂU��"W�=9�3���w�B�}K�����y�[�Y��v����n�ap�� ��8J��p�����wŲ���y��������Z�|d�)7!��[ƺM���k\�]�孭�$xj��k��l���:,j��ꎱE#�#�Ff�'����hCm��l5��0���|�>�Z���Ç��~�ɣ{3-��T�Ky/h0�6�V/kQ�Zk� -� =YsQ�T�m��ZgG��V��}�o�u�G���H?GU����3��fD��S����>< �ӧ��mV��yi4��NF+��N nz����@�=���;d�~�urX��PN,S���K��W��]p��uiwo�8 ��U��I����C.���e�� aZ��aM����b�~a���{(β����`�N�>gӑeK�_QN?�+��9�3r���َcF��y5mR��1 ��W��aZU;�J ,�u%@�ސ�g�i"�$r�V+Ŀ�dw$�v.Nl����ٽO6���#S���$?�1Q~�z��uk�n]��y� Y �p��LWZ�71=�;Z�~Z�=�_Ēa�z�����w���8 ��B ��$����6S Jl,9?����{�/�k�w��\����b؀¡�Ύ.��绛ۯ��� ��`\~�E.N�VTt��'�Af�13�,vendor/phar-io/version/src/VersionNumber.php�9?�gkJ}�r�6vendor/phar-io/manifest/src/ManifestDocumentMapper.php�9?�g��q���.vendor/phar-io/manifest/src/ManifestLoader.php�9?�g�q�̤2vendor/phar-io/manifest/src/ManifestSerializer.php�9?�gM�ۂ��Evendor/phar-io/manifest/src/exceptions/ElementCollectionException.php9?�g0_Uj�4vendor/phar-io/manifest/src/exceptions/Exception.php�9?�g��P��Jvendor/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php19?�gFM�V��@vendor/phar-io/manifest/src/exceptions/InvalidEmailException.php9?�g+�ˤ>vendor/phar-io/manifest/src/exceptions/InvalidUrlException.php9?�g/�Y��Dvendor/phar-io/manifest/src/exceptions/ManifestDocumentException.php�9?�g'�� o�Kvendor/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php�9?�g!��գ�Jvendor/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php�9?�g0`'�i�Cvendor/phar-io/manifest/src/exceptions/ManifestElementException.php�9?�g%#mߤBvendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php�9?�g�4o��Bvendor/phar-io/manifest/src/exceptions/NoEmailAddressException.php9?�g4�q:�2vendor/phar-io/manifest/src/values/Application.php�9?�g5f��=�6vendor/phar-io/manifest/src/values/ApplicationName.php�9?�g��-vendor/phar-io/manifest/src/values/Author.php9?�g�vendor/phar-io/manifest/src/values/PhpExtensionRequirement.php�9?�gw�(��<vendor/phar-io/manifest/src/values/PhpVersionRequirement.php$9?�g��"�&�2vendor/phar-io/manifest/src/values/Requirement.php�9?�g 1�q��<vendor/phar-io/manifest/src/values/RequirementCollection.php\9?�g�X?��Dvendor/phar-io/manifest/src/values/RequirementCollectionIterator.php�9?�g�� �+vendor/phar-io/manifest/src/values/Type.php�9?�g�<�2�*vendor/phar-io/manifest/src/values/Url.php�9?�g�#lͣ�1vendor/phar-io/manifest/src/xml/AuthorElement.php�9?�gn���2�;vendor/phar-io/manifest/src/xml/AuthorElementCollection.phpS9?�gS(�7�2vendor/phar-io/manifest/src/xml/BundlesElement.phpz9?�gf��}�4vendor/phar-io/manifest/src/xml/ComponentElement.php�9?�g`$Sg�>vendor/phar-io/manifest/src/xml/ComponentElementCollection.php\9?�gT6� �3vendor/phar-io/manifest/src/xml/ContainsElement.php�9?�g�a��Τ4vendor/phar-io/manifest/src/xml/CopyrightElement.php 9?�g}�t)T�5vendor/phar-io/manifest/src/xml/ElementCollection.php�9?�g�砲L�.vendor/phar-io/manifest/src/xml/ExtElement.php9?�gK����8vendor/phar-io/manifest/src/xml/ExtElementCollection.phpJ9?�gQ���4vendor/phar-io/manifest/src/xml/ExtensionElement.php�9?�ga�z�j�2vendor/phar-io/manifest/src/xml/LicenseElement.php|9?�g\t�TX�4vendor/phar-io/manifest/src/xml/ManifestDocument.php 9?�g9�S�3vendor/phar-io/manifest/src/xml/ManifestElement.phpn9?�g��p��.vendor/phar-io/manifest/src/xml/PhpElement.php9?�g��wO�3vendor/phar-io/manifest/src/xml/RequiresElement.phpK9?�g^��ܗ�)src/commands/composer/ComposerContext.php�9?�g�x���)src/commands/composer/ComposerCommand.php�9?�g��`^��/src/commands/composer/ComposerCommandConfig.php9?�g�l� Ť)src/commands/composer/ComposerService.php 9?�g,�����'src/commands/default/DefaultCommand.php,9?�g���e�-src/commands/default/DefaultCommandConfig.php�9?�gt@�k��src/commands/help/help.md� +9?�g�$4w�!src/commands/help/HelpCommand.php�9?�g�J�D��6src/commands/install/InstallCommandConfigException.php�9?�g2��=�'src/commands/install/InstallContext.phpN9?�g�|S���-src/commands/install/InstallCommandConfig.php{9?�g��k�'src/commands/install/InstallCommand.php� +9?�g��;��!src/commands/list/ListCommand.php�9?�gD|��'src/commands/migrate/MigrateContext.php9?�gPX'�e�'src/commands/migrate/MigrateCommand.php�9?�g�� �"�-src/commands/migrate/MigrateCommandConfig.phpt9?�gt��W¤1src/commands/outdated/OutdatedConfigException.php�9?�g�-4��)src/commands/outdated/OutdatedContext.php49?�g�#>��(src/commands/outdated/OutdatedConfig.php99?�g� �� �)src/commands/outdated/OutdatedCommand.phpQ9?�gPK��5�#src/commands/purge/PurgeContext.php�9?�gf��P�#src/commands/purge/PurgeCommand.phps9?�g�(Ҥ%src/commands/remove/RemoveContext.php9?�gQ'���%src/commands/remove/RemoveCommand.php 9?�g0��M�+src/commands/remove/RemoveCommandConfig.php9?�g���/@�#src/commands/reset/ResetContext.php�9?�g�E�Ŵ#src/commands/reset/ResetCommand.php09?�gr#�\��)src/commands/reset/ResetCommandConfig.php9?�g�I��Ť-src/commands/selfupdate/SelfupdateCommand.php�9?�g�gY�U�!src/commands/skel/SkelContext.php29?�gX�_Mʹ'src/commands/skel/SkelCommandConfig.php9?�g�!�E�!src/commands/skel/SkelCommand.phph 9?�g�V����%src/commands/status/StatusContext.php�9?�gt�&��%src/commands/status/StatusCommand.php� 9?�g�����+src/commands/status/StatusCommandConfig.phpI +9?�g<U氝�Csrc/commands/update-repository-list/UpdateRepositoryListCommand.php�9?�g�8H�j�%src/commands/update/UpdateContext.php�9?�gz�f�%src/commands/update/UpdateCommand.php9?�g���k��+src/commands/update/UpdateCommandConfig.php 9?�g�@�$��'src/commands/version/VersionCommand.php 9?�gO���src/commands/CommandLocator.phpq +9?�g��X@�)src/services/checksum/ChecksumService.php89?�g 2w��+src/services/key/gpg/GnupgKeyDownloader.phpt +9?�g��ݴ)src/services/key/gpg/GnupgKeyImporter.php�9?�g���~�(src/services/key/gpg/PublicKeyReader.php�9?�g5��W�"src/services/key/KeyDownloader.php�9?�g�}.e� src/services/key/KeyImporter.php�9?�g�j�$src/services/key/KeyImportResult.phpa9?�g��AG��src/services/key/KeyService.php 9?�g���U�src/services/key/PublicKey.php�9?�gr:F?�&src/services/key/TrustedCollection.php�9?�g��a~ �(src/services/migration/FileMigration.php�9?�g�t ;Y�0src/services/migration/HomePhiveXmlMigration.php>9?�g�?겴0src/services/migration/InternalFileMigration.phpY9?�gm���@�$src/services/migration/Migration.php�9?�g�X'mY�3src/services/migration/ProjectPhiveXmlMigration.php�9?�g;9̴,src/services/migration/UserFileMigration.php�9?�g`�p�0src/services/migration/HomePharsXmlMigration.phpl9?�g�]]&�+src/services/migration/MigrationFactory.php�9?�g�r�A�+src/services/migration/MigrationService.php�9?�g��H4�$src/services/phar/PharDownloader.phpV9?�gx�^��#src/services/phar/PharInstaller.php� 9?�g 5�JԴ*src/services/phar/PharInstallerFactory.php�9?�g��� �*src/services/phar/PharInstallerLocator.php-9?�g��V��!src/services/phar/PharService.php�9?�g�s���%src/services/phar/ReleaseSelector.php� 9?�gm��*src/services/phar/UnixoidPharInstaller.php79?�g�n0ޱ�*src/services/phar/WindowsPharInstaller.php�9?�g��G��$src/services/phar/InstallService.phpv 9?�g����*src/services/phar/CompatibilityService.php� 9?�g�>�~^�$src/services/phar/RemovalService.phpu9?�g��%�<src/services/resolver/strategy/AbstractResolvingStrategy.php 9?�g+ז� �>src/services/resolver/strategy/LocalFirstResolvingStrategy.php]9?�gqM;B�?src/services/resolver/strategy/RemoteFirstResolvingStrategy.php^9?�gr��ٴ4src/services/resolver/strategy/ResolvingStrategy.php�9?�g-W$���7src/services/resolver/AbstractRequestedPharResolver.php09?�g82�Q�+src/services/resolver/DirectUrlResolver.php�9?�g7�k��-src/services/resolver/GitlabAliasResolver.phpG9?�g��v㵴,src/services/resolver/LocalAliasResolver.php9?�g��_���-src/services/resolver/PharIoAliasResolver.php� 9?�gI�D�X�/src/services/resolver/RequestedPharResolver.php49?�gW�R/�6src/services/resolver/RequestedPharResolverFactory.php49?�g.�?L�6src/services/resolver/RequestedPharResolverService.phpW9?�g�z��N�=src/services/resolver/RequestedPharResolverServiceBuilder.php�9?�g�+��&�-src/services/resolver/GithubAliasResolver.phpt 9?�g@�Zt�6src/services/signature/gpg/GnupgVerificationResult.php�9?�gkL�5src/services/signature/gpg/GnupgSignatureVerifier.php89?�g�e '8�,src/services/signature/SignatureVerifier.php�9?�g>P���-src/services/signature/VerificationResult.php49?�gJO���src/shared/cli/input/Input.php�9?�g()D�U�%src/shared/cli/input/ConsoleInput.php�9?�g�K�﨤'src/shared/cli/output/ConsoleOutput.php� +9?�gy�T:|�&src/shared/cli/output/ConsoleTable.php�9?�gr�d#;� src/shared/cli/output/Output.php�9?�g^���J�'src/shared/cli/output/OutputFactory.php�9?�g[Q�@�.src/shared/cli/output/ColoredConsoleOutput.php[9?�g;�b�'src/shared/cli/output/OutputLocator.phpg9?�g� A��src/shared/cli/Command.php�9?�gǯG�*src/shared/cli/CommandLocatorException.php�9?�g&�t��*src/shared/cli/CommandOptionsException.php�9?�g<��@�src/shared/cli/Context.phpz9?�g��4nN�#src/shared/cli/ContextException.php�9?�g/�U�#src/shared/cli/RequestException.php9?�gGM�ꪴ"src/shared/cli/RunnerException.php�9?�gm;g��src/shared/cli/error.txtR9?�g���'�!src/shared/cli/CommandLocator.php�9?�g7��o6�!src/shared/cli/GeneralContext.phpB 9?�g����src/shared/cli/Options.php�9?�g����6�src/shared/cli/Request.php9?�gu�7N�src/shared/cli/Runner.phpV9?�gE0��N� src/shared/config/AuthConfig.php�9?�g,d���#src/shared/config/AuthXmlConfig.php 9?�g�S�঴.src/shared/config/AuthXmlConfigFileLocator.phpx9?�gf6�ô)src/shared/config/CompositeAuthConfig.php�9?�g�1z���*src/shared/config/GlobalPhiveXmlConfig.php@9?�g_�fK[�)src/shared/config/LocalPhiveXmlConfig.php�9?�g��q�ٴ/src/shared/config/PhiveXmlConfigFileLocator.php)9?�gWQwŴ+src/shared/config/EnvironmentAuthConfig.php�9?�g�Vo��$src/shared/config/PhiveXmlConfig.php�#9?�gh} M�src/shared/config/Config.php�9?�g���5�&src/shared/download/FileDownloader.phpj 9?�gFcz���-src/shared/environment/EnvironmentLocator.php�9?�g�����-src/shared/environment/WindowsEnvironment.php�9?�g��� |�-src/shared/environment/UnixoidEnvironment.php +9?�g�Rs���&src/shared/environment/Environment.php=9?�g'f)��'src/shared/exceptions/AuthException.php�9?�g Z +P��)src/shared/exceptions/ConfigException.php�9?�g �.�´-src/shared/exceptions/CurlConfigException.php�9?�g�?c��'src/shared/exceptions/CurlException.php�9?�g α���1src/shared/exceptions/DownloadFailedException.php�9?�g˫)$�.src/shared/exceptions/EnvironmentException.php�9?�g��bb�(src/shared/exceptions/ErrorException.php�9?�g I��W�#src/shared/exceptions/Exception.php�9?�g +�<���+src/shared/exceptions/ExecutorException.php�9?�g.��1src/shared/exceptions/FeatureMissingException.php�9?�g����2src/shared/exceptions/FileNotWritableException.php�9?�g�g )�&src/shared/exceptions/GitException.php�9?�g +�=��5src/shared/exceptions/GnupgKeyDownloaderException.php�9?�g��m8�%src/shared/exceptions/IOException.php�9?�g +�˸˴5src/shared/exceptions/InstallationFailedException.php�9?�gu�.src/shared/exceptions/InvalidHashException.php�9?�g`o��-src/shared/exceptions/InvalidXmlException.php�9?�g��}�5src/shared/exceptions/LinkCreationFailedException.php�9?�gH@!g�,src/shared/exceptions/MigrationException.php�9?�g �g�3src/shared/exceptions/MigrationsFailedException.php�9?�gtshg�3src/shared/exceptions/NoGPGBinaryFoundException.php�9?�g� G?�+src/shared/exceptions/NotFoundException.php�9?�g�A�)�'src/shared/exceptions/PharException.php�9?�g +2eh״0src/shared/exceptions/PharInstallerException.php�9?�g�B��/src/shared/exceptions/PharRegistryException.php�9?�g�Qz-�,src/shared/exceptions/PublicKeyException.php�9?�g���L�*src/shared/exceptions/ReleaseException.php�9?�g |���*src/shared/exceptions/ResolveException.php�9?�g �Db�.src/shared/exceptions/SourcesListException.php�9?�g)z�/�?src/shared/exceptions/UnsupportedVersionConstraintException.php�9?�g��R�5src/shared/exceptions/VerificationFailedException.php�9?�gV��&� src/shared/executor/Executor.php9?�gZ'&�/�&src/shared/executor/ExecutorResult.php"9?�g��8�� src/shared/hash/sha/Sha1Hash.phpL9?�g���|��"src/shared/hash/sha/Sha256Hash.phpR9?�g�$�U�"src/shared/hash/sha/Sha384Hash.phpR9?�g�-G���"src/shared/hash/sha/Sha512Hash.phpS9?�g��Cc��src/shared/hash/BaseHash.phpN9?�g���qQ�src/shared/hash/Hash.php9?�gB.�� �6src/shared/http/authentication/BasicAuthentication.php�9?�g��B-��7src/shared/http/authentication/BearerAuthentication.php�9?�g/��'I�6src/shared/http/authentication/TokenAuthentication.php�9?�g/E�� src/shared/http/CacheBackend.phpL9?�gS�4�src/shared/http/Curl.phpu +9?�glBi��!src/shared/http/HttpException.php�9?�gfd�_�'src/shared/http/HttpProgressHandler.php�9?�g� Y�_�)src/shared/http/HttpResponseException.php�9?�gmȑA�"src/shared/http/Authentication.php�9?�g�����src/shared/http/CurlConfig.phpG9?�g��� �%src/shared/http/CurlConfigBuilder.php9?�g �����src/shared/http/ETag.php29?�gPxp�r�+src/shared/http/FileStorageCacheBackend.php�9?�g�9[i�(src/shared/http/HttpProgressRenderer.php� +9?�g��F��&src/shared/http/HttpProgressUpdate.php�9?�g'mKdĤ'src/shared/http/LocalSslCertificate.php�9?�g��ۥ��src/shared/http/RateLimit.php(9?�g���$�src/shared/http/HttpClient.php�9?�g1K �e� src/shared/http/HttpResponse.php�9?�g|���&src/shared/http/RetryingHttpClient.php�9?�gp+��@�*src/shared/http/RingdownCurlHttpClient.phpn 9?�g� ��T�"src/shared/http/CurlHttpClient.php�9?�gC�DU�+src/shared/phar/ConfiguredPharException.php�9?�g/4@ý�"src/shared/phar/PharIdentifier.php�9?�gl��I�src/shared/phar/Release.php9?�g8�E�ȴ%src/shared/phar/ReleaseCollection.phpH9?�g�cG�!src/shared/phar/InstalledPhar.php?9?�g)7�src/shared/phar/PharAlias.phpv9?�gm��y��&src/shared/phar/UnsupportedRelease.php9?�g��-� �"src/shared/phar/ConfiguredPhar.php3 9?�g�o��src/shared/phar/Phar.php�9?�g��=@j�!src/shared/phar/RequestedPhar.php� +9?�g�0�$src/shared/phar/SupportedRelease.php�9?�gO��1�src/shared/phar/UsedPhar.phpv9?�g�<����src/shared/phar/PharUrl.php�9?�gb�,g�*src/shared/repository/SourceRepository.php�9?�g*�d�д*src/shared/repository/GitlabRepository.phpI +9?�g� 2t��)src/shared/repository/LocalRepository.php�9?�g� im��*src/shared/repository/PharIoRepository.php( +9?�g{���'src/shared/repository/UrlRepository.php9?�gV$t�*src/shared/repository/GithubRepository.php� +9?�g����ۤ,src/shared/sources/SourcesListFileLoader.php�9?�g9�>K�1src/shared/sources/LocalSourcesListFileLoader.phpC9?�g�n�CҤ2src/shared/sources/RemoteSourcesListFileLoader.php9?�g�sSD�src/shared/sources/Source.php\9?�g �[ �"src/shared/sources/SourcesList.php� 9?�g!dN��+src/shared/version/GitAwarePhiveVersion.php�9?�g%��BǴ#src/shared/version/PhiveVersion.php�9?�g��L++�)src/shared/version/StaticPhiveVersion.phpa9?�g`V�kشsrc/shared/ComposerAlias.php^9?�gI�����&src/shared/FileDownloaderException.php�9?�g���f�src/shared/Git.phpV9?�g�5<�Ӵsrc/shared/JsonData.phpM9?�g�讛 �%src/shared/TargetDirectoryLocator.php�9?�gyH>Of�src/shared/PharRegistry.php~&9?�gc�� �src/shared/XmlFile.phpm 9?�g8�j��src/shared/GnuPG.phpX9?�g0���src/shared/Url.php/ 9?�g ���Ф$src/GithubAliasResolverException.php�9?�g��Q��src/PhiveContext.php�9?�g�4��k�src/autoload.php�S9?�g1 "w|��src/Factory.php�F9?�g� +�(��conf/auth.skeleton.xmld9?�gR�&�ٴ conf/auth.xsd�9?�g���:%�conf/pharBat.template%9?�g'����conf/phive.skeleton.xml�9?�gR��conf/pgp-keyservers.phpG9?�gAfd��Wmo�F ��_�v�$e��t�:s�"M�����)��DY�Hw��$h�߇;�X�d7��O�D>$RO��� B ��դx@s��P� ��`)�1S���O�ý&L�{� aZ�[�0 ���K����S`�|{;S|���1�� �,_$<�(q)`>�Ф���ۚ{��ڛ_�b��'(t��\�����x��܅Y�r�[ �-݂�zr��K^�"���rε�x0�w`Z�oUP��lx�l��l��I�f �u����1���@��I6ɨ��.Kd���M S(L.��6��"��1��13�'�Q�W��M�EW�@H1L���� S�k `6�Q?ȟ�!KX�R!���zBZ�L)v?�1�ȭ�j�i�W�Dcw�%�<]��”qQ��{dYh9�׆3d.��aI�q��V��;�î�˴X)�B�Y�!�L.�;魓�X�'��m�[fW1�4��$`x�N6���9ca 5h�39^7�B!�i?z�u�j�?�?� �����ys����?��+���o�Va��k}H�7�5�(��­�ͣ壖{"y�ˍ ���k�������o +A�l���نW��Z!���M�z���w��b%�c�QqqV������,��u8��?N4�� ҷ���m�= �0���~ō:����:�����Air�4��] +���/��u�Z&o2OD�#�hI,X�lZA0=K2�x�Ln�q��E�� +�����Ls� �I] ȃr����@DL��w��(��n���hy�U�b��(7�y�X����h?b�G�7���Ͳx� Eȱ +�@ �=O�����8��Mp$�<8�pɠ�������z[�h���GMs�玟MKE6����U�w�>)c<=��D�ŝ߇�"�q��<���?]�PMk�0 ��W�ЃZʮ��m +� z�SC�K ���>��iɺMG��}=>���'�Z���.���*N��6o���}kkܟ�� ajMqg!�Yϝ�2>C��>�N3��0��C�v��i���ۏ�8�ΰm�iqh �Qq��'�Ģ���ęqei�IXX�Z��������徫9��ju��*����$%�� �Iy*� +�r1�$9�M����I�D~�FJ���4"W���MJ��/��[o�J���S��6"����4iKu"�i�pt�6���޵v�!��w��|�6 /�z�7;���߽O�����ܧ �4�����H�M�|����[��� |���r4�3쒵�_��l2��g����&�l9������^�I�$�a*}�J�f�+iH�>����܃_����)���*�(i���n&3����*�FJ��3Y�� KzRJsB.pS՘� ��/\��r�����5'�$��Cp���J��Z���Ů�tZ�Ss]#��8@���*A��P;f[�&�£�� ��O�}�A�����_?�Y�[�O\�OJ�]�����i_����%��&�����4��Bm����M)��?EZ�@����x���u��L��Y?� Exs��z�v�U˴� :P 4_*{�d�:�L��Κ��c�S�z��ڮTu���ζxg�ʧY�-��9/�T5�X�I��5v�ڴ�#�r�X�E>.ۣd,����J��~gmnW�+�*��Ҹ{�4v؏I4��ΙWp�pxs83δ���c�p��5k�^���Eg�M�|���;��n��⩦�Q-���Ns{��98�r���6���<��dak+|� +��&ԫ�I��#\�o�8�z`0��N��� 3�U<���a��''�Ji�B(��� F����?;�txʕ[8��u�p�̱��z�u���;r} 7�C��U�X���|j[� �8�^_b��o���T���}�_�)�߬�����m���ayw�uY��=���/�(PHIM�I,J�(.)�L.�/�,H-V�U0Դ��K�M-.HLNU�H,�̏q��I �,.I͵��J�I,.V �ԹV$��d��)�V���+ D��j�u�MK�@���#��xM�z�BAQ�� +e��������"��ij���eٝ��}f��*+�I(t�xvR�y��m:� j� +����.�R���3�q ��� z~��,$�O�?�h8lN��,Bn.5ʹ�W����9+'��� o�����^))���`i ,��Ϯ��%���#ĀK�o�)�t]B�.�!s���C�7 ��[PN��p������:�{F]%�0 �pM�KkH;���L8B���zj�FN����ݙ���3��$W�&D�(��{M�Y��9(� �e� w ��pĵ3��'U$GۓI�oU�7����h.n5�`e���f���� w�/�SAn�0��[CF$Æ�v�����h�����" ��LR�{AEbHYM�C̋���hgt�UW:��AҌ#����S���ȝUf�e�B�V����,6��g���׌z����Y��|�̰z�\5 ���0��5B!g�x/d� ����Br������ݮNr+�l�X�W�< +;����ؠ$g�C�ς,Cv���(d��(� 8��FH{(B۟�)����I�3�Ta]3s<��0zh7Y�e�L7 �x1E$i�rV;;���Xw�ħ�������ݛ �:e���uD��ʨ'J�ڇ ���b����cw��(�D��_�e���m�Ӓ.ȟQ׃���#�)nc2��� + ���N�cd�q ΰ�9=�]�~({���'�(4� �Y7JPD��]�7�/�c���߰��U�1 +�P �=���`�gq��"�K�*��Ѥ�xwq���|�K%�FeQ<�n>MeO� +������|�A[�́�G�[$�W�mF�S����Ӫ���7}��?O�0�w�JT({ U�@0�\�4��:���*�叓6 �%���ݽ���M,�<��r!�5��l>�G)<\2&R���7�|Jpb `E� <8�#Tz__-�Ӣ:p�p%L�q�[;9"�6O��>�i�%��Vf'���(�hG���% �r�Y=@3����Wh��4�mO��m�|{-H �M��,�U>0oK]é�VԧD�����Y�IU�P�p;�P���I�����Q�֘tt2�޽ҹ�y׵��c��B���!�%���`N�ž��/e�#��ЧpS�U����'�Y�~�\m��6��>��,��nbImKN.w�������$9���@˴�m�TH���E���(�b��{n>I����b��<��ݱ#;V7T���(^��5��i�=�^}�BЖ�֌��z+��G����݋uC�&o��jcM���!�|� yO;r`�)j؎2.�?����̣T�1c��ɞ����t�oX�7� �)o��-���P{�F�0,�`���TcX1o��t۰�˳��pI����򜦓�H����\k���{����dG ���f����j1�1D�]'&p�ف��X˝��լڼe��E��aJ$ܡ������˃�+�*��`�®�wJ>��0�)�>J��� h�:�C:�'ip�Z�&�f�@.o����C���+3���G���D0:*-濌�������� =R�k�N���C��(ڴ'pfi�@�µӝ�d�'CL�z!�Q:Ư-"ԛr�(��/�c5\r�uO�����/��Q>9�#>\���ҤUE�o�ž&'�z����KR9)���F�%������`���~�U�!���u 8_/��U�e��믃����׳�#,W�8�ю��[t��#��3��=�{��k�ʛO�}�|����]� ���� 6��x�Yg{�j���8�W*j��w/[,<�o��<�"K��=u\�� ��${�,��b�[�*x��h{�X&&�4�6!t�,�R}wXN����C/{�n��\4�b�0[��ϊif�b��!y��Z����e�?���0�=�,+f����f�- ��X��]yJ-Wm���cQ���U-��W�q� �O���>"L���{B�*@�@6[ϋlZ��eaGY���O<��x�yvF�d-̽{t��Cqfy +� n8m����*���r�0xCDN8/1So����b��h�m#��b3Q?�)�����I��*{��3�M� �µ�:��W��x�R����@��ydM�;�kR��C�P��Cױ� +�[O"��es�Er��0� �&A���y�$ ��;�$2%� �d��ҐI@���g�L�l�¨t�h����M��\O���}�4�tȺ^GhxN̐=\c������J���E� �<�f��D�� z�Ұ��/�� 1��`��f��H�r{�@M�`|��ʑ*Z�g���F��K3p|i�$��fJBȹ�O���1�nȎ뮡'r��\>�:b ���.��<[��p /d���\� T�ݱ����2"]4�q�G [Ƽx�̿�6_���V4 ��̦��F�H��X�.����+�I=eFr�f��\�F�H�����y�P���2���D�;v��)�<��i�+җ���>*H��.����`%�j��#tgs/��Xj�]`�g��T�<���%J��.��|d֚�$J�L�=��H��t�Ƚ�{.�KM2�������פ�=aȣ�yT�C�IT?PPb�0e���f���=�B��/Q7��/������C6a��ۘd��C�E":�]�w��1#/��ۘ� 1]��W_�&���e}���#!;3pt��� ?3��KϏGNȑ�s���^�C�Y���G����~��չ�;)����>�Ϗ��E�m@>��똩��.�ZzG���<� Ֆ�v(�]��;��$_ΉC:�5�*�#7GBmz��kz��2��!����% >+� �?�F�/���Z�ۻ��Y�e�+�kց��4 S����L��Fi�(U�Q���Cl�8 ~�C��s�’�KK�N�pZ�"���#ƾKŖ�������� [����~o BU1j�5I�n�X�3�l9<=뜲��q'w �<*%�3�n��>����6�,;g%�3�1��av�n�mS���r�����2�&[��u��OUp��� �[��֫���\W>ﮪo� +�E�u��.ܻ��!�"k�b�M���R�G.v�q�&[�4�_���\��]T�y3����8�@��>�T��t�j�ЈFh�F�k�.��@[v 7�������qQ�JX�-\��q!���4�Yh)��{3��;tI +�]�'�3�� +l}V'B� �P�3�dH�� �r'��ܵ�[��|�C�@��+�?��V>MEƸ'�pQFF-�.L����l�MM��Q��3Λ����m ;f� �pe޼���m& ��Q9���*��z� v;7�F�h�$��χ��:���e�B?2�B�=�U�]@��"�$�_�?�B��H�]D�aC����������.�������P��*�լ�7qO�b��V�����w;�����Ag�0���?���Z��KX�34�.r�|f�y��l�|���z���Wl����D9{�����9����W�H�!��CV�˦W"�q��(�$ c�ү_̵��ɮ��e��[�=� ��[�J��xB?���z#���({6��<���7𩦈r�Gƣ�RUɞ7�A(Waui�]Ȩǝ�s��{�:�s�픐N��ӈ����B�[�8[�K�\�i�Y h��|O��W��:��Q�h̶�W.ѝ�:d�̣.�#)]�o��"|�����:�AbY^�`�e�d�T���Kt:�� ��6^2�*�^�N��y�w�L�j���LL"oU�U�!����ZHd��h>�[�jv�� +젱S|�^��se�u��,m���Ξ2?�����;��� +�S�U�ʏ����D�9�ڱ=�5�_ՠ�����uJv��� �P艶_�g�9x�S-=9 v!Ɛ�<�� ++�c�O\-)f�\K�@�m8Չv�j;z�/�y��a� % �{��W�1��L��֓*[V��d��T���?���fWYZ����F���z(6��F�9������������@8�*C�v�rM��F���"�K_�*���y� �J�1���\���s�A�V�����?0�7RHƟF ��M�RHT�M#���)��S�����nQ���\�X���Y��S��dͣ��1�m�T��*�//��=�f��o �Fn7�h �[��+W�f]��a�qӾ�Q�q�%��O�n�^����J8㽙� l�BxڤY +�j�~���/ݶIw�{��"+��*��'� �S�!?Q�يx�u��.Q���t�F���*��U���;%�8��q�O^�la��r��H������W��F���W]]F/1z}�Bh(�^B��]FW�_F�1�xF: N�X��3�̰8�gęay��3�-�h�%Z<#� �t�E���D�-��=j_.��b�?@l����\aT÷��_Qe��(�����`(��7����^�!W�F�V*C���A��b9cwS/Ǟ76U�{.�������v^/Hjh9/5��(* �y�Z��|�\�Ҽ6��tc�Ej�-R4'�o^ϋyVwl� Q@� +��b~���Q:�޶i���,A��{�[[lդ� 8L%�t�]��U�Q��� V8O�s�e5��>��pm��� �-�=�@��%@��&��)}��س1|��F�y�'��p�o ����7+k�G����� �ANR ?I�RM�� ��J��ʮ��D�F棞�_v����Fx�e�,��~�C��M��}�'�>;27d�l3>�D�d3R/xbs�2����,PYj��6��3s�MV "�M�����2���hoH,�ܷ��/���� ��؈ �h����l����H�B~����A����{��_��̼Ԭ��mK��Y��_��p�gH/����9zΧ��x��y�/�x�E�1 +�0 �=�Ȩ������$����P����t�*ᝒ�ic_�V1l�*k�t�)��R�y��4�����E};#Jv��p��>uT]o�0}ϯ����@�=��V�(E�(*���L�.�R�-�)���>9_@H���>���{��V&�T!�F��Df'Q��z��5jIc�YB�D�c�����t}4�b������Kq����O�06B�>�_q�8��.�q���&8�y/�RhT��A���s����ӽv�j 4��{{� +�i/�b�� tVr��qjs�,�/tk_��ڨ�n��]��T�����]�(�eo)�a��"�(�}��c��ԃ�� tL�t��r�����`�Bj0��;�D���z���QD�b�x� +��qW�zǹ��� '�x�{6~|�5��o���P=7������-Ɩ��2���h4��Pk�@R�`�����'O��W��Z +M�xn}�?`���X��j���&Tv�x6.�0�&���kYSq;!��:.�6�"��<�-�F�������j����M+�b�y��)Ǹ����n�L��F�h��#x��Ry�|�u:��"�+%gs��8Bh7)4 g���y��g2u��� ܂�IPa��B`w,N���)�Yj�7�bLB-�fI�+ W���x>�+K���:�e�^,�9�y9�%\�d�EؕSY�a�&q�r�[��Rd|=�)R�`_E�܁P��64M�$�����U�w*۷��8�� g@�V|�!��+�ߚr=�lUcɱ�_��c5l _��]�<5�����h:�Y����h�&2��т����;{�?�ks�6�~�ތ;�|�%�n˖'vs��5�8�I����1 �hG���|$�Gnz�b��}b�͛t�B�~D8�Br�˅ܤ(` ��u��EJ|��5����=|��t2Qm�E?� �~\�~F�E���3��q#$Ƴ;�ї ��BP?�TםN�����eMP\��� /��)r <�Xr��, aI��4��e����G���T��>���6=HX��X1�u��GF9��@���.�ӄ�P�Jz�D������6] ��i�12�A��~�r?�/kF$M�d,��D��Ȓ,\�L�n&p�E�J8$r�Ҍ��@с�~Ǐ��]v:��Sx�L8�nPx +�r�L$� VޫTiQ�� K&�t'U��Q�������rD���J��$@�7,��lQV� ?aB�̗ne����4[�=C��&5@�2]�SsW�D���&%�|sl��Y1.1 C4PMi4j�� 욈 +��M���n�<�u�`!�<�;�9��4�Se?-���s$���&|���&6d�:9��(7 �� +G-��r!�^m��')�5F�[ +�M�x���nE1�z��L��tM1*���S� +HW��R��"&�_������>}�����pg�?�3ឝv�Nf�f'���+����DӢ���G�quu�ʹ�8:&�R&��9���&YQ"O9e�Q$����<ߪ�m�h)��y<��|ol<�F��*8�m�Q����#*����p��y/T�=S�#0�߇ K#�繖2�L�"Q�xE�b��[����j���<%\�G�J +�2Lj���N�Sx[�k���"��Է�$/B�Kb��؃�G4dDfk��@�w��׼BաG$������\���1�%���!v�S��m�Y֚a,��V��઒��-���=D:|��Cao�}�kF�J�����;VewGr�,���{�'���V<��2F��)z�J����u"$����F�K��M1� ��q�BBc�ĩ�B�~dq�-�I*�*�H�5�{ N"ʰiғ�p���~- + �J�q�Kg��$c��ɺp?6OT�O��,3��[�ƹ�v~��|nc*��N]-U��o�x���Ӆ� �mb��0���oFs(i�����������������ggg�n��\��?���`x� .�� /?]��:�<�\��0�`w�5��os=mmU�oŤ�BP=��F�{pcXkA�b�_'64AÅΑ4a��H܅�U�e���)�xI�\ЄY��g ��4[>��#Q�X�k"ֻ���Sl�)ܤ���꾷J��-��&�˸oUD^̯�x�ۧoe aɑ<5��`\���ި�)��V��W?1��K"$% �!c�܈�[Qn{�b�,�DH��.�6��?�N�r��r�%4n����nTSLn�'Pyp?��ё7�����&��xu(4�篏��Œ�8V��e�c������{c��?��Tn��aЀW�qr��V ļ9� +���M�\i��+6�-��P��;��|�Q��d������X��a�:U9��fހrp��2;���=m��yd-#Hn4o�� ��ɋ�H�4j���K��T����( �h!G�V�򑗰R�N%z��k�.Հh=_�f�]NW~%L8[��tm�TyV�B��.����I�#�?'��X�ϿfQ�z"�J��ÿ�{�P�t�e�pX90��s�eQ䘭<��/��>I߫�[��~p>��9�n�~ݍo9�`mNu�B�:N�a�i��5n�;m���3S�Һ�z2j<���Ë_#*Zn�΅� E&���Kܾ&�˛�����?h�:��v�����v��=i&��L��m�Y��_��X_Dp1ܷ�T�o���9 Ճ0�/�-�Z�T�����3�Z}����[R�v_py��M:��!��d����"�e׫q�����/�y4�n ��D25j�IFa��Ŷex)bq��*��0�]�x�R�[�Ms��B�*�tF;`��>��s:Wbj�-b�!�Vt���w�rtC���[u.,�x5J�TWo�[���c�p�'�-��v�]�=���IJ�4���0�4λǝ�{ѫԨ�]+m3>ʥ/�Su�z_�EH�ZE��^=R��>8�\;鶛Vh���4�;��J���bX��J����0+�O��-�~_T|�%2m����z����g�W��T�x��!�N�_�Qu����~/�Q�I�$�.r�Ci%&4r,����9�jݑi��H";���߷z�*�3���R�\7� v��ˈn�S��*�`mI����i8�#> �j�Ŭ�r���"ø�ˆ��n�`�"ckx��}�����p��ũ��E&���ؙT!eW��E3�/8[0V�6i>3h���#�hS���$d\�"��?�]�X�No`��`<b�%���s^!c��-%FO��>=OՑ��b�y ��E�nw o��5e�t��� �֩yFn���6��#ٚG ڟ0Z= ! �4�t�N��9z���קں��zP��~���o%��}9 &Xyo;k�^�t�����#�br��w�z�mǾ�;��!�jq(�F��T��I����|�|P�mP]kA |�_1��&$�5v�|��@)�>�ޞ�'��]$]�)�����S=I��A�ͧ�W��rTZ�+'��J�x�\��U� +?z6t� l�Q���>�Sy�Ij\�*`�.�w*���7�6Q�n��|���Kl���_h$��N�m�+���xO���w��z<�sQxO�Ɯ�J=(�zG���X��Ct.r��)�i?�>=<~�>NV���G�>Z��iF�{�>�be�DH��)�� q �1�Y@�R�f���z(b����C�4�����O�:6��Q�t9Rjf��Q�w/��� �R�Q6���*pi=��ῖѶ�,���6woM.V'���m�Ao1���+�!RwK��+Ɇ6%�J���8!�Y�ll�ؖg�i���ѦI"|�G�{���Mh�x�\�fg��>'�x[M�����n��s����"v�f)?ğ?8��a\`����]���� +���6������ܐ���O�s�T��d����s^�)�3�Nt���1C-�뽇��9��UPh�� ��פ.�+$�$�GǛ�������r1X�6VK� Z7����-6N-thEb� �Ė� +n�@k�D�� +���$����c�䂂��C+�k����?���Π�����K�`��r�ޡ���e�E��Q�"׳���]X�դx�]��Z�y�|���>�RYU'�'��o.0�7�L{=#9F���������)����I/�m�-�m��n�@��~�9p���J��D��T��@9EB���G]�ng�ШɻWkB{����~{�9TJ2��Q����9P�)|�o�� ��*��aK������ +��?=�D��:��n��Yt�I4��Ѽ8\_�4’ +����5$0���< o+t%X6�"�����B���`Ǵo���p���\$U��V���%�~�F��=k�Z��C`|I��Q氦��YA7Yf,�_�PIV����Ӡ]����.� ;�����E��H��F��w(�)B��*A�f�5ڷ�ݦ�l`�8�Z��ڴ��h��-����G� ��3a��h�(�t<>����������f�u0��8���s_���t�.� +��I8!m� ��m�P�ݸ��t:��}4���rb�x����a���?uQAn�0��s�A6���n�$H�E[ A.i`���E�&�%e�(�����ڊ�Q�ٙ�����=JV���E���;��~8�&� #<�:�҆�;��}9)F�<6P�fH�#ak|m���oO�Xs] C����ƍ&�} ��7�؜��+��� �(d��[����`���i�ް��Ș lk$&hg��� �n�����˶� SA�bbj�wŚ!�f +���!��������.Q�ı��T��ٴ�+$6���`[��V�*�v)�(hRJSX9VR�z���/M� ��tkM���D��Ȧ +Xn�Dſ� + X��#��I卥#��"j���-$��C��S��R�ѓ⋀E� ň�g��>�N�;g��V���mqS%Dɿ���&l����,c>�= c�'�Eq��h�f�*m��Ne�AI�q�b�n�%�Hf�i��"KQ��N�*���T�������7%�A��V̥D.��V'��T�~����;�:X��/������Dٲf�<43��ċ+i'M������e��_�������E�= �0�=��F��89t+ +N9��� G.����������R��\q�VŷW�(�.t:.s�){дr��D5)yp�G6�1�9J�*�`�c[��MJ&���$iDBnF{~���Eʱ +�0�=Oq���U�����SA����둻JE|w +]?��YG��X�ag�r�F':� \a�t���pG��y~�{n�C�p͙ +.�XYj�>x��8��;�Ƃ��4�H s�H��4}b=�&�jCi��;F�'�*���ۛ����zY��8F l����g3{l5F�b����J�E�ig2ѫ$� :�AM2Rw�2�k--ؿW�<����9�.ׁ�A�?f ���ѩfN�p|��ݿ�?}�Ao1���+�!�lTqm�ET���R4��Z8��UU�;�6�4|����o��M�#Z6���I���}�^׳j:�0�]o:�6!�(B��=�m���%��/+ ұ��V�X�;��L���"=��3��L��b���| g �İ� �#��_ :���[> �O��n��o +j�X{R(����&+�8X������ -� +����H�� +�U�q�Vٺ�3+�'%D�ŸTBϖS>d�9�p�J�}�3�tQ.J���^�(�R������k���7��JM�]N=��p�Se���N#�}.�8�6�3l��֨ ��O��3�Ԫ�!ܚD�{ w"��m��~�P���_kfk�"�3�g�ZٸU����&���|&a�l&���T�5gj)��Qi���L��� +M�D�\xBk����c�:��b�l�ڼL�=�*��bL�PH �DŽ +��_k��;��:M�s��w��Y󁋺Q8�j�t�/}��1���v�?N']���]=�m�vT�XS�7��fS��BeR��Q&�oQ#�'��{���Ӧ�1���S�q )�� �@�gs��`^�;�X**��+ha�O! Ƀ���_l�Ap>��v#[H��ծ����y ��KXn�^lv������_h��0�Oj*p�F_5i� ���;�*@�Zz�\N��&��s��۞\/mI��k����τ ��Nyt�>l���9��)}���XmS�6��_��p��ㅼp�\�L�2�%)�؛X�#�����{G��IJlG����g�]i��Q +!1�� +�i ��S�zp❶��[��"*`Bc* %\B2����dx�\Є�[�����9C8� ���%��ٸ���78&BR��W̐CW��(�$���� ٌQ�Du_2B�dq A�>q:�$BLd��I�gD҄B#s���o�?]\�\(Sy�2"���*|ƙ���@*TD��!HBT���HI�@��V!@�����>�� e�M��8�tN$�n��2k��\�U��/���y�Č���W�P�Z��� 2UXNjk�`��M6��Ǫ�!`�r��8����L$��oC"��O�qL�d,P���� aB�,��F{w� ��k�>���~��8����$��k螚���A��T���xie�dēW� ��c��JH�Y���4]��Z�3�pu�fA����U�DH}� ��CS��s�@����� �FJg6���p�gPM 2K޿k����u��k��X�����ɮ�z0)�LN�r�ǰ��9���:�)ʯ�m]/����� p\+�z��תa-�+q�U"ǎ֍��R�dF������ +�68����*��>֍��Fׁq�ĵ��}G�����X��<�Md���j�����=-5m�kP��X�� g��i8��~gg{{ �`o��kg��!��5�(���:r- y��%���D"������5��ok�����o+�KN�or��E����o�N�i�MN�O�66��c���9���ic�;k��1�z�-���������]-85G\yܔP ��n�qq�;�����eGmms�G4����֦^4�~mN�g>����W,����V�7�Wϡ� y� |�RR��~#�� %/[/ ���9?�Sw�7-���� �fE��n��WG ��L_��Fj�A�B�7�ds��m�u��s�dJ�(�E�pN�V���Vw̼񠗣TjץLz����7�3���y�meA �,�M=�B�t� +yo:#/_�v,���6Q�d� �!� �7�㘜�]b�*��1��� �|��Ψ�֦^FTn�z���lA��5���L[�{��C,<�8E��s��|P��\wsZ��w'���0��J�a���h\��*�ZN�Z5ό�S1�[�vM���A� q��$�� �Y�E�e�t��ް=P�:�����[�,��c��������Ag��xr6:��s�=z��ѫÔ��>'��,��]�����I�P�8��d�ɇb�Ҕv> +��D�$���npv���4��YZ�y57&�*��P����U]o�6}���<裎��&q7���i_,���+��Lr$7h��>P�e}ر� +0��x����Ë��%�H���F1j��G����;o���p3 K�I��mL�G|F����d[]��Pq�� ��2.��x5[�wC���gDF8��)*���W2�(d����Q����ޔ3c���,�!J������c���0�\#0 � � �� � ��׏ף��#*clbb`I4���3K ��d&cU�"U����' ԒP� t�jфh �����(¸�%J��o-���gO����J,5|�:�R(�a;�JQZ"9��=e:K�(����G�W����~ I��Y����Xn���B�+@�'ǃ7�>D$��vۥФ�C۪rr&x�>(��<�w^�[�Lo�p�eA �]�w? �����`�]����t}o��1��&P/ S 8.̭�� +�T���urЍ4?i��\(bw7�Tbm穗��r-&��z�Զ���9k�M�;��x#Q#TS�M��^�fw��vI�~&xC��� R9���r��7�P�wr���?��s͋�y�������V�L��i,���Ѭ�fΆ��T9d����u� +,8Sσ�N�mTo�Q:�ꐇ�J?����A/V���+��It���N[�Qͫ�eG9[����9I�V�ʽ���^��ծ�� �/u�:���ڮ�Ρ���~N�:���KK��h<�����kz�pU�m��L��Q��s�o�UM��0��WL�I(�l8Bӭ�^*u+�J\6(2fW�������|�*��yof��x�=Ň�H"ВJ0�|�+F .8���$D��<�%�V($���0h@����Sĥ�q�"A��������H�%�[nP�x��Ƃ�B� ɏH,�AƇBb�� +�-�}-6  ��� �$,�>�"��"C�Ǭ���i'��Q�%�*q�}�j�Pe�\e����w�����*y�'*��DH�U�*�4��\��W-�e�/�ފ��/9Ri��>��AZ���|U�yE�v�g�eMZ6�kܹ�;8��pf��ι��П3�"g��+i�<���1bۺO���[o�_���+X�/#�\1��w�TG�`�܇ȕ~=<�A�E�|0'��j ӋM�p��ϲ̖Ǥϓ�V5���چ'�Wv�́'AP*6����u�I����m��5�Ӓٹd.����#�G�YK��-oMc�v��E�W�V� �5d���ѱ��G]�g;��X���)4��[�alOk������ ��Wr���Ru�\P/�zQ�/���I%|�q@(ZW��zf���}4��ﵹ�7i�UD�Or4?ACt)�����y4��gow��WZM6R �*�s���}��jA ����!ۤ1��v�$�4PB!%�����ޡc͠�ؘ�w/��C0%s��Ӈ�ϯR�б �<ʦ����3�:�5�I� ~�>c��g$RC\�WOz�<�f������*��@��2����X_t|y�Gn)�'�.���cc���1>oX7[9a�x���ߣ�zƺ��A��7�tޱd��u�-��r��2c�y?�����{x����b�ɰ����|�b�aקּ�Tr,�.v\#�6B[Ή�4k(g��e۲�i`:�`�#ŕ���L�wd���³W2�6x�uW�j�d��l4 ��c<x}gu�/�C�w]�z����k9�����>����� H �S�����9l���zi��W_o�6ק�"N���I��s�M�� + �t���$GRq�"�} EQE;ي�M�u������쭨TX6Db�����C? +Tp���ͫ ^�mMli�@"5�-��D^�oׄ�-*�:˻��QһZC^�^2�ˆ�?QjgD2|���_Wx1�/�!JS��#�(�L��w��E�1�%ʻa,�mՆ���LK�i5�����K�5¶m(�����%2�@ٖ�є�9��B����r��V��_��zC�DÞ(���ta{�k��c���D(y��;o2Fv�)1��2�Z婿�T��o��1�G��eJ��?v��LiI(�7D*��m-��l�^�-+�b%$ez�̲�!JAo�lw��5%|�D�ih9��c�U�W��hN�o晙X��@:PN/�P�ǺACˋ��$5��s��(�^숸}�{-��))Y��yl��{�b\��T�C5��-8��ݑ:��iY�`��;����/���Xf#$��� �}���y��{!Zں�Lv�3���T���H����l4d��Y�����c#���OP]֐�z�Q�js6�ڸZ| �3���F����r�t��+^Y� C ��oWp��������PC� C`��S{�q,J߬4 � �>YL���,k\�VC7����p[42uԚ!��=staOO���P/���G�L�Bpi��mK?��9$���%G�>����uQ,��|5L� �3��K�0lY۷o,�z�-�H\�8Pk|'䴫�0���:b�U��� RU�嘞�<�8�BO��D�� m��Z]�yK�vE `m�LЋ�]c��̭}t�!rߧ�y��h�����]i �j��U6y����9�s!�Py2��� �G#1��Û0�fy9P;'&%F��B�+����x�����U���ױ��>2�;�� �?l�fgbn�[���I$����Y��;�����D�&��t|�K +oz�Ϙ����y'H���!kOV �B��(P��<�M�Q��`v�ZC��`n{0zʤ�M�?$C�������&0�^�~`��/����iT��&�� v� �h���]Y8+�x���E=vŁ+{v ����0�opI�)�?J�a��wG[k�X�?=��������Q����t �n� �}�N��^�M7V�����3�������)�œAo�@���s�]���&���VTjQ�rDB��8^��]͌I+�G�8N���؃�]�7������D��8d*D��������r�M�28�O���#�Y!�p� _�Ϸ�mM�� �ڋ�.�”��=��C��XN�=�-��IE�c���Z���:b8���Yl"���PxN�l��m�&)����l����*0hCPw΁@��YC^����6�1DG(�,�־��ˏ����: mPa����]T��j��б!0��>�i�%�h�0�y�u�мI��D�^�y��"������{���DQ��yk�u���%�m��� u� +i)?���bҎ=�V��}0]K^o1F�,�O[��_Z���l��q��g��YX�刎O��������=���R��0�a����z/�<6ȳ�4�ɮL �����uy��2���e@����H���w ��<�X6��������XKo�6��W������w��$N�h�H�&��]�4���H���E�{A��(Y�c'�.��y|���G��DB�# +m�̽y��a���to{p�P ��I������ķ����@N{.䳢�� +�Tq�3F�TF�QO���~��c��9ц�a� +�t�p"�B2l��zH �-ڌScI��"�������K��$��1�*�����F�|!TJ | �!���9����ŧ� +*��I��%�S]����$`�Ǵ�T���w�NRԒD����`��j�T� +��?��s��Q�rs�E�~���%�us���U��xd͆Hd%I�fͽ����^Entk_u�P"�vL���Dj�҉ΤT�5�P�)(0d��z#b��9ܢ���P���\�#Q5T�dw���� �RVQldsF#X)�N\R�A� ��{[�?��i�����a��j�>縹l�`r�A��5��ח�V8�v��!5��!��*�,En�й�# q|.�!������'�b����2ju��Yb:c� +\��4T�Wu�����gTa�G�)����}��a��X��E*��+v�&S���������4��^Q4ܾ�Uu�� 8.�4|*�ɱ��o0깏B����gT���\2���`4���>ƆE���Fc}����� �6��׉;���T(�pm���:M6������ +�:�vj�Ͳr�7 `��[nW�ި� G��:2��0�.��-�������������������o�~k��������9�:6��f���f�{�A+T�#�H�*��m3&*wϛ6����^ u���J���y�&�9z�ZI� � �����^�~�lv�B�|lj����i��:g��;k�v( +� �u�KKA ���+�"��]�ˊ *���dv�=馓q�ˬ/X�ԗJQ���4U�1���� +�q�; �{{x���Jb��Pu�w�E~�!���6�,��*�αwq^�q�H���aJU�l�34|��{^���������t�sI� ^p]����*>� m�z����w��\��RB� :�I"�1D�\{rɺ�����"���]/f����h�i�;r��Ј}��k�>6fy��s������P���&! �X� %i��j�Y}���a&!�Df�'�YN���"�Wgm�_H_�� ���>]�AKA ���+��J�xn�Z�XP�(Hv6� Ng�$k-���V�DŽ�%�ovQCE�>��Do/���8��h�&��x +Q��Ĉ�Jb(ɪ<�Q���8�ثR�_��ȏp)��H��XL1#�/ƪɆw�ؐ���7؈w�ј�FF� �9 J=[����f! �X�+%i��z�Y}���cf!�Df;��$q��H=�8�6��"H_���_�� �$q4g�-��t����u�OKA ���)�QEϭ��XP�(H:��g3�$k��V�P��KozZ���c��;�U�?�{a�1�v'�p/`��ZI 1���⾣:�O��Ҳ�A�����ʲs��]�We\$��n�RU>[l惆O��� 2R\��S�g�+�K�m���'�-vP�i��ի,��~�]� +���oБNY�!��ړK�}��d�W���w3���=��S�6�#NJ ���/n���cc��17���aP�� +E��o�`���R��.���go��fBLd�f=I�݂ߜ��퐾$%ß�>�u�OkA ���)�1 !�g�m��PC[ +io�"�j�"��A�� �߽�'`�QO�'=��c:΅�O����G# ���u���Tz��Hس��=�l��I>ŵ)㦐>��cI�|�9�8�=o�CH�'6,� \��qm�����H�G�3 +��j�l���/��!F?���t��dVg���F +�z�V���(���>�oW_�W�}1P`G�N��/sc^'ˌ\;~i�2)��2��Hir�Z�Hwm�id��S�6�Y�� ��V^w�`���f�� +ϒ�M�����e��JA E��w�"��ߎ(���$S���թ��v�ߥGGa\&��\���t �D�7̫D��†}�mN��V�:1��b(T��mG�2?^�J��;K�4��*ϝc#n�*�$��puÔ���|5�4|��{��������jqT�¹$^O�>����*>� m�z����O��\��RB�-:�I"�1D�\{rɺ�����*�X�.Og7����҆w�X����� �|4fy��s�?vv�R�V(�I�1�u�y���%&!�DfXqg9=���7gm�_җ�#k�[~����RMO�@��W�D�^HShP�BU�*B��8u�����^�qH>��3�ͼ}o���"B��ƞ(��;}�(p��q2�'Ї_ ��H V9�, _��K�)Gѣ�Y�OL�B�gS�����Y� {�.��GNp� #J��w���Xօi,"��px��,��;�ʓ�P0>�2-* ,�����B^9�Eh�vd� ��z1SF��wc�0�%�|��^:�Uޛ�ۙo�����e�_KA���Sԣ�(y�K⟜(h1�雭ug{��ޜ���^<�c��.j��c���?�K��#>.��Q�~ �ЧL$C��(=� �n��;����$`�^������8��8oJ\d�'67,�)�ֻ���c�s-�Iלذ��pV��R3�� ��QT��I��(D;Ģ��z����Ui��觜ߊ�tN�jDҾ�Q<=F�#~%n��ۛ������]�ql��%���6������E"������V%r�E��'�4r�Y��b3�/%N#��V�7|vjg��ƚ9; ����O� e�]KA E��W�G����MEAE�GA�٬�� ��U��.[�B}L87���Q� +��*o�W���� �ߞ��N�:1��b(T��mG�2?^�J��{+�,��*ϝc+n�*�4��puÌ���b=�5|��{^�������zq\�¹$�O�>����*>� m�z����O��\��RB�-:�I"�1D�\{rɺ�����*�\�.��7����ʆw�X����� ��|4fy��s�?v&A�g+y��4��w���<�\��b"3��y���6�/ ��7k�[~���]�QKBA���8�)b���%F�E`�B��sݥ�����T���4��9|3s����eX6� +_�o�C����鍪�~�>ޝ�>0� SQ�o��<�^(��E��4�}���2=<��x ?��`L%�d}����X�D=E֩4�>�r`Ɨ��ao1��^������:RlI`���b��W�Ij�a�d�h纊԰d2|�oTU&�N�"��2��]�N9Z���&n8��~W?�/u�OK$A ���)�QE�=ϸ:ʈ�"�Q�LUz:lu���e����<�嗗Ǜ�׾"q�����I�'�l8Ï�Y8= +8�C/�N2C ���t�멭��oR���$`�^���d�;�!M���77̩)_�?��?�q�k2R��� s�.j_�����%��@�{�� +҄Xԛ�G/�޳]��ݘ3�gЉ�Y�!ڕ6�K�c��d�g������jy{���vmxO�-���/N؊��1+c��X��s��J�����0c�ϔ%-�fX}��Naf!�Lf�-ˁ$/Rjl���8k�o C��3t��=;��7�pm���������N1���)N��M�z�ʏ��ԢJT�B�&�I�±]{6 ���7H����̜�g�g_}�Q�28����<{��Kg�����Uꈙ6 �)� ?K +���Y=�(�����A�KA�:� �qe�>r��3 +�/���i��=��h�����M�—��7�/��0_��{��jIR�-�������[� %cV���F+����̅�v�o�"c��n���^����iT���$�)��q��j-%$%]C���t���GO���fY�U�>�|� Q�p� ���̆Y� ňK�V �-3�w��XR@��d��>�% �(q �u���^ &�l�P)���F�iG�u�.xr�6V����M[�V������6��7�x�X����5�T�n��p���?�|?�5�S�̇�1��﹵Q�ؽ c'��N[~z����gΏ��C}x�k;�OK��jX�qk��f�b�'�>m򝶴�G������\��s�v:�ĩ rP��7J��#��18�qR��D[� �JJO�+�z0{R1֭�q,2OJ�c�(y-u���e�_r��V��5zϱ\j�X�Ғ�i�}n��5Lꀬ��<a0�b�Iut�=����iŏu~�]w}��L�Y�Y*����'B������Y������������W�˓u{�龇�({��5ʃ��ɞ���e��FڃY`e7Ō$S �*�v��~xe?�kXyo�$ivz6i~����lp�w�R�߂�-<�Z2�sB��O�!f��{;>D�H_�?}�Oo1���s� �5K(�*R[UJoU�wvת׶Ƴ���w��h��{����=���}��-2�����C^Ex�w�L�& +&�2 +c L��,� �Z!o����LAQ��ڵ/l�J`�ǰbG�h��$�sdG��p��i1�g�c�>RC �8�� +䃥K�#qY�s��IR@���N����l<�TEc-�SФ�F����k���%�C��ާ�����SB�Ӑ +�!7���F*�4������Og��j��_�T ־q�{KY{�1��UY2�(}�h�NA'y��l2��P, +ݚ:X��I�̍�骑��"�c�����R~�8E�^���h��=g�K��lIv�m#�V�]��^��F�y ����?���p�&�M�w�{��i�< ڬռ�!OweK&i؝����_#�$�.�������Ƽ'�g8���I(���u�q���J�a�z���wm���Ou��d��~��Oo1���)��B ���$(U�ڪRzK#d���������w��,�@J�fϛ�{~;��+j�� l�,d�1��w�٠�A~T&@i,� � ��W����WE�� 74ڙ�[6�J ��eB�����%�H1�t�?�8��.U�>cD�Q�_L}��y���;�U��N���$)(*@;6�(�C��c� +��ւ>Mjk4R@0T:��G=�U@X�4{_��o� մ!�ب� ;/,`c�I�Y#hW`�� #Uc�J�i�,�a.�J�SI�(�]�$t�0��U�}S{�5����Ȑ�n�T�'�O[�.f�Zlx{5�B��@�V��>>%Zx6k%W��av�7$gR�i�1�w��ǥ5��  �(G-�i܃W� ����q�N`|�'+�"�a���)��P�w>�ڙ�~��h��i8K��?F�Lg�����o����� ���"GGf$I�]I�ŵ�O�O���B��������Ao1�����C�F\IZB�"*A�T� )r��Y gl�lj*���v{Yy����o�:u �`�FY�;��S�����lb0���g�>|F���->vV���}KY� N�uLO�w�b��x#LX�_I4ca�i��� �N�H[��[�;*$X�a�L]��] +W$��e���Z����EV�ۢQr��6 +�#�%�3hU�3�seo�G�"��p�t<�{��{x��V�6�����h|��E �^;hm,�"��bC};3�vO9YG��͍)�<�L�}��?7��3V��@�:�Sdb�7��˃T �ՠ:N�V W5un~���h��Ӹl�wh �� 69���>��>=� 㞭~W���UX�7�ž�Y�g��~�;ˎ���i4~5��g���_��g��W��)����{6���A��0���s� vQ� KY�VEj�J�E�ę$V�O@���^9 t�,U}���{o�^������A�Q�J~��S�x7�'�I�V��6:�Gp9|-����g�:� w 4ڍ�'�E)0RcxdK�6hK���Uz��e���3�D���T�"�V��伡�pM\ThmO[[-Q +h3P� +�ǡ���1HI��ƀ��j��@�m�B��N��@p�tl�>m7O_��"����(p���ep�R�ĉW�"P.�n:��bE������IR����`jh�l�B����(� +��8���AE���r���ð~�Խ$�^Z�VLR�}��N1{3tAү��= 3�^���ײ�����|��V����x�OrAr~�ȿ�GΪ����Ե�.a^�߅�Oo�@���s���Z���R��*��4B���WYϮfg���w�ց��������{��㏾�P���������=����(�2����X�`x�n ?J�K��"�� 7�څ�{6�R����� an="K��b�Y~<�8���*�Q_0"�8_�|��y�M�yS)��6��$EhG�&��8�}v R"����_�&�5) Z;��G}�U@���s_����w� U�!�ة� �^X��H � .�FЮ�C;ÌT��+���FY�R��8էu$���v��d���L��V L�-VH^�Ɔ�?�TX,��d���U!@���Y��Ǒ'��'�ݷ�[����$��V �Uސ�Q��aHZC�S�;�w���5�_��v�����eN�wK��*��`������`�AiZ�NwT�N�d�*:��u�h[���h���9�s�?�D�n���t�mx9�#�U����.rtdF��jw܊z�p|�hI�[ޮ���y����Ak�@���mܘ^k;ubRjHK!=�x5���w��Y�P�ߋd)N���&��o�yZ|�uD�Ƒ�(�X�[}������M +L� �u �I�·�d~|!o+Nz]�e�!>��׊��V<�Α�ɢ ϫ]�~]�������<>sf�"�V����!xDz?��6{� +�%L�*v�5H��>�֌*;�"�����a}�@j��":��8Z>��6�����MTۆ֤8QBi�y�8Y��Mc)d1 J�ڙ��"�7/ +�(�K��� +~0�L�:��6kd�cӞOf-�IW�i^�y��r�w�wx�;g ����ۭ >�d��7��Qӗ�>m�Y7�USȻ��������Us��w�=�Y)����r1�,~ ��n�&���_����7m��n�0��z�=� n�^#;��*��(j#�ƚZYDi�X�r���^P�b�7-?�̎��lc�"��)u���+�a���fy2$0�e#�RHك��W�\��WԲ&����gc?Xn��`ƚ�I��C���5M��������5:/Q� +�0v���6��Ut >o�������=�u��]���0���J�8�����#��6�E/��U�����������碈R]�A;tPIw� +v�7�cc��TԷ3J4n�Yt�_�$�EX;/�|Y�^�����ٲX��r�w@���bO�j��$ +��b�R��`4��E��Lo�w[�-z�;�l��a��8K�V] §���������<�v��s.�*Y�=ۋ_�]���<^�oG@������ٚ�֟T?��K��n�<�Xe�Y]��s�i���J��jT�.ˉ�7lv�i�n�g�_�dc�������?�S�o�0�_qU�uPF��Vi�&��^&!ǹ��۳�04��>�%alyI��}ٞ|����Sώ$/yk��5�덓Q?�><�� �@�p ��/�p���g��@�W Խsc��V%C*{p�4­�;:�0N�,;�_�8�#f�3 1���?,�li�X���[t�Jh�� �8���9H��Q�8��vgp�P�@��nE�G ]W &�` +��&��s��狇�E����R0l����� s���11o����Og�hQ��Bb3�q�\}F����{����h�N��R �a�Q�`��=N�mQ����ꆐ)�P-#0,��n �Ӧ�� +�m���c|.� �o0��놵�@ 4t؉#/ݦV� /i�}+�W�98ݡ�K+�������"�3��{3��f���I��L�+�Ϫ��_۠hB$EvbH Z'���'ǡ!�� +��.�G ��0�c�&�F��IL1w´� �˚�ϱ��S ��:�u(,r�)�H��[���1�h;V�p\�'6 �Β�1��$���t1¿-�<�7Iv�B�s�HN �@��������ղ��mX� ���>{Q�C�V6��O��v��iv�N�7�*�*ڼ�i��]��.���������d��� J��R���g4g�s�����t�˨X�}b5鼍���?�I9ge���������-��z�h�vH���N��|�~u�Oo1���)ޡ�$*���?�FETTQ� )r����mfƉ*�$%�����{~Wos�ђNh�&�maO��x=�l��|�Y�q �";1��������ܑ�E�����$�� #?�;�����S\9�4[��-ݜ㑖N�]�*$��}a��L):oIVk�["[E�b �� /�%ѝ��I`=�+!���t`OQ �$kg��9r �� �v��x?���xWG iX� [�hY�hQ�-[��i*� >��Kg�D�&���a~�MS����D9��>OQMNJ��T������YXhM���������� f'8�Z�U" o��6't�,{t%� ? ���x����J]g5�W7G �O�ז��.VdG�����.dE�M�����]��NA �����x!I��hb�l�i\f�NWB��n�Ƕ_�?��&���}Cʽb*��m��� +��� x RPKÐ�LjH5��2�=R���]8�y�{�M0�|�����LH#Oק����!V��bB�ܲbRN�i�Sn���n���F�� +>ESY�����%�F�6 �Ўn�s, �u�-��8Dn� +�Kxw�{X�O�Ew�`�vTPI���vb�+�U��⣝�����󹿱s���f/�ي�����_}�Mo�0 ���<��i�]�4KtX�m�ݲ``d�&KE'���A��:�o_>|Ly�ї2R�AX+�-{O��0M&�F��rmt�,�r�^"/ݯ�huNA�h� ����R`���Ȗ�ɠ�C,�Ȗ���]F�1���h��jb����ܗ��7t|".*��,[[-1 +h3P� +�M-�C���1HI��ƀ:�ƴъl �6w\�hg�� a �j�5}_���o/��lCJ�a�L�ve��R�čW�"P.�n;��bE������IR������Pڼ.���cQ0(�q^[A�x�$���B�7(t�+o�"+�0�V�?�o���̢�2�N�jG�`G��%�%���[��X���1{�o�����i������0�=�I���Ng����ĵ�����N��&��l��̙�f{��'W� +�^8 ���̸� u�+^'���V#O[����+��Ђ�pi}���ˡ�vﷴBG���?��Mo1���+���|��*R[UMoi��w��k��1U��7�,%�����3~����Q�� LZ��`�ۃ��ɠ�J +P�A�^��+�G�x�~S� + r�A��:�aZ�-݆Ol����`���d��_�8��-.TR�`D�a�=L|��y���k�e��=�FK���l�YaZDq��>;)�h 藠ImH� d Ǖr� ޠ ++�u��u6��~{�P�6�Tk ���9�IJ����"k�r�n��YUa�J���Y�L��8Է"Z���v�&I�Ӂ�`��Q� Vh%�� �J�'���ue�L�Q!@�u�����,��Ԟ+�M��}⦪gZ)A���jd��d�H�]�z����}\үS���� ��N��wmo�s�~�7n�a�/( lh�u���4�k�y��V��c���΀V�P�Y8gF�l�p���o��|��$����L�rtdF+��X��̀w>�g�,����{y����t_��m�O��0���s�DtQ���nY�V�{��X5v�lCW߽r��[^~��7|��I-�:γ�>��59 �%�4A�E�J� ʡ�aK��O���¨���Oа϶~g��<:��1“�7�w64Z�� z�aN+��(c���Q]�dkM���z#��b�Q>����ƳZoٵ�^,�W�2h y +i�$GP���^Y�C�I8�VѮ�7�>O~�'Q�i�W�c' +�^T`�|s6�$H[P�N?1bC����˒$���2�-&���x6�:^L���,k~��Ș-�{�|+8K��sX����i��V0b*���q\�� +O� ���0 ++��Y6ϛ�ﴗ#�m�㹋�}z$�Ӓ�Խ ��Nõ�\`�ak�������N���l��� �R�4=�)F�b�s��m̴d=�#��-򱉓��\.���U�EU�s~�f�ޭ7�b8���e��4Aahw+kvB�������Ok1���)ޡ;� ��n'li� ��� +aV;��JB3�J�{����%������[L6�g�)�B4;���X����1� �3� QV�_-������X���N{�cv�U,������*XS|����=����;u� g�e�H6qL����ljB8Ж�JA���A���,3ۧ���1�a^@��;�A. 1O�.�$�$����;ߗ�����Q�5ԒbK���s��:�к�Ē �Ğ�uΚ@K"Ç����x������㪛��3�3��I1�LH���P��^��w�i � �-;/�E�*&�í0�.{�Mb������|���9i�Ւg�S#�L��+����#Y�=� �8و�s�P�$-�ѽ��E`�0���FG:�@���C���T�5�WŸD���o�ŏ��(�Mâ7l��O�{�zl�Eؘ��&�JO�t.�5i��N�:�W�m�Xd�iMl�3�� �����j[�04�MC!�I���KȚ0�6o�ɑ������+��N�@uG��n�W���}��jA���uXk�C��b; +1$&`a4[�2�3L�F��V��C�4�|U�]���e4����J�������x_ϫ�� +�x邢 ���!��޹�~|sZ��T8����%l;����T�XF'�XL�pE����7 ��̍S N�� :5r��r�9�d��N�$؈�I��J� �����9XG�C��EG:OQ"H�J�,$�B�tJ��t_W���huH�:g�9E�8� v�:ؘ���x§�o��V�zjv���ͫ�G��� M��#{����(&n��� �<�A��2��U�s�D�����*�ȣ�h4V� E ������ލ�^ߍc��BY�\�م�.ꓢ����k���Ak1����P�҄^k7Mb\hC!%�BkgwE�#�� %��ȱ[0�R�4|o��-��1�c��L�o��)��������_Ǡ�CdE�bH=��Tnӷ�$�g�s��J���a4���E7��S,�_m��/�p�R $�ȕ �z\�1sʑ��.�D"Gl�` I��J�TKE�nR������G��1xe�S��B�3�Ȥ����]���j}w�n�vm�H�-)��/���6�k�i��3|�x�΅�X3y>�oᜏ��U�r[G�X ��X:Ł<�9�u�G_�7i lw4�l��I�=�^a�E�ٽ�خm��@���ٝ�������.��߉Ǘ��+��7e��n1 ��z�9�� ���n�f�"ڢ@z �Z�W��H�nP�݋����I�>G��+CAG!y����`��VH�7��]_8\��}L��(^ �����S~��9��v�e�\�$��,4�"LxH���b��~u�_ut{�gZy���TI��C� �rIt +>��G�|�V�6���!d6��jYt��k�@�kJG��b VB�>��-f�DI��'�f;���]�x^NR�4l���WtQw���&���\%B�h�εc?��4��s!yU�y,��m�h$�6�Da2�kĝ����RW)��w�PE�m�|:S�OL%dUL�3jv���0���횬� ��5���ݽ�����j�@��z��P�҄^k'Ml\hC!!�B�F����;�kJ޽�#��P����|���O� ��8�\&���O�N���j^��8�}g�6!PT��;�7��7�pҳ{v��.ڶS���u�ґ�� ��W��~V��)�xCI- ����4�B�����cۓ�;�Ռ�����n�1�n�}�v�fp� �ig KbXi|�I��Sǔ������7����:�ڧ�)��P����kl�vМX�C4 �k�9/�zN� �7/ +�(%��(YIk�=��+K�0�S�Oa�8k� b�3Z�[깬>"+I;r�D�! +�e���-뵎��.gYnV�� O���?pL���V<>7���~�����>�c�*c�y�q��V�� �<@���Zu�����w�xj�U���T�}��j1������ݐ&��ib��@ +�Pd��%���lS��e׻.1Iu��o4�~Ϳ$�P� F��*��/�%���Oլ�8-p��g4>|F2�� �;#���a�P����i'~���p#LXÿI4cn��z=��k�:�#�MVoߨ#��gQ��W�˱K���1���MK9K��͊���W�Zb=+q�1�S�O�[o�tl{ilHo:uQF,��%^U�1���+�%��0������ׇ���6�K�C-ċ݃i�<1C�Iu��ٰ})���g?N�}_W��<���ݤ6~�[n/�_�T�n�0 }�WpX��iz��v]� +�v�Z`i(2 U$���C�}��ĩӋ�,^)����A�R ��3)�^9�p +��^t܉�w��)��<8A 6�߹�K{-����Qe����Y����A���< ��� �����(ų.��TxV��O,���׆s�;�Nc;�i6ƴb �8��0)Hk�Դ`K����p��Z�� �ZI4A���\��� N�� ��2��r0���R ��RxH��ja +K�9pP�ۂ$��)��GF��;!��_/� +�����P� ������ᒑ[�n� 2w+��փ\�V��02t�k��m�<�Zx߲{G�p֋��N��N �C5w)���W�� ۳М�z&!Jl�]�5V�/�������%�h0�ӑZF�36-Ws4�E�Ӕ�hg� +e�XWL��M�����3��-��*�+����&zx���S8鵝j�,q��qR�=U>�^�ظ�m�k���W1��q�V��S>8xƤ 򀫀tl �$�Fzg!�J��Z�.����k{9����Ke���mF�"�(��~�J�z�l����/i�Zǭj�njmo����Qf �̡�&�J���pT���xF�ͶZiLJ�9�%\���D>❬p��D��%>�R�.�,��'��m~4U I�����j�Z3���i�|��TK��]�MkA ���+�C!v���n>qi ��� +E����� #M�P���8v�M���.��ѱ�Tx�V�����Y��� w~�p��1(�A��R�#����;I�Y��a�ޥ�V�0f~��"��H�)�T��������<�� �q傥��y̜r�C��0��[%XCA��'���Rѝ��T`#��1��mt �EA�T&���92)�%�f��p�z|Z�S�4l$Æ]Џ_�al���4��>u�K�� M��<�p�GR���V�'�K��3��_��c�������&��/�ld�q� +[-�O���r`����O��gG��h��.��w�e�OO#1 �������XWZX���e�G$�f<�hS'��-��W�Bŧ��g�M~�������HMb�W{/���y3vg��x��ŋ!wx���ϱ#�S�5;��]�7�B�_„���/�)&^����iKW'x��W��qG��5�K_(�D�� �b�����[��&q^-�nw��� ]M a��@����˲�3��$�J�i�~��~:��4��iX� +�h�nfQ�U�6$��J ���6�3�~IZ|����΅�U1{�Y�%�MsJ�U@oF�*�+J���U�tC!�Qs��o�%dUL�/�h/u4���Ղl�1�Q�k����t���MkA ���+�C!vHz��|��RH����ݡ��A�� %���k'`�s��G�û�,}A�!���\c�G.l���Euv\���hhcbDC!u�_{һ��3Il���–]����0ǵ +�&��duÒT����O�8��dI��GV,m\��p.���n �v�� +�!�k�G�j;�۬�ю)!��Nt���Qڬy�r�����+�f;w�ZyXO��mxO� �hoq�M�>5fy����];g���V(�a�� +�̰~r�Y։OAcؓ��we�S hG �4:�۬��9&%�v���}T��I��E�~�;��F��Y��-�R�k�*�<։��Hx���R���Kk[1���gQ����M������vU(cݹ���#1�5%��ȱS0tS�4|s�q4��nj�}$�I1 �~�&s�{�������1�PЇ��Ԑz|Ioӏ�$��bg[�&卆a4L�W*��H��jsR����~��� �yI� >qeż��y̜r�C�uX��[%XCA��'1 �jI���cR���k�庶���F�>�,$9A�L��x�ݻ��Y|�_��m6�aM](/oq�u��+��g���s'����a3�|�Rp�b���b1�/c� +��~��@��<�*�)c`{�d�Lߡɰ��Q���7���b`����w��'G�㏦��³�W�7��_5�Mv�VMo�6��WLYH +�8�ڱ�&�EH�b�{(`���"J�*9Jbt�� J�-Ɏ�]�'k4��oFs�K�� ��`d�NsZha�8�p���� +� ,���Ș����gJ�h�,���F+#�A�c��(�k���h��%3 +���Y��<��YL�oX��K����@]H�;^�Y�L��o�9W`*��(I�}�(CHK)���:o)8*� T�M�Hh5�B"�O����/7���[��b�2F��,$�ֵ0�gA�c���p��� �r����o�E����'���í���cf�3[H��Rq�.��́�/�� Td{;��U��K.�\"3s4F�~��p���/-΅"4��ݎ�0BQ:.��А�P�E��������f0�0#*�h8,2f΄��r��Ë��pT�Ó�zb�ͭ��*�O���{w_�#�[\�����cǜ��x�l�)DG��h�\܏ܭ���\�/ � ���g/ +.���~� R]�$�v"����5X�5H�Q`Q���;�Cu�n�-=E;j�����+4|�䲶�'��0��=�Ȕ�:�=G�xst2��m85�����f�V���t*5K���E-�=����m����:v��3�D�y��3�:�����eK���\��*qj�M�O��d���,j٪��S�K�9�{�� �V�|^� Sr�� _��u�F���- �hc��A>� +lj�ji��b[�kK��� e�����L-8����j�Q�H<�׫�1 +�� ���.F�����Bً<���_�Rls�3�Fُ;��qc̘�.U";Z�m\!U������-]��CT���K7�pQ'�өD�� &� \���6��e7c��C [c|��~���x[];�'-�v���w�#�ϴfMꌫ�׍iΤ�Z ��� !|���z-���ʣu?3UÙ&`�Wh�h���y���d7_L���z �,�m���#�e�I:�yt��=Z�Su�B���9��>Zȅ�B-����&��k���oO�0���S�I�?��ut������\��Xs�ȾP���>9u�4�&�w���y�s{�!Os��Kf0�d�{��hag�8v��]*,$B" 93:��)3���-S"AK���+�o�X�!���(��d�'�pΌ‹U��8��W̒` +>c��mup��9�\b;p�f�1�Z���B���VdĪ m���I�!)�^u�RpTA�D���Ъ�Dfnʼ���b9w�J�2� � ���6�R G���p�c�t��bڜql�Aa>~��K�PѸ�/t�7����Pܹ��(��Z� +�t���J +�`X��7�%L���v4�)3����f>x68팃2{����3 [�#��F<0B8��nS�vy_J��S�(ReEަ[�Z�o`�(�����ф܁�5�H���c��M�Zñ���o�H�£}��4e�.�R��ܢ�� (ܴi�9��L�����[�ZN,(M`�@+�(��N����� W���3�`DG{'Ѹ�z�n�¨��M��k}��j�Jkـ���3-z�p\�BƳ�#P�����zc2�ؘ�Ï��v��ckWf� -�d4*_Yo_�?�Yx���V"BYb��Nn�s�:�z� � +��\��4��i�� ��P��=Ӂ���Ђ�,ߟJTkJa2�������]���^}q%������ð{�G�e��)� ���j1������uHz��4�qi� ��� +E�ήD�#���mJ޽�z퀛ҹ��f�O3��\BE6�L�h�V�.��>Lg��Y�3<:/�} xA2Yk�;�o��}M�v�.��)J;�Mf�"�EYs���ׇ����s<�ڈz��Je���p�\���� �Mk�O؎��( W��5�u�1���%f�#�]�Gў� �ssk�G>G +d���i3�}�]��V����bc��}U�xu��b�l 6V4^�`Ӓ$c��~������ޥU��XA[%������ԭ���;��.�'��#�ӏ腸Ѿ2i��z��W 鍎�O&tTN��Ó�l�y)�LqFVۃ��I��?s����*'����nHOV��������6o������P&^��LK�ّ=:�]�Oo1�����ݪ��J�R��"�#r����wl<����M�2�����~�>�#m�F��?t�Ip���\����A"!�-��c�m�O�,��D/ v�<�m k�h\�υ �h�'Lma�]�ˎn.�D++,�*L�и�>SʑN���`�O��AG�;��Zªj*���%�'�5F��Б�� !p��`5$�@�d��h���z?_<>-�S;7�[�� +� ooQ�MP�T�#���ޝ+�v ��ѩc\�"�F�j($�H��~+q'8�����bp�+�Q3֤˻�iڏX��������ys��n����5�܇�Ͷ�v��,�|��v�K_ͫ� U�]KBA���W��*�t��u�"�b�3�]Zw��9~��8������<�3��.�f�pO�x�z�,�7�� xw^�����LE�,�����DT)��t����D�c���ES*�g��<��n�oH�S� �\0�K0�.sʁ;k���M���Y�� +�1�6�{Go9 +��&�-�Oq�����?��.���jޝ:SG�= j/.����]yIm� ��K屉�e�dKGe�֧�L�i�:ZW���9r�P��|Љ16����I��s�A9ւk����uT�O�0~�_q�:�Teh�[�6!��$d�Ks�k{�KM���$uC�����w?}���J�F�Yiy��|���g'� �p_CE�� +��ۚ6�!��p�%Ъ�u��E�b���A�T�x���?�x1�%>*R�a�θ?���G� F ([�vV=6�w��.��Uc �A=� i��@�ra�����7�aC����,�~.���60����b(��ZX�I �g���]ه|�Y�F�J#��*ܸ_mF�Y�p��� �Y��Y��b��[{�n���Ⳡ-n,�2�?���L�p�Qa`-1lHc���>�F �D��ϳ�Я�yC�x��}�hH�\x�β�FK����p��Ut ~go`��cw�.�H{���������i��X�;dg6��OSN�K4���p��"�.�mVT@+��i6�p�x;�ۙ-��I��QB��@G�6nح�~q�z�����̋S�8*��&�� +�+��ŗho+�B�6/���>0/����r��!?��EE�\([R�9��D�0�d0NV^�&�g�5��*�ߥ��Ҷ��λ�ˏ�ނ� ��f��� �K dWyQc�q�7�l�a��t7��*{m��fo�4�kZ���t{1�K�ʬ���> 3�p�5U��P�`�_<�����KQ�k��QF�Ђw�'���84XR���3ֽI�m·�Jػ�(f ØA�`��U�=L۱�������uRMoA ���)��W ��)j#�CHh�����Όl/U���,�M��?���<y��U�m2Z�r��:�d�M� /�aȌE0 A����97;�'P!f>�ls����r_�r�H�0V�p���~��,q�X�r�K$s��<�#�KA{'d6�x���GO 9BVZ ����ht�`\�Pb��A��agp_�=-f��ylU �\ �Cj�8 S��A�x�%i��Z� q�@J#<�~Umd�$%7�Gcqy`�b��j�t�D3�%���f���f�(�Kg�ef �GХ Ǣ��N�M�.Lw�����L �S���{z5n{A�/7�h�J���`��ޱP��]�Y��b���F��V[�U��V��ϸ���@'�³sF�(ڢ4��V� Z�a�\wV�Nf���T V�N��K���w䯉�b�5�[�z�'�Ӏ��~ �}�[���3iw�P���$wk��$^{�N����ww�z�[��TKo7�ﯘ*�+H1z���]�N\����Asg�lV�ffV�P���>�zH��� ��}���ߪ�� u�c2Z�eW!�If��8�1|* CnJ�P)p9<f�o"/�ڑY�N��,�沈����J�����&�tK|Q,FYx�5\q'��� +]U��f��2/�8�6��#�!��tݿ.�F����J���JT��1� v������U&��*��p 3�)@2pE�J> �ۯ+/n��v�J��Ъ^��#m��Δ�ܱ�:}��_Hl�����,���E�4�"]*fX�u�i��1���r<��"X��ްxp^W��(A�^7�U�R�G���C�ZK<�44Mڐ~�<��t���Q��5�l,�pM(5Y��_jd�̳��Ԫ/�&��-��L�9�X��n)�$ɯ���n�h�s����l/��0�[��qҒ�W��. n��P<C��i�F��3��>����F��vG���*��f��gX����w�� + ��#;��%��4����i��=�8��S��45��u��ڜz���L�dG�_A+�?ӷ��d��2��#�h�m�D�ܵT_��:���49�?�<���հp���IAn���ԉO��+�XV�Mn0k��� �C�B���i��T�R��U�$�0�l�;���������=�)���$��YA+�&�B�mH��LS��[�;R+OD|њy4y+<���C�8 �@��{�nuk?�?���{�'�c{����c`�����Bן���G_��}Q�n�0��+搃l�1z�u�H�� ���M�"�I�+�A�/(K��$卻3��\ᐑ��S�+�[~q�F�d6N0�c�r� *� ϰ9� U�u����ū炑�>{C����M��Z��uF� h'+apKy,B7X�‘u�"�d�ְW�����7��!���<�G�V�L (�[_ +V�L�4�@������͏���T� �؋�L��e�+.�1|���i�.�,1����$���٧��y�H-B�W�E�yc�2�R��T�ဍVO��o���Zx�"�5�v�wÈ� +����5��S:��ڪ�´ʑ�^�� +���NG�)�M:]�T���'��9'9$�����}H�m�Ok1���)�!�Ibz���6���Rz +�Y�l$*KbF�&�|�"�I]�4����iq�lB�Ɠ�D�8���9�^����lZa��)z�N�H2b�{�F���s41=�{�S��GO�KV,H��������%͎������d��A���!�k�E��Q�-���y��w��2\�l(�.�<�2F����׻����m��˖2����Y�a�E.�5b&v�ʳ*І5�a�[������yUO���= >7q���51�� �+�M�X�$h�{��� �$-�)3Όwi^핡�Π�)c��&�2�<9 :٬�r�J���_��B���m�%�ɢ.��{R�G�?�΃��)�KKz�:�!����R��V�n�6��W̡E�Z'E�C(�41���S�b�4�S�3ܵ���^ E�jk��K�<>μy�(nشX�����;����ָ����^W����pkB{��w�UQ�O��_� +`S������  T��z�J{h�6`%FxVcc��W���:�x>Cy*�@m@� �[OC�^N���2�1X�BcBM{��ԬD�LHWEѡ2�O�A:ˠ�@Q�(`| �`E�wؒ_�th!��$�Y�e�]j���/�`�5� E_����Շ�?�m>��ۻ�� +y��;ų��8�e)&�(����^'��cpp���^��2�+ �2��6)CgԖ%�m�� ��U���6�0��.$��ܱ����}g �V����h'·@_��3]w����)Г��][�����A�Z9t�9[�C7�3�R�`.�c�*�,�\٣  + ���`��U4�OU� ���>�8�Z�Y5�c﬿�P�BMc�,�SGFA�90�)ō���{t#�+ڀ ����Y�-��CQ7e)�����^y�L]zQѩ ����R�t�N�6D����G^��u�ō`u[Q���1���Ϝkx�Ӌ���h�o1����@����ȷ +�oϲ� +��t�p�_<|�!`c΀q0���#$[;�TC���TRF϶�Xo~��Gc4$}&����1��g#̋�/L~��V�����@-�c�"��>�:Ġ� ߢCA�>�'�����)\���i���!�ޔ�(�����?��u��f�v�D��T� �^zF�e�Q��S��C����3 �F��ao��-B"� ]3Y|�s��@rN%��;�d��uʻ��&J7��M@M5�o��7��a�vg�w�C���E^���N�TrQ���vv�NOs;sx����Z�1�v���!�_�RJ>!�f���`j�!�TXҡ ��g0U 洃����Ɣ`�s�A�E[Q?y��I͞c������Sck�m����Jx�_���0��\,j�Jz���t����;�&�Ӷ��Z����Y�wߘ������֝����,P�{h�h�Z��sJ%���0*@dG�q�Fl`�SS��X��� �N���U?� xZ����-�t��t~�T�Y�����^^]�s�"��,G�ŋ*W_�Mo�Yj���c|�U3M���Ѳ��mR�n�0��+�� /�^��u�]��GM�-"I�#�A�/(�Qy�̼m��GW:$���Jr�/���}6J��]�.U�Fi� +p�3��R�h����[��նd�2�'o��0��9`,����t4���"�T��8� +3W:�NS�0�5�պf��Q��������<��i�$�@Pfc}%XYӃ�$a�h��=�?��� ƥ`�E@��A� +���>��K���)�01����$,K�����(I�}��|K��d8�n��}��i!#TjH�����FUNS��k�:��&0�v1� ���)oM���b�y�L��Ko���"�Ϛ]�F٦|�z�����c�����^K_k��ُt��w<�q����7fߌ8���a����'�!Y3���*����t��w� �7�y*���m��y��wz�N;^�%n������4kC[_&��/���t�%i7��Nv��0��ʬ�ݹ��V��E�AO[A ���+��8'P�(���*�T9�~�ž�j�G@���)G�����rC�X��y�����]_�.��Y��) 14������J�[���8����+�[���� K�ow��"��9�yG�B�y⎥�ۖk+<A5!j�.�ɵۿ�;����R����"��1��Gr�z�V���"�?��ج�?����gr�ɐ��fq�^<�gxөGF�tD� �F�F����7�xhdB,d�M5�RV:�T�J� O���m~��\���S�����S��M�"q����M�iwNۮ�Q �q�����Oo�@���s�d�L�^�iQڢVm�T� 뱽ʲ����(߽��@�?s����޼��U���C��([�w��$�����$��VY��:+�Bו}��Zb�mY)�L޲'x��������b�}��8�Z��E�&���������9��������|�"(j��t� ��!/����6� �#�K���������U3�]L+TX�@ne�E9��V���j6&仕��K����B��Y��0Iji|y�(��wȌ�a����M��} O�n�ң��8��E�k=*�\`��� � g͋"ӏ�2�wt�椾�W!6?���m_6Ť5���m� nSE.I� R�ZtP�rpOkI�(���S��� J���sR�~�a忶#��?�V�X��k4�S���C�i��6�?"7�9dK�~;�\�(M �Ϙ��c��p�� CC���%]�ZH�+�($��2e �B��f4@�(_ +M�C(�{����xry51�2��hx +B�6�0��W���J�2@D�u���I�*!�tE乸�"2�tRe(��0�:�4��^���_�ߧ�����2�1 �rm}�s��L<�����\%��L�ꏔ��Zi�o?P��r]#�l�3z;qLx�%1���{��[���� +~{������# ��I8�N��3��D*�N��RpΕ&��Ό_�����=�`���4x��D�{����ѰS��"�g�r�6cR;��'��J�c�����6#2B] w!��lH�N�\X�. �L�ϳ�i����<Րt��_�Z�[��tU/�n�-�x���9|�隢;<)A��3*L�u��6nc� KU 6����j��0?�o���^I񠠥6�d���șj �@�t�/��n�� ������G�N%w��$B��{O�*=��Z�xS�;Z����4����t�V2W�i%��*M!5���e3�y$���)*�1���)Lk��@J�$-p�u���LF��jlf���h4�׻�Pg�>J׻�^�V�njn P������, gb��8��U��k���"j���4Ɖ�D�{�5.� �,0P]sE#^ �s�f�I&�0�%����]���R'���iE��D����n�%�K!��"�꽵���(�5o�D�2S�ƴ�M���"?�|x�$�PW�S�*��>���8>ԣ��(�������K�ҳ]�7��5z��b,���3���}�nc�*7&��,�?~4��jsXc�]�Y4Q�t�]%R�V��� +�F��DFY͖�=�p����R��!t)����/%DrYO*�Է��̉k���%� +Y���%�-���u��y2��ۉx��OoL8���R���ϳ�� +��^��%�}v� *y5�B�@0��JYl��f�Ѣkl?mڎɨz����v^����;\ �N��z~��\iIydվ��G@�Ёl�ʔ�k��r9�zG7����Q�_���m�u%vE��2�g���n�^ݎ��w��㐧nv۶�7��۶�ub�sZ�����$��6�͝+�ڢ�3!\s��}���XB�6 C�Gc�����U��7n;������^0me���b���m�y�]�����Xه�"�`�oNY��[Iu�8֦K��]9k���ٕۙȱbY�W\���~W�ջՓf�z��䓈5�Iu�q��=7���*7Cχ/�O'��;�1�v�N�(�O�k|�n? g��?v��VMo�0 ��Wp@��E�nצ��e-V�(��v(P�2 �%M��C�� Kv��K�#������H9���Ov��,>$���8�cxș��qf@mAfp��-����XI��l�[�i�Z |�D�Dm �-��}��rk|&�2"� ���ܴ*W(GgD�@���=�Vj���l������;k�( +��D&uA,�b�#1[�����fuu��rP1� ;b e���)���:�F��"P���O#A +4�P�����XUdE�鎮���X,?3��J���b�~:�yQN��a,�|%���b��X��V�=��"���c��8�����e댔f[b�hu<�&]ר���ȉyס�W�U���:b_�H�E��،�*��ȫr�|Ms����3 +Y)�S�����ڸ2p�dQ�*�B�6�pow���m���hTI��{�\Ý,���CX���~��0*p&X#�r�m�}"���eZ����'g��,��A�̓��Cm����I����I�y�"S����������G��$��C����.� �іZ�[$��i�>����K����6���J�O�p�t�P�)���Gl溾��?4C5�������3�ToR�e u��B�:��q�q+�OL��6�[C��R'�Lj���-�o°��VX��K�g0�sFl�%�a�=݃vNΠ�>��Vi�7'�V�:v.�V��5>�۳u�t�t_nqG�+�K���AO�0���O�CRؽR��V� �H�X �΄X8�eOZ*���rHۤ�Jln���͌s�Õ9I-<������+G��{6J. x.U@�4A8���S�t��QL�[y�Z2R����O-�y����|s>�i<Ĕ�"���&��� ܸґu����ְW���/��%����ܺG�V�L (SX_ V� �4�@X(Z6y�����],�4ƥ`,E@�§�X*.���`k/ �曖/#* +NH�S)���5%I"����ֆG�X��GI"�*��VUDW��T�ဉV�M�#���7 �1m`BL���y�L8 ��Q��4m�Э`?/����Շi� � ��k%w]��Hk�Zr���2{T=�a׸u��Q��4.�l�)\� `O�-����ğ�m�z�k<��wL�$k�4��ª� �X�j%§�!�����U�Y;���i��^� c|���l����9=��fD�@S�ey3vi�+[{�U`�.g�$+�2[�>�N�N���<��\Yx +V/(���X�?ڬ���-o�ث%�+��D�{�r���,����,���m�GQ���6��.m��~�6�N�UPMO[A �ﯘ[�@�6�mQ����T���/�겻��Q���5��O�x��x񡆊�}"ᩚDo�mWY/��������!*��QQI ��}��|�02���$n�a�g��̸I����bA��j}��:�<��I-R�gX��pUC�R7"(w�%���`Et/��,0�!%��zc��9+#��Y,�51)�9�v��z����p�V��,�aK�.�-`ͼ�A<×�`��ezb����䮬Ə̝�-�Z���ę�Ҳd��;���[��a��q�o����J1���~Ⱦ�†�K.����R��� ��'Z� ?�@��٠\\b��c��|L_ݫ��T]o�0 |��������^�&m�uX�}a�֢Pd:�H�H'���A��v6L/���㑢|q� +J-<&�^I~�_i�:�F�FpW(�\iE��g�9|-��"� �~y�.�µ7o�0?�3����V��Y��1,q%��0�K�pA���+Z�1A� �5�ժd�I��z�!/�y��ZI4��Ln�F��f N� ���]��x����� RUa\�� �չ0���8O��A�l_�yd�� �����W�FQI���!3���p>��]��B���5d5މ�L�|Rk/v� ���ӸA�5����G#�� +�#-���:� ��V0‰����%5�~)ٕ< �*�'Թ�5K�[%q@���@� #�R����<�Mՙ�ډk7 ��/WZɶ����b_JNj ����͸��5��R����iHu�6W�I��ɼ>�v����}S:l[�T;{���Ӱ}��� �P��I��Ve�*���dN��-YpII�-��qpH!��J&�5�wB� J��K�/���!���1�W.��i3H��[�B�t� ���Wp(���̥0� +>8��� �l�sZaO�g@M�O�̍�����B����f�_S�E(|+�)�-��{��E�}���ǿ���v�\#wn+I���<����&�W�w����%-�+�N��x�қV�>if?�׼��\k��c^nMn��s�ħ�N +�EX!Ȭ��x�f���W���K�)�7�i����m�Ok1���)����Ibz����@��c��jg-QY�Y�P�݋�vhuͼ�==-?$�б����V��ݻ颚�*���:A�<� eE��h݁o+��&���vVQ�)>����)�⬂%����r��xu� �$�(� ���Kc�l�<!(t01hv��1���s�P���a^݋�;�A.�1�I] �H�I��q��Cs�}s_Pc0��8��sr��G�Z�K�a��]"ϫ@{�D��h)?ħ�GUe<����eRn�~O�kb���+��fX(����G*/�B,��݁�11ޝG��4Z� �!���vkb̓��-hO���VΤ��Y�e��U�U/�7�%���G�CͬC��oV��T�W2n_M/6/�E��NA���uB �AE F�$x$1�L/�qvf2��O��nE�]��ꯧ��g8�������Lr{ӟT�A��= jdS����FΎyʧ�;���>J$<?��`jJ���2�� ���e�L-L�"̲ϔr��l�Zx�j*�W�� +��6�����R�:��(�8Dd��g:�s/���u��V������o9X=����K��]��U4 I6���,����IU�`D�֪3Jn�bͻ��R�N��l�79PCQW���~�Q�n1��+怴I���$��*P�h�".M��޷Y�6��M����v��z�<3�͌={ۈ���L�$l���m�4;����|omBc�&D͂�ඵOt��+��l��`d�xϞ��i��Xf�=]V��iM�Q��X��21fi\�6R��:!��a��U��i0�������S;k�'��M���ёN�'K�~��uy���[��V �:��i�E5�VZHW>�̆`B��|��^Q��n[��aѿ�T����E���yb����62U�8�n��Z�p�F�� �r<+��r֠��t���;[��C���<�ě;r|�*7Lv�I2�=��|�"d�Y�i/zQ;B��0Z�|�a�o��4�C3������;�c +�0�@�XL����u\On�&�d=���Q��g�X�����C�>����_���Ok1���Sd�$1�֎��84�$��C���Y�ZY��7���w1%�褝��{�N?��C��ȀqЊ�|�H�������6�����Jx���4 �Ao+�L��%X��F���`*������*pv+�Hb--|È������4����7�]�x�p�PFc@�i�h���-]�I��^�7( a��n���],�V�$��J2Ԓ��Բ��Zs�“�A!(W������� +ᡒ��=5��X��Wb�5��d"�2��#��X8[�-����{`a�ӽO�)�R����p���D��1ZA�JeX����!*Ά*���C�s��]κ\�S����{|t)P���sf �c�'ʗ�JRk$;w�}���MF;�/-q����K�>t���a�t� +����җ� +;Sw�C��C"<�8�L�G����ȧ�ߎ\K����}�V����i��3��ޙ��/��X[O9~ϯ8Qe�h�V�IKS����Hز������=k{h�_�s�\Q�y���������qC�#=�% �\?Ũ�w����nvaQ ����H b]�^,�D�O�.# ^Ї#�>2��� +����>�� q<�)��)���p��q��B <�@p-�}��T���B�� c� 5�r�@�B��T�� �BXS|�|�'���e���@�T��0��#��y% "�]��p�B��""�D�ZD:�DJ�4|����/.�g����'g���Hx`̂@$\�֌9�8�s�5r�j�+��ȍ)�K��"]�t���� +\#o'��NW���3cu��Rp��h 'b�2`�U��Q����v�ww�Ú�4�Wl"��. 8f;�tM4�N\�>�T����Dljn0 +�\g��TC��%*��(�(�4��%MS�͊)2 �� ^�m��J�Z���9N� �`��6Edh�&�&`��ኇ��O��z�ja��Ȑ�g�6�Sñ� �:�5�\{�껠6$W��yScHm� C�/MB_�"C��(|� ���G�4t� �s=y�2��+�C�DG��*��M?�� �Z �Y�L|� ��GŃ�:�ɣ�7?�:��D�ӎl. �#ʅ*��� +DJ�TB���|�ؽ��µ��oz��_�PfzK��.���zŶSy�b�ilc�0��flK�-�#�Q������M_J�����L�+X������ ����sŻ靑����2~Ll�#�k�z•&�ah>�ք2�ֻssv!$� �� +v�k�Ԭ�?�0��Me�X����޽����Ai�*Z�����nUh�6�K6ͫ}���ބep.�]*�l��{\��-��oT�jQ=����͢jo� yY�i#H3��'S[}��k l������S^�ܤa=�&��;�1UE65+8��k�<��o�� LaK [s)5)�JR��,��Ke�f�Ҕ�9Z���u-�6�8����?Qi�̓i��U"���*�\��˛W��̣���[�-\��U�]KBA���W��)�t��u�"�b�3�]Zw��9~��8������<�3y(��&-Ӎ(��z,$w���� ���AЄH�bY�,|����DT�9l�����ȉ�m�"V��r���2k�`Ik+l�+�Ę�%�_(�H�j���ú��r>g�zB���o��%!��d�Z 9 P"Y!��O{o�j���u�N��[�� +� .���]y�-;������$�%)���<�N��u��bX�P"���I�cc\�"X���s:(�Zp�����_�R�n�@��+�@�D�BӢTE�RԴ=E������]��BQſWk 1�CG>xfߛ��ړ��P���0g!��E���m1�n� �{m*c CP$�+X�f�7���[2�Z �| ���*� I&��/�M��<�R���3F$��ap�>XL@P�흐YF�ĝ�'O 5B�}TOhk4:F0���(1ލ XT��6�iy_泇ǧ��� &��(���^ K��ARx��4���!�m�T��FXԊ�����q�EN��N�����j�e�*fXDZ��7M�j�`�A' 3k��?��p�kE��o�JƶI82k%��و��8;���_��(g$ߎ;B�Kk���� E-y Hu�'::"zr�����Ҥ��������4�?���vr�j:�����7�(��`�Mٓ�<��5�g��+�.2�)$�(�A�����?��x���IȸU^�m8��n`�}�]�UP�NA ��W�[�@�6�Dբ­�*g֛1��ބ��߫�&���~~����K� +����j���]a��4�����tAц��Bb�-����3��1�e'a��~�kI��H��3��W�C}���)�yIj��g�L�U� +��A����$,{ˢ{��Y`��c�?�Wv ��2Bj���BN�(�I���a�������m]5�� [R4A�kq�m�V�k��3|n��]�5k!�x�H�b��Թ^?B�y �;N,�9���9I?y�7�G�oƩQ|$��"��׻�>�j +�ǔ��G���'�A"��O��^~��U�K�#\\b�{��C����?�T]k�H}ׯ8 -�����M�MkR(iHv�]���J*� �a'���2#�և�]�Ϝ{�=���]i��kf(���܋&{�!;O�N���JX�& ͌�*pW� -D�J�#��!�>I�T3�����`F��z���r�Z3���B� .���JW���)�d��3b�2�#�� \E(|]������%Y(�0'��C��,a#h�ެ�o�C�(�U�a�,ra[.ʱ�� ����w�����q�]�̍z���'��a.�3�6B��O��a�{*���I��]� �4߼��M�T<�2� ���o��ׇ����NV?��N�ը�`�4ґ��AeMt����v��Z��Ҟ�����]���[�l��A�0=o;3��G�͇��m�aY�� )>]���qy�����2��*�}w!��Ȼ�cT0� +�����陸w�f�c�D��#���[ߗ�eI.,�5�f]��# +���p��� 5il��M����B���}V^��Ϝt/�> �����{���ʡ�ȳxn�9�Z'd������z=�������������zX�n��(��^����tO�������G7�P�����b!����� 1PF�v�����0x������e�e{��Hc'b���܈�Xq߃��3!\�� /�y�U�{#g}Ί�Hh[�I����}�'����D�Q��]����,���L�x��Ytڛ�gp +�Bx�-�q�v1�囬�?P�\��vx2yg^���'}R�o�0�_q<@D��:(�˨V���G$d�Kbͱ������CHiG�'���|������2�p��� <��ϣ��$x)u�\�Kbp9<�z�������d�ܒE�f����f�,.���u��1�p+ki��H0 ���/=:o0��(g���fG��s\"�1�:�Xm�B��U���c�e@�i�7���t��ZF�&��a/d:�0���8��&��\v�<VV�TO��{�n:2��՝6�:�j�]*vt� +�� ��r;L]UI���満?`�$��I����O#�(�<�d��2�}����Ir'���dG��Ų���[��UQ6�l`��] �q3�S��Q��_������4���#�/[�^NX �0}��p旐k��7q5�����$I�� ,�$���Ѷb�[a�kK'}���=� +��8¶�u�����-u����:��/U�]KBA���W��*�t��u�"$�b�3�]Zw��9~��8������<�3��.�f�pO�x�z�,7��� xw^�����LE�,������R>��=��}���@� +&T"O������!��&QO��r�D��4��)�@P�aS��׭�"'�c*P�h�`����( �T6�>�!r`�����2�f��Yw�XL)v$������Ϋ�v�%��2l�ϕG&҆%�e,�yZ?26���hU�z�ȅB���^���@"xca=��r��,�͏��T�N�@��+ !;J@�6�T� +!h�C���z�X�v֡V���Nb;�{�����7c���-�(�p�w$��W��S2�.F��{N )b��y0<���j�����*��n�F�UB��� ��i�^��S�����= _�D��۸��Ec ��4�;Z��8� +>>G�J�@��Z�D��3� +���1X��ք5�������>����\x� )q��)|�����lJ'�Iw�/"- +d+$�s.ܣy�;2����/-�2�^�D5�"�3� ����^�� + Ԟa��m��7���z-\�27:�U��h-<©���Q����*D��(�sxH��krFgGP��}u��� 7��,=��gXJ��"��|]�����¢ �^��,|�q���@�� +���F۴j�q(d���6���`L�����C���>>ne��i�;��9�޸*��4��yz���U� ��������ֵ�D�q2�%�w%5%��g#]�w��7o`Ɠ5��R������¯��|2�L(��,��&��PMo1��x�vWmW҆�U�T�c������myf"����ٴ����y��cn>Fђv*Qɒ���"���jZL�5~��#XFTI:����� ���J]a�<�S�%aܨ�i�>��[�]�֊�*�/�S� ��h"��(�| �$��%$ ?�1��w��=����`}�V� �ёb���~����Y~{Xf���%�+Fk��E-�V $���'MС=U�^m��҄{��]x62- +�3~�4a�U�m��� +��5�;��8��=�|��2��)!\hgGhZ�~��F�{��X�t�,��R� ���F�|.r��� ���&�����Q�pV1qY}�:w&�H���տ�mHi�o�Kz/e�޽r���Q��\L +{�2nmTZ��4�F��͐��%�JI�7%��)�S��W[o�6~��8RX2�d{(4���K��$E� F:��R$G������d��� ��!�y.<�w>^��7]h�1�`b���VTk��_����dxWp k.�� �Z�m��x>�T�6|S$Y +/�Dx)����,��������L�����z��f��}O:bz]�a��£o��M�5Ћ|��QV@�M��q���� �_.g�5d]�L2�p��ڳ���������h-�`��y몼i�y���6�w/t4�e�5$h���+��bm�eh����\�r ���O���8��'��&I�-�W��d�X&pE�Q; �a�u �%���Ht�hh��m����*��u�������S��ce���8�>�l�y����}�?=lXд��DRd~O�U+�L�~r��:�I� -R�#���OŇ��$�F����00�V�h$�F���)��f�<G0��v8��=˻����֔cd�^����fBD���]�;�k�!^_|�em����Fm�"�Օ>쑍w)�J�`��ν-��:k���X#�#&�����a-Y:�:(���[y�8�S�P�+�a�?ۋL��8�P6�*MsT;����\�۩� 4�L�;�?��^��r���'�/���z�Av��#��Z�m�:ʑ�/Q��G�^�����v�$?�/�Xmo�6��_q2H;]�ɩ�v��H��I�if0�)���k���wJ�V�����y���Nz�&�Ry3�}m���lS��/��8��HhX����2e Y�e$���1Kҭ�?�;%9�3��+��5��o��ߣ�� ��3m�p�3��.ޢG���3m�?u����ɖk�|���ʱ\�kj.�ؚH%Oy�͟�wA�a�|�*�!���d�M����*[b{H�u���N�jrh��f�4���,%��Y&�z�8> ���Y"���$�v7��t=˜�ة���VVG��3��όkS���>p�?!�Blp���� �~�QM2��}��B -��\�N�q*� 2��0��W��Y�|���G�!�8=�8�x�k�$�#���-t8n��@c�� ۹�8#��8�jD��d��V�b ~-t6�W''�N��㤆��R��G}���9��V�kA���m��n�U�-(��ۆ>b�" X|������lK,5wz��cC�=�b��ѕ��-��d�o���.$1��E� nB�B�M^�z9i$�D��o��]�/��P�To{u��s\:@��\�D{���yp���v���F)�����|uuu���ϗ��|y�x������I��A}Y�;���Xԫ��r��0�r_?`�d�.��"�h��D��Ѹ��#Ӏ�~�$?�aԫڳߕb��[�p&5lq �"'h��1˿qh�$��C�����x�J������0���e�-�� ���(E��u�r -;X�d����xLn�"��<�|&��?�3��:9F5���#o�h+��F���]�:u�!m]����k�|���|�>n��!��|r2��'��h�ͺ}��tJ�����Q��78Zi5�Gk�7T͗N�5�U^�/l�w#�7��� ��:)�I�ݱv��jg��_D�/z;��@��6�+�'��ճTt�[� >����a��5�,�,򐦍���^�VG>����~�ȰV���^�* -ܼ��ǔ�*�v��ֽc9/@�~�H �#FNt������m��f�t3���o�V�r�F}�+��$�0Ǔ��!)�p�(�0K����p���jU���8�a�+� gn1C��:�"F(��"0B [eF*]�$�!ʒ����Nx�BWI�f�K��ct����~����uqc]偙��2 !����&& �e�$�� -y�l�:et<�����{�L[M� ,��7o��w?�>�sA���R�(�R��D�^�Z�[2�*.L�>5j���P��Zí1�\�G�ZߣQQn���_�0M�-e+!��^����Ն)�N���2�fƆiE�a�/��q���J:��J�j+)�zW��Ж@�!�ݭ�LqD��B�k�G���p8}Ds�G���[��d�B׃�dg�:�G�ɔ�]#��]KP��P�w��rr\�ba{k��V���E�u��y�C���D,чR�i*<����B�#�a\o܌�-���x�d�������h�����A���4p���}�/�=ٟ! � �a�L�5Wui60���;���9c�m�����5�ŽJ�����uݶ�=�� �4�����a��|�����z�w�Z>�z�o�}�?��%e�� S�0i���Dk�T�0������_�-� -�F�~vhb��[���.�}8�������9�����%�� -y~��{w�t�3���걜��>>�{�6�x(��S�2����m3�}~���4��n�_(t��`���<��L�<ԩ���n�̇�3�r�����e_{��V�O�0���␐�C-վ��hH�Bc��$�&��ZjG��m��wv�4�ʦ-_J�w�w�/\�ϒ " S�����y6/�ٻ�y00�/ ���~3� ���3ڶ�k��(�L ��|P�c��wTF����E�~��q���L�'�Q��.���,E &"� b��H��o�� �y��~y�E�Na�;f,g~`�&��U!��� R%Iخ�J�+��`s�})��C ��4�U�>2˕��c�8�|��z���yr��1�3 'f ��…���)C��*u�D�˳D�#��Q�-gz���D�IR�:�L�7gc����y3� �!}����JIRƸ�DA�2�aB`�*�O���dˊ�·<8�.\h^1�pw�І^)",��޺���C�#��"�%�*����ؽ�+�(w��a�ԕ�v�z�2�#p�:?�i c�|G����� ]����rއ�Cc�_Hݹ7���W��CX_�����:U�pWճ�NY�Ғu�V� F&.�r����о����KX�m:m�esH�k��Lµ ���?�a��a4H��0��u���~fC���hʍ�w�������G��UQo�0~ϯ�J�(�� -�u�Z�RWUb��$d� ���v�h���$! ��ay��w�����87��$�Ô)��Q<4K��P�>���x���'\C�S�Ϙ2 cxI�G�w2�+�N �a>+��%e�*������|E8`�+� g�b� -nt�pK�Pf)�D`"�P -"�ʍT�(� �!�Ӕ�eu����&�"�j� �"�c���s������ 3 3�c"��0�7 EH��� -� -E��'�u�h�%a�Q�t�L=/ז�me���WB�S)�h}��7����Q�����d�Kr�_C�,�F�T���R@Ӡ� �o��ƃ�n�ٺ#�V�]��2�pm͘�R ���mU��nm8ɍ�7�DA=���C�`&W�@QT�.L�?�W4��,�4W���/М��"^2 ���t�l)�9v}� ����pn��}�J�Z�n$�$�h%$O�+�[��;�����F�L������Z!!���A�[���.���nD�4 �ׅu���6f�f�fH/XCiJ�j�;6���"�0�8�nɭ�L;ɝ�q5; Y���țD�����^�"x�y�E櫓<����)�>��r�@��� ���%T�<��hz���*/s�ݹ ���m���Bz��=5�k�4��'ܻ1��I�D����o -&��Mګb���ٺ��ب}�Y��/�d+����˴֛!3a~����ⁱ[�@��Ą|4��!�@��9��ymQ�j�0��+��Cb҄^�G���J ��BX��XԖ��N%�ޕ-��"iv4�;�?Vy)%:zv:�#_+���LM#���C� �+t 6�}��4�r`lluu��3 ���!x*�|�cs��*��c8P��5x���}��#�UA�hRH���⚭��u�9AV��{��.tB�K�&��D�֌A�P���K��u�پ�A��sd���T�֋R�hΥ"C{[���(�G�*�%� -���v��Id��d�}� -���� ��.��δ ��ȚF��� X�Sp���`�Af��mvi|%_�e��eu�N��% ��$!8%I�� [�HZ�n ¤w��ŏk(�Կz���p��w�+�kg���b7� =P�N1��+��@� -*H0�C�G3���ƥm:�b�wgW�^�yof�{]>d���m�Ј�+r��w7ㅙO L���:4zg,���g�)�ulR��p�#;�u�� �O*°D�W�P��O�@���L-X�����P���X�J*�'�� -�'�ۦQ~Pﺛ`)�z�u*'���t*vt��^v���aۭꃉG� 2���Z���+��9�Œ -�!��D<gTx���{�# cB*�?�(J��f�2�'��ZT��v�����8�����6?�TQo�0~ϯ�I��d��{Ж!H-C�� T�I.�Z�-ہVS���!֢������wߝӾT���i��<���Y��-/ -=�.�R�#�[1mA�0����v��TϚ/3 ~@W ��9�P[mF�W�����E��`�r&`�jh��pE�P��H ���- -+����!�E������9�Q�*R�W�r)@���7��Q�?��]��0�1 f �f� �p����6��1R��.9�[�Q�̓�鑜���<�0)��I�n8~<F��q���*�i!bG�P���l�QF��ڬq�H���+�Q� �n��{Ԇ|��8Q�)�?u�dQ$ܞ�H� -�u���SK~{@K Rt�o����T�|��XVX�N��N��j�O��g�LK�4�jC�V���F~3 �f���K��F��}2ږx4f��4�Nc�O�| ��+����h����j���G�� -��•ʹܐ��JIM3Uu���C3j\�)F儭E�^�v�W��)��l}�o.�#��R���Σ��Σ2�۪�ه��A�5ͯn�ړE����].�b����-��6�Z��9�=8N�3G�n����̽xuP�NB1��+��H� -*J0�B�x21}�����m�[1��[��E{�tvvgf'��Dh�v����l��|���/�c5(��� Z��FM ����;��va�B<���^݇;��N�$N0��V�����!��҉���� &���CtX��}u�b��(��6;'�N�����'���@[�6�!�:-����8���͗�yYu �F3�u�Ʀ�6��l�#�S�T�5]��z�)j�WF�"�/2V*��)�~W�i=#�e�O�>ȋ����ű\z�c $>z�+�Bp2�s�|^_��J�p�b�KL��/� }R�n�0 ��+x�! ��.I�,��P����@ی-L� �v ��Qv�uk;] �=��Gy�(���4 -¦�rl(,ߏ��l���j`g,�~d����tt�pdl|sdS��b kv�,���`�z_��~Y��n)� |��!V:��R$� -�t����d��3HM�k�U<�G�5������=��n -*�Z� ���������(���4a�FjE4t�-�Fe�<��)4��y���̳� q.WDgmi���ښ�[!F 2�6���pMສ�*�S���#/ӧ -�S�h㭥���5䞜n�,;}) �2�3�L`�!'��ǘ)" �.�.x@,��Q�{��u��aY�N -���)OF�\�%��J��<��=��u�~{#4N��1I� �G|�_��$m$�>��Ky���_o���S��T�n�0}�W�I}�����V1��TUc��$d� �fl�v`�ƿ�:�Q0�4/!�'��s� �]h�1�`�:�3�p���c�& ���[Xq�@w͌���o�=�^�W�ׅ�^և�F"|L�F�,�2z�.��u�w)�qɬ�L��h�ֆ”:B�z 0�C�$5�,�2��� �aU -A�Aݣ�PZ�U���0ǕL��նw�{��gO󙧪���9�1 9�-�a�]A'dڪ�dHBy��^=�W�{�W?mRMo�0 ��W��C� vM�.[�`��@�^�!`l��H�H; ���Q���Xu�������X�5�U�i��M&[9��7Ƌd6I`�Kð7��5z���Ҵt���q��7E)0����[�����a��vC|���6�C��PC�<$V��\]Qh�9dΪ�]#�sO�v�$�7U���=tW&#˪��?�g��p����1���pw�ms��`R��r��p4RjE�f����(F�%�5j��D��~FGI��9�V�͉��x]{&Ϫm���>3<1�菐�9���t�&X�����+ �ڛ� m b��f�n�I6 6�v�V��&�Ql���4�3=g{U����R �@\���{< �������֐���7`��b�/j֏he>���v��ӿ��v�J��]�v����N(�$��]�:�I����>?{�˂�)����~��=����%��V�O�0~�_q��h+J��A �@�&DǞ&!7����l����wv�qJM�K��ww�ww���s�da�2�mͽy�PO?���(�|K������1e@�p����"�e���Cb`�L �/)����cF��Ey>����`�p&� -sTp����e���R�En��E�K��$q��d/�[t�C���X�%3\��p���8���������†r�L� �����M.�`�MB�e�B�DQ)y�D�1z|�0u-��AN�.~����G�X .)��I\�[a���&���\ -"ø0�:�5���)@d=�wtMF#8]��E�Ps*)q��L�3{�2Q�-G/���`��� 1*pmwA}�S֤˕�����^�G��X��|AA�"B��C�1���ժ\�T�W�W�E�Vjd��� SW��tV���T�3�!�מ���IM�4L����{*,���-�-9e�V�}��@h0���BWz�躻��sw���� ?��[�L�ɕ�+���m�@cg%׺~� �d���3]Rp�Fv��o��3�[��S�������8���W��W�n��n!!bB�����L��JK�t�V������Z��jX5ӽ��S��ʾ$E�ޙ���^ o5�t��ڕ{�f؝o�� o�ձN�߀L�t9��"���xL[���tvL)�|V@z�^��0���Wh�"W�:�/�˓�o���}�g�����"�`uhw�����n/F/�zo����9� �V]o�6}��� H -�ڗ:N�fٚb(���K�tmq�I���C��)ɱeŀ�E�xy�9�~��o�����R��32s3���N~Nƃ���ЧBZZȒ �JGzA�B�����ZW[#���8K��(�w�P_�8K���y�>��"�{� �P��k6tn��@ĺ*��P9eZؼv��6�ڐ+�uYb���K�����ڬ��Z�w�֒7����7�o��@���FXʥmbqN� -쀴յ�����@��J���V ����z\*�b&����g[��������֗[�������W�H�m�FP$�Z� ���W�J��%��+��>p���g0 X�~Nxr~�2r-���vc���94~�2�!A`Sg.���Ġ�z!G�/�4&�`�������w�;��f��x � ��2yC��k]����CbZ���P�l�d�d��~M���.杖����B�{ԍZ����(@YA��pt*������p�4� ��tF�;�4���hź��[j���+�;��d>�쇆T5�p�V��gSR�`�irh?� *\m؛u&OIn3�Fdg�ǐذ��sX���C47zc��r�Q�� ���Q(����􈽋�F�Sz��d2�������m*gڬ���'��*6J�B����W)�JRz�N�͢H!l����kN^|�>P�!~�?6��k��+_8�X��v����j%/��� �"G��o\y�?�Ĕ�}Be�;��Twc"��^����uUi��ngR�O��y�QSW#b��k���.J���K�K-ѯS�O�g����2� -]��Dc�{�Tޣ�A�\�������{� _s CB��|¥f ��-4�7�1�{�4��f�����v�!ݽF��b��Nw� �T]k�0}����@��ڴYӒ�B����� (�u,�H�$� #�}W���Ma0�Xҽ:�ܣ+_}Q����i ��<�+�Wh�?G�`��/97�����i 2���pDa�q'�^�Mn!L"�i�p[0���b��Y��Q��ĸf�r&�K�pe�R�R���)Hغ�R���^j�9BV�kv�]��!�"�z�,�b�ho��͟{z��/⹃�ٜYxcRn�\���9E�h#K� �u��@�-�h�9g�Q��L��4.S ϳ��������,�/f_��J�8Y�%����vsOz⽱��SGC�tA���LX�D% '3���T��{��؟ �~nvL{�%n\}{����;fz� -L��T�&OBW+_�.vp���A5zγ��u�%>f٠]��(s�_�Ʌ�񅝕�k-�K���ɢ�#VK��0I���?� ��j7�g�%9��NI�c3a�}8e&���0��:�����7x�� �.����2�&���#��:��F�Q�p���'�ȡ�j, �i�a8[�R)�I[��y�GjT-�/�h���F�lj��y�]�En�2��1�f��Gd��Fu;U�{��{"ηj*���?�.��cݝ�����3m����n�WO���M�d�eՌ���nj]u�_�-�&��{��MT 9U�AO1���s��@� -*B0�C����vgicikg�1�wgW6ѽ43o�ov~�l�����~�&!�^gj:R0�W�*��M:3� -�֝p"r;�����`f9 ,���`��^}=)�n {,4����� s� !˜<���C &+j��.��1[���^�>����` a U�G�.�1�������{ެ�/�uk��V3�5A��7 K8;�����lP���� -������V�M|���L)s� -�nm�)���ȗ�B �=���;�i���F���u𯂫����em���k����RM��0��W�a �C����E[u�j��nO��Ib��������q0l�X��\ϼ�y�ك��P`�a�<����΢���I��@ ����R*�ZA��K��.�bb�����|$�p����� �Nj�[� 3\���+$�cb̌�X�B����7���O����J)����9j�\���Zxitx���F����8yx�=�Q�0_ -[᠐� �J_r�E;SQ���8J�%Z��Y���RУ�^;�O��9x!5Ek�d�;�kf�F�v��Y�گ$�^�� Ь�7��%�`In�G��H����� �����;�r��݈�s�����<�� ��u� ��m���1��(�q�B�0ʸ���O�Cq��$�B?��B��n�?+t|]�ҳ�Q󘽇�61J�aV�'řA?�+l�E;C�%� e�����"�:�U��b��Έ5� i�q^(�X�].9M�DQ�g�Aͬ������Qq�,�/���ѧY����!�����|8W��,�ݴ����}��UmO�0��_qB�$���i��+�"�6�(�Iۄ���XK��vZ����s�I_(�����s����I�@�~�$�JK��'=KP��V�Y�A�C�`�#�M���6�< �A\��L�Q���=8��<�JG��t%=g2͜�)՝��BON��{��=e�լ-^��FՕ�ݬn�n�*�V^��M�s����|Hʫ�6:Tԕ�kI��vdo�ú��gγ� �p��yC^j��V]o�6}��� H -�ڗ:N�fٚb�d�K�tmq�I���C��)��b@�b���<����cUT�sV -ñuFf�ɭ+������h@G�[!-�dɄ�JGzF��\�1��ť��F� Gq�ЅQL�J���8K���i7?��,�{� -�P��k6tj��s b]�� I��2�lZ;ml{��ڐ+�fuYb���[�2ce�UʹY'�J �֖�W�ܯחW7�W�U � -�h%,��6wqN+� -쀴յ��品 ����m!̵����T�o�'a�X�w�m=�=�b{�6[_��R�2o�Wg\�#���/.E��_�+��+m%�Z�\����p8l��3����/��/V�����ߨ�\ -�4���7�z -�_�<=!@`Sg.����A�z!Gg�/�4&�`���y��C����U�E\��Ό�f{�|��إ.Kn|m!1�O`Pȇ�8��0����-�3��(��q2:�y�����0�EVP�G>��r'��"Bf�-�.���p��Eߦ@ܸx���?y�����1e�y0o$@t��@B��ЊɢX�+�XsrH���:L2Ƕ�IL={Rz����ք�絔� �NӔQ�0�Jg��JS�l�V�k ��v�L�оBq� �g�4��yO��ٰB7%�V�&��C ��Ն�Yg�c��6(Dv6z|�J���"V��mvn|�M��C�Dp|h\wh����m����į��4z����&� E�^��֎>M��6�؃Hz�{[v?^�v �黔�%)=D��fQ -��G���4jNބ�4`��}�?`6i�k�� �o�X"M7 ��IN^( -�i�:S�#��;�<�6�J�L�2�S�iobt} -�%��^����uUi��h[Y�V�i�F��J�Z�b�����]��� �[�ԯS�O�g���2� -� ��D=��Tޣ�N��\�����w��[� _�vCB�>}�[h t�54�7 5��A��at����Kw+e��V�lq1 c�J�{�_�mR�n�0 ��+x(;Hc�ڦ]��� -C��À@��I�, "� ��K��b���=�٫��DhP;��$NV�O��Cu[�����X��:yF�B /�p)pf<�xJ��a(u��G����� VJ���x_6x��-��U�`� V4�2��0A�t�2ؾ����)$`���� >�g��=ɬ� �Sl�_��)�,��������c�c������� 8Z6�����Q��q��C�J�/F���cH�(z�JO��=c7��!H�D� Z�� Lq̈́MP��a;��C/�]&�. -�S��>��x�+1كb�����P�����u�v;�V�5����T���������ݹv&�^vp2pYݜ�q�������W9#��O�w.���0��?��0G���(_diC�0��+���x�.�L�4{Wӭ�~-�mT�n�0 }�Wp@��A�`�m��k,@�K�@��L-L� IN���Q�����<�᡽���)D -��7V�о�S���kp�-�L�9��\ �oʴ������q�ғ�o�? �FK��ɿ���%����:�G���w�X�$�� 5,M�&F�R�LF*I�v�Uڔ �J�������;��!JC\�^�Y�� �:F��c^�������wW��l�,�����Fp�6� �6*�!R�����$KФ��O1���O���2jt�,>�7I�Y�w����ۓ����MTG�0Q�yC�@l�A����ґ�����yг�N���4���Qu�T����B e.{��ʠb�pܩ�E�&�/���1�if�*� #�%���b�f;�Z���WZ#�6 ������ dV��Q�ڻ�����X*jrC-%�������|Us,�W{؊2����hK��д^A!� -��ʬ���V���G��\���5 -��KO�|��2~��W�< -��3�w���hG���VI��m%m�h��� �{�b�w�t�?�p �M��5&����X,R�h�s�И*��#x� �5)�!ͤ��Sб�^�"Co���:��_3�ݿ��:wJ�bԹ��]�A�h�����WG�-n�+�O�hØ�7h4B��5�|5�"��n��c�WP����=�|z��SQo�0~ϯ�U����:hGW1 i�*��e���\�5c[g; U���C�m5�$�;�w�]�me��\ ��y��_��Ew��?Iƃp_I�T���<��*Y�ӱ����u�!��pC��7�w0�=˺�Q��CXb&��B� H0u]`Ɗ�X���. 7��e�r-�gC�+�2(���=V+��v�U��6�K���p�c��ms���v�m9�PMc���A!݁ �J_q��v&P�LTt-�-6���]%ha~6�L�$��K瑙U���MN�ΒԾfZM�t�F��^~%yb;�Hv�H\;=OS�3��@����:Y�r�A�N #�~��&���l�)6����>� �?�[R���S�Q�aQc�_:��+��#T�2\���'X�$7�YlN=E�R�D�y��e��JH�:>ģ��8P䅅��hH��3u��Rg/�Dه��b|�C�КR� �k��l�!e:;0?3��F��+�<6Iu,y�C��-E�� -���ű���v� �`� �Y�o�~�a��z�X����7��a��e��B(4�_��JFs��G�秉v�{��J�m37�e�J�Ye��� �&���pKͽ\�*v��GB�kT��� �{��7=�9:��<��ĤC����;}L��UJ�HI�eE��LK�oA N�9.����~F�{��� w��[ �dj��c�6���]����7��[o�@���+&6J@}�V҄��-� M_"Y�=ƫ�]kw A-����\�jWH�gg��ά���b�2aзddD!�s��wA�k5 '���zP/ԋ�+Ug��j4`���8�R�ȥ ��\�y��Q�t�������:R�$���t! K&SD�;3�T�n�� ��\ḙ�+o�V�#U��� �AL&�W�\�z%���[�0 -N�;��ʿ$�ݸ'Z;B������0�M��Po5�կt���Cƫ��̵����tp�w����>��#� Ef�7�cj��Wmi����G1?&?҇HP�����k�yyg5��lop�!���x�uR�K�@���|P�H������� rA88&ɤY�����R���fcX��2�7o�ͼ��GhTT�)c���� �ߏ�9:0pw�e�mK�1`�5�6��5�S�������2:��-�G��0A�_��aE�C�Q�,���"Lx \hG�CK��*(��ƊN|��/A��k[ͯ���%9�^]����n�*�li���\O��̮�S�`Ҡ�*�oZT��J��}KR�j=�q�DP�����;26��ԗ+�2T(��@�uR+ �R@�g~���"'֋=�+t�m�dE�i�n��g��ת �H�ōN����"��0z��i'ߎOG�� ���d���R������|�k���)��a�oP��Rx5}�����������mPKk�@��W�����Z�K�RKO�l&fi� ��X)���Ƅ>�\��f�{����KHQ����J����$����)�-1� 6��q$�0�t��>g��ܑE�/�}GbS%�E��G)·��Dy6��#VH0��G��� (��vV�%;���#�!��B��z�.�F�ū�g� tJ����{Z/W��U�j�q���Cj�E S8Υ#���H��]�qd�}�����$���ÖEZ7�+���6�� ��@jǰ�AP�����L��W_�����DRJx�C<���D�Yi��Ơ� Y��mf?hC��?�����t��I �"���#yok�-h��#pj�p\8���*�B�+� ���y=����z��;��g�z��ԝSPv�FkH7H�|�9�5i �J��H+| �vt��R٫��L��u�6�b���*L�������~.���(�R�2�%z���\[M�J��c�/��H4#��}��֨��}�ꔗ���R#yă���3P�=�x&eR�zh�t�xg�7Y�S�NN��(�=�Mz��R�U���m���ʈ%��׃ݎ����Y�)�j3r� -͢����^�WMo�6��W��r`'�[��N����z�z�!@�H㈨D�$�����"%����`[�pޛ�7��*�sA�P�T���Er��O����鈜�o1�d� ��S�H�%�1��>�7Y~�-V$'��@�H(���������?�`9%x�R1�ɟP� ��-\##���!�<"aƑ�k�2!-�]&���l�$��][',.�+�f"��e|J�ŵ�w����f�ެ�+���"�T��� "��T�O0h�"�\��#NS�9��ǘ����d�b4*����,,R���-�hܯ�[������G������C���T�>�2!��p�@�z��#U,���)M��1"x������cn���Ak�\�U@N�,E�=�x�v_�ӴvL:�ۚbc�� �W�����2e��n��o�ĵ�E�.�=�фE���>�\kɚX��+� aPf!���ڄlE����V�dN$$[�c}��� �d> ��,u �+k�����l��\�v�!ے�6^,�x<�P�e�#[�'>���v�L2=}y�|�����¸�1 ؚ��{0�6- Hd÷�-��~��\]aDږd!Mt}+c?���ޗ��b֗U^Y��l:lѫj��`6PiE��Jѕ��N��J�%-D�:'���-�^���;�e[C���-��^ �������q(�ke����I���Y�@ �̃�l�p������@L���s�P��x�L�ъ���Z�x��݁�,[G�-�e�q��N-��6P'�ԻKގ����F��F�O�k���+��DgK������%QP:��t�M;���F�1\X�1˲q��i�-�#7�1� &ä��X\wsT5Fs�/a4ݹ?ܻI`��v=�IiaNȎ%Ɋc^�^ kv�N]̌�\�$�����1��M�b{hV�;20�S;`x(R�,zk�TI��Ծgd���`��i��{9�> @Ï@"a�W����u��t|P7�"��h�:}����T���_���&���s�&<�vFԤW������9�վ�0*�Og�!���ow���~4�:���33OdK��m��_��B兪�p�COz��s�?E��NA ���=!Ϡ���<��2�u&3�i����.�zjڿ������2X2 X�7�&�L|}5�U�Q#xq����@c�"�j�:���]�2�s��N``�pW"�}��AE��b��K7c��Y"<�� �=����'(����w��|�l�Dk�Tۣu�Ğ����n�>��UP�n1 ��+�v`$�v���A�6�s����r#!�$��8F�/v0O��P��Ⲇ��}"ቚDomWY�?M������!*��QQI ��]�/|�0*���$>��O�E2�k���b�I��~>m�b�oH-R�w�Y��=pUC�RBPn�K6��ފ��"�������:E�Y1wE��b�3�Ĥ����q�������zxj f� [R�Q߽��6Z� �������g.�3k%ϸ $�e=^d�\���z����3 �e�Ư6w�'R�����8��c)�9�R����~�G��l��:�:�~���cc(a�%�� �tE<78�@�5�c�z F���g>�o����S�o�0��x�JDA���u�VC��j�X)2� y±�����'C���vX�Cl�ߧ��'_z�QŘaҒ��c}h�~'�<�� �@�bW�CIk�%���8�eZ��n�g�_��+d 0Tl�fq\�rwa� ����X#�07n|��y�q��A;+L�Z�F��1H�P�ƀ��� i��l�RB�v�TaM����&��緑jLJ%�Qr +-�aCR����լ�ˏ���U�4�C�x��� ��ї�QX��A�h�B�� +��U����ق��+�w:p�V CO�>"CԈg�i��Jj��sT�l�������8�60_/ ��,���ZKz�&�=J\p���]]�O�f�����0:��^hu��o��׳(c��~��aL�a�9��(5�K���R��w�*k|Gi�2� d�7,*5������B����>����E?=h�{>^�~��~V�uj���o<�TΈ�����7J�� �?�ϲ��G� ��W;[�g� �6�>�kD�d���U�o�8~�_1�Z�սB˶GYi�TG��I��qbձ#{E���Ov����P���7�}3�~͒ b� -�iC� �ݟ�4�0��DX� � ,d��<'����\g'#� A�#x0 +�/��;�pˌ��m�{�lk�2K�)x� ���p�%�L�s�b�Z�ۜ��e�o�%�\J����[ +��"��&e$�B&�Y��������/~������X�"�p�9�V�#pW�o�R��� 3K���An]]���}�G�8��N��g�o�mf��]�Jfc0��cq��_D��4͉mei.+�&$�O�0������o�����A�%���(�:M��"�$��Dp�����f0��3���V;�wR9�̈#��ܛ�A3Η���:z�Cao�tHw�>�� +�O]��|+�ȽqݱdrNa�`�hXA�X�&����v�v���\ʨT�}����f>��E��G��<�Z�O� ,�uSx�2;����%ٯvZ�./t봐��V� W%��9aM�E\���D��:�)lH4��^0�$#�&=�j�`ehy��ݟD�Mx�B��{]��^�~�z�aKo��l��N����P�8�+,��3JG1�� _Ͱ�5eu�,��N}�<"(4�1����Q�^�sh�h�a����.�Q�H] 3$ϔώ7��������<5Ӟ�h\�iq���Z��8�e��"_�@O�Y�]OrW�̍�)���[}��B��A�E��XO9c��Cޣ���d����@��l��v��B��-�M��ʘ'�� ��/J � �G�TE�W���BE��t���&_�ˮ:��c�j�㦎��Ϊ �[=��L��;�J����V�o�6~�_qX +�dy�C�Ɍ dA��%Z:K\iR�Qv�5��@��$w[�����ww?�x�.K3�1L�OF�ȼ�}�4=&�ى'�r�5� cڀZ�Cʷx�A�1W�^�$5�G\k��`�3jCpɴīU�~�,���$|�5\Rm��� U&�:�1DJ�W�Q���7J�Iֹ5٭��JB�r���d�@F[��"��v��~��B����1��S� c�q���͓�u���n�̓l����!e�V- F&����KF630���uò�!������\���o2�b�Y)_�ѥ�-b9|9W���'���H0"Xfr����ReQq�����/�����L[��&ˍm�ne�o�A8V�y�ul)��X&������v��+��� ��>L���Zv^_#%��<2~�`ס�0�&��M���ǡ��� +**�:�B���zk�g�S"BgM[�{����y�ao���/������n'L�[I� �qU�7S�g��8�&B�����0=o��@�5�ݪ�~໇�ҕ�ݍҋ=�tP��hr-ۢ߼�e��Vbd�kR+ն_l�{H�U�h�*i@B1:�#��t +�|��R<��Ns�O���GKy�����#ى��I�1�u1i��/k>]ʥ<�~�� G�\\�Y.Fp +G�T��N3�����] ������(�����=/&��Xk��r����`�?�nc����J#�R��F�� ��g,��ί�]�\��EL�}�4��0������j��u�%�U����O��������^3���.Ħ�ysVSU����&w~��?�m�x�ə��\�* A�DT����z��q�hEu�-^ʨ>j?�wN���e3 �g�S�4�/s~� +��ci��]G������} ߧ�^�wT] �����S0L�-E����O$3��.ԙ�Ґ�Rs�6�A�}s�����~l�]\;һ�����Ò��Z�o���UMo�6��WL+ʦ��c7��E�E�],���[D)R �x�b�{A�����K}���y��~�� +b5��j��=�d��d��r���*��-�@�ڂ��璿��|�Z��w���ep�%��@�7ik� +��������V9<���(�7jHÕ9��&U r������|�X�M��4ؒ`���g$ �[�+�\�jAh^9�}����Ow��o̖ha� +nZ,*`�m �5oT�Sű��DbE�FF�D}���"�$i��%�C�/֋I;�Mz4 ��j���t������T�����{�Ok��֪�PԮ����ȟz�_I��Ӎ<��ZIc5rG%a����1��Z�-��? ��|ׯ��� ���՚��%�1��k���<�o�5��u�=NF�'�܍&R;gH���ଓ���VfӘ�L��|Ho�'�3�AfA����Xu*x��F�a\�P�QB�ڄ�}��^�=�k[j�7pfL�a{辜�vG�ic��C;�| �O�8 +q/�E!�H�X4��d-�U�Xu��I/Mc�p��^ij,ژh���%2p\��L�ۃ~1�#��.�K�y����5��IG����'�(���(WmꙞ�惘�4�eϋ�-0j� ��:��i�-3p�%�T2���S�|��M���� ��abN��̧^���"Ls�xk��Gzr�0����͐��jZ���g�Re�<�蓕��CZ���瘧�J�I��L79L�o�|l�≀�tZ$�{}���r�܎�ܞ+57}\OǮ{{v����l�Q��U�}*.V%����C!�eo����j�tX?�}P�nZ1��+��$��B(- +j$TE��!!c/}��ٖwU���hU��O��ٙٝ��M�'l��ha�[=e��7É� F�ܰ`ρ��l�"������ z�"�Sᯍb��x_"�C��Lm�4�]���f�X�Ί���HL� +�s�)�@�=\�Zx�i*r1\�m�.�_��Q�}*�UN�9���ϭ���֏U�_L�8Z�g9{�Ǒ����%u�\�ו�&ږ$[GxnlyJ��"c:��,o�mm�c\�"���Uz���5�ӊE/p��Up�~�F�l� �Iiݧ�:��@�d=��rs�U�M��9��.�þ��ۭKQ�tN��� /A껩����x�mT������:���-�����f>c��%��D��U���u�Oo1����I���J�P� +T �T8�&�٬�k[3�M#��^�D +��y�����oJ[P��$ �e4]������sRFHM����%2)�1�n��|���r�_5�� ;R�A�fq�]�֗�܉g�\+_�D��<�%����Gf�uzn�W1�?pb������f��H��^j2>��'�T+�a�qP$��]M�|_ [�O)���ҏ:������K�:I�q2zUM�S�M��a����X���<9��p�2�MC������Ƽ�X�-���� �9���ٽ}TMO�@��Ẃ�P���@D�H!UHh�ǫ�w���Ш�W���>%3o��{��7�H�KF[G��Ww0h�~I��,�<��B&$��`9�<�b�gԈ�6��A��&�p#����,\2Rx����������:��� .m�2�Am$V@`*��#�-�&��� \���R�Wh)8*� T��`Nh5#�Y������n�����V���\��1 ���,L�M�\u��%q�����H��a�!g��/5#�(�Y �&eW�(��Da$����/]�_p>��՞�_��*�jR�1$��!��:���ʵ��I�A� ��"���G�S�u�V�<��r��Q+3„�j~��M��ί�٠D�4M�l�h�L���CV*^� ��\+��.��s�ʓr���g�S�����N���w���=+0N�iG%z�d���S�}/{I&�xt�D_��4F�ôŏ�Mh�N�a`~�^�)�!��q�б�B��9x[~�&�+I���~šmF]����@���E'��O$+�Z��9&����-�i�x�9���ة��}Uҽe���Vmo�6��_q�Vr�f��i2/� +m�4À$�$F�y�g ��iٖ�%C�I��u�**HQ*a1vlI�/*tӏ�$:F0��9�H!��JX������C�bf����`�e�V#����-;8V�����C��#��Gᘄ�_�F 'n��**4�BoB� �fK�5��Kc� ��V +��[+�����R0=�J�pO���w�yv����C��� s� %������ޙ�Ji�U�G�%�JH�o����]��$�j�ui陁�V,&�scyI +������,J6v1�"��spS��qf�R�tftF9� �p�$,��}�<��J�^e�I0�@*j�&Q�+���T f߱�l��� �#��^)��-�iրU��"���Ã4ڱ�%����vl�`Fk�^=��=��H nI�h����Ӎh�i7�=����S?�=��ڼ��z��u��xC8�ȵ���U�cL}���7�G;��#w�]�C��vV2R�KEAq��/�)g��m^����ISt�5]�sE�ap�4nH�Nt;��*���_Z���Tm�PY��~�2E=��1j/��[Ƨ�p����dl�(�3f�J<�+�юr�*��!xc�u����ڦjA�L����U�wt\�C�)�޷:*3�, �y�6m' � �k���>�~Q�,+^�[޾�7�����Qb��V�G��Ƥ헿�H�8��&wc�ƝJQ3e���y�i���P9�2�\ +4��3>�-�|��ӄ�zE�ݔ�?��I�� 1_5�q�� r��1�Z�I� ���}��sn�C!��7w���֑ѳp� +�'?�q��`��?�qұl%�y�*��;i��f�¿@�#�̯����"���lr��^8�v\Z{�O���Z6��Z��.l��y�:�af,��)�4���Ϳ��?i�N:��y��=��j1 ����q7�,�6�6��@)�����ѬE=���݆�w3�T7����i�����bJs�����}x���v.��0r&��u���Ot�a!�>+�c���B����&u�>��M����x��9�7j���ڸ��R��fA�"��7/j���'��rF��>ә#�XƢSp.r��)��t^�}�;��x��G-b��� �[ 8�'�,o�i$�2�ʻN�DVC$ܧ�w�i��u����ԸȡLӼO5�D�C槵������c�8� �C�9m��q*�Rs�v���/� CB"%K�E����D�(�ks�P+��e����.�_�����mO1 ��ߧ�&�>���[ +�[�m���BBi�׋�K���M|�)�PZ�!�������N,9��]� E���.�W���!�}ꍓQ?�>��A�4�"p�3� ns����S랼Z� ]ك/� |��,�3���'���8���p.��0p�%z8��0q�C�4�� L +��j^���/���Rk�zح�DC�d���5p!��+�������"�� +�\0�A��fa +k�9p(�l�%��i[�(1�@rB"���_ۇ�D�IRR���@r^��I"� ��- +a�+[�p 4L0����o0��a�.�d�<؝W+�GYm'���k%_2x|���Rr���Jz "��P�����V��~N�djE&�{�>����.������-�Y�xfG���l�ʑ֊e���-_X2\r�s���G.�yU��|�� 3Q�M.�񎶩{���v��+�.�BOʚH��ZR�Xh ��U1 [r*�H��F�Ҋ8s�(�\�ɸ ���®b)w�(CK�����.�t�#!��(3��,�*�;Z���mbĶ�G�X�]��@�-�%���i#�j\Rl�T�( �m�����j��&pL:�Z��\�&}�5[�qZO�C�W� ����=YX���i�减�!<Ռe�Z�7T�� ^���`�7�''�fi�/{��c��zN���T�k�0�_q�Cڐn#n�l���1 +����9U$MwNF��!5��CՋ���u�Y_��P�4"`N��{^{��(�A/��R���Ax\wJ/�4���:�z�rY��`�a00��'U�~Z�efX b-,�`�.�+L����ak��r�U�.����`�дƀܺG��-!h۸����7(a�q�x?n�W?gWQ*5�J0�A��� kXiV��yrm���]˃̊��N�p�~�)������� s�{iQyX'��湺��gkb\��2�� +������3�A��v���Up+�[�F�7��գD�6�A�}[-w)�t��#��ѣdL�>�p�A����u��i�6�u�(e���J�T��xŞZ\2~���H�����|��V�A��2Z�Sړ�9rW)�St� +(�W���?�=���>~���[�F8��=����56�5��4�8X\�:���ʏ?P���c��{������$����; ��v��\�V��#��z���VMo�6��WL�;n�N�f7M� mjlR���id�IbH�+���P�����Cy�%r��ޛ�P?��@�q.C�H��ٕ���|4;�)f��Y�hTO�%kt����!m�׷�_�((΂���o��h��u-�H5��3�� ���Al��V���L�S^ �?]�х��=�Iy����f��hp�t>��E�%��J���`�ATqQق�c���=�=�W��c�84 ������=I� RR�Á1����AUPU;E"M��‰�É &0F�ΕN0�����Z��0��G�D_R�c�5|�ÝJu�Z�}R6U� l�< ���.BW������V�8i���Q'e��c��満��3�d��n��D8��R�)r�HQ7od���#�/�'<n�։���P h�)���2Ϊ�D��u���v���iZ\�,��'�F�'Ƙ�h;-�V�d +a�<]J��� c�A;id� +���ZQe������}���%5�����@��p��F U��{�T���m�Oo1���)�ĂHh��o�)JU����B��v׊��� T�G�J�����x,O�(H[ŔGa�e#�@q�~0��� C��MDi,�D�_b]�=�fhw>�T� �|dG�d�{&���bG��y[�|�'ڪ(F9|���x��䃥d�r�w�fۈ�x +��R��Z�Kzr[��E�q����������C;�uuw���>��bR+�AE&vYT�`�����7� ����̩Š4a]+^�_�L�L[#\�G:�v���삥9����d0��+Nc뇄Oj`�WB�U 6�:��Z�Q6N�n�l�wQ�ђwӝ}pB��K-n歎م��^ަ��~�tl?���{����߫��X���N���v3�Hy;���4����?3�x��?����o,x7z�UW6N:c�1΍_�W�U[o�J~���$ۈ@��H�iҞ�U��OE���X��XU�۫� ;P)�3|��7W_]�\A������Q�dK�f�.��qc��3�̀"ڂ��!g;�P!>JUj��-D4��@���Em \-�&iOS\N�b,#�C��Lk�Q�B�8z ���j�8+�i~�l��9΁v�=�3�� 0�I]ˤ���H Ž��}��x���λ��9��'Rf�X�ž��O�H�)�i��,�@�Exȉ��?��̃��[b�;+�(�% �yen�����X,~�2��J]� �}�yPN���pF�`�?�5� +f�1�숆��=|���Ui�#a�Nm����>����2}$T�0> N�0V;�e�`W2'��<=uШ�Xk��T�a�&'���_,+,,�T^�t�����Y9�hSϻ����h������c��s�d���F��J�q�z9�K?�ш���r��b8�X��o̤FBs��У�5#% ,��_���j��;�V�p}T�|��2��P��*N�ғ͵܃���w��W1 +�I�qk� �b �t" 㳁F�J3Q��Mȸd�� ׫��*��5\_C�[� +4�����.:P��r>Lv�c��0|s�}�I�ʑ�*�{�4Z��0V4l%K'CC/���/L�h�>�Ʌ�›��"�'�Rs%�e��;����pf|�WCQ���X� �V;c�d�Q�0��l�ܵw�H��W�S.���{���i?�y���.y��&y�~��~�?!U/���ܗ3߽��M k�y�i6����X�\ �g�CR�h1�mE=Fç��9�=�AO1�������xT1�!���鶳tBi�v�1�w�+x��o��{���,�3 �d6�%m�rw3���Ha��5{$������ +=���ͼs���1“�aOY +f:�W�zl��[�t���PƬ\��������A2W��\΂/1C�n���W�hφB!p�c>h���<�B82�����b��]v�zc�� ,�?-�8�8Hg��&���by��>PI�6N�U���*�A(��`M�s<���o��<�M0�k�g�?�p��ve������ک�Q�EP�NB1��+����xT�`$Cģ�)}���6�}�㿛�=�����Lo�Ϩ�+4(*��]�L��r81�s�s�z.�9� [Q�k�42��[�W �w ���=�L�D�mO󨢛 lhk���x���rZ̲ϔr��+�Ux�h��k��� uܟ{��(�:��*�x���ӱ�=-��͢;�SoG[Pq�� +GV�—Ԉ#�T�"�M�*�:��[Y�����1�����CN�$�4��m`����{ ��+j���g{j�W��*MЉ�2�}�Qo1���WL�H� �k��%*jUE����Ϸ�Y5��^�P��^��P�y5��̎?�6�&mS/ +-K������ ��5�� |���l�@����c�j=]�#;�'��/b�+v4�����XP����1��0��6������� �*��7|� i M�����hr�`\�y��x7@��"ach��}���-�3�+&�lUDm���i!�|�5A��PyX8���&<����gw�QQh�b������|����w�~Ӎb'�g��F �J��dT� s��G��"l�!U�h4���˥�. +'-������Lp}]�#�w�[���6���1�_�s�y�,{z=����5��+oQyoO�$�;�HfٹW��d�w�ž�o�_��t�Lzx�y��]B������UMo�F��WL7" In���8uDp���\+r(.��%f�R���=�%�Q(��ov޼�y���g9$+��2�/����~����e��ʤ�T*i lA���=���{��,���0���J�+�5p#��ns|�%���3n��R|�n���]��s���5Y���j6u�Ϛ�fi��Mv�V2F2�R�;a�� � +�A�K<�����_��U�0� a ��ʅ �����.8F�ur,�* �C���)��k��< +�xQ�2���,�y���Y�M�A+a �'��:�۫k�zT�#S��.��p�޿�wǥ����mF�3�&`����Ɂ��Ł���I�6��3�F* |���&�;�0�A$( &`5H2V(9�^����HM��7��/����4�����#��M��!GN1����P2�p'� zO� IVHBs�6�*2�l49�p�y�}v'^L�unW����q7�$�5�/ J]�`��v3�J1�G퐾�ǿn� +���hM#�A��%�:��VkZ�h0=�EB)}�d������&��YsX{|8�� ��$L��pU;Y~3�I�XFQ��{m��p�!��煨#��1]�{�$u�)����ѵ7+�]��RG�����ˡ�����7� �]�c��T��:e���W�=m���^�Fk�)���v��"����g��E�)�� �U�f>�'� +e�a���T]k�0}�������I�i�l�`�cڗє��ױ�- �:%���!Ď��������sΕ|�]'"�)3�[2�Ӓ���k0�.� �9b�" ��c"68��@�(�5b��<�F"�L����,\1#q����^���+fI0 �0GW�.�t�Q����+IF�rR�V �J��k�� Σ��v�u�Oo1�����."A��?�E��ZUHɍ���kű��,5��HZ5���⦅�G7�I�d�f���3�(�Dy���AO����el�'���Y7X4nGW +�bӁݶT��g�/^�'b�i4]��W�&}��Zgq:�+����b��D1y*B�`abv�V"��.2�!lZ�a��E흡� .l"?kq1��<�L�9�w���������F �:ú|d���I)�sl�L����3� a�h�ǟݏ �jsyW0� 5�> �2^�n���E��(��`��a�ӌ���0�Eb��B�x���f��\ ��]{g�a���h?��k좳'D����rr�Z�0>)������F�j���ѡ3��X���f����VEu9ْ�mU����!�R���7�i��߹�%N��/���W��SY��0~���²�!���u��]�t��B�X�<�EeI�����ȉ/Z�m�$�����+�(� �=����C�z���b� ���C�4���1�Ku��ڊ;�N�%C,xG��;{X +2�ɺ�m��9�0��0�k$X�.�q�C�4�B&i ��j��/����K���d����h<�2��J��fN��G�M����n�y� P�1.C#<�ʟ�0�Fq �{[�D�6�,/"#*�NH��RЃ��N$�����{�qw�U{ -i��3 � ��B�:P+T�4Vh���,f3�A�d��#u�p�� �)��j�M�Cu����6����5����������p�°n��:XM�� + 6ᵺH��� �œ��q�2k����k2S֛5>)�>N��^ �C⏄�l�,��?�U���U��Lw��3O;.�6��/��I� ��|$`� rہN�G.����R�\����vN��;��2��;���v�<��eȩ���y�~�RMo�0 ��W�P v���5I�ni�؆�a��,Ӷ0Y(*�1��R7�>}�D���i�ֵ*�Z�II�r��_�)���e��Uj��'����تN3H+�zRMː�ޑAx������ ޔ�����6X +�Jx��  +ܸ֡uc!S���I��-���{K�-B�9L��ZI4A��R'XY3�Qx���}�}\��>o�b�$�[��*������8��6�D��:I�ʌ��;![Ak�-md�e��{�q�{�.#d�eR ���v� _;�I5��>1����0�:�^�?2J�$����n�5�)H�W�Ԫ� ����P���lv^=$�w�L� r$w�%[��b��[�G.����Q1�#�����k�Yh�x��P\���9���.x>�ż�Ai�>S@ȁ 0�k���$��g=5ͯ�F�V�ӓ��ć�$��uP�Ur��Ab8Z2��A�QƗ3P��I�����ُ�%bp_�`gUuF1F'Kxe ����, E=ip�ſ�4�q��.�H��V>h~�~U��n1��~�:p��B"��%�D��O����=��׶�{vY��;�$���O�U]^���“K������+��7��\���[��1&BTˊ2�!�=]̊�R���s=�s&|H6�"V��r��ay_x�>ǖ+m�G������P��DM�=\��q�������8�wJo�e!�<�Y�%��&�B�G:�s�6�w_�w�j.��*V�&�Ei7_���;��u +��6Y��MM�9>�2�R����uRt��f��T]�����n�I'�mZ���<[qQr���-��}M�Ew��xU�ﯰ/��c���kO���~�~2�RM�A�ϯx�$,����kĀ���M����LkOWS]���.��$�!��W��R�а �<ɦ��w�'�/�M�|Va�/��h}`��Dj����m�����W�� 7�k��7��OV�X�F����ۆ_���k��)�=�X���]�K +<A���h���D��;QX�h��N�#:x�13|lE{2/�)0e����}X߯>=�F��:2�(����� v�:�>ˠ��9F�W�zΉ�#]˷2�EU�h��X��7Z �W�٬��a���-���QD�h$�Z�g�A#Z +�B�Ϡ�L;t�@Q����1/g���!����$O��Q��Eu��A!�a���ך[Q.���XAv�R�0�3&����4�C���#�$ +����~��_����:O*?���_ƈy(�~^>8R8�����kf=-���Ep\��^m��rv�ӑp�ڸ����ڊo����TMo�0 ��W�P�v���5�ni�X�ݰ���ZI ����^HN���E2�G�=�x�j� 2Zd�߾)���i��1 Kc CP$��pט53HS�d�F��%�'���*��$ �^.��� +'�Džb1��Gl���������b �*�� �E+�xW��H��l��W���ht�`���J��n��b���M�}�M�?�_�T��4J`�*�]-�`c���ٷ������3�V�Ai��F��ϓ"�,k�7���[\�k���L[� w�����������,�����2RDg��jY:]�(G��޾bF(-9X*���45��&��#��w����[�ƴ:��yƱ�:�� ��a��s訶��U{���qK�u\������N,�Jop�8ܔ#X{S��6Z��GCA�-���a�b'�!:�2�N�I�+s6!TUD�����_}�(���U4t��^�c�z���mSMo�0 ��W��!v���5i�n�� +l]�t��,ӱPEH:Y0��Rb'٪�L��=җW� P���0g!��U�y���f� �����X� ����q)�Ƈ-�e#����C�j�{C�KE���{\�| ,�Q�a����C��1��@{'d�V<��'��n��w���ht�`\�i��x7�`Q1���&�?��=.�b�$L%�Q ��]/�`c���ٷ���:��S+�4�S����$G�Y�r��t� �b����02N�]t��7[\��������V1�/F��?̒�6�߂�b8���.�C�^+�k^\h%2�/��Z ™��i� ��5����U{�B���PaAG�,.��Gn�=�x΢����Yߴ{���drܯ/�j�r�?�i8��kϋ ��ۣ��Ғ�O*���:���D�zn�5����ᖝ_���*�?m���o�bko�c[VȬ��n!��a���O9eW�j� 7X����ַ�QN@<�!����Vi�����T{�����ϵw��Uޑ%ۊ�q&�7�Qڬ��<�<�+^$��1 ƥ�omLO�)����]���h��`Z̏��S}���TMo�H ��W0EPKAlc�u��6M��EѴ�H�#J":���c��^�lىӏ�b�����ș�?c�D� c.�d�V7e�G1˦g���*r$ +� +>6��I=�*� S�(䶀��#�v�EV� �/���I��s���%��v�p!C�26Ct��`| 6xeZvXv�o�6U��={B;���|�5J��CthaE����~u���:����(��@I���֤ hj^B���rhy�yӢDc>6�߇�zGfY��>���lD����Y�YgD�]h1�����C5�J���E������� .W�a(�T��ȴ2�p�6v3˞ g�S�CB얎,T��=��� ^�;��U��pj��b�(}N�A�Ŗ�h��QS�o��j�M^���|Sߓ�֍��q�A�Q�O�p����c����o��`�{��Q;�Qۉ���%���_3� ӣ�'TTA~�ț��IT��}dU� �������z�&��qn�c /j�+����am��h�Ҁ��A^�`BP���c_D9ɏT3�2M�F� +^�k^���u^�/�(���4� �iL�`���bT��|�����I�E��;���=��0��,��\�-���5������S�ѧ�]$���8 lj�>C�����. �n��G&�_/�a!W�ʧ���X���|����_̲?�E���A��;���n�0��z�9�` N�^k�uk$����$�AS+�-M˕ �Ȼ�%��ϡ(o~��]Mޅ:�$m� +-K���o�q6*2�R���X�����}m�t��%�>�٬k�@�xώ��*��X"&��V��UI�!h���pG 1&�f�䃥B��;a�j�s�o=CjB�X }LO�5�\$Wy�(1� ,�H�ڵ�>.�7�n�U;��J�S���,*�3RC���7� ڗ�ȣ̩ Š4�V����F�Y����̚��[����3f[���b���V �:��szn��…F~�MR��AlV�hT��)˥�. +7Z}^0<�<��]�t.�ؗӎ��/�N��)���J�|������Iv?����]���}MrD�  Ŭ�';����������/��{��Ot9]�̽��z��ÿx�-���=����l�gk=1}������SKo�0 ��Wp@P��#ݵn�tm� X��N](2 S$���k���Į��k��t�E�{�����(!C�a옔� �Jt��I��t�k��J#(� ��M�x�&�Җ+R��!� \�Ax������N��� Gp�S�X �B�s�\ ˢD[j� L�&5�ؒ� ǖ� ����#���J�q��悕5GPja�p�����r��v�6Ƹ K� Sn˅,�޼�Ii��r/2b�����~߼HE���� ��j�+�H�nH��pER ��Z�h#�i�$¯����p!����dK+/�'���:��>����T+ْM&��TI����ڤ&�����^?O��U�c����3XX����P3���}�ԟ� +Y@��z<�!?�uq�Ag�\�r�Q9�m�x0�\]�q�Y���(�̻{�;(�!_���J�'�N�:j��PԦ�q ��N��rAv �m��x�>�)q��ԧ�.aM��v��. �"�b��ס޳]�!sH�K�m'7�Yc��Z����������X3�t�� _~��8�T� ��!ex�N�*υv�*�0��� ��z�]��x[0�VC���W{\�3]Y�Ї���X�G ��ܳ�E���gv-Dj�Ҽ��ÿX��9��˯�ߥV�n7}�W�9�5�}�,Չk�AZװ��%�AqG��)r��J�{A��b�M� H�\Ϝ��ٯi�B�\2�Y#�}��i�K8����A�A��X�k�M�?��K\�toD�Xx�B�$�zBc ΘQx�*��p1�%�Y�\c�Ψ<8O�u*� Sp����jC��+m�&�LJ��w'-GEB���0+��@*��V��������fy�L��l�,�A$(���M���Ig�#p�)��� ��#�&�|��<"��(#���3Pj���?-����,n���h�%#�׿青�Eh�����η��RĊ���W4b-и �DjĖY�1u%f�����e�%����3���]u���}O7m\v��M/�@e{jIuU(��J +^��ȵ"k2n/�> � �꾏U�Z� K�Z��@+e/5s��#�t�s ��Bu�:8�^y:J�x�y�.���b�.�;z����ùM���d�bBbt��c�j����L��Qҕ9�yT� �Y�jc1�ü��&���Z4�v��vr)�ٕf�iPjN1�#�0,��B�h|��T&e�V����0���j�1`��f�j��4���W+��F�832<ώ�T�K:�3H�v�c���R�M5�Q��ҝ.-3Α�O �ץ�q|!�>5ܧ�$�Q. VW���:�S����� !FG��t�hi�PqQ�2����:r �S��HG�'��L�7�����Ѻ��N��'�}�V&�3�\V0�.�?�o����)r��=�h�6�KP���3$+T�>�a|���@"�8�r9�m�/w$��1�J���󟺦;�^�����|KO���0~r�9f�S �J�:e��u��9�q�������X򏌔��L)KS��!���J�o2)?�8�4^]����oX��g��e�C�dc����iA eQYO�V���>��u�ZT�ŎQ�]�l�ug�²��kp¼���3��V��Q`�Mһ�ӫy�h/]�V�؟Yo�z�Q�J׬�e1#܋&�k��W�Ï���N�ؑ?�y5��2Z�Pz:�2�b^9Ó������j�?���V[k#7~��8g3c��}X +q���:l mR�K�Țc��, Gg옒�^4����m�����w>��$N B�a�����t�?����d�+VVJ#(� ��/����2�;��I�c�@��+�ߴ0/H��J���e�}���p)+a�3�Hp�ʅ�$N�&�!���I-S�䊄���c�U�5�*���J�qʬ,m+k��ha�p��=>���x��PYc ��p)���v�c`߼�)Ii��勑t��_bA�9Cd6���e�� K��[���2�ب��Q�c�Rȸ�`�}g�!/���"&�H�����U�Z�x�4>�����"�li�U����ć|��%��&�b�=���������9�2�D�~���C � �1�λ2.%�� G��=*��YiSe�.��O����d��(�aawg#/�}Q�w�P�Uݛ�R1t�Q�P#��k�[=ȭ7\���Q�p�iWr�<�����={�bt����S;�@9��w\�_��g��G�g!����)��������"´��{-t�9 ���WU��ap�do����k�0���W�C�vHm���Y֮e���uc/s�ϱ�"��9Y�ߋ��k�ل^�����O�w� P��ȔEa���C�x�&��r�`�Z�1��D������P�w\�p`�i2��{vW�/b�0Gv�\��i�Q :�H1�� ,C�K��ՠ�6�N<�S�g�����������"�q��-��n +�F���}?�iu}���&Y�`Ң�#�&�����$�G߱&о�K�pK1�&�k�W�g�#3����r:%��Ն�J�$Δ�cSG�[[��fV), +wZ��a�O�%��ũ��|S�a�}C�ø��� �, c��<&�؁��hg�r��ņ�K'��,���Ȭ�>��Vp^��j�2��W(��6X:���!}w�7�+�1}i��HO+=�G}PMo1��x�( �J5RTEJ�^*!�eG5�5�����^�*T������ɇ�F�d�*� +[]�.R�W��Ѱ�_[Nh�8!Q�/-o�����Nx�*J[�x�'g�OM��4[�滚�7x��I���3u$��0�m�e"��a�W�e�AR����9ط��vl�'�&��(���$†i{�=?=̿�γա��F�5 5�c�ز��\>�N,���TyTx���%��F�Ï���:���I�s$�� ����C�6F.9��j�]�Τ(�1J4G|\�n�آ��ͰX���Jg��jw�W}x~�\�v�op����Q+ҋ�r�7,���+�w��_6;K��9�I�Rh�}��������������i�������J�7���W���ɾ��QMk1�ﯘC�Ǝ�v\�iB%���T0���+*K�IkcJ�{����R�N�{3o捦|�H�T��Z�e�{ +w�I1�[�jm:� �p5^���q������(e��l ���?�c�T�����*� ���Q �/�c���7��7��VA:Y���8t���B�yPOh�%�@жv�Q;;�7$a�i�y_���iT^,6"b'��Ha�c����eI�N�+� +6����F�����4"�\\o�$�*`<`��[��NJ�4)5=뭈��������qB_�ԡ�}�2Z�n�L`��Άȭ�噁���R�dh���M�d4�i�Nm_��3��f����b���n�J���E��H�+$�|��g�'u����hֈ�`eO���ņ�j���w⠝-��t�t�b��2�L���Ǡ� �% ��gn�>�N���+~�U�n�8��+��SK��`�u�5���lP7�K���FQ�$f(��&���,�r��e/ˋ(�ͼy3C���+�(� Lٓ����p��e��l������PA18Al����i5bn�R��C*3�@���g8d�r����8������ + ι]�t�C�4 ���ƓZV�7��[_"�� #{@k%�0�2����ʚ18��6 +��ݟ��W7����K�a+r�;.�a�| >�g[�D�6o%�%F�������O����4I*q��)�i���ϵ������#����έaOB?M�3|�Z�5Jo �I�F#�����aXv�6�# lCՂ�/�z�dMX��i�i����%�m�x�]� l�F�u�i�3���PH0Pq>���`nu� ^�[�=�Wl�/��Kk5 � !�V+�y����E�%�y���P,���~�Zx�X�:���EmҔ4�� +YB���(�[��H��\a��7}哙�k�r��V蛬�Y��1�p���pz����ʋ�Nx|��<���kBᑾ�´�?�NYt�8�?=�?H۝�^�^�`�\�4Lf[R� +2ʬңa4W�ˀ0�' '�Nx8~x����4��N�� OA��������-��d�����~cA�����J�v�rx���[�O)x�VF������-�n�ፅ��k���=�o�V���h?��c��� +����^�~�ڂ������2+h;����Å'�f8�>b���6ׇ)ʦ��&��Vs��>%�}��n�0 ��z +Z�.���Y֮h�� �m@��tDD��N }�A���;TI�����h#�h�NX�$2�*����SY��k��b��!�@ Q'�����ot!���@aJ�O��~�I&:y�[�75NG���f!��;��`���]�Ct�A�L�h�JH��B�M��=g;2��|�F ?��P3–p��=�,�TgL��i�����v$$���&�`B}�|��� Gm�V�Y��M�R��ܗ7� yi�����#�>D��'r�س�;f�J)�43�����2=�v������? + � h��3��2\D����kd!ߍ�� �@u/��E��x�B��Jl�(��]"��oBqJ˫7\\=�_�_�%����:�j^H"�*�р{�R�D�J���܆�^5�����k�Zl��4,P?��9�M�f�а�p �3$�:�U�K#�̅%��8p��~��� w�����[�4��Ԛ���_S�ȯI��Vg(hr�[Ѕfw.��*����t�Oȗ�a�ƭ�������3tuBVp�`�r�u=���/M��N��`��IƟ*դvA�*�O�K�ٕ�w�X��1�Z�z��/�J$��|�GO���Ol�$�^��f�|��ͱ������cj&�-$��Tb�V����QS�p�*��:���~H5K{x�ޢ?�Vmo�6��_q(��SI�3*��(�7���m��ָ!�0��g�Q��nnd!Q��"��(���P�;� +L���e ��n�KF�k�s�*b��)��F�1|q�_�W��ַ֔K��� ѐ1��� ^�)��䵨E�"kS��8�PKB>D݋/�"�QTk��3h�7��;��w���^������@���_>��oW�k������ޏ�'?*$�cA�o��kM�Gqr-����_ɿB���d����Dk��ڐ�\��1��-�8;��QM��Y�+�s��\X�TlG �Bz�e4�� ��X+��=����~�R^0j���Ɇ���$1l�Jf�sx:�� �zS2z辧'�[S;����a���ktE�З�� 4&S��*�>�r��7�] ;���6n������c� �Ć.��b:�ݏ)�|����b�jр_�������u-�P�l���j6)<�����dσ���jø�<7B���eF �͓���`Y��n8�M���-����IP��<��7��g���m�{4Y����r�D����I���r��;B����P��9N���K��?���6c�mL�-q�x芓�6���d��l���71Lp�� ���9۶vqO�.�/흍3*I.�O�� Nky:�n��mBܢ�܏�kܙ錓9/! �-5#��~?��I�2�����Y?���$�9����7a������o�!�� ��(r�z�LOͱX�'����&o��]�Z���iR�'p} ?���6�}t�[ڿ��7�|�ߤ3��~m��Qw�59F'����M�5э������I����W��w�F��VQo�6~ׯ�.,���1��d��xh#Zua��):L&5��c ��)ʖd)m��E��ǻ���;^�ϒ "�)Sj�����e�ǿ.���Nᯄ4Ĕ"���)2�YB�w2�)zN �|�J ��2�7*��)�7�����b�p�9*�ҥ�&K2�Y�LD��0�V��J{�H&A��4��n�)q�D,՚�bY�L#l�n߇���a>��\`&a�LCD���l�$`l�Z�#p�!���Qg�#���r�2r��H� ��gˏ�>>-?M���LJ˦r�Э|�|���']�8�����0 ���X��E5Դ�RaC�3E�ą�G�� �Q��,�&��'�''�k,���?�z �2��Ki1Yޥ{�MS��.�b�R����cf�h���e�i wr�1C+J���6�� �OO�f��mխ8S�a�'=�:��p�%Q����9-�]�U΍T#���UJ�P��r =GOj�i~> �z�D�]0�h�F�O��^�V���\�6������2����_���:a�<���.�&W����������Q��~��t�7h ^���T��T�x�~��V��4��A�d��d� K�����vS5$�a���-7�Ŋ��v�q͖��-ܹ�Hԃ�.�v�K����A����e���_�3��� =|i�Ώ���NAԇ�q߬iM��w{��3��]s��RBV(�Z(Ź�� �e� ;�v�g��2\x���7�*7V@r���V +�Lύ"��FW*ډ�����L"G��x[˖�]����u1\���-�ߓCb���퉺J5�'�,M1��q��@��rv���j�-y�t�z�v��'�G�s���¥�I��E&�Slo�>O������Hgge�r��i�'�����ן��BN�(Oi�ZV�\Q�~�&��0��K(�8�R^� +<���8A������S)Hu�/��e����k�-ݬ��8�� Z� �,�QMס+�TeE�2�P6�vV<�kq>����6�U=� k����p~����2��aڶs���?��� &�lU@�a�E9�,%$�����]�E�L��P��&<��߻e��I���tdž�Ah�^�Ȥ�oG�s�˟�T�L�D~��5�,�7� ��q�(�[۰wvCV��ث<7J�t�M�㱝ƻ �I��zmX������j�� �k-i_��3��T��o<�q��7&�@w����.>_N{�qY�v�y��eWh�=;\ =a�me5��ls� ���8�ώSXڢ�M_3��>����J�!�>Gg�Co��r��f�D�˹�m= =*o��0�Py�R����� r2$����>����3b�p��ǃN�ώ��Ir��$��'c��v�i�}—�?�TMo�@��W�!�P����(i#�*�F�R4������;EU�{��ҠD�!>�����7�=}��LQ�FouI���'Y��A��V`n��� +aץ������<��E��19��|p���)���l��+hpS���E��"�S������T� 0�+�Y���%� Z�+��l�S����X?�D��Bt�BP[�[����/�L/��`Z�� +V.*��j �/�bC`B�9r?�$�h�K��}��I��"0��2��W[��*��bv-ɫ��� ���°F� ��H����T_����$"G�5*�����dM��9k`^y�<��[�(WF;��np�VLz� G�6g;d)}���~�����M�kk�yɏ���!��!r�AF�)���AR�w4��؀v�駪� �fV#gQ�=����z�2�����8`A���-Kޚ��]X-�Yτ� 2�P~\�>V�?�w���_�w���1�}������Z�c�ܳ�mNh�^+����ߛ�X^?�v�F��j�L���۶ mW��?s<��V���e�t��m��n�0E�����B +�@���� b4@Pq��19��2$;3�c��Br�U�����ŗ�xv��k5 �~ٱ�~��̫�� +���E"#( +�!�x�žo*���\�v��v V�_#��,�X�$��N���6�%�@ ߸g�B�����%� %��I���E? +�Y`��c�;��'e��fy% 9]�D&e�F��������5�YG�)|�S{�u�^s/�Ჟ�o�D����#y�?��W������(������i�1!���f��b�Ur�Q������>��Ʈ7�_�O�j�擙eò�q��M3�>�5���zI�O��b �^����,˱n�iX�@z�6��VH8�l6��\{��W��?m�Mo1���+�D�P�@iH�HQA���Y���blwf����j�So���;�o�)��" Fj���c�~�2�V��]��&(mB��b�7�ք=�*ꉧ\����ȍi! �9�CLiƒ�P����֨Y-p��h!4���4�Dt q��r2 ukY���Bր6m��.��CRPH�,;��� �V�>��セ<=�X?w���5lt`%��O�` YW^s+�*�W�w���ְ��_�E�U�"�� +�lXQ[As܇�]��a{$|�WZ�j��?�ߊ���u �6mr]�\k��|}Z��!��@Wzz�'����U���&�As!�d������yt�]�v��-�Y��h�O��q\��zA�g�g�Y�=P�NA��W�!!�؄���EsDB��ZO�af�ӻ��ߣ�1�~ԣ��W5Vt �+g�T��ڮ�]^������QzI�4T����1��3�=�ԝ�&fa���m���j K����c���)V\�f�3~p�bَ��+KM���C(�Tփm�ߋ�"�)!|�O�$������ޤ�S�D߈Q���~>���Z�OR�`�a�:i/v؊E���AJw�|�g�>���Cy�d�d���≭�Q�feꍛ�:��:I@?�0��a��'�،ݤx SW�Q���W�E������R�n�0��+�"���5�]�A�(���@AS��(M�˕�Ϳ�%�yݓH�汣�O� �H[ŔGa������b��� %�5&�6�`"�b���И �g��>��<6�\�bG�l��I,���W�������RQ�r����q���&���U�� �U+�c/��3�!ԭ�����F���j�k%ƻ3K*6���w_�o�7�� &�lUDe�^�*l�4�>��5A�j�<ΜZS J�w�{��I��1�r:)#6N�I��UVZ���W�qA�Z�BUbYP�vC ����$�}�� �e��F1\k���布6%�GO2��w��}d�䞞$�����o��C�����,Qc�8��{�C�ת=��j"9:Xv,(�h���0.˽�si�o��:›'M!)��=x!��=޽��G�M����Ӯ����3G�7��G�O�}k+8/�bp0��۫>�ӳW�F�G��������h��RظǼ(���D�E&i�W7� U��>�����Q]O�@|���J��#��R(�T!���J�r�ħw��:!���Ȏ +��{9�wf��ǟˢDN��T���{]�$'�Qr�O���� ����4��s�nI� �<�kv�B�� g_� ?�Upl8��{sbJ3#�L�U�8���iY�KO5&�1(�Y����� -��{حz���R� ��F] �(=!,�޷���E�� ��Q�� w�Ѣ+��/�bK�1�"%�<���n ���G��(I�7"�꘬ޱ��D�$=*�\p6ecuB�*���oQ�8��q�4�+���; +Zk֣���(�WlG��#57�o��D��yآ�}��;�yl����tG}G.kM֧W�3� q��=%o*��[�Wz���>aڔ?�2����u7G�a�0F�اٮ��0išs����GM_ɍ���t�b_�'x�� m�^h=&�|S��j�L��۲Q��e �Nn�^�*���B�g�B�w\�[�u�[R#>�*��H3 q04bv#�[L��b��� [�fi�y#�_)�x1��=%ϭ�Qo�0���)�U�j[�i]���`M�O�&׹6�6w���wGN�Ҵ{?5�����ݹ'oB�@ma�BF˽<���l�����4 3c CP$�gpS�%PG���Hf^ +�:�3ro�r?���D�����������T���VHp���P��b � +�� �i%����R"�*kAo�c�5#7����xw��b���e}�������ELU�R ,Ca����FJ�h�}EA�bm9O�z@J#ܔ�����"�$�8r9�W��G�]d��vw���Q�h�����g�(�E�v��t�ٔ���[�U! �a�;ȇC8](�Kc�_:�U���q �P�Лu�GIsZM�����w,TiIw��$�Z��z�z��n��5c����ԸJ;V�G۟�1��-���و��- +3��E���T\�6Ͷ��"��ܚ]��W���6WZ��n��hoGk��jN� 7O�Q�!l���9`�2��0S���!J�2����ʶ�԰l��D���[w����b�1Խ��;��$����elj�������U��Ե��@-�a�~�� ���^E��.� ٴ�}�� |����H��\�/^�.�}��_]�9'����V�'�6 +ۓR��8bϽ��I��N#�����~�R]+1�#{�N�Jk�z~Ͻt�f��YO��RKo1���*尋��B($J�HQA��"�X�������*�����C}�g�{����FI��\���g��$W_�A��d��r���'� +���[Q/C�q� �e��m� µ7�� +�����{���%f47�����C�?�몦X{J�0���A���,;�a�x{`O��Y +Bpa�Ũ���'#���u;wws�0�MP�1��bm��-�X;��ɼĆ-��ro���BRKx� �şm"�,�wZ�S��W$0�;#����Gk<��(N#o@�$I��ވ�>�L���A�J�L�l�N�wC�T&��?�;�W���-��MBOŚ��(�w�A�}o��Y,�`Stx~�1�rc5?�9 ;�t.R\�Ѿ��S����1 o��gv��ɵ��Y���1�#�[ �t>�UF����Td:L�p��U�<Ы���������ήh�]�QT�.�b���<莖�Sj?�\o�ܿ���� ���_v��j��o�[��VQo�6~ׯ�"qܾڱ�,M� A$ P`�:[Di�;��]��@J�%�h1`_,Q��}���苟\� G�aꙔ��:���$ ��ByX(��<8A v�Z�yqmݖԲ`HeWd~��|Eb� ^Λ��gg��s�Y �b��ٸt�C�4 ����I�K��뀷�� �E�5�]��J���,,�+k��ia�p���o>=�WQ�a#<��W�0���8���$� m�H%F��;! +Aw����I����#z���AZ lA�V�Ο�se��Y������y�-���{lkg��hrWs�$$?�_%z�<�w�o �h0�˵ 0��?E �>��"��Z F8���Iҵk��*��V�H�:n�m��G�1��Ѣ�6v�\+ ���P6xy��x�Rrz�K����?�3�I��pV�5�������=�ߎ+��"i�LpB��l\'�q� +{�j黮�pV�"����"�LÝi� _9��k�i�ŝ�1u�OwK�VҬ�sk�bԧ�Dn�N�������nZ���~N�џI�5���7��e�&�y��b N�K�ߨ�&I}�:<,� ��t��(��Χ��%b��O�a�oޚ��Es�_[�h8Ͳ�^�w���1!-�/B���G6��Ռ�װ��t���*���!�8��S������~_oI�Q���m�Ak[1���s��6�&��m���J0v��"����(���Ϯ)��E�vi��ݝ�F���+: +� MԄ�}�c%}{=]���a�ϑ='+�C鱎���è�/�(�-&a�;Ʉ��S��d��]�yGﮰ��Wc��H VzY��X��DM�;��Mx7X=~,��~H �OzS'����/��K�BM䕰g:��OO��ۇ��Y�Wt��,�p`��V^� �Jw��pٿ�V���|d��b6>qCZҞB�҆\?�m`�ec���:����o�����g���:��C��t�����{�ޜ�6T��9.�iJ�L?l���9EOo�/�-�/��T�N�@��+搃Q�(4@A�*�R�Y��i7���q����+�8"͒�P���������K9j�� LZ���.?e�d�O�/%(� P�X��T��h#��L�R �ܰE�b���,.[��u�g9^��3�TR�B���׾�����栝�Y%��[�{� %BQzӽf�h���B 9{ޠ +K�Usn�8���|WK5��T+ ����V$%H>��5�vyy�X����Fx*?��D�I�� +&��� ��� g���J��5�I�>\/C� JzE���f�4��u(�N��A�Ғv +ݑ�E����Wo�|�[�_��s�6׍!��s3�0� �dp�T� ���s�H�s�([���� �Imq���Jzz5džz�V�4�4��C鿳�8����{0�c��FL=���!��f}��Vf{0�9�q?��C����_ai� ']��۞�Ɉ���}%)��μ"�Q^�rGx0*��_�X�rG�͇����Q����U��D-���f��j�.����Ӭ�Z_�.�8& ���5� �R]o�0}ϯ8�� +���|� 1��TU��I�qnk���oh��� +�C�d'��c�߇2 'mS7 +-�e(N��Fٰ������(�%���X� <���� b�Þ�S)��>�#|��� ���bG�u{��iz��U�>SE�ql?�B�K �rh�ͺ��`��3�$���G���F��� +�%ƻK*jCۆ��~�xX-�TLJ%ت��Ŀ^�ck�����W� ��m�a�ԆbP��X*��ߚFFY���K�YQ���%Eok�qm4�W�~�Z1\e�d���M���) Geo�[uNQ��@�H,�JB�ȃi�t�Wkk4���T(T�����>�zw�����2�g&�ɤ��;�uz�Q��%�W�M��4�<�N�/Ӟj_��;�a�� @��$�ɱw�U�|4�y����d���-�/v�B��{=W֞��Tx&Ўt%�=��^�.��$���L�l�Ҵ} +����ݢ�r��P�Ζ�,�t���S����`6��Y�?譒��h}����b�a�j|�GMg�K��-#�$ւ:ζ����"2�R���d�Om�s)����ɻN:,�&_J�����s�ٜS\�!M�*�hO��%ب�?_�E� ��z-�}a5i�o�բ7�X�����m���Q +0����1܅�E�� m�j�� +⃦�t�1J/�e��Eʭ�b&��)na��g<��R�5]0wGV��x4���=&0� G�$ ��t���H e�{I͙,����pF<��*�K&$��M����#�z��K0��.�V~eVy���)�S<�(�*_���5� ��G��b +S�L)MU ›���pR�y�ض�����z�#a����P=e ���-���C[���X�r��jd��َK�Ss+�z�NU��_�l�����;�͘�!�C��3�Ú� ?��r� �{�09z��� r/m]�RKT.��UP&�nl7��[�a����r�EZ��=�+�!7)#܇W.��v�Pf��۲e-� ���L����i����0zε"T���@f�q�e �!�޳yG��ݹ��zʜCKp��Sh�-mg���LJ�a�E�=��ԟy?F��A�[\3�A1Tߵ�V$`/q?�����&�a@z��s��Wh��<-�Y�X�A���M�<�?�,:��ղ|��!Ԍ�a�l�3��R�Q��2�ci�g^���������F���^�= +2�jA<�� ���ɼ:]/�~�f<��D��ƶ�S8z��C�"��L��ѿ�TQO�@ ~ϯ0Zӊ����Rc�!� ��^���9q��l_K5���%�d@��Sr��ϟ�9�ˢ� �U�) -#Y����v/��$Ё�aȍE0 �"��eaf��@�8��̤Hu��!|��� Á"�G��� w`�c�b��O���.�ʢD_Z�@P.흐��K�SO B��b�hk4:F0.�4Ub�ہҢb���y����d�u8��*aR(��b� �\���Hų���$�%NM�K�. E����H/IǾ��̠��bt����U�s7S�d�4 St2��XFx]�;s���j@�i(d܄{I��b�3��$�])������cE�����N�f��֢�x[��)A؞5�>*Q�����5�Q�h��c��%� mgJT{I�v���J�Lk@�Y� !��R5��e� ʩq�������FԠ'�@n��V��ߺ�Hf������E${o�Ӥky8L���kx�oߵa�ߏ)犛p�F�<���G�~��k��� ����6�����'>� s%�[���X�n���.KW��"nr� 3o�F&�t��z�+�;��pw/ �h���G +�sp8_��i�΅�#��R���q@�+� ��[ ��/�p���T�o�0�_q��� Z���26DY�nBe��6Uƹ$V�m�(���';����������ᳯ>�LC���X1B�:��w���Y �G�O�VU��E.8$��^��#WҒ)8�%�C�{��DG�:���a��:r]�;�ؕ��6#��mĔu]Qf���9�D�qsXUj�������;"S�,�Z�bꀭ��3�m��$�Z����FH�����"��v2�֟�jʜ�Z5#¥�v�p�eG"��^"��]��|(��v��0� =�Xje��|g����p��)RKm�NYp�kǭ �� �����2H���E����1R]��^���%[��xa3^��C-~&A���� ;���ZRsƆBRT��*��N��} �{���)�ovtY�?�ΰ�a71�Nχ��ykL��?���w���:�!a�ų={����\��� ���wT?-��/=P�jA |߯��<��$�v�4!��RB]�T(�=ݭ�Z�H:S���.�>j4����c� ��.͕���s#{�ڄ���+|�l��Т:j���G�������L+|R!ܗ(��n�F��/�uG���>�s|��[[����Vh""J�Tŕ��W��OU�Џ� �w�؅�X����\e�V(��t������EM��2�NV��;K��=_'g�+ܷң�� =�p ��s+��4Ad\�s�edU�wN�+���c�s�4]n��Ӛ. +��Fx�B�#��p�'�Ҷ��U��Fe4;���8?&��8pKhz�P=gl%+Ҟ uc\'X]�*��$iu�\]��]�P�1nc+ &������F(O���,E2$�cG�;(�+\Z�D� 1�|�V4����F�����C���gc�c�őP(�9�c�ᜄɾ�DT�]�f�_ �>9Zket#]�K?�#��#��pRS#z�(������ +ݑ�dc�r�.>�"�;���tV�6�d����m�tP8�Av��I6�/n��B��Z���QQ̚��D��l�!�M�23y��Z��j���bH�/�N2����^d��9N���R|y���ߐ3'�,yL�E9���G�>�������g�Q�ؑ�&�r�l�'Sbe�&�ض��~�^��9B�/�.-�M(��ߊ���kq�X�H�e���w������yj���d���V�n�F}�WL #�V��7�R|�� +m,Cv��V�P\x���]���^�&Q�I +To��9�hw���t�!�@0B�X�ەF�>������17q�� hFT�1�cÁ q���4��u�&�p#�|F�.I���ύ;><��˙��0A�KS�t�Qi�)� !P��$V�)~P6F�! XwOт( ����\I�@f�Y�?����C7-� �1��`Bn�^‚�l*ި��@���#� �f�}̨�F�#�[�[���-����������z�#Q"��!D �w�\�[�5�tL� p0!&,x�1=9g���4Mf(mw�NsZ�f �*i��~bub�ϴ�h��|ut2<(�~�n��qw0�� g��/׃��ݟІ��zw���z�r2X���j�����Y*�9��s��2ܮ� EtqQ-�[a���+��K{OjJh�'2��H,���3���M�l��y���w��4=5�w ��e���Ro�Ճ�1�i��a��)��HewYN�4��6���+TL�.����_�SaO�0��_�H�R4i�Z�`�mH�����*׹4��NB���$�aB��)w~��;��S��H�Kf(���f�)'�&�� �7��H�$����*+�S�?��B��(�g��gqȌ��y�:���:��QA��I�YN:��`*��1/�6�n�E���R���=Z +N��J�Y2'�"��,a%���.Χg��g��2�2�P2�D�M/JP +��y�V��N��bK�9ㄫ��s}WM�n*�$ +�)�c�'��$�K�ln�r�$�d�b��Ւn�\�ȋ��O�:L\��~9�:9==���'A�: p�b����V�F��#�f�2���F�1n�kx��U=�UbL�]�ah��Z��v������x�wn�$?u��]Fc��H^{�������mN{�P�0�5�����C����Kv��Pg�t� 1�!�c�B%����ٝEEɵ,��H\�Uo�ZV�pCM��*%�;������vhw^ +����;��q����% +찷��~m +�}�#���~���w5���\aTS��������P(W�!��5���~���n +��5�i�d�a�~F�m����\�aI�����o&����Ak1���+ޡ�8��^c�Ik\jHZC����] �%13�M(��e7��>������⦄��|tBgj����[!��y6���+��1���H`Eqb� 6��tYat,syn�����M�{t�� '�n������x��Sc��:,�(ܖP(�H�.��9��,zX�# ,�.F�i����))�S�e�s�@�䔰g�G�n�\�zX Qc1 ��;E����j�l6��܉'�\+_U��H��Mp����E����U��H���wg�3���t��M���/��F��j��S�OF�vO����5������:5y"��'�ON�v����d~%�e�i�Nf7�[!� /��{'/u�Ӕ�;�����Ak1���+�ЃLB���&165��ԥ�@�j�Z���x�m�)��e7v ����6b�͌�J(h�''4R���������񬺺�p�o!*ژQQ���6�'��08\���0�c�H&�&���b�$�u}֗ }�`G�S�.�3u$���⺄B\�F���s6�ug,z +\����R�M��)z�J��eyt9OP9%��V��o���{���-�P�*���OM��_���N1���)F�(́�˒@Q��H ��T��d-��5�e� +�^9�@��Tߍ矙�-����A�R ��3)�c�;��n=�ڍp�+S��'��N�:W؊`�Z7'5�bY�Od>ka��é ��u��pЄ��J��%���ƹ�Z�1A� �5LjR�%���p�0-����ZI4A���B��� N�� +�Eݷ�������ja�s�P ���Y�A�8�-I"H��-�## +�NH��\Х-Nd4�*���6#�tp��1��B�X +�'Bާ��H��Q$���V[�lh����J|d4YH����\9�JnV�/�,-����Ct�ދ�{�`U�*k/ �5Q\u�䮓&�>xXFE���_�i���^o����9z��� ��lC^-�����i����� �5�a�{��g�2�j�5~� �3�`�Z�jkl]���(>nǣƨ�4��V�>궏��Y�.Q,��<�c����`E����K2�<�$)j���ܝ���*�)ji�������4���X���`F��_�=�g'�:�����)|,��9�U-��s����nZ1��~�Y�A�BiQ#E-R�]$d|�r��53��w� ��F�Z���3�'_R��B,cW��ӕ����،�����^ YV����ፁ�Ɯҁ��Q�|�pl|EV��刳����p:�'\[Qo#|Ì �4f�IH)`Y+p��:+����Ġ B�C��^��w�ǚxk�S@ +ha�q�=>�ߟ����U�[���9 +�{m@ �Pf�਺ �L�[�d²��@/�y�?6&˻�"��H�97(cy�\���9�H����ïI��=�`��~%��@6I�ȯ���|��b�����Y�% ��e�9�*>`����u�QMk[1��_1�lcbz��֩Ih�����e���Z"K�j��P���{���ĥ'�jgv4����3jv���T�}�����ᬚ�*���KA#�!�Ԑ�y��e��L�Ye� 7ĕFƇ@�� +椑�cY�1�yEń">rˊy9^,�Ϝr��5\���j-i9�$�yFӆwR��A��$ݐI�c��T[�]��t���|ݭꍙ'Î +j){-����|I�:�K��򤊴��1�<�mz�yX�U�Dcm��2E�'�� +r� +�д�u���$�<N�J)̪��1�ʗ���?�5.Rߞ�)�hE�|���1�l{���KOz";O:�b߾B�TΒ��s��\��p��`���Г�z8�6I} MF����LJl�k\l�X�ɋ��1���� +�bo�PΪ_�o]�AOA ���+��p%I����� �]�q��L��1�w��@Ɐ_��:��>�"l��ha�o��I�&�� x�,�9X�mQ�k�42�Eʟ�w^�s}ܖH� 6�SQ�̖H���Ut=Ć�V�m�5T0��0�>SʁZ6Vp)j�m��ȟ�}*PO�����-�Q�:��UNq�� +����=�˧Ͳ]�So+�X~���C���.U��c�$[GX{[V����"�ԘF��ˣ���25�+�E�JG=7@G�X .,x��)��"~��6�k#��;�{� �&S�m~m��N�1��~�9B ��OZhD��E�� �r� ��خw�U}������~;���w5Tx�d�D[t�]�*ezq<���| Q�����j���p�g{bV�S��Aq�q�2�>���M�2�������)��fܲ�a"��U ��&� l�p%k�ˮ�ɋ�Mi�@�zJp��#��c"�Uik���S�D+�&r������?��G�}0 V���ًۨ:��қ#\���&�5�ZG���a���Y��1]��α���1.Y|��N���Sf/Q�2P�2E7���]�S�����̊).�/������,5ś�A�l�/��S\��]�MKC1E��wiKiqݪ�R� R�˂�y��`^2������Uۂ��̝��]��` ]�v���Lrs��Qߠ�7ς���E�������ј�|(��W��� ��O**��i�9������e�D-L���gJ9P'�� +.E-�i5� |L� u�9��;�B�X��X�ȁ��L����b6YͻU�b�bg�oUرzhW^R[���Tyd�mH�u���e���Y���i����1.X��1R9s�^)V�� +nr��� +.��|�]��N�@ ��<�P�Aҧ���8��%����z�ON��(B����|�[��B�)Ti�=s!4��̸.xH(����Ǯï��|�םU�F����s>2�ORc���%���D�Qő�Nȩ5�4#�l���W*$\c���B�JM��������aX�s\�I��夯,C;� ld/��J�P:�ש�L'�g�-J���p� ~4�m#��P�KNu=O�7]�AKA ���+��C�H�W[�Z*D +�(�t6� Ng�L�Z��.�m=x +I��͛L�Ϩ�+4(*��]����pbƕA�W� d+��`���F=1O� �����^"�!��A�S+�f�s>���k�آl#��%��� ��3��ac �� +oZMRN��I��д!���wt`G�86IvV9�K�@�>�����r�xY/�U�1�V��5���سzhg��V���lyl��Q��V��2��?�6<1���4]s�v;���&���qU�f�%��?n��(wO?a�>�vءi��zؒ�����-.�1^�N̏��V�o�6~�_q-�J2��gɌd+Vd�:�% �:YDhR;Rv�5��@�eYv��zɻ�>~�����<�!A.ah, ngv���\D���pC�+R!�����§L��l�b�� �Ef!�ܐB�E2��d \2Rx=��g ^����X���\�z�:�rԹDgL%���$��d*�;M`3���x�� �P��%�B�r�� ��������Ϸ.�ߘ͘�53�Sbaka3�n�F��N�-�[��G��1���xE�L��q�w����f���> c͸o� #�u�����δ�I(�vg-IT���͍%�-pɌ�_Q!19�����%.QY�Կ����W�����3n�n>'�b�D���A9]̥�[�ٌke,܆Q�}'N�ӫ�&�p]C���۽�dIrC�‘�wS-���"�J�� ��U��{�������mFz�(q���Ǩ��{i�%��T �Ɋ��Ce +�{m�Z�Rp+�"��+�}���u������L��Cݒ"���ш�)'�P��s��~�b�d$�i��C��:vZ�^�w�}��'y��� +H�r�0�F1j�s�z�5���qc��1 )�LCA���]�*<�E,e��.3���@�Ή���h�%p�m�����=n�6���%*��fcQdʂ� "�RŶ��J{�R��Ғs�-��挢�L�R��0)&Pp$�b�wy�V���k[�5f2b`O4$L�X���� �m^�RQ*���@�uA(�]F�J>8E���APj�MP�D)�����a|���R�ޞ.&���r�5�v[���b<�EET]tfɊ�r��ɥ�e� +�*bF���a����u�z3H"jW�(L?���Q$�!aT^�� �[�h�!4ua �К��h +�d�oЮ��˥g�������E�o�eD��줢)l����T��h�>�M�D8]L��^�R�9����E�χ��� vR��@)G�C��~�d_�úqv9� p�gxn��^xv#�֢>�>|�g�h2�<1�����f�a���v�L��y�;I�+o�V���G�Z}���<�ڡi�������0Cg������m� >ESZ�;����� } �wC �;�DE8kG�������j���U�@�w\�w�O[_�Kz�/~���nx!C������MZ�����|�z�礥�nE�p�uѦ����W�n�8}�W�.,'v��e�)6@6)��/m��X""�*I� 6��/�u���ˢ~����9�3C��,� �0%}� Ճz�P'��w|��|N��M���|��#L�Ϟ�~8�w�!�O {D�$���|Z�Ex6�;��(a�'�(�T�Y�!�Rԁ@X!gJ�i���n�\�Jfy�BX���S"��͸�E9B�"� �K3�������RCa*! +�DBD�] #XR����%�E��|�12G���cB��n�~�҉��Rsc�^B�35���LP�f�Q%Rd��|*���������9���S���x^�)���Q*��8>8��@� Ϛ��]��#"^L�z$e��q ��'�������� ��iJõ懇�3�D*��7�N����Ό���~ +I��n�|�3��V��0\����[u͈��_p��Ii ��`\�^^&:�|�XB� >)P�d�k56,FeÜQŏ��H���� �������l ����x�Yl����KD�7���I g�Jܴ@� ���mCg� �p2���������MZ:�.TK�q���v��Bw 2��2k#Eט�q>G��5�5W�]���0: әFY���ѿ�@��D��n%�z�U����9�����jF +T�`��ݯx��]S�-;Fѵ�*����N4�� +�usG��D�m����p�s���� +ٯ��iq����F�t/����߮��<�)��F�t�>���]���o��a��ڃ1,8�Z֖I���Re��l6ڝ[k�[y�NK���ihs odX���5���+� )�,�Z|mk�ֺ�ȩ@���9nT�W���5,}x#�`���q�����t�c�}��QkN�[U�z��~G���qx���<"J��ȱ3���;�0K|�P�Tx�0�\�aB��6���D���W���H�� �Y���t�.�}"��M^�IX���~5�_ �a��V?l�4{ �&�*������nn&����{�P�% 4��N�{_�_>e�?��AB� �}:�z~�h���E���~�*��9pn.�Ψ���LP��E�}?�ޠ�V�˕9��&%��yTʬ�ҖT � +((�ԡ��f�{hZ�_(%���c`孼��O�8���O\Q� ���I Sf:P� +�����m�I���P�.��ʎ�:NR�v��c$4�����{��y +1�ı/$'� �S�E7���lu` ��D��$��q l +�s��u@c���\�0�����K���}��.�=���HH�(�� s� ��O�<�,M�BDc����g�qa.F8]�WEX;�Hf�����a�Ɍ������oFLj$8^�N�>T�{lRJ����(���-�}��;�(AB�$�s���f� �L�OË/Ї�� �ف������.O/��pr5�8�>�6�s�GWW����? ������/����x<8?�>�؂9�L.&Ї�Z����`\b��k<:� �G���`t6T��U���ōjײ����q8�9���Ї�6��-��8�����EH2��������I~��P]d2�d ��c��l���x��J� ���J6��fX�Ix~^�[�R�6~��a����H�A�8�-L�+���V�{Z�m�([� 5uj�p K�р�����gg��H �O� -��}m�-%��-����I�eg��#@�P&JpG|�4*����rZy|����j��4��V^�sF��/�/�Vmo�6��_q�I�xÀ�K�:]���Ս�C�t���$KRv�!��GI�d�y��E��Jg9U�t���9�, -.��+ѹ혩i�ˆκ�U�n\Z��SBm�T�k�f�g��C�B�.���=��֔\��u�*�V��hج�;#���ott�kf�t�'�/k���!� �i�k������f.���X���&m��Ҍ����98��R!y�3� -x�k����(�O�1,����?�pJ���Q�~X? ��=�ؿ�(�.`���j���"�V�~�VZI겅1�����m9Rw7��}ڀ+3��`YK���)�i�+��a2�(:�Hު�q���=����~�zt�-��uO[yc4l��d�m ���F� <:5/���U5�ͪ�b6@����^���� W�5s��֜��]Έ�W�����>xw���CI��C}��0�����u}� [ٸ� /���bيʗt�y���:Q����.46U�� >6#iQ���5��줭4��ϑ��IT�S��f�W��7��A'���K���kAI�%3t"œB��Ǔ䱇sI�\�#e.S ���F9�*Q����ɦ�+a ��V<�4���ZȒ�J!�n]nԦ����-�>H�����l�'��%�����gC[����ֲ�ж�T:����� �{5�a�<��:o�#� -\�>�(GH����� :e�B�Gh�c�;�Zmo�6��_� ,v�~(���4M�bk,m7 F�ca2剴Sc��)J�D�J�~�Q�6_�w�;�}�z9_��� �X d��Jn�LL�^��{=�G>�cAfq�����$錜��5ۇnq�.7Y|3�$�(㌼I(��eR�W~^�����9��BƔ��l�2�J��CЈ�˄�@ByD”�b�+�f"_�]�9gd�J�7���$�+��ق�8�C�(��cv���������E)��JrK�b��b���z�h�����B�1y��t�ĒB�ٜfү -����J�^<ĕ �2��Z����9]�|�F�6T���+��q)*�1�R+�b��\�t���?�$l��ߧ�������9��_�� ���'�B����F�׶>�����2�̿8���v�{�!��z>�=r��)��]�0~M%#;K�f��N�k���IѵV[����J`�p#@�U(#`��2�4q���$����z2)f� �!����w���o`�����DbE�3�VLǡw~�h!���� ���X�*x��zaA�LM�3_�F�����k�hf �]3����O,����0���4��8��`�e�Z��3;��``�:�)����$ 0(���hJ�OԜ���f(8��������8�XSAP��Ss�M�`�m����T_�Z �@�Kƣ�y�DA!g`yn<#F-�>�o -p�w��'�.�_)�� �H1�ֱ.�jݬT���+� pV������ӣ�XR�G�����f V�C�RAW�� -�aCo�������6\^O�.Ը���V���� �����u9TtA�rs��̐w#r���˼u�%BPG�@�T��fAT[��^\Z{�h8�w7|� �L���%�(��W�<�O/��z�F��()�]��-��-r��,��èOiI����ElK��aV���*,�i�P�k�&�gj��3z4�B��8dfg�4�:�i����8��+��� �3�:%xV�b�'�����^A��m9�3T�\����|��u��D�'�� ��=g~�j�]�x*5�$��©�����IZ�zs���xs��N��|��N+WL���u�&ήz��)��x؛�����g�7�ɡN.C;��ϖs���m�wj޲|h��B9��sz�:|�����m������uP�c�h:�nr�7�\���]��� �-ɫ�Ǵ%x�Ȱ`���@��}`2�1ZWh2�e=�P&.��xX��{W�N�ы~��,ƻ�g�& C*�4�]�Ol-�@*P�'\t*��S�(�����[adN�����W��g�i�^���|%X��\��֪�ߡ�MPb�Q���c�,%��e ���|�v����_�����!~T@]������WW�#y�٩7��% ����@�]q�C���N�!| �`T�T &�Ak���Z�G �G -o�x��W��r�=�(�q��T��ɺ5l٩�Ͳ.z����p|X���Z�������;w�mYz���n�k���Q_T2m��S>�Q_RT��� ��\��j�̾(�w-й��4����;-ݬ�����OZp���ۏ$�2���8�Ss��E�NS�g�Q;a��Z~�S�G�[��t�*S(O�r���1�jVIE c�B�#�2� �R��媱0vן!~�~�^�;�N ��|$��yk�z>�H���Zڣ�N\�lZ���:RW�w�hW��r����]-��`"�Vt��%� a�� �����$t�;B�jL@��e +"Y��{�*�u�k����� -M`�fm�V��fL'y�Ӹo�}��/>-�� F�$�������T� �6z�,Tv�G��-'y�PIo�5����.C����� �X��������b6�'���;I7�n�b|������Fr�α���4��|ƺXpK\�Q�X��bv�����0kg�aC���Ϲ ���ԋ��`z�� �rA��B�A:���>�Ϡ�2*�!������>�摢7���)��'�i�щ6e��3>^J ��dx����y>�����%�� ��{����v�./�߳��Q��T�o�0~�_qBL���Z��UTC��JL}�&d� �l�vB������)d~H��w�}�+�O*S�`�3�����vm�����8 ���Hy�@oŴ��}�KҵC�H���6��}�k��9g��50a�=�4���#X�˙�/X���i 3R�R���D�$lSX��'��l��yN� �C��D�»��-�yZ�zy��������i@�n���A�����[g�Es2�ߟC� -�T�O�0~�_qHLIQ�� h�V` m��<"UnrM,R;�/ ��9MhZ�4�$�;���wg_\fI��0X22�9�3�ï�sop�� <&��R��̈́!�K�&��>�]�Dgk#� ;��(���P�h�…��x�����0Å�$��[����m cf�:K��P�Z1�EN���F�a��)�t���e�j��J�Ԫ �N���XV�~�M��g�.UU%��"i7XA))amunBd��)y�)�B� 6Oa��S�ȹ���E*K0������+�2W��aI�g� �L��?���HE�}+ҩ.�Cx�`��X���jgkK�z��C�t͜'��cW�DhD�w���Zy<��_���^�Bx�ᢜ� ��Km���� �?_p����J*��l�}8ݩ��:v-��C`x�x; -�f�J�Ǝ�A�`�uڂ6H�izl#{#ag<7*:�Џ%���Jb�X:J�.+A�_B���78H5F��-= �6=��3����",�Ղ���:��֭�(�)m~A={��źw@i�n�e�(MV� �5��Rl�h��������$bn��]��w�ڐ/)B��>���b�~8�����6�u�xWX��Kt���)ˉ6͜�ķ�jN�Gl���ʭtp4����Ot�F������s��f]]��+�w�۟F����w�=>� -�:�� 6G�y��pڅ�<&C.�/���|��>��GX x�� �7�uS�n�0}�W�J�H��k)�,��J����.2�!V��m��؄4��/��r�̙��e%2�|m�fnv ӽ�A��4=h�4��G6(jJ��U�����*Zy�B� ���;�v��v4B��J`�V{�"�es�B�ִ�e:�Z&Rr��u(�k��,c�B���B]���󡜸����s�u��Z�~D���7�jEM]� ����Uh�1���ͽT�<�=A�׃�ۇ�pz��{>����+n[������x� �?O���ǿ�����7Z*��J��L�����8�tK��2�,��?���R=s�*h���a ��X��ON�ڴ����T>���,>�i*e�k�{o��EP�N1 ��+|l���s �T*!TQ�Hț���4�boB�;���������&� 5ـ�,�[y�S&��N�dd`/�34>h�XR��t�t�X�|*�� �%��T�a���U�/j�Ö*d��Z*0�1׋(�@�0�`S�êVR�_ûT@Aӆ�|�ީ��Yo�M*;��tjo��p�{\/WO�U��L -�����E5�8e44��XR���<1w���qX������1����rg�X� �����#?����': -Ś����|�o�Q]oA |�_��K����&Z�*�B��sr+6������w��\��_+�N�Ǟ{�6��V8���W����p\��p�Zð0A�AQ���֬�L�1�aKf�F�!\�Cxg���&J�˺�����k��(1!���ĥ(B,f (׀�N��)z�=�{O[�E�V�={F[�ѱhu O+�w#�qJrk����������u�����Q ��6�1����f�H�5���©rP��m��y��qQ$>N�g��?�CRv&V�o�l�y��}�C�0c�_�B�řv:[�?��we��rn �|������}g~�1���PU��_a9� �ţJk ��h�V�"���t:]b��1���֙O.���g9�rٸq��%����G����%�E�'�7����>��qH�W��X:�.���P�qt�iц����䥿�q�T�/�\0����}��]o��ݿ���V�P�c_'v��Aʵ� V�Dx�����1�����/~ �k����%K� �3���ptoζZ�(ƌ��Ѩ�-3���n|z0y}�^��[��5� �g�(]�ٖޓ>- .���Ͷ@�h�.XB��'w�9z����e�}�"�GhA�8/(N�'R�����;�I��@���҄3�,�����W)CŖ�u�|�Y]@�4"I�yM�)�Ⴆ���0���A�}������GAJn���=��h^�EV�[>�7��%�_h�lyr���3̇g[�n�oR"�e. ��@�O��/�S9�.�H��6��V�`�y����oW8�;|D� 9Pk�&,�D��/�2 Ӥ�a��W��s������&�_�w������_�%�9�n�>�|��=.z�S�.L��zm ��7/c�mN�Q.�U�ε>&���Ɏ$6��>1�����c�j����%7�N?��Ry�����{��Ҥ�l�ɥ�\���J���y���� �N�b���EnB�2I���|嗲4#E��ps��G���4�u��v��|N#̍f4>ྔEV�*J�+ � �0yt��� @��l��}bkύܱ�>��ӷ�'g�ʧ�� -�����KcqGb�S��ͩ -�1aLVd�+��<>j��&����>Q��YL/\�f+��$Ks*�g��x����븝�]Z���Trr���)^��˳��' -�ڀ�/�ҝ6ݫ���_������~��� ��)�u�zO���] �.(dB~�h||��y�5:���LJ0{r�8n�P�i�IN -MK�7�$á�n6@E��О�r?2B�'�ca�Ⴊ�Q�#naX����Z`�¾C�-~k���_��u@��i(��� �/\i�rWdU�N�����5��� �Lq�{�m�Au�� x�^͏�`W�1S"�d�w�Ț�/�uLn�g�J4E� �*�Xw�;��{!ӵ��[\;O6#�� YTx�%R��+�#rx�:+�}+�R�ݻ�0�%��/��?���! S3�;�~�u��o�`�r�/)��'�%z+6��-�>H�lҘ�$�x6�Sy$�u�a[��- -\���Z�@r����:=������u�������]�f�d �p=�H���o!��i�-�1�yc�{g$!����r�c�I ��Cnٚx�a�z�@�/��C�pq�xl5��T�H���#n-X7}�N�R��[�^����e����[��Y���� -������g؉����՗��QSw���E��h�zT] wx?�$=�X���M~8���)WV�G`.FB��Ihql�#���juH�Z[��$�?!WV�ա1� �~�!W)�r�y��_�J�g������ sH~TQi�,��X� -��!�I��Hq3m`�w ��u��(2��A����2�<��і����/|+�2\�%��@� h�c��Ws*����9߄��YqȞ.�b��Q��ˬiA��Y<(6��6�؃@�1¿��|���T��:�m�?Z�8'���p˥�O'��;��N0'v耓�z����|v��Ͽ�"�ǻc��Ľ@?�w��+]�b��mM��LЙ]��I����P���U5*u!k�^��:�\3�(�R�D�=�U�b�t(@� ��К�&�� ����'�U6���82�6� <E,>�}�83�)r �Ww�{�mȍ2$� wʜ�;7j���71Y7��\B�)rϸy��O/�� -��2�d[/[K�2�G߻�t��v�zK��G�-V~�tsUy�і�JVm���=E:�^��E��)�� ������1T@ }��.iL���� ����c�� �kjT�j�i�< -�Lk�fG�hn�##��zp��T �!�u\��ԕވ[���Z ���7|�~���3�)r� ~. -�6�Y�G�z� -���|�mk�Vl����3���I�mt�`����tM}��rK�����:#�A'� M�5x)7c�#�?s��,JO)��֙m�#� -��g׊o<�ф3�9��s��7x�FkOY��K�=M0{��b u�Hnu���M�U�#\b��v�"�35-��U��u_n*А}m2�&��aovY� -�q�"��J���U�T����kkS�l��C�p���bš�����Dq�"�������������D4M�Mv|Gy�*��l���@fE��9�/��N��_i_ͧ_N-��B�U��ۇ:��@�|ʕg�a��>,n�]TJ[�S퀽 -��/�(TJ �M�ݗiʓ�*0o��v�Lj0#rT-DK<[9��^�@>t+L��(1R��~���^jH�}5�I*�h����!SHD.�8*��X��.E���M�U&����tߕ���.�O4��2H�l�V�\������z:Љk&f�Ù�E��h�HY��� |ū�S�����|e�#�U*��@��y 6����w� ]mE�g���e�=a��h���8qG�{����N�N��엍���W>�y�%�X� N�V6�� ����|�2�+��Y�đ��ٴq��"��#���d�_8����!Wm\�2�g�ɥ��2g�ө��K��ٰ�=p����*��~�����<9�zeX��5�z�C���ύ<�gz4��|�g�/ -k~H�ǒ1�BPS���3������|�s���1� #�f����e�8Z`����.S+��ҵ�ؓ"r��� � w���ٳj+�~PD.�`Z 5�i6�b++z�DH����7C'K�}/r�b���9�q������=�K�$ �Ҫ�C�M��h��\ɀ�g��j�ɍ��v�t -* �B�\�}�-�\q?�F �z����U�v�Ҫ��� ���� =GL���� ������ {��f��Nҏ��x��=�F;��r; �^�Hθ��V�Јp�M������7 3���x�7��a����qSڳg� �B��h�^/Lp� W��J���T �RmIވ���5gF�G��mUJ���t��Χ��z�+E!{�c�s�{Z�Q��s۽@���!�Y-9���n��j��5���$+�BQUm�fD}����~NC5�f�`�a�G�aO*��J�Ji~1'�I���� ���N'�������%r�w�L��w/�*1:���[�u"���� -t#� -,���wQ�p�`�qԹV�c�T�dm3=g��9���<;஝A��M�P����텏)��z�ZV -�;�˷ui�0���L�|0���� -�Mwꋵ��|l2�0~�B�H����O^�D�|�2�]�'ꔀ ��^���*�V���P��Lef0�j.)F'���3�z@�tR� �KuŊ�Y�P�WV��hE�s�%�I,��ZC��$ �7Z �F�z���][q�Rov��^�LȬ��P - \l�����������1��`�&@-�.�i��E7���~Ą)�[co���#��s�ŕ�)�gY�}]Z��,��C ��� ��VH|�jSXk�i`�A��蟱�������Ɛ����p6d1��=@|���ï(*�K�4���RQv�s����fem�<{E���D� �xU���@U�b�@ͱ�\���Ԭg�8v���H5ST���\̀�r5��[ �z�l�<��Yò1j@�ݑ�ImE�.Mf� -� ��V�@|�p�@���,$7������M����&`„�������.���z��c�i��q5����8�~ ��=����.�򎲌?o�0�T�_��g��(��dC�J����݉�V1�7�讉�G��1�7��ķ�Ӆ�XNēb 2 u�؋��`6c�C��C��ϙ�/����Ej����8i��^�j�<3ty`jXs�Q��{<�����c���ρ{lo��^MS�Ľ�0 .4��P��æ� -pm%`a��#�Ds ��[����6��&x���o�u>�u�KO#��/Q��w魫1����׿�\"��W�"��ߴw�!�w%�WZ�?,`~t����qp2����_̂��җvi��'.&���0f��C*o��~��A'�k�}R�P,R�l��@{����p�֛ܳ��"�z�����ae�Q��YO-���#�kIb�;km���D}�p��@�(�ٲ�ڨ��1�뛠Fh��St��Ν\訅��p�� :� _,wθ�+�1���K^Rw2y�yB\�����y^���N���q������Z\���DP��B#g�&�L�4n��v YX�"~a������ 0��̱�J�M�s��H����*F�K ��l�� σB���*\�w��E�=݀��L�����x��C��_�4� -8���~0��;�b7��{�ۢ�D�>�@t;�).�=t�w�b|� ��A� �P@���.� �˹���$r�7���$"6�>�\��h����"h�o�h�or� q=�Ɓ��9��=w����6zoE��p7� -.Օd�Ҽ6��Y��:���$�{°�bs���~�F�V�-��}XW�6J�ܽe��VO ^ʻ1��w�,��#��$�e#�栄St� ��@HC^�ZDZꭱ>q6���MD�5�u�:ʗtN_2A��czX*gvշݷz�a=����HG%��ʽ~�wq����"��VxK7Uu$���y�f��� ����/��c��5��D�3�c��/z߀w��AB'���F��-ɲ� 1�i�w쎯���ޗ�!Fo?�{�훜%�p���' �~ir�C�T"���������$U)������r}{�|����俛ۃ���o[U`=b�Ok~d�����/�(��*J-)-�S��R����b���Լ�����tu�xjQYj�^iRi^I�^r~.L�47�D/5�T�+֚ sHM��W�OK�*�(PPRV�p �w��q�s�uUVVRP����QO�0���)n��֦�4�aj�:�T -/�.ɵ�p�Ⱦ��e�}gZ` -�*'>���w��~��ye�$��~L�h�{ ����mǗ�����r��_.��Gv��dq:?�z1lU)k� ��[�5�*P�6d�!S y���߿͖ג��k��d�����p(��J=�,�'I�����QS�K���d��T��0��G�Ex�uP�[̓�S��#ѫ�|�-eeJ�&��T,HPǶ<�0*��jb��O�3J��t_�ζP���B�!�J�XwN��Q!5��-����d�L)�؊&U��Vq%�io�v, -�j�ț$OeMcȋ��$��'���.f��Yv�-V�`���ƒ���tҁ!�#��at2�W�İШWu#sd1j%�Nd��N���Lse�M�����4��� -���њ��N 4Ȁ⎡-y|@�1ׇЭ�z_/'��g1��i�.�``�TMk'�k��s��m��I-4�.9�.�6�m�p��H �N� �N�<������#�!����� �I4]t������q#�jsHg�!G��U0f"�u� ��ëu�p%�=IY�#�V�a��Չi ���K��ݥ�����MO�0 ���&R�݀#Z;u�m�>�!햵f �I�����deB 1DN��<~��=Tv� �"$w�[������L��*�x� �q2�<�����(�\�OG���f%3��8ž����,��a�5�X��x���*�/��A -w}����� p����4aBRZ��C��J�[L�O5}�< �.��jk��R8aL@)� U��e��7�\V�QaH2���@��@a�N�������Ў�`�@+��;�G � ����KĜsE��K]�D*`и�?�Ro�/�^�z0W�7���:�y~߷�Y�Awnpz�F�F��S�vn��~4�w�ME��TKS�0��W0� U/S�AǓփ:�u���$4��R�B�*� ���؅,�[YE�h��*c��E��΅*2��|wvŮ��EkS�K�^ٴ�+���$i�&n.cm��b>?O^�:(1U(Q�;m�-n�W.c[���Y���V ���q�h��.��B'�]�z�p>�X�"z��^0Rę�Aq�Z���5~�E�[O�*���Gν� x��^�!���N0͵�Xr��L -t�G����ޱp���sF��Þ�̉O@eiԒ���@�ٟ�/�u,򖎆� ��tϳ� �=��km��-GLt���'�����_6eE�����U �������E�%���Py�S��SѦ���v� ���46��in0��Ѝ�|])��'����� -D3� )��� B��+�iO­�Q�7�2#�V�,U���՘$*��s,�W�Ξ��z�ߖGBMB \ No newline at end of file +7W*�EH8�F� +j +���Ӈ�Ѥ³t���F�֝���]�3�{@��l&���UQAdI�~�߄#���IF%Y����Ljẖ̸z���ޞ.�+�g������ų����A��bΖ%���-���ʠ��fX��"syi��z�N�� ��k�h��� �f�z`N�Ib�j���K&*�e������t�o�����6� ]�yeM^Y�WV�V^;ww��r�|�J~ո4Н�L׽�i�꩘��j�{��#t�E���@5�=]� �8؃{��L(��#���e����rVkKRѸ�{��Hl�t� ���RO�����ƛ�7��<%�w�A� �Nْzn��q��RD������s��[YB};���f� ���"E-@O�=��ܙ=X�G@W�Q �%X��UĄ�5Dz�Q�jn��� ��G�a +}3O�f����h4 ���;�~�]˫Ed�W�[.��~ ����y-3��g{�x��ߌ�W�Í_į=�"i��7�Q�����6e#U��ݐ�n���+$N]|}�����ey����s*�J���yqX�e\�mY��׈��2�h���7cc�=���:��L��@���A�*y��Ǐ�~-���rD�M�S�y^�X��wW�r Z5f�!��~[ B��^��(L�'XS��nv�`ɉ�:իni�Um�Qd���������n�߸���^�[I�q�ϩi.�t���L#�i����NY���ZI�4������m��f}�{1i����z��Գ0k+��V���z{wM��gE켨%}��s�<�(n~|B�?��k�z�)x�t��3&�v�+;�K����� +���@�����H[Z ����rf�z��-��e{,%(��1��@25B��x�3����% [���c$�E�wKo�6l�[��\����Yo:�\[?jT y�CVk�j>7صu��i��o��ބ4�Ѭ�XPQa���ڒ�WS6[��[���Z]�6S�6�����\��L�.Ԗ��+�>��|�+{?[���X`�l̑�Y�b����mE2��"q�����CW[�\�(�kst�����ryS��Q�*\���c����~z^YZyFLj�j���Bd6}fǣ�� i��>�E��=j�du?�Ρ�Hj-����B��g�Ro +�7DXEj�p��_ަ�6z��§BΟ^neº����X_�u� ˹vR�"�"@&��*˶}�n�h����Z�Nr�����t�n����'p9���^ꦪWS ��^�4'q�xa�}�m�RͲ��s��Q�JCA ��W��C[���Vm- "�z$o_^7��]v�Z����V�If23��6�����̃�Y���1q��N�xd0³��F1��VMo�8��WL�4� '�^���$�bt��C�m���"J�Zrd���/�[Rd ���&�o�{3���,� B.������h����μ�ȃ<&�B,$���1C�cxH�/=("nu�3b�<�k�n$S_ѐ�+f.W���c��+fI0b��l��̒ u&�Sp�ȈUN��*�;m��8��>������B�ڤ��Vc�$2���-ν�������A�(a[f!�̅l%@N�չ�\G�䉧X�6c�!a�^.�y^n/�]f����Yg�L�m�����ILQ���d��uNɧT�j�5�4+�ˍj�?`2�r� |J�;'d4)�3#6�ξ��3�/~a�����drN�\VP����?=�C�:g�$�H����v��v`�`⶟�^�Y�  ^T�hI���H�L�p ++�e3Ͽ9���v:�'��UbZ�g�hK��o��/�x4; ��$G���bQ@e��r�=)7�>x��:W����-u ����- x��c�ĝ4z�4dE{�a��!xU�yii �,�{ +��p�f�����SF������a��K+���KgNC�z[,���i�S��JC����f���V^���X�6L�h�R��q���eɐ�z�&���zA�#��0���bVp�������T$��E�7ָ��O����`����~߿���Ŵb���&���6��[AV�S��̑9�c�t���k�*�����G'Q��� :??���$j^���9��K}��N��O��g����S�n�0��+��K��N�$n�Z)�����$�\�1��{AY�ɩ4�&I�o�k��n���g�L�Dj�=\��,�ܚRUq��J���%� p�[@T�eGj%�'��49_��"k�h�w{Ǵ�V���e�rKp!�JB��s��Gi�g +��}�}�a���<�Wk�gm�zq��^8;2��ݖ�v�9Bm��P]����q� +9�K�[��Wi� �}��$�<���W����C��Y�����[�f;���Ʒ#��i쾣/��E���^)�΅|ڑ�F�0�-=)S}S��-m�l4�� ��x�G"p=~^�~6M��F3|V�}����NqW>��� �fkR����2Uڿ�\ø��t��C>t��A�ϐ#X*��ۗ�K$^��[����H��;Sw��C�Ԋt��5��RMO1��Ẃ�$�C�6@C#�"U nm�&��ت׶ff����^y� @U��em�7�޼����2Td2�D�[}��Lr�~<5��w^���� dd�Tí�+:1�1�)?�_:���%G�O�Ob8C�4[�瓊.���(�1�j��L��Yv�RT������_��X����:�� �N�������:q��S<��`�i��}��_��]�V�`�Pa����U���@��Z�6U�ȧ&bC���:����KdjL+�W�E$��ZO��E`����+]���)�~ �Ɂ�*0��mN'���ȷE���� +��w�LM_�}a����~�aŖ�m�.����l���Z!3>�.�:(I_ P8�О̛:K�b��z����1�%T�A�`(׉ ���P��~��5���Cya��YZ��a���A�ӽ�'��m�qZC�u7��/Ky����o�~ *������5��S� +{�R��"Vn��۲k B}O�]P�j#1��+�Oȃ\�^�a��!�s��B���X"�$�=q̒_4�C�N�]=��.�c��D�x�O��,�Λ�9;28£���d*��c�� ���E���7N1� �Jd\��\T0���=̧Ϗ��D=E�� fr.�˜r�J�6E-�4��M���gzeo9 +��>�-�O�90 ���n��/7�j5SG� :/�Y�a��AkyIC� ��C�3i˒�2V��2�/25f�O��^�Ey;~�dj� $���Z +���6,R����;�7���\�����C��6��Ɏ%'�(J!pW�����8,�aV_aJ�F=�5mN�� W��0(�Hݤ��o���uQMo�@��Ẃ��HP�@hZJT��B%Js�������Z�cST�W�uhs��x;y�Ǫ� 'cQ( *lt����͇l� � ���[P�(��%7t�@���� �-R��'q�-�g 0Aqt�9��9M�� et�j��S�*+�t9�TxS��p$��Z��`��mِ � +/;T�n�%  Ӿ�w��Ϳ��qTkLKT�c���+�g-A���k1��'����B��`Y�,��v#�$�ùtǖV���k��e��?����μ *�N�(�d�$�bp� �v���μ+x �K��.ʿ�a�� +�]��ݎ�FѱW 7�=��'��zc�@Q;��i%�Fӧ���gZ����k����U���2P��hԥC���l|�ы���v���r����W2��g�[Ҹ�� ��<� z�M��n۱ ��� �մ;4k��dQ��%j���e��^��m���Q/�4�N�_�?�SMo�0 ��Wp@��A>���f]�+Pl:�;(���ʒ@�ɂ��}�'n��t��G>�'��/=䨍bL�0iy���p�1�$�~}�YR�� ��\�V8J�A̜�0-K�Tg��-µQ�Y\(�x�hϣ�x�� +B��7���"��+_zt�`��9hg�iQ��#�u R"�1���mH� d Ǖrvޠ ++�u�w7���pK5¤Tk �����$%H\�A����9d1%͸��P�#�o'�3�P�˓ҽ�Q�o~�o��R�= ߥhA;��w�&��TQOA~�_1Mh8J�*jB�Ѣ��>XC��9n㲻ٝI�o�8�8I�>@��f�������,$$:�=;)x�+K��K�u��a�I�T҃E�`R��䂎#(cWN�2�X���i��B�B�=���t1��>N�4E�5\QNN����f��U��:a4;9��8_6�fpF��J��vh%iO uj�Y��=�BҲȻ� G�P��2,�C"��%������N�l$w#�s��}����.&ҋ��^Z�΀��j�B� �JϾ���I��������$Tj�*�L/��B�a��='͗9g�S99������:�@��g���M�w7���S���V�$g��X�y�Ρ��z|�؟\>���)��.a&Ya �L��<��܋��n�  tUa�U� ���4�O���M&�� ������R~8����y% +g�>�v�[�> �4KQlR�D� ���ԭ��*�d +��q �'���T�hUɆ�s�!E婷���*��Ž�3�?�I�*��uz*I<��$w��u�6\p��r�G���/i�����S܏��Yj}�Ʀ@}�Wд����-n� �~�!5>�fgˬuЂ��a��0#�� �r~)Yd�US#�?W����A*{D���� +�ն�ڕ��Ϻ}BG�C�J1W���ΚÛ7��6K]3��g���-� �Y[o�6~��8�Z +�e{�7]�a�.h� �-��hQ#)����>��(�"e����%E~�~�ѫ��6�FFR �������:^L�N'p +�[*aM������vK���̎+�? ��*��ވ �F�?P( ����rU?��rw�"RQ���X��W�^�̷9��$K!�tU(.dE�;.@m�c�X�z7� f�fk.vDQ�� gH$ž�9�����ۻk eS[���HH�,ia +�TmAi�%/D����l��ʜ$�["n��F#�ɤ���,є�ʇ��Ewq�z�J(��#��d.h����?�x�p����oy�����s}�|����2�{� +w�K&������������;��gR B3uK�ԢL�J�$ +F�,����ٚn� ���)\�vL�����sA�D!�$f�~��tO��AͶb�h����!1�DE5O-&fA�!:q%���h�/8��.��:���TPD}���D<�K��Q6^S�8�j���6yt�IEC�'��8�w�g�R�oE�1>�=�i[��4���r��-�aW��?���R��H�O�}d �v p^���� *�P �!@�M��� a���9l���@U�l�Y�OπL�a^�ϗ�@����h�7O�a���ި*]ES-�t���3}J$Mk2�u�ٱEǶ�(? �M�2W�ys`6� �|I�4�D��� pS�`��ARA| �`�"� 4��!�xb���c���j1r��X)t��@���Q �a�D�S8��0��v�x�G���ؖ��R��*�M��✵���!�VK�@�"/_�s���3�0wP�����Sd�l� �4�`��e��{�[�(��l��=8_���r�{�7+�Y��m��6~->�P��ߣ��2��btmqS�s?ֆ��'4s"0S��%�W[�Roz�:_v\�}�w�œ���ռz�+�I&MFr�o�� �a?���hz���w����+X�"K�f�������/� �9�7��f��2|�[�e ��֗� $M�6[��M����1D�}lI 8��U?� ��6w`jѐ�ft@�K�H���q齥�`�U�K��T��c�LF�9!�S[�Z{.�����콧����"�k.�$[�Ӷ�ş��hzvf���)��������0�x���+������;T��v +��/{�9T�[&����]� ]Ӥ׫U�^\\@V0v���mw�k�`F:�y��d�k�!����u�?�ؠjZ�.�10���O���,][{�tmY��8����T>�^W�,�� a�1Euz�9�3��QZ*�^е��R�j]� ,� +l�wb��7s�H��V��|W l�G�v�d�Ωi<_R���+��C!��1 9���EwxУ�f_�Ku�ћh��Η$�1K��8s3�=����5��e���n4���L㬷�s�E��rϣ��_��g.��D��I¶'Cc��ͰWVSM��k���+o�� �C��%a�9�d\�\o!+�� +Й��-���׭�a0���(��{]N������4>�lw;i3b��s��ł�ո��a?������ ��������~R�oc%d�-��?���i��{�B0�����toHz�۟�j����t���/�%�o̭��-4M�{���}�G� �wH�*0�ɽ�PH����B���d�1|��Z�[}+~��\=�x�-.>m�HV��~�����j]�eK��1����mA�K�d!�O8�Z*hA����C��kuue��3��L����/ o���̺˃_6�tTbԏ�x���_}�^zrVJMٵ�`���+��iJa��{��֜~���:��!&�3}�`zOo���Zy��8�8��!!R�ˍ���.��Q�FS�A�q_�J�x���Xmo�8�ί�*P���r��v���M���m��K��8C�c[���N��';BR��O�?�3��h���&5#��b�� +N��0�� [�n �p� +S!��,���8��oA�i��"I "ށ/V!|�L=�%G�*<�,�c<9��0G�)��-��©I j#�S1p�ȊINں��\[�a�K |���PSm3FB�C0�Cx8 箯Fg�o�2 g�QX�2T�������p�7lm+�G�kލ �g����L>����yϵrdsNQ0�OUyU���b)��j���)�+�cP����S�?>�z'8�E�f�&oڈ�������R�RΚ�:kΧ:{q��ia�ld�K��*ע�'X��h B�~�NR����� ۝��Xإ��3@ R h����w�ų�p��&)�*XV�� �k >�����ofEڗ8�� =���H�]jB�Q�o��vE�) ��&��Z֦J�H���4���m ӟgr)t;��&�䔾����GYT�h���@�f��P�I�֜�wT =��޻��b7q�����q����LL���ȌOw�ƽyS"��s7�-fF[f�h� T�~*�)3�3���u�1)CO��4K�~y�:��u���3���9���j�5���?�R�K*� ����:^�/���~�n���(H�C�F<�v �.�Rj���w}1��*��s���l�1�0���Ŭ86f[/�6��Ntu�G:˘�,q� ��x +Q�i^Ʌlt;:��c��]Α�=rb��0�N��N���-�"'����*��[�r�� ��B��r��P�yB`C��5��RZ��ݞ!7at���O+}�Z�� ���C�v���}�1#6x�{ksG{��u���b�:Y�ew�0�h�v�������� ��ȧ�"�P�Q��}؜Y6Z�&&sp �:vpGxO�y�aӠ��w��8jl������Ch��;a>���q�����<]�F�TI�f=(���?��K���Vr)������(=띸|-g���Q{�Ƿ����zj��U�o�:~�_q*Q� �ޫ�'h�����v�jto�*��5c[� m��ծn�������oޙ�@�\2��#+8��֠K�N��� �<���BH��0K��5�� xL��Z�� b��{��%S�ђ�f����7�a�s�H0�X���߸3�Am$zG`*�Y1/H[W|�(GXR��{o)8*� �B�#���D��7!������l�S��(g� ���l�@�y� ����-_G�����rf?�o��q��R�Wg�P�����AH�m�*�G��9��z��fZ�\p�f��D +T�{��b���צq��0��=��QeGa������F�I��q)��Tq��K����p�قS�@܀8l�*Q$U���yʯn��[�8��NZ7�W���2dw�[�q�g�� ���G��Wn�';˪�����+�LFab��k� t6t���-k��&�ns榊�6�]ǼD�[V�Q��NOv�@ +ZtF{9�Ǽ�|!װ�,9��g�s��i�^k^���n�Q���W)����W�? r/����'@k����x�3�e��'c{%Q��sl�q�O'G�&#����0�Z6q�%���h�.��g�]4B����sǵ��0��'���;��K7*� �ˬ? ��enFV�e� ;�zO�����i��ۿ��Rb� +�'^�q�}��W�&�Z�hE�J�$z���ο��H[<��\���{��m|Ɵ�w>ݮ� +���9����_1ǧ������Z�ʣli�ܝ~Y�����IuϽ�ǃFگ�A#�ϺQ����l.������;�E��QMk1��W�C!��$-�y�6mp[C��R(Z��WT��̬]S���n���ԹI�>����e4�e*D�;���L2WV�����;/h} xA��H-:��+�q����S��'�������*�Y�tS�W }�`E��6��Ę���&w�R4ac����{M,φ_C;Bۇ��>��w��c�xcէ8Ad����yw����j1H�Ŵ���4^�����k�K��\j���M��lᡳ�L?����e��� Q���L\�-�Z�+c\�"Xĭ�7�.9����@����+oMz��,��&eb�>�W{Qڔ�S�g�a|��5S�8�O�v��e��|�����Ey�1 ����c�vr�5���6�> ��A��z��9S�����R8����T]O�@|���J��M�׆RH0M�A�PPt9�����:*��ʎ H����������?]� F�a�3)�^9�?��ݠ� +����(��<8A 6��Tͱ@��[�"5K�2�32=-�{8d�t�yn�xrc� +�J`��~S8u�C�4@&i ���l�W���S�$��V�@k%�xeK�`e�8��#�.��~��hT�1N�Bx��_ka �)pa�ۜ$����r'0"C�D�N �C�H7r_�ed� �H�&O���Ry�ݷ�XQ����D|��ڸ/���]�Y�*� �q���ù"�li� �D�Aj�=�+ۅ��\�5\2��C��7�Z�ZpJ�9�,Xɪ�)�.�j%���� �l�;�_�N����;x�I�]\��E�iL���]tvK�KP����l�[���Z�K�>�fcpu�� ����z��H!~��>����LXωS� ������5C��}��S 4�T��f֐�U�SF��fcd!�����Ƞ ��hT�k��-�u�7A��E�m��,�}�|�>w���Ֆ0�����?�?������ڟ�����DY^������p}�{ʜ+�h�{c�>�M|3�2�V�����2��7T׾O�L�6˄��ύI�ٮpc�"a��ذ���V�n�6}�W�$N��SGi�� �� �}�M�,biR��|A�/��-�N�]>Y�˙9sH���,� F��Ð�)�S�dH�O�agpҁxLA�4�"Ȅc� ܧj�g(U���a%bE%ưR�����N"H�-:F,�2!�S�n�ׂ�a�����H� �9��~��׊��m3�Q�[�"�CNm<Z��3�P�*��J�dC����C��m�밗�1uv%f��(#4H-����V�c�TΚ\3�����w`prWK�`�F������̩�`�.V�a�`r��� ��A�*U2��B�����V�+�c-qv1�3t�k;�����*�rȹ3`p�: �������C�ت�{�a�҂��M��b�K��B��-��w l[�5�3����y3AZ!���;��@��RA7v��Q{f�����U7_��A��n����Y���ث�^��ס�*���.��]l?���wU���7Y�������೅�.�-!�2�;%�)U��jWB�ʧ����Rڐ����,��id�u�9����t)�5�NHVK<�j'��h�E�C =��4���sk��y�r�}F5��^p�h�K" �csFE�,�H��]t}��_8}���Z�)X�n�K����.��b�K�D?0��!T�{�y�ȵ��?�e��yy̑���Z�ʽv�J=���J�3e����YF��&�_��݃U�@[�ը�/�����x��#!G�K�C�G��A�GN�EP���;�3�.[{��8�4�ϗ��_�? +6/5�ŵ⑍�7�2]�F������Ih�5�%lQQ����7dT�P���/�X�R9}�WtRP3��8ɾ손%�Y�J�„d�Py��Q!K�R�/��߷47����[��Q�Q��Ea��it i�G�����A��ׁ=� ��1�@�4��u�g�߁D�DEK�'!��wᓖǂ�G�d���F���?�`�#f�3 ���&_8��U$� +��J�棘�6��3��B�q,��w+-��� p9Vzʈ+كH 33��D�����r8����Q���@�M� �s +��ፊ��� ?r�#�M�|��� �#��A�+) ����w|q��柃����fxquY^ޞ^}�MWƱ�-p`Z����Kܐ�m�*�T[ ���ł<�S�OI� ����,B�LO� �K�P6V ��JzB���v���ɟ�U��ޤ�֮6����D�ו� �F��m�HsI���"�Q<�n�Hd֎Y�)T� b��1���^��3.p�4���\�OJ/� د)���'�3rƵ�S��:��=8�1��߲ͮFZ���;� 5���A'݌G���y^B+��V2�n��~v,��XX�D��N�%�߃# +��Wq�d��`&H׌�[u��S&���� �����{��SF��Ă�0���e�uv ����� } �涻����M�;*�(����Ӊ +��E�~��b^›r��'�H���a�2���"�Xt��0�T?�g.�$�N����|��7{� Y#�Z&���e3�o����k�ҹ�b�d��J�ެ2�P)%���2��K�s�4��Ϊd��U?�*ׅ�s}~q7�ί� �� [��:��ֺ +�����x�8��&s���\%�{>(ߔ~�rҞ�&V���lZO���h��k�X���^�W�d���@{�t�Ke��b  �\����5�O ѽE�KL9�&9?V{b돩[�ޫ�~�Q9�Zfo�����,s���4z�‹�Y�ܐ�j<-l- �e�^��Das��V��lQ1r"ٮk8�|� �&H�\2���ut��ro_�3dk��r�݃����8I4�˜)4�T�6k�Hn)���7�� ��4c\�u���uA�OmO�>KʛX�6s�jS�rU�gc��* ����*����y�Cm�M��N�*B͈�I�v��Bnۃ.6'�h�7m�yi|�n��X���c���=�l{zn.$�}����iL�I��w�r��4�Jv�17���7���X�j��1��\onX����V:�<7�����Qy��ތƵL�T�\'�.�E���Ɲ\u����遣���Z���'�X�yg(��o�|�IɄj�a^�</,�*��}0\�?���O�(�S5��>ߓm��!�!�?D��J' �P�X�]�aU?�V��FO��&�-��=��4�:v�����&��S��|�=q^lC�n>���p�*��t�R�~�|��ӏu‡(d��|����EL��@ $����jdE���NW�i������~�J#��d�*�2;����l�G�h]�����2������ť!&}�6��$��;�{j�S+��lD^��K�l�г<�/Am�$�rs'��5=�J��ߧ��E��JA���uLB0xN��1 �ǀLf{��ٙa�7?��.�s좪���=d�Q� ��@���w=g�����LF#�y�,ȶ(R�����e���^1pC,J$<?��`fK���2�Tt?Ɩ�V�m�3�T0��0�>Sʁ:#l��R���VS��§T��P�!���w����8֩4V9�1r +�ӱϽ������[Ճ����T,�]T������8�K�yb�mH�u���e�v�G�Ƹ`E�hկN�rw�+��*q�5Up�̷�E��JA���uLB0xN�h� �c@&����ٙa�7?��.�s좪���=d�Q� ��@���=g�����LF#�{�,ȶ(R�����E���{��!K$<?��`fK���2�Tt?ƆvV�m� �T0��0�>Sʁ:#l��R�»VS����T��P�!���w����8֩4V9�1r +�ӱϽ�˷Ͳ[Ճ����T,�]T������8�K�yb�mH�u���e���G�Ƹ`E�H�����(w'�NJ�l�79PCQW��|�E��JA���uLB0xN��1 �ǀLf{3��3�to~�]6s좪���=d�Q� ��@���=e�����LF#�{4,ȶ(R���=��Uʧ�;��!K$<?��`fK���2��t?ƚ�V�m� uT0��0�>Sʁz#l��R���NS����T���t!��������86��V9�1r +�=��{]V����_uSo+�Y~��Ɓ�C{xI]q�� ��Dےd�+o�2m���AՕP���nqt��3@G�X 6W��������e��E��JA���uLBH��1 �ǀLf{��ٙa�7?��.�s좪����g�Q� ��@���w=g�ۛ��LG#�y�,ȶ(R���M z�2�s���!J$<?��`nK���2O*�cK{+�6�Z*��EXd�)�@�6Vp)j�}���_�S*PO�����;w`GQ�T���9����^����vխ���[�� +*��.�pd��^R[���<5�6$�:��۲N��#3c\�"X�%�N�rw�+��*q�5Up�̷�E�AO1���+��xE�$Ɛ��� ��m,mәe!��nE���޼�fz�]�e�pO�x��z�,�7�I5T��yA�� 2E��v��� +g�<�S�N�3}<��x ?��`J%�lw�G�����D=Ed�zB����޻cp��R���r�D�B�:�sO�e����Ug0�Vq��:�w�8��^rǎ�r}A��d[�ba�-������1.ZT̙����_:*�Z�����Dj)��O�0�� E�AK1����ؖ��U��bA���fgM0M��l�"�w���� ߛ7o�����e�rp�����j�0��M�� (���·� b���7��1�8�M��*XZN��.zV��{��h� ��c)����B�D�A�T���V3�o�Cf�'4m�p�=��$����G�!�)J$+�S�n�=mכ���_5So��A~��F�C��[v��K�I�HR�#��m~>�0���s�l{�7=�9;*�A��R��ç�2�m�Ok1����"������T("أP���&4��̬(��e�����o�͛�C� �3�X�3�&�D<�� 5( �j�v��IgA����@#� ���ݻ�L�9����1�9м�գ���Q�Y�x��2�|��&��SB� +&ɮl%f�1\� �����׽��3��Bs���0D�ppt�̽���n٭��Gͨ_���щ�t�9��L�n��*�8iC�Z��q�H���������P���B���I�I� +��7�S@jK�L���(�؆ +3L +�o��KO��P_�u�Ak1���mR�^m'uhK =�X;�]Kbf֎)��e׻N(�.B3O�7�O��(��$��8����&�$ :����%�W���� ��n7��OQMoI����l�u�(��;�fs����6�Gk�U���}7�&$������ �����'z���=���oHak$�E�^�E�KkA���+�"JΚ�(�r��lo����0�냐�v㱋���z~�\BE֛L��V���Hno��b:*0«cA͞��d�"��:>Ф@�X�t���;�c�'o�'e�M�P^�IEwc�4�l�����\�����3„ +6�\����b�:B�z��޹=[ +B�P����H��L�>��^,7�e��SgG#�X~��‘�A;x�m�� ��!I����u�����F+�����YM�iy���[@'�P �W��䩡����U|?E�AO1�����@<��TcH�HbJw�N�Mg0��nv9�˛����6���\�������g&����td0«gÁ��l�"��x>�Ġo,S�,��7�]���`��m��؟�IE7clioE�F���� �޵;�B�X��X��ȁ�L�~�y�\�lWݩ^L�U��b�eQ�#��v���.Ug婉�!��6ޖu�����#���(w|�I)V��%�&j(��~�o�E�OO1�����@<��"*�<������n�tf��w7����y��mv�LЅz,���S&���O�x�0�����gd]����=�:�<�S�;'�>�J$�?�c�K���<�,� ���f�:�*��Y�e�)�@�:Z���m#��_�c*G��`��[w��"|�R����ȁ4��]�u9_����L�4�z��"��i�95�L�g䱊�&��VN�e�t�(e�f�Sl��N�CҖ��h(�瀎B�26��9PMQ�K}�E�AO1�����@<���$FI�HbJw�N�Mg0��nv9�˛����6���\�������g&����td0«gÁ��l�"��x>�Ġo,S�,��7�]���`��m��؟�IE7clioE�F���� �޵;�B�X��X��ȁ�L�~�i�\=oWݩ^L�U��b�eQ�#��v���.Ug婉�!��6ޖu������eur�;<�+��q�5Up �̷�E��JA���uLB0xN�hH0 �ǀtfz��ٙa�7?��.�s���?�f�eX6� +D�7����rw;�V�Q�ޜ�>0� SQ��|S�O,R>��3�c���@� +fT"��}c�~�-�I�S�3�\0��1�.sʁ� (Z���}���p� +�1�6�z��p��u* �Oq���q�|��^֋��vٽꋩ#ő��/�-�^�+/�-�a��T�T��L��qT�i�/2�*H�(J!����vy2�; >)G+�]-��� G\ͯ��E��JA���uLB0xN�h�$ �ǀtfz��ٙa�7?��.�s좪���=d�a�*<-�軞3���pZMFFxs^P����LE�jl�?�M�ޱH�\��S ��%2��O.*�Q�<�_���clyO��"V�r�L.�<��)haR�����"�ϩ@�nC��o������N�!�)���0��}�e�X�n�ݪL)�$�^~�����A;xIm1 ��yREjX2��QY�]��iU�@"X�oW$ny2��;�'�h����ᨂ��U}W?E�AO1���+��x%I�!��3�����6�Y����U�8/�͛o��eX6� +D�7������j8��� +#�8/�}`xA��H56�xR�w,S�,��)f�����̩D^������[ޓ���Gn�`.ga�]�wFP�0)j��VS��‡T��Q�!���w�� Ga�X�Ґ��ȁI��>��^����nU��GX/�]lq������I��<�"5,� c㨬Ӯ�Ȭ�L �あ��MX� �� �I9Z��"�&n8��"~U��E��jA���uT�HΚ�D�s�8ۛi����?B�=�&�c��Q5y�>�"l��ha�z�$��� x�,�9X�mQ�k�G�3���/�?����x.��l�SQ�ĖH��U�U�8ĆvV�m�+�`"Wc�}���A�X����wME���T��PB��������8֩4V9�!r +�#ө�[-g�ͼ}�So'+�X~YT���myI��.U��#mC��#��-˴�����~V��/,��gG����R�ۛ�M�PT���2��E�_KBA���S�GQz��#�B�G!ֽsݡ�������ǽe���̏s���gT�-�-��Cϙ���?1���5 �-�Tc��@#��1O�\x�=��C���`�'Lm�4�^�QEwC�ikE�F<Ӟ +�rf�gJ9Pk��\�Zx��T��� +��}p����Q�:��*�8Dd�p`:vw/���m�h_u��[�� +*�_U8�zh^Ҿ8�K�%��Dېd�+o�2m�F&Ƹ`E�ʻ��'G�@'�X 6W��������e��m�AkA ���+�!ۤ1��N�$84Ж@z yV�:�$�S�����q ��HO����Ki +*���Gj���PX�?��n:q��gu���($�\� ;�r���$l�ȏq+�q)�b1ł$�rs�_U|s�gސZ���ܲ`��²4�s�� A����$lZˢG�CXè��?�:�I!�Y^�BN�(�I���~��������YC�=)���+�5�.��V<���y���򌧆�1���;�*c��t�s>�*��������Շ�f�*=����d��$B��U����m�n���<�6��a��9�I�m4���y�.�l�n��ϖ'A!�d���n����m�7ώ{�y��J�{r{wE�KkB1���g�"J�ڗ�V("إPb2� �MBf�J�{���.�pΜ�f��]�%t�K�F�咉���5( ��<���YA��u�H#��1O�R���L�%fA�O*˜���p�G����A�x�B L�*V��Z|�C�@� GO�.���/6�E���'Ͱ������AZxNM1��y�����6���e���G&J����I��j�.�ej�]� ���Y(Z��&�:�) +�&~�o�E�Ok1����"J���V� +E{JLf��l2�������v�q�͛���˰d�.4`)�Ȼ\3���p��#�ޜgT><#�"HvΟh��9�)_��p���D�s��0�DZ�yb�a�=5��/�P��{a�]���F�haR�⏍����T �P5!������ E&�X�Rk�)��i&�<����f���W��L��5�z��"��i�95�L�=�TE]gm;��&��̔2A3c�d��hWC�=t��q�I�΁j�¸�_�[�E��JA���uLBH��1 �ǀLf{��ٙa�7?��.�s쏪����g�Q� ��@���w=g�ۛ��LG#�y�,ȶ(R���M z�2�s���!J$<?��`nK���2O*�cK{+�6�Z*��,�ϔr�N+����T�/�)�'�mp��:��(�u*�UNq�� +��t�}/���u��V���[�� +*��,�pd�Ю���8�Kե��Dېd�o�:���̌q���pur��@'�X vW�M�PT�~�o�E��JA���uLB0xN�h� �c@:������0ݛ�w��s쏪���=d�Q� Tx Z��=e�����LF#�;/�}`xA��H5����ƠW,R>��;�c���@� +fT"�w�����16�#QO/�r�L.`�]�wBP�`S��w��"�ϩ@�nC��O���[���N�!�)���0����u�X�m�ݪ��:RHPy9gq��W��Kj�e�T]*OL��%�e��U���c��a��,��sw ��+���|�7Up�����E��NA���uB �AE Fc�x$1�l/3qvf2�����fW�c�����}v�@�{��[��sf���O�x`0�����d*�Tc���G�b����S�l%2�O.*�R�<�^�Q�wC�yK��"�y�S��Yv�S� +A��MQ���5� |J��>���V��( �TR��90 ������,�����ꊩ#ő���,�p��myI�b6U��c�a�d+Ge�6�G&��@"|�]kw^�,���I9V���&n8�� +�̷�E�KkA���+�"JΚ�" A0G!���f��3�t�B�{�M��.�����m�5�` D ;}�S&����td0›gAÁ��l�"5X{��Ġw,R>�����D�C�� +�D���󤦛16TYQ�O�R�\��]��R�ac ���ZME� +S�zBӆ��޹;�B�ؤ���)��Y!�}�e�X�n�ݪL�U��f���V��%��\���S�$[GX{[Vi�df� V� +��<:����R�ۋĻhGQ��|�E�AO1�����@<���$��HbJw�N�Mg0��nv9��{�͛�m��` D ;}��Lr}5����`�Wς���E���|��A�X��Y��+n�� ���**��i�?ϓ�n���ފ��x�� +�r�gJ9Pg��\�Zx�j*�|H� u�?�sv���N���)��Y!��}�i�\=oWݪ��z�8ZA��ˢ +GV��Kj�#�T�+OM� I����Ӯ����^�G�N�rw�+��"q�5Up�̷�E�AO1�����@<���$��Hb��m�Mg0��nv9��{���mv�@���[}���r}5����`�W�� /�T����O z�2���ߝb`��+�q(~pQ��J���֩4�>�1r`����=������[Ճ�#ő���.�p�����X�M�yj"5,�,c㨬Ӯ���H/,)xu����'�X v�79p�Q��|�E��JA���u��I4"B�c@fg{��ٙa�7?��.�=vQ��_O�Ϩ�[h$Z�雞3���jbn� ���Y�p � ۢH ��tc08)� �{��]�D�S�� +��D�W�����1vTYQ�/�Q�T.�<�L)ꍰ��KQ W��"��ϩ@=��B��k�݁E!plRi�r�c�@V��[���nٯ��[�� +j��.�qd��^RW����|k�mI�u���e���G&Ƹ`E�"�f���Q����b-��K��@-E���rWv=�(��I�<��I���Xc����2�E�_KBA���8�*����%FB�`�$ĺwl���ef���{\�|��9s�7�� +j�� uԄ��۱�^_uGհW����� G+�C�`xG� +'�4���G0t|w��ѥOS��$���󠦛>��vj��!�X�¤�B�Dj�p���Ʉ׍eѿ‡,�@�41�����Ȟ�8m�l�qN}�HN ;��)�4�Ξ��v� ̂3읢f���{�k�57� >�g�a�ܖ�8OX'�:}dTU>:U�&mJ�bT��(�4�IM'�<��0��(Պ�E�m���d���U}W?E��jA���uT%gMb"J�CNBg{3Mfg�����w���XEuTM�Ϩ�[�'Z�點3��Mb��^= jd[����F]b����W�\%���TT0�%�lwѣ����Ί��x�=L�b̲ϔr�6+���n���p� +��}p��6�Q�:��*�8Dd�p`:vwϫ��e�h_u��[�� +*�_U8�zh[^Ҿ8�Kե��Dېd�ko�*m�E&Ƹ`E�F�kvi9P�89ʭ��b%�^-nr��� +���6?�S]k�@|ׯ��HƎ髝�i��Bu �|Z[G�w���v(���$K�dC��ܓ������O.s����cL��_��n>&�h<�`?2E�RA8�� +�3�ū��;�^�Zg �L�7_�0��3���o���gCX�R+a���ᚪ���Z�1�0)Hkثe���a����*�d==Tk%��2+�7��5Cp!lLJ���b���8 ;A�**ga +;�pX�l�%��i��82b��Dx΄�? E�Q�S�ed� HR8� �~=�$�(;!r^^���Ҹx!�M�O�HjA�=� ��\��J�'�q�=i7a�*0���YCO���2�Lj��H��rx� �h��r�a�W�wA����X,5�k9�`]7�"q��OЧ�a+���d�h&hQ,'I��Z�66d��!�l�.�!��l��ȹ7`pב�ͽ��Np�A/��^ˑ���� ��3owG7��]0��`\�ë�����<���1��֪�a���C�b4ò=IU��Z���ښ�w������-V~.�qѧ H-�P0����M��&��'�'�'��67i�����q��?�_o�����R՝��{���.����Q�1���W��}� .�*���&F7�R:���M{ K ��t��+:os���{��q�CER O��^I^��Q��ꏊaY���Z��&�'<î�P��h��u�65�'�x� ��'y oh���o+�`A+X ��c���Վ�Ӕ@SAZ�^�"[��{��5a��|rO�V�L (��~+XY3��$a�h߼�x?�Z̓T�k�؋�J�֋*���>��%Aڪ�<,��RpBj������QQH-B���d�� ��� +�%�;�L�&ɤ��j'�p#�v+L5*�i�8<�md��U�����xf+ʬ�+�$�����KiM`%��b�&������^?�J�M*��]~LN�\0�� q�t ��}�Ϡ�"JI!�����V��扣7ϥ'����x��$���O�LoX�-^b���o?�|x��s�Krmp�����)f�%�\>�5����c�m�[k1���W�B��&�k�K�ͅ�J ����0��F�Ihf���^$��R����f�ӏ�F4l:J\�&g�N�"���pZ�GF�j��u� "%Ehqk݂�+��������� q�<�#��� +N)y>�o�� 8Č�$���{N8�m�,��!v�A�� ^����l +~ + jm�u0��9�s��0�oCz u�"vL�X8^��˫/��lUSK�% '�Z�`��Bs��d&4ۖǕ��H�qk)݄e"Ӫ�%sy�+Ò��ߡ������{!19����Z穃�H3K�k ~T�����K����;g J��ެ �2xe��Y�=��<<� y^��O����:C��4�e�i����G��~�3�),7~A�k��գ�969�5j +�&�v��^��߶�kF�`\�Ե�_��Wƿ��9zGG���oVc���v�B�Wa,-���̾����}PJf��G��{���{�ʪ�m�[k1���W�B��&����m�@)���d�l$�HB3k'��"����Eh4��7#�}�&�!�T��%Y-w��?�폪��_�e��,#�$-n���a��1�)�{#�u�.��) �L%O���������L�X�qM%��&pM��D(�@/��: ���B�B�9�������`}҃�>�#ń��E�}�O�L'٪4&F ��X^բ +����%MСٴ<��z �Jn�J7�G�Ȩ�:�\^��0����PLt��D�{�1Y/���Z땃v�S���O�У�o����+���Y %V��ڐ�� y)�ﱧW��)^��J$]���k�]�������Q�,�� ����bRX0n�\9�d�ɣ��I�9�l +B:�w J��D�6�-m��bl�ն�_�fX�����9x�ڟ�'�{C�����B�Wa,M��^Ͽ����}(�Jf��Ó�q���~�s_m沪�}�Ao�0 ���<�`i�]�4�Zth�a(�a�-3�0E�H:A1��r�&��dQ��#�=��]��l@�J��Օ>g��uc&##����/�����ҕ�Aq��3�'�P�>q$� �� 9Ң=��:��aI-�z�pO=1��XXd�)�@E;�)*����r~N ��}`_�E��(>�oP}�cȁP��vCߗ�ۻ�˻b5,�v(�yٳ���WZ��Գ%��;�<17$-��C~H?�Dcz)sE[��P܊~��1[QF�`�� +ݣ8�hCQ��o0�`�E�2R|*�R�취Ż1�Z�oO��ʦ(ʽ���=��uy.�j�s��3}��®��潬T��-/�F�,`UO����L�s<����>�j��"�#.��ڔ�߶gaWg��Y���4_����m2'%[~���2z���w�y1u�Ak1 �����n �f�m�%%�R�c�h<�Xı]I�%����I7�қe}OOOۏ-5�3)��U�������z.NN�-�a��C#u� �I��<`!v���<$�*��I �:SyduÖ���p��G�p�=d.Tp�3+�v��j�qm�;*#b-�2�^��~� +O�i��ͽ�Y"cH��>�K-gh���‡E��nw�u�G-�<��@�Q�ՋG����:kd�:#_�BOl�"�>�����E6!Hq֩7n�~h�%œ\"��ľ���Z��/�/8�����yڄ�o:����:����ϙ���(�TO�}��%�Z�&�]�Ok1�����:�B�!�4�Hi Cr,��v�+"KB3ׄ|����M�ی�~��hq������̕hvV�u�X.�Lksvjp��� Z�N�(+b�e�^yn0(nc�e�����:ƍ���Y ʁ�Vc=o�� ��"QG?�猅����%��s�B�f��5f9���1��{أ{Q{g9Å6� ��a�䙄��x;�{�����xWP�`ڑbK���ދl�v�2��>[���8� �aIdˎ�}�=l�6���+�� ��̡���i] �I7$�^��qPg���?ʡ|j�H��; QRg�y�9n�څ%�lcn�� k��ҝa,��~za�����M�#/��?u��`��������������փ�}�N9*۲��k֧]���G}� � ˚��w�]�OKA ���)��V���ֿE� "�GA��lgp��lk���j=�[����2����a�Hx�&�۫�+���x�&��X��hcbDE%1��!n��aP�K�K��ȏq#�q�(���bF��z}�O�<��פ)�;��P���r��{!(7�%��ugE�x_m���W��9+#��;Y,�51)cy7�=.�wO˻~�`�v�h�����.Z���t��4��靵�g<�Ey>2u���|"U�2 �Mg��E?\�0΍�_��@�b�{|�e?46l�}����qy�+�C�:�8�M�Ɨ�r�]�OK1����f+R�j�*D +�(H��m�qff�E��eW�����e~�evUcEC!{����`����g�;=v8�*&E�2!)�Ci���&��ԝ�M4��7„�����3/L���y��� �i�ՒgG��7��~�����9�����7u�Ak1������LB��ۦ1(%��V(��ٕ�2�YS���n�Bs��{�ޛ�� +:�����4z�i�����j�.���bE#VRC�q��fb��Q� K��g�M"yb��-��u{z�w�q�n�Z$�Y����u �sI<� �೘�v�����6+,0�1%�����g��(}�g��e���*c�0���oʹj.f� ��b}���h6��yT��;U�pB�\ y�} ��?�l��b����#���K�_�ئ�я�dT1=.�k�bԴ�B�sڸ���.���[�Y��%���o�=�ZV�7�z�`�a�ꅅ����>�n�~�?�VQo�8~ϯ��x���K���I�^��3k];�'�j��~�I +����'������1W_�$��dCKFpZ�K���g��4`� +!�����I"6�)��t�b�:!yn�B��L}GC��Q�]�O1^7a�KfI0����-�i��N%: 0׊�Xf��� ��%�LJ�o�-Ge�Zi��HhՄT"��[��~�F����O�F�ebaw\�VP䒷:3��H�(��6ea�03������ ��,A�uz?�Nj�|>YD�^�O���a�"���]o��:����1�Ov���h|;��V�L�O{�YU���c��f���V����3#h����� <#�Z_#�c� + %�j�"�����V����.�4l�õ��!rɬ�(3~�F�f��@��-�Fl!�2#��1�S] EMx�_�lҩ�g����B� >!J##t�s�O��|��K?�5:Q��`�E�g�F�9��k���\|)^h�ŕ��J\�Z�E���r�o��n>��w���%_=:=ǟx<+���( ���� +Ԓ��b��}r��g�ca�R���Ak�AB^L�����L����H�)3�V�/?��Z�E�N��LJ� +�4_���_ʴp�I��Y��v䏦��#wFhH�g�}!�.u�ߵ��صk�����Δ�҃b&G��]z�.8��b�S�q��&9�GQ���R��w]7Х�������9ztlU���3v~Bbx�}������ ��['w�A��p�f)��kA��bZ�{�5���u�P(��c;����ɻ!V�������)(��5ܿ�^\��=� r_����ȯD=g?�5� O��{�~��W*H���E�]K1E��+�c[J�ϭZ-�D +�Q�iv��$df����eW�>������˨�*<-��^2���pf�#�^��>0� SQ�[�<1�eʗ�?�b`�x(��(~rQ��J�ž�'ߍ��=�z�Xs�s�Ev�S܂�X�����ME��O�@�nB���[:x�Q>֩H}�c��$���S���Y�^v��TWL)N$������ɫ���%5�2l���S����2���&�u��cu��[��Hk�|M�g�X�?�/�m~mQ�n1��W�C�(jĵ P��R PD� �ڳ��c[�q� +����ԓ�3o��{�}�]�eH�/*��w}�\^�Zl����_�/}`��L�H#�Ο��Ä�K�Y��)z��;�����-ؒD�����7+<�@E=E츲`[��mv�S܀�haRT�C�$���I��1�`�No�� ���qLr$�)��Sa�<����w����$L)�T`}��b��Wm�K�b&�Y�t��0���!}��t���2��N5�%�K�Q��?;�u�`�ѴQ�D�' ��78%o7݋��G_���z��^,��%ۮe(�YI͖� FIG�P,#KA_sHd�6�c 3��*#��Є���d�S�0�s�V�!�N��v�:u���X�o��EMnr�iڰ�׶K +W��7R +��W�E��NA���uB �AE c�x$1�l��qvf2�����fW�cW�����}��` �D ;}�s&���O�x`0��gÁ��l�"�X{>�Ƞs�S>������D�c�� +��D��.󨢻!6���l#�����\�Y��R�ac����5�+|J� �>�����Q�:��*�8Dd�p`:v���|�Y��:0�Vq���巋*Y=����/��RuA�h�la�mY�m���1.X,U�+INQhqr��C@'�X �W��������e��m��j1��z����]���;�Ӑ�@)�X0�v�U$13k㖼{���%{�43���ξ�6�!,S%���J���s=5���O�l| xA��H<�~G�}�]���VQ�� _����U0�i�Η ݌����6bA1f2\�s�)�@�66p)*�u������6]p'z��Q�����V}�c�@V;O���������}���*�V�xyeQ���Z�K��\j���3I����Z~H?��L��xEWȐ�>�fj�]��u +�n;m)�w�!���F��,�H�m����~g�p昚�d�L��S�޽�V+��(wN���]�T����ͻW\��(e/��,T�lC��AU}5x�A������;��ݧ�¹�\.ƃ˖�鐩�������֙9)��Nn�1��Լ���Vmo�F��_1'%g���S���8�KHc�1��Nidm`1��X��؉���j1�|�R���>��+3s�k��a7D k\0⊅x�1~� Z�n ����OB �C����>iA��i���2����o!��b&8�#�������=��G�A��38���eĘ�!�@@�.�#����g +�)`�0w�]�C��c �O�7$�z�q +�ש�d�ә!�R�D�����.�����t�ӄ�\��.�[��y�\ V�ؘ��Fd�j%\"#.@���r�9���o s� ���xbN �6m�9��wS9cs�BL̻��2���d|;V�( ��v��6���Lui٦c�����ڋ�̰��+սm8s{�أ���P�>�M���ˍ9S:V ,C�����z4sL�PFO8�ݘ�ѧN/n�����ȕ%�1�������p�+��I���)���?h��qz�B�F>Y��-�~� �+� J��Y��h,�Q1�O�0�'��$��C�+��T�  �����v0�"��q�!%^��YjV;\1)�����K�W�Ke�k� �$��'�;#L����K�f[���p�N��ovz&�tH�l��m�Ak1���+�!ۤ1�&N�6�$PJ�9�X;k�*���]J�{�ƛ�nzz�{3Z�_в�$P'�k��TQk�@~ϯ��D��O��z�,-���� �f4K��0;ѓ����1��ګpy���|�|��\|˳RR�b/��Ld����T0�c�!Y_0�i/>>�zz� vUA�ް���~H�9�"C?����� � +6�9L�3 x=���~��0C?ʴI�p�7���:/�b�$i��la��ӎpŨQ�D}�� ��TuF� +=�yr��L9۶6��(s���sMY����1�>�9I��mn@{���]ٸ��L��Iy����Xpޤ3~�d,���AY>�H�����Ǵo��k+t��2�*9�sX:�6�^���;#�[q���;��?��q���ۧ�f�I6Co��6�V��<�`�����.�'�.l��J' �@�׆�r/V��hb�Tn0������7�������Wỗ��~���V�r�6}�W�4$=��8N۱L5��Ԟ4��v���"�&��Y�r�$����7�K:�E�Erݧ�Ag���<�^�RL# �%�ع}�h��ԛ�y�w97�q�� hFT�9_�%�\�G���� � I�3��oH��)#����A��nq���L�%Hpj���:ר�@&SH����Udj�w���Y!$kv�{��=x�b������.J�����E�ry�u >� }�B�!.�d]��� W�v���k;],O69����S�ؠL�>��u��wVm@� �(U�`��j��vnc��eĥ <���鐱 �_���l� ˸�&�4��Sų?{ n��]���~�1>�Y �S-��(�Ó��V�APo����^��1���UR(���?0!�c8l��=�� � +�%p���������d��zp�Q�zkl����ՏZ)%���d����۲�� ۬��t����f{4#]���T��&o��k�� �i7A5!��K7����T����Ζ��ť�Ab���?��26_\�'�g�/����<Ѥ�M ;��h�V��m��QГ�c���}>tݷ;{�hn0A�rS<�����=K�S��Ao��=>h�株��� �I�FXK��|M +"�6|8�wb]֣����<:^���W;Y�9lݕ���dB1 ��[�m)-��B�7e�y��`G�Z���Y���L���z�k���_�:�}��m�Ep|�~��[�{_��T[o�0~ϯ8} ��:�c�Zi��h�&!�k�m�����>9$%�H�/��'|r����-Ϥ$�x����~�mGІ�\yȔFP� ��$WK���D���I�[��i�xQ[-9H�mŊ@٬�<� ��*������'2���.��s�}�&Y�dď{߷��z�^����J\d(ؓH�B�q���g����bF,z� +��`��0� ��ڬz8v�7A4ȭ��e�ʙ�����YF�y=4,��ZZH!����-��s�A ��&i��3��t��ar��)&z�;4�o�y�$��G��t�&&����>j���G�sۏ[�K�O~\U��~�`�Ҵ���!cyfڭ�t�{���� l�����1L�8N�l��Gi��� �~�o�_��Oo1���)�!@4��B(m��HQ%�r���f`G�ږgU�?I��G��O�a1���b����$EX��]h�!>'^V����k�o��?�T02�Ӥ��/�4��J#���'5�0��`�H!:�B?� ^���$[�!A+¢qv�Վ-y!�_�T������VL���������MF�Ŵ2���Y6^4ǚ�����d 6�w��75I4�pW�4 �ۍ ���w���5M�QS�<�Έ��(�r͊� z=LV&��fl���+�� ����0Qmس_����x*��M��b�x�����/���l� �o#�z�O�l��m�|.�&?�[B;���;���Y�ّLhOڶȷ��]����;��m��d��I����H��T�h��!�<�V����y&��x>������z���>_����KA ���W������W���RA��q$��v�ә!�m)��.�ZO^<&y���L/�Ϩ�RSq�f����dxV�*��쥠����LjH �^�<��+f)�U��0pC\kd���V0%�|�:ԓ�/�x��Xpˊi94��Ϝr�N�5\���j-i�1�M +� �׽Sq Cb�tC&)��Sal�w����l��4�P}0�d�QA-�ۋk��<� _R���R}�|\E�p��KOz�^���U�Dcm���,ςp4|T��U����� k����Ƹ�?�Gl��9b��g�R����0L��������s��4�܇q����M���rWf�ix��� |oܭ�/��=?;��G"�r�g�<�Κ|�Ey�r����&�ҙg(�3�Q.���J�U;������z`4�>�IR��Re9���)��۫�:�������h� +�f[��]2 �q��4쐽 ��鴕If/�N%X�y1YAY�xŘs]ۉP����ZŸm�^U%�͊Xl�����5�Z΋W��o�+T/*l�� q��Y���U^d=�o���O��x��0�6����[���n��AJT����x_S,kCt�D�%ا�yӚ�W��J�ʖȖ���/�O�V���|q����?������n�z5_��od�� +��%� 3���w}2��HL �I}������ҡ�����Q럛��$��1Q���E��� ޓ�7�V�o�8�_1��KRA�}��Rh9�^+�h��C[!�L�u���,���O�IH'�_Z�����L|}## ��(t�Q�7s����߽�sy��9�DTCH� �2 Bx��/H4FBn]F\߃���3��Ae4\��v���pЁ.�6�p��\�\p+#�B2��@x��F�El��Y�B��˜1���V�Q�F�<jE ��!�k�����a4����UR���� �P���6�D`l�Z��G�E��|�p�B-���� �Dz�k�ɵ����q<=�>�M^���������i��+i�Mf��`8�da�}[��v�B������n|�,�z�D3W� �.�T��0���(�8�Y��}���Sldlz��3�5L)_b�G�b����� Еd�Bn4������9ܮ�*ߜ_&WR�51-?���� 2<��C�D\7H�=Py ��]�Z�5Ї���D� F�=��y�D��-�U�)�%��S�r�2l�iY�u�1��`�R��}�5�ԵU� �����h�WŠ+ց�� YB �x̘w��k�Z +;j�|�X�<(�B�oE�D嶗hڝ�e��;�C�$����^Of���H�lm7_Bk�&A�;�V+�حa�+�,��g2�+�L��i��� �B�G�f�e�,�1< ZT��t���@��f�~�{�Nb�k�L��l5��C�f+�m�������8���{��=Ӟ���+�e�Q�Z�u,*��|f�ع�v��,��1{�٘� �-��g��֮W�N�ܨw���Hv�c;i�*-��)@�c��ڃ?ş1j!� �+8�rLH�'jM��z^���*X���K?�|�_*`L��!b4�QPs%���ɚPF�L�z �Ts�g���H� p�@F�=��!�Rd@�ꘙ ���-Scy�.�R����cK�>���(��>(���P���F9x+�>�mQ�H�]�ǀ{�?cTۻɬ<������g��p�U�ꃖ�<7�,�FrkX�L供��Y������0R�v����E(���3���oʵ����@���o�]Z)'Yv��4�f���j���@�=��G 4���cS}1V>I��[��V�BH�%p�jo�a2� ���T����Y���8�!�yle6�[����o��!^ �B��ݧ�8�s��X�o�6���6x�8v���S'�<1�� ��fC�-�4���cc��������֭������x<��e��#�Da��b���6E=z�w'8���iX0��4�D� �M��pc�n[&B�;%~�D|Fe4�%J����?Ƌ��3цט���z�p�&)ʔ�e"b�RŞ3#�. �� +L���8ZZ�ܜQ���T+b�=H9��f���n���ǻ�U�3 1�B4�L�0�f06x-3E��w!:��P��"�&DM�'��y��i뗠�2��v΄A����}�ϸ���L�̸I���[eb�x˫�6����S�����x�*&��_5�H._Py���[��)]x�+1x�V8]�2C���r�5�3ů�Iǜ�0�V)� +����g`prWk���X�[Z�-)UlM B����NS���Ur�P�k"b��%�6龊�["��>��8�2�'m�А�_.U-NE ް3�HbTF���Z ��W��3g�J�|N��FeԄ��~5 �l��W�����kh���/�jG���ٯk7��E�&(�Q�����Y�ܭl��/�2P:� |-A��O��$J���7�PL-wA�6A��:l9���%�eF����6C�JۉZ`8WGE9y�BO�zjT�&K-�S� �h/���(� M��/2��c�n�]�?�Ae�Kn�t��m���� �٩��.��W�z�uz� Q&��ژ�_�θ���u��� ���Ÿ�>�xJY������x ;���O�B�]�� ]ܤH ƿ�Q,)���{i��Q�h�~�R�؆.c��kn�� 5be +���9;���g�_�j �<�4&ÆV�/��W�T���a�AҀ#��T�%��^}����T���g���.gۉ�C�=U�" �A��a.�'��5�0�{|��B9ez�&屛8o{�]��k�VsmK�,5 ��<V3HX�E���tBC2�����FL Y~hͦa �v�J]! �huV3U ~;�.g��4�D� zP��xQG΋W����}x�~�"������؟��o7WT�_�� a-Y��w[� fj����;W�a��2��R�ΎFac\���xI��b(ڇأ��A�nv��!E �w���������[Yu���aTi~� �G��`�8�(�~� B�za�aݛ�n"��7��������;o���I ��|�ʰ��%�4�z��cM�=���_�9�߱�J������Z�5����.�* �C0|������Z��|���2��06F[oG<�WHۑ�zl���-/ݣuS����@�#c +� �-�R!�I��YK���f}�WՎ�����z(w{޵z��ƈF�IW��J���I�o������c7C����ТAn�6r�vm-=�.������*&�� u1ߝ�W֐'5��av3�����o'��|<��O�O���'� ����ZmwH�|��Y����V��]��Pk��0��—ľ��<vt��n{��C٥?�O�G��U��1�>#�� +�а��we�����4���>v�_��! �Q�Ŷ�����͟� + @�+�Oq�B��"(�B�ؑZ�V?!�M_� �T(��Q��~bVt�~�� |�R��U�<�|vփ�go����;}1��P������>�B`��j8(��Di�y�� ���R<�����y�l^.�bh��'�������a3k%u�:>��v�0^ﯻ��v��Q�3\&�X�4��6)��!�jO��^�WG~*y�a�H�RE�^*���c�Maq3}y�>p�(@x�}�H�'f�h=��Um����������?m�AOA ���+����P�`4!�D����ٮ�8�L�]����U�Ɐ�}�:��>�&l��ha��z�$���Č� ���Y�p � ۢH ֞w44����߼b��pS"�6��NES["ͫc=���OTYQ���R�T��<�L)ꌰ��KQ W��"��w�@=�iC��K�܁E!plR�Z�/�Y!�����a�||Zv�z0�V�����'�j�Y=����Gp�>"�L�[�la�myH��#c\�"X���[[����G�;��k��$�6�RT�I�4��*��> �ǴJ�� ����Rf���/� =�KKCA ���+β-Eqk�T,��R����Np:3$�-E��r��]'9�2��������HM8؇�*���x�.'�EV����^ ��:�.Ţԓ�.Fa�Ʉ���'�)�^2�o��EC�Slh���g�EBۥ���ޫ�J���{㒧���L�a�e�X�n���̢7��a���G���t�4g�K�����@XG/��>|d�g#i� e�I���v��m�C�nL8�F�k��ܷ�u�AkA ���+��6&�׮�� ) �S!��j=��A�Ƙ��^Ɖ�Kr�����s���fj���XI?}�w�r���Ȋ��Ջ�����H'�ԣ�.fa�/� _��HL��iݟ������R���g|��+=�5V*5Q��P� ���g�oE`�0N)!��7:q���c��7.y���+��p��q{}��޴S�`���� 8�EX �e�@e8G�t��I������>}�snҗ�=�r���9��Hƶ���<�uP�>q�8���u;�Z�h��?�/%u�MrG�|���J�����y׹�?}�Ao1���+�!@4��)�*RUEMnU�w`��ςP��^y�%i�vo���=��ɇXE�l ���5�]O���}\�x�l��:�M�$���}e|]�q,B<��U���c.��ё��� ϳMw�.�v��PRK��f�$u�Y�"��8A�� ^�nj �΁��@+ƶv撞����a�6Ȟ�?DtL�q�|l�}Y.�>�eTSL+R)�����G�4�O��0��*� +O{N� �"Y��f#㢨S~�79&�^��l.B����iG����q{���n'�#=��,���QJ��M�Ep��$�����5�`�o��Y�h0��@ҁV��)+Q�!���U�X��E+�g�K=*�ޙp�п�!�������w�p����7������7�^_񄵖N���/p��m$c_m�-�����Ϩ���T���0��+�@tQ���Ң]i�Z�����qb�ؖgB��{e���֗$�{o潱2�� +9J-<�����Cz�<�f�Q#�Y(����� �`��V��g �N^� +���7o�ka~�g������>��1�p#��0�K�0�zc� +��i @&i {�)�z� +>[\ lK�A6�Z+����Z�����(��y/�����)HEc\�� ��kaG�p0O��Aڼ�<Ɍ�#9!� +��=&2Ͳ���g�qu"�}| �i��zR����-�!�B�f�Ԃ��Xh�y`�� `2�� <f �Ϋ�`��s �ЪB�U�~��Mޥ�D�p��+�:�������6��.@t�F+ ���@��Z���A�Ue�7�u ���r�Z��s���Y��[�j|a݅+��12��C=���oD��밒����:���U[�iu��`b�<���q{h;�W����K=�K:��������$�l�T�����k$�U��p_�����T��2�5�S��N����z�j�i�g���m�Ak1�����1��N�4��PJ�=�X;�*Kb4kJ�{�ڛ�.Bo��73�Ϲ�h�R�S񶳗����|��C���tR�I ��S/G�q)��<�����^#�K���� +V�����}���5���bB�x`ŪL�:��S\���§h*����K�פ��� !���Wwϱ0$vId��5r`*���i��}���c�XQ�`֓�D��s�8���:|I�z�O�4��E:p��O=�&�7�t�*e�P�r�G;����I'���`�4XIQ��seW9��WT)K7y� ֙�ӧ��R��}�n��.��O���fg���A=Wu�w��۷�Zzu��Qَٳ����8e4��N�W����n�0��|�9�`n�^k'u�h�"⶧M�,�4IpW6���^P�&�u�~3�Z��MDE��D#�d���!_���l�0���2j��u�w��ӹB�� +�!�m#�1�%Ox��NI �<-7�����)V��,V{|��\��D +�QB� +&xIv�JH�~ ���9��=��5�`}�N� ~��H3ao����tsu}��Ψ��4ZpЌ��U8Xi �<�6� U�b� +�uya��ʌ�1P)�b��H�� ��"$9�@���E��H��ä@Ƒh�-Ý�{|��-��l+'̤���h��>�1�I�X�Z�"P��G� ���R��7��$�r�/�3�O/���}�%�����4���|��\ +ma�L��r�5̥H�:W����}�m��C�-S{9SlK ��ClTmO���9翊>A5��1�:O��\R7œUV�R�(W��_I�lv +`��8��������:�95]��p��8�WCCǎ��ҧ��:U�w��bJ���S�d��5�[�".v]ڌo�6�5-��@�,�DDe�Z[f�Q�uWi3�@[ . �s� <���؟��͂l���I8ڨ��JT�<�*���f�m���>���>�L��y`����Y*l�F�1� +� +7�@�����5�^5jv92 p�Ƨ�q�� ��N�8�Bc���8�+GA� � �֨̐%C�r�rڒ�bNM���M+�y1=<��S� � �B)я�,!߫��?�O��?�N�F��sS���YH�25�� ������R�yK���&�W^b�P�������GY�(_8�Ct��x����zm2K��2��L�?�g�����o�U]o�0}ϯ��*�k)��j�J]U�iO��qn�5ǎ�(���'�$$�t<,�����s�Irs�%D�%3Z2�ӊ��٧�4� ��DX��D2ft /���8����ވMB�>|6 +�d��pÌ�ź�G8��̒` +�b�nlyc�%�L�Sp�ȈuN�آ�6@ B�K ����RpTA�X����j�Df�w~�������ޕ��(a;f!�� #� J��x�s���Jɓ@�m�8�K�̣���An/�]g_}��’���� $.��0����oL�-U'�>i��s��1s��h�Ъ����Z� ���`�-3��Q'��Ό�2B�r^L�&�(w��Vmp�� 6��4�*��O+6�Qn�A� ��E��,_K���Y��V�L�),�yÊx�tx��� ���3O�_��+��ܕ��1+�k�}$πv���p�s ����;������g�bؿ.cqTm�r����U�p�U+�U�������+��U�h�[-g�e21k��H�#29�"߳��c�L��� ܑ[k-;ɝ��avY������;<��_,���A�w2XC����)��wxF�p�$��5T!���H��1����z7 ��;����K;l�e 3דIƍ��0��E��6Fsf��İ��ԛ���k*{�Z��N���<�)&�o ���帾��N]��2���g�;>Fp��mu�,��� �Ѹ7��CW�����[��U�O�0~�_q��h+J�וB�TM��{��\�B,\;�9�������W�����ƹ����|=9K�B��5��{�� M>���h��ǂ A$�X���b��xąN��x�-�x΍B�*�zDc N�Q8]���O�`�KFV0ט���?L�8A�Ht@`*��5b�Zm(#��l��R/�Z +�����Y1+�:�D"#�������\\~�_�P^���� #m�0���1X'�tj8�a.y(�BJG�����?}E�A���o����c���Odq�_]��4$��/�"k�Pv\2"��_)���y��`4�t��6J�ʊH�q9:sbĚY�Q����c���n��!��f���J���פ͕���ֲ^�Gt󘁓t)�(U܅��{�sL��y�{j�����46��*z�%�]脉/H��B e0��IBo�gS��7���2��0��~ ߐ��-�����ij]n�lB%���5t���.��><�7�m��^�K��e� �Ԩf�v����66zC�� �}y�K�(O�*�"�^�iB�e��߲�S���yWrw��nʴ{��*����;�����a���_��_�^� #����ݬ&��&fT4�_��)jmeIV�R,������ k��Ͱ�oV� ��jY'ϕ?���\ ;�К�ܧ���gJcF� �Y�"֧�vG�A{�H��4B�>�ͭ�����t ?��5B=8���0��������R�}�?/�S�n1|���Jy��ڄ@Q%RE��S���Ϊ���5i���+w��^�xfgf�j�9j�{,d�<�K@�_f� �0 Kc CP$���P� �gP"n|x!�*z���!|���F�+E����y��C��B���#\q}0 E@,& (���N�,�x�J�'�a�ݨ'�5#���Vb�B��ac����fv?�%�ҘJ�Y1䆷Z�ó�$�gI#h�ז/2���Ai��Bѝ�U&r�e����Hl���/�L[� ��'��;n3�`q�Nꣿ��`Ӎ"�(�r:d6J�6 �< �Hv�v�.Z��Vq��?�`�vo���j�JJ���aج��H��L�� ��tj���w,��*�Rc�R'0l<'�C��?�0.G¤t�c�z֯bO��T��:��G0�v@� �i���d�MV���tF��w{~�`a8�j���t;��vFԙ�/Tc!=��ʊb�)aB����Y����В�����lOl�,!���|0�b���d�pttj�}}���yHRM0-��d��v�89-�)�Ě-�Ƽ�<͂9�T��JÏ�g��<�j�@k�is�Cs�������ϳ�z#�g��:Lu7���R��z"|nJ6�ugE�'�,�.%�����8PV�ȫ7.�5�W�;�f����w�Eo5�� �hX�����-���Z: �P�]�#��+i��p�ܔ��#�8I�/V�lI�([�->�n�8��r��3ْ�u�]�[Gj����{��O�#���h�O��V�n�F}�W�H���Kd9v\�IP����1�9�^�;K�D�/�]��qw�rΙ١��y��SM�c�N���W����t09���J�P��9��m�V|:@mqe�ʩe��#\:����<��3r�/���4���<'� >r�g�m\y��� A&Cj�wj^z�M��u�9cQj�t�=Xk���2 �^�+k�I+�����OW�7��!TM���&A�����������ҥ��f���� KA)�6'��~���\& ���9G�t[ʹx�l���e͗OfEZe�����k��N�T�~Q^��� +�[WA��_�x�}�a��LNNp�"��b�O�)p �S+����t��s��-�����L}��Q;�����],��im�?��ׅ���U�x΂�� +C���C�ve��&���� ��Ƕ�h�5U��fY��K��Z�ģ�9ɽw�,���yaS�#> _�ޒ��H0d�]�+Wx��F��p�ԼE�v@܄x�<-���q�E�1R��+��!w��@�6�`���x]A-�u�ձ�aR����0v���+�㾨Z=���-� 2 7Wޑ��i��Kv���� +K�Q8�b��m1�"���L�"쭆EN�w��ؔZ��E- ��q0�,�L�En+@"�%z|��2�=��w��mNnB����C�Tpzl\vhw�K��푱Z n�L�M�`�6��f��Ӡ@�kO���n�U@�z�.;�w�`7{��I�f��!:%I�ѩ�e�����G��>,������l�>���o9��i�y�a,�P��c��L�Q +�� ����T�dR�����1�95>�,�{��Ԍ�Ea��eq=��ͨH�Xdvm����q���.��+�k7�_�V�Z�V�M9��� +�Ø ��p�K�G��*~?��)VW��(Ϸ�M&���e �S %� +9I��@���(͂w��0��_�S���\�.�}�̖N���:��T�k�0�����!?�צ͚���,$�> �"�cQYҤs�0�)��4���Iҽ{����/&7�"��b�� +N+�t���Q4�FЅ�\8ȄD �:�y.�8� ��[�� b���*�{��Zrpì»u}�8���̑` +��D 7���3�Am$z 0�׊�X�������RJ���GK�Q9�2m FB���!l�B����t��z�P�`����0�����ti9�i]�0R�@gG���>럡#�(*�G*G0��>=����'��l�u: +��T���(�6��ͣ���;�"l��(��d����L.�h'H�=��H,P��e�؊����.�m� � ����޾+��:� +���}����|�V�[r��x�Ĥ���[��!�m�{��c� ��wr���*��^/>;y��1��*�AK�G���-(�]b㤪گL[d<��]��ɫ��\�?fnIV�M�$�t���2��� ��>q�����?к`�?�0R��M�4��=�_�mU��,r8;5-�Y���-��m Ӫ?���Ը����T��c����ne|�w=�o�z�u1��A=NU���I��߷j����?宒�������if,Riթ��C^=���7uԖ��U��\��֧���E܀۳׻��$��C��Umo�6��_q(RH2�xI���c�M������b�6�t6��$ˣ�C��@��%ۉ�/�wϽ=w:��p9f�YL�Y���4H���:t�� �����0�@O�s<��Ը�fiŔ;H�>Y�p!���#8gV��>�8<�1>2r�)��Z8�Z02ܠ6�"0�C����p�Rp�E[paRH �ʻז"CEBM��1'�:#��\ࢴ�����f|���g� T���qp>y҅�2��)�"�fH�e�8����"�(*�ǥ2��X�ܤ_J?�����*W���w�$����(�$# +׷h4 ����T��2���@�ӁќY�s&���:�Rn��3�p���J\ˤu��<�g�.��Xa5b��p��������� ��$q�g|�� g�������[z�O��p#��p�����;d4vV�i������h�,㐴+��q$U8)0Z�q��V�[]]��_wy�$�!��u�_�qa���P�m���w�z/��I3� +Ņ1K�y��/�x��V��SX�,F(��XLs��M���_�ŖA[���Ӛ]aպG���u:�bF�[� �Vs&E�_���xJ��l7笌���g���/Έo�k-�3�ً`J�;6�a3Lb��ݡp8K~i���T��5�fK�Rܴ�ߙ,�%��W��cH �)\������!&κ���VKC�ʑ��"�*�Flm���{�NN?�����{��}|�Z��㓽p��'��5�\Ҵ4���d�e��+��ߝg�[�{�lK��y�`ukv�g�����M5���7�c�ʦE���@���9��յ��F;�]���k���pV8�V(A|�(�W y���R�n�0 ��+ޡ;HZ�6iע� + E�,; (���)�J� �-�>�qS'i�m�"��#}q����ULy6Z�d(?��Y/C�*13�`"�b���2K:�� n|X��W�\�Ďpm��I,�]M_ާ%���TE1�� �ĸ�/��P��RB��;a3��sl?{�T�Ym-�N=����"�����]������U������~|��cR)�JE�&n����HI棯Y�/_,�eN-(� ��;����y�i�bĄ�#�x^�,��9�7\�گ �z=\-������M��,�Nj���_b��O�����Tڻ(\k�|�"Z�zj�Ƭv:�OO;h~���06�}l3[�6Y��R��F���5rP�u�FO�M��8s�Gj.�׏�\S*��N��,>�m���Җ�3'������y�.4��!�8���`���+�����ި�ќd�6/#z�����Pd�>����j)��q�g���u�j�ޙbN�R�jAy�����ı�������8G�b/S����o��6��V�o�6 ~�_��N�w/M�k���C��^zE��L��"y"���d;?��0��K&)~�G2>�P%� zJY�V�"UI<�~0�Nzp��a� �f(� �9L ����׮��^���� ��������f��E�4C�n)��s�\�EI�4 m�Y�z�yn/��y��`���=Z��2��s�(�� JC�+M����Ǜ�& +��!���E9�� <���r��gqI\�"���ܧ��q�8�eU��}A�s����u��m���dkֆ�� +��:{����tNV�\���[G|RP�+�џ�T fh�L�G_���K��Yr�M 'Pz�t�A�A+��qfh舄�]�,��� ��`̡}��¢O�lcq`��U�I8y���7]|6o����S�I��o���B��6���C�L�t���}�0�L 9�����Xi +l���m���}J�T,�3��4Z��v䔐 Y<Ხ�Λ�5��@8Ƶ���e�n:EVI�)�E��@�7�+Lk��p_��tMk��hk��n�j��( ԩ��D 0}8I�n+��,h�y��Y��Y8����-�����fh��U����u���/;l�C�{���œowp6�_z�M�QKBA���W�GIzM+K� ��w�C��;W��ǽe�83ߙ3gf��g8�� +��������z85���/^* ��T���ˑ� zb��ȻW �%2�.Z1�y���W����� +E}�>e�>�:�p45�_/��H�@sx�[�!rUӢ$c^�����Ȳ(:[��q{Ŷ�eQXoD`���^X6$� �hjd�6yl1��e�����`}0 �v^+��`��g����' Ml> �v6Q�NG�q�|��u��p}7@p{�� o�|4���9�q�ʨx<'LG������?�ĩ&���� /(V�) +i�ӵ'�Ʌ��B(�p9��!�[�mS�n�0 }�Wp@��ARc�M��k,@�K �@��t-L��NZ���A���d���9<��7�j�1̠o���^�]�����p��Sn!��[���R~�s +č��~�����ѐ�Kf$n��<���x`�8��s4pi��F����d ��d�!'el�p� P���B@�twh�#���D��WrZ �G����~ws�s�*�Q�N�B�m� c8qJ��x�r!D*�%��dZ�"�����z.&���"�2�G��.�rb��"\!�\���fŧ{i�y�`��o��hh�%��W,F<�3�da���s���'#Sӆ!��R��Xy}p�dT��G��$E�{��M�o����iT���`4�Q]�ޮ_k�:?A���m��DJZ2yD~p�ͧȢ�7�h��s��-�#�R���Z*�"�lt�̭�r�p,�W=ؚ24��� hG��شAA)���ڬ�������G��\tw�3 +���e�q��7n��|| �\s�K�hS&���;�����5*+Y�A����Aʍ��.[���K�Ʉ[�~b¬�0K���C�2s�UhP+�I�����f% �����As z��y��ÆR�Nj��� ���"�Ν +N:75� 8*�w�ܘ��d8�N&ʟm����Wh5B��3�b5�"M�n�?�c�O,ז�����|z��R]o�0 |�����/�^��KWdX�a(�f/�(2 S$���E�� �n�~`z�������'W;(QjA�y&%y�;���c>NF�p_+��ʃ�`+��U��Zču;R�!�9\�A������&� NW���ī���0�L|_��ڡu#�)AZäV�-�N��%�� +Z�|T�h�$��Lei#XYS��(�E��1��Vx(�?ha [�5pl��@Aڲoy��A�D����_m"�$ >�22*�2KA$v�Ӳw� W��i�V�5�� ��Jt5N���0?�?$���� �F�:Z�eG��p�7NN� �/p�tsa��|2�\Jk������f��|��p�{}vHC�l ���u�k�V#��x2Eȁ̱����#S��-���(i^��8����# UA��ߒ���g�jACe�ҵ�:��/-V�����S��k�[0�}s��|<�"f��������ݺ:�[�A;����,-�FN��&���'��T�o�6����P��d�q�Վ]gY�h�ـY0��"F�����%[��-k1~�Eݏ�����W8�PjA��'%���:���t� ��B1�J#('ȃ��K���<�*�ʺ-�U�!�)\�A�A ��g�dp��<�g8�-> +�J� K$���b� +��i � L�O����n���@�K�Ah�$FP&��^Y3�Q0“�M��������u(U�� ��]/�`�|>�g[�D�6k(�##��NH�/�����J�i�p:;R�����ǟ?_k\���(�Z0�mU�?)��W0 `�$~[�e�6W��ԓ�g;0�M�&���A����=��[:��ץ�G�����>4i��ۤ5�p΂ �y�-�z�B����/�n:���Jt�?�[���,-]j%8 �W��L��tRWm���D���̚Y$�x�¬&�t��J�޼�7��=�����m<�[��o=)�J�tz�r��r��H�Ѽ’� ����Q9$���F���f3xז>�JD0�9�c��xc�Pr[� rKP���|�X���+Q����b�\j���R���X��9x�?�f�N�/��N޵�F�K2-�I���h�B���J��k�‡ �aϕ����P�W�}��uJ�q��3���x�˖����Y������� �h=Yڹ�f�R�o�X��.kub�i{�KY@æ^���4��Q�S'���7�/�5:���A���_�`�ы����ob�0~y���~�ݿj鐓t�Hl��_����� +Y@��bt6/N!,�����v��[ ��}����*� 5��j4�=G���N1���)R~�zK +��FmJ(�AZ9��] c[�lB��%��j���љ9ߙ�w�CFR O��^INy�(��uI�����R�JT��as\�jI���̺�WE�h�N�!|��ܑ瀡��N�����0�����'��c� '�td��(�0�5�բb�Ccxa=�$�֐���J� er��+k�4�@X*Z�}_&g��|G������Tx� +�%8��� m���qO� I�*����z#�$�¦t�4�ׁ���\y�l�z�$R��Q��Jx��n�e ��d��?p�B+�ߦ���ߧ��x6�\N1B�2wƮLk���~������'��#�Z +&��we���g o����'����+#�v��5��$��Y��i��ۏ;<<.c�X����� +�&w�s��e���n6T��j�m��SO�����&���L� \ ʾ�5��?E���U�up�zfw�Q���V} S���=m��/����˛��`L[��XA��Y�l���l^bώ�b��|n�} I��Z˷�ǐHeL/y��2�TC��]U��Z��S�Ί��,�D!!����r�����y��4B�/����7�+���X�#L��x�A����1w݉J'�}s;Y�|c��a��1�g�'Ú�MW�f��w�jz�a(˳l Ͱ4̲���/_Y�q��+�N�y��U�բ��=���a-q�0�I�TQ"���J&��{��� +�uE�W�H���X/���q+ +�����h��< |4��a-ZR;h�����<E�AO1�����@<��"DcH�hb��m,mәe1��nv<��{���mv�@���[}���r}5����`��� /�T����#O z�2���ߝb`��+�q(~pQ��J����CjcHTq���'�Xɿ_����T�n�0��+�� I�}ܚ8u�m�>�:�M�%"4)�+�F�/(ˎ�8@y���;��%/ߗE�����NI^�$?y�^D�~�>� �R��#���Hv���x���L&�keb�Gl*��qF�������F��T�O�0�����H�Ĥ}Y)�m�M�|ۦ��\׶�NK5�ON���I˗$��{��}��2 +�b�Y +���%�?Mz�I'���A.�t`�=�� +���*�gcW,g��X$pɚ��B�@��!k��6���waBSt^���TÙk.la�XE�3F{���vu�+�� ��T +Ħz@+)H;�s�s���.XE���վo�σ�dRU�|��� �n]�2XJ_��)Y�5�O"�sr�]�<4?�����t������(�4�����I:�G׽6`0ߎ���h���k@f\��J�Q:�Zaz��d�Z�.����9�ӌ�� UD��)1�{4���1� T2��Y9'���l�ы"��9�qFA��;8�t�b� a�z&�-�z�C�e/�GV<�-��l���Ё ���&}�WG6_�Y�W;�qu�r����!M+Ÿ>���Vx�Gޞ3.�_ل�݆���U�.x.� �O�u_�JR� ��!n�'p��]��Zx*ڠi�*�vޠr�t���I�e����A㻸���GG�rA����r���*+H�M0> Oى+�5��Z���h�ŖB����v�����2N>4�l�0������JtW� �֞~-� m=w�\>R�9�,�ﯶ���&UC��p4{'��̥�o�7��A�J%`j���z�t���&�&���u_)�:8|�U�*��ڷ_��u�̵3����^�i�oC!����?_���0*�ͺV8��ZO�uR[o�0~ϯ8�*� (��(F�V�Z+@ڥ�qN�Uc[�C+��CHsi����v�/�T� BʉF�Xͨ]ڽB3���^ۃ6,f f�PD[�1<$l��d���� > `��WN�3jk��h�����<�a�"�2"����Ҝ +#�(���k""�RX�V����7R�M�s����挢0L�Ro�eRt@q$a�p���n'���ʂلX�3G-�`�lօ72������ �A�ExH���a6��祦(�0�󽱸 ��Fj���� &�p�+���=�rb ,�^�-Pw�+5��z�6��D�D����WV�m�E8�Y��U�3���HU����_S��7��M��B� +�ʰ\R)��)�~������,t +ђJ���}gn9���T��[�Zc��5�3י ق�וŘ��6�l��΀[�Dn6DD9b��bvj�f���޻�\��= ?��Oi:,���;L�9���͈ZAy�YzEl��6�k�ut����#x�x��`׷��dq?���OƳ��~//�4��bi�^���������O/ ��)xe���JC���t�F�jwo8��J��1�U�O����L�K��i�x�`-1�]}Oٰ۪���3���w]|�i!%7 ��;x��Z_o9��� <�u��8 �ݤi�]�5W\�{�A ����Ҭ�Ij���$�iFc�m�pF��E��#%���"+ �$'#�MԽ�(�/���d'�{F%�h�@%D(�+���|>Cqɋ���LA��p!›����P^��|Y�~��b7�$RQ��,Q�+Y�Y���Qa)$�)A���BV��T��*��fwM���D�l�ņ(����Dx��h����˫�+��(�2���HH��{a +�Te���"AHxZ�<1�AY��CFį�����hTJ-K��@� ����?K<�l��Θ�~��^�L��=S�@�Q��h�/��&�"��K+�-�9MZ���s�J���jG�헉#��u\��?G�� � ̛�Ն��[<] �����T*����?�g]v� +͵��&�V����n�����ScoW�����N-��X�4���3_ �f���W}�H��gFcM?1�.$� UEa4�N3����b��l0�Y�`��[]�Z��n��l}Ӝ'�0�k�N��!V����>�h��n2��"���� �V�������}{w�� ?��Xf���9��@�^f4O��O�x.]A-VF� ]3�J��([�0��n0��&;�" �+c�OW�U+�w�2vX����Y�e�邤i�Iô�5��ȵ�@U + M�F})��� �6�h�Ƒ�N�+�i���^�5?�+�a{6;9�3'p^A6 ��ÑF�j���L��u{W�΂@�F��dd�ħ6U�(�Ŗ0��;�LV\ I2�:��g�bU�8�f�.N5��s���ٱ|v�������u���vM2w ������ &�S���@=찕�,�}��$kt2���Aa| +���)�R�T���L��PO�κp"��L�s+C6�_j��sg��\�4~R���Eߚ=�ύ�vϊH�B���EA2e��2�K���:� 6�eӅ� � +xj��1J����C�fmA����-^�� n���d��?TQ�B(�/v���L��$벱)V��5�tT��Y`BWS0��o U�+^�t���j'-4&܏��5�� ��E."N��%UJ� B{�Ζr���]��ռm�0�p>*W��u�a_ٝR�J�5H�G��� G�U��6�|{�n�vO�� ����ٕ�"�1[����@��ߘ�z�v� )�^�@h�WV|[� +{W�MaЋ~��.,�w�)�E:6,����0OF��<�[��?���̏��F�M��<]���*��L+_u���J���s��[e;�u�.?}0�"G��n��W-G��p9����cE���y>l_�����M������T�Z�C���{"ϭO���m Ñ���'оkw��0�5_�Րn�d4TM@'�xg�;���@O0O.�` ��o�?��;�l�q�S"C���8���<�T�͛���&i����q8�H�*u�(�[��A�'��P��޽O�R�;X����]^�����u�n��_v2m��i�`$T�4Un����)�T m��\�p� ��E��[*zoZv�ƒ���v��\:�7" �eD�f��-B�Z�\�KPE�9P��&���%a���(�B[�*���;������Z- Т��{����)s�G�vW*k��m��1�x�:��J\�{5�v� ��;KM�8B� T���: vGS ���д�xt����|n +�� ,������{�|+��k�$�L�q�S}���1�J�C�r��}�s�k'�:p���y��Ϡ��|�%I�ܵ>�Ѯ��ʅ���m�wS�(���$�������~����! ?��KJ��o �ϳ������vf��BS*Ѣ�}�q�Eo���L��F��D��[ �v.햬����f�n�6�+�#��j�^; ��[�]:(��e�+��t: �3�0^x7�'�9�*�W4W2�����~ E�&GL���{�� &��y}��5;k͒�*������z��V5r�9u�����&;ht��ě�-�a�$I�r��L���,�io��5) +�e�xUARk��� O3�, +]*��1mt�2����?_�qŌ��I�iо�5`����K��Ÿ��C`�U���B���'���T�c�M�I���������&<��Qx�F���pj�ٵG6p��`B"(��?�aOU O�.��!9�@Wb��7j�6z5�>`@� �x V�����u��t���o�F�� +}@bA��{�k� �Jhx� &�)�mb�����@ͤvr��!�a��)��ݫS%Q;�������}�) +��+<�}_oO�U�D0���H�� #8)N�}xg2��DM�Q���a�z4�r#� Ȝ���;��u>.���^��.�qs�%*�A S�,u�Z¯`���<�Xg6���lI�:eu�ֶ#��'�w����R%Ϡۭ4�1e��گ2�_�lftkB:~C�Y�0m��u��$��� Pu�`N�iD�xދ�a}5z]�!.�^C��%_������j����bz�+��L�u�v�����A.#f�]��*�5� ��`ɤ�� +����sc��6�#�}�:�s"�>� U�g���5�����_Iˌ��_���Hd(���&�Y٘���Vmo"7�ί��PwA4�:�jC8r�)j)I>T���Zgl�á*�����H��������3_�Lj A.��ؑ�f�5�?w�~�]xH����a�@/`��5�� C\i��b�ļ��B�]2�-9�`V���>K�S�q� �`�-\�r�Ҥ����T\+�b�I[W8��(EXx)����Cj�튑ЪF"sk��l������ׁ*K�RF�a�r_��FP +�w�[��uR��o)�BgG�����2E��wwz)��7�&�dk\+G0Of=^O��Mo�>���/�q�P`ֲ��+ng�M8r5�9 �k�\{E������^�d���nkfìÙ����7��ב}u������V���g��+TT˿��FH��:�U�3O��%s��[�n.��f�e<,+֌�ފA�]�T;*��n��\��bo՞�->{a1#Kк��Rpp�H�t�=Z�����9̵.��R���� :���|a���A��� �c"�^š�PQ.�=�����fY�[�i��[�k� CP^��!a_�:���\��V�(�ڡC������.N:�=87�r�b&A\��%r<�FO0A57z+��aQt5{Y^�^� ��Sʫ��wzcԫ�t`tH�A���~� �Z����n�rO+�����y�D��)|1WBOr�ao2���i\��;��w�i� ��t��q�*enz�u|��>���6~��N�Dz�M�]�b��X"�Ww .7�nvָ^���n����ԝ� ��{&]�)��V-�J�!r`�����{Z-�ϛew�TL)�$������ޫ�v�%��2l�ϕ�&Җ%�e��Uz;}dbL+���r��cl (�Z�c�2����MoA ���+|@JR�-\�Z�*q!Mf��#&���$���w4��JQ)�͞�~絧oc�D� �P���_��(�7�Iq~R� |k�@�<���B����Y-�qǮn�vWLA��*05Lx����c�Õu��#&d�JW��M�=f �`)�U��r|�A���`{�L{g��Qxm�C�ha�p��}�Y\��ΣZc���(�쵰���4����"�Pv�� 2k�h,�mc�&,ۍL�"�qi��n� ��E �{���Fd��C�^�J�c +��V�Y��l �$�(ߍO��Q �B̏� X���951u�0��`Є5&-�X�S�X�Q��FR>?8��t^�~md8j��u�p� +����5��;J�������h�%��5�' +[�y���J�� 9dq�m6�Ce����i��A�=�䥿�P8�jF�A������c��\mo�6��_��� +I��@/w���״1v�4��`�Ҭ�5WTI�/8���"��%%��J3|�/Ù!�����5��<�W�KZWEꒈR�U5�2褰� HV�QD(-S;*3*3Rg�U��QPf[� {��<��ʠ��lؕVb��W**����,˶m](�맏 F�|�����RE�� +@f���e��^N��Ӄ��ߠ4�<<I�ߓfG嗗͎���%��J�B �����r��[O���Ȟ��--@�����+��J��X;�wͮy�7�.F�J_*����€��8I,u�v����vD@��{��V�V�'jE �w��S�1� �}�1h[�w�%�~���랡k�G���(.� ��h�#������I]��� ���#�b�� +��I�z D�@�RI�)vpM��K�J��Zq,�������N��܏��Ģ3zyYp���%g�[մjҍ���|e�WV����#��ɴr4�� +��=�����]8����!�&f��UL�2�Fko�۹kڳݲq�'�㘂�\3XB����� +�=O����ن4I-h�!|�S+Ox��N�� �;��š�R��d nI��xX�}o���A�4�O0N�{<vb�Lgu�I�U�u �X��x���4 +��� �L�?��!�^SF�C`��E4�A9m�}å�L��ʉ�hn<7��Љ{l�T=F���(���44�s(#�J��4��ҤT�B�����q�,(g9V����K����cp7l:'����~�V0��� +�@�T��k�������� �[��Ǝ� =GJ��� � +(�Z*�R��(D���%-SS����gH��~;�'@��� +(T+�bb��h^�LF������-� ���=q��>���������F�w��x��=�F��r= �����~k��p� ������/���b��L��z����tE{\��n���@��<��L}^�e��2}�Q�D�Z��j D��TJZG�5���V[:��٧�t���u +��WJB��9�����Dgg�{�!���B@�\� j20�@����@ʫH����BRUm�fB~����~Jc;uE�#��-yT��|�5ϵ�{3�d��O)��WT��k@.��F�D�ș��2}xڻ0��QF�p�q�������#�u�H�[0���_f.旣N����U��n� �iݞ�b���ñ����4jS�h+|L1����=Ա�}ÅZ~�3��������w �n�1I�p��t���2B�c���.� �A!� X�D,>�5��L���\D��{з���E̻�tVA~�3��k��؍��#M5�b]*0�z@�p��+R�t�֛����N��J�Pk#�D�vj mS �F �.@6z=1��v�ĥ�젱�Jk�c��Ʀ�e���5`������1�?a�'@M�>�i�E�%W�7�~ĄI����,�Y'G$��ܚ��ť�i�@ԄEF_gN=% +��-a��3_��0�z�ķ�)z��Ƚ:��{t���OX�?$�K���ߒ�'D�H�E'4��\p�~_�w�F�(g�BK�7B�o +(����7+W&Ͳ3*Cyx-�?R�����(��uf�����cK�L٫iX��©�5 }D�h��bD��t�k�%oER���y��s*�ec/t���#��JV�P6��\�F��A+�N��N�ۉp�y ��O���i�n��M��n�Sl_r�D��X��sL� �<=.g����g����'�iּ�US]Ӛ��-o�������[S�.�gT�L*���JGue`pwb�U��� ��o`{�cz��6$���5!>� ���B_z�b�0��X�2�z��h�sf�ˠ'�lz1���+?�:N��W���(/\ ]��W=ER�t����Ep�~�1Y�s����e��khx��k/#�BK�v s�J��b��TV�׋:��N4���ظ�[_mS�.�w������X㻴x��h�$�.cܥSWc +���F�?�P����-���o�kF�8�R:�?7�_ z�t`㣃F��C��ba��̄ي*vY~����d��ƌ@1cH�M��;L�& +>jY,0 �Uc�V��)8cP��:�U���^�X�ۭ��X���Ao�N4�+H��M�oJ��@�G,�$H4�-�M��� +n3��j�9DG���ɇ��H:����,��`�w� ���`.:��rH��p�� qN��7�_�y�=8�;ٯ����� !�'7�|����hh M��#��7|L=�L,F?�$��TC������k����!h]�$����SL:K!�m��@�<(��'���y�Z$w$��� �#�#/_�8���Տ8���a���~�!�z�r�Ջ����g��� ��hhQ������@MqQ�ɢ{��c��C�wҬ{��H�XTt�e�\�=�q&�˶y�����[b�D+�b�4t�U>[%t������Ղ�CyV6ϑ,/!8�������Q%\��;k����1��p���X�8�9��"����m�eK:�-��cl�-z�*gvY����f�e=̭��HG)��̽9ۻ8��s�ȹ����ÅDeZ "rC����rx~!v���B�>̩��������ο�m���Ѻ�w2ʀ�nu�ܑ,pǐb���f�����;\1Bxv�8�_���E��Τ���w �/M�?$I��N�?����Ϧߒԩ * ��:9[_]e�e#�)�_���h�����g?>���.��ko��������뇢�:vN��5�7���<��<;3�4vm�{!�K/�h���E�%��(��H��/�uA����DH�y-���?LO������� �d)&HA�$��\����q�[�VkI&ɔ���7)�n�KA^P������hgd7THF3�J��h��uy��B$4[�$�$g7�̹�|�s"�@�e���]]a�,�La�2�*Y��"*��1��t�?�������JoL��$�T��Z� �L��T�y� I�h�|��� ��&@.ה̿i����BafB����_�'�,�D�BV ��E�)/�����{�Ȝo D�o �/�˳��s� �,��� �'{{IJ� �\��#�����ɫ;�IV���K���ْ��6�x���@�%���^�R�^ڣ���.�yʾ]�?J�#�<�ֻ��<�@��A7��I���*��E����(oR�t깾ֺ�e"'&� ��%�f��T�=�E��n��2�#��V\�9�;��Y˳���=7z�J|6$,Bgh����gxv�5ꉗ��R�S�3̩�o1� V.db+��P>j��f���tzP�D��G1�x �Z,��+(r����̄4$�3�P�|�W��%���"�{���.ԉ�YQ߁���e�x�����`w�����ȷ����Z�ң��W��z~ۤ�j�B�OL��gk*����*�oh�����J�� ś�����oLIE@G�h��4r����~t�� I�T�q��j"CT6$,,���=^ ��"�=bJ��o�#~|󋖾���=��_��� W۵�,*�yz�M�R���n�? g��H�3.d�̲�\r*a��q W�#�9�z3�}���}kz�TQ��{����4l*O�5?��]M�]=������Y�a ��r�� &S�D��̈{�a�3����*��t=���΃�� aC�ƈTp�j���^�|e�h�;�+��QB�xn��M� ����:�Ns�_j����Z���ל߲l�N{+6�-�>H�l�>f��l���@�����>h.�,��Z�$��h���o� ��n��Ӊ���Q�!]�n��uy�=̿�ﳴ�o1�/�\�7�SFEc�;g$1����r�c�J@ C�(,[��; �@h ��c���/^�O[�?'U5�<$6$�E� +�f����4��}P��E���L�ӆ�E�P!ܸ�Y�k���K�^,�|Ɲh-���^}�[�4u� +� Ylh�D��c�b���)�hd�a��!�GU�zʕU�Ƌ��|xԤ�8uӑ_y�juD�Z[�e pB�O̕5tuX �B��^���[9҈��ɯl%�Hև��Mn sL~Tͤ�"]�A�!��C<�� ̈ 3�P�w��[��>HY���7�Q3�e�� +�i��74����b.3#���� ��pP�h_�{|���e�s� [�T��=�.庳��/�f ��y<6e�!6؃"��m�*��%jQu +��U�dIS��b-e!f��Ś�#��R��xn�Oɿ�壳�~�ݶ� P�n��%�I��w^�i ��lkF�n�μؒL�IOO�.b����Q� y��?�]3�(�R/T�}̪���&�M��ž7֛�z7�@ 0]O��:�o8^ d2mx�|�X�f�'���H^=D��ncn�1�M�S�`2����`t�}�&0&��K��8#�0��,���&��B����� ]�6 �ekRi^F��p������ΌX-YAݶX��h���2�������U�j�5H�� +6u���ۧ�^��P=u=�y�߭���"j蛂Jv�R&���9k�iDŽ:",�C^�״f����c��((3�90��50�iU}0��zp��T �1l�\�4�ި[ׅbw�G���� �8�V�̌��H�_hìX�� +L{����|�m�Vl���#��YY��l�QYr��ْ����5$��� :#�A'� !͈D�Txg��?�t3�E�)UC��:��7@N�P������'6�r&����;RL~���?[�)7,�� �(�^R��:I4���LZxSwU�(��p�*͟�i�VS�3�}�uh`c��*>�6�Mi�l?n���^�6E���*�٩�iL����!W��g�!��tQ��V�T �WA`��eO��#x�b��&�}��q��sl��j�h�o]8�t��:��ߚ���^gPz�M�a��e�{�"AOo��h���0��7�����Y7��˧�W�y�o��}�15c�0#����,�y��Xƴ�< F� +�@���Žv�������z��,��$�ѭ���b��;s�\�Ɯ��C:T��&�]���7��OO�@����&=�Ѱ5E 4��P0�6�#]��nv�����j�1�f2/�y���%l�:�gW�K�Qп�t��q�L ^�c��f�t���y:�`��L�|��0�$A��+��EH�:����(P�lH�EO%���0\&�y:����0���$ �N' +��� +��T���{�nz=S�� +�;�X���h���#�K�mS���J;���_5�n�k�`��\��bPX*Iy��qv�/u� +K�SЋ�`i�����9@' +�#��&�b�e)����-9ӆ8rX�?��n~�O ?=��hf֯f[��M:�dq=��'���o��x�ƀВ�=�G+���4�6�~��(x�T=o�0��+�5�t)��dj�!)��L�%"")ߑ����2�Xr�t#�������5��m�Z^-WR�7�������÷�v���`S�ѻ�sѳ�u�m�T�u���2P��W�+���ϧ*6��LJ@������.Ac�K)"P��r ��\(��@K�X�Q1�� � !��GA����G���6�?����84w �9�r;����L~�/s��^�(��:�W�Cybj��z�2_�1�ݤ�#�!,�G K�Z�2{�d}5�?s��H�Za��ǻ��KxZ`���e"_9cb��п�'1�? |eSl3,��]~^a�0�/6��d�v�'����Zn����6�E � �1�����cK[!�ɜ����c,�ž0���;sHM��W�OK�*�(PPRV�p �w��q�s�uUVVRP���S�N�@��+��|K�V�z�BP +���B@�RilO�-�kw��K��� $@A�O����{oތ� �N�9��$�#8� ƟF�����.��y~6;�]L��y7?��l9��lv� lY++� ��;�E��uk2dQ�����w��f6���@�N�YF��`��jC��h㎢Z�u�Ӵ��&���MvS=�w�>{D��ߏ*Za�e�T�-9@���b�p��� V��u��d#��2khH*e�O�N�~�=wP���F�&��`���WV�R)l�!��P,��4d*�@����JjP� �m�%��ZC� E�c#H'�J2�f�Y~��'�p5ͧ��U�/?�e� ++j=S��s����r~ul��*��:մ����F�:����Ž�������u ���C;M�!�NB��h�XUtBCp^ m� ���B�[��~^A��Ԛ��nz`�{��mg�D�3��5��������s�6�+�-�vYҽG#�dĢ+��p?��#Ԏ��h�vq -+#>a€�i~�?�s�<���]Qk�7��Zj�)�Jn +eЇ��:�'��$]�¬����M�G��P�G��v�;���/��/�(��*J-)-�S��RPPPP�N�,��/H�+H/��/JW�A�����&�敔�%���p�Zs +g��40���&�6cӮ�k`����.��^v��nc����h��??�߈0 �2rݛ�DCʋGBMB \ No newline at end of file diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer index 2776418c472..621dda34649 100755 Binary files a/tools/php-cs-fixer and b/tools/php-cs-fixer differ diff --git a/tools/php-scoper b/tools/php-scoper index bb4f7834ade..fc805cf28c1 100755 Binary files a/tools/php-scoper and b/tools/php-scoper differ diff --git a/tools/phpab b/tools/phpab index fa027359c8e..27d91caf950 100755 --- a/tools/phpab +++ b/tools/phpab @@ -150,795 +150,840 @@ spl_autoload_register( ); Phar::mapPhar('phpab.phar'); -define('PHPAB_VERSION', '1.29.0'); +define('PHPAB_VERSION', '1.29.3'); $factory = new \TheSeer\Autoload\Factory(); $factory->getCLI()->run($_SERVER); exit(0); __HALT_COMPILER(); ?> �*� -phpab.phar8vendor/theseer/directoryscanner/src/directoryscanner.php�"�eA �P��7vendor/theseer/directoryscanner/src/filesonlyfilter.php� -�e���;l�<vendor/theseer/directoryscanner/src/includeexcludefilter.php��er���Ť1vendor/theseer/directoryscanner/src/phpfilter.php -�e�\�n;�'vendor/zetacomponents/base/src/base.php�Y�e��\��0vendor/zetacomponents/base/src/base_autoload.phpN�eH��¬�Lvendor/zetacomponents/base/src/exceptions/double_class_repository_prefix.phpV�e6��w�7vendor/zetacomponents/base/src/exceptions/exception.php��e CTs�Avendor/zetacomponents/base/src/exceptions/extension_not_found.php6�e~�9 �<vendor/zetacomponents/base/src/exceptions/file_exception.php-�e����5vendor/zetacomponents/base/src/exceptions/file_io.php��e�O��;�<vendor/zetacomponents/base/src/exceptions/file_not_found.phpJ�e,T]DX�=vendor/zetacomponents/base/src/exceptions/file_permission.php� �e�D�g7�Ivendor/zetacomponents/base/src/exceptions/functionality_not_supported.php>�e� V&J�Fvendor/zetacomponents/base/src/exceptions/init_callback_configured.php��e�: ��Dvendor/zetacomponents/base/src/exceptions/invalid_callback_class.php_�e -Z»�Bvendor/zetacomponents/base/src/exceptions/invalid_parent_class.phpE�e����@vendor/zetacomponents/base/src/exceptions/property_not_found.php��e�"�yA�Avendor/zetacomponents/base/src/exceptions/property_permission.phpf�eW>�D��?vendor/zetacomponents/base/src/exceptions/setting_not_found.phpT�e�H[Y�;vendor/zetacomponents/base/src/exceptions/setting_value.php[�e���3vendor/zetacomponents/base/src/exceptions/value.php��e���.Ѥ6vendor/zetacomponents/base/src/exceptions/whatever.php �e�8�K�0vendor/zetacomponents/base/src/ezc_bootstrap.php��eU–~P�+vendor/zetacomponents/base/src/features.php�.�e� -'��'vendor/zetacomponents/base/src/file.phpIH�e���u�'vendor/zetacomponents/base/src/init.phpV�euko��Gvendor/zetacomponents/base/src/interfaces/configuration_initializer.php��e�E��8vendor/zetacomponents/base/src/interfaces/exportable.php��eB���5�9vendor/zetacomponents/base/src/interfaces/persistable.php��e9�J �+vendor/zetacomponents/base/src/metadata.php��e���b��0vendor/zetacomponents/base/src/metadata/pear.php�e��<���3vendor/zetacomponents/base/src/metadata/tarball.php�e�� g^�*vendor/zetacomponents/base/src/options.php��e���K�)vendor/zetacomponents/base/src/struct.php?�e�t����<vendor/zetacomponents/base/src/structs/file_find_context.php� �e�-�њ�?vendor/zetacomponents/base/src/structs/repository_directory.php �eL�8'U�<vendor/zetacomponents/console-tools/src/console_autoload.phpr�e��i�>vendor/zetacomponents/console-tools/src/dialog/menu_dialog.php��e1�w�ԤBvendor/zetacomponents/console-tools/src/dialog/question_dialog.php�"�e� ��t��Qvendor/zetacomponents/console-tools/src/dialog/validators/menu_dialog_default.php��e+��Xvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_collection.php��e�O��N�Uvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_mapping.php�e���F�Svendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_regex.php��e���{�Rvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_type.phpC�e(���9vendor/zetacomponents/console-tools/src/dialog_viewer.php" -�e?R28��?vendor/zetacomponents/console-tools/src/exceptions/argument.php��e�X� ΤRvendor/zetacomponents/console-tools/src/exceptions/argument_already_registered.phpE �e�/wm'�Svendor/zetacomponents/console-tools/src/exceptions/argument_mandatory_violation.php��e ���Hvendor/zetacomponents/console-tools/src/exceptions/argument_too_many.php��e*Y�ۤNvendor/zetacomponents/console-tools/src/exceptions/argument_type_violation.phps�eu[�:M�Cvendor/zetacomponents/console-tools/src/exceptions/dialog_abort.php��e�"��@vendor/zetacomponents/console-tools/src/exceptions/exception.php��eP[� �Jvendor/zetacomponents/console-tools/src/exceptions/invalid_option_name.php�e�d��Lvendor/zetacomponents/console-tools/src/exceptions/invalid_output_target.php��e�]vmw�Ivendor/zetacomponents/console-tools/src/exceptions/no_position_stored.php��e��RGy�Mvendor/zetacomponents/console-tools/src/exceptions/no_valid_dialog_result.php��e�X���=vendor/zetacomponents/console-tools/src/exceptions/option.php��e�}��Y�Pvendor/zetacomponents/console-tools/src/exceptions/option_already_registered.php��e�-/ߤQvendor/zetacomponents/console-tools/src/exceptions/option_arguments_violation.php��e~x��Rvendor/zetacomponents/console-tools/src/exceptions/option_dependency_violation.php�er�����Qvendor/zetacomponents/console-tools/src/exceptions/option_exclusion_violation.php�ehV��m�Qvendor/zetacomponents/console-tools/src/exceptions/option_mandatory_violation.phph�e�YXpA�Kvendor/zetacomponents/console-tools/src/exceptions/option_missing_value.php��eF�^�Fvendor/zetacomponents/console-tools/src/exceptions/option_no_alias.php �e�����Hvendor/zetacomponents/console-tools/src/exceptions/option_not_exists.php$�e�6E��Svendor/zetacomponents/console-tools/src/exceptions/option_string_not_wellformed.php �e��0���Mvendor/zetacomponents/console-tools/src/exceptions/option_too_many_values.phpw�e����Lvendor/zetacomponents/console-tools/src/exceptions/option_type_violation.php�e��D/�1vendor/zetacomponents/console-tools/src/input.php���e�&Ǖ��:vendor/zetacomponents/console-tools/src/input/argument.php��e�h"��;vendor/zetacomponents/console-tools/src/input/arguments.phpt"�eث��Jvendor/zetacomponents/console-tools/src/input/help_generators/standard.php9�e�B�j�8vendor/zetacomponents/console-tools/src/input/option.phpJO�e"��N��Evendor/zetacomponents/console-tools/src/input/validators/standard.php��e x��=vendor/zetacomponents/console-tools/src/interfaces/dialog.phpT �e*/Z;�Gvendor/zetacomponents/console-tools/src/interfaces/dialog_validator.php��e� ��5�Kvendor/zetacomponents/console-tools/src/interfaces/input_help_generator.php�et��Ӂ�Fvendor/zetacomponents/console-tools/src/interfaces/input_validator.phpy�eeutov�Lvendor/zetacomponents/console-tools/src/interfaces/menu_dialog_validator.php��e���T�Pvendor/zetacomponents/console-tools/src/interfaces/question_dialog_validator.php�e��&c֤:vendor/zetacomponents/console-tools/src/options/dialog.php2 �e3�Y�?vendor/zetacomponents/console-tools/src/options/menu_dialog.php��e�1v�f�:vendor/zetacomponents/console-tools/src/options/output.php��e�0ِI�?vendor/zetacomponents/console-tools/src/options/progressbar.php��et�e�%�Cvendor/zetacomponents/console-tools/src/options/progressmonitor.phpF �e� ��Cvendor/zetacomponents/console-tools/src/options/question_dialog.php��eW�ia�=vendor/zetacomponents/console-tools/src/options/statusbar.php� �ep�~[�9vendor/zetacomponents/console-tools/src/options/table.phpL"�eseK��2vendor/zetacomponents/console-tools/src/output.php�M�e�W��?�7vendor/zetacomponents/console-tools/src/progressbar.php�:�edm|��;vendor/zetacomponents/console-tools/src/progressmonitor.phpZ�e���q�5vendor/zetacomponents/console-tools/src/statusbar.php �ej ��rM�?vendor/zetacomponents/console-tools/src/structs/option_rule.php��e ��Avendor/zetacomponents/console-tools/src/structs/output_format.phpk�eh�+-�Bvendor/zetacomponents/console-tools/src/structs/output_formats.php��eb!-�1vendor/zetacomponents/console-tools/src/table.phpBt�e��e�5�6vendor/zetacomponents/console-tools/src/table/cell.php�e+(Կ�5vendor/zetacomponents/console-tools/src/table/row.php�0�e� -/���8vendor/zetacomponents/console-tools/src/tools/string.php��e���F)�phpab/Application.phpJ&�e� ��l�phpab/AutoloadRenderer.phpc#�e� -��� phpab/CLI.php�_�e*@�phpab/Cache.php@�e� "9��phpab/CacheEntry.php��e���Ф"phpab/CacheWarmingListRenderer.php��esy?���phpab/CachingParser.php��ePI!��phpab/Collector.php� �e6��6�phpab/CollectorResult.phpT �e�-���phpab/ComposerIterator.php{�e�㻂�phpab/Config.phpZ4�e wH���phpab/DependencySorter.phpm�eoɢ�p�phpab/Factory.php� �e��#�T�phpab/Logger.php��e �I9~�phpab/ParseResult.phpg�eW�ߪ�phpab/Parser.php�S�e������phpab/ParserInterface.php�e���phpab/PathComparator.phpf�e��ha�phpab/PharBuilder.phpg�e=61_�phpab/SourceFile.php��e��%��phpab/StaticListRenderer.php��ez���phpab/StaticRenderer.php��e��}xȤ#phpab/StaticRequireListRenderer.php"�eLX��O�phpab/Version.php� -�e���j�"phpab/templates/ci/default.php.tpl�e/]i�phpab/templates/ci/phar.php.tpl��e�~���� phpab/templates/ci/php52.php.tpl��e ^@�N�"phpab/templates/cs/default.php.tpl��e�Bw#��phpab/templates/cs/phar.php.tpl��e��2q$� phpab/templates/cs/php52.php.tpl��e�&�Nˤphpab/templates/static.php.tpl��ep휺��"phpab/templates/staticphar.php.tplW�eT�.�֤�Y�s����b��k CL^�_j?�[!QI��4F�����6�ۻ{:��&��)�nwo�{�{�_~[<,N��ޝ�;�ċU�RhL���Ç?�����?��D�B/z�I*��?���3��J�Z���p��'��{����4 ��4�#�"��C���Ʉ�7� �L�d.Z��'��La��4�x$�E2��Â'� M��$~ -|�^�8� ��9�f0�#? >!��<=Wz��n�i��$��z)Rēz�,����M喊�4��V& �$�@�S\8�KZ᪓� �<9;� �Z�K� ������(�~�;7��]����U{�kҊ=\R��f�zghh6I ��0 �]����g�3T�v�L��0*#�"\x�PY��`�r��۬��ͨ�vD%�8��i��0D���!�d��e�t8]��]#�F�@%됓:C��Iq�G�������epmY]iy�ٷz�9`X�4��a-\��Z4�R�j8�㫡���H�n�̶�W��&�-��j�ݕ��L��e��$��!]т������L�d8���;n�WE{��`�kC�ff�ѬE��t�5ehٺC4z����+ �t� �ܒ��Z�V'�3bh]��u��e�|g|Zx�Go�i[u��OpӮ��E<����Z"�]���i��jp��U"�bf�����邑=�����C�.�������l�ۘ��I�N��Df)��}��}-��?NNH�����d��J  &x�!%�f8e�+��9,>��H�~5��� L��$kr}_��i�*M M(��P��W��_�����bM�m/~��-��j�����Ά{e1�i�Fܱv@m�D��,���Æ9�,34����hM�s-�{ Ձ�T�5+�*CP¶�TB�0����e��ȅ�[/ �q��~�C?˝�^&|AK4N��X���~���� ����!�͋-�ߪ��oڭ�f�y �^���� ��_���a)l���1�; ��@��ԑ�WπOØ2���(�A{cȬY��[�y�>?�Y�aݍ���������޽-�%U�,���%`��U~Tvͣ����藡���M��~�-�³')`�B�ה u�g�po�Vn�(��@���'G5Jq[ 5��a��-�y�rD1G�߯��U�q�=5K��s|8���z�#�9�^�_̕�Ŝ���o�Ϻf�,��痽��*�kX���R�6�h�B,B�3�����]Tv̓g���N(�1�bT�Ю ~kK�)[�4�m�'��է�Ԡd}8�s��nע8�c�d=�Ơp���L/ �~��Pu�@Q<���PŽ�C��qS kW|<�1�ɪ��2jwɡz�� r� ��7C�S�Hת��V���ci9��UP�c���7�.AZ -T�(��C��K)��QbS��ϟ��"}�,,#�l_+��Y�Qf�ڑ���OGK�ٶ����ꧥ�#C��E1?|_R�Q⯺H6�b�]��:`� ���;�/�HUV�j��z+�ނs�>A�e�m����e�<8���q*� ���������%G���'��yv�k���ʞ�������x�y�U`��e�9ا���L�:���ݓSka�B�+ -W�I�J����/O�� f�e�6���M��|Wq� -��wo�Į;ɭ��Đ睌���`}kVh��g��tD�LUI�Rz������ m���HsFͽ1�>k��� ��=�xN��#��V�����&�gv��S\���ґ鈵�Ee���ڎfw�2jr:����k�u���i=g���[ ��|ٹ�_����_�����oůU�K�#_��ͫ�_&X� ��yxʡf�4R��t��~_�U�|ƫK� yi��������.ٚ�3I�֌� d��EI{s�t����g�� U�x��ɷ��Vmo�6��_q�V�.ܤ-�em�E�蘀,yzI��@KtLT� RJ �ﻣ�F�6�%��s�=<���v?:}�|����wF]ox���/^�|�xFK�UB����N�����~R���U�h FZindy��dJd�lcԺmT�A�Z+Ai�uk -�V�J s����nU��ڸ�m`W�j� -AS�F�^��jY���7�ćf+�'���[����u�(κ��l����o�,ԛ��.ѻ� ��$K�b]ߐ頔�U�i��,TH8�ĺ�$�)�+�sz`V�%W�B�E�����!��9�8>�H�$?Oؒ��s�J�Y��,���q��OYr�}���0N�pyʦ�$�dGT ��<�SN��2�$�*�q4A.Q!d�atഎ#W3�'W�Kb������:n{�F1 �) -�gCO̊zf�b!b�!?g����%O�ĵV�S��]�K3�T��4��=�y�������ٵ*�����4�����q��'q-�X���]Z��qv��Y3��}��rv?ݾ!b��MgNFm�Y@ؽ��t4r��Kȶ2��|<&�FJWW"����n//UU�[������.Sp�n�*���DC#�Qr��X�FU8��D8+�T�z���oE���z�ih�["+)�|O�ŧ�ש��k�Z*^�XWwG*��q��>����E�e\J�����5�ѷ[�n���G���-��5�3y�>�H�t;<�Ag��3��+�����~y�~�b߷k�;ش��~��O�`|,^A�v�=]O/��LǓ�eC���q����Q/={�>} �i�C���A����5De��c#E��#�Wh����'ߒ���V�Wro�7�W˹���~}� �Vmo�H��_1굒]ѤW��5M/��J|,N]���1*k��֩��f����ͩ��;o�<3;�o�ܮ�������b������a<�W/_������_��s�L���Jx+��l�}%�Y�Yƺ�J�oTr��$ -U���N�u�9�<��T��P���YY���;XzSZp�Vk(�yu�"IWi,ɁE>�V�Uz�V�J`���4��j-+���O��i~ q�')ٕ�h��7-.����bՁ�����|*�`ɭ\7$�ʋ*���xTIK��!��ΓT5�d�Q��h0j�� f�Ԉ��6פ���+Cz� ��.*h��J�Tf��pƺ�H?E_�Ɩtr�Q���b/.[���B�����RQ#a&�� �2�W(d�󫐟O#���B����|��\xb �|b6�l� -��yȄ� >�{�a���#΄�w����s Їa3���3�fX 4v�1��Й�=���L� �|�8��6��0��³C�4_��@0�].��3�! � �����y�f�I��%�c�`��ǚp���C�D����A�g��3�� {�0#;�2�-z��\{f�c���ء"9���x0!Wb1��� p ��a��@��Y$�-��d ��>^N��/�G, ��I�D���֮�:�M�HV^�_"Ԃ�)�u,�oF1g t��&FE>�^��s��3�a$ ��%ldZ+�tx���� J� �5��v�Li�O�v/8�o�MK #�m\ gڲ�팳��?�kE��M��q��D,�\i#�5�5m�ݏ�,g����C'�e8��  ��Eǃ��5]A�VB)��<�;�)]��j�G���L��Y��Z�4bs��u�4����OV4� -�q���Ҧ���مb��Y��c��<{s���9|,w��O��u�4�A� -U�0�7�]|�j�'�&e �ɍ5�0�>�tMJ�p��2��M|�=�VƀNŎ=(� -�o%Nl�1�c��/5��宷z���� -��eyں=y ���?J��a(BU�j�\��J���j�A-Py�+L[��Ae(h_1��K���_?��6;n�4�?�z��Vu�Ka���U�r��A;��'��{J���w�S�~�E�����+ࣩB�=U��_GU��:�(����@����`S��s�J𒄞n����P��N���;!V�Zk�G��AT�U��~��eQ����e巿���<�Z3z�w��"��=��c��;�xw��y+�z��~�ᕌ��~e O�*y�~� -��/�d�:�>���u���^����σ�����+0Ϳ�/����c�ݟX���σ��Vmo�6��_q�Z�������"KtL@�8�xz�`��M�|oON~:|{��Gp��0�2�I�����_���G�<�h���F�`d-͝,�p�L�,ʺ1�mJ� S���RA�[�K��*Uf`�Ͷv�l6��}궁�.�u�g�ff$�ٖM# �}W�h6Y�$�T��/�-�Z%��6h+�w=/� ��zO*�z�u��4�%�l��ȴWJ�̥ӡ��5TH8ÃU���WY������]�l0ۢE��!�s-t�n�j��{@ <�{��``�5ҔYU?^��8=Hd�b(KK>*�J"��”~4�=�K�zOd�=�JR!a&�*�(�f��V7:��@�X��FC�H���=V�l_f�N�Tg[R�*/�Z]2I�\��fɵ3��2����|�ޠ��-ob~9O`>�����a�i�D�����~�5���(Ȳ.�o±�39yB�N���N��*H�x�,�I��I,Z'ׯ���T$���I*�I�+��h��C�,�r.�<� -q-%����9?�)���'a���U��˜0�$�"�*�LB\:�D�sB'�r��l�,�i8��E��e�݆ X�I�~����^h]'KE�E�bFG�@H���O�pRC=m�_��y�q��e& �B~�E�b�E�f+����G$��D@��djA����B�����[�V݀�&鬧I��^\�i=�]�,n��2L�ǣ���8�Q�B ��H�U -�g��i%��T0M����mɨqr-ί�ד����p~���w7���`prqs޿�qzyqv~s~y�^����8�o�g!�e����H��4D�ʉ�O�%�l!��4y�l̤�%w2�m���<�P� 9���9)U�S+��]� �� �� D?�) �����y0������k��Ȟ����%V��"|�2�M&��A�,KVKP��Ha��N| =s�NZ�P��-y���KA��4m Jc"���HA�B�� �nq��+o4��]�G��E�rqֿ a��n�W�%�H4̌Ƌ'߼��{�������Y�L�-�,�E�L�c���d"=��^���W�˳w�7 ��� '�NF���Y�}������� }VzO���h-���#b�xFj��U5!JP+4w�{ -����]� -�����i��wA��`{��^�� ]�ܑ�D�������2M�`��{�$]w�p|��g��c�� �Y e2G�7�K b�x, -��Ι�艗QT%���0_���L-��P�1�X���ŀI-��̱�iv�y�̔\�y��DN�e�w4��hY�K�`q��h_�Q���M��Bv7�2]�J2M��[%��/�8��� 3������ʓɂ�a�khzG�\#:p]�P�r9G*�R�H��l�D�$_���lA�떁����2�L�K�@���F������+�%�5)�0��f�(�"��6܅��� �,��S�10�Z��������~�r���UH.���L#���1}ko��ߎ��o�G`������ ����^n�&sw@.?  -�+W���EN:8$�4�e2ѡo�1����1@#qB��a��Z��1� a�Jà�9S(��e���dWF].(R��^%����F��> - m��S]�$˒q�dL���@���(p~TA�52�\M�^ً�r�"8��� pdjZk8����@��5��,���}U/Q{P�Fi���� �y�!�9���R��Ƭ!�8L�̗):�t ��1=bDUDۃ��1F�l=1���?�a�L�0R]Ntƛ``��M4�i�M��k��� ��PDW���Iv9����V�eb�(I"wC�l����5�[6eY*��B\� ��dO�R� ���(�7��9W=Y �0��;���p�qZ�3��Æ�+d|��c�@�d�� -}���y�q(��Ob�d���q6���+gaQ�Χ���舕�Z��<~�th� ����,�}�nx���C�/V���R�������lj�o������/����/�|;^��E�+�vD�F�u�&P�מ�6ȍy�B̕S��F‰5=�$d�ф�j�,�+ �a0pAf��Ɏ� �� �lK4z�������|�o�>��������C��qTlo��o�����ܳ�m�'/�Zw�2z��~z���D����v����-����dڲۦac�F�rX�]�T���CMB˶�e"�/���L��h�>=�|?,�� �l� �~�n���3�b���T5E��l�\?=�\�`='�ǻi��}�G`&�Pz� @��x�}���;I�{���%�8I=�AR�SӞ��7� ���v�m�Qy0�p$����J�ffT��@�@8`��S�Wv��{Tt;��Xղn!�%�=`@t���O�R�lwN2���47E@��8�Q2����)��Sf�m�X�8�D3�q ��T�h y����@������R�El��%����e�0����=ú]�����-f���s��>{��}���ּ%����o�ác1MT]`k�a�����M��{I?ڦ�`�{���~�<�Uz��A:��Wҥn��x" ��񻭵}����l��|�5 ͤ�� `����v�ڨ��2�� ���ԭ�5����3�[����]�:���Mq`�-N96��`>�b.�֑�)����e����-��pw�+n��^�A�e�c2ֳ��a��[�� �K��/��jL��*{�?�T[L�A�b�ĥ�7hL����|7�öPƆO - 8�>��c�#@W��`XY�ƀ\}���yv�1!@hb@�µ�瀭/v��%� 0J%'B)̆ꈒ����>����0�d�lMѾ�X�?)P*�[����U�U�i8�鬾�Z�]5u5Ѧq����IF�>�k]͞�,�z͎S��$k�D �/ ��І0b�;5���yo>8d]t�=U*�D�Z>%� p�wt�Fbr�N���i�FV�H���� T�{5l��:�<���,������]�����2��V=��b;�e�b�FB�r���!U�o�*F�N ;�|)�H�0b��3�bp�} -�J=������e>���4�ތ�K���|��-�6x�h3U��~s]h��6��l�"��Cȧ���&���a.������s�j*�~3�Y�e��Pd^Tk{��.È�o�S�� d��r=���`��z -o���9C>A�V ���Mx[�:w9�������?�ˎ�P����]� ���J\��`ب��a���b}P�� -���5��t� ->s�w���D<>>���;�)��v$Z�x�o�]�վ�L�}c��~'S�`�ڌ{>��A���.e�7�f�1ƤݱzMh/ĺ�d%�1 6E���4)6zY:�5���q&Ӝ6�9n���j�����c[�U�d;���s�R$yL�"��EȤY�Άۘ�}]D�&�oMU0��>�aR�L���`m��!��e�c0�U��n�w�����i�?�������;0�7`5�;[��A�cE�MA�q�L@����w&@��� Zczꕌ�h��`2{�Vo�@� Ѻ�FUg���S �V����V����b���6�/����H��� �W�� � -� >J�/rc�R9�A����Yg7>��54?ҵp' p��J�6���u�~��� vե�)ͮ2�Y<�wv�� L�����/s�'+ �/Z�ʌ����JcϨ���*��1��ip��!.CZw�h�k� ��6��|\�wDK�]jXZ�$UG��8 ��T]�1�U��g��N�R�[�Rz��K��}~tEW ���ġ3�\�Z�� t��@�ٵ1p�Ú�E��y��V�!淪Aag�[>�r��<�#�l)jb�Ub�$~>�f �'�+��Q��(x~o)o� S]�q�Ӊ�̚��u] -T��ﱷ������&W���v�/4 ������@BVχj��lZ�:�y|����ea�ԡ��D�1 ~V���`pT��@%��:ƈ�'E��Du+�|�V�$;f�>r��Z��^�]��OtwtJ�" �����.u�����8 ��9�9/ -H���@t;ı��Ֆ�u� }&�ޖ��^�A����6%�ly{z�oN�u�Q��ma��V�J���v� ?9�� +TVh��hkó�Û�W����Mpq�S�������{��cmZ���&�NYZ���xMRq�[�F��t[�oJNͯ�i\��.�W|6/���!rS������?��������8Ƈ�䔅�m{�[#������([�Ә{/=�-�_�z�س�%�<���� �E��y[�&Q�a���{%I����M����$�]h��@��v��k�NnjPK%�^�Ia�s\�)�;l�F+�פ�����]�����UE��p m=�Bc�NT0��n�"�fj�՞�}�[��깛k�*wɒ�d���׈�U].X �&wi���_a� ��mQ���f�A��������E�oU�\� �5�=�x�6��*�&�כ�� ` ʘZPF���m�e==���A����,��xHw�h@_�n���d�LA2?<��Km�#�L�Sj����\QJ�9�����X��e �:�ȝD����+�"� �U,)ަi�ҹ�`AӅ}q���D�U�����t��썎;L���DZ�@���.R��̱|�g�e�o��+6�B�q;�|e�������D�f!�&���[ٛ���gtk�S7�yy���x�!��+K�6��8�p�C�+Ԑ��=\l#���z&}�7�Y��y9�eq�r�Q�_!�aHE�{s�],�@ i�Ȓ2�?~�{\�����%�˩,��WQ[<��� �9Y�ۀ��z���e�n�Z��� ~=.O4U��6+�nZ*�7_�R����]�~��e��b���J0JV|6 ��Z�2Z�KJ�K�kF�=�%�����Щ���nEg��#^d�U8��J.0�AT�yfC؞֪*�;�� -(�( @��"D�e�>�h5a hb1&��M*��ȃV�Y�2+|-��|U?o:�z�k_�����ܙ�%���Z�NL����7�u���b7^K(5 �"� �Ҍr�I�+,*�-K8�u*ӯ�93�xu��|� J�u{fQ��d��}�"�� �,��O��@/��v��������F�b:���� ���rn��W��m�P|���� -���YN�w���K��/F�a�k�� #炙�I�#�K�U�;�jk���ȒJ9�+{�/ �0�*��ZG�����rH�+�q�L� ��|�?��|^��N'7x�΄к]�$�V���i[�p˯��4��x.n�5wt0�^|�����*^��=~Ʀ�N ��`�L靃���́����x�`Y����> 2�:4�\��:ף�x����N~�^��"eQ��М�n-�+i�e�����VT�����x��w ���els���.��\�,Qp�dY��ASС��M_Te">����I���}=ͮ��#c}#�9��L\RzP��Y��� v�-Q���V��f���n����n�8���Dnr��Z�����+l�6,�A� Z�DeRKRQܧ�!%�JCQm�$>��́�!��}q,F��È<�i�E.h�$�2�4��B}�* �8���'W�-�մ�1��D�+*�,E�jAw�hyO�-�aԈ> i�\Kv@���5��L�Е�X�j� g �����jP1}D�H%�76M�� ����b�2*�3t]�%ˎ����TGV���I%Z���ܺ�\ϢlR�d�cL� Ȥ���wr�Ft�|{s�ƪO�L�ФTp�x����-u�(������c��@�AS��6"Ү�ڨ�/!G��?���� �O�̂6��Vv-~à[�g��RX��J&�Ƈ3�F�ƚ�ʬ�](�E%��<�j���Lע�!b�],�kt3�Hݐ��(���1�����#���v�څ����d�^��]�^�%����?�j>&�%CW�\H�F�LE!��SC��6�b����YI3 �x�M� OL��Ud��qb�6�2�W����C��?����ȧ�}P�7n����t���[����<����H�.%�YJz�ak�[������{K�<�~}W��]�&ރ�����Z���\Aʚ;8C�8��9�������jg������p�}�Ӈ�k�)��w�{v�:*3����W��(]C(�E``�/ش1�9:���Jb�x��%� U� ;�3%fja�����r8(���s��O���F=� P����b�"�{p;*x��_�tm��披�z��r�U��^�ts|��� ��w�sI�k��oF�ߍ��T�o�0��_qB�Z*F�}l�u�ZSCW�Se�#X vf;K����;'���� �ؾw�޽�dzrYv����H�h/�V:�D�_�\8�4ռ��B8w��q���fq����$�^ �� ����D�0oΰ��R��#S�L���a:�}��������M1V�+�E�"��+�� R�?���W� �)��Q��K:���>‚�D�)N- -P�V��� �1yiʵU�҃�5Z�T%�1�t�q pLK\צj��n���wb���pP����NC�J�A��gt��2�K���B -Ct�n��j�oA̜ "P��=�s4��ޗ'IR��@����I��\�����cnu�ΑZ?*eI��DIUIA.�B���Ш`��������� v��,Z,��� �D�Qw��8���0�}�ϾLogp7��Nf��7p1�\�g�鄾F0��s����H�Q*|*-��J+�َ�b 얶O�D�J=�W"G��O�aRJ�+帵����+僩x�� �)�Lr?2�XX��:$I�>G�$a=���{�/�d��-����%j�b�e�]S�����ѲhnZ�X�U��p��� � -�"[�Q�db�@s$�߬S�i���>y��6f���ݡـ�?�pA����������l��`��PE�O���� �Q��Fd4%�n��֥�|~�n�}R̊���?�l�lƜ�l��ԞJ����2�{x�Q����� �߃�^hD懒ҥ~r�R�/�='�}r�{��9��=�������H���:��j�"ߴ�V�V���Y5�=F�~�9t�wxl:��٧��TaO�0��_q��(Ui�}�+�jնV"e�O��\���l��M���ݤ�M#R�ƽw�޻;���*�0W�2!������_2��'��J/�1# -w�o��4��U>z\1N_�Jm�4�T�2a�G�#�WԠ$:��P*�)�E\[:(6�e�Di� B�����j)�t�D� �4��# 4J?@J�X�W� $���j̘N�D���YnA5��EE��NJ4�ȘM�,i]������5c?(���q���@����ѩG�l RY� �d�6]"VV�`�{t�n[�8޷IT���K����>���I6M3b��H�,�$����y49&��Vh ����<���*b�YL\ ָ�F� �&�e6th� �n�^L�(��������̢\��Y4������v w㛛�|9�D������z��-��6����!����C@��J�S��b*�����S��MK�'S!��$Of5�2����C��Ƶ�Ʉƣ��q�=mݖ|&�\*�I�ౝ�0d4��0�������5|�2�v{o��w�-;�N�*��ρ�ּ6ڸ��ɤ��$b��4TݾWo��:ϸ��*�$�ɢL lO���2xQ����W�/��Tt1��V!N���������\�բ�o@��۴W$��[g��f���Ƀ�j ���f%����y�IZK�ӭV�+��"���nD������o����*q�H�t�� ���������!/�a��uvϋVVړ���9�?oaa�=�r[~�X�s��n�� -��spq��Tao�0��_qB� -��i�umAE��j�~�Lr����NSV��wg���v�"!���{w��tR,� �v��Hf�VNH%U -n��?�Saq��PY��D��.U2|��p�q&�%8g��1�aN{��1�Ez�*a@���O����ě���[R��Pi� J%�Dri��T��{" 4� -���X+#ӥ])4v) �7c)Ѩ!c7����u��Zʞ�=�F�X���hS�Z��V�ȣs���w�}��.ˋL -{t�n[�8��I����K����?��s�aVU��p_�4l$����I4|O�̍��Z���R��|� V���LT -4���?��R�As�_?ؒ}n�@�Dv-O���:Y#��{w����y_0ً�?���.~�pFp���V�'���&�7"�! ��9�[�/�C�m�o@�^�5�7Bs�(�n���a����/J����Ǎ���joW�T�e�]��t|�M��� h_�� ���]|x��vk�/ �=��]Ee�x�̱v <7��-�m���+��7����,e�]/���:X'����TQo�0~ϯ8�i�J�&�e��KY��JM��G7�&�;���ݤ bB�PU>��������)�?:���BȤ0� . -0%~�ΙF{>e�.dӚt�3s0���e��ȵi�B�ˍș���� -�@�� -j�vU_m �]F`�B�Q=H]�x�F�����s�w8"�rS�����֔��9��Y\P�vD,Pa�TnUf��*^�d+P�7T/�R�yOF��eI�Vn:)՝c��DV���5� u��éC�l B�h|��\&�D�n*�D�Н�} �x�%�+�1`N -���0c��PӼ���m'��HU��D��������17�B�ɭo����XC�2�"�km]���V��[��`ئg�z�$}��lc�G� �(�y�D���rq��]�\q� ,�0[�Q-b:�!��-�*�/ƀd�§FYĔ[G1�S��NK�'�`��<#y�ذ�����J4�j�mk5��iz��Ua��8��_1�*-�8R�G�z���ktT���~�Lbk;�8��j���8�-���� ���{3o�o�����k�a!J��� )d�����oX�i+V�C�k#���dM���)� ��( k��W�v�c��B�2c6 -���kP�Zi�����b�\(���r�yťi� �6�r�����3��8$� S`�h�S�v��e���Y B�Be�P��t���k�T'�n -Q�y��,��O�E�{�R�TŘ�ߘ�$�:} # �?�������2�6����2�EbU] -&S��=��?IԖz�J�;fM�˜z]�M�%� �8��]�y����]�^��M%�Z�|�|o���.?�x�vK�G�Z�d*��<;��@n���<;��<��,琫�\ۡ���DC�m�d�������y�^�t��?�������.| ]�svݹ��{��Ô�� �,���]��E�V�� -T���� [m�<�[sf�Ǩ`�U�k�jD����9��}r�0\f'�O��Wm V =�0�Nݦ��w����g�E�L1R�LH_� -ȗ(Ԇ�[A���!��4���R��c`m�Bf�/�P�YY��g.��3:�y6[G!�����:�D����#����&��xj�.\����U��Q�C�b�/l�f�7���{���6�ӧԵq�W~��sr�� -d[�0����4xPZ�h���i�BO��.(���N�m;�o���+��ЖZ������O� ;W_-��+��t[����QJ��?�i�\������{��J��i�]]���Vzhɥb����[�gOD/���lv�� wz�чq���{�^����U�n�@}�W��J\Dq�ǤmJ.��T1i��h������14��;���� J���3s�̙��Y�ʽ����c�"DZY!�T ����E��h��X�*�ZG�[�D�( -B2���P��8�ED�^�J����O4�2Zȴ���(-�uF�A�P�b"����|rqK���X5�TҮ(FPi�KJ%�Xri��Tt�9" 4�s���7F&+ �Rh��̩ޜ[ �-��Nܖ�^7�lZ9�c?(��~��� �y��N:P�BY�>;�S���,O�P�C7��jǻ&�^��@�V@/�@XF�`em~UU �#<�& ��o��4�zK�[̍J�(H���4��b"'V�X�TT����2��Q�m�f;h8|U��`_�>��KP��/�]K����\.�ۤ����?�~��l�}��ËeZ�/Vz���n{��wEҘ�?NN����\�v����''۶C�h��N^�@.���W��z[����Vao�6��_q��e�C��)��%��\�C�8��5�@*��0�^����IjA�J�2��$)�yG�lwۢ֍�Z){��d �#"��~�>. ���z�S�����P�b��2�Eby�I��C��{ ��m���sR@������?���b��u]��#<�&�;��{��, -C��Re�,1[�V�`�W`��l�\3VS]���Em0�*��L�_�]�:�(}���yA�ȃ�A4��p5]��_.�*X,��rF0_�d>;�.��~�C0�L����!Ln%n -C"�����x�OrK[��\�%Gy*�X" �_�q�Ql��D�1�#�֙���Gm��K�`��)����:���>���x�����!�aH�k�_4�$���S��CG��Ba4�},)�c՞��fպ�y�č�t�<�M��ǐٿ(y�/���t��rdD���S��p7|����`���w��Mw�۾v�~��ō��w���5y{�����ߍ ��;�1��Î�w� o'�уA�w�{�W��T]o�@|��X�>D����jJP�F&¤Q���^�)�ν;ǡ����M��V�%�?n�fvg��Y���h0`S�AȴrB*� -pk��� ��ZeNj%6�m�Һ��q�O�3�� damH�#� -� ��������A�D�l�"z�d��/&�"�ϥ��H@#ݚ�H �6��"*��ro����@��09{�t�5�X;ЍBcײ��l%�vb잸ۖ�nu�Z9r�c?��-?� -`P����zt)������+;�/3�%ae��Beݺ;�A�[��>��V@����p���ڹjEMӄ� �)��btM�M��G�an���j����/� *R��%i݈�;��@*CWŐѶ �q�^��I$�� �l���7N!N{p>N�tw��jv����|>N�$��.f�e��g =Ma��3�{�\�d�>W�M�R���(O�NK�'[a&W2#{��E�P�'4~P*4���ZK"s�G)��e�_޺)�F�~d*1��MH J}�*���.�o����G�Q�M��]S.9�f�5r�8hi s�\���%�FX����{-G�?Oޡ ����8|^ -9x�| ����L�9�(l޾I�RtL�ƈ8��%��qI�{R��1(,�gD{�f�y0yd���}x�:'���¾_����Jg�h����w�Xn��<�0�L#xi9wa���q삳��o�T�n�@|�+V�R���cR5%T���b�(O�q^�)��{>ǡ����&��D !�������rQl�^0�`s�!H��PZ����KQb����,[ �05z���b2{�X8e4�L�%q0�O%Q���3�bRI?�Y�ZX���t"<�t�@�h�hd�����aժr�"�3�H-b�ڕc���G�e8����3>Q�Gj�6T�J��}�5Q�$Q|��@iz�{! �� -��oi��U�Ɓ�5�r� -:o�V�y+���ǒ׭�+�M3F�������'8�����s����qP����LrIX^dJh�э����!1+�!o̺[�1��犳 ��z,�౱i�Z ~Rg�x��D���aYR�~W�"�-��TI�"���y�~P>����q��]�!��i�D��-�� ?��$�0���$�܆�%�N��'�2�Ű���"� -��"��9L�;F��� ���§² R����t��j�4s* �j�$��i%R��<��KR��Uɣ-IdB�ȕ��}��۸ْo������/����ԧ����o���� Q���܊�ni�ޠ�Iuv�ӥ�W) ��d���-r��Nha�{hG��@� ��EfQ$[�i�'B�IP� ��v1�y�c�C����{�4��ǟ!�rZIGn@c}� ��Ґ+r��S�>�}������;��"jzS��u���s/[��]��;L0�{��!�;;{E��k��ù;鎶3F_�4#��;�ap����v������Tak�0��_q�A���c۱.Mf6�ӕ~*�|v�:�'�u����;�N� Fg0ƒ��{����ZUQ|r� LU� ��Bi� �+|��a��O�(U6e��~\ -�&��WF��_Ba��J�v��7dT I���ajj��v�L�|FS�n9�[2��e� -7�e�T����Sǁ�����P�\I���Z�y@ƤB�V�K�dF��V>4���چ�|!���,,<�ǂ��@�a������_�G�q�������l�_YjzP9��⅙�M7�V��z���=2+ �b�[�D�y�sU�6����h��;7u��oDOu:�������m-=� 46o��6�������'�V���:���������ڵ8|�Zː��Nv<���~8��%�k���ULow���u��d��/�ipCc6�A�/�D�K6=j���}R��#�#j�6�=�F��st�9� �TaO�0��_q����k�}�i��V��ډ�!>!�q���l��C���9I �M�R��{~��?��Y9F��`!s\+Ǥ�*� ��9�"T,��f�r��Y;�rQ:�pzE� �a�_�1GUw��̃L� ��|3����'��Y��u�h����`���Z['T�ߺ�0x`x�۠�fXog*� ��]�A�6kn2�ڑ�3� -�@b�0>�c.{����2�ϤRܫ����7��/H�����+�Γ�<�f��c�t��[�����S��KR�`H���P\��diz>��S��i�48�2��Tao�0�ί8E��DY������6��*2�tU?U���U��mJ���}gi�I��ݻ�����.� �L��B�\I˄2[ �~� ~ժFmw�� ��l��X[�$�C��Jp�3��'G5�tK�ƶL#�T�ӎ�t1zD J��V*�;Z�Ke�,׈Jkf)��O���b���3a�<"� -[P�0�*��bY&\iV��tPy".Qc�t�sU�� ���M!j��rR��@�t�CYҺSM/�@uߌ)|# '���S�K�oG�S�]�He�1���Lt�XU��I�{u���Qk7=`^ -��a0�����> ömg��)������:���Dzȹ�%C���M=^��Ċ�5q-Y�&�� @,ZM���e���czi�@��Pۘ��(J!NGp�q:��x�yy�����:JV�<��5\,��x/zZ@�ܹ�/qr9��Q)��ډ ��u�? �[�9���N�dް!W���z�BT¸�"��=*a���>��m�o�'j���r��{��!#��(�П���Y��|���Z �rM[m��!yJ� ��xh��sE3Ω�42��}A��Ŧ��Ƭ5ӻ�����:W��l�<dt��5!R���p"Ej�}����d���I�f8��?|��x�?�.�����ܗ�����`�#:)��Փ�_BG���b��GE�y7���տ��RFݚ�Js�n�?�[���7��8�T��SMT-��q��M�W��2 ���X��$�C Ԃ|KSj!�sNN���OQ�Ϡ�h�;�*ۇ�_�Z ���E�Τ ���`�S)o�rB)�����6ud$(��O�䷃}(�B���]V�݈x i�}�?���D�2 ��r|���Y�?��F8.�B��Jo4�������?tb�U�Wp{�v�{ �A[���>�E|��b4z���4^���_2��{,_�6��9X,�-� ��!� �m ��6��g��}p\� ��T]k�@|ׯXL!�q��Ǥ4u��"�!O�,��#ҝzw����D�дc��3;����WA8�0��,R���J���g�b���Q��J�*[>�X9����vNhO�]��,f�4��H�+�;���X���"YM����У��R�V�����A�2�� b���9@����z�/aG�=>��ő�F�=�H �6w�#*�eҧHE% �@��0�w���`d�w����eE�6�J���ؖ�OK^�� \wŘ�"�?�?���� 'y -�����o�Uao�6��_q0 -� ���Q�+���z�\�=JbSy�~�[?%��OB%S�I����$I\�����g�ʤ��gg������#@͖ C�A6IaU;�#���nt^�v������_㬜%� -���V��n1��_g�AT����~������O���� - �΂�2�������n�Q�TIҫȃ^�V�B�O #��7��m�ryW��;�ZwdЧ'K���@�� �+�V��+� ��i\Ex��|�0�a袏4�?`�8�5PEa�D5c۷��{�$�7�ɢ����wx��O�.��i�K2uQ�Y�i�%׋��rP�i�{p�r�Eoh���^9�v�1��m� �����'�v��L��D��t0�&:� -����O��ϱ{x���K��i�JN�F�!�]����$7��� @Gv������#�t[�#-��|� �y.��(A*��<4X�3�L�{#���*4v+k��b*�,��]�P���u�S9a݋1�O��)�� -c��A�~u8y�+��4������Uu)��|t��P�0~��5� ��zs� ��h��:W_�q۶���#m�8P�?�����b>��%��m�!��{5��Ě�����Fy�֐⪘r� &8m�Q����n ل��0Ia��]���)��W-?��>��K��m -�;�Y.��W�傾f�,>s�����) IF��6L��JV�? 얾O��LndF�Tш��;4�P�h*i���@�d�J:o*ˡ?p ��O�� ����'v�C�X�� Tq����{�~f>J���a�lmCևv� -w=~�������B��wf'���h����2fԾ��8���t��P�����ۀ� �?�pC-p��5��$�_fr�D�Q�$�"�FT����ÅEǞ^p _ >�פ_c���VWHg��� �Ξ�h^'sFg+�H�����\���p���XPr pݡ[{4���Bs����.On{Q����{vwW��1�WS W�����蒗��{� L��5? 5b��4*�d����^L{����A5e9=w �>x_�3?��Q��C &o[��t�=c�~�E0x�5���Fz�!�]:�1R5�9$:V�c��I�Zʯ/:"�lA��Gs��u��F�O�K�ftf�[��|ZY(=��BO��t���l2��g��4~�� $��<8:��h6:O�(�Ĕ�F���z�N� �4x��c��T�n�F|�Wl�>؆*}����m5B���Kp>�ȫ�;��h�)��=���@E+�&��ٛٝݟ.���egg3:������QklA�d�O�� -|W�ȏ쯟47�8K�R!%�wF� �St �j��c��S�i�Z��;Y�֧�W��, �y��n�澍�P I��fÒhǜ�oo���k:���st&��1�:��T*ύ\�*2�DD�� �sQ�]�{S��\gه�4�o/Rv�LO�Bk��Q� �c1�;���?� 4O�� ]��������8UtA�n*��N�Q����aL��k��r��a���叨��y�e]�-U"�t��&��;Tv����'�{[q��_���}O�+����R�t05*,:���b!�0��e��m��/P6�z4_�h��ӛ�n�[��f������V����~s���[���^m���-�ִ�~�o��Ղ%�U��x�F*�� ?M�-c�B����<[��`*�Ƣa_� � �����T2�j[�S� �� �d�҇��!Y����m�����{���|H�M#~t������ԕl� ����bG��qm�z���b��9 �-a -�M&J���q��x���(��~HYx���VU��U�4?)tf�Rh�Vp�DЕqA�H#�́�����j�,��$H���rm� �q��,�nR'��Y�y�|أ�YP�$������{I�U�%�.�����d���Ȩ����5�F�{2Xt Bd������"Jk�i��#B��k�Y���;�O�m��|0�{��@� �;�KT*�V�Š,wGs.G�W�j�1F���m$���_ e����?7��h�8��N̆ц%�׎��)�҇o�?W�Et���ϱ�����e�yh�N���QO;D/FA����� 5[~��~�|�/�}i� ̿r��޺��w+�_ѯX��m�U�‚�0��aA����� �>�.~���Ua��F��_1B�������5!w�Z=� �F�(B����]ww�C����Y�;��U>��g޼7�v��m�����"� JtVֹ��*%��� MI��9m�%A�*{"'}]��"J���n;2Zj��b�{�I�iބ�I%2|�f�a%�L�s��4���!�QZg��� *3�[��=�"��J���R)�|�Jn��9��ʝ�@�Q~��1��$#��%)��} ‰V��J(]�*v�L��u;U�ފ�����;we��h�Vʙ�#�@,�j���^��7��{q$m<7���LVtAl_�J�,d��k��c b6^ ^)ݰ�.z������M7M3����"�$����<�^�t��A��9t v����H��Ll�� O0 *,���b�ٮ3���^��Q����M��&)%i��O�$��d���Ê>N���|�LSZ,�v1�KV�b��M揜�K2��D�PJ~�,�S�����:�vN���ڪ �tQ�BRa�j6K%�^9����+L�8�ں[�3����< �s ܛEq^������:���:� w:Y���:�Gq�*W�n0��^h��%�������݃��!�,M���d9�]-���t�0YN�sD��Ҁ��>e��쀆tI?|����;��i����j�w���F��#�ǵ㾝�����.� - ���tL����ḟc���5zH�G��qſE�dc~���V�����@��������-��J��ê�E�e�敊;��0pty�=Yc�=�x(�e�n�g�k�r�ՌpZ���h�2�_����q�E|�"W�kviiD����u�v@�B�XGD�.�O�9��O�_�Zmo�8��_1kk������M�l�\��&E�n�� Z�m"2�%�����H�%��I?nu(����� �_�DӨ�{�/�Z�P&LH!'�L9�/�oL�kΒ4� -��)���4��T܇$ԝ/"���p�,X��:L��Jh_ ����1����0�Y�c1Jl "�I����Du�k��ۻ��+��4�ʌC"�b�`�0F(����f� 3- ����>i��*�i�B�XME��ݑ*�k'�2�nZ�u�V������H�Wݗ��4�i�6�O��[� H�с/=%(. -6����G[��9P�?-H8���U�p\�,���`�$�I��X,�L � �Iϩ�{���\��B�1�d��Bk���m�>�{�<�Q�Y ����j�{mE����U�3e -Y�+����'1^��a�>���nU�D,��Mx�t��^����#%�S�W�Cȷ�W��a-�n '_D�ޘ E����]�.�8mh} Ĩ5��g+�W?w_v_��mu������,o �{��:s��7�Mi_�A;�~0��=T%�R!ӻh� ����B��@$���Ƞ� �Tܿ��q1.��s�زp�0N�G� {>g1P�����?�X�Y��FB�D�@�Ap�x�™���!:���0�01䩄��-TϤC�j�<��O��!�YH�-��w�Fqf�8�Hv �0 k�#��ܼNrXź�W+1��r��|��B&A�eOu����m�; �mG��n��[�(TbI�wR�m��ʯ��sE jo5v4Cw�1��Zi������D�6(���m�Z~�X*�e�$��3�[�9�L�̼Ų��vA$����T���Q��?�^���HȞE. }�j�ۓ{��~�c�/��-X�/y�#��%�bˍ�@n�KK����������JV :��=n�{�$g�u�f������H{kJ���;�w�|����ʔ�I��������yh�О��U�ܟ�YRq�ݨ��_�]�r�Y�@�ۚ��,�'�!�˄���e��)۩���<Œ�+:A`� -�0�,=ԁ��̞�l�a�^u�\�k��.(���v�b�`�,22�SNZ����������!���T�D��C`U]X�@�ZX�V -^���z e�5�-�B_�&����W&ӅJG��0����)��P�Y3�Wy�������C:Ĭ� �4�m�s�:�|}����⠙1im�L���FU�}mq)�Pĉ�U�Ɂ촙�>��[E~Ɵ Y֜���4�^f"�y���7lƟ@�\�5���r�Q�v_4 ���s ��A�8]�-�)s&��On�� -cL���ք��� �*&���,.���s�t��. ����2W�6��~E����4@Gt/L!� ׸[fwr��ƛ2����O�uA�Pn��v�5�c �t)f$���"�J3U^��EЋI���������W7�ۼ��C1O{�R����"���\ϛ��~p�#�Z|{0��ds�TF:�l5|���S��1�n {h"�B��b�^�Lu1@�`�mǗ�} ?�c�-��]C��.�{{S��$v�D���Q�г� -Y��㰴����-:j�T�pA�����A�K�靬|e�l՛A*Kp��s˕ޗC=G��)��e`Q�8!�U,e����3mR33m�Q1k����z:�>�ɤV�儢<�LR8̘���b�����r����`�+ -L�1>��iSa"W& چ��Kg��x�����! }��7*�3�1Z����W%�<�7�P�#ͧ ��<��5Qg��q0Pz�¡���3�h���X��� �]b�M����J�������f)�/>�+K���U�մ���M���r��xQ��'�֙R�y*R��4;�Q���,Y6w�Δ�n�T��ʚ .3���#�6���e��/�� ����yU8��/P�nl�/&������q/�l �%�o��Z�|oc^��T��ŕG_w��o���zmFy �6�J�S��YL$��}���d(����lvr7�u���/�|��3N���g��O�$�y�&�>~?`�Sd,M�N3�HaV�&yM�� -"�L��Hr9dl,�������d�(��a$�:@`�s�I�J�G6P< #ܚ�,J��������,��l�\g�l��t��LΣ%�w����d�l�Z�i�Iq�����_��o�#և �h_���i���Y�欐����S �9� �-�qē�Vk����OH:�9��D -K��4�s-(��y���j�r�w�f3�P����_�z�_�XH ���"ʀÓ5�K�)��4�+����8�2�w2�p�4*� -�d�Aw'�8Ih�|̮������j챏Ww����}<��=������[vqs������~�g��?��?_]����V�i�!�i����M�-%�A4� /�|&�,�"�Ue)�E$Q�� A9QN*%qi�����Q��%�I�����9X� ̄�|��P�dZdJq��#��K|��i�e��K�ns&E,2RP�4 �P,���l!�yJ��R�b�" �mE��!I��5�E�e.lb��a�*�x� �3OW��?[����oEP�_��|�K�i(N��!��� �q��Z��֏� .��e1 A��3����s4��\�����넉_d��*���>n��1�e|�g��{�qp�c68�DW�D��)5�`8�8<�EpxL���`Z�N� -@�!���L���s�[�)A��+�a&�"KDhV�����g��js��%�R}&�"���Ę�v�(�À7���P|�a�%o�3�g2 ��7��!�؜�9~e�$���(�(s$އ��"#���>Cߥ<-g��}��[5Z 8��3���R�wJ{}��m&�Ѻ�dC�L�\��~�Cv���G�[$��+&�Z��ji`�A�+`H�P��@� �YR�E�0�o�{+?��� -�"4��_ tMV��x�/�`��\<帜�{/m`�,� �A�4����M��@���sN�L�+!��є�;��`i��qx -�:�f�M��v����mZ��OdC�+ ��e��̳>,� Ac��~pֿ:9 ������/.�^`����8�+�����������zd9�b�nr{9쟋��g4���{P� -3#��l �`g%��u���2.DuP�����W0�T9��©����l�^E>/��Ѐ6V�G��h�`jM� -"?����D�p>�Pp���5;z���ё\�%�c��y�ݻX���X*m� ]���)���\\I$�wW��w7�?=�/?�ߞ�Wxji?n@���m�*%���%�>��y�i}�:<��;QH��ה.��F��2��@b2� -��+�w��4K�sr-ϱۜ� �����`�r�� T&0�cz��k��D�G�t�w��',%��#��D���mY9'��]N��k y,  �9R��Rf���Fy���C�PHyu:���� ��,�M��&�jgc;�X �s�.��k���������ZB TlPxeBMXb��dA��KV"ku� Vdž�*7Z,��qFCD�O�Ձ�@p(+�a���О,)��� 0i��&�7E|j����?�J������a�BcB�����K,�3,�~=�Btx�����[G@��[bu ��,�ϊ�� -�Vu�Tc��,]�������t��󄪋PPE��A� ��]@� xB. -��'4 J��H\GŖ�W��zڮXj؏��L����d�k����ڴE�����,�N]>ϰt�:ͩ�tI�:�F9+7\9>Y��2������c������6�`+�ݶ�g�wǝ���PLc.d��� �;+��К��c���쳒��,�&h��*R����^7�o=��\�U$�`wS,n:�b)K�j�H��,?{T4��s�W���]s9IGO�ר�i/¹E�X;s������]���� }L�T���(<��"4�H� �P�`�m�*V$�b��G�F+��T2����{�B�8 �m|rH� ��uҢ� -��^3doE�� � �qJ�#�A�P�O���w��.|����v����I������f:ޢ(Q��B���u����ڹ�q����*�h(J�����@gr��~}���S59ے��$�e�Ӭ[���r&I�ʮ����LoA��zB�x$�x%���lL�){��fr -��Ɵ�BpɪGK��a_jΥ{εLx����Aq�, T��̠��& �P*X�98�4���7\(YR�T��>A���̫+ `�9��Fp[��n�K���?� �kڱ=�-<ɥ ��(��;3YI�a��&ՠ����b��w��𔡝#\G>�^�]v�$Nӥr������~>��r����Esw������/nQegEG���H;i�O�>��C�ڗ��o�z��e��c@- �T -��,[t��Z�_�膹m��zj� V8��L}8�� -H���]��~��{�4MՍ�mg���e�.=����l�]B�� ���3()ё'X6��ݞ6j���3��DO�D�q���I����D�iH����I�5`xi/�/�e���Q"�?U��M���f'U���_z*}��Vb��^�V^��\9�jV]3W�Kp�N�1� 45�]Tls�V'D���T�Gُ ���;��/i�J��9Z[��z4=�ħ��y,�����J���������dڂ��+�u��jy�h�:N�8��}��q�෻I�q���s�@��*}���S$T1/w��*V�|u���\q휧�T��%�xBAm'���fP�]T�Y��\6_D��l�sG�/[�[ ��Ý=�/\>z�d\Z��Es���[bu�"��1R 3������a�d<��í��Ok���JM����a�u��1��ۮ�Oߺ�y�H�+���|�N�ѨRb� -b�cD�7�/���� D����I��I�ɒ������ �n A�� �(���h�o������� �P5�=s�kNsf�\T�C���̧��̓� E Y�ԗ�{D�)*�2gJ~�{8���;)M������-QGH4��~� �� �G�ne�UP'�N���:S��t��+�,Mm��G��mNRC¹�K�2|w�T�7��b�JȦLy� ;r��E��\��:�&�<�3��O7'F��Z�T}��S���c�D9�� H�+~A�e����)i|X��ɖ����v(o���bл��{^GY��Խ-Hؗ���Al�O���߶����Aۖe4�֤�:�x�O � -��]����(���6�v��V�}� ����\ ��7���<���� -�Zgֹc<���Y�y"�3*/C�P�}^U�R�)�E���9�[�Rh_��Rj�[[� �)�R�5O�s�J�1j�[�`A)���cm�g<��[-_t�RlcYF�4g�Za�Rrc*o�zII1�d�ꉇ�E�I,�V�R�������-��׶�9fP˻npܖ���Je�t��@F��=7�y.UW��;���r>��o�9�Ř����=n�t�� �8'�ꥋ�IӀ�7��=��,�Ca��LSa#��D�㙖���B���͸��{�y���q�j���Q���+� S����R�B�c���J�F����cX l�5���}�b�V�Ϊ��0�T8xp9b�1�o,l2���O���y���E�lo��F1��c ����B�F��^����.�n����-}�g��t՟�\�T��#jz���%�S���L���w�1����T����LX>}�c%�~������oz�s}�y�y�ӽ��X�n�F}�WL � E,�(�N�F�&va9 ��0V�چ�ew�����ޙ�P�.v��S���\Μ���r^%ggGpע��*i��B�`����3|,���`��Q��ߋ�K�3�� K�⟉����U%3f���N���?�%9i+ ��iZL+� -oX�9_piM`¹3s{?]� ]$�L�������0�R� ���2AG��� �)j�3�Qt�*�Z�s j%�6sQ�y���::c��x,ƺVU�uHF~GC�O����t�wϝ���A* ����S^Zt[��`2u�!�� ��s0��T)`.P��0K��`nm9H��j�g���yCL�cfo&W����Q���ߕИ��X�^�l��lEt�r@/V3.�i��f�6I�.b�MLs5:N`<9�w��x҃O��_n?�ç�����~|5��;���<����k�|&�_�7?��c��(�Xj -=�Q�5�} ��:���b&R O��9�jɵk���0TZ�Nf���T�Twb�]����LQ�ˀ�$a��a��$q�b|{����Lbg���Rd�f n�� ��� 2^�5V�� -V���oQj�Om���"�C�wv��Н2ٰ����b - ��>y�}�1��x�����cx#��]�j�O�Fs[i�-�����$&�~}�ufV]V����H�5�6�'}�l᱗�c��4܎�~�������tޚ����-jҨ�� &-פ�l"���t��}� �F�R�*��(����ڢ1#Z��n)�#�`� i������8�3!�JkZB�����:�> e,���Z��0�d!�TUN��Z7�vc׼W��Z��`;��֍Ǟ�?�ދ ��W�D�۞�q�yd �hp�gZ�5�W��Ez -�`u�x�����vF���O�:k�P������*�.V���e|� U� ���dB�-J�ֹ�P�n����O��:���w���6[�˃���t��K���cV���\�B �'_x�ٹ����ack:: -D�l� .YQ�⎍��]2jL[��/.��n�H��x�b�,GX�X�C��#�cf��r�VՏX��FQ�l�w�/�f �7�� -���nqj�޾X|�|��9���e{���ܚ-3a,6s� ����s���zDU�/�gW����?��t������p'I`4�X8�Y�ޖ5��R[�ak𡷔]r~q\s�K7��S�����q�枖��_S5��"5�6�����-�OGߙ�ƨp�c��''B>D�}a� E��6��G�I�K��cr�O;L�q3b lHe��B��/T�� %i����m���?o�J�Y���\#+�{�}n�"��=�Y�?٠k䛚��'����-�X� ,�#Ͼ�[�m>T�.;$����v^&��̷i/�k#1DJ��1���W�}Ls����n�sW�3����OG�� �T]o�8|��X}H�u�c[��1Nha�{E��Zb+�*IEq��߻K����a�$� g��|񪫻Yv}=�kX�@YPm*5}U��Ӎ5{]���&7:hl�Wr������O%��? -�:���M�rY���_���u�Z7 -pz��Ќ���#j��(�"�z��onaϪ_j?�X��C�5��`�g�3�����m�C��Q������NWu;r��﷕V�U�G�-�z����Yדs����始?� t1�^\=��`l��Ӊ�AQX. k�F�Q=uw܃5~�H�N0�v^-�:��Y� ð�(xa]���w�캸��E'�{Ӑ��֗^;�xw�X��kmp���*ǎ�j.h�Bp~L'ӒDn���m�xF���^/���Ç|����>,���m~[��n6�7�6߬�m��GA���o�@loE��&X�G�<�S� i���w��^+n�T=V��'��#�j/G�Yd��hu���y�o�iJ�b�? ��U�p?%$ːS_�ɲ�=�����G�c�,M�V2G:g�uS���@n�J �0V�Igʞ�1�T0��Pl��� ��)�CxLmI ��Xz�ĖT�F��w��f'�?��f��8����5��ɃJߓ�A�D*�%)�B�̸�nure�U�#o�?��(�U8=��b���0H��3�T -�?�׶o�IB�fK���ӑؒT�X��WD�j[>�4}�<�Ǯ;l'm�M\O��f·��$�O/C���w�P��F�_%�7��2���ٷ٫���Tao�0��_q���N�A|�(�������i�&׹$f�lgY����9qW���TE�}���{�wgm�N��� ��J��h/�V�_!��(�?��z��J{�����.�P��� %�VHzd��+��\xe4L��j��F�6c�{��u��Di��-2���l�O�PY�ϕ�@�|Eg����;(J��EM�i� D��b)l�:�i�V���k��R-ݷe)�*�qp����M7J9R=6c��%�^��)࢓q�d�6T7b�x�>�>Hl=�%bM[+�e��� �7#�ٱg �0��1���Py߾I���"^[&QbrA�]g�/�t���5:G���)K=��A��J�0Ԣg�Q!Ģ��q]ι����ԴH��������2�4;���,��p�n�l��p���\���y�K��YN��fMo+X�o��k��<���U��ZALw����m:� �+:-�� � 9�e���n��ຖ �֐l*�2�(i��01��c�2]�3#�@��P �9�d��T��+8�9�i��W���%�ΆL:1GFg`?�}Q�GZ+�i������,�A_���y�A���\c���M�y��BD� FW����yE�;�F*�=�k��of�e+�a/<�,��j�m�����G�no��ۀ2�s�v�k29{?� �Ua��8��_1Z�D�Z�G� -lEjO�r�O�M��!���l����q�nD��]���33~���ϗM�L��� ]�RWL�5Ai�MA�d��k��ov^��v��&�۫��#it��sN�ƔE�2��v:嘖�5� -��.�匰dG�H��j��:�k6�cER�c��?'J�c��f�zsM{����bt:��ў:���T�k9ZU���:�DDžr���lsp�(�΀h���*�r㏅�c��`۞ʈu/��#������"@�.����1�V26P���:�]�M\��J+�؝���}�����T���a��d˗� �y�$]��U<��H��(�N�����T�=���j�wR Pe� �꤃�Q�@�9(n�+��� �m�m���Ȧb�.)�� z�HW�}Zm�m>n����f�ޮ�S��Л���j�ڬ�Z�b�Y2߯�o��!��� բ(�#? �-}�|Ù�� �LѪ������P4�j��� sأ�!��K��y?%� �7)%�7n{�$��� 6I����^=d>�L�ߊDzJA���[�GW�&+�xe��B�8����P�� `���+�PM t���Y� -��\VF3c�e���$����}4�wcS�(��8��N�6���,�$bZ~DEUg���mݣ���Uq�)1k����:w���]��MҢ$<��<��混i����-8�+#ZY!�TK���?�� -baL����Z� �`�3�"���^�BdW:W��R+� �W]����V��:�Dg>c&繥/b�2CLPY��"������T&���x?*��6"i���=,(�CɩE R��+�3\�,���2��,�Baf"�R��2���1>p��z]�l��u F��@��Q� tȀ����v��y'bJ[� n�>�Z*� -K�X -8ﲻu��sD�yZ \+�u3��� �6= ��� Wp_g�A�����O/_Sѕϝ��B�\f��|"��1�ZcQ�ݠ��"#�ղ�ަ"A}LЪ����&܌��)��mx?���=�4�}������d2�F�S������b4݌�� ǟ������H�Q*|L3n�*��(�5>U50[�9�������X",�fN)f�4���0�|��my���k��������R>�� ���lKdh ���W�Ln��H5��1|�p�L�`�OH��!c�v�5չ��wg*͚!� -�N���y�����5Qޮ��6c� V4G��n����U�'T������HO�����;&5"��]:U�y�̮�b\�ԮzP#:����ߝ���Y�yF���ђ��{\��|e5=w�'��~�U�*F�Tؔ�A��?��3��V�Ӄ�p`� -$�����"�!�}r��t�x��y��*���ؚ���e��J(��8�|adA�?�lݒ�8B��,b��Vr�zfA��#��cD�~x5�]��f{_�̎XI��a�{��+���C�dD66T8��Z�8Y+9 ī�dB�&\���%�3�.,պ�Ӽ�R�9��qɇݗТ l�����B�!� �7��x�J�[$��g������I��'��c�pU��������M��,o���7o���b�R�ܹ(�u7:�V���˗�w��{<Ȕ�K#��D��? -���B����Kp@�(5�^���[�$��5;�a؁��Y�v�� ��"B�l9�M3��y{�T"I�y�=��m�r&|�M�BZ�S�ND9�||a�C|�'p$�v9��?%�w����/�X�+���R�c�t�D�Dݼ�6� _��wZ��ل��|.�e!#GrF��n;٭:"�i�_ 4 �nP:W*���o��f�8W�RxcO�|٥�i���D�Bs�R���ω���Y�*��UN�‰�����A����}ϫ�H�s&B:5�X�th l~�-����,pw���Q��G ;�,�Ma}|l����W���GkH�0Y�D��Z'�Jz���, f�[�3�H̺�]i���;ڮ�nlֲi���GCMׄQTݱ/�a)�4;�3'�\�%Nv(*k�LW�'ٵ�}���K#lKfǵ��?�:?�tX�o4��+s<[x�pا���͠+���07Mf�n;d��>o��m��ک��Q�C��Ɏ^>�~S���%��m����]�CE>X�v5�;7b��#'w��o�o��'���WmO�H��_1E�IP�+>�� mZ@�������� -m����o��&�N���Y�:����t�ݙy�gf׿�*���t��℃�g��Y���"�o� ��=W�)6gb���3� �&���d}{<���k�q�<�3��d��Y^f>Sq�Ao<;���>�u. �E^ċR�I�X(8Oy��0�\��^�'oO!@�d�Dz�C�XE�'����w����B�� �H52f �����r�X+���5aK��.��X -d< d-��eZ�f!b�� H�5��`2ۃ7��d6�O�����s�4��O��\\�ۋ��d>���g0�~!��&ӓp� C�BP�4&F��Г�@j1u��� �0�,,Y�!̯�нQp�ƒJ+���Hc�E%�t+7�%����䊺Jqm� U��Y�z�6���k��Хk[��W����� �l%I6W^DY6z�"/�N)Τ�!�}������Olc�!��z�K51��j� 5� -������#}��fR㎰P ��P�ߟW��y�'��i$�X�������s��VęEW�-D|����4v�������C4_��=�E�<�M ��A�t6�� �>e���\]a�8��z}��b�>�d�����K�S �&�WW���㘟f\3��~h4��Hj -��*əE!z�H���v+�K�Jar����G�M�5Գ)�EIճ�y��d,�5����Nj�qE����]�U3t�d }0e�G�=�¾���z�: -}��� e��R/w��ht7Ի�5jO5n,�v����%�?bn�kN]�6V��v�ϦFa�@9�����덺Զ3����å�R��� -��MK**���h��[ȑ@��x���՜w���8�B�z��g#�`�;I��z 7 -��GX���W�%8�[����YRV��:�7�^Cp`�L�lYO�VF֏v@�^��Zc5�B�n4E~�y�s���Q�qK� �c؛�Y�k��^�l�v42�_U�K��t]�jE�����tK|����yFZ��hy�E�U� �D��[sƗ�3�jE��*1���UӟM���}m_��D�ߥ�u���oU�'ھ�������'�H�z��J;��}V9���q�}���"� �r�e�D� ���|_ C���5@���\���l�{�o�e��T��n.|�ʊ|6�n�p��_m�ڒX����7��}�V)�+�ͬv)q��i���v^w��Xmo�6��_q˂�R{��eI�Ig�u��m�E@K��E&UJ����#���A�a�>$��{{�;�����q�spЀ�Oɔ )d���{�~�B���%I��7��2�>�ʬ�����S�9\�L����\��r Jr�V&J[�Z����F`��|�e������a�����}�X9t`*�1� L���U1�d�E $�01����!�>�x�E8NAM%��X�hoH� .rg�87���T�B�D��8���B���4q ���{�##=a3�*�,�v���St�đ`�3�.�����)Q#J0 -��� X�0N���Ng:����t��#�A`�����y/#�$�՗LhDx4�O���R�L�L�ч�F�exH�IN�j�J�r1���� �u�����78������C��^]u����.�����7�]���t�I��^��!p M��XS� <�_aS�q�e)��'�ax2�X�!Tw\�Z����$1E���䘈�P*!х��9E�oIU������a��I��NǼ��}�ֱUvym�yP�*�UE��0|ò�z���N��0��K��Z� �m1CWk6�zf�z)� ��� �(=pf{!�`�"� k�YHOܺ|���HHu��$�㓉��~�-��(�r�x�_j:j,��J2 ����35�����<8f;�A݁V$��^Q��<`Y��Mw)0x��0��aFf#��� �������$�{��;6UN��f��䛇�&����Sdr�|^3Ԗ̴�Q�Ee�C��Ng�ɱ�|���Χ᱔l1ej�#6M��\z/��l2���T?�<�2�p��;-L6���D�Ym ��*ӚN���.��u�;%�G�l4Wt�����ť�{��VB�v ⭺3l�2mvɒ�?��d`����n)� -i�������4:y�H�1�$[ �Gqf6����=6�h,���q�\]~=���-\x��*ٽ�8?�;P�Ac��>��]"Vե*�ٽ�����k�/�f? ��l�l��߆a۶3� ϴ)�AbxN�&���r.U�֒[_i���DM�2�&��h���Q~�Ek�qUL9�C�ߦg��$}?�l�G�(�8�I��������� -����(Y���p�L��U�L�mQr͙���l -H�Q)�� � ���|o�<-}�l���Ȍ䩢B����5��T�rk-��i<*��PYN�I۰%��[���w������/h�E��Ӱ�߮_�A����\�R�~?��_���_����!����/�τ6��\󈟍&+�.��_�����҈��d����ŧJ���܉�y�$ E�J�����{�n�� l��K��Ƣ;쀧= -�}B'���Aa;�|�>��`|���`�h-���K������;�Vao�H�ίE�����>���4 *� -��^U�*�؃�����]��S��ͬ��R� ;;3o޼���*�z��E.`* �D+'��*�#��䝰Ȧ�T�5Yq� )��crb��2Ae1��Ϥ }�z�a��V�pR+L���'� -�[(�i��mD�A,Q9;�}��b9��� Ab�T�֏4��tGZh��� �i*9�(@*:(=v4� �r���vFf��(46��[r)�4��m���j��+�ꎌ�I�����/a@�鬳� _{�R�@i�Ň��+Gp XYR��{w��sƯ]�榁��^�؛��sի(j�f,<�6YJ�>�����>�U��[������P%bMX �p}��Ec�q�����遴�J?�@� ߣ�I �� �M�Y<�/����%|���N���M �[�^��ϖ�Ŝ~Ma2�ʞ���G�D��me�B*�QL�0�Z�>� -�� ���Zd��G㧢BSJ˭�2%y��yQYv}R[���D���y���B�H��3��Ȣț��z�~�? -�”��ԉ�&�I)n������ &�m7�0Q�QqLT��M��?j�z~�<�`�*ݛ[нz$a������hA�a�{$�!��� �1b7`u�l�Y"�Y�k�αh���ީ�.׼�~�B*w2pB�ϝ�괣��y��;rxQ��xe��D�ib�z�H���y�m�Yh�;I��EQ��>�^ #��\>wt�Pv��20�SElj�x��VI>x��]�� N'/Gm<� -ms[���wꋫ��>������l���[t�Q�jI��n�1-�V��%NODM;g羼c����\�^${�Y��(�`?�4ίB���~�R����o��8�jeѭ�Cz�܅'_7��J�w�/�D�j�B����h��P$��)�%� ��@EtQKt��'w� -�^.d ���H����/���[L�e������?"�LB|��=!�6c�ʱ�TϏڠ������6ڟy�����`$�����/�Vao�H�ίE�����}l��4!*�"L�MOZ�Ŭb����8���~3�^0����`��{o���|��y+8;k�\��C��aB -��Ys�?�O��3��B�7�B���cE]��˂�`���,¯P�L�4�+Uʘ�$t��U�'נ$�h�!S��j�,13�.#�Ds�qi�>@ȹM?���#X!W��E��@%�ψ*��`��X �f)�2K�5O��If���ڀ�$��Z�7')�'S���nTYKi���у�0I���:x��N�'��6:c��@Y�]v�� �EbY� -&#]��b ��:�ZRˀY)�V�c� E��ژ�]TU�g�p_�$��k��$�A�>�LyQ`��)��1,7�rd�%rMYE���@�ƊˤGх7A�M��y�(�y��l�N�!���4 �a��矧_��u8� '��(�� .����|<��+Nn)����K�P�!�$� -�(�~��-u���Gb%"�'��%uϵ����L��I�h�Lk��B���)��徣T4M��}� `��mΒ �����]��S-?��ed�Z�h 1_ ��f�_?�؉���� +����f.��ǧ�o � �#}��끝;f�*� �!�ԭ�.pDp���hH��I����00��-Ɠ�h6^�޾o��IK^�2����~q�%أo[�ߎ�iۜ�Mng�!6�r�g.#�5, �]���g�x�4�L�d�R^.�@�q9�E�9��DR��k|)��|�I���*�M�jI>�q� ����.y��I��� ��yI����me�=�en��Q9��3ͲZ��x�Gx�v䱂�J���by���%��'��M��>��7���R ��>8��28l�uԴ7���c�N@TO�w��Ɉ��oo��:eZ�#ú��U���.>������5�����46K���e�=8�giɻ���E����O�;�:Qoˇ\ic�V -�|��4�K�.���3a����v]E(�Κq�V�:����)�ڧ��I7�o�0���L68��m8W��Im������t��m�-Jǡ��z��{~j;u��m�h�Go��[�y����z�j}8o��Y�r�6}�W`�b;㚝>6m՗F�XJEŞ�B͉l�#�"�+�0�� �WL�F���s���z�����n �IM`k����c�-B#�W��o�tq���E=��^Ѭ�6}Ch�ZO��!��pG���A=�5D� ���bQP6 �I��J`{ U�������H�`��-F��@S���"(�Z [�`|�EG��\H�Qr�<~Y�.���4#Y7G��2�K����|��i����I� ��tY�� �9�� -�[5H�8߻a�6>��Ϥ��&���$�6�� -���P �=�j�w�5� ����Ț�k�W���=�.��l���o��7ܠ�zb�� �>����i�3�\Q(i����d���f] 4���`�����7�u��,1\Y�:"B��QX�!�����xf��z]B�b�;o*rL����j�b�{��[�X�� *�xk��o����mf�X&��7����9�֍J�W�K���g4�~Q;����������Q��?$�ރ� �?烞{��a�jw�����a��um�J{�� �vm�bH=��f� q�II����)��<.��(�#���ۥ��gp��w)��&�7�3�Y ��&�W���թM��;���^�.�s}!R�*x�Rk������!w�=�zl��u���G?���X�n#7}�Wp?���ȣ'��� "�X� �� Z��u��l�ځ�}�x�{K�f�����d�N������nҳѻwg��� #��r�Ś� #��Z&�Ld��&rM�j=���c&4[#��8�1|����b�^fbI ��D��}���L)JKE�R�c_d$N#�k�ؖ ���������Y��(���Ɂ{n6��k��Ꙭ@].9M�<�ZCPP�5UKt3��A����L� O�9�2��h�8 �d�])y���/�]�n�w����^�����ҐL�B;a/1K � �mӄS[i�]~���W"2B�+D���5(����^�F��~H��C�֣��� ;��]��A�H�ր�oW���@h -V�t�&t����+� -�J�@�r� -Ђ��zy�Fm�z���z��l2��'�>�������t>����Gr�0���'S�람����?&��a�^R�N��e��� �'����x �uF׌��)�)S[�1��\=��XRim���#����|6ͥL��ǝg�hD��k�;]�Fv)$���v��V%RL�LYN��B�㸥��"�FJ�H�-� 22C� El�$ks�e:�3M�A�����&&�*a��.c�w 7�h—^�%,FeB������K��/���ST��ԁ�(��ϭ��!��{���X�@��n��2����ξ��AR��q�f�!�i����P.� [�'#��*i*p�� �o��II`��r�R�=t)M �+�3r[�ӓ����&"K�Ai13ifp���oZx�5���\h��;~˘�T���,��Fȳ!'I�:\��2f�d�v���{DR������֙�1r.s�/�玷z1�����o���������ĕwl8���Kr��԰SC�ꅏ��]�+�*�N�ϲ��ʄ�K��]�ev�i��m�\Yv��=�kΠs�����U����#�S���J?X�1#��\Ω~m�Ѣ� _�� X)�v��P�\ -�hNV�����ȼ<)��o9p��(��A�Ć��X�ib�K�=>v (Y-G�., .X�E�dT�?' -���01�B ��r!�M�H��…)��vpp:�xO��ą��sg�,l.�γ���k)�)��=�t[�mJ��ؐKxk�Fg�7�f\�>|Uq~�4I�� �Q���Ȓ�[W�p�ʗ�S@��+b9�G)�f��M�X�Iy���]�~�Xk�},�Q�(�uW���mjJ��3`��`2?)�����v�i;iS���d�� 3�6|V�7/e��p����Go��pAzJQyu��ӫ� ^��}0�8��5��;��0t�N�ה��2.�R�ȍ0�&ol���d�}��f������8C���ކ�#f�0���\%���>tq� ���^A:�a0� ��{-^����;?/BW��w��8<��#u��g�� -./� ��*�%�C�mxt����n8��5��5�rkz��2n��#>��*�r�D$^��|i2ߨ4r�:&���bs,$+���U<�������%6�O�iQ�>�*�v��ɢܮ��xW�e1�W����å -4�,` K%�o�!�KB&c��~ �bȯ-]��9�,��3U��� ���� 2��;/���(v�U����c�r�̃+�3T��T���Iܙ�Ol=]}�_� -��1��H���jD����ay�����c�N������j�8h�-���0�� -����Ba�1���m`����)d�Z@9;q=��j��v~{�|����^���W�S�t�ΌjG/D�U��ﶲU)�����I��k1V#�7��t���\ ���qĽ�j�� �O���ή;��_kb��qѺ�f���")���e�ۈ�D�ѬW���y �������e�e���<�ꇆ�X�c�辝�퍰d@�!�\�'o�O��m���]-n��J��ɾ6kY��UsHY�Z���^��1�z�J�:|^O����m�uo��P`�����i?�t }�.يB�o�7�� �z�q�Nv(�0�}���_��Ax�T��z� �����}j6���?�A��p}�?�Y�o�8����'�](��>�M�l���9��MQ,-�6/���8�"��� I=-;�-��i�E�p�y��ާ��h���{�>�X�H%��D&KfV���DW*�*�ʅ6R%ג�jɢ�k=2��$#�h1gF�e�#������L��*O��Y�r���ȘJR���Uf���,7�"�_fB�Eb􀱉�~|;]ݰȋ�s�-��f{�f�=����ģy�d/�$fbɳ9��t����0�ID�W2����F[��X�u�r�JEkg���#T�o���l@�����D��[�(�r-J�L<9� �п���pHK>^�}8$�1��05<��_ l�#׏'���w�� �y���ܤ��<�щ�=l*`gd%�����L@Y���#� p_�$�"�� ���@�(Xpmo��[�LE���'�� `� ��9�m@.� -�A�د���x���`C����X��6] L��wpU�ؙ0oSR�e���@P�4�����cرJ @��_u@w� �Xߒo�}6X�55��� 1��Mc�����=<���%1J�=v����Gŧ�)�s����Oʉ|s�fH���A�yǖ�m��gk ��ɔg�v1MK�s��w�M8_���K�8+�dH\�/��^e����M��w����,3��TVk ���� -����<��*S�Җ�.�򌯅A�)���(^cԸP.���+����\� �� oVj>h���w�5�V�\�G����L�%�I����e�ȓ����� d�<2�N��� rN(a}:�{��c�V�����֎�O���1}O�9 J��� -Z����;�d�E��>7��O<�sDžUG�� -[q�r ��o�"�����"��ق�e�<��=7��#���*qI·3H�;��p]J<��d��I�t�;�y������!��IK�:Q��vV� -S7�^�<���6�r*�>V��u���ZR��m�����;�"M4 �R�1c�N��8�JZ�� (NZ��J�Dl��G.j*cs��؍(�M��V�¿Mv3�1];d����Dӑ���ʠX��"}�]�~|,��e��{�2k�� ��ع�UDW��o����?�̐�uY_OO B�H���:+� � �5�-+�%d�<,�v��I# ��=��W^l�u��Ă���i ���l%<�?q�d�:��W �Z�v9Sف�p�A+�'�������jz����z?B�>aw����.��`�����gӫa�^%O/ �`����э5�-�Xy���Ϡ���̍�N/ -'NhR�j�T@���_[*ar�cq�� ����������a����*������S� -�����7�n���j!��>XT0�S3� fG�xPc�B���,P[��eV��yV�\���xah��TɊ��+�`�D�Y��/���5A��ϝ`��|M;��m��x��� -x�8QR5v�l��Ѫ�)�Q"�Ŭ��m���A��f����* �A�ش+x{�� �Ej..���}��/h�\��v�� �;��¡G�sȂn^A o~��_��~��!����� �Q��`��q���m�b~"p�]T06r��{��%�|�Wm��*������Ѥ;o �>/�*Q�M��:����sX��5{A��`����m��U��橭m�zCզ����d�'OS8��o�W@{�>s(k�$g��ۮ��t�d�ⴳ�����������כ�p��W��@"9����I�^M� ݟ�$�ղU[��}&�Ah]��J�����TƣHh�g��R�'+C?�mR���>��i�t4���9tݐ�`�Dc����~ � �����=�gc/�1AI#���ā;�zz IvN���G�}��J�(�%�r���n�'������v ��e��Zj� -�>"`� ����u��!�K7�&��~.~����G �`�2�VU;���)^�����:��Pæ5���0�C'��mTbS� -����A�y����誰O�]yU��-L'����;�𩛪s[|�`;��j}� ?<��;V�p�lS�c��}dP���͂�\*��?/�73����]�W����/��G���5��u�kr���Ӆ�.¡ڄ�a�B� �i�Ru).�Z-��3���5�Ze~0�܇zw�����t�/�� �Y[o�~�����2T�8�v��udT8�XJ�" j�Z^-�p��� ��3Cr��6��DY��/� ��_���dt~~��ٍ� Tb�Ld1�L�\�$S��M$�[�c�+���#�eȍ�,�y� AJy'�d"dF�����5W+��Z��'�&U�ή�7}�B3��i��Z.s[��GZ��HL6dl.���.���ۑ?����Ҭ�Ffl��[�(�U��~ؐ!ȨE�u�n*�i� S�D�l-Sз@W�7ޘ� -�j�םʝ+�]0�#B��;���2���^���7|�eX��R:O�H � �m�X�$ n�]�l�� QKL!�� -S�*���ckcҋ�h��9�ا���� �����j��N����]���N��<ݰ��������� �O�F'�R�a��� X-.OY*����D9��ԣ��#���aj302���HCE�!k�7�%o �(�u�B��8�<�J�8T�Σш^�Fx~��!���T���F' -\��Ǣ�1]`�AsP�����e�`�A��L`?�l�B�� dJ�r*b��('� -jvCj7H�0�_eh� Y�?��< �w�BC/�2���D����]�]>�O�R���J�"�O$A���y��,�L �xx�&"����c�H����N�TAq,�]E=y��r�j�8�[ܟ#Af8�F�CXe�U�4 X��B���'�C�Ku��LbNM2¡��T ���q �a-�G�*�`އu6ljݩ7 �jf6�eb�i:�Z�S��-�b ]\���ngv�4���_,������_a�����nH]e��Q����E_�|=A;�]�s��ۘA�1���~�ȵ͘�eD�e#D�Ӵd[ʳ�k��g�ǯEPҸ;�I�8��I�9�Līz�,y���۵���Dl-��M��6u _e�;��HX�5��Yȩ���e��sg���8�Fl2D���VZm�0|��@�&Eh�S�MHE��'Jrf� -Đ��(�!��dܴLc�C(̊�#�D2DI�G+YxW +�.E����xՍ"Us* Zԃ��)@�U���X�,g�R�p � �!ͰU֤�%PT���',}�_'�`D �Gp}ڕlZ�\'�Qɰ�x��5[�I@{��=���s�rg\t[i��vW͚qGC�>��Z��)�_©Ke�M�"8��=��*v]�lI��Z��b�nUiǢ@m�+-��~/�ktj��ZQ�{�Ǖ��f��@��з^3�K��~,7->�g��g��f~��(�B#[�����Q射�[>)�j���A���Rf�v�y({4rG�J���\'"|��[���D�y�M��;�<�}K�r<�^���C��3�����"*I����f�����!.X���6�'�6�z�ӊrQ����bm��/�ɤ�����3r<���N�o�A�@��*t,�Q�,�)؀h�y�)������Xgt��Z�0eK%ej�5�V��K�ǖ *�� �B���p_�(_ds)����#e4���5���G#�*�ק�>Z�1-3e15:�Vۘ���!�K��f�e�$t���X �7U�[pB\wv�;����^��KP �%k~ÄW�4�d+.-�"�������Qk�JA==����&|�)�XP�w }X��_��'`('��޸/��5>. � ��U�"�.�y掎�.O/�W���)�y�}���%�=�pz�-��߽;�Zh9 =f�`q�~�ɖ��M��9$�\����*�}uC�˭�fd�B� �IJsfBƎ�w���G�<~�)�����;��@#�$����!�%�j�y�yù�|a�#���@�Ĩ�1�W�|;��6<ޔ -ҶF'�פ�n&`ǃ1��J����"H镊�?$����p@>n��.��z�"�c�B���#h���hK�DR�����2u�A�A\c��4�~�`��q�p# -��X��0>Z8&��1h![�y��軗w/`� ��X\�� l[5H[7� ��G��"�ڤ�չ�Y�zT��֤�UӇ�G1�)���jf cd� g�Օ�UYlzM���cW]�-E�V�m^�O0��^�"4.�x^',� �� %��]��/l0 �nQ�L�0G��Qt�a��{�.��L{s++f�<`����N�̪^g߽i }������k���u�@8hIP��@R؜ѷ�Y6y���}�@�͒�� �8��`�4��u��P���!���8�rKO���q{ (YڝK�r<�("O��q��ؕ�g��(e��V��]��� ̔^K�tɎ)l�բ��Q��d�#�RP���C2��s��D�������Q��x *�g؉𡽑Ī�2j��Y[سW��}q@f�`d���u�U��p��;�� �� � -�Z���{��f��Z0������"2�{�\B��FQk������Nt�$�t[}{�9"GXy��k�l�K��{Z��ms?�E��_�e5�G� \� �F��Z�!��f����ү��Ω4�J���3�����gɴ=�Gm״ -(l�k���Rګy�ڑ�tm)]*j _م�m�SE�綾d�dW�"հ�k0�����AQ�%� -�����������Z�PaWq���:�[���V0)3{+��X�G�R7`9����b���� M;p�G�����w��~;�W4�e%��:A]��.HdM��?w�o���[�l~&��X�4��^�q'��m�I~r4����� @GDZm9ӴG�eO�*Jw���7B���������~Մ�F� +�xpx�I��6 -{��+wcRi�� �D������8�#k���N|o�m6W�lžpmtE��������Xmo9��_�*�T!���-)����R� �rv���{Y{���~3c{ߒ��� ����gf��e������{�]�T�H+˥�*av)���iet*�-��R�7��:��g���Ss�s�ܘ�AMoe$�1����f<���^�5��Ѕ1�ƺ�Ӌ�?Eδ( �V:w��r^���id<ɅX è���~r9������4NXK��3Ұ�ί�T�8�h��L*��"GP0 �c =��&���2�V"7K����2���8��X7��Ԣ����C�s�;�������1I���)mYaD����Hd��VY*��H�GW�?y%z�ed�BazQ?ƸEi������h8\��Nt� C�÷�����98dޫT��V�r<�0@H�9���5V� -E/�9d\%}�6�2UI .B���6N5�N�x�a�O��i�}���|?cO��N'����]^���ɛ�l|9��.���J�=���3)S�6�1�TbFE\�S����d2Ʌ� <�<,�7"�>�D��Kk��౒�@ePt+��%� �ר�w�L�$^�x� �П�y2ң�?_��pH*bF9aj����X��X$rB��H-�֘Rj~h(�MS�O�����{ �����!�x �b�%�Fs+�m��a�P�X,�������Ȧ��5���g:M�0�]F|�b��{���^@1� -��X2q�>�r @����Ti��sN �r�����,�RD��`b�|�Ak$ q�F�k�/�/��W�4@�����V4-�A�-��n�§�/���PΉ�����H���� \j�����0lt_��'J0z��O���e~O�A��G� KW>.�R1�H琍L+��X Wf І�Q�1�X��IA�ti���Y�9���0,>/���$U ��R��s5�� ���b��Sl�[��&8����@P�NN&��%)\%=�_���j�R?h*]�[psZ����޸�� *9�,����M��#�[�IiA����� �݇���rx@�Ѳ^BH]����BE.���S�v�i�Э A�)[����� אN�c.�F�g��h���hz��/��PeL �BIJT�'��U�����Y��%m��ۙ|E1�O��ry؆Z��d��.=�X���\����6(�21�?�"��2�����sثK�_�H^D��[���~Ј�"M� _GP�t�,��k?��pK��1�ې�@ R���0ʓ����'�����>�����^y�oGG��vF� �wL*��ڼ{EID�\Pu��uH��e����G]g4��V5Z���Z�$�q@�" ��) 톡�5�^�m�8���JfEJM�<�;��"���%��TG��tسg��W����xS^�ͦ�qy��f��m��� -ߣ;�F�0L�Q��0��XV���Џ��@��ǏY�E�Qk@�O�V%�g� L ��� 4�g�� 뱗;��-:��.y9�eD �E5���td���'0���j �M`��h��܋DQ���%^j�����P���D[���~��eڝ���{��=����E6 I�a@�'ߟb�W���.�ǂ���6Md�`D>������)mwq�U��=�Q�Qg�D�|u3a�0qĂ���}��pQ^i�ݙ�Gb������Qw?�ў��C�mWM������ݞ~ %4jB��4{y��?�Xmo�H�ί�CUE" -w�ؔ4i^t�*� �UUU�b/f���z�!\��~3�]H�^��P���3�<3���W��JB<$㰫-�/I���3Dc�����>��*�*kִyo�"i/��m;��H�7���n� =��,�+hç��ӻ�mx� �kCw)V@ A ��%�rgq>#��� �q����{-] -(��vd�^�9�0��i���3���U���/78�7|e*��1J[�TU����� ��id���\]�e��w�]u:�T �y[#2Q|bz�� 8���T�%�ϗEmx�DžR��*�6)m -�����]ϛ� 0��N���Խ2W�:��f41F�����d��Kx�a;������!]hX��P"�B�rN���6����r��1���@���^~h��?ݲ_�jj������ϩ:��F%_�s{p����e{ �'Yq�� �� wAӏeKE�{�9�o8ۏ|��"WW�Fk6U�$��+��W����Xms�6��_���udW��ާ�U'��4��ZKM'��<I�)�%H)�����.���N��Lb��}�ؗg��e������Gp -7*���0*T����_����/�4������7�|'5����1� ��ʗ��SH4C\F��?#=K�"�p��� -Z�����2J�F�������XD�Xʥ �I������fh<�O��zh�Z% �Q�:��B��T��"�%B����xJ��u���|��^�26 �zc����c,�[���i��Ү3g��і�A H���z'��u��:�O��4 [F�������5�����P A�V@��b Ҧ�$�λ��z�lpG���b�-zv0�~�F;�_�@���3U1�x��U�����XS9P�h�:F���6i��0Ns&����6�1�.G�y��r�����_�_�������`ܿ��� W�q8�_7p9xO����� �e���Ŵ �T�G崔O�ʖ,N&���)��S1�0�+s�D2^*C�5h��c�N*C����*y��'����Z��j�eJ�+0���b���+W�۾4���l)T�2��[�6ݭ�<H� -JL7,�r��)%2�*۩��e �F�ܕ�3bl?����\Y����o����\�s���͓+ -זX��FEY �\�PEi �l���x�Ii�����]�����R [�����_������ٳ�����.��[9ÿ/��Q?�ߜ�o_S�[��\[3�.;�l�l���-�[ x���$���{M��-���JN ��=(g{װc��EX����^�} I5bq��,�!��`B?���1|+!eZ|��E�O�Se�@l� Y �t\�H*�*�γ��ܭ��搤���G�l�eR+�Qf�:YC���\&�l���{c �y��Z?�ݼ8�SB�r^�>W�>|��L��_|{f1�!�ْj��?��k�uD&���{�u���A�/���B6�m����q"� ��Ґ.͖��m�YE���f ����׻;��;n�5Y��WN'WM�^��kt��s����{K�]���ӷ�vP0|�X����@a���:�XA��η�ne Kڜ�)<�]����8�����<հׁ&� �\�}RT�.��&�8��҆�_��4p֗�Z�� -���Oڱ8���E%'u���b����4Ҳ]wi=M ^ޒ����& ��ir��V��Q�hpϰ{��2�m;(�W`��T���"�>�Τ����x2A:���,�۳��#�/7�r§ �v8�v���^�f~�;v�ů&Ī �8�.�*{;Ȫ�"�Y�-���*��o\� z�]�#�5���0��/�����Ξ˛YQwnєЁ/_��V�n�6}�W �}��*��M�q�58��d���D�D$R%�(�E��3#Rv.�nօg�̜3dξ��z��nT)!3� ��.�o%��ٕ�Δr�Di�%[i!+�s�VeR;��7���"ß�l|+����\xe4 ����c- m,T�v��Z7_�]D����ڻ @*%�_ܭ�WװA��ϕ�pH�U~�k����G�`(��R���!����9��zgU��`Z-�۪󭨔�&�q]��kݙ&�rPuh�0����7���G�ό�����8���9��G�H��K%t��P]�9~ A̚D����.� M[���$i�v"����"�%&���Ez}��#�^��9��ߍ����D��2�F��hIA� �,Z��Ř�.��P�}�"E,�p�M�FG����1M����W�ݯ��t��.V���pu���W�>��t���1Hl��ϵ�"�������O��%��j���ʰ<]4��P�'iy*ji+�HZ�$s�G�<���]mqJ.�ݏ*��ʘ��O�)I"���]IŸ� ��l��CIs���d��ޫR�]7�܋��ϭ�i쪥�pHP�vMUI�5y�摺���u�G�����Q�c� u�^x��!×���tL�O�T9Z�5�'OY�I�����)E9�L./�.I`�%^�n'��G�����4p�^{��3gIb�U��(ji㼩B�1���U� ����r8z�_2�a��l�VK����&^����*;�3�?���K���?�1���8tìk��kC�a�o,M�k]���f E���"�*�����u �c��^������g���+������In}��>�e�*���h�! �ہ#"�ߺY��{0�M�3>!�8맜F�S�������t��E�<$��륿k�t� ����=Pk�\�p���%nFB�=��>�E/��^Y��`�r���l�80� �e�-���)�� QU�{�S0�#D�[���}o���Xͧ,�/Э�����;������߳�����d�Kj���\��#p�� a�msÐet�_䂟��|���\+h��G���DZQ8������T�n�0}�+���VY���MkY�lh�]�Gcn�*��6�Y�ߵ��iR5�!����{ι���E[�Axv�lD����LH!K���WJUc�ˮAi�O[+�^3cV�t��Q,�*�Z��+U{�3��Q�,�ǝD���'jPZih��k�w��"�R#��f�"���6��ְ'�_3��@/lEw��^��S)Vµf5I�'�K� �����EYYP�Dm*�R��II73�ڒփ�F)3գK�A�����pBh1��8��� ;�T:����f�KĚ�Lr�ս� ��c����R@��׀Y�v����< ��_1Ox�tN�r6I��􄹕5Cn��&����Xq�ך�n�~P>Ģ��,�m���t4m�H���6�g��R��|��8]�]�}��fp�vQ����;��&�qo���(�w��qr�$˨>�ډ ��9��,O��qN�E.���DP�GV��m���4��x&�����e���s*�) -'�/�;�0`9E�q;<�o>��dQ�;����K\|~�U_o�6ק8�����n�h��km�r[������Y&5�����;R�-�ِ���������m�����&��� -!��0.�(�l��N --+LT��P��R���K� *�'�ֆKYŴ�T���Ph��H�*�YF�T�e -a*�3x���k���@ -��R�N������A�eV*D�ERD�~�X��&P �:GZn6t�kh��BA�X�s[�U����d*��3Y�/7d+P� ����RI������u/O��ڋ1����R~5� �� ��o/��.z�� ��F�);�ә��]]q&2��k��>�\[/�9* ��5`&8�1��q۶C��*�@1~G����Kb>� -�&��l85���jB��5a�XktF� �"�E9��:4Aߦ�h"Q�_ ٘��"Ia�^��I:K�i��m�a���2��f�K�[��g��bNߦ��?���g�� IF��V�!�VQ�{�0�n�>�3^��色a%B)P�a�Q����j�S{�qM�m�7����$�֦�c���r��S�Q��(�؝�)x�g�Մ�2����VNu� 91������S˕��Cg�����7�8r����>y?��.�Q���i?����Mőf�=5m�r!�{�>�����=��������n�<� ��N�cT�ձ�d�� �?g���,�W��`�=�@`뷈õ�33�c�4��00E�P)��?+k �R��wY39��$?���< H��V��'!!�d�{z"b'��'����a[G ���ER��(�9�������8���SY�����>|�.��B�(��g�͚��Fdη/_����U�7��]D����iR� \��>�⺈�>�Xި��[���Ѣ����o�d���/<�=e�0Ë�7��ti;�7 ��g���s���q8�E�Q���\�P�� 9D���/�ߝT]O�0}ϯ��&��L{� Ȁj�X+�� ��m�ؙ�����{ݤt�DB���sν��O�E�����P�h/�V:�@�o��hg -Ll^���g�3�]�(S���x�X�������f�M`I*!鑚�o�E��I�I:�Z�����Bi�Z�U���b�"��,� R�@?�LGg0'��ϔ[�H@����(��0'*�e��(M/� ��sa3�.M��*_x0�F����l%vbܚ�;��.M�Z�r݆ч"b��oa�60��~��t)������ ;`H�䒰�*��2�[w�3H�]Kbf\F� -���6������0���� x`lw�KJv�^��s� t���Z+Kϖ *R%Ō��� -�B� ���u�g��`�L/�u����M���Fi>$�(���h�qr=����*OG)L��l2>MG�1������i4>�Rdt>U�M�Rʼnb��O�N�B��J�=��"G��#�0'�R9.�#��G�|h*�П����S����� �S��c�)q,��s�q�wS��5�]eußP��C#��}X�o��hg��3���gd���~�݊�URhFJQ�Վ�Z���ʝ��A��7�8���z��}Er=��5�fO��"�1��>�Y��_��f˲�}A�����Ŋ�C� En�M�k�~h��N0)k�4j���բ���G���]�U=�N�y�e���EJ�TK��K{�Z1 M����?�{L�x�(vV�?8����ퟚ�{G���9�N���UQO�0~ϯ8U<��$�at@�h��H�ir�k�ؙ�:�ߝӔڄ��b�w���܏�������a*K�L+'����B��٩VV�����P�)� -�����Nj2�2Ce1�=vR��^�^�V��nT.�I2}�����@�MG�ȴq�PvAџ$�>�l��O�aI��K��@+݊b��V�[XR*�璏%HE �'�@��09��zmd�r�[�ƮdM�-��dړ�]��X�u��M);Uo��wJ�%���4��FG]�5(������+Lt�XU�R�̣7�m� �7�$:���^p}�V�ՇQԶm(<�P�"�K�.H�Yr~@�{̕*�ZR�W# i��A��*)q-E��� �!�U1f��M�ۦ'�z�T�n�&|���d�'I���:^|�_-�zry9�-���p:��ŋx>��)Lf7����ƀ$����"��dE1��Sρݲ铭1�K�Qy�hD�P�;4~:j4���ZK$s�G%�7�e��xLx���TOsUz����)Q$���(�����g�2!���~�+CV�$��5��w�j��gs �xhѰ)I���E���uۤT?m�]^�$�7dBAJ�-�Ʋ~T��ݹ`�>v{�<��`Q��������(:T�nl�� �������{@��v�J��$iB���7`DEB���U1�=��–R���H�`O��'&w����ߒt�Qp�e�Y���nRr ,�y�?~ХKĚ� ;bc>v䃻�=��W���g�9g! s�aD�g�^ڦ ����?�@O����ȓx ���S��U�n�F}�W � E,���IXGB�Ta2 �d������.E����3+R����Aj9�s�\���f���m���B��RIU�� �_��VVW���Q�l���+�V�~���N9 �}�9*�8��D�����AX�V>\G���'� -�[��9b1r�:���A��q�)@���'�,��Ú�!�яt�m�FZ��šB����ZT ]�;,�)�{�����Ɓ��� �˘J���c�!-q�붧r��c_(S�q�\�;������޻{P�Ak���Lp X�TR��{��N9�CD��� <��K3n��ƹf�]�M�<զ ��'R6I�o���YUh-��g+ i�ڃhU.V��W��7�� )�� {ۡ .�tm�H�/ H6�k4�R����q:��q���s_���(��y -�{�[&�,^&�kQ������� IF��1L��JV��~0p��u� �r-s���V��ޡ�Ҡ����ZYP{������� 7�����r�~�2�+��a�wJ -��U��a -��f�Մ2 ��H�~ġl�"� -dUɒ����q�y�͊�V�<7�ei� �ھ�FN�����s�E�CU��p� ����;��Ѥ P�]�ի?0wS�'�0�~%\��~��<4v�{��Z>�׫��Z����R��J ���ՠk����E�gӮ�/`ݪ�|��Js�����`N��7>�Q>W\�D�?øU[E�q����R����A޼c�>��0|ra/��Īi�l�=�>��l�����R����YQ������d%j�� �#������#fEc^�1��(�OfCf��x��h �(w�긶ޚ���a<���!8��w�ߝT]k�@|ׯXLb�X��IH����,�!O�,��k�;��d�-���=K��PJ ��igwfg��n�]��Q#X�!�� ��*���g6���gR���6ڸ�K���ZM��;�������"�G���a�� ��Y�,�@рV�hm���X��M��vI�}᥀ޞ��p�S;��0l�v"<�6E�K 勉�d~N�{̽*�Z�֏F����&V���R��7��h u\cF�~NmzoZO���Pۄ�h%'�%q2��8���O�!Z��e�X�a�Z��4^-����##�����ZF��6,��J�(�'��s�i�|�5fr+3���F�ޣ�Q���ek-��i<*��PY����ׄ_�R��9U�R�֥G��I CA�_� -C�o��{��!��a�o�*�cA��πE�[������[)�^{��:9ϕ��(eN�m����AV -��� ��<����w�������`jP8�R -�n�8��|��M����و -x���JTt��O�=�����2�`r��o�o�5F�^˼; ��n6� l�yOOt�Q�&sgCq��ci5���8���}��W������7��o�T�n�0}�W\�i�J�xĖu-DL��dL{t���Zb�YZ&��k7�E �x��8>��s�y�k��iSX��+阐BV�j��JZ��r�Q;�$��Y;'�� ��b N@���\�\� �Ju�dw��K�G4�$z�2�*s�jĶst�+� b���9@��g�"],aGT=���#�p5�ze�`G�XY -ߚ5 $�����)�J�����v�z���BS��K�W#{,<�%�� R�Tf�� ���_�]���vr�.�[v�tO���D����L����A�"j�',H�;��y�����q�����se�x�ߑ�Y�|E�G̽l�Zr�k' y�=�Ċ�-qmX�'@,zC��j��v ���N��I�����M��|7I��3xH�O����&Ɋt��z�uv��:��$٣G~N�� YF�p��AL�w˳<�|Z�9Y�\�'y��X�P�g4a%4�VX?ZK$K�G+\���ߴ�[rMv?�R�2J5q�<$%���B��|܂���BH%�h��H9����F���>��3$�V���GlKf�?���ޡ,ûfO/���{t�!��Tmk�0��_q�A���c۱6KfV�ݕ~*�}��ڒ'�q�����b�.�QƲ���r��uQ{�t��V�DH�0� .r0�NRhYb(v���6\�5�p���{�-���y�Bc -F:�f ="���)��lD�,��h5zER�EK�TG�oC �"�\!V(��"DW~����2�m�)�Gh�)h��J��bi��Ѭ.h�rD,Pa�Tj'��+�d+P��t^l�D���>�%�{�tR�;3f�� -Yɟ��0� 4꾎&�]�=i���Z�9Lt�XU���ġ;u/g�������9) ��6`�O -�0�> ��m}��R�A/1�&g���H��[Q���֯�+�x�V��m�k�Z�� �5�h9.�E� �1���S$�� ds�����Σ0��]���p7�����p����U��5��`�������jH��Q�T++��r�(��~�9�n�r�5&<� �y�r�\�P��QU\�h5�L�=*n\Si �K����~������,�r�uJ0��E��~ -ޟٿ��J^?�q��Ձg$�"s#ҍ5r.���5UF�6L�n��Vc0:M�e�M�c�<��8�K������U�����מ�KJF��禢� �t������X������P�H/��v�h���OL����I���v/����g��e���꿀�F �I�vk�{�͖����C �w�'n㑷��| ���1�,�a�'�}8���E�`r�����]|���Tak�0��_q�A���c۱�KfV�ݕ~*�}v�ڒ&�qӒ���b��,�+��޻{�Oj��p4 -`s^!dRX�%�5>gS)��0V�|�X�ؔ���)Ce��� �`^ -��8 �uh�X[��°m� �'R�a/1���.��{"�cnE��P�~6\S�W[`�XelE\+ֺ �Ay�VS�E9vhӛ�xL�M�)���j�3D ���DI���.N�.oS��nn�E�X��t����x���9D�{��/�ƀ�2*�OJ;Ĕ��b~䧞�sK7'�0��H�(V"�r���B]s�Fk�dN�����2���~K.�ݏ.U�Y���G\n:��!#��(�П�[����˄�2 ��Oך�� ˔|���mx������o��/Ύ��T��� -A*����Y�h&o�kh�E~~�/x ����>��T#��� ��R�Lr�3;���'�׬gB��QQP���?�.5�F �H�wg��fE�������@%m2{z�:��{�D"���o�wu����;�� �w��� `x�3�`\|~�TQo�0~�W���&�`�c;�ei��U�*tU�*x%6�Mi6����@�4i��H���������Y[�^�Xx�� or) る -L��?�Z6�+���R$F*,�O9���[��_����tذe9=Y��)���D�,��d3:�)Т���u�:*�!#�J!�P�$�.}�M��Jbm��蹩)�k�z��R��p�Y\����@�S���v�xU��@�k�R��JI6#}H<�%�{� R&�3���Y���pL4����c{�@��5;�s���]�p&r�Խ� �wC���sR@��0`f�@mL{}�������QbpI�����17�A�ɭ��C����eĵa���k�b�+r\TK���L��j�H��O�6�z4 ��|�(Y�m�~�ޤp^_�q��^�j_Di��鴁0��ȯQ|�$˨>�ʊ ��:��d�FvZ�>�s^�䉪cB%Q��hQ�����D����q�J[�����d��M5�U*e��ä��P�����=��R��W�hń�h㠮�-;�7���CY(��Gw�^� �]����R�d�0����+�a�4�Lx�3c�^���x��H��[]`U�[?ke��� DI����B4\A_(�Ģ��Χ���&��ʹ�"I�m��h��!�G�uG�����6�����b�D��7��n��$�n�m ��#�G��) YFG�Ҳb��QL��s�n��T�(U�$��y-r�����(�Tť��dJ�qP�7U�п��SrIv?q�n�c -������y?�p}�( -.?f<��|/�s�Q�n��H����0���:�����:��0��[�P�����7��E�h�hl�A�Lf����a�zi��V�ѰT����w�?d����H��o-�.ў�c����χ10��%��hI�-й�{�;�����>�K�_�?�T�n�@��#���5U�iՄ&v�aɐF9.�V�]����Q��� �T����f�o�7o�t�Vm��pQ#p%-R�l������Fոm�Pr����^3cV�s��Q,�*�Z��+U{�3��Q�,�ǝD���5(��44J���;K�PX��֬RD_>�f���D�� a腭�0�+�{*ŊB�֬!��DPc�t�r��(+ ���M%Z�9)�f"c��S[�zP�(e�z4c ?����a�N�-��=�a��Bg�X��Lt�X�ւI�ѣ����~,�r77`^ -���0���PY۞�a��+� ��.�IbxC�&����0��Fcȭ����q~�+�r�Z��M���X����ҡ������MI������Q -q��/Q�K���o�� ��.J�x��vW��:��mBO��{��'�K@��Z�S��b*��X��4qpi�dZ�b/8ɓe�J�R=���Ѣn�q�5D��x4��P�K۴%�d��+5�T�T���cRQ�K�a�ϧ-����B*ӶE�9�7zX}�>��O�DZXi){�ڿ"�\�59��74͜��du�z�3O(�,��ٱ?ml%�!EEc��&W��)m�ժ��@!Y- -�7L��|l�R�d�|���b�v���Jp������% XN1e���7^��dQ�����Kp�9� �Tak�0��_q�A���c۱�KfV�ݕ~*�}v�ڒ'�u����;�N3�,��HzO�ݻ�z[{�d����DH��B*� -�[��εjt���J��Ҡ�vk,dc�`�xJ��̘���2E�`V;��)=b��N��nU&�A��EZ!���J��#7���r��0�*��bDG��p����3>#]G:i�tF6�i�9Q�,�|�(A*Z��,���w�띑�ւ��f+k�/a+�r�쉇k��N���#�}1������8� ����CWbJ[h|et&�$��K)T�н����'����:?>�Il���|�뺙p�g��`ѿ��F��=�07�Ħ�j�l%����T�bCZK�q�.(���3TqUL� Mp�k��d���M��FA a<�/A�S� ����n��:��p�j �Ut&�*�K�;F~ ��) ���§ڰ R*����Ӡ���ϩ�1��Lɞ*ZQ ���M%��!��G%�k���y�1�K*�S��h]:��c�)�/�� T��և)x{f�jB���a򹷵�bP�B׌T�}���uNE�ǟcm�ߋ便�����B�YT�1��={����gs��4�v��0������`87&��Nh-�����<��@T�*3�Z��;��@,C��|�hۅ`�M;�:�$}�&|�zQ �_�x�a�|��%���F�d|��.���q2�N����##��'W@���—ʰb*�Q����qഴ}��r.S���Z��^��SR�)���Z"�Qȸ1�"�r�0]�����8��=������\������AVG����#�ч��ӫX|�O���@%ݚl��J�{XQ(�$�S� ����aG��0 3�u�52];ЕBcײ�| �MZ0�ܦ%�[]6Tz�1��1�W��pJ�4h��^{�\lAi��}t@�0�%`y�I�b�ݰ�rƯM��*��T@��f \[)��s�EVU5�H�4l)����,�yA�[�O*CkI��4��r � T�X�LT\A_(���2��J��m�&�i/Z ��� H6�k4G0��~M�!|�.>�?-����n<[Lo"����|v=]L�3z��x��=?Ng�C@��R�Ca�!��(&�~j1p�4u��r%c���R��ޠ�cR�ɥ��Z�P{������zčDŽ_\������֙���4����?E�����߯��5!� �v�ǐt� ��j� -aD��T����<��. �Ol�b�`IS+J��Ҳ4�Y�-�nc��V�0�3A����DX��#� -�[(�i�9�mD�A,Q9;H}��t6>9��g�L�֏4�-�FZh����Y&9�(@*:(=v4� �1�TW7F�K�Qh�RV�o�T�Qc��!-q��uGe�u'F.)S~;x{d�N��mo���.� (���x�+Lp XYR��{w��r��]=�"��T@/6�@�P)��s�a7M3�@�<�sRv���&���*�ZR�W- i<�Q�T� k!��/�oB�R\�}��� 6�t/Z�H�7 H6�k�&0Nz�i���>|�>O������b8����^��tr:���z�p�=��'�}@��R�ue�!��(f�0p�tu��r!S���Z��^��SR�)���Z�Q{��������� �8&��8T7_3� �q��:%�u�*��y�����oMH!�(��Pq��� �.Z�*aD��4����<��.3�O ��P0��5� "in ��zj��N���"��BP��a;T�*{ҧ��n#���u'�.�6\��'�n�.܏�\�2��זWKw���� tX�/��Y���G�� ���w;+Q�xA����� -�����}$��6 -VZf�Y��U=���E�R���Z�4�u���נ�,���#PuQ��O�V�����X��=�������Ph��wY�g�y��{��2��m!zu��i����]�+ھ��ҧ����l���B��0��ҏ���nx&7����<<ܪ�]���p����?�T�n�@}�W�P�Dp�ǤJB ��-H��(O�bf�����Ј��b'T�W$dy�g�3��� �^/�Ld��h�TRe�V�=ieu���I��� -���V�\���)A�i�1��Y&�,�ഏ1,EB�H/]- �DW� p<�&]�W4�2Z(��1rQQ2�wAd�@�� B��g�x:Ò�3>�v�#�t+�#-�ڬaI�D�JN-r�� -O��3aR��rcd�r�k�ƮdI�b�MZ2v�MKZ7�j��n���-b��o�.0��|�t�<�P�Ae�5:�w����̥P�G7�^r��&�^pAx)����@��R+���0��z <�6Y�J ?���h|B�[̍��Zr�[% y�؀(�U"�55W��7�� 9��>�m��ez5��H��/�m�ר3�`u��0�F}����71� ����x:�`~ ���jO�3z��pv��O��U�,�T�TAL%;��^?��[�:��� �SY%2�L?��SR�)���Z"�R{���� =��c�.��5�j�+�:���ǦS�PP�g��П�S��5�]R�0hG�@�!�ZX� ��Uo���L`N -���6`��@aL}m�����ʃ^bpI������1עD�ɭ� W��r �&V)[ג����Qn�E��q��-Z�C�ۦW�z�$}w��\�a Q<�/a�c������në�p�D�W0Y�/�$Z��� ���E~��c@���§ZYĔ[G1ۙ������O�Ɣ�xJ�Dް!���\>jT���Hf47n����i�1�/���[�KV"e��ݤ���Q�[�S����k�d���Hbh �� L �G�qh��,IY+��Ď)�w�L��������L����%�rql�ג�KKFm|��D$ �l���٣D���~F0Q� Z�;*����C���Ū��Cg��B��v"�ޒՖ��w��_O>Wh%�Q�[ ܳn�4�jD�j��ӅJ�jRs�w�C����PT�9>�=ءx��A�NK)�����E�.5�f�� O�9o�yg��o�Tao�0��_q���U�A|�[�Z�Riɘ�ir�kb���v��i����t$@L�����޻w�w�M��t�V�B�JZ&�����FU�n�P2QQ%�Y>r��st��Gi0�<0j�G���ca�Z�3��(]���%ѡ��Z龺���A�gVh��5s�ѧO�Y�X–(;|.L�#��%������T,υ+�*�jO�5L�N-W�N����:�ڔ��z����F2�O<�%�;�RT͘�J�$����c -p��p;99���@* ���쀾�D��� �\r���k��!��8�y)���a���@ims�]�͙'��f5��#{_IV��}�cX�^'t��� ��i����D>���ٴr ���>��}�^���P���Z<�vF��ӟB'� �:z���#rcK-v� �xQH��V�IRŊbv0O���'W�Tk%���k�#��m؍ -m�[�dF�Q*��q�+nc:�?�I�{.�nUjL2��I�"Aӟ���p�m��{��!��Q�[����u����ޢ ‡�f;x,iC�$י-�c�� �����ysj��<h}���x�2v��Fԓ� �~+B�:{�ro=�h H��!L- -O.@cs@Ѭ���c��'���X"�F���%�i7OT0�R%��!{]���}m5<��gQxV��|�u�epwG�"���?n� B��BH�������~l]�wtyd�"Y GO����7&�ap��z�����O�T]o�@|��XE<�U��ц4�#�)U����q��w��9n@���^�� �"����ή�_���ⳳ�`!+�\+/����E���L+�+\/�ʼ��T�;����5��ل���Z��uH25"�K�7�a�U���4[�=�����B�큉���Ӌ�DikT�M2Đ>]���6D��th��R�t�j�DDQH.-*��" �X -[��\����փnZ����XJ��ɸC�,i�릓2P�5c _(K~;y'��Qw::}е؃��/�C��.�M%���Sw�A�$z�.�R@o�a |���{s�m�ND <Ѷ�{��5u6�毉t��U:G���HK=^�Ab��5q�D�����R�U9f��`h�K�z�$}@m���4�$��i�dc�KV���+����L�U2�`y�ez���eJO �������Wc@j��'cY1��Q,��s�i�|rs��9�Se#J�R�І51hk��ZG$ �Z�0T���i�5�Kj�#��l�u��nR�X�����8����=��R�8�w�g[�-�7���օmט�#v���=���j��������iU1Ϣ�21�;��E�iG(l� �$z�s?�>���+�^�+B;��t��i>Xم���|�D%�]"��� -vZݻ8\M�&�`Ө<0yx�/%jrr,xBj�VE���_�GL�X�������kL�w :�`��Z�B���9�.>D?�T]k�0}����A���c��h� ��ݖ>پv�ڒ'�q����+�NR�FY �:��s���jYy�`����@H�0� .r0K����в�Ee���?�X߳�F=yIН�,�������t�b =B���)���E�,N�p�zER�EK�T[�ǵ��bX�KF�BD~��f� dD��S��8"�p��;\C#�3d��)��Y\�A�X�œ��jNd�V<_��@����|��N;2z�KKZײn��n��=��?�>�)]��^��׿p蒭AH��}t@�0�%beUp&�n��r��6��m��9) ��k�LW)��1չ�7M3b��H���$����y89#��N�5����<���*b������t�r @,E��|hѺk��2�M�(��� ds5�!��| �Y8��Y�uq�Cp{̣�$��-��Y4[��m -���"���7C@��R�K��bʭ���S��vK[']a�3��<��,G�� -�� -Uɵ-�&�)�Gɍk*m�G����d�� Վ U�׫�S|�Q��(|ߝwS����� )��uS�n��)3�L �# -�.h�I�]�y��i�uL"wVօ�UwC�E���n7@��4Ԭ�cUۈ�J����v�-���Z�{I�����[�ҠH���nx�� -8��oc��� -` �9�)�d��{ݓ(Ve���v��,#����:݃�Z XI��g�{VuLU������DK���N���s�v+�E�q��� �g����zvYH�oNv��5�@Ӻo�i�w�2�A����xϻ����U�n�F}�W � E,�(�ITGB�Ta2 �d������.E+���3+R���1�� ^�r�̙�/�M5��p Y �Z9!�T9� ~Mo����e�Vɾ��R�o�)��v��G�������}V���b�v�0 ]�����,^\ݢ�����R�##W���1"�� ����Ĉ>|�L��9� 8�g��@#݆l��F�-�)��2ɩERу�aG��0ל�jod�q���ndE�.%^t`�1p��j��-�WuK�>S .���OpI�4l��n�w)������st@�0�%`eUH�R��Vw�A�� z���K�p]�6�U� h�f"<��6yЕ|$f�x��@w>�T��[��ǫ=��P�bEX �p}��Ec�q����v"�陴"��7 ڄ��pC�Y�c�&�/?%�evw7��p��n�ч0 ��-`ݳ�a�a H�Q*|� AH%3�YOOVK�'[a*�2��T^�!�;4~@*4���ZK 3�G)��e׳�xL��{�{ˡ��J�.���]�� ��U�����g�%B - ���)�~��l��N$Ȣ�9�����s��:�}H�fE3+j���21OXmϖ�i_�/� -u�uk�P:Tٹ��b�4�IO�k�5(M��M�L�� S7�ή�� -�(�2�EK*���R���87��]x�M�R>���N5B�$�^����><�6�j�`�e�> �U�H@��U�a><����Sw��u�;xW>ƑF>��H��¨V[E{ttszmIV�.�8o޲}�e$>���oP���M�����hͧ/���+���rwgE-�޼6O%?H#��q���zL�����ÞNFO'r -��È�)�Ey$��:�F���h �1i9�(w��Y ޚ���a4���ap�{;��=ks7���+`�*$-R��wUwrdG�؉��e)qm�ZՈR��8���dn��~���mgﵓ��� ��/4�=_]������P������*ɖ�r.��T���ȗe�H���u%&��,��4V�9���2��*��G�d����.)R�*_/�I��K18:}5�gZ�|�b��7y�-�պ� �(�y��7�*��8MS�����K14��4+� p�U�P&+�]^|3�L�6�,D��7�V,�yRL���|�)��u%�eZ��� -�;î��RȔ X5 }��k���r0F�7�]~��H �V�ɯ��S�}�l�2�ĺL t�~��� -��nV�,YN���np���_�l���"�]L$��(!��ju��ww���{y1�W=������p�U~].Ҳ����uV�_mD��&�`�H�p�h�h����{9a�R��=If���q� ZB3�;:ǧ=����ӑxw|���_�Ļ��o�NΎ_���oŋ�'?��>��^����`��8>�a$R0h*��*��i��N-jR8 ��Y*W�$�e��r�N橘�iA�a�7Y�[�S ���"�*�j�7�F�������2:�����V���~�?bO����I-���D� ���Q��7j͟խqq �\��0d��R�Jw?)�kZ�8iXd�T�ϐ�P�X�w�,ӐV)�a���-�W0�@r�%�q� ˑĈUM��X�+������TE�-�wf���u��(+$��&�4}��v��O4�8�w�h �Oe��}�6�9C�2#VI�ܤ�b|�?_��Uw����^��׌zۿ���/�"�,��G����X�q�>���?�?���������/o^^���=>��K,׋��% �>��'���n{}�u�J�@���g�V�qG�!�%Yf��g:�y���oShA@`��n֋*[��T�:� l��::��uR������xF� �|��B����4 ����(�㎈��� ��Mt��b �%E�l�d�v�H����JS錨S ��k j ��q�.�d H�R�{�Nz�a@%ŵ��9��i����ȫ���t#�ߘ���(�@Ul��YJ��#�b�5�}��.3��=P���� ���qr-���$�N�k�N'�9��9�����M~��U��j��^���J��F35���Z���b ��D���1��wY��΋�V�z�ȗ�{qs�*�,{�=3`)4o���h\7L�\������Mq;��ۻM�KP�������8���d��],r��S����҄�v�Æ��Pz;����� �p �@3����L@��L���[�' -繂�}x��4b�}�!E$Mt�"+/%s���D�,����|�~P��h�,gD�9Dӆ�4�%�KJ#�tk�^/����z��v.U�0�H���l�jc��#���(���tF51^^0Q�����G�9�͞��H���y(�i�2O� -6��d�}�U�J��p���%}:��~S�J>�����J�0#�0�O΄��� �@{4�f����?���܍�` `9���G�X�뛫�P���d�Њ\�s���C�_� �"��hE�<� -V�f���1���z�&�l�*h�� �5S��($�4�K�w0�BMP�YZ!� ���A�E�| v���� "��A�:�*�[T�w��v(��9��+;�����KE:E  �Ƈt������� X3u>�m�]J�z�q���O�5`�Y�u*�?G4�D���歨�`a �$ޥd4!i+5v(��_� �F\z�Wi��X���p�8,tF������0A���2b3qBd��'��%�-��(�jK̮5�Ӵ����4d���_mPUN^�2���k��I�|��14wJ��j�+��ؐ�X;ͦ �JKKT�7U8ޚ�6�C-@�b����5~T�G_ �|��M��τ��Y��# �&��/��h@�;TӺxd_)��C�fa�J���j�M�l�dL//I>�I5`X#�g-�񳈶qHN��~I3��C��0��@��ߟ���5z�ن4�b�LR2po@ȋ�b=G7�ߨ����@RL]z" ��Ln�+�`9�2`BB��E9 � X�,$Ӎ*��%�i]5�eP��M}{�x�%�d��Rn����+�9�Y^"�G�9CJ�����v���"� ���L����z�9U�~5R�C}I��/]�պX���[[��������C_ -�ۧ��PeZ���LKm��d��]���@�=Ծ i�[�ª��tD}bGLo�ph�n�ZL�ᯚ^�΀�$���G.jw���A=d��t�U�_�R�,L�ϝ.D�p���|���j�)��s@�!��<�M���cj^�/m�<͂S�8F���=ANN�G�C��<@r�hSq#�`�n�.j��E�me�T��ҷ -Y��m8��o��=�K��Ϫ_ -F3�n�~���a~Rc�k�!Ÿ�3u%ZEeP�$�^��KG�P�1��!R\g=ff���_��F�6 ���EZ5�Ke�f�@��\䢸ͳi'9 ��<#�Qw�@��AvD ���O���c|fX�;�d8��ǘ u��6���>�M̽ Z�Eo����.�xg u�O�n�z�z2�9�����W�I��Mp(,�$�U�o�y{.w� -�Xl�+�3l�0��i��`D* m�}�0�g����������������~%�˃ ]e߮�hʲߦ{�:���R��ү�� -[ԯzM73h�����y\aI�K �$jf�����P����V�4�o�jU�QX� -�(u��#u����<���������������~�x8|�)���E�,��^\���+�@��AX�e鲞�}y�[�����q����FΠR�G��`���Tqc������A�}���x}�*��poܮ�o*S\�0-}\>|A�x6%=��٠ėo�G�GH�xTH���9`p�k�z�7�jca�DU ��W�u� {����kh�y�� �f~�U|h���P[��#�^z����d]`d^�XUK�0���Q�V�X�v���?k]�ڬ�F�.fN��W���N!��ܣD���"��f����ކo��O[��H�}h!�!:R� -�r���l��������� 8KT��q��,0�2e*&�d��j��H%����ZlĬ��x�M��`�P�F����2����G��2�k�×������ �LG��ݭ�����RۙaH��NQ�k<^�-d�=@1->/����j� -�8�\/C������-}8�E�T ��#)N�v��+�`��hUOAR���������6�&9��S�S�-�`b�����kg�Yݩ7���]Ah,o�˹��A�]�����nŎk<~���Ǚ�v��Tޮ}��W)���b�e烩U�p+�3]�Mo>ͥ��c '{�����%��S��#{���n�8�;��ٻn{��/ݎ��}&D��|��I���%���u#��Tݭ������U��ی� $~ޙ��Vą �2<�9��v��������V�n�Ea�'7�k�U��©&�e�xaey��0��@�����x< t5���ꨙ]��ˊ�>� KG��igz���Ԧ\]�Ϡ��iҪkqK����ǭ��)���R�o6teW�dv�*����s��q�SDA�VXZ!����,��Zxkܬ�� +�uKkY��N!���64� _�M�qK�"�x�͸��80�LNRlFИ< � YyZn�=18�* E��,+����b���.ٔjL���¦��*tZ<��auHG)c90��yl��%s�U��OLO���@�����@�RbQxX�HE$@�9�2H3R�^���N��p�i)�ύ�.xR�[�<�%��-����شq1lD�^���-U��ê �n�-�l�o���f�vjg�\i(Ŭ��#�^iBsqD�}SYQE�.>R �R�g�@�6�� �N��N��KpӢ���wZ�K"��$pv�Ƿ�%#7�-$b^ �6:�XWv1�-� i­^�(�U��ӗo{����l�/]����h��p��:Ƹ{���<ȥF��ڊ�[<8 ����3�:��/#�3qt 51JGvwl|e� �Kxx�D��Je��W9����R�7��+���oSbZmN(yDM�o��*b� X���;�'�I�Y5�@ɣx6�O<�Xư����#8T��^6���������� ~��&ɸ�B�8K���yt�P� -F�Y̡�-"�d���;&�1�����&7/Q�0���,tƞN�ЉzrPh�ZH�uO+(1���i�+�$1m� ���/�r���r���N���Ɯ���u���z����W�� �UytLY�wڕ(�[��%#��F�� �z���ȨS�-<�4��������I�^V�X�E2�dM�5`��� �f��@�w� b9����o";����8����)�����L���-=��e`(�^��2���G#�d$�����[�4:�ei*��Vm�Ky�Ŷ8�?���8�|L�4M*?�N��_n�S;�k|D_h�eZ�f������>h��)�')�xQ� �x�Lҁ���u�p���a[MR�-6v�t�6���u�_Ü��c�˺��tB�ǧ�{]7������r:�B�c��� -��Zz��o�u�4/��bw�y}�؅r�d�ф%*~Fas�%�W�l�#f֡�r�&c^� yc`�ȣ�������A]Y�30��l��8��<�"kQ�: -��M�d�*�?Dj��z�K�����}��'������WGo�"��b���d��y���h�����WV $�����aU� �5���笣_� -bfW�P�s�EN-u<��&�2L���ZcŴ�s�t�9��8�g�o��N�����q�5�~�"wD�ҡ28x��#vc�����n-�c5r���x[;�-)��x([��u������:���/��F�����纴����K��D� �Y�qhUM �I��#�g� -L�O�/7i���;a~�a �"�Cۚ�k@��N><�S�H�uU�~f�j�&vV��0d~7�lj0�>��w�!pe�I<�"qEaE�Ϲ��Nۄ�L �������m�j�@���O U}&@7���>ͻ��bQ(Q`D����f�J���l��:�'B��7k���T�_� -_��F�vL��ŁnyZq�k��(�^���`�1�]��>t*Fz��Jnd�OڅOJڸ���W#�ʮ�cTAQ�U� /�D������j���,]L�CN��Y�m�!��w�"��}��g=�JY/�*�B����ұZ^X;�!�S�o��"��)�ɖ��D- \s���3��NT.����<�� ��8c!�� �q���xf6�E��PԂ}D�!�Y��R;���/m���)_W0H��{�KDŽ����3[�������~��N/�ReV�.#��!���}?�R�{���e?�Z������T�g_ �g�;�S��:0߬�`D�C��F���EQ%���#YFJQI -n���p?����"�R������������������G���2M�c�s�����HZ�t �啷�|�8a��IJ���Ho�Ε��d�G��J�);8�Uӧ��S��G��L -b�Q�3SV����5�~wlfΨ�Z$\j����ۦq�9|��z�(.��*Ą�����������2��c�Ak� ���#�gv��D�юq<���Y!�����|t�F�R?�&�m�k�^1p�)���$�$:�� XTz�Z�3&��i8dve��!��5q���c5Q -��������k����8�mU�5�~���/��8����`)��6�������7������w���Z�L� -�Q�'��[�U����v<��K���eG��A{Z�;�ᶂ3OTC�y?9=sR.aG����T�y��~� v�}����;�����$[ ��5�9t�Np���&-昣_��^4�]J4�ӜU60+p���>�0rI�#$8p{4ܦw`�eb����UA�e��$y(�M����C��2>��6ujm�9�'8��p�����Ro4wj��W5�����~����xj�경� 4`��Q!_x^����Y�˞Ռ2�~��,�D���Y���B��EwV�M�N��ubF?�L����3�͓h1���Z���ޠO �R��bc�q������}cn�8'�|���,R���"``�Hw9�I#�u��a_�e������I@S�Z4���8�n�i��J{��R��K>��F�i��i����M�ζ���O�����_���,3�B-C��i*���sw ����o��h7��?},��4!`��3-�����d��~�-^:f ����T���cHl��h��A��:��L��<� �N�>� -�k%�1ª>���QC�.��po�9��z/��{q�8,�8z Lk�X�uK -�(��uܲZvؘ�k=5��ߘ��[��� ��b�Q��;ߟ�Ի3�q-qa�܅%F�Y�2cdz���0���xRd&��y��33e/.�A^�V�A��=������%�Nxs �{aDv���F��&������?E��a���������3���ŜϢ�IA��l -�� $���0��V����:8y(ڟ�۪��b��w�������\�{Ԫ0�y_G[����뭜���;�M�&�P���7�GX��9:vfr�O���fi�"%�_���p���z3RA�U8 �~b��}�Хw4ЇE:S]0����❜4 �]�,(�� ��=�6 &��ۂ���0���x�݁�=kjO�Nafu�������럟�0�4M�[����bzW$�8y�,G���ԧH�[�)||���;fx;�S���eo�m��#�]�S�}P?\��M2�˙6�3��4�G�3I>t+Łx�}4l��~@KZ�� -XO2��w ��#?|��況5�C����) ��!�4�է�Ҝ5��f�� -j���g��l'pm�?�SN��HL}+w��4�K�� qfc�bn�S����=�ڄ�����ZA9:fM����U�8�3��n` �$Hv�%q���M�{�W����"+��n�"#���Mp�&š�N�u����� ����ds�:{h�T�Ɓ5����.�TtV�a=�vcV��b��p:R�[Ю �wO����.ې��/���)X��d�e�9)SU�$�^a���D�q�E��>CIY�ڕxnm��@Q�@e�h��>n���4j�)b�q�//�� &]��{5j->�i��q0itܨct��s��E+]P\[�,ꧩ��L|�'���,�Iym��sB��2��� �#za�]���߾�K�X2�C+��t+o}�? !��t�`�D/�c����z"'%kseJo#q�·;�J󴈷��n��9�&-���:�*Ł����~�!�\�!��B��m��u��NS`�K�(G�� �)�;�oܸ�� �n@IPDّ�Z*��}��Et\��t�`ѵ�V6���S��Źݒ������ �9�̔Rwqp�ʒ��s�q�?zF��1�Ϋ����a���JQ��H�i� -z��|Y�S�n��fn�����ī��Ӵ�Յc%O���~�0C��?� �Ɏ>/0�ٲ�ƀ -,��]�y��M�:�A1��ށ�)^"Lא����t�Wg�s�D[O�՗�*NuD��9�_���FD �`ɖ���&KcASD�TR��_7�#�=}#5��ߦ�s�ۮ�?���T�7�qI^�@ O\�� ���O��a��DWg�UZ ��>�����Jr�Щ���2 ���T_l�S�%��:��Ӛ�T�k�K� =�{�-���:�S�w�ӽ�]�w�}��ͥ䔟ܨ!���O�۳�#�� �V��f���\�{}@��U��� �B|�DN�A-`�E��(�J�Qp��ڼK��6�2��B\�c���*���\G�����Y�v��l�h4��r������Z?d)fE���i -5�S�{TZ�(�#`�$�� 4s�ݖv��_��f 1����ģ`���tϯ�Cw^6��Q�r?���ߡ���̵$ �y�/�֖hPr�]�d�� @ٶ邋%@z����^#�S�h˜6�ٶ9]���tv\��2��L�i�ӟ�9U8 �E'��_��5�Z�*/�g+u�Z��'e�-L��?1/��o^�`����hNZ4�ҲP�IJ����v��T���Ih�c�,Qf��T�c��֜�cj�}ocv����W����Ѣ`4������:�{ s�A�u%3�>㽭]�8H��uc�e�.�f�P�U60FAmb�HN0����e:|&��me!��{ˮ`�@�������Tޥ�P@��a]� �˃���1j����r+Ѹ�2B�����4wJ{ �\p�7��GV�Ь��_H� �)��P�|��qbYo�i�bI|��KE�r���N�8���k�3-#F�j`����U�q?�}�=iM�D5���)��e��|�'��\�Q�"i�S�}H�e��6��Ygv4�$̋��B �>�j�&�pMZ�I��p�kל�����8c�DwӠYߴҩ����i[[�@���H/^��#� -`���򅕅S-q�8{�ɤKu�/��TɊ�oMbږ?8D������@�nj:�n���`��]�J}���|}�Cr�� �E����G"�s��"^#��eZ���l�2w�l��n"2���-*p�x>1.���/ -���B3w�u��6�N�x�%�Q���yr����F���r�M�B�P1"3����� -�� �� ����T=�h\M-�`�x���*r�X�`1�L�)'P�v&��wB���ޭA|��L�R4h��Νz"c��F�O#}��&������#MW"�gd]���x4 �������of�/�������C|�Bn�0������"���1 ��V�Ol��a�ł���9�|� ��є�IJ��V�_�䏍9�� R�A�n��*��8�KS}��6m������"��tSNiV�ppGN��`��+;7���F -��؋}��h -𕫡���%o�”fS��lo?�o���ZyԆ�|?ZE•��CG�l\��{����˻�/ Ov���Oi��^�gy��`�f�� ���֛��d&J���L�m�I*�5[�� -̄���w�!wi�m$���ʌ�Ӿ�iO�^҆^))�*��! 8�����t+��p��DKj�g��hK���/�YhK֤��X����*"a��ES��Bs�>"���>����1�=��C{f���(� ��6�B�I���������_�ܶx�K�����:�<"�!����[v:]-2;�3��[鏔 `���M���x�z�HY�z��(�X�!R�T�f��8m1�0�oIh�;I,꬐�T��pG�`�y49��:��_�]|��k�귖� -v*���=������?<��������jV?��ǚkFL�q���;q)��v�#��_�l��ORK]�- .�B�����o� �Ymo"G�ί� +�+r��]6&��J� �jEV34��azv�ǘ��߯�_���lK�I�dcf����z�e�۟�E��zՀWp-b�L4�H��_�{�(�~6ϗ<��L�*�ί"��S����S��H��e�e�L�2�Vt}��g N�2���졙��/��"�y�9�:#΍���x�� -f�)�O��z��J��+��� M��T��,����q�3>gٔ��d���|�A����H�1�2���(k����e�B �v`��w4D!���Z(@JMw�y��h/��!W���1�Fwѱe �DF�E�=}��� % � �,�I�~Z���j��0�pGf���+";]�F���oI̕B���"C�'k`)z� ��e�$��Xe�x2o���$�T��]��C���5�#���s4���`������{��W#����7����f����?�L�� ���2<�?��� -B�O>y�-.O*员��K�9�s�����Hy��R���)�c)�!�"�Zl�J.�{2�ji,el4.S�]��#�ټ�5�|!���C<4&�bJ��T�� |��1���!� ��D�hdu:� ��* ��7  JS��l"s]��� "�4pe�@-x{֑V����L� O0ǡg� �A`��M���OB��͖\�{D?�H9u>�2q�=�87�`3�GVO�-m�������L�S�t�h��S��J��b���ȫ �"ZP�[sF��$i�/.�|���6_��i&��z��;K0^0/ʹ�䨿Ū�ΕF����KnS[X�^�Ό�d����s�Z� ��S�����>�X&s#�*�w��0�r��%�I#�5��i�M��[�JTK� �!�Xk������ bf��> �Til�������#^�XWec�3Φ�' m2��2��I�6V�ٖ��U#�#�a�l�o:O�\�mu��I��ڠsMˡ�+�h�8^�%�~��p�d[�+]��i,4�%-�{V���5��M�w�$y������f ���Ww���`��@iK�&)5�Ғ�=0�0�4aOܦ9����̘K��-9�Z촳�9TgH��c�=d�b�u�m�D��Y���M����HE�3H��w�` -���y�t�5�p�Q��+k�:e�(�m��*��|K����f��]��z����|E��5b_!<��`��_(?�78�f��R۲3n - ט�n��\+�{=K��60��3�re�Z 7*ט-5��8��e�%p}� ����*;��4 ��dꔑ���#3e����R�x�T�e��C�d�yCʙȸγ���[j>�� -fy�����&�EC �ܫ4� q��i��ޑV����FZ*�r �ӫ6�i�m���׭Bނ� k9����7"]̓��[���fa�^z�ɕ雈��L��)�W�y�rS�m��q$�q�'̱��������M�"����bb�aM��ac����j��5�~S-��n��䋌���jM<��B|{�.[ ��ăb -��U+�� ŏE��ūԱ6�G����� ��j�W5 �Utغn���R���G�Ci��lWϮ2_��qZQ�K�9�H|Q�A��=����A)T�<�rqq{����Mpe���Q�&�5�^�޾և����ty�*wC��j�8���=Ŀ6��V,�;�n7�=��m�B>��\����y)$�� ���Z#�7!O^ -�49@�LYv0b,*��}G�v �4�elT�=$�R�3�� �n�Ʌ�I ̼���^�q30�6A��ˋs�XZwQ���:@ -�sVГ�d_�����Ɂ�A���J�P�U<1e>��|1m����PZ�[F55|�O���Yms�F�ί��|�)���: �K�4����d2�C:���N�;Y���{w�E�\�����f�}{v��;黗� �h�8��X�2��T��`t�/�C����t` -�����s+��K"�!�f+��D,3�.8��RNEd�}t� ��W�DN1y��P��U�jP�27&;�����Q�pO��B�dG�ӧ�t��)���=� -0�. ����N�ה�A�([�E�q�tQZ�"��iZpB�.ب�Q{0&�q�����.y;��p~9!o��dx:&���|t2� �G�� F�����I�0� L��La�)GDY\���V�ϓ�X�g<��D�ӄ�D^3e�#cj�5�V��1�ǂEk��.yp_�*�W)S+���WJ�O��(w����Vh���vWZ�XbZ���T�H�x��-u�J� �A?^b�C�?��⪲t(����B��� ��r��1&��(E��(��v��0E�8�`$��2[[P��F��r� 3 � � j{$, _]S�i�Щ�q�W���LI�����8��[�>��bo�-Dr�bO�5s��� +phpab.phar8vendor/theseer/directoryscanner/src/directoryscanner.php�"|NgG �P��7vendor/theseer/directoryscanner/src/filesonlyfilter.php� +|Ng���;l�<vendor/theseer/directoryscanner/src/includeexcludefilter.php�|Ng~���Ť1vendor/theseer/directoryscanner/src/phpfilter.php +|Ng�\�n;�'vendor/zetacomponents/base/src/base.php�Y|Ng�����0vendor/zetacomponents/base/src/base_autoload.phpL|Ng=I� �Lvendor/zetacomponents/base/src/exceptions/double_class_repository_prefix.phpT|Ng1�MԤ7vendor/zetacomponents/base/src/exceptions/exception.php�|Ng +�F̖�Avendor/zetacomponents/base/src/exceptions/extension_not_found.php4|Ng�߃���<vendor/zetacomponents/base/src/exceptions/file_exception.php+|Ng�i��>�5vendor/zetacomponents/base/src/exceptions/file_io.php�|Ng��k��<vendor/zetacomponents/base/src/exceptions/file_not_found.phpH|Ng(���k�=vendor/zetacomponents/base/src/exceptions/file_permission.php� |Ng� ���Ivendor/zetacomponents/base/src/exceptions/functionality_not_supported.php<|Ng����[�Fvendor/zetacomponents/base/src/exceptions/init_callback_configured.php�|Ng�̉��Dvendor/zetacomponents/base/src/exceptions/invalid_callback_class.php]|Ng�ݛ��Bvendor/zetacomponents/base/src/exceptions/invalid_parent_class.phpC|Ng��3c�@vendor/zetacomponents/base/src/exceptions/property_not_found.php�|Ng���w�Avendor/zetacomponents/base/src/exceptions/property_permission.phpx|NgWj�&��?vendor/zetacomponents/base/src/exceptions/setting_not_found.phpR|Ng�� :��;vendor/zetacomponents/base/src/exceptions/setting_value.phpY|Ng�A�]c�3vendor/zetacomponents/base/src/exceptions/value.php�|Ng�����6vendor/zetacomponents/base/src/exceptions/whatever.php |Ng���*��0vendor/zetacomponents/base/src/ezc_bootstrap.php�|NgNy�֤+vendor/zetacomponents/base/src/features.php�.|Ng� +/��'vendor/zetacomponents/base/src/file.phpHH|Ng�'���'vendor/zetacomponents/base/src/init.phpT|Ngk;�5�Gvendor/zetacomponents/base/src/interfaces/configuration_initializer.php�|Ng*(��8vendor/zetacomponents/base/src/interfaces/exportable.php�|Ng;`mZ�9vendor/zetacomponents/base/src/interfaces/persistable.php�|Ng5��a��+vendor/zetacomponents/base/src/metadata.php�|Ng�v�bM�0vendor/zetacomponents/base/src/metadata/pear.php|Ng�L��[�3vendor/zetacomponents/base/src/metadata/tarball.php|Ng�\�+�*vendor/zetacomponents/base/src/options.php�|Ng���K�)vendor/zetacomponents/base/src/struct.php=|Ng�l �Q�<vendor/zetacomponents/base/src/structs/file_find_context.php� |Ng�JZ[Ҥ?vendor/zetacomponents/base/src/structs/repository_directory.php |NgC� ��<vendor/zetacomponents/console-tools/src/console_autoload.phpr|Ng��i�>vendor/zetacomponents/console-tools/src/dialog/menu_dialog.php�|Ng5lj�Bvendor/zetacomponents/console-tools/src/dialog/question_dialog.php�"|Ng� � ���Qvendor/zetacomponents/console-tools/src/dialog/validators/menu_dialog_default.php�|Ng+��Xvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_collection.php�|NgwO��N�Uvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_mapping.php|Ng���F�Svendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_regex.php�|Ng���{�Rvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_type.phpC|Ng(���9vendor/zetacomponents/console-tools/src/dialog_viewer.php" +|Ng7R28��?vendor/zetacomponents/console-tools/src/exceptions/argument.php�|Ng�X� ΤRvendor/zetacomponents/console-tools/src/exceptions/argument_already_registered.phpE |Ng�/wm'�Svendor/zetacomponents/console-tools/src/exceptions/argument_mandatory_violation.php�|Ng���Hvendor/zetacomponents/console-tools/src/exceptions/argument_too_many.php�|Ng'Y�ۤNvendor/zetacomponents/console-tools/src/exceptions/argument_type_violation.phps|Ngr[�:M�Cvendor/zetacomponents/console-tools/src/exceptions/dialog_abort.php�|Ng�"��@vendor/zetacomponents/console-tools/src/exceptions/exception.php�|NgN[� �Jvendor/zetacomponents/console-tools/src/exceptions/invalid_option_name.php|Ng�d��Lvendor/zetacomponents/console-tools/src/exceptions/invalid_output_target.php�|Ng�]vmw�Ivendor/zetacomponents/console-tools/src/exceptions/no_position_stored.php�|Ng��RGy�Mvendor/zetacomponents/console-tools/src/exceptions/no_valid_dialog_result.php�|Ng�X���=vendor/zetacomponents/console-tools/src/exceptions/option.php�|Ng�}��Y�Pvendor/zetacomponents/console-tools/src/exceptions/option_already_registered.php�|Ng�-/ߤQvendor/zetacomponents/console-tools/src/exceptions/option_arguments_violation.php�|Ng ~x��Rvendor/zetacomponents/console-tools/src/exceptions/option_dependency_violation.php|Ngn�����Qvendor/zetacomponents/console-tools/src/exceptions/option_exclusion_violation.php|NgeV��m�Qvendor/zetacomponents/console-tools/src/exceptions/option_mandatory_violation.phph|Ng�YXpA�Kvendor/zetacomponents/console-tools/src/exceptions/option_missing_value.php�|NgF�^�Fvendor/zetacomponents/console-tools/src/exceptions/option_no_alias.php |Ng�����Hvendor/zetacomponents/console-tools/src/exceptions/option_not_exists.php$|Ng�6E��Svendor/zetacomponents/console-tools/src/exceptions/option_string_not_wellformed.php |Ng��0���Mvendor/zetacomponents/console-tools/src/exceptions/option_too_many_values.phpw|Ng ����Lvendor/zetacomponents/console-tools/src/exceptions/option_type_violation.php|Ng��D/�1vendor/zetacomponents/console-tools/src/input.php��|Ng�&ԣr�:vendor/zetacomponents/console-tools/src/input/argument.php�|Ng�h"��;vendor/zetacomponents/console-tools/src/input/arguments.phpt"|Ng�ث��Jvendor/zetacomponents/console-tools/src/input/help_generators/standard.php�8|Ng� :SJB�8vendor/zetacomponents/console-tools/src/input/option.phpJO|Ng��N��Evendor/zetacomponents/console-tools/src/input/validators/standard.php�|Ngx��=vendor/zetacomponents/console-tools/src/interfaces/dialog.phpT |Ng/Z;�Gvendor/zetacomponents/console-tools/src/interfaces/dialog_validator.php�|Ng� ��5�Kvendor/zetacomponents/console-tools/src/interfaces/input_help_generator.php�|Ng^��]�Fvendor/zetacomponents/console-tools/src/interfaces/input_validator.phpy|Ngbutov�Lvendor/zetacomponents/console-tools/src/interfaces/menu_dialog_validator.php�|Ng���T�Pvendor/zetacomponents/console-tools/src/interfaces/question_dialog_validator.php|Ng��&c֤:vendor/zetacomponents/console-tools/src/options/dialog.php2 |Ng*�Y�?vendor/zetacomponents/console-tools/src/options/menu_dialog.php�|Ngw1v�f�:vendor/zetacomponents/console-tools/src/options/output.php�|Ng�0ِI�?vendor/zetacomponents/console-tools/src/options/progressbar.php�|Ngl�e�%�Cvendor/zetacomponents/console-tools/src/options/progressmonitor.phpF |Ng�� ��Cvendor/zetacomponents/console-tools/src/options/question_dialog.php�|NgN�ia�=vendor/zetacomponents/console-tools/src/options/statusbar.php� |Ng�p�~[�9vendor/zetacomponents/console-tools/src/options/table.phpL"|NgieK��2vendor/zetacomponents/console-tools/src/output.phpZM|Ng�ᬲ]�7vendor/zetacomponents/console-tools/src/progressbar.php�:|Ng[m|��;vendor/zetacomponents/console-tools/src/progressmonitor.phpZ|Ng���q�5vendor/zetacomponents/console-tools/src/statusbar.php |Ng[ ��rM�?vendor/zetacomponents/console-tools/src/structs/option_rule.php�|Ng� ��Avendor/zetacomponents/console-tools/src/structs/output_format.php[|NgXU0��Bvendor/zetacomponents/console-tools/src/structs/output_formats.php�|Ngb!-�1vendor/zetacomponents/console-tools/src/table.phpBt|Ngr�e�5�6vendor/zetacomponents/console-tools/src/table/cell.php|Ng(Կ�5vendor/zetacomponents/console-tools/src/table/row.php�0|Ng� +/���8vendor/zetacomponents/console-tools/src/tools/string.php�|Ng���F)�phpab/Application.phpJ&|Ng� ��l�phpab/AutoloadRenderer.phpc#|Ng� +��� phpab/CLI.php�_|Ng@�]�phpab/Cache.php@|Ng� "9��phpab/CacheEntry.php�|Ng���Ф"phpab/CacheWarmingListRenderer.php�|Ngry?���phpab/CachingParser.php�|NgOI!��phpab/Collector.php� |Ng7��6�phpab/CollectorResult.phpT |Ng~�-���phpab/ComposerIterator.php{|Ng�㻂�phpab/Config.phpR4|Ng ���)�phpab/DependencySorter.phpm|Ng{ɢ�p�phpab/Factory.php� |Ng��#�T�phpab/Logger.php�|Ng,�I9~�phpab/ParseResult.phpg|NgX�ߪ�phpab/Parser.phpnT|Ng�����phpab/ParserInterface.php|Ng���phpab/PathComparator.phpw|Ng�`=Τphpab/PharBuilder.phpg|NgN61_�phpab/SourceFile.php�|Ng��%��phpab/StaticListRenderer.php�|Ngy���phpab/StaticRenderer.php�|Ng��}xȤ#phpab/StaticRequireListRenderer.php"|NgLX��O�phpab/Version.php� +|Ng���j�"phpab/templates/ci/default.php.tpl|Ng /]i�phpab/templates/ci/phar.php.tpl�|Ng�~���� phpab/templates/ci/php52.php.tpl�|Ng^@�N�"phpab/templates/cs/default.php.tpl�|Ng�Bw#��phpab/templates/cs/phar.php.tpl�|Ng��2q$� phpab/templates/cs/php52.php.tpl�|Ng&�Nˤphpab/templates/static.php.tpl�|Ngn휺��"phpab/templates/staticphar.php.tplW|NgY�.�֤�Yms�8��_ї���f���%��q@Հ��&���Uʱ��H�$�R{s��J� �@����U_�����ӭ����������_��t:SЈ���ݻ�����OG0��,b(���#���}q��/Z��20�J����Q�c�J%���J9��%��)ɗ"F��>e�x� sق�T̀ �ϗ +���h@In�wܐ���� �]�^��r6�tHC҅�k妑���`H�N�qC�hxkf����3�<9~H;��kM��?�b���C��{ +��rM���3�D �_��^P�r@���[�R�tB�l}ա]�Π��t�� ��p4p�[���5��ۘ�!u�u�� �Q!�ŽvRg쓡6��iU��2i8 \y^�0��vHp/0č҂�:-�~�{=���rP͟�Eݐ��xR�mB߻!�ć�3H�p��s�'���j2�+Zp�'a��@]��5q��#}� �#=B�K`�%WzE��o=���iB˧�C��o�[���q�8 �e)�[Ƶ@{�t��6>lB� j���A0��-�������h�zYuS����%�#�P���R͸0ˮ^e���n5$Z�*|���4�]]��{��7Sjq�nOS5[ޟ�|�V3����N���d�E#�3 ���8���Hk6ul���B��Ҏ�p�4� ��4}D�߾H�sXDj�έ�}���E�I�)mI�V�\fJ�}��"g��8[&y����z)��I+T��+�]�cu=��IV���a$� ~�����?�")�� z��\�����/�v���a�UX)1S� p��^JO۫�� +c݊�j/ ����C�*����*��0��ES�Ck�)�L_��u�8U("�;�˳Tb�}�~KS��f��L~����$ D��`G�W�*�+�lر�D4ݚ�����Z�$�''P-�G�&�1,�4�ɒ�y��$4���gi��k18�mȏ�X����0Ɯ�}ً_�ڀ[�P� �u~�'[jrj�Fܡ�V-D����z�`�����u?ߺ�{7*v��2..@�%��嵈�:x*n�Պ���u8�~�6k��*�#��� +�=�X����o�%�ܒʊW��F%nk����3�;{�yѰP���,���s|�W̭�y1�烊y�7?��kԫb^U�����aU���_+���.-;�����^T��׋�5�6�F!�^3��o�Ϥ�YT��3{�y��c5ò�>���Xpbt��H_)O\<�F��U��js��UP�f/�t�qU��d�&,��0�a�o� �])N��֦�n�F��t�q� k[}" 'Ȕx�Y�J������I6e�`F�4��=�}��j�ͩ�>&�9}(k��e��w]R[�ZP���3�FJ��q��P�ߴ3��D,����M��������G��]�q���\��OG���O�����e��DV�E9?|[R�^¯��4�r�]��^u��̓���S�lV��k0L�1Bo ᨿO�b�Ju��wp���%K^�8���qv��pa,��:{)��ؒԿM����BfZ�g�#�9�����0��u`۶�y�O�Ge� +��Żg^�����x,{����4��[�f�Q��@̗L56��M��zVqf Л�o���]u�-EE��;���ÃթY����w����妚A-k���v6����7�s����]gHZK��P��.�A�@���%wC'�XS�#���Y)���ʖ逹E咛��n��#��)|����ֵn��O��`c|s�@~��.�u��J����W ���(�X��� +Y"�����/D.�n.b�4��V���7�}E���w�P���������-���3I��L�?�ۃ������f�����7����E�|9�7�VQo�6~��8t+fn�{YSt�%:& �I% �a`�sLT& �N��i���t��/"y���x���/�Ūwrtԃ#�����E�~3�W/_��������Q��t��� �]���-���Y�A���У���� �c�}p�z�5�L k�� x�v ��km����uK?�;`]��u��m�\7* #�r+tK��r�V��BX�a�0�]gﴹ�ƚVG?���^oy�g�<���Tc[���p�6 V]��h�)el� 7ha�=tڇ��ش�Z�N�%��`�;.;6+g�u��!����f�D��;@e����:X��N��?@:�佗�~������H�Œ}4�-� �u~Gd���c!�,�i��kf�����L�C�N�b sg�E���;�p�+3��&������by�M�y����P���e� P3�.hA +]������� + �dU9�$��Z2.�Y&��g��Td��w3N�ƁNg%%\f�g��D �VyY�:¨�IM&��S*I� 7�����0%<�d��F���*ESYňc�!�Y�%��2�iV�b�y��))��VP1 �� &YY>�10��n(i6*�&\u�$�1��QN Rɬ�����yG��2�W)Cƣ���Z�JҬ�"�f�D@�@�u�!�5'�H��#��GBRYK�IyA�͉8���$\-��Lf�h�q6�R���4��h% ��LRV `�.��g� EҚU)g9!�_E�(F:�!\N���JOq.�r�i.�w2�q��,T伤��I��tI���T�=t�2�V��ӡՂl�{��z�5�.�a팼_��|�� �n̩�Bz�ƺ �h@�B|�,���Uk`�;��IA� z�y �C���m���X���g;T_���������m:�}L=3��� +�g@�zx��U�o>)��¦7��F�w Lź���A��[���I�vM�"/A�`�j�{;���\:g���Ce�L'�����T1��ɾZ_w����4�?�����N�f��>��o�L��oo0D����#I�<���~x��9��)����6<|�������#�;T�J�/p����w�? �%~�s���Wb�U翚���aS[��C�/�VQo�6~��8t-�n�{Y�t�%:&�HI' �=0�9&*�E' �����ԸN�6C�Q"��>����zY�ww� ��o��\:;��ի?_�~��Y�0�����5�VZ�G��^��uTU��ؠ��ro�a�g���)�A�� ��И�-0��PZ�X�jFp��� o�v�2�Z�Bz��ǔ�F�R�a �5W���R:pK���*s��%F���5�h��M� �qk�,zR�)V�ƁE'����\��^)m�*pԢ��j�R��8��u�ŪTMQI�B�� 6Jo�ҳ��)��*B��Z�b�B��=��徱`�-��C�d��m@ظ`��f��`��h�BOl+ô��n:�-��MOd%o�}"�� �.�m��Lm��8�V&�@�V]a kV�"�Y�ki��Ӭ���y�U>��O/�&Z�lD"���'�,b(��OiB������9��S������!g��z�1��F㔴�sH(#��ݍb��LD������{r2K#v"̙����$4J!�N�c�a�%Ж:~��9#'�x>�P|>悊� p��IP�vJc� �yn���HD#??c�� +~���9�^?�E3A��ͳ��g�0��9'I�:�B�bJrv�q�a+Fp6%bJ�,�b/\�����Xl������`!#�)=&YL�l��(';!��~ m��E��}�a�朴Ít��:�(9��|�8�D�9��'����N��dղ�(/��DY,��7��Z� �r�Ɔc�c7��]u���c7U�@�fc�@�~`w0����X"G������������[[-nj��T�m���+�����yӞ)m�*qء��j�V��8���IT�j˺P�'��҇��i���%�_�.�ʔ� jH��:5�[��M�Ъ�n  +�9L1C|�]l��Da�<��]����>�M�K�B��@]ۢ�Lc��8��&�B�V�a+k6#�Y����l/����� �������ֶ��  �X�#N� +�qvC��hrB f��� �&� ��b�ING�d\��H/�������3N�ƁNg)% �#ΣLR"�@�8��]a���&���)�$Ɇ]h�g`c�O�LF#�R�������c�!�Y�%��4�i��|� q�)IN�f�1 7$� &Q�>�10~��Hi4JIw]���rK���*� �d�A�HL���'�Y�EȐqO� ��$�4J!���5�B�v|�✓���=��GBR�K׌%�yA� ��8���@\.��HFCo�q6�R���(���h& ��LR� `���p��\�$pͲ����דJ1���� �@�Њ=q��CHNcyx�q��˃d!#�)�&YL��y�9d�ũ�ghw�l "�';��/�)ʏ�-�gm��MQ��b��Ɔ��}�걫}����&\ժD݆�F"������i�zLS�r��~H���� �Bk����~dqt[��C�sU��зxޙ����ƪvh�C[8�� e����h��~ĬT�-, �Ìm������-q;�����Z������«��ݩ��[�E��l2{�~r��><��q� �ϒ8E�6��WX� +�~�a��g�7(]�� +A�)�֢v�n@ig®��'��l�2,�����ӯ�~{N +��7�e�JXmu��� ���#�\)�2p��C��iJh6fL� ο9���u�1m��>��2����r�mp|y�nV��Wx0�k�����fݼ����=�� +��������B"�/�CO-���}���g/�r������sBW +X _�2F�D϶��5,>E)Ѥ��À(+(��R���p�^��w��|�(���.nF�/��)��J ��w�Ma��Ze,!ӌBF�(?-&-~��Z0��ygK���J�9�����P���7��������o�|w ��^_�^܎G7py g������� \��Ӌ8�o��>P�T��H���4 ��်b�$W4a3�@F�yA���� +� VT,�D�J y +[2�UJ��mn���"��������1�B�9�����S�ݥ�M�������r�jV����]R�੄�����(%���C��}�%�, ������$��x�����CK��H��)͓ $ �ܡR!^����p���ϊ�������K磫��/W��ގ.nƗp ?����W/o�޶��ѵ[ڎnX���\Q#���i�h���)m���|4���<wvk@�dJ�i�W(�0�G�Go.�ގ.n�~��)4Q����*��C���T�AM�8I%��� �:F9x?����y��J�{�pQ,���M��<��HMOό�2�$�H0zܽs.6}X/X�@�S��� ���o�D�1 �*�}')���Η�`��ՠscȋ,��.hVd��u���%5���M��:��.>g��Pñ�9`�����<H���;pS��,|S�ܭ�H��/D�)˘ڀ�D�\��R�UI���QUy�ܠ�IW��_i�������dQ���As�L�Pd)Lщy:�A�Rt�T� �⒡�Ι��q~'!cw^وd��1�:g�S"�d�Z�0��d��t�>Ôf<����F�"�t2�l�����P�Y�?!Y� iz�0����~_��\#��Dh�,Wt����5�.����F���'���#�Y�۱��ߴM��{"�J��3�2㾜�� ��7�(�+2�8h9]�:���/4��|&�f���� |�4w�����Z���V�g�!<�� ڙ3�b�� r؞�ʨ˕��\�W;��Ѳ#����3L{܌��z*%OQژ�|�����b�5��"�,2�\G�[��b��fEn� I���{ ��M�Ԇ�?>H��u�D�A�{�A���=���,������So֐K&LAU!�ɋ��K�3�� ��&�L0J6��4T�:�G9YR =�T�ԝw96�w�f�eF����9텽�"m�qw63kj�,�\��j@ �0�<�7������cv/D�*K+��#IՕ��т™�u� ��QoM��LP�n@{j��Bd��A�����L�g&� q�HUL߾E?&��8�� +:gRQ<� �"��h��� ��u>��|��c�L�����c�����>D������G(�`9T�r��o,���f��e�m6�����_�Ȯ_��x}��/���4F*v�>��� ���(ogLH��_c�I�m�I���]s�vKx^�J�{ \h���HL+A�%.ۃ��z��������������>��_�~v����--���A��8��hϓ��U��4����S��O�}rOX� +8���5�=��w�g���4,�h�ߎj�[������f�H`ҶiF�H�~_Sу���/>=L�o?~z� w`�e3t/ޏ�0k��L�S4�"��,׏?|�q����Ӹi��C����;}���й�|>x�,�w�ܷ���K��$�bI=YN�pF�_PZU�X��o�f�Ӄ„#�߃Si�Y��F�(P5DF�����U�y��N�p�l٠�0����$�2��Oѳ�Bw�e >�nw[tFr}Q��Ӈ��O���aǂ�V4�� �GN� +���L�\goDZ�T�$\�o�O%�|Nń +����%�*���t�ݹ�r���9ˌ/��[���Lv���l��Xf�����ܖ6��c[��V:\2?W�>�V�#V��q�4���]��P��J j �Z�=k �:>���Y�_��R0�WoG&��Z{�?���>��ʼ�B��V%��`���Z�,g�`���)���qA�B0�:С�'CO�gQ<��W�h�g��ߓ.41ܐ}��d���B���r�(19�W��>�=��G>���4�FAOq��X}���6����q�iK!��dIż=�Z�]�u-����q�JPmu����ɮ�Ї�{�n?J�7�֚Oj�D;E�6���˝y`�'fs>�h�1���$���ƺT�hxߥ��H<��I�N���*Fj�*��mg<1�-��-�������_h&�Z����њW(��q�X�s��-��ᾷ�6FIleL��UyX�G�rp�}J�*E��ʟ��i>�Wς�����Ϝ*�Y�^�}c�M��7S} �؇�K�gY^gk�șxB>v��I��'�� �������~l���ꕞ���zro8��,���U7 ���Ȕ� f�X��ZYLT�uq�'�z�>�����nʋ�J��4��['�|-�]"� +��vLɝ� �o� \I,�6���Kc���2�`-��x�Bv���+4��f�� �v:�q�Q������4���7��l��5a������X�67Ept�+��K� ֙��1���&z/��dMue�kŌ�J�3�"v���\R��&�"�-��)�^���Z��ة�t�aT$�&K�%i��I��R�a�^����*�)���k3�}xѤ?��c O~���5��\Ϸ@5��#�)[d�*��Kp�1��������w9S�T���V�[���"�� ��>�[�qg�;���k�BO���DZCHp)�hu��VD�r��q{�a��8%l-�k�dkm��}~5�U�~��ϝ=�� +�{�� +~G��.��4�\閳���A �_�ZL) ��b������U�n��e4� vե/S�]e��xt����v0���AG���Y��p�R�Ûv7�ƞWM�*��c�����C&4�q&_�\]�6�� +s_�wO��$/��)��P&�a�����x���l.�륤�t�����*&���d�ijQ����zQs��#� V��Z{ cص5p1�5�N��y6)<���z���n�ꫮ�����dKQ���h��_��4,R�)޿�y�<��r�i�t�~:��aX�� ��Ak���7�=��Ƣ�}ru���+��x �������}>�{�Bp:ׇ��q�_Z�y��=���.��0��JV>t���1����1"�ô��0.�6�Wo�ʚ�Ȭ��CQ/k�Z�e�kl����ԕG �*r�L��i�5�@{1gN��hG�[�������8q��^��.�v����f;�^mS���QcQo{[�eX�k�؀#��V E�0`���n�L�G}ۆ� 4i�ho˳���W����vt}q��_���]K��ێ�i�K��Dii[T��5cHe��ҍ���ԾE(E9��m�� �������B���V�AkK2���n� +���� �8�E+㙩r�,mh�G�5�a�-��A�?ɍTt���䷸"wh=����!��x�l��(-���� ���g����ܫH��W!��I��Є�uv[:�Zm�5A��Ꞃ�Ι4Li܉t�t"zGzi� [0�AK:}Ͳ ݴ�\B[��Pb_)b�L�K\��q�9�z��AT��qmh� Ή��"��諳��TW,�T�%\���V��r�����-��ַe_�-I��~*��w�N�Zߺ +�ԗB� �zT�a�"hU0��7�o@��7� @ �8�H�V��:oiG�~�/a�!e��חݡ��F�� ��� .��^� +L�S���ml)Ձ9���ğ�}�K"(��V�E���7�zWE��. +#)�M�r +u1�\"�u��}Iz�M��4=��? �'}L뫻���/�W���8�dfTa��ܱV����{l:���v*���7����� @��|]v�-.7��t�@�m�������C��@�!��� /ğB^�����b[i,�a�Wz��D��Bв����^���0����eS3V�� /���(C��M���:"���<^��*�����K�,H��R[�1�ؾB��2o7p�]��� .���n��Y�����r������]�~�IK�5c���Z0�E���yֶ��j\Ry\��2�س]�~M|�8����b���3*/O���͂��$�� ��y��$e"XuK�y��qI�>p���ҩ��,)2�q^�5�l��1DWl�L��A�άm�5����y�=�b��6�<����ۙ����-j'��45/����|��nmK��Ay���@1�'L��Ƣ�����0M��ud4���n���'^������3�Wo��~΋iF+&”��j���f:*��zc��5u6��tV%�)v~���������ӷT�X�m����{�����~��(!L�F�����eQ��$)��E[+B�;�ZS��cdI-�h{�/ q �e�Ȧ���j?�,�R �rه�����ף�����������-6"����qR�Vu ���Z���W{�Uv��Y��{;}/g_|��$U�ft�|aZ���s��:;��6��3�w�+O�7�?ŷ��k���t-X��鶡��u�is=V�ʗȹ?���Uw+Reۮa����,��VUFn�EX?�*v �.�����}������(c�S��va}C�e��+.%�נY���yW�kT�CW����m]�X�X���e�y��4BC��=x�,$���z��W�ѷ��G��9v><{u����Ms�H����._�Q^��q�MB0Ԫ6 .�ĕ�H-i�bfv�eL~�� `{$NP���O� ?�R���\��fU)��@F������+\|EK���V�$�5�DJ�R���HcZ$*� ���e�t1J��PK�W��ZX+㰒�Xլ T;"`a��$�"����x���>v�� 6�K�RX�(����,�9`B�ʬ} Nh�@� Y@��ֈ�dPIƖB.�d�cw��-+ت�I�(�����u)��� .��yzv���׸�jK:�SJ�AH_�J�L���n�c𳁨���>P�� 7�(���Q��l���*SDm�ѷx<�%�?��5��"k���0��j �u%R\Un\�|�|����,dq�Զ��&J�(�3%}��F ��|%qr ������܏��F�EjM��E +ʢƂ�P�d�Mf-�k��Tb-؏�u�W���ȗv��ށ������waͪ �XD���Ȫ����������\E��!��4��s����?}B�s���� ΝuD��=|�R�_?�NE�r��RY��ەՏ��Q�7�^U4���;�� +Vf{k(OG~B��+���.�^��^p5yb���3�~U �Q.��e���թ�w�^�fo���S���߲.x'����صLw����LqRk� ����c��߶ڀ�X +cU�0}+���6��P!?��]��t��Nb��������=wv�UG~�.��ޢ!�x_;�K;����^��Rȁn��������;O �;�N� !��){�o�?��{vg��I��r�D�G2���7�2��Lz�#�ڙ�*ɦN����<�9Ex�Y� ժ=���{�V�i��q{A`%~��C!�L�)�(=V.�A<���s��:���^Zʄ\:��=6o�;���-:�D8Q�T�l�$�S�Լ���Ʌ���vŽ���v(�� 2��`kb̐��tKh���� 1_�"M��@�ª� ����>���מ�����2�=��n���} �_���:����~�a�����T�n�8|�W �N �T�1=�ug\a�ۢO�JZKDe�GRQ���~ %9n�.z1���rfg��[]�(�L"L�5#Wґ�B�p�����j���5Y{�ZY�9|4�����J"�����YZ.�T��i�+F�v�%�X�F`W�ty�Fl�${�2�+�]ƈ�qʠ�A�a޳t6R�@��lW�v��B��Z�*�JX��|�NPQߚj�Sf.⁆K2��+}0��T+��J��z)�r��툇�N᠚^ʙ�ތ)>��^��%�\@��:�~�{:@*���;8� !�����<�{u�1�'Q�$(H�ڝ�~P@圾I��mc +���)�Aa�a5_�����=䓬�Z��� d�ֵ�)�5�~~aLa�B�5� YN=�8ғe���递�0��,�*��,]�S|Ym��|�����n�ޮ)6w�oַ��j�N�Yb����ַS�p��6^�2�O.��4��g���՜���Q�,*��g�D�� �kA�@-�…H�m���>�Ӕ�T~���}>���J�I����G�_ы0I���~ w�Z��b��lB`�9��~{�ul|�<̉� *AN��*���0d���%�(�2��)�������eqœ +�c�%�M0W�:��΂ �}V�S�:��6h%��~S^dd�#��w�q�VT��sO{��[a���/ �w� ���G�C�����)�ҩ?���]#� �۷|p�jh7}��^��:t&�O�a�nn~b���"IBb�x�>�qu�?�K���9��f�E ���Y�*� +��x6�[�C~Ǐ���8��u�q����7ѿ�T�n�8|�W �u��*��4m���׳��mѧ���Ң�#�*�C��@Zr�w����3����7�lm�|<�0ƜAk�BM����i� �6R �'�KҞJ���VȚP�m�#�M�K�p�i1�@�Kr0�"�84��S:޴�8�=#D���O��(�/W��� [V _���DǡF�٣3�;��A�%��B��ָ&��*��(Q�s\����|�v���b>���C�`�3m/�HuߌK|!��דW�:����g� ݈� h=�d�6�5�i�b�eB��9&����l�]I +��8 "�Fu�*ϻ���T�ĸ*���e1{�z�|֊���?[vTb���V�EP���%�����9��ˈ�����eC�� �:�Xg�0-�%�.ֿ�>��uzw7]���;ܮ���jY`5�t�-"_,?^�8��@�EƁc?�<����8+�Kޒ�-K(��VT��� ����k�Gc=�.���F�G艶aG�[!�G��G��G?y.�`*�y�����V���e��f�l1�41i��a�L��fYC(�7����ܾCq.��{ �'�%yI:���<��a���I�t�q���� ��g����7佨���7���u���׶��G��]g�io����2�6i�NkKy�g�a��N4�c���AO*ٶZ&��{9��H�{��Ǘ���g������[�`k{��{-]��/��=�S�T�p���տ�s}~<�#��W��Ш�gB�Z8���1{�޽���T�n�0}�WUH몒L�я/�Dv?���<�}���iV��P�x!YE� ��J= �7������gB+2��n��,ml�o�H��r{�{��]��}�7�MY��)w8�9v�sKd��< +{�V�4I{z���`uH G�����j��[dM�Kp�w����!��aG�����ʞ��}O�_�����m��T�n�@|�W���F!n+��7u��$R����bo�U�;sw�[���⤩���U�n�8��+F�؁�*zt6۪�����Ӵ����%"�%�*i�_��b�u��b[� ߼�G��Z�*����W�L +˸ࢀ- �-{� ��D�w)˥@V1cx���0��J��JB*w�e�p%�3G����IC +rh�QKݝ����R��� +MT��f�D�~��$�;^y|�M��-�%l� Z�o��,Ϲ;�U�b'u� q@MӹәIu�yQZ�V�6%W3`㤤WC1�#�������1�G��I~5{��-=hԯ�&]�{i�ڳ�|��2Y��3�yt����'�[����;�f{���Z5ömg��;����d/���W��=�ZTd 4��pM9��`JU�v�N6��4�iD�)^\obH���}��3��(~�VM�8�፿���jvx����ys�|��]�]?�{�K���0��.�1-�Y�q�����~�{2f� e�����N�[�'v�Q$@ZK�͈��)�����w_�����'��&v{�%yS�oM����=�_���ʹ�%������w���f�@0�xx3�$j�'�@�����aP@圾MӮ��M�)�Qa�!�[����/�䣬�Z��� +�`Zׂ�]M�Y����/$:#���ܣ�h��!]Z6(� J�� ŋY��"��9>e��6�����_���2��w���l�m�96+,֟=��l�~�":h�E(��Iŕ���W�)YM\�G�dٲ�P�od�bh2��~�L�E#\��߆kK�>�Ռ�T~�B����4e�S%�4 ���?>��a��K��B �o�:��"�`EePC�)sD�,�aҞoGP�$s�Z�B��t��_�i^��k��#Y<�t>�"R�7Ý�֙�; Iݳ�ɀ�o53��7�,�f� +�d ���;�<pGM���!k}�c$l;�a��O�ユ����·^C�u=�$����i� ��`��5nn^��b��@5=/�B��j|!8E�C O�k|2�c��12�����Qb?��sէ����ж� �j���;�,��H��Ƙ��N�)z�k�7�U�r�8}�+�0� dX��#�lK��e�  ���#䋭�-y%9�m��;�m )M�I�t���\��wy�����Np!RW�2!��a��?0Cn�t&�J�kN�J��̘<���$ E��C�9� a�V�d�p� +1� �]2" %ɡ�F�tu���*��b�5QFҚ>0'����b< +���G�T8�P +��& T�+���H��Y +!WJg>�39�\�-��B���ID�N��� �T�ͱVa��Zʞ�:=|&�R���7��ă��j�{���@*��Ў� !�U���I�ѵ��}�KM���v`^ +�j�� $�� (˲�|�}��Q|���<��m�M ��)M�BS��,�S��2%��t��e���Vȸ�Ц��~�v)k��%�|���9��6> ��y���������l8Y��9�3�����b<��1��p��!�O�{ aҠu���!\>)�sS��J]%�+��2,&�ꖴo�|� LFHE&�������<���qT����m� `�U1� ����^�Ǭ��Iд�/ZZ8�kUJ� I�% ���V˔2oCGǼ[{��&n�޸��eP�ZS�\�A�_,� +�֖dto�v���^�')i�.�5`�T:إV9i�9�^[� �{p$Y�*����9�,s��/�rfe��M������Q�"��Q�GڄYW������?:���p��p�z6^���t��?��Ꙙ�����J?4 ��~���$8#c���͌�im5ۿ�x�W�侦_�����X����d���zd�"7L),OЩK��.춸����*��m�p����3��]p�͇ww Τ�����;Ss�V��¥&vs�Ԡ�^-���qT��^�������T>~J0������x�d� +aU��i����b�<�n|�����}m����ɦ�km?�n�d�� �ǻ]b囼� ~����43���s�I���������3�ѭH�Zw�w���T�N�0}�WU{�U� �4(��P��2�r���"�=�!t��>�M +h�yI��sϹ�v� +D�A�f�$�RX�9lA���34�Ej���v;�6����R6}NI��%3& ੮yJ�P+=�X�� $rc� 3Y��y��8��Q��4� �����Z4_�Vj�{F�\U$� ������x2ņ��q��Q������ԏ�H �e|/\l��|!�)g:s�S���煅liSp+'%�uŘ=qw���ʺ��Fuی!~�6N�Ix�#[xP�]��O=�b[iQze�6� ��R%g"��V���oI����@n�n��Q@a�EQ�4!���R�Q�0��'�y2�z��[Q�1����2��`J�th�E�I�-� +���)��C�q�8��b���w��jq���x��W�4�b��b~���<�b����!���!�ۂ4�Yi'Bjp�O�ޤ���e�u�(J���(��k�r�Dڏ�"]q�5`"C�+n}��������s��GG���xj�E��2'E����[���DA7�O�8w�ײh +r�r! c)s\7W7ش$h��9޸I���(��'*�gK";  �K^�{�Ha��Sk� ���!aK�1�+�Y�R��KE�8m��ݷ���l��Č�~��k�_K����i'��p��@�����ݣ�&aG�w��K�9c�Y��b�Fxi9wa�Sϸ v�����T�n�F|�W ��� ��MEB�`* ����0y��C�����)�(�|!@������oo۪���<���V�D�*�*���'˩���>P~���(eg�X?��:� +yM�F4%ge��Ӂb�R^12}t=�Fw���^f�t�`�أ�A��y#��i���* s���Ș�v�OWk��B��zq\%�6�8j* +񭩆��6M� �d +�;���HY9�^����1��R��8�=�m��Iw����a ���zɯ�W�vU͆���7�� J;t����a��\7m-���]z����D��� �8-��(�r��M���c +��ڔɨ0�����l�������j�����p�� Զ��t�5�޿`S�_z#NT��h;F`j�����>+� +�-3�� �Y�-�9����������r�O�vwX���}��f�m��~��?��X\���/B��'�4�3�� .ٖs9J��T�Q�(�76ሴl��X RjiąH���_m��w-�������ې�$���U���c�_n�Ϣa�����G[|ҍ���Uȩ3'�.Y+�y�|`7���C������}���=Sm���O��_�;���[L�@�R���X��ˏ�{a}��c��u�˝Aq���i޵d����*�˓�h#�(�/-��-5<�'�}�T�|���s^O�?a�M�8+�OK����}F6���� ����ډ��zn�G�p�ǫx��7��c���=��T�n�8|�W ��>��cz��u�p� D�}*��JڋL�H*�[�� Ғ��CN/�I�pgg���m�6I��S�H�PF{-����M�'Ǚ��{j�XR��H�-rn����b4T�� }��qo"ɢ%U3rS��,��t���\�7W�t�Fs@����J��:o,�##���{��́�9ү7�l�B)M��8.Ћ��kq荽Ci,�($\M D���c!h�"[�ʴ+U�az����΁m��ߌŸ#�x�78�n�r�zh� �uA���+\�:�&����MD��m<:�O���e��2���*�u�;�����삋�(���E��3|ζl>m�yq{�Xo�U��-����l�m�967X������ ,�f ~hma,$����ik�2��ZVR�BC��bT�m I�v/.�@�@#{�q�B��m>��]K�.P�xŅ�a>Ҕ:o*�i����U�5z �i2&�)��&��kkz )ArL�1�a�%bMjx����=n� ���ʇ�Ru6��� +O�#��_ ~�഑|O J�K���������������@<�k��a�u����˾��F�a-��e�U���W5�y� ��ѣ��dY���g��������� �]|g�A��� ��B"��^ �Y�[�$��7��ٖ�x>�՛X�c��=��T�n�8|�W �I �U�1=\�6N��>Dn�> ���HIEN������8h{��E�3�����oLm�t>O0ǖ��� V�*��@��;�(S7���oaI�u#��%�ZA����KR� +x�+#dM�u�{a [ݩBD��*�^�SYhE�-Zm�,_w^[4#De�ZR�-��(����l�A�M������5;��~C�-DQp�Z4`Uj��D�R%l4Kmn-W���YW�Y� %�Nɸ�x��k��n�r�z,��Ⱥ ���%�}A��tv�:�[q �=:G��Xe��ԭiX(ѣ��;����D_� ���4 F���2M��_���R�*����f�o~{�|9B>�����:�T��˜���n��Ѧh?+��=�j�nj�S�K6%��I�Vѡ�*G���n�g���ß��|^]]�v�l�c��~�>;d�]���ݗ��+۽_���dAGc�m���T�tӔC��%gHr��PU'*B�o���0d[v�X� +4ܲ�-��Gm˱�o���U��q3�G����T������o��^�y:�����Z�Pu_[�+p ��ø�N�pa������p���@��G���T��p��%e�5�Z+�m'}p�9���Sx1���w,�N�o���EhHU� IO������2�߲S2f����?�)�� ."|P�����OxfC���)�� �&_:����[�j��QZ��v=��Y:�g��3\��Y�'�ɛ?��T�n�8|�W ���T�1)���� +W�E�6�SASk����T�ȿ��Խ.wz�D������յiL��� �X�� 5{�Xq ��|+}�ڐ��B���Z�K2^i�l�s "�%�U�:�FȆP��%D����E�>G�Yh������ +���^[�GF��u�ޥ@I��6_��Wm�W�qTaP��o�à�7쵅�*R����v���T [�R��Uu�&�eR`���w$��z���G)'��f\�3Y$�L_��74��ί"�����b�.C1��L�ˈ�=�H�/#��� ���i����e� Ð�Xo�m�M +��rU���/�#���,��+KvcZ%Ů%�b�E����1X����85�W˦��-@3Dth�(��3�]�yy��|�~�i����͢���,7Ż|�o��5ŗ��3/�]��oȂ� "�� +���d��¬�.9CR�D+��EM��ٸ�l�\0�Ap�Vu�Ǒ +��Om���7F�o�*�U<��#�D�uM�e�|���nտ�^�y6-��K�Œ[=0�����B�5����f�:� �$6_q�ԝ����Ya�W��?�ӵҽ'�?$?���5�R���54<�=�‰�#�,��h|��aE�0�\C�#>���7�,��=˘��W9�x6�Lj��pa����o��B���y�?�|�#r<^ՌwT��I����c�X���c�}���[�_�`��~p�}�ԟR��Z�)ת�h ��@ �X�N�r���:6�Eg��� &sʦ�� �'���2�9�^��;r!��ۧ8�/�k4ѣ%�B��|sܚ���wj�؂����/W�X�����g<6�w���d���~��v��րAR{�N�N~?���o$�hH:�^�L� +�ʼ �������� i��7��:�߼��:�,UKF��o�Ҙͼ���N�ɽ�o���yԕ6퓍}`�[5�$�lv�8�nNKz�ӳ=�uu������0T��j/�Y1C�- 1~ ;W�����>O���_�T�n�8|�W ����*��ں�� +W�@�^ѧb#�%��#�(�"�~�JJh��"�ܙ�����lm�t�H��F5���@J+]!� ���@�sA�jk��t�\�l�2EC�/�gU��\"�,5#7�Гc�w��7st�d�9��Ck�P�S7]0���s�:�%�3 �v�Ϯ�8�F���K�*�������q��T155P�`\+�D��\�����kv�Vv 죔|3��)m08�n�r�zl��b����W8��f��l�F�-�M@���,m��(LkE����!��6����H��N�@a4 +�C��i�����ޥqU:)L?gW�m�~�z�j�|� {�w�q��#��Ft�0�b�د4z���\D��F�ԤǖM*�$�h�84[���>��,���l�i�e������v��s�q��~���n�c��j�-"�̶/�*���w�E�A�~ry2MS qVF���BT��t�QŨ�-;YˮU>�A�D�Zd�|���mڑ����*��܎��S�NS9�f��[���K�H�En�Uqgz��fͷ�'�4� K>.:�XK.���5B�A��1N������"�1���Ұ�g|�|��]Jy��U�w�u��p��L@������uE��?K%���-��ha��[r�"N���D��"��t�{싼 Sc�.���Ӆ$�����=�=��A^�,9���� b�?�z����,��n�Qe|����������o$�}r��{�� �U�n�F|�W � � ���M�F�`* ���y y��M)����#)ɪ����������׷uQ����.q�JFj�'���� �HߓㄽW:�Le÷۔k��FZ�s��Q��g�& g5�#1ߒeܙFgp�������h������[�n��(����2W�����9�_,W�[lT�r�3����rh������,S��J(�1� +��rN6���wV兇i5[W�:VB%��q]�!�7ؙ��rĺc��l�P~�¹/h�_� �v�ƣq|�2Ci���KE: ��>G|탘�X +T`6��@�7 +(����mۈB���y<0�?�on��/��W=�.�9X��Q�3�w��.UJ�QR+����J��Jf*h7���IɆ�{��hPphL��l��� + c�DOΎ�i�Az�w�՜��JQ����y`&�f[)'�:��P�J��RN���6�Ȼ���Jf*�x��#���&g������ݪ�����0�/N4#����i5ڂ5?��A6��ԍ�D�FoT��n�����w_k��$����|AԿ Q��2�(T��s���u�ߴ�0z@B�K���mR/ein�M wv�Ի�,U�^�9p�*Tq8/�,z�o"�e8S�/�3�!��թ#c�$�Ԗ3g�!���� ��6����q��7�4ޞokN=gAĞ��ˣ�*K�J�]͡���t�C�8�7�NC!߾�����t��$��ɯ����E���%�YHz����� .�취 Ρ�7��v�b��i�)&f�'�^V��il�\�o�����**Տ�?*�\�k��L��O��(9�[�owA[rN�ZVC�L��z��h)�@�ʢ� � ���T]c�Y�d(ˁ,_a��$�K�j�����sc%p/���i����ߕU]o�H}�W�H����>�˶4M�h+�bڨO�`_���3ޙ1���;���I�5�̜{�����*�������!�� ����r�H> +K_EQ��cB��Z!)����Y&�,�p�Cf�HrB�7��p�k� +����1j���V�hmPj��5r];mP�!2CT�r6b"_~�\ͯo���ǧҶ8J�H���Ң��;6�@���֢�TmJ? e¤�3����,wЍ"csY����ķ�0�-ܷu;]wTNXwbL�e�o�7�܃���p�ΣK�����cu��R!�eUH����z�����^�g� +��t��r窫(j�&~�P�,�F���7��混��Ed- �SKC)�;��*d"�� ��m��K��H'U6a��#pj�Q�~@i�m� +�;4�Ř�C|���x�������gww��j~cy�����|5_.b,o1[|c�_�ŧ H�� �2LBH֓ғ4�3pV:�lE����PY-2B��d���ȔҲ�B�(d)���e���g�C%��\���a��#�D�tF*���}�ݪ�Eo�˨?�/e��6�QhrR���w��X�-����-yg��H�i}��e�)q��_����L^�����zX< �3���ZYg��Y(j^)��gr�DIaW����F��H� ���8� ��k��;M�5V��.���Yؾ��������Y�R>R +�U�[�w.�w�-�FX8#�C��C�i}^�@�7Nj�Y�@�Uq�E����s���F��^-�k�b���>;�cJ^w� 7�0Y�/�q�#�ߛZ%���C�{;z�ŤjrNp +U��l�)�`�[�!��µ;2r|7�-�;l�� Ճ0F�F-���O��&��oJ���6 ��P��G���d�(���6��c8��v�>hs��, �љ��VCX+3�w�% �N��|��ڊB������Ư����)��uq��YCW"��-�sd=L��c�{obFC�)#P��) Q�MUu�izT��F���ʱ�B�B���m�`����{�T���vP����a��d�����d�eO6f��#_�CG��Eb��.a� �&Ě Jt����R��K)1uM�Q�;,���x������u��sT��Oj�t�v���i����� �����l}�FK�I�l@��wN��������!��pP~ur+ Es�E�����F��6!�ئ���ߥ�]O ̄�<ҁ��Јő��o�އ����/_�ذ�^�l��<�O?O����qJȆ�o_���R������������V�`�+�"D��8�X��g�!1�a�e��P�x�;�gq��.���2�u�ur����T�n�6}�Wl���Yֺ�� ��rWdEa�Ե�E&�K*�3���R�b�Ч=��9��{���olm���$� r-��$lUC��BcCh�����V�8�����Wfg�&�]� r�*I:�����VȚP��� &ܘV��+�1�7c��$��І�3L�F{V��Fsd���hkQ��/V��,*�R�#�J앯�k�7�p�Q�*� ���E!�T .�� �=��j��ĮV6V�JqӋqG⾬78�����u׌ ~'v��yz���#h����h�Cӟ�A_$Y�!��6Jhѝ�o5R�#1/���V�a��]�ݠ��{�:���}*���p�����j6/f���Yy�rL�[�Tbs���QRlB#�a~qLq�Jc��+]M��x9�������qB�i���ݴȋ >�_�W�0].��U>+�X�j1��W�b^`q���> �����51��`�0T�'�/��kY��,I�U��U+*Be�u��%�)� t�F픏�r�o�?��O�)��,�7i/�,�W!Gδ| �[+�C��N�ぐ2tIJz>eɫR1.Q*�bG#��7��l���"�� �����)i��|9�Z-���bv7]NW���e|�$j���>J�j��c��O����бb������pt��i���:�Y�.���g��c$�.�Ϥ)�Y�B�Xf�0��^ ����V� %_jB0��yg���W�$�F�7aIp���R*apoZ�G�F�� +���ò���!l��֦�kH8�6���K_~[�(�l�q� �'��IJO�6Yi$�A0�5I�m�!��嚩R���1�-�N0� �)����� �Zmo7��_1�J6T)M?`׭�ľ��F�(���I�W��JV����ew���K^>��g��p�|�p�ܟM��A�����'��q���?F�1�W�L�PC�0�{` �������/RM�rlL!\�L��p)�s1�:�LĨ@ +$k�`&��Q�Qf���!�(� +�{CD }s7xu c�X��kg�1,����r �a,�8��5K���T3�*�0S��L��O��B��S���(��UpF;�Э�������.��JS�/{/�c�֨�߶�N���-AH���)���Y�p&"k�������A�f � �� ��05&=���E�Y{RM�!���������/{/����֠�+�a��� ��(AH؂��N��~.`���b�%k(P��bȂ�\�4��������v1 ������͇;���������n�ë��׃����n����O�����u��)*��TQR��ĸĦ�q�ϒN1�cA��$c������!E5�&V1$|ƍ��&�Jla���,z$(ZF�����g���M�}�*����ľ8���}��Ǩa�f*c 1vsM1rkf�yp����O��#�/�/+p M%�t��R �O��u�89 M>hT��s=h݋�i �^�.� ���\��ɠ n6;w� ��t��1�64঱|�-�_ ��{�{�pn��Bۚ�P/�:����\���U�{s���.J�;�~ՆvC����v�߭L.d�'�,{"��T.��,�1&]&�O�`��=�8�Y�|��L�Ixַ��ϙAФ��8��Ȓ��� ���G\���^�}��e��ц z� ���M��� ��k4�� ��ǹ�j���+(ƾ���\�ɔ�����J��(�Q�7x�Rl[~�x�����6�mr� G��������Ϸ�]}����~*5� �6�]����t�0�~��W����u��/(���w�r���[f�Q���E��K����>��T�G�̃ ��adڸ�"^)Sl�W � �=�H_uL�N��\� � 1_�y�k/��� T��|��V�fC���0�t�v ����j�"�.�~9k}���ǒ�v�xB�ϕQW{_y\��T|2{��'����n�"�c)�^q��.e����s���C��l�_��"�� +ߚR�5�m�/� ���`qB������;_i'��*��It�NN�43�u�f�"3�|iϮ�=�-@�,�CI>�{2#K%ۦº`㧣x�P����fG���{w�#����Cx�9ꂤc��S_r�W7��ܯ��1��z󍔰�ar�Ⱥ%���bԲAz�VY��6��e��”hH���|>�ifîӴ���fR�6!��ޜu����bʣ)ܾ�%eP����u����9x�T��I ��mQHn�v�t�� ���%�����VZ�XD��ܶeWD�J!�6|��� ��X�8NB�袉����xp� ��n�(�HR� ������Ͱ�~�=s�#�����ް3fV\�h�z�ӉG����:i�:ަ /��8��3h��lo�p�S�j�1����O��w,ڳg�حW�hy,0����]���Rl�~���1V�#��'�V_��t�n��T���%��)�� �V�c���Nn�ڂT�:b��ۋ�7�bΕ��l�3�m�2b��xz�1��rW��.�I�{p%U�n���t�M�}�^&�{`+�`F��5�F�-k -�fK�/�n43M�:A�bށ6�C V�b�Ç�������'��_g�Q�� ,�hw7�nJ��і|�G��bV_��S�tL)�|x�eq�iC�@7��9ua�s>묎L}-@����)�W���;;s[����]]w�YnM��6*Aѩ 9�x��� =�S/�K܂�M��6Fh��P���E��w$� �o��o^3�����ٺ7�LT��.F��*}vc�H��)MdL;� q�f*˿�T��:{��5�6�L�~ߦ��?�Ԋ>�ДG�0E�0c\$K�*6�<�#SV丂�Q���ʛ&b鲠MxWQ6��'�#H��� ��1j�����f�� FX�yբ�(B��ɡ��F�/A#��# +ԩ�&,_�h."�p�F�� !�O|Pt)���0�?K����D�������s��ԗ�3 �6Y�ZO+�l��O�O�#� +/�v$�*S�v!O-C��6�f 3�ж�%���W���N�zrx_s�*�3;R�Ok�0d��N O�V��#5<����������ib�oa�Ɲ��Q+�!��Z�}n e�Fwo�ǝq��3�B�����Q��'�'B��ߍ��"*GA ׆E(�µzS#��Cv�2(���r�_�<�o7���x5��nMvoop�4���ƵI`{7׫Ӏ��$�G�ɱ������c8��\���8c��g�����ɳo�e�(��G����EΥ�Xv��b��å��5�^�Z� +%�����1Բ���[iX)�!W�j1���P:������L.9'�o�^�����(i!��� X ���J��\i`E!�hV��s�W�n�|�t!�rUm�X,-����,E5�BR._d��� +6���$T{f��o\$���'pd�����=8~J�WlRY� o���yeAH�ժ*�9����3&?z jf����P�t0����:ɲ�z=a��D�E(̾??{�����?N��-�%74��Zh^�l��J��Yɡdk����/$���B.Ƹ�H�԰, (Lk���HB��p~yߞ^�_�����wo�z�O/.N�\�����p������o.��k8}�#����7/���]r ���H�� ���H�)����d*���ȡdrQ�����Z��T\��A�`��R��%�2��G����EP��%p�F�[�w��ڪ��-��^�Uk�8/*�� �2��G�z�V���^�T�� M9Y��˂K +n��0(���y-sg[�n2�TYC����|K&�R�E`�*;�c� +�,���R��3s!� ��ڈ[�?���rU����!� +� Ja�V�~sɍF�mGB�eM�n�Y!4ϭ҂�c�Kf�����&��s��yM���ɑ}���I �#8�C���f�#��&�/Fp �O=�-4Qd�b��UHB ^Itx��Vs�N~�������/�o3�K��"7�v�����E�u��6�-ӂ�z����b�8��|�5�9)4�w3�9���`6+�m�i�;���gF��䫏��I������YX���7� �|&$C�#��L��h��Jc�F��<-���Q[0�vj�LȨlj*��o�E� +����,(��k�+��̌![��R����A&�?�܄q*��r���4m�Ҭ�\�� +ɞx-H�3K +����-qo�ʛ�?Ǥ�~+/4���� YA�k��D}1�Z~gq;k�^:��ULH�)���r&a��X�� �0KF���5�� +1'��+K�xfpx�.��-��(g;&���6���gF��� �����K��чI����x�V<���5�i�����#~�cX���1�M�n�a�rZ ,���l�{7"�G��?�!�r��h���F��7����8�2 ���`/ �[�w����q!�� �~MF��������(����y���ܒO�k�p�v�G���XHLm����k��95p~��F�Kxr��'O�x�m����4>��(=�� +f],51�����y�2�>�4!&�������ۋ?^�zwzqz��& �O�媖�믇^�R��ӔP|4��{�����7ƇG��$j�,7�.��Ɛ� +��@b2� +��;�O��\�U�R�K�֒>� e��j09t�*��ڸĘ����&��y&�8&Ї>�'[�ȹ�$�q�Лq!][v�)��Z��)�lc`��X�Ɂ�J�ZH��ƥ�X��,�B!����N�_?<$޲I �7���7qT'�5� t���msșp��� `���6^B \lpxi�Tq1Yp���퀀�F�K�DԱ �ۑ������xVA��(|���6|^�M;i�2:2 e��%F�듎D�����{)���sϲR���<���/� z�����2<�����-�����e�#Լe�!Vw��r��.Y�cU�H%��sE�|�k�A�ߐ.Rz.��(8U�h$�w\�p�V�bF.JZv��A�W�����sK��|��v[, �����uN;��Qgq�u����β���E��ޥ�.5փ���Q�zH�(_G�8g����'�X\�I�=昊�2�w���=����~���3���I3��l��j� +W����vLe�Y��"��V��JO\��t|�鸑���y�nRC���|,�N��fK�B,�����M�(�sDE�����!m�i������)��a��C�a�0�4��u�6�89�xu��MD�u�Ĵ��?p�pX<~��qt ���t +sV�Q�;��SMF��o��Q���� Y']ObjLt�������E����>�_tS���g�š�G�>�'cp)3��=�C����0&Y�9���t��u��+�o+P0���s�i���8H��J���n +��8X�s̤&x:搄�<ąw�UU�CȨk� ��'ۙ�?�0>ֆ��>z�$>���l��"5iUS]AF]0��{a,L�e9?9��w(��!��}׸��g���z���ㆆ����l���]A�w�wXh�m�����6�o7P�9�K +/�.9V�\߇3���Y����z��r��+�ig;��[�VK16�J�"�0L�b�c���-93�~~Z�jR�� SAFr�cHo���G`���#�v ��T��~d�x�(�ʋ?3�����n�� +�S�~��v�#��Op]4>Sp�r��u<�$Qל�cr��r��"��۠���.1�n�LE��B;���wƙ^|�A��Λ��ߌ�6�)Fމ�w�ZeXɇV�Zc��ܷOb��/ڥ���^���6����lv��ӎ��~ͮ�7ro���.8�����Eg�)0�uyh>�ۚ��&�矱1q>' B�Q_fUٍ�:�#84��ρ�4ĝm�mv��ۿ���1j1��2<����ԉ|��E�}hk��sk�#���Å��҃(��B2w��G��,]K�e�lT�c�o���!sO�Ū��B]A� ��gj��+�$V �j���?�CSsx��]L�G�Ԇ{,��/�Fe,l��u/���ݵ.��.$w7�\���6]�:���!�S}���s��ۧ�ڷ������_t�!��o�%:�8�1�tX4tOq�]��@H�o�i=�U��7(����So�p��9Z[��[h�=Z0��G\���_�uܠ�O���? U�g!3��\q��-f��T1u\q�c����]���"���{���D \����7o� m��S���5"�~�{�m����SR*/oD��hVP�)~j���**�$��G.��C=Sզ��=%���n;f\�A|�b�f��st��]D�dr�?�Xեv�T� �x�~2i���J]�D��1]���N�т�n��a�����s]�t��&��Z���|�8�Q)b� b�0��}��縚�@����I�?I�ɒ��(��,�Yw����K[�E��w��]����=H�� ��Q��=�/Kf�ZT�ǐ��q'�����;$g���Y��мdV�,ťWa��5Q|t�/G���$cÉ� ����S�-n�Dk��~_�i>��u4��O+���$']&�ڍ�Skٜ�]ij�#�r�LbN�A"��K�2�t������d4i��b�d����mSiGs���jƚQ]�(�;����"���K����}����Iaq�� Y�_��Jk��(��4~�Ƅ�d?pI; ����������xKY5�4�,H�7��σ���R�hhCd��m;�ѪN����:\��8m��Q�N�{c;j��>���=�)�F)�;l��;Pf)���A�Ū��]�O���&۷�F�a�tjs��QJ#��)�d�`"����pJ�pk�4��5�8��87��c�n}�N +6���-�� �f?���Ν?�Ʋ��i�N�rr��l�zCI=3d�����>6V�\X��O=�L��J �2�0ep۷���&���3��H�>�K�å��7}�C��&�}�gU��H��a9��I#d� _1� p�7�����V�k"x�Xa�"���S#���⩚��na����e3w0� �u-e��S�:���Q5cl/��Re+.����cd�0���v@T[����!�{� ; ����֚��e��W|Vs���fU��_Ѹ�P-�&9�G���F�>�m�Im�;n���b���뿌?�d���(j���Ѿ䙠k���jE:��B�F�,�ڌNz���'Lj�/�]___�Z��`u�⬛�S������亷���?�t��?>��a/� ۇ9w�ıw�-��*����]�|M�R�Gȵ�!�����ه�]�'w#�n���^��� Y� �y�9���e�wZ�X*�e� +�$,?=�@c��;U��)�������G�}����Xmo�D��_1�*%�B����!�"Zt�q:!Tm�c{�f�쮓�P�;�}q��\��RǞ��g^����UY�&��=8�!�V� %T�D�O�;fq��.���x�Ge1���b�DX��m�A�ѵʘZ�`��B�24���6��&�fĪvڀ �q���1�ћ���_̯!��g�=�`+\ ���,��{f���n��K�{�������%�����#i����~(\��2BO�ZՔ|�Z�Y�r� ���fB�7h|#Th��Rb-0��k�|IYR=��z�ۊ�G2E-�lb}L&�v�@�X1��W���y�>W}=8���������5�R���+� 2�l�%�`R| +]�sЫ?����?�R V,%��'_ۜ��eΤ\1�h�@�U.�ڄh UHtZ]�cX(��RC���V� +)�d�&J� +L+�-�u�(�aB�sR3��2�ΐ��I N���M�9:^ν��!���DJ��J�'F�1"�p�ᕿ�D +�P�+)8X�y���f�Y��+�&��a�>�Z�X��tz&�, a� ���.A���c�}�L��`x��=�����㎑�����й��s�1�j��%���y�5I��_S���Td��^`[ +^6���Pz2�{�@*�Lw���Ң�G��Vƞ�rk����Ⱥ�*�nH�q0���P9� 4���@��}�G_~��N"z-o'�<��M��imњJ���w��eЀPM�8�b�3̅B Z�� +1SS��Tw���1�TJ��Ro�^ؚ���׵�`��֍��Ѿص���w$�H �9h�t�ϋxDOܗWD�6�=}�, �a����0c�n����=�>\^�3��n���滜bml��MԚ�&F�����J�d\5�Ɩ�����e~k���֕V��A�C��ur��(�O�Dȇ�b|Oz��Ԣ��z,?��:'�ha��lK41V �P@��#6{���ȯ.����H��V���ɚ�l:�� 31Ǵu���*�F�I�]�Fl�C8K�f\F��Eo�,��c��y�Z�O4�B}�r�}� [C�-xB����C�é�3Fǰ���$7s����w�$��{f�Xl�t u���J��i���J������8�^=����}?�빹Ci�;� �K���V�ޖ��͸���;�;�>8_D7�\m�N�^�:E� ƣ�{:ƺ~� �Bm�ټm��#)��X~����F���4��Gb"�C"�W�J�zx1��%`�U����E�|�4�_@j�RY�Ԫ�����v�.��}>n�y��� +i�gI#�zrM��tF��I�|2G��,�BZd�բk����������[�ؼ��� �‡�o�.�f9$Aa-���t���T��k溕���Cm�<�AZq�1Mo#�ð�W�3(��V�{o�z�T]��6|ׯ�pg�V�Ǧh�\Ψ��NN�<��J�E�$u�K��^��l� +��/�ŝ���,��:W��E�6� Қ(ذi;}��E�[kn/"[S�,4%�E2�#K2���ص�#Զ������� �^כ F��5��֣��(��a��C!ZOԓ�a�D�~��W�whXg��pđ�ȱC�8`����Jq���`�X�g! �^���uϞ�.Ž�|�ح�}j���b‘x�l�x����E��0���|H-�^��u�2�j:��y�ѽx��C�3;�I��`i{�Y��Sw�o��/�=$� r+��eD����we9��Jd�+��r��X��m�o_�^M�OFS�������i�� Z�ɿlS�� FϑM�L�0G�Ҥ��f��R` Dv�j]����~]W������O{|^�߯��������j_��5v��_�j�a �ؑ=9�����I�"M����ɥ�Hr�Z�v-�����n8�=�dl�0 +�{�9Ri���j��;'�C�J[�_�_������_�����w�݇���w�[�w_�[���q�Ɂ�:'"��<��J�܃der�w����2U�*Be�ŕ�ȵ��XeJ4�r���}�mޑw��_�J�)g��[�_�Kp���ϋ��F�LNj�5�� A����()����T�a� 9�T3�A^��BmK��p� /��^r�M�R8��PF�k%���&OalD�*~$3��:���w�r��M/��G��i���ذ�\g}1��C$Y|G����$y�K��Uю�6|�W bW+�c�M�\Ψ��.NN�<kj-��H�\��-��iɧ�����m�;�ٙ]����.�g� 3,u�P�i�M���zG�et�5 m���gHe�b���T�p�jFa�r �X�Δ$�L�r�Δ�aM��h�?�����ќA�gn�H�s�_o���;�u��KNu\⠥��:�`�W����WSm�ַ�H,�\�/�Ne���؃aj���6J)��p����z)#�}3��[�5x5��ԩ���jz��[:�XA� ��� ����k4���A���9����(I�ݏ���7 +�E�y~8��έ��Aa�au{�.�5ٗ|4 ��t�s���\�UJBC��_�)ٯ ^�6�u�C�&=�l �ÿXJ]- +��+�[���V۟7�����_�����{�n��W��f]`��b�9V��Z��k�ك��"�����r���C�J�Rp��^+4d��*Feا�p�[��dJ4�Ւ"b酶y�Ƿ����)=x���ԉ���yz>d��[�\�2��a��1a���}�e��h�!5������5i�#:�O��ctS*"P�$��f�;+I*���S��I��?[�gO/�Q�WIb|���*k��N�e�; {�e�����T�8cA��ڞe���$�ڐ�G`���v�i*K!$<��e�m9@���u�F+�;��*���Lr2�ɾUq��������s�Es@�'Ǥ�#�()��s�nJ~��[!o}ByO�I�:S���Տ\N�%\��]�]�Ƒ8��ERע ��y��+*e��H�[G�����s�2=;qY��M�w��u��V]o�6}��83 +�\;�Ӑ,i�|��R'��u�KAIW��H*�7���$�n��zH���{��oo�4�;8�5�PI˸�2�M �o���@�]2� +f̰op�C��"X�/�s����m�4�Z2b�+��xv�G!#�P����Ȕ�"jVi��#X��2�� ��w?��O.�s��#n*;�Pr�¦ܠT���`Q�]h&�e�t�q���#Wa���Ij�JIڤ<sW��I�T���Va������k0���q%�г�7�֧������RY�V�A_B�-�D��\p&Co]W��1>�NT���@����lM�Z���FeY��w�t2j*�L.������ã��A +2��)���,�Y ���?O���K��[.���6M �����I��� J�y���&�.ލg��'���s|�ߏ���� �����^N��� ��O?9��'���۔4�K�]J�;<)Z�&�+5K&���<�`2)XBH�i/��tƍ#ր��g���2�t��F#os>:WNF����?F#VX���,��Q���g�{����Ѷ�^��CQi %[�41�0uU�),P��%qi,�"��…�r%+uN,�* ʔ<�V�]M���;�9��pFP�HP�??�8w���('�%�$RUnFq}��ᇀ���z�I�Ck�u�����}����,��)���)����F B�4�Ȓƫ:�dS������9�"���ML�E��ڡȬ?�����zعA�� �����G�%�t��_�Č�n҉re  1N�D�#Cܢ���R�Eh=������V&��;#�?/�Cą }G}���k����77�{Ê +�m�j���Ym��}rҰvr���������e��1Oخ�˔��c"Z���)� Sl��_�X����i��20!N6�|mn�>���AR��ye|ﯜny4���Asb��"��/Q� +a�C�T�rW����_u\���M�HY��&��.�{s�ک���|O���� �̉�����0��S ���m7�S0����ѯ���q�)T��;4T���UMB��O����FjW�>�6;݃ ����vY~EdB���[� ֯�J7����eh� ��:��41&�l�Ӭ�WaSǔe���Z�L�5�����aJ�ٱ� 4���2����Yi�9�gK��۵|�� ��lܕ\�����CO�k�<��k� (dn� ��2k�1��H�=1.ܫ��-V��w9�f�!��^��Q���r�L8< �;�j������ab�r�'+�����ڗ����6؜aa�DU��Oz�(�DC���ӓV3�sEh�T6���򍃒 fr�`�����i����!�9g>���|��|)�Q�o��F����ۡ����A�z='������dR%�� ~�Q��=��:OE�m�֠4��ԏ������~�w��� S..M�� 7~���[�B�ď��Kp(B }h���r����8�"���]��BzppRFȓ�ƙyy����S� 5y�߃]�5�31H���Ki� �z=������{��<��8�u��ڀJ����w��i����x�%%$>#�����6y�$�n=ۄ��yv/�μ��/r��d�RV1�m�_"4…�6����1����>�+{Rz���~٥����I�0]jnX��n��s��;{Q�2}�ӮqR\|р̿q����F�RA�\�[�m�m?lK�~�9K܋������#�:攬�Mi��p�&��uy���ʙ6��M��hm��u�)�, �GڍSh��Yw9�L�J���k�M��Y6� FmW�=�uC(�;�c5,���F(���5Y�d���&�t�?͞�g+�2¶d�oL��Yw����e��&�nH�6dz�'��~@��~]%���y��F��e�� |�n5ێ덭��e�5}'�P*B?�灋��֨�q���?V䣕lWS�W�����]#�[�[��i�?�Wmo�6��_q ��)ȧ��ӺyA��N�k��0����J�I�����%�v^�.�&�`�^�{�;R���Ӽ���`�'��0�s0)��EM���)���!�W�1)�8j��Y_�����t����`,�@Ep. �aR@g0>�B!�)�ZK�Tex�f�� +x�p��2Fc"�~t9��A¸���.�(�3)��iXH�����ȁ�D��%b �Q�n$�b�Ԁ\R:ey0�P��U2�t\�5���P�}1��wR�B> +�cRg��Ww�=g���4PhZy��(7�D2�9C9k���|�N��R蠀L���' + 5&���"@�o �<��Ó�����(8�&'�A�_S�l ��E8��?G��� X(f���[k]I�IҪdU�L�m��1�3�p�o��x> '�.?N����j0� ��py'����dx9��9 F_��o���>3))��\YR������*�ϒ�)b ����8'��kR�3rRӖX (b�,c�IJ[�-lU���1�n]ٞr?\{}�!F�I���[����l=���U���e�R�� \j+��L�Z��N���8HLh��Sl=�p"�\��CJb�������gȄKҥ^���8j��p��'�,�.�E�--�x����쳍����-O|P�{s� +Ʈd�3~V�/����5�ݛ��Z[�(BC��q#T����8ɜ�n�p`spI�A�g$��7�i$�6��L��v���Ϯ5��!fʾu`:=^�M�Ѕ�a��f��&���ʏL�7�>hW���O��xjCt������+2��+�mËy�x ��KkVX��:T�nt�Vw;G���G�ю+�ťx��M������[��o��B{W��W�Q��c��ceA����&"����t�ج�����T�?w�<��O�ĕO�C��L9b���q��d��ݻQ���꫉nG�0U�Ü�I-��/�I7L �Eh]�ܟ���UI^#�����W������1]�X����ոJJ�� +W��՛4�I�"�R�x{԰[��n�k�>,�N�(��k��r���w�z��������mk��oN�k����/95K��JU�vgS�؆$�v��ZR��7j�����}�K�R��� +�3m,��j�����<��D�auxW����Sb���,��0X �F{���$���5@�p )j��wZ� ��z�}w棆k�E9�+g�Ľ�W)04>�Q�SkQ�� ;�^����� �{"�n4E~��L"b����/s�� uu�B�I���Cc��[���}���A� +R[������ ���>,��9�T�b�:]�ӱ��.�;P�l��"�n͂��9�."��㪄˦� +,$Jf�}m_���m/��G�~��'�}�� ��˗�:�j�xW�vz��*�<|Hկk9�W_�RV>����!|�� ���q���V�+%'9f�����6�5� +,���������%�ߠߨu���&N�5�-�u��^���Qm �^���zH�[GN#���m��q�_�Xmo�6��_q˂!R���eN�Ig�u��m�E@K'��Lj$G+�߇#)Y����M������y���y�d���a ᜧ��q�E &A����L�Ef���i�i����(4�`����X� �ddfL!��\���?:oC.BT ��T0��T|�� u�� +q����Ѫ^��g��ʇ\;9 a�M&�fR�@$�0�d���E$����L��` �B�81 g�Nx��+���0�).� �̽+5�}0��*M.?�<��X�=���>��SV��r�s�wf��@N��3Xi�]e���+� �udT��x�c�����l�a����n�a����l8:{��ԋ�)j +�̹�&�,Ky�&)B�f�������7\�G$�K +�A���< � �f��`0ڃ���`tW����pտ��ǃ�\\�����`<�����Ï$��`�����]�� ��S<1���<qţ�3 x�H��s#���ͅ Քkm���R>��RJ��oe���XpC�(�����.ˍ�Qw�v������ľv[en�y��,vY����[�Ia�m�sw[l��b��5�b��J���� *f�j}i؃�w��QD%$2 md3%3T������/n�F��L"�L��m��k�ϔ4��皎[+lk�����*fr��Sn+�c��9 !Rr +Lx1w*J�#���c�K% U��bJ%*`iZ@ƴ+<1�EQ��W��k�� S4�� ���ʴ�Dž�X���x�D�Y��;�b(���g� �b���� � +�$0�"I +�;� +SbTt�����7Hni��g��j�� �8�D�sSl�~�/�FA���z(5V�z�䓔�"��]_WPx,+�=o� �g���ER! 8�of��z'��5�.�ƨ\,�l�N öZ��"O�m-��R�J�m�;���b�J~�S����jX.�>4X�/���� ���]�x+o-gL�)Y��㞝 �;�]� ��y�|N.�H#���z�����dx��v�v����Р�Ƞ��E�t��E��Mu�{�?%�%��G��^��woPX��{|�z=W&~�i�C�R�[.��P��7�du��o=?i� �T]o�0}ϯ8�xت�L{t[+"�-Ӟ&7�M����v�����$� B�%J|Ϲ���y�+%�I� \r%�`ɲ���5?�2g���B��$-p*ʹ�+B�����P�,�c%�?�heAJ�G+�F�>��M�A�3B���!�l dD�~�Z�'sl���m��� +�b�N�l��( +��E �[e� � ���Y���pY9�N���X{+�bc{�1�Sةv����P�)>����Q|�}W��p�w�:���T��v�]Nځ%r�蚅�zp�#G \ $j��@m�A��Q@�~�$]��"荕)��ar��̗���Q|8@.dM��З� �� ��9��P���/�)��%:Îe9�h;���&=�l��I���C{� i���Y�fS\����5.g���:�gX��d�4J`�֦ �xGC9��*tuo8/�F��W#��/%��dl �: ���J٫�k��"c}�/G�wEp:�'�����{P�Am�h+�r� +�.+ɨD����|�@�ʓJ�޿�:� +�WI�4�C�#m�$V�|�^��қ/G��g%�Z0�O͆2X�V�d�+I �������1�X�C�m��IzhYL��� Z�N�)L�x7N���L��e|{;�-�7)�o�z>{?]L��Ͼz�?���C v�me|��~R������Jǒ�H�HTy�9A�7d�LTdJ��X �2�\� ����ImqF�V(�<���p����$X;��r�'I0E��>[�R_Γ^��ԙZ8h +d�f�M��BKIµt{c�e��Lh��=��`�G�I/l��� m�lgn�����B�w&T�u^�!���o7h����W���% �U��,��d�c^��Pu����!X���B��Guڡl�j�;yx�큅�_��%��x�Z+:gAQ�\��^aŎQ�e(}�B���^>4��+��~��s=VĺV"�o�1��C ���8\�Ű���%���N�F}q������);0w1�>{��'ݾ%W[��:���]̶}1Vx��i��4�w���4��6�d�_���=����Ym�W�m�Y�TX���U�zpq���֡#(�.�{ݼn+m\���� �e{a����I�AG��h�@��PKr�Π)H���*46>�>|� =^^���lеlw�^���#!��Y��!�6bd�Pq]��G��y�}�x�m�; ��?�r;��(���7W��V�o�6����m�V֏��M�X`��. +�L�%"��To��>��3��-��ݻ��_?�Yي��t��L +�\p�����5ͨ����W\�?�-pQ7��Д��.bX"�b�25*�kY� ������HH�d���B*���2RA�3��� at &r�'���r+����kG ��d`2����VR& �И+� +W� T��J,M&˵�if@ւ��x��[*�u(F���HX˪��úiF~'�-���sh���4wO:]t�k�@�i��Qi� `�(s������p�$�K+��r�{ �4BdƔ����>�z�R�Q`݌/G�x��}�� �&r��]qE ,׀e�s�˜ ����dr�s�↋�g�u���Hۖ���; �S�d�8>�/�x���x�u�m߇��p2�b���r:����I �kN�l�o��U����c�, ���~R��P��J��.��g��H+L R�@�MDI��� +�E9/�q��6�[���%�{��Β����#��22%a0�"w+���j��t�V��ب��3�2Hh�i@P���p��Pz�b9j���z4$��_O�����~�{ �ԡ�H��^i��.eQJa��~�$r�L +m`~w;Z�'��l2���l��+^�UҮ4��I�5أ?6ؿc�]sj֥�)(������\h�6�v���p=�!��T`gJ�{%��2�̃=]G�v��UΤX񴲓��PK�t�&�8k�\b����)�觱.m��٬��/��vߊ�%q{גp��|Znl*wt�J9�Q�¢!�!����� �TCV�`X�@��{���D�q)�@�ϩ�ػ ׉A����M+�j�C�e�<mF�R"(���f:���� ���U���_ /:0Ϸ�KR��͚{JB��}� :\<`^Q�q��dڠ!(�>�L4��ʸ���jᯅ4;��HI�BC�L����;.��x>O��fs��M�'��l�������������5*��R� H�'����Z�;�K���g �,*V �JZ���6\��j`2�7�8Qik�,��J>���OAHS��<��P�Ұb4r_Y%i�T-��%�~�8+y��@����۽��Y�܍�"'y��?���!�[��?��<�|�?VEe�;��g @7��=�D�"| �,�ͱ�ڠ���/������C�����v��{b�EHZ�0�bWb[t�R�+���3A�xI*��[�Rf�)&� ��ʘ�M��C�Hki��m0�T��2 � +�*��!��AtsM� i7�CO$���lR� |D�֧:Qsԕ81(IiU�*�0B����򍮯��Q���{ _�Ч���K�9ʬ��c�=B��c&*;9^"�q���EݱB;�eq�D�U�1�5�Uk���Xp�} +�JR�,D������ �@�}�׸b��,�yVD^#�Z��M��3�]��y m�5�xJ�QUf�#VU�w�r�Щn�T�ֽ�/����nV�X�'��7����{�-t8_���I�(�Mm�����(���$��/Y?���{=������t�'�/�᎕e�*����M�C�98墳 �9���K� .e�b(�r�|.���.݌�, �=���0���XKo#7��W� +>HY +r���8�+�D,��faP���-�C�-+��E���`O��<��bU}_=�.�m6��{7�w���q��������2şP�7��rQʴ��J}� +�1i%�2m�21{�>�\��p)`t��8�\Ĩ@ +$i�`'�;V�un���i�Q�;FO��V��n5�����V>���a {n�`�\�^�GH�ǜ�f)p�H������ S1����fk@�*���`E�,?c�S�52��T�����T�\�~���� + ��p��J���4�k,�>G��"��R�Dd��w�S�_��&ȀYW@&�m�� +`kLv6����)��N��̂��O���������w^�HQkP�{�ư>˲�Gl�"�lO�Y�,�\�^q��fB�:P� +R�` ׵ R� ��0_�ǫ�|9�_櫿�}^�/W��W���v w�p}�����w�%�}��ů$����f���s�� ��S<1��)�@\�(� #��R&69� l�*����&bH��K)M�-�B�|�X�H�|.��L�ć'ϓٌ�FnP����RH���v��V%L�\YF�!�iM8�أ O�QA���B���P����8+I�����<�:K�A�����#�+ 5��e"v�!B���R{L1"e!���kܲ'.s�8^9_C� ���ІdB�_?�\w��8)��<�����vh�2�6Ѱ�+4�s��ӫ?�$����L{5�H^�H~��� +M�a\�-��E8�Ic�LK��kDJ_�"9�>�T2�Q:��bn��ͨ�� + o,�9��� �����I�c�!bi�^��&�k�Y��nS�ڕ)_=^�C%MEX�(�6hz�ē��������!l��1�G�d�4i\;���7a��d? m?��~D*�����j;b3����`�m���7�$���� ��Z@3�&B8��� +*/��yz�r�k���-F�6�*9\{b<��B_�T:M57js��Z�WR#̩��{�ru3_x��^��8�9NТ7w� ��(L.n� ���k� �^���obXr.� +Y�-�^��bF�Ӌ �[?(���4�<�&�Fz��2=�).L2��B�K�^�;��(wIk�~�C�WkD�sgR� �ȸ�~E�i��#��L$� k����oR:r�Oz�|���$��yV�g��_8�Q��QG���J�*�����?qT�5>�w�V�C����T +д�@,�Z�uz(����T�X�B�eb�v,.�qNY1窬3��w +W��cf�wQ}EQxJE�N�żc#��zDxĬ�P�:���MpNj<}]�_�(>,E��AY�~d��B��[�殊U�@��ھ%���Πm�׵Â���)(,�F}7�|�ͫ���K(� ����)k�tv}{�|x��>smtC�����?-�ݼ�gM翺jVm�8 +Z˭W�Boa�G�o�� :� +9!uq?������+L��x�z��M�!�ZM��S7���T��b��(5�HSݢ��&m����;���d�4��$g��j�`�<4�[���=~l�d{��mH/��%�;��WS�p��.-��pI�}*�X=�UsȰcm����w,^�N �~/�/k���}���O�_L���������Bc��Դ������D#\-B���i��kzJq�k�����5j;H�_�[k�k��?�[F������b��YYo9~��� X +di0�Nl�c;Xa3r�R��Au��\���mY��/�G�j�� �i��G�u�꫋���!��0.�X�I���R +-S�w��p)�8K� +��i=:K��G(4�`����X� ���l�B� s3������r�)�����TN���H��l��(��-���|ry K�Z��kG�1l�I�$\�F�XJ,�9�f)p��jm!B�+�b:j$���Ā�T:��`NG�}�h�8�5�2�G���cw�4���O�7�%����୥^�-i �Xr|�03�Dr�����R��2F_=� ��G��nf��c���x�ٌ��w$�jN8�8���ή���I>����\a �-�,Ky�)B�6�?�&�~.`���b5$j PuRi�� ׵ R��]�`2��/��l_&��|�×��ۋ�|r=��[���^M擛� n>���+Q�k2�r��|�B*�dO�+h +:V��t�_�R&V9[!��#*�5��X LĐ�57R�H[g 1�>c����4�2��=N�c��Ba�j<�K! ^ﵗPhY��̕E�� 5�ɏL?M�B���>�����.s��F�PY�)w�D�kT�J�vg��\+.�܌G� �k)�-��A� [C�zW.�6Lڒ)!��=��$������'�����hq��_!��(���G.s�"�<(��K.|�B +K�o�S.:��Mf���`�Hp4Jf��v�8�Y��� ���d�.�7ֵp�\�`����?�0�:K�6������A���X�� _�����8 Qz +�*�yj�˰���)X�'������)i0��r��޶�~r��w�dJ�mˬ �S�����~����5��:��D!nJz�&W%o-_�<�e."���H +mT�~��C8�&�%0�ҾH:�Du|�pX�m�(��,O=�s �=��pR�:��-��Z�h�˪�CZ���#Ky�c�0�d�X��i`�Boa�(@a���K�$��<�rn����SYg�-�u$y.�LwH�0]*�"I4��\�0�B�J��6cz6pXPxe���)�5l����/k'�����Ldn�H[ ��»Mv �.}�g��������h^��2_B��"w �]�~z��y�%G�e�z~1��"�ʕ]�<�)$okg�a��,���� ��(���:+�,7z��7�$�!pT���F��&@8'>�<�z��Չ�5��Q�Q������Oi�늜J���F�_�XH�'4�H������j2�G~w9��xv|�2@�aw T���C��~����kP�W��3�OF%��ȍ��+�Tu�zЃQk��23R�>���V�^0"��ʸK�bK%JC(�$zɟ����� ��*�����ʹﻘ�?�_�0����Q���`�U5��@QY�`Τ�|�n�� `����)D +԰f1���1+��\�!WiBGp�4v�g�� ���c�/�@m���K1����vmym��܉ug�״ \e��jڢ �9I�T�C�$�����Ѱ�*䑽79�ղU����LbйvЕQ?��2J���u{�(�_��@2��~ �l�&�E�������C��t\��P 4S�F�?����Њ:7���l�(�(i�JH�{�>Vh� Mv�������'��n C��ܳu7�Q�$�����]SFmVy�i�c�E +}�.������\A�\H�ҏ9���I�������Z��j�Q��p'u����J���W�T�`:l� [��&J`7�ۘ��� >ie2����yA.�^T�b��*J�V��Vj �D��Mչ�\߻.�u���D{o� �X=�U��p�ڠ[d��Ef��z;�T�tx�[o +���]v�����'��G���Qg��ׅ��i;�/��� +�*5�5 ��-\ӈ��Ԗ⪨��{��nW�m �w�]���� ��!�������࿽Y�o7���bN +�P���Sױqƥr`) � 0��ъ���#��uA��� �}i�G���DY���2�|S�����hGp!s�D+'��*�B��&gZY����wR�:{�KQ���e*�6�����X�{������,�� +a��n# ….U*�� +N��P� h�ĭ ���fy[���K��5*g�sD?�Z\���R�̟J��0��t+p+ia��,����T��Zj�fC��`&LJn'�����BcW�,ȕ�E4�z�Q�Ӱ�ep��u�>�����_����i�O�{-�����b-�!�T��u�K���U:�!��R�]�l��p�MV�Ǔ�f� 6x�M6�.N�_�����?�:�%�|T9Z �SJ�)�nAE.q�#�bC�DqH#�Tو�m,�f��E�mh�s4<���|���/�#�|�����|>��>�-.��pu gW�w��˫��.�t�q��r�n(� + �Ca� m@RD1m�S���%���ȥL *+E���{4�#����Z B��˵t\T�Xw|�]��� +ݵ�:g����R&Q:��r"�L�Ul����:d�TbV��kj2�PZ��x�9����#�X`�Q��Q����hG��R*L�4(.�0�=Mr +� 4n;�v3h�܍��{��b#�ҢQ�$+L�0��B�/�pkA/�)�*a�؎�슡M��[�y��j0,��9Z�7x� �wL�h\�Go"��z�A�#�u�c�� +m��ͷ �� �vv5�t~��9"�r�G��#X��e�4�-CDI� ��B"ju��Nb�MUZLDZ�_P�\~�'$ȇ�N�z�Cy�٨�U��i��2�C/��Wu���p�m@vp���>��I���o�� �L��0�aB�������`}Ƽ���u�8�4!ā�hT�� �:��@`1_�3��O]�� +�) +7O�.B�a�������d�ˆ5:4�CN��,_��2kh2_� �-!��K��^��rd�ʑ��`*� �8��I���؀�Xe��C>�0-�\l +m�z��I�"JG���w��� +tᗣ|[��G��9��A6���ㅤ�䵡Z��`�-.i ��2�n�S֬�%P��[�*��_/�:`� �G�٧m�fЕF���i����\&�,U�{�� ��)w��δ��֩ﮖ5Ӟ��C���j�W4X~~] �V���t ����Cװ���gO�}���96�юU���7Z�&�^����Tݵ��)��O(� �� �{Lc�u�y�u�r��G����R��O� �Ew��֗��� �� +X�[1)�i��A���S��n�E�h4 +G�F���\/"���{�\ %�2n��?K�P��/�P��N�0�O?u{�S�~��-���6[j>m�_v#]����6�'�7�v��rܢj�a�q��ӆ�'O����q�eQ�L&!R[d��^=A�I't��}�0 ��fn�����Vt|�t������ZS���+ �Cjo�$2�+S� �62�i�%+mQQ15Gn5P��nU��O�����]3����J������P���˷6���C�#�VaRh���鸎�w�`��h޿�k�}�="�r>�<�x{��z�!8 ���B �+�a"����� +��A�6p}�E�CK�����p+�7|�MX�vδ�{�s�ⓑ���0�|�[���U� �$��2S����x,�<����0<8�3ɗ���]8���OOEj��'k�n���Ek��׾8����v�@}ܯ&�l��@��D�|�ҫ�m�FѪI]�m�*����)�Q7��2�GoO5�&���$�;�B��xh��ΐ��m��W�����}�� ٔaw0��&~n ��� ^��{l� �˴D���,Qs4��5�k�=�q��`����Y��������d��������2ܷMxj�Б��'g��m�������nKKW�#��?I��ԁ/��8���]���7� ����� n=r�o^��X�o���_q��*�(T��\��VQ#�5$QUUְ{�^f�3�`��:�`_��4�HÞ�����^��3������ !��0.�X�Y#���Zf����R��,��k�e���,� 3RA�1�����������r��25;�nd!F�{5��A!T �T���Y��� �l�7(�����l1�C�3˟p��0�7k0k�a'�=�RKN�Y\�Rm�!Ĩp�TB��2�+�Z�;�J�y>X�+�`�v��Z#a/ �J�k�>|D���?B׬-S�F���{�� ��Bc)�!���M�q&b���;����%e�udZ%f���kc���p�� �5x �j\��\����? ~ <D�Z�¿ +�0��X�g䊜� +8E� +�� ��'�c�SC�Ī`+��ܢ�����pM���D�pcA����[��79��I�/�����x��HYa� +�a���> +���i;�C+� �e�,�����M�ܺ�� ����e��B]dƶXz�u}��o2.�Ov�G��?16��%���`ŷ(��� ��S.0!��� ����Q2Ge���b{x�����P>+c��ޡrF��6�x�`ʊ��%�u?:�|\2Ax��e$]�����M?�ow��t ]��R=[»٧���0~x�~|;��r@z�X��t��$ہ���!�,~�u�]����)|��2�r���\I�1����`�H���xT�<��W���/i|�#�}� +D�e����� Dc�����{/[�]+�!p��@b�R�� +���}حy���m�N��I�%�L��WhTe� +_K�~�t+E��$��Ж̓\�-O��!�Z�e��>$�+� ZG� d��m�� g�mР�� ���� p��N����Dj��C�EV�P,1��G��7 Z�Vdi��w/�^��jè�p[Aض��n���U�뚀z��G3H=���hRG|(y�B �J��+�Xf<���ڻ;˫��t۾�K;G��j&�����χ2|A1��U%B����M�Ra��"��P1�>�j��Cw�Zm҅9ZVo�������ۈ���ϴ���b�[ �h�k)eF�Y���;���7�u�4x \ܹN�G�{*� R�[Ȃ��ܧ���Q���f��vXKw0��JzCպ�P���!���8�r�ϋ����,����O���P>�FE���M�7r����"*����5�x�O︉פ�U6�d�Lc���qY��X��2��sT�_>%Ӯ;�efrwF�����֌�9D�qH�@!�]Pةr��ª�2kf`dz��V�u��}y@�r02H�"�1��5%���IcǩƂ �e��o�:`� ���5��rk�s4�&eao+|&,u ��fH�0-�4��iN����]���N�e��串���G� ��YS�栫WNx-��#S�p&>���Y��Y�i'�?=�����pf��pTnND䈣G�4�#rdO�"Jw�� �3n�J/�>�'w����&<5z�lX�œ��N�m�����r7&��5�v}�t{��t9N������ى�m�����w����m5�[���N������X�oG��_1/���(��h'�c�EM!5$QUU�r7[���=cR��fv��l�EJl���~��~z[,����'p-s�D+'��*�D��ɥVV��[��I��K���WQRe�E.Sᴁ$��[��>����f-�H�S�pka�u�RA���bz݅R�h@+$im`��w��yI�s�DfW���LY�x2]^�B�,�J��0��tKpKia��,����L��Zh�bGH�`&LJ�'���-�Bc����(��ut�z�ѬӰ�ee+ꐌ|Fc)��o��-Y�N��3�^� (���Xk|H�p $zU�R���Ct��>��A��SAp(���@8��K���`�^�;��&�F�W����?��D�O*Gk��ߥ4��|�(r��y���5U� �� +�F:��I���2�I�.J۸��Q�b +�i~����=�2���i_.nn.Ƴ��&7p9��F��&�p1��$����[�|( � H�(�[x�>ZB�l��\�r��Rd��G�}R�YIK�� T +�\IǠ�$�[�w�H�HU谙�9K��H D�t�ʉl0���^��p�* bV��15hE:��m*�J��ad�e�����r�Ì0{ߔ�q"�y�Ku�$�<������ ��-��=Fs 2!�u ��#XS\H�) �h{�"q�ryǵ{���sL��*�GH5i��D������R2�>s�]-�.иMdX��P �ܠ+O��*�uH��� ć &(�1�٨s�)���J�dIa��&Zݣq5MGA'L�L���q��^�G>�\�;�9g�:� ?%:�s�3>���O�t6h;0<�Φ�k�(�>Q�'?t˼�'�StٓA�ʗ%Q*śhc�ZΏ8�j�Ȅ�k44<�<�+T�*�N�Y�F�����i��7|^�U$W� +��SA��5K��2E��cI�W�JC-#l�B�H�� (f'/q�A��.��m�����JW�S8Jq!��55������r� +�7kf��P�����d���fv;����8��6���a����~�����tn��U�W�h�ke5B��lc?�������ڨ�� �8Xs+�o��:�# ��Jw����Kȗ�i�2��U�d%��hͪ����Ƌ��U � +��Aƻ�|�1\�(��z$�@."A�ս�����֟$�1��'���i�XHOe�0 ������߅����ʬdQ��D�ˇ���eq\�-�\@}8�݆ヒ������#+�7U�'��;���E�ތ �����OOVpq<+�/4�����ǟy�����z��'^iqKC� +�&? ���v���_J�2��� uO� {���E��P���;-Ћ'��ﯱ(j{҂.�e�*ۚ&2S���z{�!����nJw���Fɞ���J�}T3!H{�gža戅�����V��ʻ��Ծk��J�PG=~KG6�5�]=��%��w�5��l�����j�=o� �X]o�}ׯ� +A!���1����@�r���(�B�Vl(rKr%����%w�+Ɏ{S�sf8sΙ!���嫼3|��/�FH�D+�B ��[��K�������Z] �:����?�):m �h��>�O"!e)�}����T/� ��.T� zӛ3(TJ�"���ڔ��(8�,#f�hM����ȇ���Ɨװ���–~��V�������#,�LS�[���ڬ}"�h(C�r��wFd+z��ؕ�3.ez��eอӰ�E(e��F>��\���+蹕w����[��(���TG�O(w $z�K�*�ޡ�j��o!�^0�����}3@���`�\�f8�n�� �Ɇ���O�������׃W��%�Z0�W! ���� .$��-3�� +�F8��>{�(�}�j�b��6 ��u/�0�v����xڇ_dzn���ww���z +�wpy;��Ʒ�)�����7��q<�� �"t�.B�(�{z�9�ZO6�D,EUV`F�� �%9���L�T)H�΋ʲ�Am�K��|�P��fZK��~�2b�tF�a6����O�c:�!YbV�kj؉��78l�g4N �o͂G�A/���q��{��Ŏ#��%+F!!�ul(+$V� �Q�π�cX���q;`�� ^��U�D�pÇVr4��$��<�dJ��:KA�6 ��"�t��|-�)�)-����r�ϼ]A ��4�P��*�30d�(� ;%��9�Y��@=�Qw>u:��^��e)�l�K��~�����TG�ܳ`0��s�%��/�*��c'�� ,tat���_/�ZKgor�4��A�� +%BZ +�4�>�'N}خD���3w}IP� �� +e`��1�{� ��8�!� +���`]X ��Ɠ�� +�x�ϖ������H�A���%��V,�,�J��3[���� �hp��1��n�UDc�rmv��~��!W-Ҧ��� , �x���D+�L��^�O��?� +�R��*��������r}�6�Ř��Á�|ʖ��A���,l �NX;?(+���G���3�P���Z�z���R|�Ho�ڮ �XB�8��� 6M;��lZP5*Y��x�&}.&tM�M�t�-ڣ����k��VH �q�R(��Xr_��҃-רD^Ht���]��"�_}������aǧ� s�w )���� ��AۇY +^��8�j.qW?8�>+��Q�JE�î�I|$�cW��� +f�c ܠ����6�e�������L�m��^����ƁO��y8��z'�|]��Ss��.�� ��'�;0�)`�����^�,�g�k�#tf\8!���Z��(�ڧ�{�2z��Iߣ���D;�����4��,��' βí2U[ ��%�s�҆eo>�ȟa��o����\X{��3ۮ�����DZF�@��SX�s�k+U�[���,�3.����X��� ��'A��Q|��n�9��mh|����!���j��Ԩ���ڞv~d�kg����L��k a����_�m��jx� 8~��f���#��t/����h�[��λ����Xms۸��_��7��HN���9��Vӌt�t�ɤ�"W��@ɺ����.�"�z��K9GC�>X�˳ ��&�����q ��F&�VNH%U n�����ZY��2�Nju%E���2�"��p�@�k;-`��2De1��"�a�'n! �T$ +/�7G�� h��� ̴�v9�>� b�8C�l`������wy ��~$����M�M���6�0�DIZZ$ �D�B�ca"�w�ӥ��ԁ^(4v*����2�)���X�iX�,��ʮsg��K[~�9�C7e� ����L,Ai�� +�!�ԁT�Y�H�B��wW������1�o�dU �#m�0u.=�v�EG��m�n������ux��U���Y%h-�-�#/A�i"C1N��r�8����N��MڶH��0UN+L��&��Qp1��0�w�ް ��F��<�_.no/�����p9�_�F�A���$���Wm@�hRC��$y��|*l�l��dS �D��g"F�� I�f&-�ւP$r&'�%��ފ*y�����i����y�)ݮȜ�Q9w���(���mW2$��ՙ�� +.X�m +�ܿBJ+4�`�p�2�]5tȃ����W U�p%�A@��%�3���:F�h��rp@x>��g��J�ILwz:�����q�逰��:\8��D,���S"�j�ƁA�%�eݩ6��A��%�w�krR� '�t����p�˟Fה��G�*�­/-2�ӂ�c�*�T�*�@��������n�n{��9��m��mG��G��^�@�$Z���y?�ƫc����[D�%�0p&�2I`����� �p�e&"��k���fѻ��=���g�u�~�I!��Uo�€0F,k���C�Ƀ�T�s/y�*)�}���k��L�l�ajW�y�^ReI�?5ͽ4��.�<� �Eή�����6,�2��P�+�S����#2�j�9J+OOQ�}�eֱ�/{B�5]�q��T� ��z\zyZ��\F�Jfр�T[+�ɲ4]YڕrF�C �"j:F�*���Z��3 \��a���_�R7�2�`�eTO�l��&� +9�ww�>���07�|C�U&�s~��})s����u�@g�+�͋<6�����D�)�͂C���UU��t%�W�k����\�br,���c�`d<��Wͭ�貐4�4�e= E��F�;XN��x~~A������m\s�_�}쪸<���B�pJ+��q<��j����Ӛ�� ��I�V�u�����'�q�B�(>̗B.���5|�ݸ���0��,�%��jmJ�����.TIγD(�c�ʏw�6C�i������� 8��u~V��9�R�|vE Q�1jT�� əP2�Af{���3�Y�����m������!�㻙p�����>����_|��A7hW�筜���C��Q����-5�шε7Cn����ٖl.�ʿ���>_oJ��eЄ���ظY`� $u��d�~�ܻ�5Wy�鲯!�F,��'50HsTt_�{X1�o$�\����P�tI�&bY�a5d�q�#Iɐγ���[+?ͩhe��Q.��*�����Xe�u.��$��-[0���� �������T�Р�7��/����8�&���ŦF*7�%Ն@*|[�^Gd؋��_�CR�ދ�5����φ`S禿[N�K0("��n8�6�WDq\�� ��佾���]~��&K]���)U����!� ;����5�[����/�������[���� �*e���=�i���|c�ֲ���5�'������ySg7�Qw���o;Д9A����'Em�*>����`~C�P��C� ��ˀf-[�P|��kqГ-����YQ�rRg��e���iii�^w�z�V��!����Ot��� ?��ףF��4�aw��e�^!۶W����:h5 +��=�mC�;�F�ƔD�ؠ���,�۳-��#\(aa9���i[�v���^�f~�;���o&ĺ �8�.�j{�˪�"�[�]��uf��f�6��w=Od����l��-|I|w��;.ofźs����j�y���V�n�8|�W �>�A"}L�4�\��6;)�HKG�ԒTT�ȿ/xD�Υ�]��3�2sĜ~i�f��p����^H-u _�Gvi�3���P�|�ԑE��s����iG9�a̴YEX��w�nL�s��O�7�:' �)��Eml��u덅�!JKT��.�DL?_�f��(�b|.]����|%:cQ ��2� +R�֜HZ*��C��i6V����4YW�&V���͐�뉇��`c�X�^ձGx �Bɟ����Aq�`�ѵ�@��ю�=��Cjd�n�:ct�n#�E��Ap)0��1����oNҴ�Dp‰�e:����.�����O��s�9K��R���i���Z�肂,@jtVz�ˣ�v� �e�5mHQ���`��K̖�c��-��u��sq������t��]/����b~5[��%7�ο�_���H��,�{cC�B��R��!�����k(��̠�.[QJ�D���![K�u:����l*�oj���c���2F1��):%ME�MIڋ2Myk�ߗ��|Ȕ�bδ�=�����RI��'�{�z��u<�}��0��k{L�r8���&_��' ����y��~ ��1�� �qL0H��y�'^Ӡ�^*< %sXr���S�2�O�#7'��439���4�L�Dx�#��$y/���wrs�_����g��a��M�E�d��Q��,}�bg`I�R�x�<�dBc��l�6 + ����Kݴ�55��l2����/���_����8a�o��i&Òom����p��'�-4x%ôU�(�,�N�H=P���" ��������E#���~�5���o}�e��C���)��ip�< R�mڵ�؃�Vg|C�r�/s�0I�DX��>�^���yL�������3ޝ��{���Z�������b�[TI~�G��r�u;���\]��{��HLy(܌�Rx+�� +�D����[_Yӽ��tm���[���OK�B�L��9?�.������������ �+�>Xr8�z�(J�n�L��+ �Q�)�v�3� +M��������� ��h��|��T�n�6��+|� w����κ�֦A�\jV��Q��e7ȿ�J�� ^H��y���oo��/�����cG��aϾE������R��@>��,����:¢@�cK>P�(������h�p'�oLƝ/� �!�xJhQD�ٕwC�;F�i�(e �&��כmu�ž]�7�8j0r�;E��iN�����!��J��&���?+�]���4t�/�m�R��ńc�9m<�0Q9a=5� +���D����c�Ag���ů}0��1z��m{X9􎍷=���c|���.)��@���`bB�t1��e9�����m9S,�U��u�����Ռy���Vj�{��{�����1)���`�Q9�o�:�&8��is��� &kt��Q�g�cYW�>Tۿ6�����_��ժ������j[m�56wX�?&������cG +z�5���Rs⧹��I�Г�=[8�����VI�h��I��8>p̦ + �?n����>�P�PmE\F����P3ͶdI�3zeM�◛J��wY3��=A����|B�Lp��I(�4{z�:ܚ�&��Np�� �}�z���L�3g�-�����"?�'X����>��d-�Dvbqݬ+��7�{�>�����@oؿp�]��ϴ���'ǧW|1C0T�Q��S��m���o1Nuc�'�U���QfBb���7�֚����8�6������AO�5�$�ht����Ey���w�D��T�n�8|�W �M W*��.�h]+l�"�E� +�:���x�$� �� Ғ�٫C�șs�9?���.J��s�UC���PZ� +�&�W�dm���T}K��&t)��Gōp���^R�Ğ��+I�R ǁ%턬 9�� aͽ'Q�q���K��$���lв9�bT�;6h���!�i�ȉ�f�˖+�U�G����je1��Ş DY*Z4PzϦ �x��J�җ.�;U��77�f��rlo��n�e�l�ɱ]#�|��_�ͻH�� �3�6P��T��i���e���H���h��zQ*�#|ґi�����D�Z储���P���?��T��v�M@\ߍJI�;�H'Ix?������Da�D��S 1:��h��=ί ���Q��c�Q��r�����W+M�Rh����.�o��u���;�A�oo�H6�����@��t��t'z�"m�9���# M�Y�\�N�Ř�M��N�>τ��V9��ȱr��K�{����'VC�7w���]�]_4Jb�k�_�H�֙^���L�2@�D�D����;�� �G������_���{���Xf�|�؆+�����\�g�{E� +�ZKl������E�� e%N�� P;�3��|����(��#̱Q5AjvB���"�W��luM{��|\��k��]�Jj��{��+Il����l���>�^�Fw\�t�mf� ���ڠ�f `T�9mP!JC��@F�ow�t��A�_(;�@�\W)�^�k��( +�K���4��*�)�`�ۣQe�{&c+����K�6#;$�:���NR�T�����d���2~���hr�;�� +�F�ڡ���: Ő�ik%X�I�}��tJ�s?<� �p��I�s�E��}�@8֦LF���t��f��/�#��d- ��)C�#D��J��&Ԣ� � +P��(��\x�Mp>�����}�"�h�̐f����l��������WW��>]g�]a�۾M��n�a��r��#�N�o �*2���x�@��Rq槑�w�iN�%�J�\v�$���L؎�L������j� ����6�&��e+�O��Wu@\ޜ��$�s�$N�p>n����W&�0O�q����=C��>zߐ ���n ƅGOƛ�%�X �ܰ���vy���"�*{�/+#'H�����߷�Z���� ������mX�ZX�;W���c�}L�-��0�̱2$Y0�g*u����1ƍ��V�@#�Son.gx&LiqOi�?�(vx���rA�~Ǽ��ϒr�a�hU�Β�m��V��e ����l�餛���,�Lbwq�(|��ƙ ��c���c��S� �Rw<�� ��P�^w�]��3��U]o�6}ׯ808 \�裻��\V�C���S@S���Hʊ���%�Y��C�xϹ��/�����(���p�[! \+DŽ�����ke���)ۚ��w }Z2'�Z N�R�ͬa�"dz�:f��UE`��,��B� +2Њ|%c��w�[\�*�F����}@�l�ZK'vP�3��u#S<�{uG1pߓ赯#X��97sC��ʹf�$]��,kS&���s:_,�św���EI���l���X�H��Z$�|C�B��'T9�h;4�y�NIB���V`�F�Y�4�Y�f|K�_W_r|���͖y�Ȱ��|�����j�au����#K��& �*2���x�@��Rq�OC �[�:ن���Tٲ�P�-�0( �ZX_Z � +HQ ��z�w��������GOՏX�� ��m�)I�Z�KRI�S��5��&�p�D���X?�蘅m׵p�b +BJQ2 �k��?��š�Y�禵>5�7R��W��qs��o6�(�_Q��H��6�s!%�\cn�9�`Pԝ���?�����i�a�+>p�L6�6�F�1��?��x�[&[���`��6��C�>A ��(l�(�oIx6�Z +�M�x��ke�i����0'��@qH�?��KV~ƸU�Jwj��xm;�x��@��7�)^��Ù=�{IUӺ�4��}�.�� ��\ ���gm�=�>�o 3��t�B�ȧ�����C̊մ��@������W3~>ƱO�n q[��|m�������8���>�G���ߝT�n�8|�W �>$F*}L�6>�F� l r��S���Ҷ�#�(�C��@Zj|8�-N�D@����������l>O0�Z�2ړh�|����hg�jL���WO�;/F��;Q���&���Q���2֦�%.���.��hhc�{�me�{cќ2�*�ܲ�. +�~�����D|)�������د8 *K ���胱ml$-Wd��U��h��=̠ٺZ��*�zjƝOe����#�3��a\��.P~��…�#h6��.�DtKGh��;~��' �P��!�"zd��F +|��}� ����@~R +��﮳l��bé�U6Q����jS�^�N_M�ݰs��g/�K쏠�kDѾa44�P��1X񢫫�v� �ez>��Eq� +05�- +�� �-�����|�a�������b��W��Xn7��.�n +l�Xl>����� +,�f ~�l a,$�(�g~�znur+9�BC��bT�m��m+.H�@�D#��h*����$l�t���T�H�i"��qtJ�Q�M�:���i +~]��0�6��}/�;�H'VF;o{�/.cĩ�K����>���a���:���M|K�%��%���Ao�0 ���9�Af=n�Z7M6c������*�'�u�a�}�b�)6 ��X�H>�Q�/ڦ���4�K�%k'����k������h�\R�$k�JXGЍ,I[��8i+ʆP�������J�$-���tE���l�cs�j�sl�!jC�#�l D!|�Zg��R����Q�^���=�Gl�@T������[6�P� ��T^e���Ⱥq�^���lc`��˱{<�u�=w��#�C3f�B�z���N\��p:9}��C�Cg�%:(�R��]���e�u�r����7~bA +x{| �y�@�\�6I���E(8fS'���&�/�b��<>�;��Z��IC6{��U�EP�� � +���N�z�i;��xL/MK����aF��@VLp�Y1�}����[�>��M�u�(���|�_g�l�X-�������3�t �sk�6���T�i���e��m��[YB ]w�&��D&�DKf'������ILe=���qK.[Q>�P�2��U .��$��פ�$����g�7F�&Ѹ�I�j�Z���ғ����D֤���I"��Έ��?>+��HW��Jؗ��{�#����Ta��8��_��&ak�~���i™+ ��[��L䱭[[�Ir�{���C���PJ �X�ͼ�f���Vm�,���ʚ!�r$�T%\����Z+�kNՉj��['��QÛ;��'�`�~����N�%Q12]�� c�;���`�ʶ t*g�أ�A��9#����9"�4� +gc c�w�C�ޠ�u��Ҟq�������E��- +m@y.��TC�B�&$⁆K2��X���Ȳrнbc+������m�d�9�x�Ӹ��Pʤꁌ �������o1wU͆���}@7t�:�����aH�����D@�=�߆ ����R���1��*���$��>��p�M��%&_��f�m޼�ߎ���fka��N�q��m-kFM�W0 z#�T�G��S��IS����@A��*C���i���n�ß��ܬ��W�C�ɰ��z�����.�~���G���>_���؀�Z���3���Oc�-�N�e! )P�*;*�>� �Ѳi���Z��Q�F�`*�?����o\�$n}���Z�qu��$�9]�J��>v��k�3FX&�����^A ynq���P�0擁�WұmIp`�:2.�{?�h�ٛ�=y����M�j�n'���%��v�Z +�5�j>_���� +���9f>������o�A�x����1z������Tao�6��_q0412���tX��6&����]�OM=Io�I���dA��@ZJ]l� �K������;�٬��3̱aE�F��u���/�2�E�> ����{�Z +�I6��y����%iO5�I��#T� �p���u-"W�js�^��`4E�q8wn���A�!ZGt$|TD�~�ۗ�5V _�?����C��c0��qu�qi���1��@G�pu�,�}t�vf��|�6�QJ����g�i�`�h�Qʅ�q3n�;9%��_�*t 4�ή�&�Q�M�Ӥ m������{������(�"�0W5A�vBi�K��@?䔵����r�u��P>{���e���T����v�Y2.\' aέ΅��`���� X�G���Ͷ�֭c�z��4D���@F�/��t:C��ϕ��(G�\W)���= +6y�5�.�lB!h�&��%7OF��w���T+O%���m��Z�x⶧�úo�_�XO�8>�h�?�;< �x�f���kvP�0���MS+�e@��^�> ��x� +�� �p�R@�\s�$]��"�)��br�Ng�l��8>0׺&ka�{� �X?A4M��XׄZt^� T���.�m�!ؕ�iC����"h�7ɐf{�8��l��t�iy�����j�X�� �+L���t�.�sL��9]\�A�Ud@���$�@��R�3OC ~Zz�lCRJ��lEI(��LpGCf����B���(��z�ܼM��y#�O��j�\��C?)I"Z�%�$ �� ޮ�߆0�(���O���g�zS��->�*��e�JX��4B\�5l(��WVI$ka�[�/�GG:ߍ}9�~F��g��!��B@S�[�'��7�.†�!��Vy���oӮk%Q�Z�ݝdm�i�;8 ۫C�0����n��������9��>D��Ta��8��_�ȧM�٥�㺹lB͕�i�~Zylϭ"��oZ��)v�R8�l��u����n|A���d�&��� m<:G?��b���[�Bˈ�}?#�$�䃈T`��0����E��}��Xpjl�����j�-��N_��Z�s���cK%g��U,�A�胂Q�8��[���y@�q�e�Ѵ�Dv? 5�, ��/����S����ǧ���r���v�X��w�>�m �6Xn�|{7�oȂ�[H ��j��´ :��$W,���;Qjs"�ђ=� �:]B�}*��p]r� +�R ���"��4LJ��Λ�t������� a�Y����7��\��b�t���������7ӗ��J�� +n8�{���^�!��'*�/�,�J8����ٓ.�ÿ�K�&?�sxfXY�4���B&s���Oǰq���;�q2�j����v�U�e�?>J�����7�q9?� +K�/�1��ۥ��Pro��{co���q&�N0}3�$�K�����T�n�8|�W ��U�1=������"��>��Z"BsyK*J������E�ř�ٙ�o}����%��4���3�E����e���G�n��)���*�E� �`4�@ "g��+�j>�A aͽkTƝ/��zא�%4 �,cm1�>����P��Űj�L�����f|cˆ���bg�{X��Ƥ��¸�17��B��&i��Ĵ]�$t�/�]�R��f�H<���'�')'��a\���$�Z��y�2�l�zv�:��� �#�@��U���w����[nvժ����ͻjWm75�k,7��}�yw2�#=zI"X`�D�9���CJ��S���hX��^���H�bx�� ���Xs41�*$����[r㕾OT�J�mF�����=�Y \�ż��#Qv�Z��q�I�9��gN";rB�O�Gp�\c�k�V�R��*� �����3ZEݥ���Cgҡ���c���x��c�{P�4io��M̱����X��O����>^_{aM!�_|�]S�B�C����䉤�H�9���[��Z�}S|�Tao�0��_q���U#A|�-�VD�VZ +h�&�yI ���e��#��(BB@�$R�;�{�.//�����<�+���N(�tW�\mMC��)�ӆI�k��u�T,�%���S�����-p&P���5!7�V�ׅ����� �.�a4y�a��u����0�#D�D-igc ' +���6[,Q�&� e8*0(W���b0��a��P�h�@��p +�@�Jp�uK��YU��4��U[/%_M���t�3؛~�r�zl�9>[/�y� ���ٸ:;{Э�C���OvP�0��4m�(�e@��ψ����켇A +Ly� �MN�s�E� ��Ppl�J&���l�\�˧��g�n�Z0}�S����%Ů!4b���(���S�:�h; ��M?�6���/����Y>��4��s|ʶo7���^_��m�̱��b�~�m��:�f�t}����s�r51�c/�0��(G�4��e��v$U�$��^T�����tĭ��Z � 4�U. ���ߴ�����N�/�j��֘& ���II�;S�N��}J��{��!�0O�)�~�MH1�h) �cu��Ǭ{W�!������$�l����s�;��1�q-�@��9L‘���ȁ'1��$]�i�����~�t�'�k��#ʒd��O7�\�wF�$��~�(���2|{+���{�NG����Pz�&�..~�:K���c���{`y896���p�"P?DQt�*��T]o�8|ׯ��T�1-��\'\a�ۢOE�%^(R�+i��^��A�� �H�����y׷}��� �X I�Z9&�P \K�o|��Ւ��Z��)g? -Y8Y�p�Wi�����T��ȑ����R��� a���#gy�>�W5hE� :m��Qy� ��1D1| +�D�~����BF|-�G5�Z�VX �\c� X]��I�צ������:线5�i����V�)� R����=Oa�ƭ����c1�D�ɯҗ8sm��������-�v��A�� +\w�L��=�H�/#��B�����307u +h��/�l��ńSm�l��}(��M�z�*}9a>*I���^�Q݂���U� �: #�P�"��d��6=mJQ�h{4�K� �eQ.����������*��U������bWl7%�k�/�O�y� ג��&��"T��?M9��}�=q���Ƴ����8%=�N��Z �jH� Me�0&��g�:P���ZF��atJ�1�tC*���4�߳_�0��O�wo��U�o�6�������p��ӥ��ؘ��"�E?4u��P�FRV����@J�dX� 0 P��ݻ�G���*�(# 1���rL(�r��@��D+�%-+'����TF��> -�?�>r +�bO�'e)�Ӂd\1^R�q 3���U����+�*#�ȣ�A�M[���i�2�冨$�l �D�~�\�'Sl� �L�G� +�BX4�<`� X� ��I�Ѧ �x�����+����Qdl!�Xy)�/ƶ�}Z���u'�Du׌>��^��� .]@�����]@�l�jKGvP�0��e%S<�;u�1�#�k?E� zs��I�s�u�4M�Pp�M������t�N_�����OJ��0�W- eX���J +�֒ Y�'@(4F8��G�~ N�tlZ_��gZ�� �)�����y:—��������Nj�|�by��rq7_͗��Ƌ���|q7 W�=VƋ��w���}�k�����V��FpH���\o��TdJa�h-�� E)\X*�/�y������3�Jk��nS���N礒$��.�����F&Q��1���aj٪g��a%92h��6x������O�����j����<�J�^\"�c�{�D\2k���zt����CD�@h���b�,5'b��O�.F��߆~�Ȁ���B�݁�Kg����S,L��ۏ�[�9O))�Ŗɚ�� ϟc5�2�@y�2�j���"�Β�_�k)86�⡎o߸V֙����a�c����o�j)qr����EI���� ']y��<���R�|���k����p� �҉ .����欰��� +�o0p�X�j��Γ�_��$�ڵ��(��7l�.��=!��!宯�Fs�j��>��>��U]o�F|篘 +b +�ѩ��� + $�T�)8W���;�>D���{qGҖl�IK@tܙ���9�����$��q%$�k�PB�p���3����j��j~˥�B�/BK֟P|����$8)K��ӆ��k�!\i�����4�:�WhE� jm�B��x� d�V����)�E��j��ͱ2� a;h���*a�js��6`E!Bj&!�V�:��Jf� ���Έ�rЭ"c+Ѥ�:Hɯ�blG<�uw��RT�͘� ��7�M���U4�ߎ��Et�������P�n�`�Gt��!G +|�I�& ,J�����aR@�\s�emۦ,�jSf����b6_���o�7泒d- �兡�;������$Hֆ �A� +�N�r�vX��1=6m(Qأ���F��|�ߦ�"���b����_�����z1ϱ��l���X/V��+L������$\Etۘ B��Q*�i�!lK?'�[�!�*=+ �ޑ�.i��†�Z0U@�Z��T6@�i 6 /.�oUﯵ�2".w��d�N���,�.�����&g�`�� +[�9��N=C� �ɑA�,v��TD���~�`~p��!p�o� ����Bݼt�t����E�pɬ�/��֑*^�t�}�#�cf�9�`P�h՛?��C��}��,Nbo������� z�>�����K�ȋ$���';&=]3U�>G$1&>2r�(�(��,~7~#��+����ke��ܝ��� +��}��YL�M*<'5Y���Q�ҵ���{�^jU�_ bm����!�=z��IlqzT�/G5�uT[z�Q� +�#���"�}�;J�.0�l�����hI�7�p���R���h"Dg]�>�'�'��Tao�6��_�` hb�Rя���smLhk�ۢ� +�:K\(�FRQ� �} -�.��� H�w���ݯo��K��<�k� ���(S�7�K.�8ִ��b�A�Jx��O��'�;I�* ��^I2�*x��NȆP������g�b}��Td���-Z��D�*{���Q[���w)PE��v�/W�+�rGU�o��0����-DU�Zh(�g��D�R-l�K�VՍ��kT�� �XOɸ#��3܏RNT�Ÿ�'�.H~����o"h6���_Et+0��;����2��vZ ##zT�#��$\�&BD)���3?u +h��.�l�TĄS�u6I����զX=���0�&�`��^Y�P �N+)JM�b���P�U^��"��d��6}+ڔ�r�=`{4[ȋ~[yq�������>/���]�*���r�y������v���K@��7o/@�7dAw� "�B��Ru�)����O�#��JB S��&�|K6NIG�U.��A� +Z��GS�}�-�I��ꄼ T�|�uD\ݎN�2�{��dY<����{�o&L0ϒi���ph��9v�$��ģ�}�u}�*琢S� ^�i@ +�R�a�z�t���y�O�����K�H-��/� +t��TO1�/��bu�o��%��A��p���?H�ӻ�{� +�'��X�Mn���z,~���{kp˪ϲ���R+�}odd��U�q��ҟ�s��=J�,’�ߡg#(nͦ�-������u8|xf�G����U���<$ɛ��ߝT]k�H}ׯ8�B�J������ڬٮ ��ҧ2]K���j�#J�ߗK���%��H��܏sϹ����.ɦ�S�UC���PZ� +�&�w�dm��]�뿔�JW�E�iu#)���$m���^tBք����f�K 8[��sx]�k +h6h�K0��� �cD�����M��(������ +�D|��G%z�j�ZY�l�p`Q�*� �>�ic!h�� -K�n��j�5[�.���|=c��Ǵ�q�~h�끌>����7�k��:�&����ۈn�-4;xK��A�a( �m�(�eD�=�H��C.�� b+���57N +���.����TĂS6U6��}�,W�|��M�z�|� Y C�xe�Dq �u���h���㠢�Fo�S���Ep:�G�����k�8��"�&���E��g�������Ǘ���b�߬r�.��m?l���6�n���k@���~����ɀn:�`��DOc A-ÜlGR�D#t�EE���L�GG� v`m!t�F��EQ�}�[�I�0 +�g홛��_J�2�W��,��.x���K� �Y2�~�����k%�@Ig!p<�^XX_�ʍ�b���A�4�H�Q���>>l �����,���q�)��k���@7�t���Í�.�HI�M�4$4�']r�7I�b�7>�0�}�/r� +^�"��*��Xp��3�熜7׬��,�����8x-c�o�$k댗����G쑇E������ėw�ջ�uu��g��Px��'����y��$y�.��Ta��F��_�0��G +�x �9�MM� ''!��z5���v������{ٕt�Ж +�l���y����۶j�l>O0džk���+6lJ��@�5�ִo=[��˚�[�i��� ��d�6���!�'�+!llg + �X�Kt� �5�V�X� ;o������w)�����a�Z��u����W�;�V��d�(8�V5؜�4��*������pYy�ސ���8*�fj� ������v#�3��0�D��� \�*�f����UD7��zt�������6m����=�H�/c{ �AE*���0(?)T޷WY��}�bé�2�(f���._?���0MM�A菎� +�ڶf��5�V}P0 + ���gS.�M&8��ihS��~�*j4[���3�[��|���ï��|^��,w��:����������r�7X��o����}E�k%�����OS�-�N�%�'֨�);UJ{KW�%i�i�)Ps�>���߸�5 ׭��B�q���q};:%�T�mI&���i ��f�d��,��}�u'<E�����ƒ��*�y��M�2C���CAHհ�_��C1Kt����/ �Γ)�ϒ� D^�c%�<9(��e +I��w�>�7=�[%�Ap�)�Q �ݏ?�Ux��XW?$}L$�;1��\���l�c������Wm���i1����QB�_]�:;<�QϾG��3�H\PnT&�"O��͆���._�B�C��}���Tao�0��_�T!�U#A|VJ+"P*-�'�:��,���YV��wd7ي��Rɹww��;�z��]����X�� �vBi�+��@?��hk�tN���Q��Ս�p����$iK%� �E'dM��� � k��RxN���.�a4y�a���Ym{g�!#D�D-igc ' +�M�.Wة&�Ke8*1(W���b0|��a��T��h���p+�֙*���+M�gU�f�ĶV] �J������SYg�7�H��8�3|&����9N\@�����e@�bmzK�Aa�PҴ]���=���_�$f륃T`v�anR +���Γd�X��c�U2QL>��U������O�O�!k���WL%�{��k�ۆЈ�+� +P+�tu��v2��LC�ZT���!�F�E�4���"O�3|I���O�,./Y��rl.��d��"�d96k,���!�ޝ����A7{���D�<��ԃw˨��H���h��zQ*sMv�#n���Z]�Q�r�T�Cq�#��脼�ƭ*�i��ztJ��ޙ�t���i �]���0�<����Hٳ�:�kK6 >,���ے�R�S�I���i�!�Ɩ E�7��~8���W���+����D���í�q��ǡ��ma�7ǒI8��4Q4��$]�)nz_t�E oX]�-!��扆I�{�o͇���\��F��Y�]�m�Į�24��4�:�;뜆��Lڝ��:+��;bҒJ�~���mHs��"�p�2佋�7��_�T�n�F|�W �>؂Kyt�ƪ"�D 0�y +N���t{�;�v��{q'�V hK� �������Oomg�b>�0ǚ5�6-BG���/��6��*86�F�i}w�f�XS:�c�x�皌�AR��UuG���k�M�"W�j}��4� �"Z��Ν8��A�9#T눎d�ρ�(��lw�r��o؟q�`��!t�1����8���XZi�� R#�U���k�O��.@C�wls`�T�N<� �'�G*��a��wr>R~���U�h6�ή�$�Q=�H@��%;(MlP��jV�N��s��8&�}T*Q�.à¤Ѕ`o�b�\��sqm1Q,ޗ�զZ��:5a>M��џ=;j���Vs�����LB�`��q`��D����R���M-��*@ T�h��PV3������������ͮ\U��c�ݼ+w�vSa��b�1"+7�n@:r�G�" q�8Qj.�i�!n˨��T�khe�^��VN�M,�#�(��2 49���� �h�xpgU�9� �� qw7�(T�%S�������0ü�&��ݖ�b4t`�,����4��J�������K��Zy��~/��@��=�eg���k��#�C��0:(&��T�S����ʩ���GʋIc_�۠B�r ����'1&rzgpn�oEz�~��ơ7u��ӧZ�����s��zf�@ʑ ��_�b[�ɉ�� �.U��Mja� 3�/�ޤ2_�/Y�����Ta��8��_�� �]�q[�Mӄ �K`���SQ䉭���I�z�e�{�b'Y�;��@�73o�ͼ}�TM��� �X(M�l�PF��"�O9c�XӺ�͆�oa�_�n���%��4���II2� +x��i#dE�y�;a nM!�|q��d���-j���ڶ�-��#Di�j2ޥ@Nݯ֛�l���_(w�Q�N� +�R�[��B� +���2;�uL$-�����foUYypgȺJ5)� T�Ő�;8�zƞ۞��|!����+\�*�F����MD�b����wP�0����J�=�c���;�m�D�ޝ�A��S@�}s�e]ץ"&��-��b�i9���������hr��i���=D�h%�V��Bc���Ag�W���DpަSц�{b�"�h4ͱ�G�0͗�_��?ן7�:����6�y�� f����f�^�X/0]} ȿ�����Y�}c �P��T��i�!���kH����”�( %ߑ�Ґ�� �u��V��QT.@�q c�!o��~�6�:"��z�d�h=�d�,�S��=�/&g�0�S�C _ �C�;��{8eJM� �N8�v[+����^5����`�K�n�[�m�6x�õV���v9.���[�,�Z8��{ t���͏�C�V�cfIxr0ԝ����>�`7�׍��~�U�E�v$������[kpǪ��x6�V+�]kd����d�m����X�{��K�_]=A�BF=��C}�N�)_��Tl[CG�NJIG�|c>&�I��]� �Uao�6��_�`p�Rя���KLX!��"��:K\(R#)+n��>��g��b �⽻{w��?}h�f_^�p�[! \+DŽ�����he��e�V���/BK�,�8������'�IY*�t���+B�׮c�p�[U,����ZU��V��ڠ�f����i��V�������2OnX ���{脫�*a�i�6`E!|h&!�Z�:$⁆Jf +ϙ�fkDY9�N���h" �T��!�w<�u[��T�X�Ř� �)�����U4�o�W]�-�vh-�x� +C(p]7R0��gw������ T���f`n�P9���뺈��#m�x�Jni�x�.z;`>+I��П�0T`�k)8[I�d��`hT�P�pB�S������R�!Ea_hz4�gH�1~�gI6��$�u�9�����<͓E��n���$O�i��-��G���� �*2���x�@��Rq��!���O�!.ւC2U��$�zC& HC�ַւ�R��QY=����_\7�?zW�h�Zˀ���J�c�:]����~�����p��x4L�\A�!F�,l���; SR��I�m ��a���Š�Y맦��0��R�Ǔ�q���[�x�%����ɑ*N����R�s�C̑��^�A�E���V�D�Y_T�:�G�^w���� GsV�'*p�a�%���J����u��ېk��F����]I��ni>@�I�.�`S�;==3==��o������ӧ��S򖧌��<�قTKF�?f���Sv��7���,'�6���XV��T9~|���%#g����#o�M�Њ����M������yAVy!z,���� � +��. +�V,�� !g�!��w�'�ߐ9O�}�Kю%�WKR-yIn�����I¡k����b��@Â-h��g�z[�Ų"�mƊr��B�a(go2����r��7r(֨�d��/�(a�/&�ȠZb��|����+�%Y^�M� t��fl]��Y�Z��f3l-G���� $���"�B����\(B�U�>�Nooo'���b�F8�����ӳ7��g���Y�ʒ��^��\o ]�S>��)#)����e����-xų�Z���E2S���A��+�;>#'g=��㳓��pr��w?���������9#�ޓ��N�;9?ywzF޽%ǧ��>9�nD��� �n]� �p�O�XԤpZ��T�ٌ����4[l肑E~� +� kV�x [�%$�+^!I��4��#߮������<�Sl�퍤��n�|���.�S|��A�Uk�B V�)j�H���=N�4KR$~F��e$_Wz��Xlp¢�'3A ��Z�i�ߖ��:e�aZ�y �rs��U����\S`9���铑��U��Q�$l�geUP�1�03��6�QVH0��򄽂��ě?�L�d�֟���l7���l�ˊ����t���x9��,]�pǯ +��>xݨ��eD��O�$ڭ��;���t���j�V|�3S�X���Q� ���" [�,��0�#!��Y�IXI�K�)�!�8���Ix�yăiOx�fU^l�s�7c�- +�����M���,X��t��I�_�R�ځ0`��[�f�������Ҭa�ch3 }{��U,KX2�5 +;S3���t"��`��E^�)P[�߯�&$@d-`�L�%s��@��r ����'�{�όV�%��E 8���X�5�-s��p ~deILp7���W���������J��F3���@m��n9<<L%�m�'�����e^T�ӱ~������E.}�L>f=3a,-Y�w�Z_�s�1�s��"�G����J�C��� -���:/��=��q8��d��MS��+�g3�&�)F��jW�� -��9���+�wCS��[.��>���ђ��L�bh�^O��`��ˆ���{)�z4� ^^I� f�K�ga�W�������#�M&0�����̪�@@H���v�*"m�����Y~�"0O +�c�qLɷ��{F>ߢ�P�錡����Y�����N5��G���KO���`�ō[�,�T��uQ���d��F ��h��5�.�B=�iꛐ��>�%3�X�2�Ӳ䋌%1������F(`��Vyqi�K�0C�p�m#��.���C���NSHG.�hGf(r{8_4q&ǎ�y�����q�&�1��n'���$3�C��z��ѩ�%��`\�iM���E_�1�jVV�Z�"k8<� '���-hcbu`I����ƥ`43�6�',�5�OJc��w���x�n��� Z���d�z��w"��z�W�UK�Y�I[g��5ҩr��ަb�P���X���g���En���'��i��1S?�~*��˒U��!��C��s�����z..����1f�i��=�G���wA����_=r@z�1�ҋ�R��@���_7q_����rB��i��U����L�e�ra�b���0��3��.k��H�"v�6R��r�߱�-TZ�?�0�g� +��(>�����bs�yS����|�izp��L�6��,�m��;kp�k2.ɔ��Б~��GУ~D�MS�'� �!7�6(��4�>�4�H�f&h8�\e�]�xPR�_�jQW�2��0���-�9$��� :�dz�Ɨ������������⯟/�?~���x�7�f:%��l�I �[= +K��� ��Z��q@zӿ.���io��Lɳ0����������`|@>� {5���MS��;���oX��0-y\~|��%q���MmPǗ��G�GH����ˋ=�\�����l����/T<�����x�h���k��(|x�3 +?�+Q�����A�D����^z����lS@d^��T��atI�O�������ֺU�]C���s'Z�+ ��e�_��n.}��?�_��qv> �}�4|ߥ��yFF�C � ё2V����f�>&w��a'm0�YG��9��ŲW�"�*���TN��I0���J�d^�+�nX�%B�1 +�+�uS2����:pn�(aTFq p�5���POu��]k ,��5��9��w�z^c�*�C�c�b�>�,_�dz�-�07Y�����/��̇�n9 Q�t@��i�"1N�v��~g�hSO@R��������6�&��K[S�[ +���`�5��`�Z�i4��[��@Yޙ�����7��6�^�;���ղO�>�<����vM���\3�[�)��|³�b4t�?��ؤ��I.5<>��SX��~���%�-��qG�3�uu�8�;��ٻf{��/u��k�� �1�/��D���/�l���ޠ��d��fh�g����g�� Ϭe�".L(��1���6i�KI�/x/������fOn�W�E��©&�e�#xaeY�U��s��J���x< d5���訙]��ˊc�Aq:����L��<�%<唫k�ZH�V�X�;�Ub�l�*��0(��fCSv��i�UQ]V�x2�c.Lj���0V���� +�F�� ��6଄��8�h����,���)� Iˡ �|Od���Q�x�q+�q0`.c��ٔ2VA� ��4^28�*���eeb|!�4���Ry��ll\^���/���C:J�ѭ��ȴ]��L�(%=�0=�v�gK��Hi x!~�, +�U���� �� ⊔�W�h��8���pZ �s�Ƶ E�V��ք���R�b�'M�h[, ��6�%f�2�%X���^FT�I�Ӱ�Dภpa����%'7�-$c^ �<�v���1�=j�4�V�(���Wgo�����E�h_����&�� h���:��{8�t�,�-k+�O��ꑟN�iN2�v��E0��&�����F������l6� ��A�m�d����Uj��3V���9z+�p��\ �M��Gը� _�j�Bv���O�hչ%s 9R�<��!#NLB����P�z�Dfc�o'��O��9�N�q�"6����yU�9 +>t�P� +��Y̡�/b��%��6 |{ �A�����F�آ8CaD�QZ0��l0��[�6Rw!� J'�jbw�.�,��0p��?�l+��r%v����v��:��a�����qSV"=�N�����������oY瘱���C���K-9Z���'Ξ[����qG��P�^NX��g6�Z�q|��6�kȥ�&t�ѯ�4�+R(�(�V-�����*n���h1�����.f�6+y`��Nr.�&1iD�>-��^c�8!��h]:4�Lj���K��e��h�rjI_�7Q��H�����+�Ԍ��� �8���#�SW�ҁ�n�iG"}�E�F�'��?y���d��L�[]��"B7��?$�kg�œ*�1�Cٺ������J�ui%�^�iG�\�V��t]����¥jY�`�.؜� -� +�)��M�[w$ ₷��q��`�(<���3���\��Xl�|X2*��s�iހ����&���nb��u�! ��8��=N�ypL���w�!re-B��$?�� >.�n�2%X<�?M���m����K�Zw�9TuR�$�u���ٽ,��i ���Y��<����SB�ȋJ����o� � 3*|�[ M�P 4p���vy�q�m_�Q@g����ˡ�Rm���iƯ7(���>� OK�\V�W3�.è��>Vq7b�R�W֗>Ԫ��p1�7�J^2�+0r���:_��ꃢs���3~���:�Ǔ���>���갆�#��f~ZOc�n>�R�����"`ķ��"��R���"r>���Ϫ��΢�c� 2�,M�,'��-ö�!��7�"^�)�xǫ�b�B.�&�C�Ёpb^��X{� K��u��'��>[�L�*Qv�Bh\^Ҥ�'*� ����RL'�1�L�qFC4"�9��&���x�I�fy���`�<�X�8ʥ��� ')�T 0`��m�P�U���� �� +�pa ���6)UiUi2r�P��a���r�ÐH�2��A�}�o�&=ڙ?X +x� ��J�����:�� +�:L3�1a�9�.&���b�A)�I�,#_����/��ȋV@Ug���� ���M �e=|�����q���d ���S�g��%�Ӫ�l0���y�e[��N��P�^)�a)�����1�4�7�*kv��WM��ç(IU�F�� +b�Q�3SVPՃg�t#���Y��^�t ���U��^xn��7���Co�O*� 2!k�Pt ��g-��������7�@��+G*��x�PE�ӆ�4��� � +�md� ��%J}�o0��y �+n�J&��� �Td§�bX�Z�g�`��p�����!��%�NpYM���٭v �^���-�)�U���H��@�q<���# +�%�d�Lg�)yW���w�;�vS�Ax�ToU+�B[�1�c��u���:�Î�U|�����(�BhO�{=�+8�Dl;"�@3#s�.�@�Om����3�k�� vG|0���Ns;.��8Ȥ��t��Op��jŊT���# �ׁ �DgU� +�E$��C�����dxr�4��;��61���ٲ�b3�{)R�⸎]V��U��ܽ߱r�V�M�f3��(oR���w˓jI�@o`��A�ie8 2�*����S����[t�K �`�N���&.�;ŵ�d������Y 0�ޑ�u�����ʊiY�c��ӊ�����IY���\n�Y�nV�=A�؊�� >�2iĆ��w��H��WE� �P(vx{�H=��� ���l��`������\5 �}�/(n� ��U�6 Ac��L#��?�]v�O&�ҞI�¬�L�O���ͻ^�0&�&�� �W�y��t�ϑ�刓�z�p�5-�۟h����3� ��M�c�O��$Α?��)��OWY?��",g�F�OB�����x��26gĺ�‼�w��`�Ϡ��,M�����#r|��l>X3;�7���"�߲* ���j�򆊄f�)5c���3[v�k��E�-�� DyP��*���M�=(H���}77xU�%lpO�6�e&�D �VX��Z�h]oU��<��]��tC���@�z^��n>���S��`��ؑH�y��H��*D�[fd�7�Hm;9 oS��������� �5Ys1��i��k���`M76 �*B� _E��ݙU�#�ܩ.�����k"��d��y��6Tg� B}u ,CR2���%SMN��-�k-� dn��]�i(C"��_�/����RQԋ VY��~�3Nu�y�4j��E��q֯�Lʊ�.����'-���9���t�2���QN��V��ж8Y�/S�'�2��^��� e��,�� [�p]�Bp/����wq�~��`r�%c<�� +@"��x�L���ܬ���^� ��=O���S��)���1�h+��&ޝ�1'' �aM�܏�udU�52+��~��m�DZH!�ܶ��u���T`��r,Sb��v��F�C��+RJ�cm� +4��qh@0E�6E�o�,3޹f®�$����V�Y_VKiJSE-�X&��Ž�G��j�«3�:�:*����p�\��_g`�f��D|�*���2c�wE�Xa㦶�V��U� �L��F��N#�P���־0��m�����gի�r�'o�b삹� k�_�O! Y�5�Ŏ�^C!����?X�e����� +����N�r��H�"��b��^]}�͊�~M�_ޫP�����wa��-�0ɖ���6K�BcL�R��_��#��^��Z s�ep�+��۶�?�Y%�n1�$�����+҉N���+�"T�g�Zt�4�ȡ��>>��$�H#�*t�����Ġe85��ɾ�N���������>�=���&Byyܫ����^ް������R�_ߨ����O���Į�$�g8< �k���G��� [����� ��j9!�ǵ��&���� +]F�u�j3/���4��m..���� +���J���ON�;�˳j�N�Ѵ�mTM܂UN���0�Be�ڵI[����U�-���Jfu9�fn�����3�W_ $����y��H'~��; q�aSh��E��Ck=�=L�,e���N�������t��S�����4=����l�v��:@z�_��(F>�&�Ӷ���%�������M�8й/N?�z�[��t���R�I)g���h+e�Z��f�V-,���X��_[-��ߠ4�S �4�c�Xᑰ6վ_�j�Rx mp�%J����u��1՚��:V��q��6f���C{�"o -F��� �,��T�c�eٱ'O��J�}%�[��yP5,Z��P�;c���l��F)��-) &7���LЄ3��,$��h�)��2��r=��,Uz)H �6l��Ax���!�>Fm0�� \p%�hp�����T7����^�3��_zY5By�/����P�@ B=�g�򵸒��6�[y/&��W���Ud����}��e�ށ��H1�Tl������Y��5 $��:\�G�:M%���#Ybz�u��Ǝ�^t�����m���.�ZǴ��p� ��ft���~�^햹n���`��]�J}��It�>����,�#�j��#�9��H�F��R���]d�|Y>U�R3w�3�~�E�$p��� �c\Nm�~Q)W��w��S=�p�q�>�=���"��D:�m' 8�DZ�`����{�v��O�}7�W��͠�[����" �χ�|� G��=��e�/E �]W�IzGĺ���:$-{�V��ŢA���ک_d�uv�y5�9�6���7��?3�&y�Xg]���x4H�+��G�_�R����7 ��1�/$����ܢa�w[�]�5D6qc!�6;�?����>����u��n���eS�6)]�Z�J�C{l̰���d��=a=���_�ꫯ�j����_��tlEH���BËu��%�L�Z�אYW�3N����i��J��r�#�Z�����Ǹ/͎�7��A~�$U���ql�������r4J4���P�J&�74剬qAy���7�f+������B�H��_����]�����v{~x��K“���t�'S5�]M��nL�ޔ���T�uV�zS�D�ڃ���γ�$�p�����8� D޻ܥ)��z�T�D�?���&�]�+�VR�*���!yCS��qC0��=�Y.����5� ZR�=ߤF[�|���[�OÚJ��1�^TE$lx�h +�_�.�K��0�� .z���������=��}|^3Q' �k��QY����ų�.��79[�N��uV �$ �<���&��t�N�]�x�G��v�l�_0^�]9R�����J�VrC +��،|�-���-mx&�%���֘� ��*cMFA��#�w~�`�f(��%���0*#�\�a��o�?^��\N�WR��Nm±��ZD߸���K�$�C��y��m����Ϙ%��GK������z�?�Ymo9��_�5�")�qw?��n�irgܞS�����3�GWY��4qҮ���z�؎�,���#�ɇ9��)������ �TI˸�r6G�o鹒F �E�Di!̘�N�7��4��UN~X�4G���]1�p�J�1˕�����J��%������~S�g�U��l�iC�L����tt~s.�~ƍ�� V��`sn`��W�+ ,�8m�p9Wz�B�Lg�d��{�����M΋`J�L.�a�7�� +�U\�y�уO� ��S��ma�{��i/�=He�4XY�K���%�jY�d괃w�=�/���Q€9W@��b�,i�@nmq��V���'J/����o������O�ۨ�h h���3��+ +�S6��(�.Q\�Js��G�&����*h��4��r�N`4�¯��h҃ϣ�߯~������p<]L��ί�F���xW�0!���z���� +MN( �"�Y O� ���'S`��<��d ���E�*�@��Rk�� _r�@eHu˷X%gK���PKS���8� H��Yi��e�~�-�Bx|�¡3I3��S�N,���)~T��$����s4�:�7�MP�&�6)( +��R�"��*mê)gKn-f������#�F�엁u2�s�Ɋ����2��%k3����&�[�4[�E ?�p9 g�hL��b�L�lnQ���L�H��0�i�nfT L����=�!�d������ț5�r����ƜӨdG�(���Qt�����:��{VhU���ѿ#ɖ�o��X%���X�!���Q��V���pd�`&Xr�������J�ECp��}�xg��%N�@�+�7M��R���:����9GW�u�(���WK~�e8g������[����> � ���.8;��R�wAg�4���Ȳx&�*�`�`��r'�{P�=�￶�Uh��1�'����h���� �����(����ѻ��@����2 Lkv����B+�)u���Rx��N��K���σ� K!z�"��Z� +���Nj���z4�[Mi�.)u�ʃ��2.�&ݚpn��Н�]��A˗75X촳������A~�9�������vxN�u�9:��,` qپ��%O��r�7 ��p�����k߶��p�p�G��kn��]��R�zX�1_�T�{ ���55���ˏ��u�9�ؠ-��2`T�'�h��{��0!`��P+��q�rliyʄ����3���-z`�J�J�� ;J��cZ�ν���:6�����.�o + k�kR��F[j ��gMJ-g��0/e�f��B��ej+B�ey�"�Z��$�Z�������Q"M�%g�&�Z��מ$���)>�c���������O�@Y��~ �Mt�\���M���� ~��^��A��q�j=o����ʅ��=���G{�Z�rn޼�:濼��� �_�mTc̮K���h�̺���|�Ѻ|��%^��J|sw[�Vl5�pwK�VL5�p�K����NJ�}�s�&c��Br��#A\iW{[��>1�b�m��2�3��Jな7h�����n#߬�Msh��x� �6`�c�?n�k��:�a�����zy�k��}}��nL�>�+�V`~죾�x +�^=^�Se-��C�7jm�x0��9$^�LJ��Ow�n&�=���h |0�����ҐTc���l���!�1�# t���#>�<�� ����+��}�v5�.h�=�^�{H���Q��p�71 ���� �{B+����ﰏ��Jn�j�xp�p�ƦûÛ�'� '���~24���ϸG�9Yx�t��p�,��Ԓ�I�t5�,��ݣ:7~�� j)�^±�u^��@�y���$��ש.1�ϯ�à큢�����U<1e1&-�����W���;n�٦�vj��;�_�w� �Yms۸��_��惜*Tz�:��vZMS�c���d2�\�8S ���;����E�d7�v�4�X6���> �}��yo��e^�;�!�R� ���K�e�c�KFC(� Cå�0cZ=���y�BcFZ�q��a&cS2��N"bVn0��;�BD�@ +$i�`)�s@�Ea���i�(Dk<�!Z�ӫ���b�Y��k'��ܤ`R����b��E'�,.b���T�0Q���W�'�Y +T:�y0�Pf�3�)�f���,�PQW��T�B�>x �Z�~��tb��lB(4��އ��B��3�Dh���j�����������ˀ���1��hT�e��ÁT�ȇ8z?9���._}��27"C�A� �0�� +X�g�������rW�p~5���'W�\����G���dz1�&Ex�+ +B*��Q�x�>Z�:�C�2&��%��Ce�#G��J���2��ƂJ�h+6�%g9 oIU�Ws)3+qvW!e4b�� +Ò��>��áUIӲPS��'����h���OR?�P�s?�e\�6�ND^����瀸mУ������x�`�[����ĠbF�!��BBf�����>/�JEHθ6�"̫ �/�?��Fm7��U#�3W�`H�}!+����Ioۇ)[���HN����f��M���"�w(@ƱFxϵ!rwK��KK4���Ms��'�F � �*f! �,�%0��M�.b�9y�+0��p%��rn��e<���@$BFT��^�ˎ`�h��<-4FCҪ0&�j:�Rہ@إ�C�\2y}��һ%[ (Ô��Y���L�%,��Ņ�+͓꫑��ւg�Rf0WB���o�*C�Dh%'�b�e�-�&U�������e^ځbǞ[�?W�\�;��5%OT̹N�}��`z��5_���g�y�D��,�E�C� ���ٸ�� ��8(����.� S@�Ƭrl���n��O�4B���?�xD�*�\_nq�7}� �7�{�Nv(�r��%��G�"3mŶ� ��]�����pm�J��w����z��.F��?G�� G�� �Éa���_�K�D{l�����������c�ښ%�}�T~�й[�V�7i�VsҒxh�e���F+_�����%����U��T{�lبs��w3���z��~d7�h̴�lc�^Նy�?v�ڂ�Q��*�)���p��W�kKÅ�s���d/�5O���K02�R��h�⎀b���iT�pmP�(�NrO�O%AB�GĚ=1OU���.<"W�������F:�tz����.�[)g +Y���R�ѣA�uZw���ώ/��9�u��9x*���?���f��.�� [ �:��E� +M�z!?}� ���-Oz`����(��sX� !��⠵7�*^�[x ǰd����N�X}��4)�1ME��l���*�9���w\���3��Ĺ�L۞�F��j[�Cl�kʅ6te%�]M~�njkϨ��F*��4;>������������1�#�jaS�~�&�6��t�Ϯx[z$��ʉ��. <"Ե��a�&�6G#g�Y~)��ȵ�cn�`��o ;��Pw�P�����6����{2���'�!�w��)���Γ7B���z�ksOU�)u���W� �b���@�Yo����܍�M����s&f{`R�ۋ��=:�ϣ��h��뛰P +��V�1s7���/Vri�t�{��X�����R�uy�[W���t���ʒ�qu�xh�X��+!����WLĕ{�2�2�a +\�d�1N�e��7�p�݋��"��MU��9����UW1��ޅGw�tqXʦ���_}�ޛu!�]��%~h�s]���e�E�x*b���n.�������Y��OH.y���Z��D5�p�u'��nF�wWUs7�o5�헶V�+�'g��͠�����ڇ�D�\؛����:�+6���'�.��}���<�޾�� +�[mo7��_1�JJe)�}98�7�[�z��VZ�P���ˊܒ\)j��~���$;�)j��Ι�3���t���=;�gp� +���0����+z+� ��i�o5�1��/4I��J���(!J �P��E�+���YJ��[1�k")\���D3��wv{ч��T��w K!��M3$�X�@��%�Z n)5䯮'�o�a��?f��1��^�^0k!?�LH q̐5I��K#n�tNd�jG"�H6_hkN�Z�t0AUn/�0��l���Ȝ*��1�� +U~9|=�0�:�m����^� p�!S4��SDS �C$�i���n�]�1��St!� +�Yq���:=�����y�B�G^�ѯ�oϯnϏ^��-�yB�I�̘�1L7@�4a�&�F�7�3k�4���V>�N�M�d��@p �C��[����Og�����r���� �~vssv5�<���x{}��rry}u �pv�����ջP�T��JTBH`hO��ˀ�⼤R�� !|��9��XQiNHJ�)t��cHؒiR +��t�g�MJ��Hʝ�����f��d4"�s�5��F�?�{m[�`Jd�D����aA��ᘣQ>�I�XM ^�ƭȑ����fR,���&R?�L�F��"to*يh��7��wר;�m�qM�D�8lI>R`�l��cV�4��(̊$̥*�����L.�/mb��|V]_Zw�������5 �����<��ެ���s�F��0p��ի�*�M�t] �*�mk��9[Q��դM�$˪�nqYl�Y�#�"���Y�{-��ovZ�����щ}9�W5;8�Se�*� f�,�,fKʕ-(DJ��*8�<��z!boc/M�YB$�]c�CC���O(�b.�rh +��:��Ɩ',�2f"I��5L&�qE�ב��I�fʿ�bh��7x��3�����"5"���z�>N���;(-~QZ� + �)��:��v TSy� $�9���c%�����F#�����u��zT4�zN�H��U�:TR�Q&34��2��'���<�(̘T"�$&� �,E�1�zK��Rk�������T�E`S3��-�1#����W�,QQ�J�2�`����ꀪ�l6��rC&S�� +J_,��6STBD8r���L��d z!E6_��n77�>>�S��{�w�����?�@��&������^�!?�5n�d\���L�oneB?�^���9!�]��t�8U��W]d�3Vu�'d}�pU��0��Ì�b�K9$L~�� ��T�|>nL�F�i[j����10����ʦG���!\j hŔV�p�b��K��!��˃]Y�%YL[��T��Z���f5T�����Rk&������Nz���s)���׆� l��1�H���Ԫ]1�x�$�bjLS� �L#x��S��e&W�d�I��6�2 k�$`A��{�cTt,�!A���O�U�g�'�k�f�PfXf�����%v�B�9�p�q��� +�2'�74ZQE��_�䬈��N��¸ ����^�(<��E���z/�Ӭ]p�]n�������!c�hm�k|�`����o�KY��c�j[��y�=�w+X�w{ ��v8��Xg��L���R8����E��"UL��SºpI|RO�k����/���W�Y�j.����s{_���w� �p��?�� UJo ��ݸ��a[��E �0���ٗ��� C�����{uҩ' �f��&r~t�7i5�7s�ODTm z|<��?�.�&��9�pC�q� ��L%%�_o�|;����y�6^��a���iE�z���x���[�^~{Q9��Y�W�純)��. +�s���&��!8�8�ш�Q(sX�j� =[�H�-Ў�d��屄�y�u�����T�%����b):� �HKĴ�.t;�*{�o%�1t�%�ߊ��0�1t�_L ��?���yoӸ��1���#��L�4g�+���0򅭽8��s�+6��J� +��&��X�G��9� 7� +� �Ƕ~��n �{[�,^��n�[(�-�5PB%�Tz�&}u:�z�0ߔ_� #��i��=*���| p��LЄ�qi����� �\�_hp�d� \����h�j�K�D�����T@V�%8��1��v��b2E洹Y�)����6'� ���� �^� �"���m 7���6N�=��M��_}��U�;����}��Y�� ����h��b&:T9��������l}��P�/*f��x�����c�~�=����残Ϻ��Ԝ +�>ad�� � K��L(�v6��SC� 2��*���Q���G}��2�uI��:�aAx��(mҢwwGG��wOur_�׹L��?o�e(!z*��_�T0O������b[2���x�}�j�9��j�:ݔG;Fa���-���j%p��ӑ$�u5��� j��h9�~�I�V4�`XZ��t��Ά�9��1�� H�&�qHh���J���ZJ�R��0A��b�d�0f~��I$%���r?Ԋ��z%Xl��|�C0��f�[ �!�IVh����D:# j�|�v��7ތH^� F2C�̗���:�p��g�KP��Ԏ�?�Cxc��GUI1���:���6�8{+��,�ɔJ���2R`�y���ǚ\���CQ���c ���X���|.{���y�A�Q�2p†����J�4L~�N�%��޶ +Q��U�{[� Ei�������Vc�l۹$ӫ����=w��"�RI*���m��s��CQ�Q�1�}��z��W��߶�S).�3��m3g<�� B8�隻�5�_H��ĕ@�#�]+���P��y��k�xb�,�k��z���D{J�—q��Eh�.���T�+�u.e��v�Ĕ�T:�ۑ;:i�s!�Up]I��H���eBy�d�^M��'G��Z�]�v��峼����)�{%kz+��b�)l7��:�s�[��F#�7�&��z!����IN[di�Z�q�|�oe� +0�w��;w�r�ީ��\�l�2ۀ��E�` �in��Q�����5��Ay`Q9�֣mM��׬����ZK�$˰uz�{r�۞�vOK�d����&$[s,|��H!-�i +�S<���G��� G�iB��,[��N�2g���7�lGI��C� G�cZz�'���X(�N�� ��*ᑙ �-gvWl� � vJ8�y�1kEYJX�,�2E� ���J��'��B��F���bz��Kn��{j�c�s�W�4������!����ܖ{]Y�W� e�Y �a.��"a���XY��z<�`�Y���R����>)��b]�IQ7aX�b�)��T��|i��N,�r�����.,�m��V�$+盀 mT��I��z�U����E �6l�wi?,@`����Kq���c�|�����j����nTɊ'P�j 8�t�=��u�W%�Q+蘕����t�2�Z���3A�de=űd�6L�ݗ����nb:̟pStq۞��%��EyŢ����6���N<6:/��x\����xGq�b����q��2˂�*:�4g� ����g�Z�c"�ÂI�*���}�FkZWPW;�oT�W���?�47L�6�=��J��Wu}��c���o���CQr�̫x��5K.-�G,���<[���0�kG8�5r��<���4��~��ք?�' ������r�oO�+X@��{{��9�Ct��q�����:|��.��5��>�k��@�N=p�j��.>�Q������~�U�8G�E��8� 1 ��CBr�x�z��I� F�ڣ�����(��k��F�Ɯ�h��.��J���t� �4���'����@B��Y�Bh�W/� �$�{��`"r��=��[4�������n}2����To����\�#�]G;D�4h�}h5.�Ե� �^vh�����K� WU_!�3纡0�]R0�r������r`�3nkĩ�#�-�]�8� ��Tx����-_4�-��,��6M�$u^�����z�+ׇ#%4��rQgˊ�R�p'Eg[�J��1�Ts���H�9����} �6n��,���n�6>���������:H�;mb�f�)���i�+䅒�!{D0Ԩ-?:A�ȳ0@��Oȭ���*�+l�=�_s��)�d��>ɢ��1���:x^�)\���&V��,�H��������*�Z����+�(��4�6�ahT fª�;@is魰���R�i��� ���R����wMo��;k���v�FG�qVy�e�Q��m�j?�<}�*q4�p��j�(:�E�mV6�92]�j��iF������͓�����0Qx��X+i�ci#[VC=��C��}�u�X|��%��js���&�c�aӲwek8ȴl �3jt�EJ�i3��&�=[?���s �������ܵU��i�oڷ;��:��F:�� Z�)_��.[i����M�v�b���ԅ� 7���+���7n�Rd����6g�����X}��ݜץKU���Ň�u ���u������)� +���?�^ �৤���?�X� {D�Ұ!� v�_��/-?�F�8������N�yrmҰ�Fi2hQ-h�4��5I�v$uH��Ո[�Hǵs��P�ף>>]���U�<�U� L C�4�4�vI�@��V$����;-��둁 �k_��iF��Ej��V"u({L%Һ�W��޳ +i(zWҹ{}�d�ᶻ��?~���3){Wm މ�l� �jp�����o�v�k�����^x���� +f�X}�V,���%�����5ud�S��ݰ{{�� �k +Uo��� G{ޟwD�1Bg�]x��9��;���t�`u(�KH�`}K�k4<��&˟6F������o/;PW� �N6�U�� ?0ͽ�Ϥ���K �����Oyy�I�|6h��5�;�A`����T\�� +y�����2���uGϒkPFm�[Xw?�˳� ��ڳ�m�i�b��b�^t_ �Gz���:����a���?�D�����LZ��|��.����cA�׌�|;�.��[��t��� +��e���C���e�/�aw��( J��3W�}�2]a� �� ���)g#��p��"e� ={��� ft��n��}ZS���b��"���>m0�� ^� z��&5�4ئ������-g�����1�A�.l��z�,�,ӲJ������-���WB�b5U���-��x*+}~ċ�'��.�T~zR�/���� +�@S��4^��/��� +��(�$q ����y~����.^:(�$y|�zCW����;��,���|�2�ݷ�q���i��1���p?G�{�k_u��e��iD�n꤈�e}��׳谵�vE��.����r��$4,y���F����v�5��[��;*�N%w�3� +��R�f>rg���M�*�f��ǎ?haOh�l��gNd������.ɞr{�����Ϙv81qR5�|K~��^I@h+��:�;+p��o9oӉi���\妌�1��%�(�� Y*��&�1��,� ��o�f��� �������^��W�n�F}�WL ÕY*��NT_P��X��<+rH.B���K�J�/f�w��$ P"qb���̜�C��:������<�{#xR�!�?{7Rh��0�3�E����όT��L�A����C��#��(a^�0���0�p/S�3å��hv߃T��@ +$k�`%�KA�eJ�c�X�W(�����t>���������١n"0װ��R�}N�Y \R�l"d�0d�'ȞL������tē������d�s��5�2͠TPg����&�/�B�D��,;=뽲�+�! �K&�O���3�Y� ]c�!s"��>` +Ƞz �!k� ��7�̀لR�������n2��|1�5�y'b���r�>,���$�[�1�Pm�,���↋�O�:'A�Me����]�����h���>��g}x?��1}7������d>����n����|<��`z����s<��r�|L�� +8U� +���-Y�t��1a�B�P�Q��HP����j`‡������d�–Oɛ�y��U6Ys)ck�f�1e8d��! +�����pz��к$�i�*˩a���F]��ibg� 0�V�h�c��G�q�C|�����:�r�<�Ej���Q���̸��9����nOT���t��X��T!S�B�F��bv�=g*L���lN3 ��W�e�6H����摒��;7�\�>����2tk�&�6� +��#�GyN���=� +WE��5�Q�N�+nh�hEd�� �M�[S�m�M� +t��K)�zZM�ơmz�͉�]p��˫����8V2o �am�h��E��Y��" (yM��;��ÉъvюL�&e��dF��V *Z��ű�К�pu4��hR�T'X��0���y��'�a��� >ρ�,N� ��1A�jk���K� ����C������˘{�³��ٔ�\tב��r�1O=�ĭ z��yt+����pu��� ��Aiwy]���� +�J..�c���k�8m;��Vn���n��ǯ�گ��"bs�Ż�l��.6���-���� +� +=���h޷����(Y�Y w��������z�޴��{�uK�׳�pzڞ�zk�9k�}>CW�6T���t����4�z���.G�vz5��:DS�ҳs�crd:}L.�y`�\]��Qi߀���Y�o% m>ڛ1Vd��;HE)v6�"z{�h�JХs��ibNf��~��i$�|"鶻(�"����ƅ۟YGkL�0��8�ƺ��W~\0o}�/�C�5�6�2�� +��a�R_�Ȭ����E�Y�A�O�"�ו�^B�B�%�P��P���g���G�Aa���d^���ݐڰ~d�6�٨�7諉�� ���Ox���u�xy=�2��\c���k��q�BE<"0t�(L�*0.�P�� ���RdC�t���O������= +�Zz��E~�(OП�V�I�y Ւ�>���paPi�LMHjū8q��{:Ejm��M����� ��k�����/�W]o7|ׯy� ��1i��@�`� �Pw���y�����y�$K�]T�!��������T�`t~>�9nXr��`Ū���=���jIW,�.�ʑY���B�'�IY*�t�7"�s�r�0�ݪB8� +����ZU��V䣵A�M��e봁�!JCT�r6�D~:[L.��b� �}��Up[t��c� DQ��ZH�ZiS">�P)L���u�1\V�Sdl�M,|*�D����Z���mLe/�X�!>��>�w�[��*��_O�>��Zl��Cki�zȩq`�\׍d����ޑ_#�^��A�T�W�� ���@�\�~4�.�p�M9J)�>M.����_�eoS̝�d- �Ӳ�� D�H��R��|C��X�3�X�Cm��۴+Z����� B�N�sL�'�c<�̇�2Y�9�[����v<]L����r6��,&�����_}�_���Į"zh�OB��({zJ�Zb�lC9�8��lEI(��L�DC�f�[k!T�5� *�C�rK.�hD~�Z�q��J�D�tIj4 +ϓ �{�^��A2�$y��&����P�>ϥ�uk](Z�aJ���Q�\�T�%y9����xO�·6N��B��?�-��:2�85��&UPq�a��u84��<[��N����oL���bC�3����r,��r�5Y����l+�S�B���m��v>�7�1@h����ҐpE]JP/��ܗ�?����&WiO��@[���Z״.!7��7!���'��!�ۙ(7(����8��s�:�ߠZ)q��H�����wk+���-;�n�{9��~�:0���f"d���������vp��!����Ov��긱��K�%&�C�N�]�ٓ6���� ��/Ѕ�����I�� �!7�W��|[�Vp��ݞ�w��o�T���k��a�s�i����6��H?[Ú��і�A�6"p��qK��'(oǐ��Eʸ���HK�cn[�C�(�hV�YJ5;�壟^��08�D�e����Uo�? _�-NI���D��Cct#�r����}8ױp}��~;[����auM��.��<�Z|4�s�����-���N��� 넣 ��צd����a�oen���(��5��*�f�jG�1V��(n���O��k�}ܴ�ȟ���1> _�������U�n�6|�W �>$��J�����&6j4��IpO-��m(�%)+���^����E �ٝݝՏMe���<�9f, �V^�bU�W��]k崤R�Br.��`��"�q��匔�^G�Ĉ�"�u�[a 3ݨ\x� +���� ���B+ +hmQk{�oyۄ��QZ���wc`M����z��e���8�Ѳ��+vh�}B�-D�sH-$X�ֱ��T +���6{�e�[E�Ul��&HY��b܁�O�5�����1�Y$_�/p�:��=9{ѵ�Ci��� ;�9#�� +���d����� 9����Do�� ���8 �t����4m�v,b�cm˴���ί����������+I��� [ʱ�C#9[I�� ��� � +�eϪ���xL/M�Kd�*@+�8����� ~�����_��.� �7�z�\�=��V�eD\��MIS�x]�J��{�o��ז0�y���������Caȣ��I����ؾ���*+M�������@,6|�qG6�J�2{j�b�-�F�1��4>�v�a����X.��\�O�I}�|�vX��L�}��v�8]����ƘoἿ����o9W�����w�������VԨ��r|ׅob�2��}���y��Zb^5�%�+��J�P4*�7��:ҝ}Y�@+&z�p��o% n¡��W:����%#��;�lk�~PY8Vnm,oԓ�ɏ1��� +[B��3�sQ�%�ъ�A�!.ܶ���ܒ ��g˅~oh�p�T�Z(6���a�̒�h����S�ǘ��=���k��i��w�rE9',�}�� .��Y�U��92!�0��O>&��oxȏ����"�o��}q���[�W�|�)��Xߏ9~�(E��d��d���.�� r�}Z��ۊ���n5����v C�Iny�����W�U��uY��/_��%�I����R�K�Ovc�3 +��n����_P��X�ڣ]� G=` �� ����ۓZd%�ܬ�FX�;��\xi4�'�4:G F#�6*c� V.R��D�E�P{7�#����bzs +��~.]��9l�/������/�2D�KR-H�2�bC��B؜\�L���(=��F�JY����.���ؚ&���u ���֑�?�^Cߗ|�Y|�l�3߮����8�I��a�Aj�LU+)tƷ�w���Q�YR�@�+`V��@��(�����x��lF��[����ߦ7����W?�^�+��B���i1��D]+���BPbC��4q�����^�bH�]��~�v!KJwp�h��g�9L�����t>�ߧ�_g����ӧ��bz;��'����.���9��`r�������P�-��ڒ�]�'�{hJ6Vb�\��\� ��E# +�¬�ru�h+�(���A�Jz����G����E��DźZ����:�d<�7j/���2��z+���$� �H4A��i`�4N�B!�G;�!��u2T*tAR� 1zL1�~%�K ��X��-�/C��03:R��y���ġE��u��z�r�X�bWs�Z�E��bhlm���;C�*��c�2�I����i�|iMS��`��Ӫ߾-�;`�+x%�Ж +;����&��o(����î ���Vj���.����G��W�<�JN ��WR���k���$(��ѩF�Ė]��`9���$�yv'Y��/�ɏ�����ʣy�&��$�5�W��(}SO�v�y���#<�*:K{@�!t$ ہ݅�3SM��P��"�y����A���hS<{pcp��RH>�š��|���1��a%Mh�۝���(��(r�^+���X���S̹�R�с��$��&��9�#����N8�VC%���0��QW�ۡ�w�7IL����f�����w�K�9s;��b�y�,����9 +nyNJ�w��+��[a���jI~����Ua�̲�V��z.��SGH��G��Z/[����������381U\n���������e���x\�SǢűC�)����ߕ��-y����i�����u됈� +�O�L�_���N���^/�Jl��<�F0]u[�t�S��De��I�/g���5Zm[w�t>tT�a#��0�mt�^v1���v���������ό�3�<��7h��$ؽs�i�vZ��8LN�2�{]��β����o> )�bN�6x*� �K�@��p�+�ؠ%K{u"��RγQG�;� 7�-ZR��i^�8S5 ��Ɵ,lݏ7 Z��2�1< )���A���b�}��|�:^��5;�@L�oIr{M˯���*"5-?a��A� ��&������p{j:���I����8�o�@��p�J�t@��ݏM�b�K +�=�Ā���Z�ʫ�K�),�Y.���{�������P� d�:� +�8 ��~ߑ��S�ݾ�0Šnliv&� �ݞ:� +�k�~�����I�W2��^���4��|~E��>��FO�=%8��n$y�[���_O�0���)�xj+� �t�j�X����Mn�+;�B7��Ov��h�V����w}��9~s�TM�L&&��$�Z9��U W�K~��Ւ>�j/YH]� +Ʌpڀ�#�9�B�+�IY*�t����+B�7��0׭*�c�0�f�1ZU��V�imPk�����G�]E��դ����(�_,W�� ��/��8*б��*��y�F��`��`�Ѧ�x�P)L�5��.+�)2��&V^J6����ñNc��^ʞ��2qK�z���F� +�A����$е�Bi���Ku�SN�+�n$ ��W�|F ��E���"H���o�p��_�r�y�$]��"4kS&���*��-�٫��h`n�$ka�sˆ +��M#9kI��� � +`�ΰcUz�&��˥ -��a�VaF� iv�w�,�q���/oV��^_O�t�ay����2]��E���Ž'?���C�� �1^�w��Q*��4������6��sH��V��R?� i��l�h-�* �fLe=���!%��|�>Z+�e �{�$�h�.I%I�}H����O&�0I�!��a�J8�#��O�o 5�EH=�c�Ka-�ֺp�!���-����<6��Hvoק����vF_��@����\k” �Ϲ�]��۵`�H��1@�zn�#Gޚ�|{�[ǘ��DO&amڵ��V���+� �h|Eߢ��4��Tю�6|�W �tg\� �Mќ{wF�6j9 ��4���I��Χ��bi��m�"�ۀ�3�ٝ�o�����"�+6�lTlٶ����;g�3��@!�����k?(Í�΃m$P�� ��k��D�(����v�xR��r�m�P�jY��1؆<�%A;����><�9œ�ZOt$C�D�~��Uw8�I���G N;ĎN���y��a9Z�=8L��S�|#���G�m�N�|�/��H�Ws3�L<F7LR.TOø��A$�.^�*v �Oo��7 }T#������4�l�ݱ7��N�I���i"q{�!T�w�,����/���_��өP������%�請�u�����Ռyo �O_��`?B��a���`�I6�� �'ϑm{#�0��rM/C�[��g�Ҏ�e����Ӳ��|�v?o���q��.׻��f������U�u�� +��'A�R��o@;�ދq�L�� ?�=�[�=��4X�(��%��| +IO��AV�l�G��TA���6��W��PM��9g��qrJY�!��lY��s +�}g�e� �2��_�F�T=�%���L�G������k�B�q1�1E���+���.z�d�pQ����,�$P> l)^�q�m%� �Ÿ��`b��;{�Ɨ+�e +f�U�C�p�Wci����{~v}�ZEB>����!�O��߁�v�3�ؒ �HM1=�_����ܻ�F瞧's}���aoX�0X�V�Rܦ�:_]���̲�?f�U�n�8}�W �<8�#e���n����."7E�(ZIDh�KRQ�6�� %��m�K�� ���̙����A|r� ,J�!� !��5���W�k��&��4Tp )#ZG8��"ט�4�$-���(�[Q�X &��1T���a55%�J-�#�B�2j]��B�\ �� *���Tȵ�Ei@��.��6�����ޭ�U��V�-C�G�m�o�3�ҁ�v7<>w�Y*���)J�C*V�Q�S�n��|D_Z#bikĥ"�>�X����ȷq\�uD\��PE�S�?L�nf�����c>q�Z��*�0������d��m]��(�ZQCy1�h�E�]� i>D�w��(�$0MB��$�d���?��yrw7�-�7 ���j>��.��Y�[�̾X�_������T6 ��ZF1�ғ��������Ҝ��/*R � �b��VTk��g��'*m���|�\H�>ZSmC-�`q��*%�IeD�ܐ"�ݖo��/��t�LZ�iQ�FS$M-3�Z2�p~$\MS��8ڟ �Rץ �W΅TB�2��0���GO��ѧ_���-�+�"��Μ�h׋�/���=QYh�}�wM�v�\}�a�Д�g�������sw��gEVt���8�+."%-��l��E�J%�7��gB���r% D�3�)��J�1XE��E�\%v�4•*e̜"���>�2F J��Vr��w͗�U��,Ո9JksD2?�-&痐pA�17^cXq��͸���!QXs� �2Q:�@��Ɣ��e�b�y�YP+��d�,\*���[�`��*���+0N�-j�R~�[�:�#���i�R�Tݠ��(P��j&c<�He�j+��%� }t���Z(%H��MŔᐕV�(-K�C� +���e;�C2�(fT��S�N�/�����&D�)�'H=�s����(9��U�ڮ��^�pd��z�����}��sS��%&nd8�s��`�-��s��m��aJ�E���kP`d{"5[���Vt �� 1���ї�n�O��L�"� �n1�Ю �� 37D�n¯�0�+��>��ж5Uڢ�Ѫ� �x�2ϧCo-�ش�V�w>w:��՝c�եC$�j����y�����U0�D����Հi툵�V +;��j���8�HĄXC��_ )�AYGQ���k��ׇm�h@��&�`�K� +�~>��V+��3��*�TYڪ��*h�y�p& X��U��E��~ �����X�By{|�e�ă<�J?:Ym�MsUZ��4�)6 +�Y��yR��r~�q�pv.��Ll�i�.�~�R��RFT���X�����Q�}��� i9�?<�B�h~�a���A�t_ d!ʔr ����Cv6��A��Ϧ��j3a�@FН�g�pX�n���zcW�]x����o,L����[��pV�_[-�IO�x�洧A�*�6S�#з���&�W$�4z��0��z����i�W�^���5�����F�q�Pb�C%�޾��ð� S4����1�rL���O��xO���� ��]4ﶉYqepG��u'r���t���@�����\�h4�� ���c�@�C�)���w�PB#�g[o��;�(�/Fv��u}�' P�6�3x��)�ъ�|m}���>>�����.�Px� H�������}�6�j�- �4��;��{�?4�S������m��U�-{�qӘo?C��|��3����yv���Vmo9�ί��PڏIII�D���S����Bfw����{�� �濟f���\ڻ[)a��̋��~�:��Z��gp+B`�RK�����hg�R��~�xi��@ ��5`�;�v�7 �JD#���g�"ܚT���и�6!�!Z0 m,,��#[9K���r� "��@�]`������R1>�.�a��1�X:Ȍ���� �PRh�@깱 N��#aC�40���(�`2���2iL���m����a���I�R6�.6��:*�e� 3�X=i^0z!V������;�}���!0�DI�F�U1�� 'fF]����o�����?�����ɲ�-8ᶱQ�,��np}3�<�Z�s`��TZ a��$Jb��Ȩ��(&�ԐY饎Z�v% 6۴޴2E� ��=:��`|o�ƃq > &��>L�����W���f ��p=�L��F�p5�L��÷-@�c�����"�I;�����-E�\������Q*"��,��X$h�9�CPr!=��t��rJ����U1Pc#�˂)��H��P{u:�T�����%Q̙�2�:�r��ަ�����< %�a��/X�+ �Z0!�N؃�rQl1|�+���\=l��'�$h� +��P_��'��.Q�t.���-���Ȃ�0�i�g��1_-�bdy��DR���B�dV$ � �-�m7��¤�S6A,���O�{��6�sh�:]�����<{�Ms�䎾t�B�x]4o��������l��1�7T�x��"��p�A]�л��R��$����L�-���z����6v:u�p��֓V���Q<���2�~���A���/�uM���P;�-{���p)(|�CA�|Fö�� +��ݲ҂��*,��99h ��������Q�\��^��b����#�\h�Y����V�5JF�X��cl��ڜ��P��o+�c��4�U��&k]��,+�/�M��,9-��={�'�w��ڃM>�-8�K�e�����a��׸��I������2���U���Z��9:�{<:t���[ :���Bgj�ח���Xmo�6��_q �9�;-Plh�4Y��� +���v�Pg�d�H���xm��p�$K���e������gY�������>\�� PҢ�B.���Op��Q ��j�ɘ9��� +%  3��~)��B��y�f�3�5���e��{���}�eH�$�VR�=���* �G\h���5#����^^M��!����~B!l 6 +��!R0 ScBFJ�n#�i�:�p�-�X�T!I�Xd#�+evQm�x���*X�� �u��!�%m8䇣Cس�s�)Ww��8�� ���� +�6�̂��4K��y���#�?K5���P@EM3@���[�=������H�Ÿ +q�rrv>��<V>odBƀ��s�)��0��ׇ�[̨\���* �Y��6��XJ�C�3_v�J.s��v�p�-���i�����'���8wmQ9ɴ�H�%p[���Q�Ũ}Wi���r�OP�h��� !�@V���Ul��Dif�_��i:h� aN��bh�n��4s�6��jel�d-�0N�R�1G���7-��p�m"�Ρ �%�t���`���L�����TU�'V]T5��G@�Uqo�JH ��B��k�`��z� +P�%�!x�7ž�@7�����!@�� oX[��;I��l�)E��Sf|L�ASQ!Bo�Ͻ��Š�J�-���v��Ek�����A����� �bN�|Kz���k�|N���t�B�4O��X�����ݛ +�%�Pr�Ʋ1Z>[$Q菮V�qR��9�&�m>&Qb���U���b�\�,c��a�8�C�� �I���z����_���ʤ���"A�� aZ��=���p=͟�D� Dn:�3�'�;�=�|��J��d�~CC����^�w�ie)��nYA�X�p�׫�gΠ��v~��,�bа8hZ4%~PY�1�}C?��>�՟�~���~ ��ރ����a�4�jY�͎�k::`��uֵU����$i�^��׆A�B�l ���h�0��F ���y��X���l���B�� ?�������H�P�k��kU�]�ϩ�sw3w?x��D�|8�r=ԁʓ�]��,�� G[�޲X~%�Xa@喏 �rAA͓�ƴ�kZ�Sj�l�f��[� O���N�M?��JS�u�,�'"�(��&��!��61�����Ǻ��6��[��m ~4;�p�<�X�Dzs�9)�͙�6h�d�I= ������#"�a>���չ���#�����4!�X} +��\���IEw��4de� ]3��x��Q�6��5�������l���0����>}�>?�G?,J>���������� �Z�-�� ���{% +��۩�q �R�ߙ���S�t��?����^��+^~�W������E☾= ���m�����k%�+��.����xum��m��Q������zώ{��U�o�H���)j�� +6���4�V��:���Tm챽����]� ��;ڵ7?�+E���f�ͼ��z]u��8�/ ��q�ES�Kr!��%�W2W���Rp#բ6\ +��dZ���w�9�a��Z6=��}1��HJ[��ç�����CWl ! M[���j.�Ȫ.9�C��61B�s�D�l���l� �X���1��(j�6d.�P�<��w���y|��y��c>������(�j V�%Oت$���t�r��↋|l�ڋ`�Mۢ���3���h0�1�x3�g��f����4���Η���k\,�og��bcq����E�1����)H��k+b�)��tGO>���O���gjR�ڍ)J^q�D�-􀛟��%��U?YK)K�8��E�12'aXE����oۿ�й�ӲQNSQ��@lT��n�m��Ciy�~�{Umwâ1uc��K+�΃��=|__<��\��y�_*絒5)�����q� o�:1�c'����F"%�(��8�A"C��}��[h���/���& +:���*�ސH����<6�ZIC���Qϗ��L)���p���3 � �e��8�8�ñ3=y�o�R{Nхs#I`IB��ݝ�>7���&��}��Ҹ�}�֑[���?� ̖٭�M�ٔ��j+ەF��O}deC����b캐���TL��l��L�j#�[Zc�*� +�����+~oхp��~z��x.���1�:ڬJ� kD�*s�Ɍ\c���Y>lګ[n��U�oaO�4a�����={x���鸏61�L��R�~��)I�ܾ³��?��k"�?��>�1�]*c' �j�Z�~�����J��7L)cMii��S�"oo#���'g�I�����IO��<�A��,��W�n�8}�W �<؁+w���i�4�[���mQ,���F�4��T���!EY��{�@����C���"/z����%���q�e6G���F ��Dc���9*[���X0c�8ox��`V9�`q��T���F�T�La0]^�� jP �4l��h�.�� �E`�Fܠ�&X":���jv~)�p�q�@�m6�*�?C�4�$�� �2Uz�!�ƌ�2�U��<�-�J�69/"���� �o8�� +���Sie]c�QJ�Y�6w�~���:�mA* ���u�� \B�6��L�]g���>�FԚ�̥*mf M?����x\UU�\����8�8~3;��//�<���;)�����Xo���1[ �*�k�#�Pin��F�6��6�B�f�\���%̖}x5]Ζ#�0[��x��ӫ��|5�X�� +��׳�l1_�����m6=�6G x[hJBi�TQLZ| +1[�>�c���Y�2�Lݠv�Q��pc�0���n� A;��)yY��3��k��p��75S�cVZ���,��V��o�C���Cv���&���a�-����Aӭ��� �L�! +�Qړ���4ŨM�� T ��?�fM����AB��l��~՗�^����9�,�K�d �6��c�mXJ�������`�%���88�p��Y�.� �gWJv�x&��GXNz�_���j�%����s���������m��) ��k��A�5L&5��wb�@yϩ��K���q��f��L�{Anr����1g���yefs8���{��7��o����{ +�����W�������0�FЧH��2�Vȸ�*����%*�����R�G���E��(�Z��gd��]�-��}���{ ��[h�|�e~�z/�z�U�n�8}�W�d��ǶN� ���^Dn�b�(hj$�I-IE���)ї8-Z�Ϝ3�3�7o벎������+i��B�%���JUQj�m̒�ym���b��<���$ e���&5�%!U�m�&\�Ff�1���'hdFJ�C+���]p-��UU�Vh�Ikb %����bzy�\T� ��(C+l [ +�V�[�J�e�p�Y!s�W>�T0��b���Z��j%iS�:���:$c:��*�Uӗ�Suߌ>�6��� m�A��:8y��+��T��-;�Sm!$�ZՕ`�{t_�&F |�I�� ̗�ﺁY�v?@im�*Iڶ��O8V�HB�ɇ���,�:{����"c���Fhʰ\��u%8[V���n�~P^B��� +Y���i۴��0{J�� &)���&�4��t�����'77��bz�b~�����t1��R̯1�}q�?���#��%i�}�]JC��R�����SK?'S�ਘ,V +uG�oFMz%��k�d�J����2zP[ؒ���[G���B��#.�z�$ k�*HZV$�7�E����H���ĮF{M%Q8�� ��b�9��ա���U�� +��֍��,��;7{���x����=�y�?*�V5i����,pd�ɘ˒�N��E�%�0�je���ޔQ�W�����!�}~����Ke�C�ڮ�����>|���n�3�YS�òr���� ����G����vs�v��`��a��(z{��Y�sڸ��_q��襐t�3�tC�2��tiwgg�����H��¶��w�,c�W����b��O�%���0��^��� �����0.�����w%���8›�p)4xӺY���{(4�`��i�� rbfL!te"|F�Pk�uH�� +�@� +�R��'F*�R���8Eat`�h��o���Lxd�}�S>�a�M&�fR��D*`��I5����TSk1* ���QO�sŃЀ� T:�q`H� ��1:��5�2q��v`4�*M.�j�@̈́��ȭ��-���AH��\:ࣇ�.���8�Lx��y����� �c�4`���"0C����Z�٬ɬ�M��V�b�]��t^�j�d��Y��r�m0�Cħ�ؠ�ĺ�[�%�1��IT�ORF����EJ��#���]���۶+�H +1-ec�Uɒ`T�4�i���$?��yU+!/0�NXn]bw�§ˈ��U1�rI����Q�9L�#�p���#�M�Fq�t���n�4�#)�=��g����'�d*`F"t�F�(�bJ��)D�ӵ��4໓��@�Y�� �ڨX\�Ko@�y�@|4Y�E����^�i�1Ŧ,`�����������f�������;�/�����n��䛏�D��`��a�g�πcDB5��։�,xE�@��^�#UnV,�؁i�A��C�nu�]����]�;\4ʒvs��~|;\"��S��Nع]@|�u�۾{�k��6j�S�AY�{ ^�� �4�4�b�6F3C�3�\1�7� ��<Ʋz�%v�P��Imɡ6��G�=�)���E �� )���p�Ee5W����b3�{p,�x�'Qd+Ќ/9����V*�;��>�%��:=��=t�H�����k�� G��%nve]YΑ�q,��eA� {#��j�ycK��,%g�ZPu2�ΐ����f6���Mm+mU�ƴe��G�·�o��^W>UȂXI��%��.\[�d�V�U-X�]{j� �KtD���������9��eծ�TK��P4pY-�&n4�w=|;�O��s�  ����P��[ͩ>����)e��R]�$��j�* �u�b�V7���+��NG�����I ��h�z��,��� ;gЕ +�̻�1�k;�3��<�f +�&~;$E>Lфҧj̅6L�Lz [�U\�=Ʀ�)'Ʋ'�R��q�-K�Z����� gq�u� ��“�b���}^�L�� ��\��3Y�3��7�EKT����Th%�Ar�Kڂ�rォ8}i���cOC�̖�gO/u׶��t��W�\�U�pڢ��z�>�k�dbhfn�����#��$�<:yY(����2>�� +���(@3��k��%�@-]�ԭ���������� �V�/'�B��?�zą��=���P_�έ�V3�œ��-p�-��0ҸE�D*d^NU�c�S�?��`�:+=�_�,S}my���i����kkʬ��G#��V(� g��VαBv�y�*=�K)���u�z��R9\�w�8�5������j�nv�ֿ=Yk� ���{).t��U�c� ���p���7�]'?\���\�$���Ee�_���0�uI{ن�<�� ���o� |���o��<�D���cj���Dž}��i_��HWƑ�0�1����r3P��\at�I�Gvj�<<�\<R��� ��-�bW˛S��J�o��6��v(�|��i��o�S��ϟ�ߞ]\dWg�'7U��v�ܸ��h@55� S��#�zTݔ'��72&G�z�`�>n?V���~������ ���۷�]� ſ4G� �˱��'٧Х�tO��w��|�J�o؞¹}��[��__!��am��>[/-2���C=����+Gv��`ɺ��:�߾*,�dv�u(����l Aa �D���-���u���g�"˵g�\no��[�IP6 B�.�����T� +����(}O}k@��Q�� �ú�k�m�׆�M9�v�Rb�.�w��p��T~x]��\{s7��_����-�Ԙ��d�P��HY�9�ˢ��rR.p$�\#���_u�������՝*S���~����w߯���W_�Wp!R��� )���-~��Q)���*��̘�Ќ"������g+/8ܨ�]3��Be2aV( ����#�d�5(�q�ҰTڱ�b�Y�!u��5�K.��pN䯮'���a&R�������.�.�����a�4�$Ț� �L�% �5�3��c��h1_XPkɵY��`�K���G8�� +6*�K)��oF?smp���0� ���O{GOh��m@* ��u�b�� $�j�J�1����y �扨)� -Ԭ< ���XX�:�������C�磰�ы���W7������2�ƀ��Ȅ� L7�V�T�l�rH��Gj"� k-���g�`e%[�2@I`���� \�������Mo.'�~=�7g�^�]M.�o��<����rry}u�pv�7��_�W?D��]p ��J�"���ɓ�5�V��̊�b&bH��gl�a�n�&oXq�k��R��L����ڂ�<]��=��~4Q*�Oo���F,�jΥe�ш7�_k۬�H���i���Ap���ϴ &��9 +�<>�c� `�խH8� i�����Q8 +`�B�'>�K g�q���S���p����ٌk.-������o �DR˙Җ�Q�H���N�B$� �.V ?�O�<לYg^h������,�A�ucGO<��n�% +�\O�v�������8��e���{Ac�����'u.iYh��l�BŨ�B�>�,�����,�\��8y�9{0���-��*U:����X��}�:�5��˂�{`�� �O�ҧ��]��,$[��&�cnLY����~L�%��?��3v���'��J�m�5dǴf��qF���{�3&�Ls�yc?��N͓�Xe�W�*sf[WYf;e����������Y�Zs �sGo }Z6��R4!��ꨔ<�N�;���+�?���VԠ�eϭ����q���aɍasޏJ�UlF,�;~�*+("S �>K&3�R ��"{���I��������w;R�sw�y�n�.� +#�)��sXs0�B-�Y�#��+� #�h_�& �Z��u� +�����_d/��i����!��_�m|I����0�[L28R�dg2q��iT�/?�"hN,����ߍBJ���;H���ܷ,��M�����\� &�Uʙ�s�)��4�}#�^��Br>�Lί۝�!�6�V\D�1�/ $ʭ4[���$�O�.�ˀ�r�1Yp�g�;����#�2�Uhs.�O@�0M���6�XO�#�i,���C��(?�ݡ˸�:\*Lq��B�m��C���C�� ��1�ad��tH�.��ǃ��&�|��P8~0�Ʉ�E�\`�BN3�{��1b*Ra7COjD��n-L�^�x>yw�z�����[-8;2�'�*�7���,�M�'��Z+�����W&\�&^�ε*MH��L7~\���i����%��ӥ�����(+�,�� JM�o�NY �CGmX0S�%���H�AfBA��TfpKn*�E4�y�89)�U�2U*��Ku�dÌ��7�Aϝo, ��R"��.B71[�01 �7��47[H۵��1��&��5�� ��£�x�Fp� +� �qx�m`�,��|���o� +21���AT!B����� OS��?�Ș���/�����_��&k��\z���ߔ{tQ&�zz��Ad�ǛD�����7TN�&؍�oUy�r�vs�g]��Ϻ �uY>�2|֥|֥||֥y|�����NX�� ��@VS�5S�������.ӹ��3>���fLHۯ�(kQ"DH���|�e���cU��� ��L��gx���[Y�*���2�<�2���!�+ۂೱ诣ڈ3�"�+|T���e>�ϙ��pW6K���#~�i�Ϙ������= ��`h�G� k��a<�v�tj�(n�y�&�^c��r�����4�V(��<�*�%�c���+ڹ�Z�ė���u�P��_�|˰�=Ɯ�|˰�ʘ|�i�܉�!|����%BBDž��P�V <�ߣ�$�E����]e�T�0ˤS��wTu�|�Sб?� �\������/JC�1��o���◓�r�_ Ue�B�@0|C�/���*�Pv��@�o�^���U� Vi-[�y��^�M�k&-n����+��b�ll���*,�9��5� k�;��6�'[�(P��S� �����g��n�,}�,���%]չ���r�V���r�� ��}�A���������~�1�xQ%�#*���"H��{1Dz��N.�"�M���n����"�vW*xn�9U �:��݅ o3Ț� @�w�\r�:�w����%�s��w�'�� r�DžūY��}�@�X��.A:��~��7U<���ۋr�JJ�ZH��)Mw�m�}%'δ.��p�򘰏~l�!��kK;\^�Ν�t �nz�����YmSZW|�b��յwĜ0e����X������ tDK�m�:��-9����Ix0��9�e���>�1�s�\*ͷn��wsJ~I�J0ka��k�B� �\?'-�B&�>۪� +�_�T&��O�݈�yr�];5�X�NsÆ�eo��7ѩ�𖥮�e��|�>7��R����C�7�ߕ���d�}�dy��S��4�'r��'��<|o�x� K��9vzF=���6�~�~9y����7�r�-K�d\�x�I�e�}����yƟ�o�C�����A�D��#�O-x7�|%~�((�x{-� �>1$���YF�:�mz��%H�b�T�үUS��`A=��Y8���c/�ͥ��'���u��ſ�w��B.�pC_�L�~�)�z�I��~��N���:w �S���{���;[E�5/g�-��Q��/����)��! �Q?ґޤ��nü�Cm�42 ��'�� �`��i���w*^�N^����*�6�S����16��B��iڢ4ư�|�άR��?�_�/��?}��2�l���e��3p(��ȿ�d����Z��VK,�}w!��}����ED�l_t��S��J���QZc�R��h�Y*~ �(B���b��L�� M����[���x����a��6@gs�G#xJ��l�Z�1<���� ���PB�7T3�F����5lG͟��1<�a��gv �����a<��������;��m[ q��u�����\�v ����n����҄�&�m�����Tq&���Ŏ�� "���9�f��Ƹ��2��@8�a�DvFՎO������@���*E�V9y�Pn�]����� ĩ��%�.�4��r8u�ŷ;�5�g�2(��X��mk�<9)�棻D��f,�h�QR�!GX� �1b6�,�6c .z��`�=�n!a�9����q��h�� +)g�t ��1�a�լ"���h�o��#�?��߃�d�9�������aXG!�40pW} �,��I�=ک���}��U��Nscʌu�w%����;hU=dH�/�����`�|.��茅y9`?+�x��7&7~IEL��V Եv�+��17�����ImI�%k�.X�j�g6�ܜ�P����U�����\�4�agE�zC4�D��V�A ; s�nYZ����R `�،3m0X�n�ܰ���;^�4�0m| �;-N���n?e�+*��i��T�;�� <��Z$�G%�!\�t�� +2��b=�ݶT"� _���ڑ�ݒ�Dž�t�A(j;��W���s�]�m�϶Hf���.�#��+���t{�O��e��,]]����m�[Iխ��Y��/���餦���D�m��s�[���i}��m0?�[^6��� �J�e�C$@Y�2�1; ��m�`P+8 ��O���*�{�\U�Ѿ�aq�9��ܗ���=���PO@&�����Jo�U����9��p���,K����U�� +MV�E� ����=Q��ߧK� ��.ۻL��C��X� +�މK6+�]�lV� ���,{��D��z͛iy7Z��|g{nP���cK�p�FN��!_l3<���~�woqƯ��+�Q�ZuUq���H�ګ�5zQ�����,Uš �Է�Q���Ϫ�5L��PCU��2�7 rEt��m8�E�0p���� �oS���e�q{s��R%b&��?�:��E�oxu=�2�Xew+M,�� �70O�m��3���B.V3ٕ�'�JOF�U�� Ȗ��á�Km[�!�[��'�n�&݌��}K� �c5•UU�5�v�zK��jy��}3�ux�����M�K��5�O���a�Ÿ�t��*�����ݣ�b4%�]��gkW��7��t����;?賚���H�>����鯻(�>[ +��l�5A���]�sA �c4��W�տ�ޞ �A3��'vb�/ +qN��4�;+|�� �:�P�Է �ك*�]��xK�LKQ��=r��;�����[�o7���b��>I�,�r8�;v�&�ո�"��! j5�ج�{$ײ��0Cr�W��܇G@by���o�C��7�2�?{���R��J���L`��{�R%F��F��Fc�BC cF{��~�&g`O�HE�D���] �p��d&�T �/&�ȒjP �l�a��[W�if���Q�Ј+L�L��������0�1ϟI��� ��.�.����`�4��L��"�̕^1#4Q�B���t��biA��f)��-�2� �G8,klT�E)I�1��Q����vɓ�����)�^� $�Bf���ajA&�UK�D<�K��1��'��d3, +�yyK������d<^��#� ��^�����^����>z>:s~Jb44�'�g0݀H�XFb#�bMdC���ZZ�,�4�'(��PZ`Q����`�_L�j��]L�&C�������nᗋ�o/�o�^O��-���~uu{us=��K���7������!��KԀ)y.q*I�8+�S�����ɤɹ� �" ���G�A��^IC�5 ��r%-;��� �B�|������t�T�3����2�̪&V,�c~��f��L�\̨L�O��B��(�j,�J�R�GS� Yx��j��"�#'���/"5�s�v�2 g��$7�M3�����/2���L&^�U�>�6��Ah-6}��i����0�bŜ�ѹJ�vG�J���Rh8��Q�k\x��O�P�7�I�g��S+��Á�blW�x��T�z4���� ���W��>L���T����eu�AM�AD��H8�c�^�>ȯ����<�� +�����=j���H��ߙ�*��+�e&FL���wZ$�>&m���\�~���=a�HZ�� �?�m�Y������%0dJj��w�O%u\�D�e�{ȏ�V) ����X&�e�s�XrEr��s�z����,�Иy��,��m7�5�Bϸ=���<h����v7�q�ދ�"���Άu�Gx䒦H˥Edb�`%�t(��ă\e�Bm�"ΐ��QD��.�Z� +# k�u��� +A}��B,U< �C�H4~\��=Y�c�p������~���ZY�� +J�{��'Vi��ˆ�f"v ��1�E�3�qĕ�np ����LJ}�*J�K��^��qL�dbP[�灔T��LqNU#-ņ�� +[�w?;�g�~)��� ћ +����Ρ�����L��JD�k�J<����MPdV��wΨ���o�əs0� ,�VY���A�;چB���l�((j�De1!�]*�T{(n��s��N��P���kW���Jrz:n�{iXm���+�k +JfŸbD��:�LkL���2����Re�~�GЪA��.�y��� �m�]I���VbΜ�q*7��`Z�(#��V+�֠��2u���[��&�N'c�*��7�8�f\ 6׫O�Z�%��W>c��ݍ�1#U���e�,��)b�1)���0ù�b�2 �EDl�Rإ��(�����*'�9�C�`fM����d�8�3�~����KZM}%��b+����I���0b���`�U�.Y���k�M&�=i����ܹ:ր�:� ۧB�U�a��ߋd������!�S�=%� +��??���$0�5�rY�=��5*�G5NZ�R^��M�l� +1��n89)���A�i,#�g �$���F��,���z���&�`+`���1�b"����L�>��Ɔ�p.�:���� ھK��Ih*J�w=�z�����Ww������7�1Z�����B�T3q�ֆ7VA��+%���]+�����m)�W�w���d����ߣ�����ke�����)���:���I������*9�i,%8�������~�1��"��[n�܋X�r|����[�k����D&��$Yѵ?D�]=Q��L�卙�7�~�EF[�Ϡmq4�E��C����Pi�(������|�z�M�f�.("�2^;�����a������O���l��M��a����K a�l�m��W�Q��o�f��1C�WG��R?6�p$��6 Ƚ[3��&�����/֡ܯ��x�jy��� ��9`�U�����Xa؊�m���޶�>�ږI�;P/���#�e�)���D��Q.�qG�]P�b��:7ki�%T�6=?!��I�ݓ,W%��&��x��� UIYO[�>�m�S"�$����*o�j���� �� ��ᩣ�_���x��Ԓp�(�C2���>/���>�~�u�b�m���zj��9h���T&�Щ�Z���%� BOZ=���rֆ�< ��؄�+'�<��j�;B�5҇��������_�����������R +����gp�.������wA��1r7�Sw/����џ ��O'�өo�L�ϢJ!� :�%<-z�С%�ܳ�/���4U�Ч��6�MQR��˖K�B�[��R,IV�m}֒vI�'�.3�_+g��gk�zɭΰ���� }�z����4۹tfa��>�6?����?Us���L�g3:�F 5cc��m,�]]��?z�ߣ�п��>�+9۾;%�������� �G-��ʼn��F� )B�0R���X7�\tbIު��=��D1Q�D���+a0�H�1�r�l(��Ɍ��E*�K|jTa�U{�hM�n'���W2�����8��=v����W��?�芇��Wgߠ_@�n��������R w(O�t>_� 1�| �i�b��|لz:�g��T��T�X�S�f�[�6�;�^�� �A+�5�E���% +KAyrBc���_d2Skjgt0�ђ*����t�����3�x-gv [9]`Bg�\�pɋ�Q�4�:��mW"*�����tCG�����ř���t� ��0�L���H���ݎ� +N�p�ya�e��]���j쬫Τ�7v}|F�R��{:�ɦ8��w<�~'҄�!9{��~�3�k�пt�����P�Ĝ�#[��������rIw�0�6L�xW,��T�����t��{_:j� 2t�C��,E�b�3N��h�_� �/!��j�rt$���\Ć\à�g�J�+�)�QK�EyiH��3��'>��͞�&? ��nG���z� <��H� �S�����㬢RM��� �I2�RRm���p�8��)xW�Jx�b�~TS%Ǥ�H��cJ���S͸`P �@*M��,��Wr���o��=�S;�,\�@qp��߈Y�br�8ع�,_#�L�2�6b+����'�o��\������m��`�*k�F�Gv��_��� +�������ūf]�-1�pe*��^��.�]�bn�=�SX�Eռݜ� +�����~}��b�����4:�}Fh 8����'����������wi���d���;cu�I�P�4�$B��ˣ�`P�iz�V���j������cBD�j�'�\D���9�>{��% %�d`G�Q��[wO2���%,Z����i���&y_���⡍���}�e{��|�2�L�ՍA�����(*]#/E�C��CUU��AB]����4+L���&Q�҇�Ï<���~���F� ��,��v���нJ�HH�z�Bo��^^Dq���T���i�G�X�!I�P\"k�-�u�,���ff�j�~��ƏB��:3NK]��" �.lRlTC� �k�'nǁ&Y��J��>9�T��x_��!��ozC8.Q/�`vl| +��K�MY�*T�n��?��S�/�����g+`7n�����?_B ��П��]5I��m.v���0�+Xw&[5v]�ى��9���@v��E89�*w��,RZcd�M �V�՟��C�:�ݤE��=���~}�����������X�����J��O{ߜ���X[o�6~��85���:��`�eҤI3 jtjbO�b��ұ�F&�$��//�dYvfg$QD��w.�?�˼��u� �y�Ka\�`��W|+��~T2U���Rp#��z�K���(4&`�%��Y�D�ʅY3�p/ �0å������HP�H�R�J*'[�yA�3�X�W(�L-��d6����,}µ����,�,���T��� +X�p�2�b!��*B� +S��5��F�ti@�*���`F�L�2�1b���,�)��3�*M&����YZ��_��/,��m@H�ƒ;�s��. ��<�LĖ�[��1��3�s�0k +�Eu0C���4&E��zȬ�C��(�}�ލ�w�o�o�'��֠�?W��|,�3�y���5E��� X+n�HD��a*�T亶A +`6Fݛ)��]��f:������ɧ�~��p3���0y�����h6���0����D��h�~���sN�%M9y� +���'�c�<����`)B*�P���Q����j`"�������Dڰ-d�u��Gb�Sj&ef)��o�I�3��k[����� �ܰ�o���6L)��Qo����/��i�������]Y��h*��aQ�;���8���A� ���k�ۥW�O}\���� +��Ur�U��{ɓ��l��68�^�`߱zi���Pة�c�[�N�\H��70f+ � �aKw��J��?c���h� IY��Y*����'�1�7���wvb�����ȱ���������� �� E�s�FR��q��\��'f�Ի��]m��z �Rލ��)�S4tp~M/��(�J 5R��3���r]��������x]�����e<���Џ��a���R�$]�W�b�n[������K���h� U����r�� �Ս���^�ίV�Rln�%��*��m7~]]�6vNݝP^jJ��9���ow�1��״�n�5-�� W�B)&��|�- �v:�5K�/CG]^I��HKK�����KgO��sY�a��R�N���j�߻ً�"O�����A������tz�a�����Hw�=�� 0�t�Nw��|7g�����S���%�0+ξԮӂ���=��*9(wWG����o�ޣs`y�G�x�[�����_9 �b{s����}Uϐg��7�>Z����\`�����mQ����j`N�=h�/޳H>h~%�Ծ�C�v�z��x��/�Yoo�6�Oq����˦ɚeɳ��!vW }����6��H*���?�#)ɖ�$E�%�����?��`�����+�!$Z9!�Tsp �7���� �N��N��$���~� *�)8��Hc=s+a�t�R�V�=_��P)� +�ZXj�w5rZ8m �A� ����1"��L�/.a&3�O��t��J�������fڀHSI[� ��i�dA���\���Lt�6r�p�W +�]�|0!U�WQ��m���.�*5��1��K*�8x ]�`�����w��K��+ ���D/�L +�0uЮ�c�g`���1� +�Y}G���p.7�V��`��̇Q��o�����񏃷����Z0�O! �0]���L&b�!dbEdGqH+#�T�>Q�u7UF�"J��@+���1\���������'��|����������r 7�pq3��zr}3�����$��^�~�J�@��RB�dQLk�e�h ~�9&r&Ȅ�b�0�h9����Z B��ɥtT�H�E�|�ErO��&ZgL��!D�p( +�稜���*��n��̒B���pL "�/ +��RK!�`��Sa,9x���\���+4����D�xFw�!k�L�D�yvp -�;�H�5��L3��!����3sc�fT�@0�@%@f4bH4MeA���BP��:yP抉P�7���S�����<�^��@"h$���F�:��p��~z -�wQ�b3���)���]���>����X^�l�0�#��f٫eP� ��7�.�8O�GP=]����M�Bs���)������.�HhG���e� �f�da��5� 0�٩(v�E��t�c,ʧ/\�@�ԕ{,�=�Ć���h���b�O�G�=��v�ݕ)��w´��n[��&F��?G�� G����İ� Nş��}�>6M�?��{f>k�Cu .m�b���*߯�ܮ;\>U�(�<�I��~�*F����O�'��z�y k��}����g̊��`�7����o����� �;Sm��=@P�f>��y��`w�]��a��"]�az�- ,-�]��� ��DX�0l��6m9Z<��B����q5�q*S5J��<��}I���FĊ1��x�]]xH��P�;>%8&xz����6�� R��xy�a�A��[��ώ/p�:�%�+ ���� -�wC�7���''�� [0�C�D ����z %��=nz�ފ-O��<::""O��aQ.�ւ�b��7�*�����,�Mg���[V���7Uʪ�L�I�v����*Ԝ�C����\�]�߽8��i�ب2[u�v�uBN��YA�oi�f\}F�1�֠ع_*��������������v~�;"�� ���Y����7��০xk?��w�����jj�[�e�"�zB�RN-���s��Ӎ1�C���5a'������_�R���"~��̂�� ��ٟ��t�����;��=U^B�*z&?�fH@�Lq��Ն���\�Jo8�=db�&v�}P��Gg��at� ���&ʕ� -�4K��r�7Sra�3�z���+��h�􍔙�=���G�����Y�R�����np����dSp�����r/P���s�5q]�����j����&�wؽ� !( -+���!s��{'@kJ��9����.K��6��c�N��J�KS�Ŀ_��\��EYp� Ō+���B�jhS5�O ] -~� ��T�@U�Tl �5z�OF�wW��+�o5��[���̽����ʦ�Lh��jN�Bn0���IE�^y��WۓrSo?F�Pn[/_���Z�s�6���q+)գ�}�qb9nb����7��N���@$�B�:�����ow� ��$;�1��H��b��]�9[/�G�/�� vF�I��0�K���?�wI��H\��,�<�q9�ID�E,$Oɂ�+5H��0�3�&D�|��3N��K�.� H�a�������VH��g�U"�2�fH>�_H!V"NՀ��D��fr���Av�? ��l�t cBŶ����@��f!�� cx�"Ap� � ���� ˔%[�P-�5�*�K+�҄-[��!Ɍ*��1z� �*�|�:0'���V�5�^�')˔ȩ3�{ �)� ���Q��f���7C$��2N��d^�x���?c�4]� ���v�I�A"C���g�����B�9�H(��oJ����5H�)��-� -9H��`�x����:Aq�r�YA��0�5j���ո�~8_�{�׫�O7&���������Ř�ܲw7��&W7�pw�ίÙ���~�cL���k�J��!ZT� -�de@o1��"�a�ŋ�/[$!i���\� -�V��3p�U��S)�Z��`�OH��I�D4���x�p�����|1�+�_�]~H$��T�I򩡍v��%�s�p��'�*�G�X�T��U�TX���dE�R)��3�D6�W<py�2��V����� ���'S!�<�`+� .e�l -D��b�[fã�+pe�i��a�����"'��i��޸�?��+�^/�����f�ڿo7\� �wC�klÎC|���L���\��b[�t�X���"܈ذT�]s�We��h_n�J�,hQ>~�D�" �N�|֥�ڂxcl���Sg|�X1�YP�+$ th�VY����P�¥�eM0��D�Lf�6d��O��j��$Z���7w�Z�d�P>�"�d ���%WDa�DQ���-�IqR�M����� -07D�c����tT~�=o�I�$�Z&2F ���J��=o�Ko0j�c3�1%\��1M'W�CGmG��S{y�8Z6d���A�� ο�������â��� ��L&[����B$�DEѡ�3�0BB���r�G�JY �H;20�NS4�MX��Th������%7M�e���3M֣F����j�T��买;2��DS�@]g�|.$�#��\'��ڗ �w�aUK t��Y�Pl(�K�)�R&�b����\���d!(w��f�@W"j73�o$4Ax��x�aD��[�'+� ���t�f/������R�>�͍���<�!d�za�w�}VYwKI���aY�+�*�qŏ�D�-(�YZdžRP�\@�;��4��8�V��k��B2�DU6�+Ar�v��W+���vMl|~ku�Db# ��r�W�Q6M�n -P�X��F�7T%^tK����Tf�7��3cZ�|��,k1�!���F�S6��E�x��,�`�*Y��|j��.ظ�[�K��8�f�:]̥�! kt���^����D�F�����B�� h�9�����خ'�/�S��ф��)�v�M�ƴ=��k���Q��Y`"�e�I����C%L_$�.�2K�6 -�7X��M���O[�:��3=��d*OS}����a�5W�֣��KG�)D�� ej�� 2&E�(��0U��� n)u; ��� t�6/�mRJ���i€8[MQ5WzN��.-A&��՜�kEvc>I�O���_���S��]���{w(:��3!^�Pw�x��ů#���o]�f -�E���= ,����K�NR@hPH�:���3`��$�2�����)�9x�?d�s�-!]`�c�n���(�H5/~a��"�X�@�ɦ���8zp�D�t`�� !)iL��-�! �hPL�#?��8�\4aLX�#|�>��`ŏUPaP����1���(��5�mWНA��LȎGrZD����pڄy:>�� -O�9zС@ǎ>��k��.7��N�SK�Ȑͯ�x��k��xT�@U_��T�+]\.2,�Gp֟�w�j��9��l�� A�� �T��L=���N 7��U����J�q_zd� -V�c�Iy^9)�GbK��]i�*����΍ij�}��]��W�;�¹O��b -���x�Ac� y��7��5jUc��Å� S���a]��� -��tHON&��������9� *5"�k -f�T�z��������ky:��}�VT��w��=͸g�7��$���'�Yݪ��̸&kQ��9�ؑ:C���w����"G��n�I�V�6��ƣv�8D�%�/O�\� &��)��B�ЀǸ�%86��j��7�� �v���[� -���ZR�u��W��쪇_'���|�Ц�n��t�����R��� +xb!T�*QΤ���e�S�R�`l���$��ڪv���Kٖ�~��d�����]�`o�on�j�� W�:����ty ٿ�,���<��{g��Ja����ǰ��dC��C�G�ި��&u�|��%'�t*�8��h?�Y��G�p�ҭ�� a��΢i�;jz�� O�X�*�-)�)��6{z�ga�S�}V(7򅨯^XJ����Ŋ"GLĦ%��Jǯ�l� y�v�d��a:yG�(���O�k�ys��a�����n�ӽ���M� -l�k� ������ K�F*S -�T���.�*��p����[�%�d�?�)�L��}�N�q|q����]����)m�~F��T&�<��P�:�-;�_q��"C@�\+����������䰃�龃 �+o����]9o�S������{�'V5�/`�\&a���O�(!�� e|�٪[�ŦQK�)l�\X��6��|�����t�\������at�e�����s������-5��uaT��� �*���g3U���n��!�e� -Ԁ��E���.� �خ�� �T���T�|��fl��2���SX���\��#W\���7I8��8h�c��6 DW��w9�m��2=}���P_e#�!�V���'�HD������U>V������\�\��{}�)F�s��'#V�?�L -α�X/x��.�Ի˭@�s2^:���㊰Б���[}�k2Y�}[��Z�S�/�lXLUm�9 ��!��f �?E�C�?�U)h�テ4� -s@gW�(�wgR�N�͙ÛC����ЅWm�wu�ړ�u� �&g��:^^����ظ�����BL�/gl��טlO���̦�{ ;�m�j�b�FG��ks�6����RF��~T�n�\5ב3��N���P"$�L�:���K��ow���H�����]�� 0߽^-V�/� �.I9��Y%Y��Y����g�&�D���U�����}��?%S� �2�٧�h -���g�&*8{���8"����.���`y�:/�2/�E2Y��"�Y4/8_�}�.9'���ћ36:>N��6I��9�`���a3@�q�KG)K2x�$B��󨈑�i���|Q�|��B,��w��\����X/ �n�b��Z ��~D����Y& С=�$�e�eY^���;��|U�@�r�&Q6%h�]����B�OP],"VX>����T�blQ���`��l���ϋ�@s8� ;�<;���YʅY�{� �ɖE+�iM��4ڠ�HM�~�aS���y��6[IFd�@`ܞB�HC���lty�~8�]�د����b��^\���Fg�����9�]�����;���������R�ê@&����c˚4 h+JKbŧ�,�{�|�9�緼 oX�b�T�"c0�eR�I ��}�{�� �R~t��)A|��d0����`��|0�!��k�� - %���Y��@;���_|Z -i9��o&JtLp݈E�|]�} (>�a�fb�([�K��ASp�?z�A�����4��,��M��M�iN���ݜ(D+� d�>�g閭��*�v�p��>�iL�����U!s=zG롊A� �ᨘ�)��B�BP�"����n|���̦�<��9��L,�^�<���e@��cC-F������y�͎���E�<��� ^�\¬6�/�^�g�v�]��D� �W�T�ϢuZ�$=i �W��?�����.Ft԰�-��� � -0_ݡj�ryu1��Γ�2��Lq�{�X�%�$��Ab=�ء�-�9ͅ�4������RP�(�6r������}_I�'Iq�ࢳ"l&�y�m ���j6R���t,�f�ќv�b�{5�0-ע$pc���U��fP�,Hv"~�ʴy/�#�S�5�tm/���6�eT��-��w�$��T�=3J[rW�k�����|^ B�ZQ�9[�i�e��K�P����o#��"�v�c����U�/!�a\6�^�k��+�Z6݂��P���� ^/��C�/ �Nw��/������=fC��f�{�# Z�nм'Y-AX �F=���Da��ur�a��ɢ�� -՞�>(d�ɠ�j���J]� -}�&�����B��N�,�BY�V��\~���g1d%�X[Y�Dj<��^�@m0�C�$&l��뤤"h�u%T�u��G�؉��[��y��W�� c�ى�.�7���HV�7����컳��6�`�'�V�s���e�r��u��暢K9�!��r6��Tp���Ǡ� ��e�j a!B�P�`�R�"� @u�{�n����������s�?:X��T5��h5���C��$��CQ��9{l��J]dFS��u�ƺ��8�ܖ���ڗ΋=��^R�-=�b��}�C�Y��ߤ<�[R�,`yE5�F��ע��� �����IPJ�6$�u -�W��ȩ��� j�HU�p Y@�I�5W��v�lzP�Iv,��ڪ�e��W��>;��m�B{�cwkIsKH�jL���N�������|�e -hE�jz�� 5 ����P�:$�_�xr[����5/��{a��B��aA�R���]�1"�N�HU>��Gy]�����Z#\�D�&ڊ]ڷ ��.��|����Dck~t��� -�jq���nT��ޠT �S�z;��5؍�a�t�E�o��xWK�Ag�|'YIOQ,�x*ʨP�o��wx0�MQ�ml{�F��2v���u6%��Df��M��vt�3/)�y�(N��tѬ�:�^�NSk��*��y�����\ZDt%)�}���˼(�=ٛB�g� \5�r��H��TjԦ��`�:��'�EP�_�g����0��l��� �����O?"��A*{�6%�ϼ��lXRi)>�{t�W���ᐼ�q���������&/Ykv(��~��L$�}H'�B�%�= �tD`�j��5D�;D�<��!;���D:{m [|���y�h�ŵ�l�^� U�ւR���IZ�f`(�5#����� �CL�:��\�]� �X�V���9�^U<�\ˈ���J�Zo����+ao�}J3|��JVn3K��FoXrB����kqb�n�Ú�\��=�0-f�sۥOZ��q��v�f��w�kg���:޻Z#�4�U{�dH�C�W&�G >O��� -��9���U�@��Jl�K\P'F�6�)�3n �X�IJYz���THv@�q������lQ$��ql�[O� �d(�g�?�����d�/�E�n�$�֩�/�;Z��7|˜��Ԝ�1^غqӖ'џ���@�H�^:���H��61\��/�}�hF -yV��6{D0Ԩ,?� =� �X}��K;ӧT[VX7dy�.��d��>ʢ�d��U;xZ�Jbv�ꓚ�:�x� X�;B���٬@O�a|� �(��4�:04�� 3a� �� ��(+ ��S�GB��թ�\��Of��kz�m^Y#UԴ+x��Y�i�:D��?�.��L������4�%?�Y����D<�Y�X��TQ��� ^����͓���o�a��C�V��#i���PO�J�P��^masO7�$�R�@m��Y�du,�nZ��ڃL��0?�FG>�Am)�6�o�}� �~�[�]���krh��9Ӿ�oڷ;��:��F����Z�P�&]n����G_�[lV6�����F��R�[��R�b!E:�լ�s����P�b5��97�KU���E��u w��&۳Z�y͢�@KE�_S�Xu�cR�Z �w)V a�(U<R�`����}i�15���e(�Ƙ>wub�̣kϒ�Ҥբj��7��5��v$&$}�jD- -�K�����#j�z���Ű~�:� ЪBd`� 1h�7�zI��k+KK��A�y�z�%��m��#~�z�ZĈ��1��>�H�~hSb$�g�)zWܽ>A ��p��v����|�LJ�U�[��IL6^ Z_5��]}�+�o�v�k��[ywϾ{�h�3y�\���P}M�Z2x�/t��D6�V��4�[�Q�T.L�> �F�q�Ѻ�r��eP���*��Wq.2�h���󒾻�/1���; ->���r�IZ�|�j��5G�of����U�{[6�𢡊q+.�8��uG͒jPF-�;��~��g�;� �a9���Ŵ�bR^t�!�{z���ړ�k��v� �>2A�x.�|ϤH���`G㫇����5�z&�O�%薮�0𐀛G���ѣ6�{�2a�/�-a�� -|�jo&`�7�( -��� t�҆�B0*�=t!U��e� ={�e0`c��N7c�>-�}b�ؔ�H\ڧ ��≼���51T�`��f*D������eq���`��A�.ld��Du[lI�A�y�@=R� [h%ۯ@�XMU�{*s��%�Jӆ��"������<����Ke�)�r��BL�-���xiIg�Dv+�PQ$�%����9^��� ������ �]Q�L@;Ke�7�� ��턛��oGw{f�D��+��Q�v��d�!��O#�juS'~�e}��_Ϣ�Vs�&�.�������Oi8k�٨��iX��������㜫ϙ͇B�'�Ծ�tՙ��nS���b������?h!Oh�d��g43��R���)�F�η���'&N���nɷ��+ �$��:���,�q��r^߉i�� �����=��J�Q���,�rQM8���d��u���M3h���~w������W�n�F}�WL ��Y*���N��F��2`) -�����ew�b�&�ޙ]ީk�(a[wng�� �ۋx_ �>���p/B�� ��0+���JFZ�|jX�3口81s -��� ��T' ���#�}0�j�b���T.M��{�� #d������+�'m���ʅ��"!�,P��yd�`ʹ5?y��_���&}_h���¬PFhH��K4�|_�k�����B��� {2�*� �4�J�D��fez�����-b��$�RA�%�s4D��] �������j��"i Ѽ����c��b`�8,�v����1�ˌ����rYfH�~V��φ�4M�<�*���1�����뼉B�5f��D(��b ,ƨ<��XC�Rm�,0�Taƣ�O�:'A�Le��zU��l�.GSO/���t<����쏇73x;z|Mf�)<<«���x6~��{Mޑ���m8� ]�"���r�§<bKV'sO,��� a�@n���s��J�1H�ƒJ�j [�%/1��T�Y3)C��r�1e8d���΂���pz��К$�i�(˩a>��F����۞ŊĬ�V>�9�<�^��abmS��9i�k��#�yT�X� Fb��s��8���9Q���\�s�� ��*d�V 6<����]e;L��>��i���6t$M$6��Q f~�sEɔ<@n�5<*�A,�`K�Ѝ-� �kn�Y1=�cº�؄*妊�a�6#��)J}E#"��MHX1�֨�ض��@�@�_`]�a5���i�6'�v.dh��.�B��_c�Iɼ-�L��jQ�t����vy���5U��"'z+�E32F�c�XhV2 V��f�#}h��e`�$� �8��G9� iZ�%�i ����C��~��'",�ȳ�6Y��Xtב��r��O=kč �pp�Э�������&��)��–,��n�^__�Q�駟�c����,�i߫����f"��S�n���/��z�1���V&�^ ����T�A��v����v�kRv�c���_����ω]ݜ��IU���2�{�ukU7ij�p������3�� �?���l��-,V���-��R>�e�I��ʝVW^��Ԡ�l�b*�t'J\݈�ł�r\Ǩ��AJd���ג�&�M\>��;� RY���J`���As�*AB�<,��lr�G�iG��3I��E9籿��N6����*ZcR�9�ę7�mF��v��=��?L=�t�L��*@j�J}i"�:����fm�ޝ�1_T�=��@���HqO����̴Uz�����z� +M��nHmX߳@��,�n z5�>���}��oi|��)^l�R�?�1�G�����q���xd���w�0���q�Æ)�px��/E��_0��Lʝ�`*5ݳa\IO\1O�ϲ������f·�/�笟*���7o���v�B�8�k;DD���Ű�Hjɫq��[:e+��ܶ���o-�_�ɋ���WMo�6��W �=$ �*z�m�q�]�@�$�ӂ��2k�TIʊw�����{�5R8�7o�ҿ~��� y�~��V�VNH%Un����+��.�Z�B� �C�)�(��>��� ���J��5�+��p�k� '������� h�� �ڴ����(ڌ r�X�rv0G�駳���V���3i�8�H��5�B��V�Jd��EA��E�p��\���Lu�32_;ЍBcײ��\��6��m�-պ�u(�Wu c��K�e�3��: �=���K������R��%`eUH�R��� �_B�䖁�^���p�k�I�4�Hx�#m�$��|&f��t��WZKl�SKC/w *B��%a-D��� �!�U>�hE�oӞ��J�/ ڄ���x���1�O�Cx�,���/�q|w7�.&7s����lz=YLfSz���� G�5�^�2� -�*�ERɌb��S��j }��r%S*O��r�E�=Q�)���Z��ݺ|T-�΢5]Ր~'L��0V��*ܴ3��O��k�&ܴ�HOk�MB W�t<�~� �Uao�F�ίE��D���^�;�5�H��T-��lcﺻ�8����v��w��"��yof����]=J��GtNW�dJ� Jm +Wm�����py�=���!�сηx��G�s�I��^,�����yөQ�*h5��ʹA�,� Gl��A�^���#�$:�;>+�L��˂���!��i`rF���ǩ�8S�p&2B��$T�""M�~�� 1�[i�E13!���V� �d���X�LR�+���������I����؛�ۃt���)�}��`cᕐYa0,� �a:`Ñ.��f�M��u��J*i|6����?u�} �e䀇��9�n���k8 +N/��ׅ�a�4���*_�JdX<�C���D<�r�AA��M�2����R�/�)цuq��a@#�]J�j~z�����‚!�ύv� �*N'�{O6j����ߘ8���T��~�d���� +�fh��_�B94샀Nԙ�$�䖒�'h����L*�S�$�r(�8=����b���"6�EJ���=f7M� #�M��Y�M �'c;��}��u�FF�N��{e��ob�o�b��i{���J�z�4� � +�9���{��{!+���{���4��.&�h��:����U$� 8����J��g��p��1�)�A#������0K��ULL%���0 +���Ȋ�Z�۪ȲF踅�+N ? �Q��v�8_r7ȕί�׵gO)Z��ʭ��rF *�\i��x������]I�d�vs]�����m��+j��ODS��z�T��tAZ�B�ay|��)� ��<ڤ`�����h�]�I� � ����P�Z�[�D�Q���iHB�q������"�Kr��m�o�Y�#��t�D�u�/ h��H�[Zt�w�:�Gi�����k�;��s�aF-"�i<���[����]H#[L����O#B�̩4���::�߶9���`/�-��|[pй ����;@�K4slf`�#R��˅�z����&����m�En�[�60���.���r�>֝�[�� �HG{��m64(:*3{y�=>�xN����]X�ͣN_#�T��1�]��м��{��>aroA!qf���Ad�~�i�BgdLA�6�1�\�P�Y�)"确��K����i89���4��KRIa}��޼S��zjh��u��g�ݶ�Q���5�n�hF��h��m�����^��.t���c��+��(�7� :}���8����t~;��g;�V�j�=�⃶�V�j6��D24�kۜds��7l���e�n��Z6Ec����~i�E�}���v��)5E얉���Q��JGG����d�(��nh�F2�}e�P��6�H; C̝��p +o�?R�,���>��"��BZ�����5�7�h�`D��T9nf����t�mJ�;��0�&�𥂧��d�h)���l�4L��W�%����]�,Ϛ%|�y�Y�]�~:;�?�Xko�6��_qW�]����m�5������a����,�4��To��%);v�6]� ���}�sH�����{���{�.�Bȍ�Bj���+�3?7��W��F_7�G �F=`��e��aް�Y-� +afJ� +�pi]�����r�.Ђ�H�������E�<�XZ�j�F3Dv?��O�/���� ��J_�������P �($� +�.�]q"dhq)lAe�^[��<�V�u��Gs*ev��q�q +� �MK٪:6#�wh����k������g�Wl�k��C�p��.�ڃԐ�U���9[��#�_������R����@x��/�����xܶ�Hp�#c��T������tv�������V�X��� X�AԵ��X(%Z� � 5�Vz��Y���1m��R����A�����`2{ߟ�&� ~���z;�_ή�Ϧ��� ����j��d>�����Φ���O����Z���RƂ��b�����%��՘�R栄^6b��4�h�5ڕt4ZB��Jz�#ӽ�K^�"O�"���(�x}�2�ƛ%j/��1/%"|���!�$�9�X�Ը��?��} 6��y"��ZX�B�l��b�VLx'D��> �-��ػ�m6�F;�S���8����%ZΊn��g��]�B58pC�#�DKHݸ$�T�A8'����Y���Ͽ�u�:��K�.WM�UuyL*��,�R�eYS���K�Nj�P��xq +��t���X�fI�����I��ߴl�9�F��\X+���^w< P�y��`��EN҄Ej��e'�x���^��Cm���`����t�ŏ���[a�닟��gm�ǜ��h�{8 ;�@?L�'�����Jh���֢,g��@��6� �W�ݼ�- +O��ms�ֽ2o�7HP-E�S��:8$�P ���:d�A[ɼ��v��N�%/1q"��N- H������Q�mf��D���˒�Z��UwR;Y��.}�p��[�j��D�񄎱���֝8�~�E�������;�=�M���#&� w�!� �(��TR�� i���x�u�tR�i� +�Y�HB���5����:36+ ��/��֊:�]j��|QCN�I7����|kjǗ�F_�?Z�7!:J�sQC-V��{E��� -;&�#���ے/�*mq�J��`i��*� b%�Z�lFWjO�j����U�Kk=Hf�J�8T^B_h�]��~��Vk��񱛔$Q���M���{��g_B@&������`�1(�ŁB �������T(^����_e%�a|ynj�aV)���9� �[�f��T�m�0��p��&�l��V�_�Ō�z�~�O苈:��Ҷ�ޤJ,�����l�o_����k0oon"滗�+���郻 �ȦO��v ���q��J?�W�t�7�h)"�7����h$��?g�L@�l1��7&�;��>ҝ}Y���fCG��Ekma6����Fe�ag�p���lP+}�o�pd�E�ˮ���=؞17W�)^ -�1���d�e:��aԩU{?��pK)/�M.A+�M���� [�} ��.ñJ]7e\cJ��Dv���=�{(;g=�,J��� s��y��.��� <:���m� $�rhC��� ���� o��<���"�� ����(���k4����o�Ymo�6��_q( -4.\{��6���flH��]�O-�2Q��Hʮ7����HJ��F����Z��=w�� ����Uu6~�� �µ*%dF{������w��hgJ9�U��e���� -o,(��]�L�P�4��2������T"�?3��[a%\�Z��+��|2�~���$���V-j2Q� -+�Zj�F3)Y���|�� ,7���9�U~�g�����`��D�+2-J�� ��,�����T;����E/�JUhoN�̮'�����ѕ��1C���?�~�s<@B���G��,�;��C�d���LV�"�uU*�3���56㇨�,(} �0��1���?���Ջ�x�ݎ[����?0�7�7�t�y�K�F�ZY��b�BT�X �Rl)��(&��Z��.�$� �ij�� ���6�9z4��t�~�̦�!����}7����o'7��ܾ�W�7����� ~������}z�zC����ʒ�^���;|J�-1O���Z� ��E- - ��H��QI�V�R�d��X+Ϥr$zǷT%?c�?��XYscJ��y�2 d�t�x̯R!���c'���_���I{��Kaa�0������ʚ#0�`���F�QX ���,͖x���|�t��\^5�x�/��<}�����>��Ʌ��[�ǥA�Z��Փ����{���!䲒�G^t�1�s�%r��I�b�j{~:��Ɛ����c��"��~jc=�>=}�}�٦q�E���V��������ЏZ�!�u2Y��Dd nWAc��b�8�2�nb�ꓺ������DyP����y��'/�!u����0��{�����RZ�G[�� N���kCVP�hQ��Lh2i��pߴ�)��5u� -c6���E!� ���"��������Wќ�/�ͯ��d���;�pP`�i�i��S���5�2h�P~�G��r�4�,����Nޓ��G���+�1��Qܧ7���qT�W��L��!Hk[�����#>C�����&��^1ؑ�ǼC��f���x�c�)���p��0 �v-�����8�W�� ύH�S�u� �1s7�b�yn1I��|�¸�/w|銍�P�Mc��C<����\kX>l���U3�j�L��2r|���<|�xr��F������ݩ� -}�q!�����q:��w�����}{j>����i~׉!5���v��;���ܙg�A�i�q��ѵ?���ԧ�L��i� -7�Ȝh�'E������<���N��7^<�� -/�4����j��v��r��S� ����¶6��NC�}�6�T��ry�?jR{�l�!<<��M�>m��zw�ř`����,L�v�����5�2�:�:�2�?���޵�x����a�˜ ��iS�u�e�9�l��~os��� ���N���S��sRƝ�[�w�'14V��5A����wIl~) 9S��)�G��.�m�T�]�䭽���,������qA�D�SGZ�� �bxE!�0R����6����ʝ���� ��/�TMo�8��W ����=��m�I��k�ۢGZID$�%)�n���J�]-����|�������4f�����V�E(��B*�j� ~)n�r��L�޿�,����h+Q��R9��,P9,�됙Qп\W~a�{E�R+�����+Z� -9�*vڎ����ܢ+��-b�ʻ%@�ʯ7���*����tc�o(F:�}��J����Z��~�N�X [2�B���u�A -�k��~[���"7�m��A��3֓ xO����0�N��^��_��N@i��Su��Op XgZ)T�'v����TD�xt ��y����xo^&�0 K/���H1yKʮ���:�S-:Gj}�%�w�PbGX[1�à��`IqU/8�E���$Z�H��H6ft���W�w�g�>d�6��!}xH���>���n�w�6۬�� -��G��7[�-I2j���e�^V�3?E �iN�`!+Y=U��F��m� ����G�dI���r����%oH�G.5m�V�6d��ONIA���N������+��l1�{<�� �ť2H�x�ǕwlВG{q"�Tѣ�l��|4��������H=^�yb�L�0�NXغo@����2��DQ���{�~�n�6���gp�g' ���Hd�gѴ�ʓ��r#�4.~�n�>5��e�}�q�0����nyP�QEi�SL ����o��tA�o�r$��u����} ~�������Í��"\�gq.%VR�H�,gj�t���8�c�pJ:LFV��B#�;$f���*��)�n_��[э��'�����"|G�����Ә�^�G��X��'b����W�qr�詗�ǘ9�<�f���}�Tao�0��_q꧶* �#�makE���6���\Sk�lgYA�w��Rd%�;����\��:�����%B��RIU��#���J+�K�������ŭ(e.�6 �C�� �%>� �����-�޹V��nA�V0���� h�����6#� QvA�B�� A��W�4�Z���3>����V�=�Zm`G�D�K>Z�D�>T� ��9����b�@� -��˚�KYJ��خ�p,i=覗r��7c�T�%����1m`Ш�w49��J@i����O֎���.�P�G��g�����r�@x)�w��@8F��w�~�m�΅'<צ��Grv�,^�s�J�����HCo jb��-q-E��� �!�U1c�Bpڦg��$�t�&|�FQq2�wQ'3�������&Z��"���֫�8��+z[B��g�xu=$��(|� ��������i��i��dk��Nf$O�( -���H����[k�dN�� �E�0%�d���G+պ���>)a((��0�߇)����)�T2 ����&���O ����S�DZ�JAV�u�C?���- ���_.|r�r{��s����ig�-(���o -t��]a�8�v�-ǕRW��0�4��ƣ;䘣�����:��oJ��Яu����Q��� -tf<9 ��Apq��TQk�0~��8���.{\�ڬm�YIX����ž8���Jr]o��N����( 8��ݧ��NΚ]%�I�� -!�� ��*���G~���~m�:�ե�.oE% �����qJ s-sT p�C��Ӓ��A��VQ*A�h���@�h@+�lB����0r���DikT�N2D�X�Ӌ+�y�/����N��H �6�%(Q������D��0��u�Y��N��;��yk����ǒ�^���#�C1N���X��[Q'���x|�kу�Z�t��Gt�X�TR��g�^� ����pAx)���a g��s�y�$]�M�'<զL���*�Ȯ��s�*�����JC5�� b�� q�D��� �����m0�q�E I�q�M�ų �,�O�,�N�.]^ެ�n�Z���*�� -.���t�.�6���3����@*�ύa�^�(G~ -�-C�l���ʜ䩲%B����!i���rk-�,��t�T�S�������P�x���|�����$��U���0��ٿLH�I&? 3LL�$ -=O ��a�%<���2�y%��uk�����՗D/�+.|v� -{�GD�3�����o+t��.;�ZGs�hj���Vβe7ޝM%��r�����,�Q��1|��{�4���ػ"V���}�����(@�ɶ� -s2�t� �΍���m��<��įM�!C��U�o]�n�3<�F����c��V�n�8}�W �<$�cu���]��`�-�"r[�"�%Z&"�Z�������D���[��Hd���3g� ��m�*���<�s������9$JZ&�����%WJ��k�r�� -+�;$93���d�^$\��U�h\��Zڊi���)#C8Ƿg�_�%9Y+ k��Z,J�/��#�Ls��Қ>@̹s?��'W7,٧��v�v�g0�J�X�+���B���k� 5ϘN��0t��"[YP��ڬD���J|���ڱ��nT٤��uCF>�#J���+8�d6��٥�^� He�4|��S� �pغ����n�kc �/����s��Z�fɚ�V������>s��Jg�O1z��N� �m>ʜ�l�] -�/6� -D��b�YEt�r@�F�e�#k�E�[�-i"��{ic�F�8�I»q<�{�y2�c�q��ww��|r���f���|2��[O��埓�u8R���S�) D*�Q����c �4u2O�R$���J�q��#ג�Rp�Ƹ6`2Ey��u�2d���w��~ WMC͕ʝ��QJ1T�rgY�-��^���й$�U�ZS#�$�LQ.���?�1� �ݎw�G��Қ��WΨ� -ٲ���'� ��������5_�E��ٶw������~�@'u�w�����zۗ{��/���' -jV�MI�d�L�>�ɛ����9�i�Ç:������v��lsZ�7��OϚ�{"�'�9'E� �a�I��C�<B��%+s��'Z�C:!�.7��ސ��4Yz�d���Se�-؆�#�"�q������X��m$�I�7�u���L\��{���>��C6k)}o5`�͒�5����J0�VY���h�%z�V�i'8 X����A:ɹ|���m��H�r%�+_礥�_��AXc�B�p� �UHp��f��x�|��m[a��2��t�c�����o����IjQ��أ Ց�6�� ]���k��+���tk'��s���Wmo�6��_q0R�R�۾%uZ�I0c��n�b+Z�d����T���)Jrd;݊}(0�"����sw��g�*� ��;p W"���0��,���� -^�L˄�ʳ�B�DƳ��A´�$ ��4�H+8�Y����̚)W��BF��ϯ���\��8IK�Tκ�����i+�S�=�sn�Og���K��e��vr��Z���R}�U�0d�% 2��ZGHP񘩐� d�Q"^��+�9�[P(�+�v��Y�u#�2�F�%'�Q�?�@�P����Ϭt�6�I��v�w� ����y"XX�2�����T"��9`6�Q�0C���2&?����Y�R�C��";�_>F��̛,�Z#ZB!�� �� -�}Mؚ2he �^�"��'$�= �i�A�.b���9��0�w��x>�������ٛ��~=�.&�s�������d1�M�� -���$��dzq!CS�.Wz*Q6��} ��y�9D$ /� s��-W�4r�R��-��H�TK*M���|����x��+Y$!8[H4����ա߲D`��p�n��m7�-�O�9��M&/�G��#���X�9>�(�&�B��[�����9�VZǖgF�d|]qW.�@���7�܌q"�c"%S�Չ�"bm �+3��U��G�!�$�@�q�р-B��򢜅77��^Q5+i$��N -^!vL:�G�����Jɵ�+�_�)�Jc��v���~Mp���XP�C�0P~��� >��=��I?h��p��U -��#%h )쟼6�3�Ruϑjt��;���G��%�`��`"l^��>�b���" lon�d��lV:G��>��짎�8���t]p�[��M�C��#�^'�`%% Բ�g��4ۏՇz����H���;L�����S8,Y��`dkcW�]8�W �ׯ5c��"��[�Vp���J���^xɻ6� |W񴙲/w����� �-cj9�S�Պj���(��j�?����t��*k���� �,�[W{k�س͔k{�j��#�P[q���:��� ��}4�����Xyl�(�������-��o\�{-�0� b ����X��lǩ��C��n?�� ����m��B��Qz��ջ�\��|R���twn��__Z_�H��g;�o��o��;����t����_�j=�� �R�0��qB��~׈ �_&��ֶ��G��̅�L��} �R�� �j��/��;�Wmo�H��_1WU4�BR����PZ]t(9�BE{�����������f�^�Wg b{�}�y�}�:�f���q ��F�"��PZ��T��'�2ڙT�s�徟y���¹j��;I�d ްR'� ��υ�pcr R�zgp�|����m,̌-<[5�=�H � +�Lj�Z)�|�?�^]��%�X�B�+?E�`n�-LД�cE�E -J�B�V&�Ɣid��U�ԃ�ki�Te�oH� nB0�0�b� ����d]� ����uu �����q��3�m<�N.����d�1\ l��J舵��*��҈S�@p*`&�b �T:���;Wk<^��0�H�1�T̩��(F1�Xq�4I���iY�"��*�eܣ����x�tM�����0�O���;�a�z��p�����>�@���4����6Ab�Е��,%��*���W�b ��}r���DE��Nr�HH̝�<��3����1�c�<�ʑ�VnaJ.�ܷd���1)k\ޕHi��?A�����0Oo�c8d�1gr˘j�����#_ 6��yJ�Ô3_�j�ֹ`H(.���f�,1|���햩�u[��5Xm�@Py8�2��S~�N�ɴ�s��JOmѥ��$(�J��� U{l���g� �dyy� ��B �4] �q�b� ��T3�?O�DSaE��xX�8�� 8��'�걜�<��&K"��4e�˥�Ո6�#����yϭ��r3�?7��Ҙ��V`�@.ö]+�oy`-�����p�|]�V���t�����/�8�KF�i\ ק�k,��B��D|i 3L����6�P��Ŏ���7�H��`���sG���0BҨ0ť�8�:B�8�kg� ��bQ'�����L�˸�!!�4�s��%���*I����VÝQ�F�~jͼ*�e�{����m�;���NpRw"�+�� -=!̓0't�Uc��d�G<��?�cr���+C�m���1�Lr��ht�,Q���C���Ұ����:w}�sxq��B� ,{��r�X��+��&�R�D�=�K5�zq��Yy -�� <{�E�) }9� - hT�ˤ�H�h�?=]-Ί���Cu'S'�X��H\%P���P�p~�T�@m� �����s��F����]bNNw�q5��F8u8Xo�A���l�>�t�1�~�d�@~�חO����ϻ}��~*�j�4����k2�$������P,[V\�|��]�� h�q��ٲ^����`�0(�� ���ʕZ�L�E���w��RK%�?H�Q�Ksu�C������1�p�%Ow�,yq��d�!c��U� �/�^�ɓY�;̻vg�wV� G�Qw�d}�k(����"�>$I������3#_?�U����V4{�v G�v��j\S��H���<�L����/�XmO�H��_1�H\H�Ru��P(%jtU��^UU��^'+l�ϻ��(��fv���8\iK��;���<���e2OZ�����P�<k&b�@�9��S+�w���\�)K�-�%x!S�����Vx���d���\��c���H���2�`�5��b� ��T�.���G4D)?���6 -�R�8m�<7�[@,5d�/���x�1\ ,JB�b�hٕ>0�υ9��3�� �b�4i��\���`��y����2� \�����xr��A;�q��G���D�O���cS�5d9U��4F���x<둶rMP-�4"�^@ؘ�Q�d�I^�LF�|]�9�p�N޿?_��&p�N�ǯG��1~���3i�9��G���N��)RA�r��O.ꖢN*����ų��8��O�l$<��Rf X�c{DB��R������c���LSu!eh4���N v� ۝�s����vW��bJf��A�Q�D����`S�����ׁ�l�7T���$�}S\o� z�����)�pa 5Y��.A���A������r,O���(P��f��H6����xZ��)="@��f��]b�������w�i���� 8� ֌�{L��^̛;�^�1��3�l��҈�Z�]{3������ �l��&����[|~O,K��G���W�[��� �ݔ���A(Y�u��k ��"2uZ����?t�=��j�G�W�����s�йӿH�����7����bڸ^�/ /=o�?����׶/$�/o6�sۺm�^���Umo�6��_q08 \kݧ��Ӹi���"r[���I&,�IE����I�/u��DE�<��s�W��U�\�����q�Ef����RhY�{% �Z�.7R�k��Ғi=$w�OQh��H�=�YJ�D�e -a"�1�g�dr� -�@�-TRy�/����rc���!@������� ���g\{?�r�"���j 9�bY�mjV��r@��‚��RNe�Q�X��@�W��| K%�0�i��F6�=�]1�Y�?�32�N��w��yWlBh4�>�X�K����L�λc��A?wA�Ҷ��2�7f���XS���m��JUāb��*;Kn����A����_ WT��XM�R�$�%km]��E���XoD�ߦ]�D��o@ec�G�qӤo��4�������|�ގg��M�[����N����&0�}���Mgo�T2J���E�mE1��S�`���Iט�DO + -y���G���Z�1`"#yT�8Qi�z�-L��{mCu����tW��R☑� �;+��]�A��m�7��VbZ6�i*��H�jR���Q;����f�S�n7�S7f� hbx���>��� ��J���<.��ZI*�ـ����o�޼�ñ��o�1�P��'�� ���g�s�B��@{\o����U�E� �0����1�� ���J���˩j#`J��Y����at ��_����S8���8��{���s>�IZi����.<��J�v��}W��4ns߸u䖦�g��¡2���mW*�2s[mi�B��I��� �`�{�cׅ$�" (��<5S�� -g��� -��C��W����)����A��~�$��5�m�4��7"u����h��A�}�,��մ��x����ž�J�}%�^��s -����~����rVR�/_leJ{����^�o��5����!��=���V����P���t�e��� 3�YS�c�G��+�P�]�]��?���n�����je7B#\�2�Bo2��}E *G�V2�}Z.KK/R�D�3̭�����b��V�6���x;J`#��H�?��\�8�Z� sz��D�Pc"t�G��j��-�M�ڬeA�\��2$c���jݪ�*�Qu�ޑ#.���c��6�V���g��-��Bip��6��R��XV�R䑳���cP�*'j���J�jnaٚ��g��f� -��P�dJ�&dg�G�t�y��h �����0^nA�U$��k*6�A�(G�b� �<�� $h�iZH�Jon ؄�Qw2�� /&��|溜_��.����z2[L/�pu /�f����Ռ�]�d��-��^ 2 -����"(SɈb��Sȁ�R��ɕ���<)E����n< -ԙ4ƍ��c�G&�#�a�VmaJ�ܟ�U5X �Rg���b�h$�� �]$��[ -���m{���%S̨R;N�:A^C���|;DvJ���f��< <���⼍V������=�xk=S��c��!��`����w"��;T��M����^b�� �D$vn�D[R]pb�js��L��w����Hi6� �"��ho�� 1��* #N}x?.^��٫���(�H|��<&�;�i��Z�|�t8�# ?���V��%��qSSG-��J�a[ ��{�V�P�Ѳ&�H[*@���ڀ��1�X�#��[(�6��4���:��(��Ѡ��!��(�O��W2I�xg8�����ܮ��8Xh@�MՖ���P�pJ���?���s4=Z<<��[����Z�a<��߾1KR����~jĿ?����i+S�^#~�Q:���=���k���}���nn ox���,�j���|s��Ϸ�����}�k�r&�Y�N+T�*U����%�6ǯ_� .G���Uw�6V;���hG�hK����i�B{���wN:g��w��Umo�6��_q0�e���cۤq�3V�C�(�!���LD"5����ﻣD[�Ӣ%(���;�y[��(>9���e��j�TR��V�_z���&N��.��UN�'H a����LQY��i�7�DJ�D/]# µ�U&�����1�+� -�[(�i���}(ڈ r�X�rv� ����|ryKB�����h�[����hsK -%�LrjQ�T���@��`.L�Ŧ�Z���F��+YQ�9��\0� �R�k]w������' -�%�>��Ȁ�������.��vP[�F|H�r���U!�J�wW�&a��� n_ -�e� �co�X9W���iF�i�ǡ��1;M�N t��� -�������8^�AT�* �Z��;��@(C��|��6��ߦ-i"��7 ڄ��`��$��q2I��y2�c�q��77��|r���.g����d6��kO��矓��! QF��2\!��(f== ���O��T.eJ婼9B����ɨД�Z?Be$�R:/*ˮ{��)� ��8T7Ss� �qq�)%��?'��<��U�o��t�C�Ĭ���T��8S��l�u<�\��f�S�v+�jW�n� hbd�Zw���� �T��ۋ�T.*��j���~`�4�&^��i�jJ_��h~۩o�����5z9�RȢ6��A�z7�Ovַ5�Z���_���ʼ�;a��=F ��p��t�H�� �1b}� -{���9 ~ ����������u�^|N����։�: -w�y�VF7�uLO�����/5�z[�p&K"6C�˦?������O&}#�'Q�����ه��&��� �9��<�0��(��0%�9�F��K���iSx������\Ѱt�q��zA� �Z����[��ȃ�����q�QK�� -Z��vׂOJ���'����{�xv�G.)���-5G@pvvF3RP��_��U�� �փ�2�p|�Aa�r�w���=��W ��[���G}���0(�v 3\��p�e�a���C��������|;���?4�m1��S�Eoϣ��Y[s�H~�W��\d �3{u�&�zq���LMMQ�Ԉ^ �m������紺��%��%e[����s���?̧�J��� -����98a��D���r�;�a�>�����J�Wp|EMd"�[�� �.�P����?�p�Lr�q�2b�Z{Э~�€w(a�D��X� ?�̓��x��&��s-�7�]w`���+�� X5E�"��0AQ�u�f>�_̴!�(�ǤK�:�|)�7U..�����!�2�Zc�D�U��.�ظ��ڀр�P��m� jH@L'f���Zs���PA�T:�g�������`����wkh�/FH8�M�]�p�%���~�J�/Z��b�d��f(��u�u����Wh��y|E��b!����r(�g �A�Q:Њ�D��Aܑ ��6��Y��,�����������7h�������>�����a�3��{����􆽻>~�B�� q��׿iG�P�Kr-�(w3�dm�h1�͹#&�A�/f/|�RgŜ˙�"�,p1�S0eO�^aRb �a�e |�TM�ٶ�}���?��t��F^�n��ޏ�k?y‚96�ܯ2 ��v��n�`M6^G�{jR���f^�3e�9 -�Xx$\s�6�j�y�]�)F���O��p9�y%��&�'�P��I�ɡ6��G�=���'��|3��'�� &��j���ﱩ��d �{2,�x�Ǿ�+�;g��Mv�{J�;��!�k��u:�� �]��C��|��ew�Q9�|I��]Y��sbt�`��� �m��Pb��i�X��%�E�T�̪1$//'�i����n��JҘJ�Y,�A�!�JT�D����Rm�%Ľ��xb���VR?�1:0��d�J�������S&=�-D<���F�%�%�A<3�pyy ���7h�� �ZЯg�A�޿��j>�5���Z���S[�..��f8S�V�'�G�D���\FU�S�R�"�#�Y�c�eM3շ��������E�ƚ2�՛���)� c��R�1:�X������Z��:��[g�����C����x��[m�͎����������z��Lg=^���`ݮ�W��C��t��5��P͹I�ֿ�K�V[È����s�oO��/�Z�<�~�&�C�)�M��Q@�D�gi�����4ҍq�F,s��8�_f� � Fs�D{�����!�� -���3�\�CΖF����)�b9��:��Ǵ-m��2-_�����a+�Ư����Z?�@BsuV߻���3f�^�@5�DA�t�pBZO�Ey��zS�1)2�;K�Y�X~��AQ~��������p��p��ekh���戃7!w9���$�]rM�����ۖs�`{2���.�.k��@v�a�do_l��?�D۱�������w|�$h�n����ׯ -�+��v bn��(!Ȍ�E$f\,م�֕��O�h -�y�^{�����}�W ��-�����\���B� ���(}O}m@��Q�� �ö�[����[�g�E�9餷ݿ�~C/Ȼ��p�Ǔ�T� �\{s7��_���djLʱ��J�bő��u$��ĵ�\�$g=�� fDs}����x 0/Ҋs[Ww��9@w��?4@}��z�>����]F�`a��Yc�A ���@�MOΖ� \3�J3"����6N6�߲�������6`o�&��y�ޜ�~}~=���e7�ً���&W7��_�g������ P��.$�P�b�����E�I�Eͣ��, -�l�މ��a-�U$Ѵ���{����J����L�<u�GR:�&iӌ�w�S�c޿w���@��l]~H$��dZd�S��/0�IKXU�‚e)żYǤ5�u��E3�z?�E6�@�PX.>�&Ih�#v����T�#�S���Y4�� �6|�Ԟ�'�k��V0/'�L�d -d�dmܘ�IE߆�L��o�1{� �+��R�>D���#v����`0<1TnEN$���TF��ŝ�ٓ#C������Ɯ¨M�"�u�k@dA�r ��D��9��=;b�g<�7����� �e��}�YF�h��x�n��k�0 -��*�Ȅ�s�D� @u}7��*��y�P0���*��ۯ�J@_�m}7O�V�VW� �F�RaªXP���Dɳ� pA.���vg(Z���rZ �# � -�F1��H��O4n�&�������+�1���qB+2��O�b��y�N~�qy�|�~2�ei���������E�VE�J6Y;J$�g�?�㙮�K�g�胘 =Q`z.B��ÒR�����G����dS(%�Ԕ6�olzM{��J�`U3_��QUD)ꛎ��r���)8j��Z�[%�)��X��:蹊�U��U�C #�>&�ͥ �Z��&w`�,�����lp�6UK�X����Y5��1�h��A}zƞ��i ܯ�'�+����㭈�t�w?�d���<^q����_9��-O*���Qb�.��5=�ٜLM�m�hs�u�f�gm΀��<���>k�|������������/�����w�]A-�'k�f���Q_-��5�� t�P0����-� -q�:�R��r��au�S�Z^�Ԟ6Q�n{|'tm����qH�ϰJ�+!�۾��[Kgو嬥�����Մ���v$�WS���>���c�tff!8�ۚB��%v��խ0�1��ne)�� %R�� ����Ns:l��4:�����\�!��iv\ ������r��jv�u����������&j��&�����Q ��\pI���"4�:�c��I�L��F3��p�B�9�#4�����ќ `��4XqˊW�!��V�zs(ܗE�g���4 -����߃D� �oQ���[̃QU���a�_ձx&�U��- g�N�{��Z�IR5��Dy�d�amQbgؚ�ᙩl��N�{�;�&� S��4Es-d[�_[�N��[��r���D��P}ԽU�L��ګ5�7w -^��M ��X��*����� ���#�^A$kA(����/*����^�qX��� �v�����¸�2�}��>�:�2N�֞f��ۥ�#��M3~�޼���z��d��F�UYL ��T�� /�I�0��D�=0X�|�2<]���j6�*�\m�M�h��~��QԳ�e���p��Rq�� n�S�,67[ZFfx��'�}r1 ����e���)��.S)���B��z�4�����V�ա�!���HX�������w�-[��w<�#CIMW������K�ÐN�]��7����I#g�Fe�P��:�W�װ¥�,�%"�M}j����}�N�!B,��M0J�>~w� � �֮����27�l�@�#u<7d\���c�Ix[���̬H�K`EC��;��^�UB{y�u������Db�t�f�)���*�)��D��v��-s~ū�~��g> -G�B���$�T���mD/%�+��:njf�gVn��$ڡ̭��\x1�EU��LUK�wid��9��ah���� ��&�){����nLTȜ��(��5�$-��R^��?�i��g쨂D�(������)R,��u�5���ٯ���{�( ���*J%-��h���ýAQI�-�@H�I�^�uX�j!`�������ˀjپ {J��ә�4�5V(�T�cv����?M2 -���KLh��Qw�G �5�C�������]� ���<� -��S �X���6�.����[9�^ -�}E�'M���z፪Q3t��u���s�uvf�Ѐ�6=�+���[�{���������(���Ęʋ4N���cvKW��)�c�ϫ�4�U���Z`M�%�($:#G���“��|���݁VHa��@��'$�sj}|�o�S��R�ƍr -��j�m����ۊ���&fK�F�㘾�#�1����\�:�D嘌VQ̻nv;�y:��i$�W�SN�+ph�"lC���=i��� ��H���Z�Y�E� ͈}/$~���< �]���b�I�%ꆈs"$���tZ��nU�?��C�|[��&��p���8\A�:��69��� -n���&��`�򢵲=Fh%f�i�wJG��T�\��I-�`<��^����Z��a��� -�����dh[���&�9�K�[��<�᥾��{�-�r�h��ROť:*��M�}��Z�ߥ��RH�r0��^����^��O��6&��i�3sUiࠇ�����Zw��:7 �Q'���-����B�霠�2��s)N ��V�9����� ���)�e�KM��N���A�d�ӣF�x������dEĮ(��ގ�7�9�Û��;�P�u�V�"EӾ���c�n��xS�)�Iel���v���g]ԧR�6���k՝�������ӳ$m����t���ۣT�ֲ���&�ߪ�A�οt�oz�]�'-� 0ŗ_D4T�l%4��d��HG�$��ȱA�� �~Ҋ��h�/�`��b��jҾ��b{/����ۏ����{�����t7����]��.�����j�<�����,�3(m�� � � =�wO���t�@h��`�櫨��px��/���d�h�� �mC%��ւf-2nm��ڴ���h� ���[���/8�Y��4PR���]7)�9��4��-��m7����R�k�[�l�j��$P��k�^`��r�O�r��F�׸k��ǟ��p���/R��{ �- ��M�n�և֣�ȩﳛz��F�Q���p���@��%�{��iy�|#�ƙ --7���Ngܐ������p`o�w���X���YF�p�4M�D�fUo�[u����S� 1U��E�&�Dvb]���ARK���c��}�y�'�Jd�f�}6[|l�<�еʷ$���|K�h��:��l�  �fO��;�.�j-DUg�*��L{>��u\�ihy�:���wg��[�SG��_�� �!��R��MlS�˒�J�.j�I�u;����~�=3�^I��é -#vg�ݿ�y��h� =ځGp�z�0H��������, T���q8��R�� �8���r(9�$�)g�p��(�%KK8�`*7 �{6:��)cI���0�|cw�&���A C�� Q���L��j|���PX�?u���,�d�c\�0���өK��n�|�&�r.�)���*v��e c�p#�7&UF�V� [���*L�*��1��;"�BФ]�v�w̳}�� L U2��ΑQ��`~�"px��.�2�a���V�Yq��f��"I���p�\ <��Ъ8|����8@�휷��NGk�7uc��d"B�1AY=�$��8P�e��}��lݔ͊�����h�l�]��lt1�û��˫�cxw���������������㋫K���.������|��&CV�.��%I]������@�b��"�3�A��y*��ᭌ9)"��"�*r��� ���5�l�.��.�'�c�S4��m�a�����h� ڬ�d��(sAk�o�+H� -%f, ;wfN{f]K%��,}�VɄ�JU�k�l�n�|Z�v9ؙ���I��{�:��6�(��^�����᮰1fD!���k�3 ��20��Y -� �ʙH�DWZ�O�RK�`Àɣ u�+���%�i�a%[0k��MK��_N4�+��U#� �(�{0Y��2�zK�+1���`_w�-!��A�k㺐�-YaOe����>A���bV�|�%�z���_b���%�ŏF0�gX�K�Vzh��fr��@�?��2Kn���)�����$�E)�h��v6e�(ٲ�pt�7CE��\�, %���"I�:ض��k��d �X�{���S��{��-�''Ez�Ձy��,ypq�Y�w}���եS �Ay �cD�|\�02�������Z9� �2�3�r��%V�)� �����ɲԒ""B+���o�mh�W�w�rXɨ%���Ar�%G�/Ä��^�~ -����b�5�]��Eʔ�ap �\,(�O��Ι�zv�Әu��>��FCҶ��N3|�J�,�e��\H#�N|����?D`B=�l<^�����xd�6��x ��L��E��M7T��[�|���?8�e<������)J*e�;h��6X��"��yJ�Peo�\�;ף�..4-�>�]�N�ݒ� -T��L�ج1m�WE��kR3�ɔI٪� �莙�z���h��y�U(7��9��,��KB ��� -i`3]�ލ\���]��e��j^�;�:��-���6��o�#�E��w 42͈@.�;G���J+\��Z� ��Jo���y���^�+��:��x�� -/ Â��ǖ -����S!��&d���+��`�4�K AZ�:dc�ӎ�|����u�Ӗ�ލҕ����yQ�v�ש�Y��{���Y��Mp�FL]�=�J٤�kS�3�ŕ�f�}a��}�=i��jM�D��Їζ�S,h�����.�U�S�-�6�V�U�2=��[~4�B�?�������ጒ{�a6:NO�Y��n���� }P�̩�C9�l5vo��_ -��O+�ө/�T��E�B�t"Kx��q@�-�������n,.S% �T���U��z�v���`k|-5K.��!-}�.�>��]�kgڐgm��T�)����}�w����[U{�g*q�\4%ٙ���ͩ��p����cJ}�H��|z��o�vyBs0��ң�=:����ˇ�Н�_��n��r��vv�I���-� F��iH�B���Nx�e���N,�[v��[�+'�2P�aU��W���#��PKmE�Pu�H� 8T=Rr�a�օmkv�ъ��J�����n�'����Ӫٳa�v�+y+�r�`Uj�+�r�ߜ|�Z~պ���P�)U�6�Ї��I��3�x�GLȋ^���&���tT�\X<�P�M,�V3�Ue��Y�M��n��pņ�bCF1wS4ϥH() p ��� �؞� -"�T�ױ�$�.��~�=�����4Y�ZI�2��ݮ��d&f�>�-��'ӕ�R�� 0 E�S.����]���/�e�j�st$�X��y�B)��}+��YR�朋�R�$�g -�H�6�͞�.?���nK�oO�_ @� �se�~٪]v@Z��,�REQ��_lkG����]� ���v��eo��[ aX5 ݏ�ۯ�����1����Q�8���\PŴ�A�����t��O־=�R;.1�C ?���Ŵ M9�q\al ��&�n��<��O�ߣ��L�]��x��.p�x�u���gy2��漗�f}������%��#�����Ϝ���ȳ��{�綸;����G�8�W�0���G��V��[w�r�����S�|ڪ,m�|,f��aq�$`[����Vm��I� uyaP�GJ�-� -��sC��P����&�`/�]����A�s��Y&Q����O<����݋�a���>�$�����%!#2����q��jop��7l�� Oy���ϓ �E�6I$X�kjk]!u��2�K�0m -�Uu�����:˒7���nGL+<�*����{��t~{�J;��q��Go�� �>^�%��ϖ�kĶA( �b���q8$�ԁ���iNE4����t�z�9L�=�y<�̇��d����~�ߏ����f�p3���,&�)�����7���d�n]���SB�%My�<9-6Ni�}�>��9 9��+� W�RhST2@xlD�A�i�6�%o���ʦ�B�HS�}�H�<���,�<����a;�C͒ ��\iLy'.�?(S����YN�NѮ<�� E�l� #����ˀ_ӛ�i5�h����|�]T���Y�%y� ��0�����}[ՖL�7T���ЇSm�Լ������s��P �vq�'�dA_�6��|�F���B��K 9�S2��������>�}��~6~w6t"�)[p��<�6z����_�C�#S����x���8�����W� ��D8f�n���L8�I��1�>�1�;l`FE���d�yh��nEO0J� B#=�0s���[��Qs�fO�E'eܧ��pyҔ��u;�<,��C: -������S�^��E�U[� 6� ���@ 2�AG��`W��� -��%շl���~��|��F���'�V"��K۰�=�� %���� ��֨�L�I�0�6��ߊ(��툁�"��瑧G��1��PW�jf�e�����e9v�H���K�f��벖�#��5;~8������m�[�O�a2��Ӵ-YE�c��2n��L �4��`Ôb�>��8��6�ì=-sR~`~|�GU4�Kli��c�;��gߵ��A� ��C�k�ەU~@}Sb�9�wq]��U��es/y��¼ɦ�w��YW�K��s+o>��N%���p14dB�|S�L� -���c_�a#�֟X���҄�Q� -g�Vr����,�N����p�'V���~�L {��TϏ�(�N�}W�`t!Gh ��Lq*6��\���G��̺��Mm��z�@���E��3�Zbˋ"�2tI�����s8��R��X-:'�f�^|@ҥн�h�������%�P���v`9�\,� �$� Gp+��J1�+����ה][]������9�vT -���ˍ�������b����Ltឡ -� �H?�a����v��\��=�i|�[�:���=�w�.]��Xhf>zA����)G�X{U�ӥ;���f�Vs387����ġ�L��/��4'�fl]���r�:=��A����;�}-��b�z>����/�YmO�H�ί(!�$lHF�vnѭŠdv��[���Izqܹv����_Uu���N������z�����^����8��H�tj�JU:;� ���t��D��y6�DdYwЦ�T$�L�`5o�\�� ��.��p��4V�ڗÛ�4�SI����6��Q����Q15R�ej��PJ&?��^]�E�����>`�� ר ��<�I�8V�Z$�R|0gAh��Sab�2ҋ�Qә�L��fj��F���&�9-�ҹW���7F�@B��z hӡ{�9��s��T[�3YR�ɅEqQ��"Q"�x�׮��2���1y �zR]��n��Y�8����eO��=m���b�7��`x}�B�=��DfZ��2h�� -����eMĒ<Ȏ�@)�-�N��; APuSi� "�^]�f���!�����v؅Ϸ�_�>�������`t{=��{���r;���� \��������&CV�yaH �T�Ee\�� E��S������K���J��'i8%��UF��P��c�,UF[k��,���~$R>�FZ'��Ó��~_`�O1�Ŵ��W!^�]q�$)�2����A��+#y X�9FS������>#�%, �-�=r -��k�>ұ��_�>kkM��;r��R�l–�Ƌ��.r��@���G�C���ؠyr�e_ZYE��0���W�U�}��^+�G����m8b���/���.w�:�0Br���"_$Z�m̽G�q쉃��9��� hٟ���]��J�AD��c�H�D�'& Q�(f"������F�'��lZ$ E�W��4���r�ͳ)�xN�:���{�6��e�ś������H�p�w�Rlb8ҹEO��F�ٶ��)�Q��(��̧,��C�[x�/"\���fWMqr�|��E�ו�a�_�3�X�/xcf����`�B��I�  - -�0���X�f:�Y>����_�xB� cĪM��N�/��Y�����q��%�QI��`��h�������7n%ir��:��-�X;�����HS�~�M�E��>;9P'.��m�D܆P�_ �l�?�\��O/�8��w��K��Y�Al�EF%��.g�M�F���%�x�@�Oƶ*Q�����f �֝��w�X��߅�_%��M��i�*U��c�#0�S�hxx�d�w��uB�B�w���ka�#ʺ -� �Ubg�k�H��� ɷT o�v)MǑ|����*���b�u� ��p�(W0sZ�LžF�ܤ�� �$��%�N�$�����d�(2�hˍ�5w�\���p�:��ˌ{��b��"Pʨ�)�=�Gto;�7��a�d�v}]���N���� ���g�Mc��c�����%���f,��'N�b�B,G�I -�`͝N �����ϴ�݈��\ڙƐM�:@ �D3�Q�Z���k���`̷͔߬�\G�ojc��Ѩu�� h��(�["e�]���g<`_����Wo7;��}�ÄB�����1��)�r��\H�,���N#"�șj��6�����Ia�7�8�I��k�C�Hg�:�RVv`K��Ύ�4SYG`�����˼������Fﻤ�*�n�Wr�H����ƶ��=�,�pXu�ve׌��PсZ��J�ֻBu�1�&.B�o���Jԫvk�S��f�zc�5P/r[S�;C8��b��b�u��������Lөo���!����� ���� L��N��M24(:*��8�\T��N�ڠ��ݤQ�_��k���c�i�{߼7���T�d�+��0+V{DŽگ�S茌�Si��9��I(�,�."�P����(t~~�O����'t텒2�𨽝y���z�=��!�g(Du -��Yˉ�[����:���iq�Vp���G��<��kM<�#�.h*�KEsC��7�J�X��l/�+�Zo�|��}�_S�f�{���rc�N��huM��~m��V��� �)�[��禁wd�h��朖��[�?lԫ/��B&���?-ۘ��4m[�V6����9"�V&���A!T9��tլT�u��T�<���>Q��j�TIs�$�jq��S�lyՖIGx��N�A���Yib҅ -��Í`��}�*��"7���6�Q���h7z4��Si�1ڴ��)}L�2 w��{��y��-w�(��k�Z]�~�4���|y?��{_�a5Ϟ:�A������u��&� upm����Rw���ػ[��ƺ�e]46�����+�l�~�lu#{~��ś���~ږ����)�n��7��TY:�(��ЩlE�6� q��Q�+Ú��!Dڅbn �-&�}��HP<��c�; I/3�s>��� %�M���B����$�ԅ� ��KO���~���V��������?�.A��f��e�bN��./?_��Xmo�6��_q �{��4q�f f�p��mQ C@˔MT�4�����;����&]�m����{QN_f���?:j�\�XB�&F�D%s0 ��"Mt��̨4�)�N,��I�Q�L���IY�<!���Ȕ"�p��L�,t��W]��2�4�$��Lsk6W�����b�K����=����~t=^\B����Li+���,���P��'�P����1��X�#$�˹�gf�f�\��2��^� �M(��wF[��,ƺJ J#jF�Q��k�g��:p��,�+HR���v���� ���-�X�$di]e}�蔤SJҨy �!i��0&;��˲� v��������/��i/�.��ֈ�߅��� -D�^�b��Ƣ� r���E�#��< i�I�LS �wCo^@����| ���>�|N~�~7��77���r �7pq=�m8^�����>����oH� M��,� �SE��Y�O�b�˓�d�"bxɼs ��N�\�̗JSj5:9Cz,�aRi݊�W�+���r�4IӘ%^�9����?G��y��G����x�*�b:-r�T��Kl�"4��)��PQ -�D.��`L9�fBd�V�oAb[�tO�T�Rʤ��>�/a�kL8�$"}M;f! Xk�ʓWx��z�W|�Ną��.�u�����J*����x0q���^��TR��8�3�IL`*i�K -���&|GT"�J����(��d�)Rɠ��?�����vڟ�T 6՝�� ��٪�B �B�א�*� ���E��U�J.�w74 $�6�F��UNU,BjM��'���a����3��-��.[�[ؽ��J�#x[��s_��Ww;����;ndH������ػ�j�,��lI�A}b��+�m�h,M� C�A�Ek��\ -C�D��̾�����J�j$��Vʡ�SE�C�A.3��P.T�@iC},��%-�Մ�3�z-z0�?��o�cES,MbWh!�K�:x-Y,B�KT�Ռ�T=��c l��Ng#I������>��-���m��vئ�>�@�4Lgr�?r��1ʻ�ׁCk6�T�66Х,{���j��0�i_��p�`���5�]Y|���R������2��w�pc���)$�� �rfL�oEp<�WCG��%�9p�!l �I�{���BY�@n0��n -��C��F?� ��*�"P��}��E�J�Z��6TF����0=ʡ���T��C:�,��\`Vi�i#)��{䝳N�ĹA"��N���L�[���J��C�~E�yH��&�T��%]E5^$.��vQ{�);�&�Z�����$d󷷴���믫�ʕ����d�q.��6>W���*�xp{��u��T8x�n_u->�l�j~劻���V+�\̘�z{n�ENU�h�Zz�Qj� � �{/�_CK�f5eޞ���HC�����-������$��d��y��+s�.���Z7/��Š9A��I6���Ig�ֽ�B}�^!�Yq�d�^.��z5�Ӌ��b�:Ǔ��s";�Y.=E��h(�R��<��A*mA�(��� �3pj���c˭���)mK�+E��zj�b�SEi���F���r��{L3����q�� -(�D�;N���ٴ� ��?e`���}�ak�ͽ�f���:.-�����__��y�=�Z���ϖ���EQ�[�tn���!�|;L���'���� -%�;�O����c��7@��]o��މ+�3�|~*X����>���f#���/E9��'vC�{w_��~Ԝ�evWn�q$b-}�Z����/�X�oG��_1��, U?��8v�aːDQ��n9�>n��1u��wf�Ӷ"��$�������v6�U�''8�+�r�df��D���p��F2�2�׹���J�)3�L�2�G�L���<����9S�d����A�ӿ�>r2��-L�r���_�N"�Dq>�� �>�V|�zн��1ZK��Ў � 3A�a.��Q�cA�Y -"[��O����H�J$r�q�'b���J�*���}]�ܻ��F>� r���/PEb:�_�jg�{��I��K��">3h.6���e����:�Ư^�QҀYW@�Wɀ⦿cf�fs>�7�5�!U� .6?bd{��S4:�|�R�5F�\(��hl�VEl���lN���@+� -#�%u���iZ-����`ؘ��Q��������:|����4�/���Noн���-\\�>t��>]A���8���>ԁc�P�)r-Q��)�@h�y�3���н,�Y�!��\٪�q5�R����1ƂJ�o�J�a��H�������xw��l2D�pgI�i?�Bx~���Њ$�i�+��f%ߨ<�MyԆ�� ��VaV� -r�@����?LЍ�(��Y�#=�����hJ�֛H�R=�EN!Ql����登]�4�sw?B�n�l -�C�`�x>�S�|���@Uq�y��e�������=B;�0J�$n,�@�c@���"n�T���#4 Q��˾R�<�6��c��/|0쎜��W��>F��3��E ��R�R����p1���Wɩ�1�N�a���������������,#������w���iA���U>��ѽ��cR����J�B�0v+�c� �7���̈{�; ��O��(�-�,��+f����pC_��w� i$�l5ʲl~�Nb \"��t�tZBz�;a{�� ��,b����/�=���y�� �0�p�G��HGG��i߈��t�̻^�֠s���`����Ӡ���?��4ei��>����W ;q�׷��l�B��Ġ�$Ą&�� ֋ �'6yO���3U�N�|*x\�M�?�&�,%A�3B%���f��޷����q}Ia�VPx!�VH=��w s�>�������*!S�Z���k9��(�h��>�%qVqGZDz���kV��L��H��i��o+s˹,f?��9�*t���m��r%�Z>po|"g�F@�eqy�YdO�!�-��TC,�+)����׆,O����i0�}�@�X�� ���|8��S �{);��#�goe ކ��`�����nb���ߞ������k�*�d1��p��U���L�=$tB�dŧ2Kq�D�����,�fm�д�v$��,!���U�����A���O4�� �0�J��"��� ,��)ٶ�#.��v�2)]CK|�[�/ty��6����e���������-_D�}��i��,�ݔf&J�C�|��o�i� -�����߈�RÓ��^����t�R��.�1��t(�}��Gw��(�n�UG*�>�\�W�A=�����v��3���P�C�g��^%jaW~I�h#�=w�����+^]���3��v��k�N���%�7J���*��*���Uv?m��'�ƙBX�z�P�.2� -w6-�Q��6@�6߿o����z���� �pzn�J�{yr��;Xث�@��!��-f�������2�# Tn$-�9���W��1#�®��������XZq��=���X]o�6}�����k�cۤ��3V8C�.(���%J�"�I�����K��b[��{Y�Ʊ��u�9��_���y+8=m�) E�!��a"Yf΁�d�e�/ �f(Ղ aʴ�~!�4��Hk��Y�/�%S���"f�̠ݟ ;�o��q�� -R����?HK�����gF�&�[����h�bL��#�K;L`)�� K�n FW,��f)�,���;2Tq�_c��~�zC+[�KOר�A��_��2���4�a� -�-U}��q8�RҗLKA�!ќ~Jr^K;D�B�����E��=ks�8���+0>��Nd;��]]ٱ���e씭Lv+�rQ$�L�������~ @���Iv3[����"��7� ����d�h���G�8�)YZFq�cQN��� N���ً��8��bF� o�L 9eF���h��d�r�R�g�t�q��������_e.�T��Y.�Y�3�q^� C�8�r*Ӳ��FJyջ89#@���,�rc�B,��N�T4�8u��8�/�����q���A6�����"�y1�g0_I�9�� XO ��gsE�E�bFG�����{O�6 ��6�Ӎ�Cz{݋4+ż�t!?�t��,��t@o+����_�����"��=LD�������/��������p� 0���lpV��KY��_�q�ߋh8 H �h��#1���E�N�|��*` �b�F��"��F�F\�l�Wݛ���x�{}��'�w�������qu-N�.O/zW��۹�^�����ӎ��0�J~��H`#?���&�ꊒR1��x��t<��R��O2'c��|(���rL�T��W=ڴ���}���eYBo���ғ��t �����6�ե֦����9i��#m�'��J2v�k���K1�㴬�z��k���{�3d����R �!D�p@D��G".�~��t���0�.�6I[��T�YIr�4���<����Gq^�¼:�bE�@��٢P�O���t@�&����G1�?mhx��&�!m9���$L@;�[�ٔ���xһ:������Y'2B��i�g�Ee,Q�s��w� ���x��w����_H��Т��A�HC��5�IB<��&*��d5�E �Ьcv��h��*`! --��;�`�0w�eH�������M*��!8B�� j��*.� By���ԑ�DN�}+RJ�"�qE@���x<+,B)�i�뇩\�R -��� (@`���j,*:G�#�mʀ^ ��<Ɵ���E -n3J�ߤmk"���y)&@"s��G�ҽ+��s����Q�J5?sSlLd4���ft�{����Xu$���o�-�{@���-��Y2��H�ih~�ښ���epnV�6���cPS��OX^<;���?R�m��#��iPe��A���*�BŴT@�1�4�"H�k�5�lˇ�� -��^4�B��Њ��C�������������:|���y7̼��q�}B���Ⳏx�?vĿ��/��d�-G�����Ė���l~�@�uD�AZ�M�I�m!!�)� -��0�,�s��l��:�yr�b��@GqaX��r\��b"�ѱi�F���� �<D���� ��"tX�*��c~̇D=PW�O��8A��9x��1;�[���ڟp��~�[��f����L�R}N8��%U�P��� �-�"��E<,'.�������X����]kI*B���E3�����fO p�Z/J�G�ktD �KK�#PSTQ ���Xta:L8*Y��L��s�2���u'�i��_�HhT �Q��/��R�����]���>�#q�Q�`f�BpBXFcP�>� �mC�F�1%Q>������^����^��<.���}�_^]����p�!�4���7�� $0�����$�,1峆rA�&����}s������y�ݛ@���Bf�Vn�R�f�o��]�zӻ�}�=�����uo� 9j�O�l�8�:����@��;��&xu�{�<�"�@�j�Q3xv�2 -��(�s�Ocv�v$ �%�~���������/g�l5kLS}���)/'D���Sy.1��=�"�s�/a#P^S�LL J"�v�L�|�r�.cV�����1dC;���z * ܬ ��Pw�:Kl攣����9<5�C�9> Q,#;�K�i�V�(7u&9�G�M ��F��L~����Dž�R���=$2��"A�C��YN����#�B�'�v6 �<�&'a�`���%J@�_\9����1����l��`��Xe�-�b{0��bg���!����߽��7�� i ��4�3*��^]a�H;����]#���I<�yʪx{Kf���v+G;�M��:1�V<�1��1�jF�;��jf�Oml�2^�Ri�Q����!���6��+� �+���nw̛�ք�����n}$���j�/�'�`��[���v:c �S4�Л�i��P��up[l(���t������s�7 x���0 -Ҧ��dC�2aM,L1K'N����}�Z-��0 h��5���ˬ�2�a��?\L��(�<�vY�+?��Jw+���؉�”B#���/9�۫3���z/���6�����#����q�9J����Pz甪��Tԓ��%�UC���ˢw?���$9� �cX� �3�{�p!a�!��%��w�� �qy_�o1�a����B���[��C�mA9���}P >�Uu_��ٔ6)E�����/�����8*��"�J���aqmV[��"�Vs�+��� �o�"�%��fs�[���W�}�ā++�഍LjP��0��7�X{K7FMg��%<�ʼn8Nf����Fщ4��wk�?ɲ�4��G�st��>�.�Ex�I#�5�K��ʯ�@<��Sl�\�\3��WP��Mik��$X��m#�g���x�x��OXSs ��\�����L�J��5Qx�F���i~�JM�d�����fY� -L�a�<\àk�� ���}�P9�VMX��n�b�w፨B򕗳j�8.\��S�a�xE��U��r�OY�pE{��*�+:��5��I� �z;ڊ�v��ߡu Դ��������M���>U����p;��aD�+lq���f��cC�/h<؝���k��9�zTo�xE�J۾�j�;�#�0���qQ�R;w ���n�\�S 4?��k\ٸ�R���b�yU�,��C��!C;���� [`2<�.#��tԲp�[*�įVߌ?v,a�Љ�~�#��1�xB�����6� w+@�l�:&u���h��+��b��c�E��?����Ñ%�+�pPG�}�� X������jd���g�8lu�B�7b ����3�1nDp��w�M�e�A�4 �\�~?+�ljz"1��VK�~M�X���ݜ|B�O�ԣx�Ϊ���GI.���[l�[���.�%n?�3 -o-Pg��P�mG�=�!���y��M�U0����u�ǿ�*K�^���xn:��i��[��I���ɨ�W�J��f�U�_b ���h�?�A�� K?�b�L������ˀ�1(�����5����e����q0Az�խǵLF�R¥a��"��ܴ��u��Re8�L�A����V+ױ�&��N���6m��$J���s�[��c�逰{L4�mn«�5:^��z��y��<�[j�A( ���U�S�YK���A_� !@Χ��tx�p�j�ܦ����.{���k�%�A� �b���B�P�#���G���7��,�ջ,wd�u7�2���f)�K��צ�:|H?�,�nK���� -�4`]�Y�?�_|c 2d=��Zp�o�����>���S'T�b{��}�ܱ �Ӊ�c. �"p��"ʇ�I1l��!Xq�� �"t� -Z��x��l�Z �#�3��TC�Ώ8�&��a 2���'S�WS���Pwikm�n�2�5T��_q_���OW�H��"��>�=���p���Q� ��� -R�������!7hљ������;KV�� x�~!o ��~P#A��R1,i��c�/�P�!j��P�n&h�U�3݇ίpXy� ���Y����rZ�u j 6>�܊������ �U]���A�B7Q%�K\�aٿ��5��T�Ո���2�X�Ǫ�=�K4��X�+[6К�U ���J&�WӇ��FxQ���a�ۆj -|�i4����-VT㓟�>���`�Y�^�״r%=z��{H�Z=��d)&�he��"��O7�b��\�k�q�RTI+��ߥ;���,�k[�UH�U���bkv,,5�wA��/��f��ff���rI>�F��u��M�Y�F��$���LÖ�[�@Q�${�w�Í/����zjLʃ���%���mʳ�x�^�ㅁ��X⣜~y�Oe�N�kn7t�#@�撈��k���@�SZe AM)��nf��^_y�=��Q��1o ���ix�s���9�������n��W�i���u~�s4Sn����7���� -���*�c[�U�a�=�j{uY���� �$à�C;�^�i.�w]��f1��E��~H�KG� p��>��V�n���'�v`,}2d�:�׻�� -_��f�Jy7 ����G�G �C�I���z�����-�{ee �>H���`�G[H;�'/�l���dB iF����s�����i}<���-I��}�cis�ٌ*s֗j�Z �*n7>����DKݣ�goZ4W�����ԇP1�:��qK�ih t��]=��Z*��T���� l}���&���;ր��,�&WP��Z΍9Q��ӻ΂��׺��"Wˏ]�zԶÜ���Y��+�+�|�ءr3�y4(���X��q�Yе��5�����+&򷵶 w ~v{���t�����0г�Q�Z���ب����o�����7����Lt����hXdC=tDd���� -���I�f4���W�Y;�� Q�%ǂҶ+�����?G���~��K�@$�x3��&&g�/��K�na����u��ӽ�W�=I6:��z��QB��?��t^Z��V;i6���ڐf5d��Tl}��/[Z�L>�o3���ɫR m{���cI�h��m!��a���c�-�<���-�|��X���#EW�=�8̪���\��T�w@ �M���R���Q~�7W� �����=��ߵ���L�E��y���Qq����>ˊ"�V� �DU�FF��t^�����,2����_ׁE�Mw@�r֌�����I��պO3����؛��cK��Q�V�ɚi}����XJ(����7��ŗ�#��7�9�^̧A�mK�?��!Y �ƅ�Q0��a�k����?vm{�d ?h/v�gC\EրG�(��b����'X�|_�߭=���BGU�s׵��8��x�_R�M�|RQ9Ev��×�i��Us��{t�э�~�)^?Qd��}`�t�}�G�]����*���f�M���ed���fx�J�� Ko��H �!:�n�p�ԕ�� ��)�}� -g�sr�K�&����C����F� ������4�����i� v����R�a�U�#�2z@QFj2�I<�����f�Lp���mg�3��Xg*��,E�Q��h�B���Tq�v�۝�cF�� 2e!7���.���c�I��,b��n����Q}��pyKh�Y;9�ק��`����!��f��=�Ņ,��`����9��@LЫ����R�PL�b�1�Y\#� $X.S���"����&�F;�.��;��mwk���p��_�WW�N�}ޅ�+8��k�ڗ�kA�󕐿�;�j 1e����h -=M(�r�ħ�����Ld� ����\�bu+5��D�qb��� =ƉeR������}C�B3)�2���3�^���.�z�_�Fx|���!�$��k�T�:�JR�honT���%���)h����2��`X3�����2�� #A/"����縖�A�%2�Op��4�� ���H�8�l�=��~g��{��*!���#�"� �֚P[���nV%m^ֳS#�T�JMCC�N�B�a��>$�a���;K���b��^r�p������K -7\!��U����K2 �����&="- �@X\m��,\�#7���W�m�$��y�~߹��+X���ʘ�)Ћ�Vo^{ -���CoN� - -��w~5F3r'�+�Xfa�[���6��l�I�T:���%��ua��@�Z̪� -��qr'�~A�?n���滅�����Z -����.y�Q���/�����< �I�,(���({ �sܥv�U�Q���yDS��d�P�0|����b��_��%�^��j ᛦ��pN�o��M\)������u�*�k��Q�a�g����"-�"�������@E�Zz�jlc���V��Ee!��%)}yr}�y�ž߉ �M���-v~,y�}){W������1h�n"��y��7r1��A�ֲ�J�Y���Zb^��Z@��S�����^GY>���9�O�n}��^@I���SQ!��OtC2P��i���)bJ&�Z�dU�����/��O���UF���XTu��������,9 -�O�y�[o�lϧ�[.~����-Ug��Wj�֜m'{��aGy_u� -O�nx��=n>���Qn�>�5f���GQ�j�\�V N��Ơ2G#(����kWH�Q�]�g���~_h40)b�� �֞е� e�,�U���βZ�k����Bxs�!,/�ψ��wj�Tf�w����5��n1�iz�~4n7I�������:��`CX/^<u}�L0��3�ar��r�|���w�v_j��t����ӳ�lb�S�:'�;��;o� �X�pC4�9�:�xJ��M�<����%�� ��~5Pt*�&�o ��'�<��b������I���}�xP���� -�+�s�Y�U���I�?�ZmsI��_����p�c;a;�.�S�lj+�r5C3�y�a�g��]����e���{�l�T^�n�%=�Zꞗ�V��A�ɓx�"��Ea�D(B���M�(�6 �e�/`J���h>���� �����y��8��k&9�GI8c��Bh����\Br��$,#i�b���C`8�%�Kƪ0�\�]L�o�`���L(C��E��9B�:��0GVl6�4 @���R B���L�HI/Zm��1D�K�+\oB��ϝ0�0vˢ��(��䴶���/ȈT��w m�@D�v���BS/��(�D�;�[��b[��BOS[��5P�_-�hJ�Ӫ@4�O5�X���y��^�{L ܋��w*�?�eG㳧(���\)��o��h��� -��� `kBP��XK�x�w�Z9'�Ô͉���'�٘��p0���~���.|N�_|������`4�����\��'Ë~;���W���p�� M�K�ە$%PRA峜?9�[,Nj�=1�� �9�� �:"V\.�"h -9C�X�X;�"Ҋn.J^�������( -4���)�>C���ݙ���!�ö�5Kr1%R�T���8��'���d,���ek�%=B[J�� ��BF��q$�C4�7��vY����PBqW2B����z/����8�t�X�"K�gx<�tǤH���, kF��#�-�[8��:��^4���߇7���C�יh�9ѿW�b��… ��f|Β �yr������|zj��@k��,!o�]��tl�3�9` -�c����ק��K�5bK��^�Rϊ<����"�9-060���v�1ͩ( - �"����?|�]��N�.�zc.�d2J�E�^���T����H/ݡ��M��a�j�,Wm7����T �����j]qc��3~&B'J�~ʽ�;+���=�+�^�L�c���e�w���u*/�O��hI�A�ڝ�-������x�o:�(�b�d�IE��$^%�]�0�1�+�k|��i�͗L^�m���*ph�;4�X���,�-�8�P� �'�.ʒlY��g�O&=��^g����R�6,S!�$�ɨk�$�]P:�d�������<��0�)�E�J���s�_cȘ��6���j�u섾��çdՌӋ��ړ|����7Z�dIy�:� �'��S]��oV��e�S)0e�2/���v���>���"B˲kbbI�,�T��"�4���R8`�$fkD� ����SNE,*�i�lEFy�v�ŏB����J)��^��]���~f��‚���S�f�{�s4K*�jT���US��*���%S,X`������0��.;�g��Q0?=���K+��Z_i[��O�R3�|_�(����n]�,3�Z�+D��I_�;p -��I�d�ѓ�_��籣��L���3��s �Lҕh���I�"S�tJ��9ң��B�?���輡�Cq�z�.�j�m�m���U~�@h��B�'�EŒ�|����%�6L�>_���t� -��_�^]��'��8�%T4)�8g�h�Z�� [�F�Y0창e�i�MS�b�Y�DX�YQ)qڏ�!-�w����ZGܫ�&2�)��L -�v!��g-���™?B -1h����(���-َ:C�0+��_ L�1��z�km*0��i�۩�I�8�PWh��,�����-Aw�W&��c�M����q��3O�h`?�I��[_a���3��g=~�y�m��0��mfWs ��� G+( �l���h���;3.dM�-�����v�K-���]K�Dc�6"ƱY��v������&��+�o.z�����Q�I���f�`M��γ˙���k{c�`�6����.�>��M�F�nX˫)�����$;�h�)�,�꺙�q�+q�+~�n�/Ҟ$�S���H��k��7k۾�j�Nu���C=���Gי�>մ��E龑�h9Av(?�B���7�Кr�1\SB���QJ)�$��x)6d��7\4k����Y����5 �/��ն���)M��w�.1=���g��(:������� [�N���Aa�tK:=��;�4,�Z�J��i��>е��%_:����튾��`%t�RS�3�W�>gُ��g��s8ޥO�)1P���Y �� b�ȴ1�5�+wxi��n��@.����b�5��+��C�q��cO{:��0��ve��aXA֡e�kQݎ b������Bm����������������:tSm�Td4�>r�5�����R$s�SM�D0�GzJ&��c��F<�`�c;�6��袄� ̅T9��񉈿k��r]Y��%䃱�|-p�i@�^U�;ىB�YQL}��`vF�k�m~891�ڄ�Gw狚�lS�t$��)��;wo\iLr��oy.e�0ɒgC���E�~%�>߼���[W�R�۟�LF�o��u��m)V�%K�ܕ�l+ʋ�Ueº��V4�� |/���4��|��:�9�J{�Z����Y���R�TJ�U�+&uע�7o��"���*��[~8;��u��1o��Ku9|�~���]ٚ;��r�mN�*NO�E�El^�b�:]�v����5C� ��zlL]�k[���%1j�q,�a:��:� -���f��¡� ��/�;��^v����E��暼Am���-�5j��㽤�#H���?�� b�̒�m�m4�k�����/��U�xh�/�ܴ/�cL�?@�����. -�kx��%~קaM���5�'�m�}����tG��:����pĸ��W��XmOG���)���I��U�Ӑ�j6�"UB���>q����aܔ�ޙ}��3iE�^U��ݙy晗����E�����`N��������A-8�?��Q(�������( -� ��]!�s���SP��8���?�h�VLp8��pʔ��{<:m�rQ�I:���1+�I��C`4� Η �g�c�t|uu<NFpq�/�����N�x��$?�:��24��cAN R���\>9 �-6N2�?�=t/�'l�a�q�k"�b�K -�D�SL���tRI����-�}K�l5Qi��w6Sz=��?�tg�^O/�B���m�C��RLF��9�s�ojX.��A�zե����I����Za&�^�+L�;C�1��jM� ���Xb��Q@c��1e<1 ��U�K S�����;?db��f�Z�t%X���$���ӵ[�Ę���le n������˳K�h����. ,1�<��<��Ԅ/؝�*�����"q�jL>�λ�L�4��z|z�S -��Z�C/�U��q�NW�ӦrNb�c�TE��2�u��G7���9M܇. #�_��얃L�`��B�qA��C�X� ��Q�K��`r1��VCsY����%um��)�� 4�ݖ'���^���U"B+�׌�۵���� �c1������P���:�j�~�:�R�Rhku&e�!��W�F�|p�w4�;�>�D�z���z����Wdgg稕.�3�MQ��}8��8�$������*)��}�:�K^cI�eV�z�ܰ��UncQR1��0o؞8��6�R�� ��q|؁�Sij ��SJvҜ��v\g������LT!����8������^*�l�:��I�T�`\e�Ibnn��D�� �)lӏ���t?��׵"��K���!)kl>|�L ��@k�!8�'�~-������~��xQ�5�^���%��J�?r�x�R+�d��E�N����'d�x�+��S�*�|�{�V+R�כ�+�vE���Ib��K�z�6(�hT#�z�Y��c�ѵe�o��rF&�x�J�1`�H^��|�6������[�J����� �� 6��r���kI$�^U�2%4�i.�¹�XH��p�y�xd�_�0dT=M �� ��^j� ���m//�������R�1���JW�L�w�\u7��O�ɵ�{mqv���0נᛘM��X�����&����j,:�([?��qF6O8��a��BrS "9��y�� v�o�c�0�lmp�%�`�urP:y��Z��,�a4���<�ps5��l\t����a��hۯ�K+UԹ���ӍT7�\�u3Q -�����?$L�9c��)�a�)h�4������b|��bJ��76վ�y9#�S�f}���`��.��m��Kh7�HI�,�H�2�v͆���8;?9���, �u�cΔ�4�Zh�Fs�S�'� �-_Ń��a�u�� R�|�҆��8jQB{W���m:�Sr�$6Yo��ͦT�7�|^U�����V*��H����)�1<��՛��M���ᡅ9���ߵ�r�H�=_q��r -����a��72[5XI���lQ j@e!iՒj����tK t�$[˃�s��V~�G���}���|�nmbo�J@���ŧO?��t�7��Íς''~g���,��q�5a�[@�������tdq�I���� `� ��� �4�s�d�,��"�ע /^��0���i���ޜ�6�d1���k/I� Q>{.~IV,�?��~��K������Hk�\frAE6�"j����I -Kd�,|���RA�xs�V�A<>$:EƁ[� -��}�y�9 r-�%��uS���$d���<]� �F� "�G�K�1�Y�c��b��8�]P���{�`��$X)‚pw,2��0� k���@BMB�����beY� e&�碐���e.����Xf"�s�3��(�b -�@�M�;�{�wt��>�F_͞у�G<4�;?Z��w�AϰlЇ=|:t,�f����n#�L<Ӈ�`�9� ۆ���x`"=d`�C�4�6���`�3��m@Қ#�� �3j+ьd��ް�w�S�1��(��MgH��R��n9fw2�-�4�X�m��3��@7�^�@�`|5��w�`P�1*QT��(�~30;T�gZF�!�vߺhGr�{ltM�b�i�F��(5D\�l�� �CO��oQG�d��u�I݉eܓ�>��'7�c:ǀ�Ѩ'-o�W�k؟a0���&��F&�ަs��V�c�~3�M��2��aY��c��gh��J�#vO�z4�:��F�#�%cHW���������,�d8��a��N��=���04n�1�t:"B�m��вL�`L��AG�R_: eS_ �ܖ��z�I�+`h3 |jO�w�������[rJ+3��+��s,��v\G���jG`��:|�"BJtc�`�~*y?��U��ସ�y�-�z����. -z�Y�胕�� ���r���Ճ�c��Ԝ`][x��b� y�" ��t� XG��iI?���vM�9�6��-�3� -�S�|�� -���M�E���TE��f��4�*���R?!R�6u�5q"��������0ίWLL/�Y���q�)���>�}�Z�R†S7L�/�S�st96�NjN�]�~y9 %�>ᕤ|��\��*r�An9�8ȭ!V��Èc4ʖڒ'{��bT�(����]6_q#`3�����{\�H���56G���L6�{>K�ӄ�#�R� -�,a����|ѳU�N_!���R����'�'B��P9[Ĝ�iiK�d으��,�|W�Z�|,r[?�h����jK3B"씫U��؁$�H�Ì#pMr>�<�=�d%Ă9䂑� g�nI@>Vje�|*CI�Z+��ko־<5��^q��j��v6��,�"&)�\eK�8�| ��Prg&r}44�:�9fQE+Jz/�(e=0�n��R�s�xb�0Z������Z2t �wԔ-ʖؒ�ߔ�:����L�+�� jխe����f��,p?~T�@��@B^ 8wUʟ,��'�@��Qg���4q×@-���������Z��!)^�cuj�U���ov�S%5�Ex�Q3i�t_�i���l�56�_�d{V.��' H-gAq'��� -&���#��`IbU������{����V`$ 6�(����U�bU,�;�kFia�)��H��Y7����x���/ b� �a�vz8��~ ����W�jU qXK��^���՝���ɘ��b�\�%;a��bW����I���K}>j�i -���U���AnF��[?��� -p��y��ď��iP�`eR�����񆦔� m��t[�Y�i�cY#� ׻q�%I���$z�%���PM�~������B٭ ���N8���~l �㙢��p�=��ך��K4�%���`Ša��5$�l�t�&o��^0��I� �P� �����i,g'/+�;�0�%̥��r�Ɓ��7ZL�"Ig��$�H!^�����G��F? j��ԕ��� ��Jީ5���,tsh���j���d-�*���7 �[�5nK8Mxˀa�qB�:��De�,�Y]P*�1�cbH7�M��9đR��w�ߖc�5��cb��K����G��l"^3`��=6ί��r�����h,���g,tۺ�z��(*��ᨨY/���i4�M 7�V��~�����h#$e��ڊ<��J�[���g�����JS{�1�� �=��pY�0�k���XՌ��v����v�DZc��b����}2fBD�� .m�T�f�[�P�jN�K���O�w>�Z"I6p�l��V�h���<�v�\A{km�6V]sX7:?WU����V�,�� ��i�֐�Ɏ -�/�\�u��# ެ[�sj?sC�J�O.о�!�&�f�?��d�� -K���LQRFw8+������m�m���wA���i\$� ��n�h�PH<�he�����,h�d �t�Ya�)\��WDj_�8�\]gg�ȋx �>n�U+ӯ5�/u���{pg���o��i}��� -|;W2���ZN�!8��-iN��B�2垘�\�A�r���8��|��J��UY�����ݿ��\�RB�����������(@<�����'�1�9[�چB�8L��J5��.�!3+׸C_��o��{�C�w�T���@r�e����q]@��p!8�DG�^9� �������|��C�n��ܵW�J�B�[���m~~�*����&Ssi���ڵ_�\׽(�0;�b�a9�*HԴ�S?�"\S\>�Q���Ib�%�8k�e,^P8 3L`�QC�膩���<*�'�5�(�k -���r�+�������ws�v������~��%�K/h�"Zh��2-ȗ2X%����޲|@�?�IW:O -_�|Em��m䱺�����P�I���аI�#.!���M���;�]N�,�^^Uo��{�+�����H�53_gb�-�u^�����s�����Yms�H��_��J-"��l����q��: �$���-J��V,$�H��]�_w��@�\�|0�h����^�Moӣ��Ϗ�9t��Q�7�9�&�|�⟿�|��`�X@'��;!� ^��������7DmF0uRdBދ�����A��2�.�0���Xf��d)g�W�a��G�'r���!�o!���,sX$A8g>1hO_ -H�\�y.Her����9��'���0��Y!�eL���B/��-�d���%�^f9ړ��,����=��H�I�DKq�f!C�S�P�,�Å��=ڠ�.Z�6X���BP�$��B�9��"� �%� ~.d�G���8�.R6q B��=���X%��d�:+W��iE�#LZ���|)(fP�E� P0!���p�9�P�d�<�h��t�e��Q�!mH�')�bhYV�Ļ�]p���ӱ�G��ݳzйƗt��kǾ���r��Y� 标�ϱ;co� Ǧ��ǜ<��\��q�X� C�Q�F~(�1�m�-����g.Z�<͡}���p�7l)լ�0<�+��^�f����5K=��IZh��\��H��]��c܇�g^�h�Q��9�;v�+R|xN��q��lo�Yp1�y�r��]�=���e�Ʈ�B!�٢��Q�����6�G��g9�x���AA����&R���mF���5�%0�-�pi�:�}����3 �z�(��J������֠k��!1�`�V�C˱]�c+�L�<&��i��z,�s�] �9���6)�6sH "v>�ꎻ��:3ޥ��ο�V&fl����K�1��vXGy��jP��:���"�k�q{�W�⫓�#�1���V�B��Zi����pw�]�eX=��>_��f�5k�,ZR��',KX���A��{�(�~ - �넿��g��K�P!�4�o�40�,������Rb ��L\�Pwⱹ�,aDk�~�MZy���%~e���}�x�VOV�d�@"�� -y��ZƐ��w|�'�E�z5���D -J�G�Oc�|�0E�>��C|�$��"ϠѨ��F��'�[_Y���̒�B�G����:�O�B,��ݏ�b�*����p��r��7��MI�k2ߧf���)MrͿ^�s��pxT�A�L�(T�E�c�������G��d�u,; >�8R$b�@��=�z���5���@�4B�쇳�~��L�=XE� !�'77�����l��*����a}oM�$~��F�3��)�V&�b�1C��F]�'R��,*��8=c�{J���{��a�����.Z�#$�<��:iq.�Am�<���2�?f˰�"�����r��I��v���\�S�KQ4 U��XbV��n���)�s�/cU &���ʨ2ob'���������ꬴ�tc�]������\o�V_ƾ;,EF����\�Ŋ�5�<9��2����0йśv�Ve����:^���a��9LD��:���������+�qEN����&��6��c�n��&� �1��SL�8��z��M)��> �C]�Q��A -�`��6h�!@ �-Fb�/�\���}�,3,>t�j:QU�0��D�5L5�~"8j�2 -�5�f�b��h=����D���D� ��:�����T�ȳC< W�Jy2�m��b�f��A������>���ơ�-7e[�v��|G(�ӧ 0|�#+'�W�=h;���m=h�S�.�Q5p�2��j���䂹>��!S�U=���`�U��9���$��Q~��%�9;��4�Q�H,j4�L݀���r�+��ji_��c<"��J��f+���ŬN ����v?�B�|KYv4M��2O�|[�^��=��z��+����G79Q���N�}�a6�.� ���dpNe�ޝxĿ|�\�m���x���*5EL�� ֥U���lu���@�m�i��_�"�n�X��#�$��{�H]�D��}B���TM��I,(K���E VH�����/O�0�ɝ��)q@��8����[\�˘�������r��:���� -�� �~W�A������Ui�:��e&���ύ4N�O!`ou�@���)�0� u�ކ��f!'�v�d!�0�}��NcK����E7� _�n�;��I����+A�f��[X�tݾ�z,�q�F�s�zNjo([+��p��iS4����co����:I�E��O��R-�F���k�ڰ���:ݝ���꯱��ȱF֠��[�mV��ewG�W1�3cf�Hj�J�C���� lU5=�m��U=x`sDZ+���z���_*�W$�9f�l�㴲�z~�=/OW�|;��<�s�6����@3y'�#ى{�y�Ĺ�kN�t�H��;��-���?�����.��@��lO5m,�~c� ,��_��|gg��w؏���#�j����޼�g����O���vpͣX��6���B��s���}F����� w��96 ��8�.��\��y�"r8=��;�g�a45v��SF�7\�l�ޥ����0툳9�f^s�ͣ��s�K<�c���o���9a�z8NР��.�B�`�&� ]�1��@,��/�lҒ -��sxMBc����'�8psTVǷ�����f䢩n�P�L1ū:�b��A/!t��̎y�پH@���F�,��Gc�O`�8��� L��"\� -#� ���삣!'!� �mh��1gRL�"��%4H���2�k�����9w��`����y�Є�p2>�ب<��Z ���O���fG_��b���˰��d�N�ݶ5�f� O{�a�h2�ÃW��|E�ښ�/��u0�F#�����x�`��;֨�:�Vw���>�� i�Ǭ�9팡�_��Y����N�a�~6�:��� a=�{��P6٠9wZ�ns����?����Z�f��j���Y��ޘ�N�ݮ�c`"�.<����Qג��vgh���Y��r"�56X�~�~����� qc�����A;k7O���jN@9頒Z��u�����hr4wƓ��>��m���~괬�;��Hp��U$�f � -H ����d�A�!�Nol �������>����&�n���=���~A�( RE�}>��9��G��Dq�@��q�'`y�3̲�����h�Z�������%�vFا#�n� �OJ��׌9�H��s̚�O$^v&��t����Ѥu���g�/s۹��8N�&�X?�]zn/`�E4�ʭ(��^�ĈZ� -�~��#�(:�YW������1@2g�)q�i������Z��V��|�%��������>��t�5R�Tjԓ�`��_p�B7�Gz�B̆�����!{�n�f!�3������>��I�� =�0I�7thO�s��ck�t��]J$��Xt���Ԏ�s\\ޭ6 -�Aa�������/l��Ƥ5���;bǶ��}���*�K� @�m� 0Xv� -���I8,eU3� -d��k\��T+�.E��|/���~�\g1N��n�}�A�Q��Fx1KfNs�D��jG,ޯyp��5@��@u�Q��yď�0a����;C?/��2~� s��g������ѻdU���R�� -�Pe��������pb�Ց#_L��OҼL��zNxѢ~J�0� ���ݾ�fF��*?l{�G�k0�K�*U����xͦ�Tg��� h�ت�i�b�A5�?x�? �ǧ`����`�L�#O&h�9��*é�&p�� �;�vgʪ N+��0�ݺs8�攉�r�^b�˫�1�n����(��1���=dDlp2�(;A� �vQ_�{L; �iQ���s���٫�`9�h{��b�q���pFB:�yJ�����>�a����1J��6e'Y)z� -��^�9�+�;����؟}�4Z��4,F�|xС�ʬ�\�@O-wO+gZU���R�hr�� �=�Cj�!n��:��� ��6$�VEa�(��$>�jA�ӓ�E��"��������/�?@H���$�w���V G�1�w4�L� Vy`��I�y�2���?�W{�g�~�ɣ�S���`���,�ȳH�S�c-m�r���"'������^m�X�|�}��E��e<Lj�]�峦3��>�&�`���!a����X7�~��"�F7�芶� ��I� ���~կc�*� �"���K�s���.��Eݷ�*���9V6�.b���m����;: �1Lc��� 2 ����0����{�9T4��Ûl�1���B2��^���������B���<JF�Q��c���VJ�����}�ɰ�Q��"c����CT+����H��_%�4����E�L�2Ԓ�Lm1m�W!,Eәy:b�gF�u��In�1Z�U�1�Δ����[:��� -�DŽ��Ef���ҤA��Q�u��~� -@�� ��)P� � ��8�մ�!#� �\�\l�����*N~�3��Aq�XRC���an�FNس�{fy�Als:h3��4��\�ݬ�,��e��Y�Xl���\��4o��dP�&�hWFm��#=�H9�$��K{y�UІ��C&�\�� �[;��F�:o��O���{;��Si���}t?��Z<-DCs���K�'���a��\�R��5ɀ�Ā��m��e -��uz��7�#c̺��`=n���|6�1�4ҧ[�'Bj@ -x= `2��u�4$ͥ���؏�td근|��޸-FSO�k-�& 3�7k����í��-�*Z0���y(hP�U+�������(��M���"��Y: ��t��n^@�_��o/�=���;�M��d���{���Uq�\�0گ!�$�C���>������A��)KY�4:����3����<�"�����L (���<�sLgB_+'�S��9bʠ8 g٠<;4�-��� K{���������m���d *h� ́4Q���~�(&'Q}8%!�93>����B��}�Y"L �XS�cI��^`� �d7piW5ǘ ��k�{�60o�e���P�I�: �\Eao4�p�� Gq��Xt���1ZЦ�#ţ�pr��#<�����?ޡ. u�U�p�{�j;x��%d�o�ܟ��t?�q�&<��]���uoo/�ݣ޽�!�u���u�:��1mj�q��t�O�ZM�(~�jp��q�N���U�h����IyY�"+#���To=�u��L,�xX���X�9�'I�6��\ 'z�@~��/�,C��x���o��g`c�d(�L�&x��z=�1�d�Gn�h�q��h#%��h��O��4B�#$�M �k+��=�#g��<�� ��R�پ��5��3|��P�� Xb2�v -?D��M�+������9��bY_ؾ��:#"RN�c� -[RrqC*%�8�R^I��ɖXutҬ��ac��������ov���;�b!�[��k~�"���T�j�sY� :�=���|��e,�vY)��3����3��C~1u>%���G�%���Y" a�~ڛ��&�64���R����&�=��T�P]�0����l��&�V��-�ä5�ku\8��@���+5^ -�r�dp�u���A['�V3!!"=oi���M��+�1� -�)G�nluZϪ�/�u�k�ٮ=��.��fu��C*���k����*l���<�̝���@"��@ѭ�:�O�� �g^ � -�ػ��{�� !�aZ"T:,�"~X\ UM`;�Ϟ���`tB�������ߣ�?0fQ���!�"�+�s�x'XSxr7@S)�L���]�X�F�?�"��%w@3Y�̀E�5�6��z"(� �0�}��ɨ4_�O����{�Um k�5��p��+���UZ�>2~�5�$Sl%�Un1�U�\�D�@���U� Fo(�wS��sD��s@��ZTT�Te!�U��X�t����� U��>�����.R��|~Q1�� �*�X�d���@����n�%LV���@bCf�E� -V�< ���R��(���2 ��t�L�OxT�A:�e�I��%e����n1������A"��I���&X��4����.Z�9���L��/D��q$ΰ�|�����>e��K%S�� -Y?b -��78Fg�1�ٝf�m��y���BFS�D�S(��V��6fQ�8����� - +>����i"J��D��h&�<���/(+� ��= �-MC��˳q��u�'S��g*�QVjmeuI.�>��� (��Q��4�����"�z������ev��F�"��-e�{���Y`����*�PG��m}8I �#~��J�Tв�Z���Qp��� \�E���-�����GJKr��q8Y{�i��˶����Co�X<��$�/�� �m-`�~� AZڵ%+����%�*U˷%'�G���I�4�����,-�b!��2���[���������f�����+)�T��N���|ړ�7��2�ٷ���Y[QN3/4y�p#IW�?ɦ��,�&Ar�q+69�\�-}��4[/I��)-^7;�\�q��� �iXLJIװ(�{�����D���v�Hw,�{�V�*��=�����V3�p���X?�̋�x��E� 췒���7��r�!k��ցX��LMP��c�����r�]���P�s��\���L�<�=�Ҟ3���3jy�(->+�jT����C'�:z�**��I�xd��:��Nq{�|KpxQo+e��X��TV��?�l<�/د�� � ��߾��ƭ1A�L�h>��6,�z�(m�:�6ޣ�T|�C�ye˻�@����N��TO�o�<1[t�*cz��.��HR���K��9�\ڛ��\'��� cA���s�_��/J�U���iȗ�y��A�I�K�VCo:�F�����b^���\���?t)������9��nUv/���(���t��_������Ca>pw_ޔ�5J�\�-O�.�Z�Bi�)�X��=�D�=�*����pT�� -�6�ܩ�:��^�[�}�������U0�s�Φ:� ���U��5#��3��<��Μ���cq������� -�ix{���`Дs�Sz�p�B�9Ww��7���~�����(��A��7HW}U�����Hp˼\,�C���9}�������T]O�0}ϯ��"�TE���04mo��� Pe��Śk[�����}���i�B'�$��Ͻ��ğ�t� I�h5ew-�"��k�P���Ea0A���������b2�a_��ـ�Ig^����6��:�2�:B9Z�5���j�Q����wH�$8��K渒�X0%�3�9��Pr������x�ҵܞ_���v��=���45t ���߹�7�9�� :o$<)%������D�#� -�4um D�痿��t�ѿ!�7@>qkё��=�c5.G'����>����)�:��!ˎ.5�`>j����Ǔ�'���۹���$�֨���y�Pǒ�,e�r�(/��-G��JL������=���H�XgٓJK��Α�|�u}��)�Pcq� -�I�#��^�sb������L�'5��Xn������ \�ݢr��Rp�{�x+�P{/-���N�� ����АjOO�j|���\����n���öu����n�0 ��y -8�j���M�;�H���5RI#�AB��N�k;X5_b%��|��3�9eqOޡ&�d�&��{�"/�NJAU:G�a�:��>��K����!��2,�=�ȇ\o�wl(��L�v3��EL@p�F.|�F�.X-������^8h� <7Q#�S5����k=/�/�H�>] ��L�����v�o�-���<��o�M\Gĝ�U�v�� }�_o�0���}X��s�s���K�bBj�J��6��dY��k+0@T˹�w�)�/t�#I 0�2 9lp�,��f�(b�C^(��b��aō]�� /���5dc����'"�a%c�t�HvJU�ga�ȏ���Y��a��E�.�(��fWp ;�5�QS���"�J���{�36�U����uZ�gd_Jf��$M����d6iȓ�0iyU1��6����֓�vh=ѹ����� �kZQy��20[�<��l�?����w6�3�Gyd���J3���` -��{wK�4}^n�^����6�[SD����x4�]����gVu��f�ф�3Є��n � ���#m[UL-Reí�kQ$����fZ�P�����w�UX�OQ@(֢�����~Gcɀ�tKIg!y�� �2=+�f�<�a �G4s��}s�k#��:���V����a2���� ��� %�p`JZg������qM����!�9u*8�ݕ -- -���7� St܌��W�-��3nX��`4��%�r{8ľhʔ���� �fe�{��N���ԝ��88]�>�.�� -��ǵ��ך��7(���\�&{agR�T��� -�+ -�J�7w�_ -�X���Ub�� Jঞ����I��' -�qʨ{~� -��O�¶ �~�d1Ϙ���X�OOc�z�&D�k����/���� -W��m��e�h����(>��V��� -"lFt���TFU���Q|Ca�P&��wt<}��F�H�lv��KH3x=�Px�s��qp�g�Ds��Д�0�2�z�|†�8��I����cL,�V!ǝk;9�& �?�w�6�Ce��f~0���G�<��Z՛��|�ju����{2��&��k��S"�&�iE�J���ڸm -�I���=Sr�����_Vo�����Y\��m�4 ����`��RH�FU���a]���䁶$��ߗh��4D�N}��ܒ�-��3�ƾ�{r�ˠ���^�H_�^�,��*d��jR��i�l�rD�ϮU[c������V�n�0��+���a#�9�Ӣi�^� iO���("J�I� -�{I��L���"����f�>^ei6d�:#�g���j�%7�K�����E9��JΑ��P��4�n]L&�&���( J�����g�=�0��2��is�fSkN����3>U�Q�v��PlPP�g��� ��5��3Ψ݆�f��^C� j���@��F��D����)�'mrb[4ୱI���[ �E@�5�D��7b�a�)�U��H-�3�b�]$Ɖ���)4���F��m���l��� �%&��U|�����"`2�ƪ����w��ln}���6��r�+��,v��p� pa ��E/N}e�m1�W1n%R!�i���k�@t�r!╓�3��R6$�-FÄ���A�;�-Q�"��vԺ�9���nO��6��xߡ�SC�U�]�����0Dt�!��COϫ�kO�v�+�:�x��Z v8��ح����ԙr݆�$�.+{(ͮ?�k3ûǪ�C��:}>��c��(���0�XM�:�u-%|���쏙�v���"7�P�rh�%bK��,��0<�j� ��h#C ����0�F�k�]�0W�G;l���L�ߞ]�>� -��Q�W��@K�U����p�`��Xms�6 ��_�v�$���ݭ_�ui�f���4��.��h���ʤFRNrk��R/�%J�Ro�DAx��y�}2Kv8�S����Ռ��T޼H�� �ϝ��D)8�D(*�4�D l��tN�VpS~̗�1��-�a~X DJr_�:*��DS�L�U1��8[��w���R�-����~u*��,�(�f��x��L�߼O�XL�x$���/��Ĝ��-�P,��ʊ�����?Cs�L�|]�z&�-pz���]@c��Xd�B׹�|�XnO�VO� �Hy&�"s���R}A��0 8�j�����/H��B�%���P�1��>���&!�Dd�5�i)Bk��� DT���4!� ,c\l¯ ��)B��,(En���C�F٤w�` -S�jźk����p�e���a+�&�Nǒ&1&�����~�����#���ۄ6�x}�7�C�?R���H y�Y�" -z��ȤBn�tf,叏������ڀ�������Vj�M��Y����)�����3��N)��H�=�e��W��ڇ�ljř䵾�k+����}P'FEe[�?4�uY�{M��v[�7'�����,r�t,|ZG���� Ҹ-�9$ �\)ͳ9��_���E��'ke^���>Ve��r͘��.�j�1�ϕ}�"kus,�6M:|/632Y�x]���r T��c�;� -+�g`Z��Z���f9�n�:�*lX�i� �Ǘ�%tukn#��\3c@�R�ړ9s��� i7�%թ�� �.l�����z>�7���%b�t}����c�d�E�������~�`Pр���8~��F�f��bǖN%�΍[�c��-�X�oܔsiWK�S��욏��n�K�[#6h�ad�^2nnmM�6Φ���$wWih懣0,s�S��E.������( в������XL�[�bW���ݏ�wo�yv��f�N^�'B��XwU�|q3��Ԯ��}���y} ?3��]�Y��#0�I��ln�@:���+�xP`Q�����ص)�F���������7S}x χ�����D�N�2��zF!H�����pT�V�4b�?�L��`4��4�T�愧$Q>b�O�a�mh�[����� ��"��߃�O��yS���Mf������tF��.z�/9���:�k�8�D��L~�oĂ���%2-�c���FU� �Bx�xf���)��M9&y�%�� ��8>{�- +,�D�AD�aQq7YU@b"�Vi`���*��|��S�-��1k���n �� >)S� -DQ9�v e��bku��;"�X�0��-_o(�`����&P!Jxh��I,�+���g�+{ȫD�-"��ۏ�us}���])/��զ����eHP�S#��=bRm+i���/D��(�_B[�}�_^��8�C7�O�]�M:�\���[=�� �Zqs�:�?�B��j�q�67}3׾�%6I���~���i;r� ङ{��+��f.3�K��߮��j��~ܬ6G/_��dn�"�n����ׯ������߉���vpO�$&�����-�=t���>a�cјF�B;v���$�n���\��)���ȡ��� ��,�hȣ��H���ۄ�C�[z�� �ӎ(��h�% u�& -<>����?��~��w� ��q1��ɻ �a�I�䠜��m��>� `��}>`�T&�C)7$^L|`�|D��[AR���4����]8����� ����vM���3��G0/!Ddm'4�l?.&�M-("�8��4�����‚��3�)�0�9���Dn).$�$$4p���,�0�$5�s$,G����"q�La5pf|����:�����W�.�84�.t���3�J54�sc�Ykcrz ���׆~~a���d�&Q�ch�Z�~��f��\5a�s�y�O�^폹��&�D��Ot� uj�9 �t4Y�����f͙E&��n�5��4�`2;#��1����>ѭk&�L��(� D�d��>ZLT9��|fjU��h���x0@.�>kS���d"���E���UO'Z*�6�P��kv��1��H�� 4R�k�!�Φ��蠟��K�tT*�X'i�0�K>;CV��Դtkai�|63˛��Yi�{2���p S�K`?p�A7|�.L퇼����bn�i�p�*�3[ϦLg0�̸F�h 6ru�A;L���b4���0��#K��`OKP�L��~�MG�ΐѕnj}�� �D=~�����& ����r��%�Qǟu��%ѳ���bt�Y��O۹��(n+v��.k����"�ݺ�(� -��aĠ�"�|��A���c2I�x���ДX+jR}�ɿp��8��1Xzw� ��I<�$���z4���ℜ�S{_�q��:���$�id��L"��>!���Q���&�I�� h��D5Kp��3Nl��ln��u�+y�0x�ⲟΦZӢ��>5��8!K�Gd�%���x�Qipa�+տ������4f2�ְ�������(�dt12a�A����%ݤ� ���:�*\�6*VPmo����6pX�}s�Df)*NI�3�������WʾK�^F�Ȕ���ff���t�O{�#��}�^�oH��yGzG�8:�(���~4+��T�Jٙ4hQu8�A]_��V��DV���wvW�G0��*��~���̮���j�-�"5ⳓ�oW��?��4WR�����$Ջ��q��e�R�����G2���Q�/�h����0�Y��H� -X8ş�x3DtuF}�v˰� -�0Y����q�q|U�߀pY|y� đ�v����`:o�Q~v*�9ڰoJ�@�I� ���{E�s�=l7}����g`�� ������S֮О�A-���a�� za"'����]Jسx�{ s®ع�v�mLY�����$�b"���E�z�<����:"�0�"�bl��+\ -���3ֺ◕��/5�[+��Z�bl��Ļ�a;F��[���~�v�ۡ|Iڵ�!�� 5�7�vM#�)�/�>*HOH���W�`�n0���#����$�Qk�n��cJQ����^��s�����<��d�7���5s�C�#ys ���_:��~�V�{vm%��Q� �����v@�od���W�A?��>] UA�_ -����^��x����X�P�`u��4Y�}�k�<1;g$��0yѰ����r�>��>��6�v��&<���?�� �A��U������4�N� �V���.� -��}.�xg��9�D(��B�]L@ -�]�A� �cM�$��@v+������, ��<���<��z<;&�3)�\�I��]�BSNW�P��ɩbE�F��lD�®�d��u\^�E�ur�%�� -vd������y����d��[ؼ�R�q7�|�:%��$`e3��r����Y�l&�! -O3P]����ay���p���u�u^����ܤ1P�~��#�B}���qo������}����1N���G������]5�c��O -+��7ހ���+��o����x�لQB]��u�j�vd��Ik�����6h�M�{ޤ�7�7�f��� ��Ο���ů��"X�����D�� �+5�,=<B�f�a�����K7�6��ķ+�ߍ�W��Kr�P�7J�m{�kU����gj��U5��-�5� ��B�|DG -a�J0�]5���Y%�m�ϻYv���۾�s!u��z��"�]��?��㱗&'�UW���~?-�w%6�-!���߽8��4�+ |Kh��|@ 77cݸ�������7,T���Pb�*�AD#��E�Ar1�lIM�!��Xu0���3�-��u�E�GGĥ �����$$9��b�I ���U.]�[?i�K)�$fq䩐�-�{{����}���ƄV�z ���@�ɽ���1�S7B���\�)���- ڔ�LT�"�N���\B�@x��ẳ�����&MF)N ���* l?��⌺d�0J^ӤGQ2#�5v�vV�Q*! �ke#��rWGkV@���$p�v��=-�9+ R�Uy��B��)t�UD_�7����k����2���<�u���~Q�;�ro��ÿeQ�YI��������%K2�/�i���kx0��4�����AĀ7�Q6B�S��a�묙�)=X�)��Ѣ^��O��Fh�/lU��O��ӄkp燷��bm'Ϊ#F�sa����p�׿/;ǔ�5�Y�j�{Y+Vk�����˺�*-J�x�uG*�!e��rZ�'�_�9��P�s��ML+����S׋m�����������n�]+��k��8�7�Vms�F��_��x�R�_�N?ԎS#8[7�@dǓd2Nĩ�����w��9�L�2��>��{{��c1[tN^���+���H��J��=xsz��ћ�7��Y��Y�E��m���8����2�� - -�Dq/�c\�-_$�*�t�,S�C�'�T��\��+�4��G��b� xH��B����2I�i�A�Q!`!�yZ�"�E!���YT�@�,�i�b�')�)�4�Y� ��)�ӆT,�^��)#$K��D��V�T.�4F�h�*��pځ�d�F��(����6��K��M���"u����s��Z�O�. -�G�(�(S�Ѕ�ޭD�)�"վd�GsA�6:,��۪&^a�B5D��#L5f"A� n -��2���J&�K�$�#Lq�RD�i���Ѐ5m�"�>Cߔ����ʫFS��I8��Uxk� �y�{7�f6��p����|~=a�96�0]W����q����><�g�w�ޏ|����#�#�M7�,0���3��{mbh5�>�!Z��QQc;����!���}���NG��K�0� #��5vL��Fc� (E��c�!����v�����8;3�$��`�!Y��*&ls�Y!e��d��H�1 1��{�0#ӿ��/"��1��>��м��m�CE��>q�q?y8\{�����-���Z�q� ��# -�������#,���ǣ�{nE�E����޶��su�(���.��Ka���:��գ��3I��¶%FE=�V��k�_3�b���-XO�����U�[#�)}]4�V=���Х~�}É|e�[�u��j0����ɸ\D��苠ce��d���h�g�����n����ƾ.3���Ҍ�� N��['���1HY@8��dž4��鐋�AϮ@��R�H)M�G�-�g��c=�AѸ���_�YԞ �����O��X'�����bQӧ�p.�Y��{��p�����{I� -u��7�"۵�(^x+�c�׶YN�f0]��b��o�Np�wM��) �R���4z���i��[eAk+��]�m8�]ϲ1~�O/�X(���E�����ѱ�DϺ[�#U�wp8M3� L��?���J���hBVz�v���a���^�ν�-ۧ���zv��kU�r�˿Jl>X�rK��ק���7C�/��2hh7�T�=��;�a�qY�iw�m�j� �_h�ׯ��*�mt���ҡ��r����o�X���?� '��.��2�t ��>�w0��B)�Q��l�h��K2Rnko�|�����O7n����A� 5�?z�Z�[Uxr���^y����V�l�ӵ;:jwd���k`UF�� 8Q��y�uE� {*��~���S���Y[o��~ϯ�V.� z�N��V��D8���f(��8"K^]����/9�8����`O�6�����Cr8�/��nwG޾=��0Mv�ixs�����Ǐ�~������&��x�g� ��_�����!m=�@hg���.�E� "$���8h�B�~��-O�`�Vkq�l�۠@��#BP�$~��q.�.Q��K�)lY�ӐE�����5G�..x(tI&f[N�Z'��YE��J�LٲGXsJ$�$��)g��6�9�aB�Ib:�ʈd�&�l�`2Ͳ�)�P7��K)��2Ѳ��wn���s�Rw ��K��fΌL�pр���r̳s�mkf8.�>]x�9Yy6>x����F��+0~_:���y��L�C���L����Z���8b�h�X�顤g�Kj�B�9\�����2�+aunz �8G�:,u�3�+Kwi�r��k�83ݩ����=�@�`|3��e)=F'����@���2Js���t��G�=�b��5wiLM�`�n�G�s%�f�RX�FĬ�����y}Y_w̿c7��JNJ����KE� ;Q�>w���F��L0��3�ʯ%�G�� i�-w9O�%m��舔谒k�0�>��G�gq�xp��Qp�O�kF�EM��P�4��8�`΄��A?⬖?��=K���7�M���g�c�ؤ��2'/1����C"`v,e[�n���(y�mk�)��/愌祖�Ta<��sL-�ݗrN�}�O�t�h/���9�� R�L�͠B��!i���5���.����n�0��� �Ac��ZR���|+g�ϟ�eٗ�ݫ �\��T�Y;����3�kiT� -�:���e��C��V����[����k˝�g22%H#0��[�N�0 -T�º\��BM�@{�%��)��3��������S�$+�7M�;��K a��L\jR�pn����Д|B,�� �0�PY{^,�}�)��]��ʐ.{Σ�cJh�V^H#��~����O9�gE��q*~�)�i���S�y��z�ְ���ݻS�󏮈Ko�|)0��Ky�ǝN5�Yi�;pɕ2�O�j�k�I�F+=J��,W)�+=j6m����(]�t�V��%���JI�H��QR�;=U�>�Z���w�O�C�&jt�8�*Z�l� � NL�4{ҽQ@u�J��������>��:�Ƃ��n��S�J�v�(��*�21���J�? -�o�R -�߇��е[���Sϵ�U+EƗ�pB=A�|ؕ��>�C.Lj��>�3��lsqu�+�d�u�5��lbھѵ;���~<#����X�z� ������;~��3�����9��7g�=�AB���Dz�����lrwi� LX~g�?��i�q�\{&ێ �-Lu.P9���Ų*&����P��x� -J�S�&e�}�Bg�mh[a�'8}�)hm-�F���Ol�XR��y�|k���YT�߬4I}Mh��ޠ�M�o*Ջ�ƍ���bъ����_��b�6d ��6D��C^�"�*�]����)w�K�.����o�U�n�8��+EN�&A��M� -�6YrE*�� -٢���%��"��C�Z�n��eu�%�̛7����?�������.�W�]�o�������7�7�ѵ�I��_�n ����n�_��&e .ڀ�F�'�_�5%2/L��u����Ρ5��j�F��uQg��JWƃ�y�ݿj�T^l�Mf<��i {���id{���_�Ǭ��8e���zU煍3.��ͻ/�����ImT�ޭi��&C�6[�'kꕪUSl�ס�J�8��u~� -�nʬ����6�u�K���[d�?�C��ڴ��'z��W�/ -4TY#u���n�\���a��,\����JZb'V����wXJ��H���Z�F�J�:G��=�\*�H�dB�Ib;� �"Fm�g��o3���g[��Ӷ��ьT"����$�}���,�LVh����U�fs�8 hD�F"a�Tĸ��p�|��H��y�P�!N�-�!C�$C���̾��+"��U�������Y��8>�D�I~�Ѕ%O-O'\0� - -�8��&�̧�˜;�RN=L"�g툂���')gV?��"A�$] -G�(�*�L FN�8r5�Xq���V �<�)��Gn[ሕ����zbV�S ����B6��O�5�@��s�Z �ևu�fNm�nӐ[�:hg�m-�)���Y�k T��Wy����'�n�m�f;i��[�,w�Y�gL���k7��q��B����J���q�&<�����^�ܔA�ģ�R�?{���hd�p.���pn��C� ' �����0%!��Gw����Fg��*1 �cWG�v��a��wg~��3*γ�1�Oy��`��!��cO�p����觹J���&w�C�L� v�i�b �a�S�}�mu}����7_[;j��� ����u�:��S�n�0 �����Mݽ[�J��S��ڪʂ["�9I�i�(e!�e������xxR� -$;�V�#|���H�5y��~�����i\����\�1��� 1<�6$�~�q.��Y�82�0�R ����w͆1��a��%8wW� ���npe�2�ag%/�`��E�!�M�(��o�~s�6ۍ�BipbbR����΀��O�V�΂� =*E���S�?KJ�O��X��s+M�N���b/[�F5����ֶF�%��>�>9KޗY��7�͋��לJ�ª�?��Ax#��f6w���f�U�.�eݻS����#�kdX�Z�R�[o�b��נ�]3_�(���ko�8�{[d�v��+�.��Ul���^Kn�H C��D�-��$���~3��D2N�l���Cb����p8/R~���b�d���'�9i���;��I}� /wv���r��+b�>%3׿�a�7.|w�~ߚҷ8ژ���F4�J�[���t�Eq�.c/���O�2���I,� eON=� o�YΣ&��� �����<�zg��EM�醔,h8��N�" �zS�_�1���g6 �<��L�Ḉ ��x/��Th�Hp�5 ���b�'v�XD�_�+���ބ696 ^Df����U0�d�zsni��Y�����%P�H���i0YΩ3��a�6�K!��1 =w� ���Y�Q��EߝS$��a~�wG �WF)!s���RT$�$ ԟB'E�Z�AL ������ :�D��,�mH��j-�� �z�}!���-�N�#�&v���h M����cv��'�4I�?�4��9���C��<�9C�`����3Æ����>�������m���Xǃ��`���s,�n���:V�}�&;C�ֱ���or�L�`�?$��}_��k9�ج����aJ� ��c�G]c����o�Y�Xv�kX�fg Ȁy����9�>2�])����."<0�X�k���54�r�j���n���m��w82���00��o#��~�1����c�$��tp�ڣ�y����=:��9&y��w��ms��j��>��m&��m6a�hb?`�A7|>��qY=�G��� �� !�Ԁ�&�~�� ��?!^[�&�xd�sX�3�(8�a�ێ ��<�Y�3�w��f�mbo}�l��Tkh�c��?0��g���:7����ρ�J�D�D}�=j%�Owƻ�;�t�)n+v�,p�칻�=����Gy�[�F4U����E�#Fс�!]�����NT� ��p�͂j�-_��)����L�*�ݴ�%�Rc7l/V+r��j�e���#���R)��%� �9�]���s��"�ھ d�{q�[������p&�H�ww�W:S�qg���R��Y�3+�T ��fJ��W�U ���3��S���l�OX9CF�D���蠢s���q���*pu��!�9�����[`�s��� A�ǽM�F&>�I���+S�H@'b��)t��f��o� -}\�3U�!W]�>�pH��,���e>�^�V!F�� 0�u��w�$�L �Z� � [Mү�kDu�I� -�!���s;�U9��m.����z�dL�D*�Kz���0�4�@Z;�0��0+~��l��2 )����iT;و��6m���Ov��Z-g�Y �$��G��d{������ -J–D�o 8�go�>��� -$�/ؗC˘cւc����2����\Y���٢5T�a���T6Oum�����x ��7u=��h��6��|i`I?��$6ŧW�]��� �C4�M�)hHMl�r�T A�� RX$q׿x�/�G�d�!ӳ8m��77��P�+O[`4����7;$ݓ��NPc��;�쭊�"-���0�7�n�[��Zs��H�mi�bf�{�N����|��W�(�3���!(��(9�Lę?q�ٌ���8��l�4�.�Q��Ɗ�-�����j�WY��}�\#{U� -99X�H�s���$�8sgQ��m���jP�K��Y!��P3��+�@P -%p�ԝ\p;����ȱIvY傸3�2���xr��&��t�1�c�rO��W��xd9<��SL�����;E����*JZz�E%-(�tdo�e�j1�C4!g�Nu��c�C��W#����0w�^�֒P�Q�� �4>�4����ɮ��)�>�x5��fMG4s���V��ޖ�����KnnC����FS7ha�€���jEL�s���ZC��B`�V|Wy$���@�[����Y]��Ͼi� �K��<�̳�v�rv�I�K�^/�$fDž�,ɳ_�gxԉ��U���g6n쳕H��vD�%.���iWU,mU��'��LP�����k}J���oh�w���8m��ڑQ�@}y���!J ��A7*s�se�r�-i��$EC����fׁ:�FJ�bVv�ek]\I�"�M��c�JfЋ���3�R���(��[1g���{&x*!O�wd�e�ޚ��04?�����F�1 �?�v�~F�?D����������]���F���P?N�������{��c�&���U��#K>���8|��gm%�AG_ЛǍ����)b�t��C�t�4b� 6�+�cF�`#X`�Y�Py�$�`�`g-�����K7��6���]�����;� =����p���W]q�Qj�ڠ;��߹�]����;��!��UJ��]��G �qg%�� *����WV+mܢy���[�j�2Z�}"��N�.%+ �I=Q^r�V[���(�b����5Y��~�z!�u� 3Ip_���T�u���Q��[�M�,��� 7d) ��3��u��I4/%�r#�n??�/]���!x���= ��_$X/iv�r�g�'3ewL�k ���=�P�TJ���Ɩlshe+N�����c"������3�cL�@a6jmg -�e�Ʞ�lW�;�'Ea��G����wX�知m���f/=�i�E�+�7���oZ�0&����M��.:S��\����2��K��qY݉j 'L���Z~�`�|j��wI��Г��Jg�R�HߐW�fNt��oH�RUv%wVqާZ�����qQ��X����O9�{HNY/$��,�L%�\c���ҧ��H�۲<�oT$GҸT�Ɓ��7�4F�;��͇���=���h�IMԓ�,��L?* x�Գ���ޒ�� >��auw8�����j: �t�$���v�g��f��]� 5��MV���%��c��l���9x�\0[i��nw9��Y�lں͚u��p�!�S�S�szSVM�Rm���KO�*6�P��dǟ�.��S�Z@��[�|��g�B�/50 kx��<����K�(����7��� !3ݫ�j�.�O���%��� R�kL��+;��m���[͙�ѡ������1Ĺ����jHV���D[��8OWh���kF��iCr���MQ���D�y�f��]z!������`g���Ƶ�쉍[kV�eY�흵A> Nq�G�MS�L���t�w� -��45�; -���Xy[�h��H�� ���ߧf��j����I*���q���+��=�J����+w���r!�ũ�[^I�&�ڤ!����f4 ~���ƻ��,)���,4zc���r�s���U�$��Ò3۟(��R_n~��Q12�hM^�����^� ��F�6W��͚n��Ȧe�^T�䲦��*gӯn���� ���vJB�:{v�H�D���sm��}� -t�\[b��Ҁ��aLg�����Q�S��T�c�Cp�� -oVp������/�`�~Y�5�p��E,�����MȻ�,�O~EU S�t�MV�!�"���&��.�0��"#��[)��b�~��̃e����aI����?��֖�8!%��%/�W��|��"�V͏�D�2C���iY�V �R{�he��b���$��_�ʎtHz��9$(���>�2��)��'��QAN�0��{�Q�<��= qA�r�M6��k[k�!���i"�^�dYk��Ό�~��⑂ǖ�m��oRt�a�R��*<�=xF&�ȁ��^�P6}F�6BGrE�C�gmA&Ap���׆��f�SC^�[���8X���~���D������X��(��D􉺥��J�H�gO/���\+���yUc �l1��.^�v6��'� � ��˩�C…jbl$B<®D� o�ē���v�c�����B2��T�ӧ��-�ɶY��S��c�V�G��R�n�0��+���%��� �7!c �d��vh����&i6P�4�̼�Y��8�>Q�`B�-�������H.��=l���56�<%Xa#U1s5 ��x� -�QL7Qe0)�D�36�=S�X4F5,+��P0��ܐ@��4�W�jZ����F׭+�qF�� -b��ݖ� � �R�(&1���:�i��M���Ce{�,O�� '�'�y����uW���A��xƚ�&;;"2�5�\´K��&Un�\)�Sn���h�Yk�fp%�Nw�y��F0�$��V��>Q�=�� ni�'��wn+'6��O\f^c_�,�����3��(N�[ E>��9f�i��G�8E>�E��AއY�g�E�Q-�k;e��?���T"5*bw]�,����x?�Xmo�J��_qZee\�$�UW��-�I��^�ɍ���f�`��\k�����1��~X>�0s�9oϜ9����l�p�����=u��*�g9i�6���������odd��N$O"�%����x>����cR�2!E�,���\!�0�y��y�&$!-��(!�.��P#�( �M�l.uz�����O�9��0�F���7�-D6��\�����(�M> r�`�q�%�4I�0�R-����f㾝��ia�$ �j)s��0��q��SE�4�&B�"�"I1@�jD�,��IDs�գ�d,�ī� ����hm|��r.�\%� -��'�[ -���A.�(��69*� -��P�˶��&�\��5V&�vZn�Xc���5V4L:x��HB�욧��u��`P���XGH������� 1a^b}�l͘�ɚ�R6x��XyΕo�&�~�:wV��Q��&u���k]��t��{��a�0j�����V�+7 � ���?��y�d��0��5l�2=�,���,�Z'�;>��[ˇ���[�r���t�7x4:V����+˷Y��40\����[� ����Lbw{���֭�;�9�O�i����~��p��z�1a���k�p�g�f�g/�w]���u�f���O��C�-�C�g�{Y�Pϸ5��V X-Z����5o�窀�Ϸ��oҵ��T6<ӽ���wA}�S�z�E���<�EL�3�,�g�gپ�Áo9vA�G�`�����c+�<�}`l�J�N�7&�A�,�L���!�]�* 툱_q�l�o]�v��Y���-�l��s-�嬵�,r8T2a���By]���+2zw;�.�Yza�vo6�勞�`�< -~2���4˹`�����_?ɾn��/���|�5F�M�������~���䍪\pD�?������%�e1׋21Am[y� I.�kP����,�:�(FE+��B�zFY�#Y�ٛ���<���0�$V ���5jB �(�6m���c��L����h02�x��_��΂��n���"��xU���r_-T��>����3}�@��c#���٧��0� ��&���1x95h�L&��ph�$����Y�H/��F*�������y#o��Zۀ��^�~�n�ywK�[�B�2�1��&��U5;�ӎr�֕DS��F�h�V%��s<3�VD���v��|��/���n%� lhd���1���[��-�K�I>�Z�b���?�;n��)I_n4����ƫuo1����yǪ�:n!���=u�݌�l�A�W�}�����?�X��^�0�N8��a�k��ϯ�W�����\�nE�ю�����,J�#�)N��\N��i ^�\�|9nb(���_�q�V��{�$������q�5��8����,c�����z-��赞� J�Q�qB���ha����bccQ߉����r�k''dI���񷱕j�D�ǟ�3�;W� -Ym.c�dN�'�R��`����=��P�Ad~�i,�\}o ��a;�7�lr����>��|�_M�� -1D���-,��/P�N�+��Lp݄d�ܿ�S�Yv�1��F6w��X��a���]�@�\��4Pe�� m(���>���o#�Ɓ���g�r&oa,l��+Jn�Y���JI 2�C�C��Z�~l�:$Y-����T�F��Z�~U�g���y-�1�0 Ew��d���L��Ť.���q��;%�/O�Oz�C�3(=�d��יf���/i��u��''�x��֑� ��� ����bq}�߮1�{��S��(Z�lɌ^�Ikw����Xmo�H��_1�"W4�z�.��I�1>�I���°N�bpyI.:���3 �;mN�ˇv��gfg�����[��zu@�h������ -���7o~}����_��I��O�ʬ�靏�O���8�XۈcR�9e2�ٽ ���[� ��ȢEYDiB~R�K���2 �ZYD��=�2�V�NQqGi�~Ӳ�UF�(�ـ�6�L�Zf��(dH�,��B<w~�$��q�%��I�^��V�8�q����e*HCH�y�x -`٬�H�y�a*I�(�ze� �� �i;N�*x b?Z���h��K�ц%�O���5L�r%�B���� �B ��_�,��|��8�� -��DFJ�e%X�’t����+[i�7@V�#-$"II&!6%� ���BRE��r�%6*F�tY<�cM��kp�A7��˸������7.���wm8&�y��Wbd�hp�M�����c�ƶ52�������gc��p�y���� �����d;$.���=8p��'LW'1Z���\��M�#K\ -���W��=�d�ӥ� �x5�ލ�z.� {<�K������2�4�9S�5�C wh����yeN�V�41/,qaN�&��l�Z�f_��#\���k�g�J�U��r�UjI��1� �V%FD]>Xug�q�~s2>���+�X8�q�j�/q�2u�^6Q>m�� 4��&}���\!�#��Wl��Ȓ�;�J��ހ��XEMGR�kPFq���-1��oe����+�3�f��%������D���#���Z�?!C�:Q�h�yN� -�A����� �6�r��I��lw{}�g{���S�~�m�?�]G}�g5F ,7pZ"'�<��X�h�-��r�B�e��2�c�`FaDh~�a�)N8���ۆ���ZkL��8=m[���m �z��m|t�7�ߟeǕ�N�*�Q��GTb�"���1"k;��dTh�c�Op��\�V hƓ�.9u���x���0���6�8G=�tu�-�=�.�4�~RU�K��B��5��L -�{�j�N��4���?��j|s�p}�i�F� ]֬�0�!?�ҩ,�p�)�v����Y�|��ţB��1��sqi.������'�; X��E�~��}��%i��t5;ڇ=:=99��X�_z��|:6���8>��1n;�N2�ϗ����O���A�Z�#�#��(������2��>|������_;���|/V�I�n��g���50�4+��ï����h� �b���c��$����/`���1D|��<^k�s|`�Jm�W'��D��2�qΆ�i���i�������փ�������:��5R��tJ�����8��3K���{�z�5L�����MK8�M�<�b�bm�F3T�$��Zϯ�wO'L"�߱���?��������Ur�U>�Z�Ѿ^g�ޏK���9�/i��D+��Ɛ�Op�����̏p�aRBJ�*NFH،�(Y�?�F~g6�o�d ��6�r��tK9j;�4d�qWm6g��J�7 ��u��"h<��e)�ȵgRU%��_��wn5���~����N�0 ��} -&��Fw��1$$���*���&�I&!�w'��ҵ�K����vo塌/Д\ �p�H���:�|�F�ȹ1���J��' ���+T;$$�E�c��6��ۯ(��`�u�\�dV�K�GnF�����pcI���������s�pK���� ���ċV=�C���R��S�J� ˄V^� 7��S���7 !F� ��m�a�1�C�}*�1�����T!��3B�H���i��]Ŝ�V���p@�{�8,o�s��L�;��6��a�d�O3�_��V��a]n��J�mX� �Oz#K��a�U����X��z�q�7m�'g�f����V]o�8}ﯸu�tD?�}���pK�ډ�RE�i�a�n�����Nhh��������s�/6��X>,����>����My���0?��ߏ���#��%����[6������Rȯ�[Ud45R��Q'��.$�E�ۦ���RՔ�����&�VM.�Ȭ����Yh�����Tc�ժ��*�y�g��p�F�R6��meA�F=��C��G���SY�S��4k�]�����oyjR�`� -�Z�� � |6S�f�s�Vm�Kg�H+5U5X}u����*+��uo30���1�� -l�Gr��^�|��uk7��ŧ�7���Y+�2��vs��Z���}�CYZ [g iH�Te���z#b���'�Ȟi&M�A�"Y����k�ZIk�Y�0J��X;�ռ}B�����K������TkcJ�^��{�%c.HD�ɭ3B{G7�g>���d�E�i̯� ���g� 7�1&1�I��w���w// ��pJ��$fBP���H�a™p��^��<�r8��(��_��I�l)�=]�5��1��<��̗< M�K�ui�� ����;�IO"�������_3�t��� c7������t�@�l��}3/1*�-��l���0���Ơ̍�/j��3E,b�w��+�����8/�ٵ]vp"��'i��*�|���7�c���F��9H����\�4ڣTp�g��Ä�q:Ix��[8�.|�Z�0/���c�ǡ�1�8�!|9��G�T/�G#;��p��ڲ ��u����{rA���A -)��:)�¹�5��Ɯ&ݠyp�<�x%m���7!_(L��S/p5�.n��s{ ��v�:<.��rNC-��Ǐ[�/k��P���dgVM�����+e���w��7�����3�рN6�^� &��_���K�fE�*��u��y��4��.�����~�m��J�@��y�9����Т5]!PjhzD’�� a7�N������M鰇��͟o���n�E�90�֊2q8�Y�VU�W5���.�5-^sކ���ȭ����T?�{ǥ�䱜�߽�bsv�X`<ۦ%��0�},ry�n��[FY��w������&���X8��&�b��|�5���,/hIP�m��J�0���s!l �>�.Z� -Ŋ� ���L����d���;�t�!C�9s�����z w�S��I��W!���� .S��^�8#��41��|�9��������pʁu�4�n�-ig�i��.�2�r]�F�Ï�@�t �%؀���c���63Z�S�����;:���,-��*�����y�^�+,�yr�}�_�hv�C�7�ǧ!<~���dw�T�6��l|�ۋ0�fz8�j<�:[��_hu9��^����G�9���Y%~m��j�0 ��y -vH`����K=�6�= �&VR��S[��w��li��Ï>I���ݱK x��T��$�N��5����G1��D� -��"����0 z�-��E(� �p҂�(%��Ul�V��a�<�KjzS��&����>���r�'A����%�^����� \�zA�մpN|�UUU�Yq�)�!� �.i�I� -;iL�{6Owx�Ø��l]샛O�3η��]�5g��m��垕l�������%fd� ]�Mk�0����9�P����� �`w�z(���v�f�xX���Njq����y�<�}6g9���}�;�ɭ�R�KK"�����k�C��s ���C�^50�`m��f���w�.��֊+���yV�UU�U�}Ɠ��6�*��G��P>����K���؜�]�V������`�})�=F�ҷ��:?��CVRF/�f����[�PH<1!�|�5ώ�,/hIp� m��J�0E��� l �~�.Z� -Ŋ� ���L����d*��;���␇K�̽aru=�h����Je�����I_ �:�S��}G'�w: -��a@Ld4�E(��a�i�X���~2)k�m�����*ݳ��:-�O�3.O�T�%z؁����f����f� �:�Ή�x%�8,+Ӻ.��a��dv?��=hMz��kr���mR��mZ����߉���Q�aA� -�:�'t���&6��.�򇦨�Y�������� u�A -�@ E�s�\@�@�BQpg�Ch�8PS�o�����{�?�벆���T$O�BXi�Y��P�|Q~��3���jfK����3�,n�>�HAԀ�XH) q��8���<�B���/�(P��W(��,V��<��Ғ��ԼԢĒ���̜T]��|����Ԕ����x7OW�� ��p� �w�� �ihZs��c^�R�&�My^�~h���j�6���j��]ӗ�u �Ł,g���Ĉ9t��u�����GBMB \ No newline at end of file +�y �t�($�G̾-=�<Yv} F� +% ��EꕤS���lCO������0Ɛ���=�3�E�[,,8��HZ���}L6Z��� ��I��k�=��� .͌[�&ጸg��w���Q[��� *�wmƨة���,�̡lt��on� +�7���|#�TN:�M�8�Y��c�Չ�1���͍C?�=���`��5J|��<�5~�J����8��`Q n�n��+���{�0�L��7� ~-��Az&2�l�S<�S��nV;3 ���X\O� +���7����E�X�򎙹���C���F.���T,���3��t]+}^�޾n�f+=�p�a��%zb)q����o���W��8y��h򠻀��x [�>t�1���? +��~��w@�0uO|�r��<��-m��#$zӽ�u�`Q�_:{��#�KD�tF��{�~�����n�˄�% ��nT���e�r_�[��y����wKϠ������\J�#�';Q��?IGB����ν�g�>�MI��Q��c�%�w��M���ߣ(�Ѣ/��%_:��~!�緂 +��{� �=� +1ޅ�Y��'�!��������?霧��C��˥PӰzz��N{�X�oI��_QgEk���#>�x���Dg�:��f���t�v������S�ƉV��B�t=�~�h�������۷ x 7R!d���Z�� ��]���pX��r7d��A����x�_d��b�<�e)�ˆ�n) � U:N��������idn20'9�PA"�� �Q;�!z������Ry�\���9,����I K2�0%"�%� +��z�%if4X����+#��Zj4v&���]�$cl��:�Uѕ-��a�� �.����n����Y��s�� +49�,n�>eX:�2��J +�y���ZG�(�&4���60s��u���#��2E7y��epu};�~�S���r�Z ����&+e�d&& +A�%�χɇ_jX�.��m���9�d��5� |��.G0��ϗ��� ���ޏ��˻�����z�;��~��� o���7�����SP���Ұd@�yb���dc%Fɖ�ɩ�@ ]T�@(h���D�f.-ւ�9(9��C�2�o)G>�"{dQ1��D�s|\D�t��rT�v��v�VJ��G�%z� 0K���6Rꏜ����G�8%c��P;�$ZF��n]H~�e�>���1��B˘��QI�x�$�zo2Rd�A채wP� ++�y����r�J�2�'�°� ��D$^Τ�9NE�4 2�أX,�`�]�x�Zѣ2']�� 7%6���3�LZ%�^�Zd��W'"{, ����\;O�j�J�3�xd'5c��MYR� @X�`^)'K�ہ�����7QI�,LPѲ��yd�YJm a�F�֚eB*��TH��R��jM#�P2K[n���Z��N�ɩ�(�����>�pI���� +�6!��w,�������o��*Ѹpu�� � =W���aE&C�G>X��,�f�؂�D����e��_������q���ߑ�y��E"� +t������ +���;�j"�0Ά4���q�ʴQB�V�Y�HV���AW ���'���!r +�č� ė����c�ᖅ&�'�U���lۍ�]J���0Y���LXL���v��.5}�[)b�J��/���I�8��pԢo�$�u_��Z������9 !��G���cIz�<7�_S�Z�n\Zcc�@(K|�I�B6�����բ��G�>����s�>=��$��;p�#>ڙZ�WB@�}_�/*1+?���Q\j�Y��PIV|o����a�O�k ��]"�f}c�ڽ m��y*�Є��J{�\e�$�_a��.\?q�gB��Ck�Bq��;P����׃�1{�����ÁVX��+~��� �P�xW�u���}�K�s��J���F�+�q?t�����9�zQІ��]��2��L/�t>7����*�w��#�� ��(x����x)]ã�C�8�U� ����flg�������f˩�,����lN�dbob���\�N����eob�)���5Z�q���=�L����51��h˫ ��Ȟ����X]O#7}ϯ�K�HPȬ���%͂u*�-Z�V+�sg�Ʊ����n����x��dB`�� ���<Ǿ����$kD�� 8�+!�V� %T +n���VVK��]��+mf�Y��Y�i�~�����n��a�7g�J�*fNh�����рVHhm`�M�وq�YX��*g;CDo~p=��.!��ca �0nn",̵�B� �8�I*�� �h0e&�L��F�z��؉�:#JexUc å[�a���R֡m�����u�B�M�1[B�l�\$��d*�Y���;4^����Z L� �L8O*Kе�J�\d�O�T�Hk�w�)Q�r�ST��Q��Bؽm�x�MŬ΍�T�(��#ES�#5R\K���j�Cz��X�D���+������_H��5�C��'��G�u`���{��|5��@N���~�"�|m%�g���yt� BYGB��kv=rSB� ���I��(���\�2�#��6pc�㒱2{t���r��=8��8G�o��X����-$�0cآ 9�R͂�=1˴qL���� �8�V/En0���du��45t:,U���.��Ry�nN���/�W[�(8\w���QF�wh�Ӧ =�+G;a�{�����Ke��S�\W~^�1z�� ++#����n�JT��u�X �g�9���e��l���Bm�m�2�K���T����]Z�WJ�����RpHrUl4߾q��39w�"ߢ���O�*B|9��ֈ�H�K�Ƃ�9Gk� K���P섩�d 6aB� ��ºR�Gl��a�u7�r�lq��Ơr�v���W��/EV̋�H ;>����u ﰦ*O��ӗ"��"�[!eo�T�_7���B��|�R���t�;:j��T�ӳ�{X�//�_^���Z�W��:��Υ�ʘ).^S�).���N��:wo}s����[t�)�c9��� ���)n. �y/�Do�|jA$�I�i�/x4�Y'���Eį(��m��i$X�\�f�&o�� a��n�Q�lL��.��:�K[RM���k��9���*wZį�Z �5����?[+�lλx�bZ0�X�N.@�)�x��qm5��� ��3 ����㽰� ��)����u�#��-ݠ��R����^�m� �����l�� ׿��m�J��j/DMx#��~Y�}�V�>���@����J�-F�rk��� C�5ٿcr�%�N(�D�Yy�sȮp����3c� +;R�V�[тxz�F�oTu��#�- �\��v}�ܿ�Z^�Otm���i.����I����׻��"�C������=ks7���+::U$ٔ�$wWW�)G��Xu^�e����T�$q#�����0̋���&[�/�4�F���h@Ϟ/f�G�Ǐ�cx%R�� �Ȧ`f�/ɹ̴L����qʴ>z����L�1I��,�q���d��+�gcf��`�����٘+�Ƿ���TvF%F�� +R �Tq>��G7�������LDJ���1,���� K��`"��X��,�M��"���S��Ha"+%�3r�q�gbq0DRn^yd��5V2w�T;f���\i$�����of�Ҏ{�spBo�� +2i ׼��s�D��/R����v�s���#�0"�$�8ǍY������GRM������󋫛���W�g)��9��a��X�"!5H��Gb"� �J�M{���* +�d�GP�h�̀��v�n��f^��\���������!|8{���jxyq�������������_���_�����z�.̌+�� +�� +�m�8��8)�O�D$��l��)���犌a��\h���!saH�4�Z���ȏ ��!(oFR��Ə�NO�}�9�a�~�y3�\j]ZH Q���iT����sř!cG�j��B�̔v�X�=I�\�j�A���'�!�$\�k`���%��`��F�Ŝg�l��%��\���� +D6��=���lq"�6P���X�9vJ.��|�OĦZ�.}���L�=�dx/O�����[�{y9� ��������c*��*������Y'2��& AZ(����Z�xb�Ur���Gӣ��S�,��'2�5 ��*t%�d:ރn$t>o�_��`�Q��ƹiXuڌO���M�f?���K��G��O���?�6*m���Й� +�����c�ix���{�����a��B �W��,�Qk�aW��?����?��'p���f�u�`�hޝb�]�C��f�3�J.~׃�{�C���x-���HoE�|t�0�,�!�4�iؽgiΫ�VE��қh��[� ����Q�3����@`��Wn'�(���`���`$.�N1���ؼv#^�@O� �������@qg��H��i�q�jL������� �D:�s����1X������� 8����� �V`W.�����۸�.�n��B�a^dv�blf1���hA-b!�y�wm$�i�DFHQ4C�j �o��\�[/ W�HՃ34�3Z*���(E�y g���2بd�� �lPf�1��u��<;�/U$4��K�Ө>�D?o�û���g��0��NU�x))K(f?�s�P�R�)�Y����؆j�R���:|��g8���h�خ�B��n�ͯ�����|T �!rˣ�� ����C}��TL3+1��|��Ը�I�b��ٛ˟�n_^�:{�f8����k�(�l����ߛ�۷g/o�]��z��)�t�'p!|�p� F��}���Ż`���� ��q �@�j�Q��� �\T�s�O�uv�HS �=giڌˇ˗�׷�.�r��Z�� �A�g1��v9�!���s����?�����W��P�L�cF�4���{�ܺ�Yq6���g>>�f^(ix���n �Y Ug�4#QGM���s��vzm��&���ڙ_ +(O33fl��83�F�m ��[�\���!�O!ha�T�p�2x�H*i6�`Y�Y���� �G���=F;��x���(ah���)6��%���Q�@�5s� +�r͆����+�U����S����C�KW�E�{+G_ 63��K�i�g4� *��^]c�Dv�R1�6�"�"�I�YU��%�Uyb�;9�sl�������ِ(� }xJ��N��#��@U�S[�L-c)� 稾x{��ه=B~�S6b���'Jd�`�QN��Ń�͒�`��}�s@�>u��r�/�7�b���֥!F��!�s4����i��Ѓ���>�8�;���`��ک����0o@�����(H�s3�c�2aM�HHf,��(N����>�Z-�w#њj�bc���v% �� ����si�)F �b�� ��!�,4�Jv^�uO��a.�M?����z_sM�l;e�dkg��P>� �-6c-��a&��Kֲ@N��e���47umnwI_׺O�\M�&v�{�/_מa�б����&W.�Nr�J�/ ?��ƦɖYy��+���a��͊7-��dn�.I�l� b��0�*I�`���;����NN춅ݟ��,��\�6�C*)�K�V��l�1ߧ��O+��| k9�,���+'�]!m�Ʉ6V���ȄZ@-��=_���d&�v!�|v�'ہ^Q��r_������~��^��i_����y���8�(}�����׉�r�,�����~���J���T�)�0�e��Q�\�V��"ם��*7�����Fr�w���BJ,Ƿ���^\�鵐��5[�KR��Oׄ�F�X�M_w���|\N&��h�o�w��S��SP���� KH��[_e����>鋷"[|M�(9L)4�_)����Q��# Շ*�vʔ�X:{@���g�6��[F B��S�&_����9���ql�����b�Z�"M�)��ԬԄ��_p�1��F�[��`����/��i��v!޳@0���z��R9�ӽ���뭦(��>:���ڇWJ�i�dF��^��.�"2�%qT:�E�� 2�5*����dsͦ�Ӝ��T�w��Ϸ �� a���n}jj ���|e�VډWV��62M�Ai�1�0ž)���[�5ݲE>�ʼn8Nf����B�9/�߭u��M�i���3 ���9$�Y��{����6F�m;�q��{�:l}]no� ����._��m5�[��s�_oS�'�0�5�/0g�FVk�u���-�Pj��c��en �U���Q پZ��Ρ_�nw� ��V�}��=5G���z�&�{����odԅ�i���х���(^iqCEC�y��?{�� ���D��x�-���Pq�+>d%ͳ��6lX�����'e;��6�D���&M��E����R����R�>H��\œe+�E�S#�g��:�l�j�¾�p�rXVü;�*j���K%�+���^�^<�Sx +�ax6.����`��&��0���W${��>��=O�B�D1Za��l3vj���Ң*�OM�Q��5�lX8�l ������Z�m���I��R���Q�E�� �"s{���U�\���2ɰᆺ�m�cJ���`�C!����u���eDm+���j�%?���+c� a�I��m&�' +E�C����u �Q���Q����`�p�(�ٸM8�������S ږ��_.�ʅص� +� �R�D�ַ+�Ǝ�-�����ؼ�v�]Ƶ �: +z�c}�hMi�c�I�m�me���'��QL3�x$���˔����0���| +���J���q�3��?���Z���I4޵�ց�gwMYD-3ie�'p����B��Ȏ���)��\�f�[_A�X��V4ַIXujn�>Ígd���)"9�>aK��^smi�7�_���6� &j��UA��sIZ�f妋2� �Ϻ�{���X�<� +�� ��3L�~�P9�RM؆�n�b���Q��+/g徱бk�xӲo������?z��O�6Y���(_儦����'ׂ����U� �X\���MM+��;�Z��*��Mdj�> �F s����`w��.��9�1l�~x� IF���o�����QՆ�Ԫ�_7Q�z��(. �a�@��T��Þ�Խ��j���~���f[)� k[��3����|z�0d�fy>��-�>ꂑ�w��Z��yp��B%߂�U��S/��x) �S�zN�B́��m��:����zUL�.8v��lb�z_l*� +,��h����AE�� 9?����*���Y��v�q�άV��:|1_���A!�1�Tҹ~K�C���o�w���$��h�E€0׻b�菤1r^�Db�)�m���~"3�=��nN{BОF�F�~�uGe���\�漶غ���[fT��ָ�&�q��w�x{{'�#�� �G����o����(��}�; ���a��Ȋ/uckd�v'`�඾��gl��/�-ؓ(�;wB/F��>��κ`�͸�4�`f�b�Ԙ6)��<$ ��ڠ�.B��BA+�a޽"�Yb�ў�Dɹ�T;?]�pp�%�L����(�貫GQ�,�pwik-w�Q�5��Ĥ^q���N��2�t%E*�>�z{"�'��q���x/Yq(�mD� +���m��9f���=>X��t8��1��%�!c�~t#?�ZKŰ�[��)>o� 9vS=p�r�&�����t���;6���q۬t��t���݂[� �P܊���C5� %�M]�d���n�L*׸"�����?�p!�G���ŕ��W(��LZmq��t���"cxmkq��G�v �o�-v,5�w3� �B`3 +]p�Su��=�E�~��Ze�E�%���L˖�۠@��${�w��_j:V�ycR�x ^�i�n���t_�G�Ok��r��5>��F nq�a����@�{� F�t �)���L���� + Ux7��ko]y�=b�M�x#�V��: oP�`�W0���{��J�W�AҾ�L󿭭�㝣ҹ�_4��Ѷ�=� ���!��R,��۫��x�_sI�4��Z�Y\(X�u ?�:UE��~H�kG5�R��uE|�����pG}ꅂ�'Ч�LT'�zW��0�kU�����R�����ɓO^?x7��&��S�i�~:�~�F��(;c�GڨHHOK>�B��|8x�d;�� Z��`�뢫�%����i{<���-I�-����X���l����9�Ku�Z�zUh� �R��ͻ�v7��2t���,�g�r��+_����\/?�ⷫ�v�'����2��`��[;Tn�Q,1��h�S.l��ZD�5LS9b��Ē������x ~�<1���)�1m{: ��xT��aPL\������n�I$�ڐ�K�>|g�ki��#�'���+���¨�d�������9K#;��j�墰��K��l��?����zW�K�@���:f +}���⹡ ��%�����40�+��tkb�]�d��ٴj��Y����g)/b?p�o�"7؎C#��xt{tZ���uʼY�k��O��e�na�ǯ?�U�{9�?��$������{��� ��S<���z����b~��=�I�CS �q���ѪW�s�6��๟�,�ж�^;崊V�=��-9��=]�E�Y�͙JE�hu�/�5�@����Uz�'���ac���;�{0G^$1m �/�Nc�V��+[�b]]C����? ���LX�j��pW�⦽n����Z l�/M4Q����d6:/\� �' �����: %��t��v���9���iج{�,�j%������:kil�DVj��̀�ӎ��A�ZB d�פp����uij�&���u>oٵ�ԇ�>Dv�h]X"{2�I*%&��~���ض7O���K���W�5�Q= +�� qp��Z��v�o��Ξ��x飪�s7��W"��f���~q'�\TN�*��:�6m�~��d9��=:�G�e���o=Q�L����!�m +�#��n��3�����f�]��v˨������U���\�M펴�?���_]���@�����p��N�p����Wt8��.Č��lˣp���"�j�#�&�M{_p��t� ۬�QԅΖ�%��Ϧ��G_=?}��XmoG�ί�ZV�-i>��1q�AuqdH�����n���ؽ��ӄ�^;p/~Kz�e��3;/�33�y��ZGG58��") るL���F�Rh�ል3<�,�(cZ7k`A ��JSȯ���nR ��o�O,z�� ��Bci�.���i�q&"���-�h|�F�*̆r�z �!4����ǭ�l6k2�pS��Bl]�ϻ�a���櫀�$2��Sp�1����<��26� +�BYp3� I��:�`�Le҂�\WH��h�3��p�u��a��G.?��K��3��C���������9�e:�����?x��&Ex�+ +B*��Q�W�| ��:�#>�dL$Ky��J"G5�J���2>�ƒJt#�����E7d*�I��"�n=SZ-V��0,i�� �Ǘ�>Z�D1- e9ժ�_!���B�``l�#�2 +:��o��#׮ � +P]�@����ы��(���� �����i���3�,㉰٤��\��Z��k R��J���� +֝ա���,��f��-���L�F׻�� ��\���V$��Cr + �� �:���Uכۭ�T��\R���[Z5xg��qa`��j�C_Q/ 큰���1���Qhc�� rR�������E���5�Tf����ٞ����F��SW��>�T���λ�Q�j� 1Z%�j�X[fa�[���2��lө HHe���q��\8~v�0�ؼ��>��;������+i0��_Z:�mܭ�g+�~T2�8f�ܖ�z^? �I1:�.�Sn�� �JM�d��Qe������T��b���JB��LI�����&v��^K[!��DS6��p�x/��� �T��m(4�p+y\�]1�x�BD��^_/�R/�k��Ac�yb��"3��s����﻽Χ�8�8BҳO������FS� ���:��X�h�Q�]�?�� ��У�X�~��78��b(@�sYw��̲b�,1Oe��Pb�̪���HcW֮����·��(�b�v+*ۯ�݄ �>�DH��"�dڀ���'P�m�}�K��I���У�Hz|�v˪.��l^BG�75��KNK!���W�z�2�����Y���Qu˘�Jͬ5g۵=���Gy_u���Uc��O����D��O��X�棨���\��g���q�P����i,[�q�݊f����^h��0a����/φ��Y��{�n���7c���dKX��>#���IF��7���k2�I�^�h�n�����K{�$���^�x�t�L���3�aro�r�|��w�q_jw�t���ڦ�����[�&'�;��7��m���hs�s��)��w����Ta�QR�l��������cPx��V�m�Ǐw�{Z�����U�#9�Ɵ�Z�\r��ʢV{{Z��Zmo�H��_������~l���4i�+�"v�XE0�hi.�wf���?p^$K�;׽�.N@�$rH>$����y:��xD�[�(�\�Bq�E�#j�`��hAD�+ �f1�=�p� ?���|��]|ç�������t�prq�f8^���� ��%� ����IQ��)!p�(�+�d o�8�9F|�#ȘHr� $����9���&b����T�H�t Q�z΢kbbI��R���2����a�``_�@����в$�2W֧;!�GF���B2 +���� ��� +H7��ڄP�j`�$p��bd��e#&�Ejk2�\�9*�Q�%�+�L���$7�2-=�]a���I��i������[B��o����^F2�#�i0��� \��W{�ɡ��z^�t_�� `�b��<3�2ϊn���x>=�Ka?Eg\��*� aI��"'[� ���/O�(��������y�dz�dz*�̲y*�@�@) �ƺ��SU��D�a�Xϟ��=�:9=�^��94��dJ�IZ��u��TȢ:�a�nݥ�_]��a�*�g�Nxk��+U ��}���]q鎏,�~�E��l;�^����F�P�:d"��:�����z��.��)؛H�:��lហ�w5���s����dqi��dM����ss�,̴A����->����3����^������:t��UAf�cسPU�ӟ���Kr��-H�9=;��~�Po��m��8Oh(���r5��\�d��Q"U�)��d�u�d���'�=t"IHeג�_���a��ǎ���Ț�� ��s% FtN^�����h��}o��)OY�uc���Sv�w����2�%*&L]����Mח:r 34����kb � �ل +�iK�fw�T�� +0g��Р��"�Q + �e0A*b1�4�"���Z̚�*�� �R��5�Sv]X��i��e9�ڲ��nQx�S`�lT�Q�J�.�mIʪد��O2�4����")�-;:g���Q0?=*����jV��Bǒ~:��ɦ��Da�������S�X��D>�b*ѝ.�t�%�j>}���R%he<��#�c*Ya�Τ����[̅6�+�iЭqk�H�u���с����{<���{�߰��=vk���_<ֳ?��c��$r?ݭ��%�\���K� +�өFx˵����嚼�(�V*paPMY�m,˨���t͂c3Êsӗ����պs=m`�F��E����)�� {���0�2���)�ʤ�h$u> N� �l�a +RPjC�#w`ߐ��3Dm0����>;8��9~�Y���/����~j%�v�'��\_qaVV�����'��j�d���`�p9:��������Z#���ĞǗ@��v������_u�����- DL��-0h 9�^P�-�R{[�{�2;r���ޑ?�M�3j,|��#ј� 78�X�;�����j�c�J黋��h]Y�*a�!DTrzϮg�fwo�ɂ�����j��n7ݷ0�w�F^m�$�v�}&� FkO5d���u3����85?{7�EOR�Y�����Z�o�ڷo7��SS=�lP�o�bF+٣Lڜj:��E鶑m7��lP~���7ȇ�uBk�I�pm ����R�!y���ҩT�%��h�(��jf+�#O�\<\h�"!_Ni��M��b��ϴ �4�����û4ö��[�jPH�f4=��?̈́�]k�C���MZ��G�����K{׸,fnW�K�U��!]ZJ7y@��;<�.<��M��(W +�ɖ�1s�@�{��ȴ5��Z�2��XL�Ts(��;nhf �׸r������" |����ޝ���R�_�G���!��Q��k,����Q����s{t�qن �z*o�;u�ډ�� R��P�3�͟�:H)��D��G1%S������6�� qytlQ�j S�� +za|�͟��\WWy����(\p��C�*l��x%���\������k�-�!����kF�B���m$�)��7�po����b���Z�""/�gK��;��~�P��>M����b+��O�A)#O�TX�c��-A_�5w Ւ|�J�H�+ʫӪ:aS ��V��&� ���~3��z��� �� ����#�}��'3n����OL<�E�_ޔ�EK�Ul��|z6��mG�`ٖ�r����[�kջ�57rm\�M��������}˶�t�k}m���}}�_��+�]�����k�c��Z{�}뢠���Zp v�v �Y^ grӡ�ǧ��#ݛ��0B~�<�J ������WG;��Xo����)��{�by��h�D�8���چ�48�@@펴�W�ɵ����Ő���vZ��.���o���r���,�F�#x���oGG���G S] +.� +� z��76�\ ��M��@U +�U���V{� +��tZ�Mc�}�>�ӣZ"�^�B���R�Ƥmq���[�������R���z��Rz�=��Ĵ�c��gke����p�p�A��(�R+��������@z�2��@,�7eDjR-���ɰ��x���"w`]CϢQM��� +Q�jt���|J�9]KY +��"F�;�!���0 KD[UTW���D��T�l�J/z���B{J�a�.��m5>k� �`f\ ���T������#���Q�~��P��y��Ǵ����UytTmo�W�5��0��Ə��F׺RT��0�U��[�3��VR\[�9nO�>0�h��f,��ǚ���c�)w�_N5;o�$�?��q�6�8�p�է��T����zx��X\����.Á���Fu��ÂܤFeR?�^ &�IS�/n�^�~�z3��Ew�q���t�q�k��Jua��tS�n�������K.�So�"�Ĕ���3Z�fZ����ӓ�)���Bt����U�'�k��G!O ������Bx �eP;�=�Z���t�|�.�����.����tn� fȌ�4�Œ�Qk���'e��5���W��0@����l(Q�� ��8jQ�z����cz%�.�l�-7QR�͡T�7���:��l���NEt ��д��6��FP���χ���h���ѿ�Z_s�8�Oѓ�0d6W�pɒL�Z�9�L6usE)v��'��P[��W�mp�!�\/1H����V;��#Y&�>}���ϓ�����f؂�ϟ��9�|�7�C�� {B�$�N�/���n�W�ڊc0�J�u?��0�R ��*�A*(�S���2"�0�b%��B��0y�`�#:�!�m�IB�bE����i��%Q��s������ET�IC�Bu���$�y!T�#�U*T�2K��^*,Ÿ�!�34��b*��)3fюT�aL� +E��4���RH��!���\׈�� +�2F/ �θ��(`E +Jb�u�q��.)RVq����=��P �a�o�e.x�Ņ,Y�5<��du�$���B�̤$D(�3F0|�YD�z! �"�d���3H��'tx�,Ф,i�:>��0��<&�����~��ֆ�;y��n����|�����s�����pb���'&y���`�9�l���n2r��[�g����������ߴ�zk���;'���L4���!��^��ֵ3r��u�c�q�z`����?Y�F�L���۠U8~d9w�� ��.�_�q��5�j �WVW^�0r�둝�?����~�5�>���=�Q���w���}7Yރ����!}��S{8�֝uc���1Ўu���SϾӂ�C �O��� �� 7�;0��m�ӷ�K��1�Է�0����'�;t�R?_O}G�Oc9�����$p�q n�{���Aߚ�������[��4�6�qE�o������R� gis�����N׃�����0�oF΍=��z��@��o�Lhy���8�{�ܩV�8m���c)��Ƶ� �|u���f��;y��C�����Ef|IH�D���J�9���$UK.L�w�|�V�#(��N ���4]�e_3y�>�*��!X��(�b�_>h�0&R��$q~ ���L�i� �Յ9 뚕��9]\���ǘ�0OYh��f!gR�4T͑�/���o� �6 36~-�k��?���u�2r�U.m�0��BĚM9�����~�Dʚ��4V*�)��8F �l�eO����St��DNU�����?�_��K�}����l�9��^P��0et�����p +�n�]K~q1��P��H�Z��,� ��p��U*Xa ��/[ +[.�h To�vŨ�Q�q��> �h3�cT/�[������\%($�j��2�1�'թ�U����9�q�@�C_!S�9��W�3�6h��I,�'B�dHh��I�-m���ݓw�zLie�V5��,B�?�`���\ C�U�V�ǎ(Sw<�cbL�Loo�c�B�g-�a��Q|>��a��1Ai�Y�V��G�2�Uk���q��*��iO��)��o�1���r�z予�0˖n�p�}c�`�qg.r}4�us.��ˊV:�0$Tg= �Q�K����rQ�,m�۹�~Hˮ +99h��h�-v-����~_>�٢.( T�BA�9J��A�X�s����0�f�r��YvH�D����,�O��� ���}2qr�������B��N૮~��Z������}>�Uz�D}�XWR���F��X���Ь5 �%Yo�M���a��-��'�f�Bǝ!أ�LFIG��`A����IQO�Z5�����l]�����J���n��S���v��֮Y�Nͺ�g��M�D�� �z=h���������pɳ�jO֢��+΅�sq��g�5S���L�"��",""�΂q��"BU+}q�/Z�$-����Pw �f������=�J����� �t�(��K.g�K�������,k�i�g���1�w��d$�+���t=�����︹�9K%.3���1��Z�`�w�&������F�D������{��N��U��30Qɒ=�ԓ�H���TJ��'�#��!��o��{���˒�KX �� H�O �*�"�j�uW_L39�J��$Y!^�x�E��� +������֕�e�(�<5[Э5��G[��P�;�n��m�O�Ώ��l��\���J�;W�O��T�&�8�D��ZγzA�HGbJ�XO,��g;��r� ��oY/�9n�\Z� ���5R�N�����=6:W��r�����hD�K����ԕ�;��@Q9tP�����G����o�i1j6x�Lʸ�S��ӭ� 2������U� +$]�e��Nݤ����O/rff�pسO���"Ѓ��ya�jFu;'oC����I`-����dB�L��H4�z�54 +� �;jN�K.1�g��fC*��΢Q���M�7u;O��$=P����*��3�k���*R��xQ+E�=�#p�<��x'_*��$��]B~���u�c��3����)z��� �I��0�ÿ�����}3�;��B zf� \b��/��"f��śIBY�vQ+]r�9��� �n +5�}X���H؞! Wj���4ڽ�D2�d�Ŋ3?C�*_k$4�F�ѪV�_k6��m<���f_�rw���8�?��fe;=E3��� �č��\�MP�"�(�r&0{��, ��8m�|����r����rw�zp69ח-���_��]>�Ø�Ү�˿��bQ�"��J Y���a��*� ;s+׸�l���o��������(5�p����J�&1 �٠ ::�c�L��ݜ/-�j��=9��E�3r�Aݝ�2k�*=*����$�Y�|��;�� +�Ie8�6�-ީ]o�ہ�p݋� +��/��\e"(Ss}hߥ��I�aA6�7���)[mss���#��@l�lu���8�>Qa.{�"�"�N����n�+���z�ٜ~�����UT?V��^��E6 �9e�|a���Q`�PS�! ��l:]�<#|�����M#�����Ё_d�������C��r�=8�,X�~�/�Y�r�F}�WtT�t��<��I�B-EpЎ*N�F@S�Ġf�l������"z��A���3������]>�O�^�<��0���s V܆ׯ^���ׯ^�l�!�S�ݡ� +~e2��7���ߒ���`�HT(�1鞀�1�JK~Sh.2`Y�B�(Q�͓�1�3!�\�AH�W"�33�&�9��ȥ�� &��L��#�D����B,����2B �o*^��M��դb� , +�A�f<3��F�ӫ�R��<�N���\Aʕ&������*�*N_��`óu��lr)�"ƿ�T�&".�ic��eə� �%,�F�Y�V0�3�k���8FndiM�H����kU/��T5�{��@J@ �,R!�L.�Bh��LZA���c3)�E���&���L�S�A.9E�����@SjM��� �.�O�����>�Cg�k/x�k��p¥7:~�xo�nz~�vnpj�g<{| �o� �|p�&#��'���q�:A��`4���OCcM/��{��B�SRsv�wW�?��ǡ�wGnxmv�p�1�x��`���Cw0�>!M���  �n0��3�;���GgBpi�F;5�_W���\�?r����0t}g�f�Ow�C{ԁ`� \����\MF�m4�|2d��g�C��о�?8X 5�CNL}犈{L�A��Ё�74���;p�sy�1�4p:0�C�C�'�w��9}�O��GX�8t|: ]o܆K����a`Oghl퍍����ׄK�0����K'�t|pǦ��l2G�� \_��z~��,��#��38��#�On�Mh�n@k�r�O�5xSR�8m8�ǵp�ׂ{��K���&$� p���. �.+�י�>g��EJ+��",1�Y��B�t;�Dy��jGHt��@�>�1f�0�C����˳�Scr#�s ��4�urB"�tS��O*O�Q�a�ZsX���8-�Rϴ��'�ZU5�����.1Xg�o�2��^�1KP���P�d`+%b�4�G`R�'S� Xw��^�,<�g�,-�MO���˳5�u���L��kOϖ�s)4�tֿ��;��2�ӹ�J?A�%�t ��KDP"KsFm�lI���?B�n�#H{���Z�Y�Y��6ʜIK��}cn i_h��1(w/ORF�B�7�ݷ�YBAN?g��I�|�J�ENިnJ"��ԇ��L�-���?� !LW��*�<�eo�0�V����ʼn�h�;v� �(�vK���@Z�>2�� � d\�)Ө�9��R�j�U�n�2Z�ަ�� ��} �X��4���F�Y�� +�3�*|�LN�Ld&��4�ks$�+���s̒������FxQ(��8ߗ\]��'�s)c̫{I�Ri�q@)��65��j�gu�LiYĚ.��s��}�r&٢:4�n�%v��-�ƾ�Iy �"+ D�5+� ކ�V�1W?��ϫ����wJHmm.o��|�_ƾ:J�T������XA]�hq�A�� �>Թe�T� ���U�X{���J��-�� �=62p/|�9jJ����S���P��4xAĄ��UA����YF+�l�@�M���� ���~�JXD`�-V�-:��=J��"$8cE��[3�sQ���_v'eU:�LUR�Z���+��h�����]��g�U˾��V� �oϔ��>鍂�Zj?�k�V� ��i�v{��ޙѲc�b�1*%� � )ɮ+ ���j��ܮ��ԣ�k��u� +NF0(]YM?bb:�3���G�z���v�l8���#,[�}��`��a撉rYA��������M��X?pqʘr��?BV�)����N0D�D2|�K�:m�p��i` �O;{e߼q�{��dɾ�y�y�,o �s;K��A��R�;��oQ�0�ү�z��3��0$�p �+B^�&q�.�#��ܓ����H;���߼��� 3�8Ng3Å�S9͵LN)��>EQd��l���G�L�c-V߈,åSA�����{+��V��t��?ȊFvÌ����=��r ��������jn�N�RPdHYB��%3���t2��nn��� x�Pm �r�0� d�х�z�=28 7���ˋ������EV ����"�O�f���UiV"�y�b�Z�?�:�:ku*���Cm��c���r����Q�Ll�=��h��/�uG�$\!�#�h7�rA=���{�Ģ�r\�nH>�)�,�R(i�Nx�ڽ���W@��mӋ�~:�zN�*�� �]���8dw���V �Я��KL� +�YF�\#Esߥ�:F��w̩XE��tg\�n��bQ��ké{!{��ȴ��v�yC�g��F��� [g�j1tM�1!�e���Cy�o̵�$�m�Ŧ��߯����Dު�H�n����τD��z:��)�ޖ��_Ƃ��0қ����s�&�;�+���.�$Q)SsTV�g� �-ڸUn�:m�x�����L�m��]j��7��U׳(�pGNQԵ� ��h��QD�fW%o�h����[e��E���p�����n�;��N����7�������Fv�� 4��ޖZ�C�{Rw�B�Y�wz�c�Z��������yv�:�D���f��b)�U;�/Z˝�l��6p̷��b{&3�E��e3�k�ƛ����ye{ +� ���,�;J~3w���:��u%7M���Uتju˶��V����1K|^=Z��� !�6 }! +3l��ycM��=x}�d����<�s�6�����f�NRG������*Kt�9Y�QR�\���$d�������o��)Ң#�S�db�~�],���2��;���=��"���r����ͫW��y���IN�($��J�����t����{� C0�H������� �Ғ]�5`�(0J��O͛ Ɖ����+Ն�� ��_�5�D��'��0��Q�bZ�")�Y@�K�A/),D��/�<`8N�A+��1]�A��H��E@a�V$Մq�\�klJ$Ņf>m[hz��Li��G̃US~H؊��{�a��(V�0.�fna �BV�.(RZ偐���DR���`ŤT�k�B���� }C$M�%f�"꣝A$Z�D���Д�q2;q�0�>�<�)L��Gw� ��3�N�'�=��� N�Á�M�7@<�y��|6���7w��L��z����<�������� �S��z���L�������}h��|f�9���=ug�f�%�) �c8u��Io4��Cw��`=vg#�x<����7s��a�CH��7O@�?칧�`����|tF3����R�a���E�G ���б�F�a�zN��e�݁3���m�N���8?;��a��l8{(ȩ��3���! z������J��=� #���h:sg��け���>�}g����|�aЛ���>����l��>�O]��rG3��擙;��d����x��ͧ���z<2<�N����0�*���ę�8�#���z(���s��|ϱ��7�1 #����������'w괌iy������gϑ}���Ա�̹mT �1�]$�v6&1�N��|��0��Ob�'3㧈�W��ꭵ �{��K!�t������j5F��v �)d>��Pt4���܃������lI��ʳ�h���1�#���?t�qM�h�2�QPI%��K?y�L���̙��~|�IvM4��ђ�q�����FetPټ�aT�xM%.����4iL[S�vM$_ y�{� ma;��˱����5���t~n�#׾n�0�q-�o^���y��aA��k%է���/��}���r�w�T�%��� ��Y*���\�&����Kʯ[y�G���_��GBh�%��������ٶ�����wս��RM��M$���׍_[�O���.ڛ���M"�� �K���4hm�V��5 �a�͝�� +�����h-���BϹ"���_�����v������63����vg��}� �`����ﵤF��X�e��3Y�����f �d �廸���է"���n(�,�G��K�{Q��P��y�s�h�_�'�_B�?t~ͤ���;_|jT /i���]�cL�����64�B��6\�5܊����h��-%�:���Eh�O��-#r�_4�#{q��.L�5m�V�����/��)UvZ�>:�/j���( +$� C���ĦH��BE. ��U� �m�o�uF����+������Z������Tnï�/��n�I�0�/�1��{���:ڄ������ԣ�Cb�u,�<�> ��&D*�H)��V�`$��j�ū���%� I�Uy��ҷ]�u��!$GGc�闈��U�H?f��E���.4�h�$�4Rۀ�( p��ܯ��r�7�|����w�B����$+�3���,YHi�*'��k�t��C�V���1�m��O�d���8W���W<+Xgv���AN��lWϼ�v%nq�#pz�&�y7=yi2]�8v��n����NP��AQ�9 ��+ +�>_�"Ra�t� �!����9�<�X��]��o�jc���'`��i *��\E��T ��dId�;��rW���,z�x���ޔ��@CL�����=$ )E��?e�k�^(G?��؊�]�c��)+�]�ۇ"!#*�1�,��Y�G"��٘����7b� tk�.\E1��N�:�cY�셗B2�\�OG�r��(��Z?��-�) �����0I믋��.[AUn�)5(���r�ō�I;"��4^�m +�~*^2�뀖S7�lnl�1un� �R�;�����%6Ϝk��n��~�����^qX��T���ˆ-r�bPm�+P��͒ij��JYM��i.�^��<��u��)qwpɔ�U�Z��=���5�G�1��.:4�e�! �B�i�� ~�x�i��m +��Y����qo8-�Y��6�?���4]E!���%���x@x; !���u�iH�k1LF�`ˤ�"7��3�ޘ3SϪk+���MV��_o �BR��� Q���6 Ī���2���8l�+�+y�R��D8��\�U6 �/on���E�70���5 Y`r�>��H<4�/K�l�+y����~5�9�% +Q�Æy-����,J� �qt�q��� ���+�gr�4���Y<���6��O�Ư�SʼnX����bZC�2,�����o�bۤ7;ف���@�Q� ,���QLA��ᔅP�x����} ��>�Y"0��mq�Y�?� �&��l���j�1�v�K샹� U� m;� +?�eqC��\%���^↳S-���W�~���&i��≏p +�E��߽{猏�L�n|��=���Wx$v��� ��?"��Z`�F(*�S���_�����#�{��Wd�����$��}�MR�hM%G>��fzD��~��0sѡ8< �ʇǭ�@;�,�'u�m��-6��U�4oX�D +�:�� +�R`Y�O= rr��$��ſB� ū��ƾ�Cb� ��Ja�Q�&-����8&6����P�ฤ�J�6b�d���� �FHB�$hf0}I�Hɮ)4�8�I��#  Ü��Q?�[�Ĕ�^�k�!5�YR>"�S�&��›���[��1�i[��I�f:#"���G� [2r1!��{,����gZH�kNOz��m,v����x����oZyQDmsL2,:5��������o&�8�yW�ks�c{�a��i_��r���]���,@5�6�^�ۜ.�����*kƸ����B��(W*��A{�����F��Cl������7`��Uq�QG�x�/#��c2���}��0h���'"��\��PL ��A�ݶ�h�Ƥ��J!"=��t��[ ң�Li*���TH|ZF%4��Z ��U;;�%���3@���` Lمu_��X�eƅ �!]��Na�U�Ld�#�?[%���qE�b�]3}k ����aZ*TsX���� ��O`�o�'��tZ�9��v�TUV��P��'�4� ���9s���\��� H���T� Eݮ.�����Hx�v�r`f��ʁE���i�F��P�q��d���x�4��oC +;׆�7�B횲ĕK%3�&�*[?R� +w�qLE��4�H�P{{���N��V,� j�߹��b%BR�jr�E|q ɝ��\�DƛEBa&�׍6����*��a1�+R' +9��}�m���IZw �����e��Cy]"��䶒� ~Q[�TJ��NE��F��@��I�f�K�;�y�0��T�?JҲ�,�&Az�q'r|�U[ɩ��^���- o�z?|���&�wXLB�Ŵ�t ����x���sxO�ⶦ�����G������SF��!s�d�Vr�_\1�~�7�, �"K +�w��ëo�x��Y���wv���s5A<)�}b^�%'�����|%WR�� L��<�=֞s��v3j{�(+>S�j������Nl�9QQU�'��e;��6u��/�7&��vRF��3>�o���!��� �[!�]�[�Oo_�j�N +"A��A�"h>��*XD�͢$��Xx����IY�-ok��կ�p���z�x��^��r�Ch�̗��2 �$m��~�D�~��u�Y��:��QZж����_N,�%U����W��?�c�@{��� ���"�&��w�Ӌy)|������@$�r��N���f� ��{��_P��\nY�����(�aq( C؛��GY�ۯ�������(Ki��V|v�|"�&o����)GkY�n�Iwj�oν��6d��Ћ�e��U��9^g�;�+���]�U�gF +�W��9�&�9�5���{���ۊx���<� ε$&�(ź�f2����J���w� opp?��p��T�w���B����wK��h4(�}\,��DnQ�ٗx��}����T�j�0}�W܁�vH�di7��6(k��T��Sd�{զ+��!űGi3�^l��s�9:��+S�D�5��m�7�����F5���$(� �o��q7���l��� �< �}�l_�F�����Y0B� `� � ��Y��_�����=*Y@�t��ѰZ�&���,�!�Z�����m( )ג�/�B&�����4#�X�M�l�?�BH+�p�C[dg5<6��{0QV ���g� +����������'��n��7F� +�O�9�yq׳=��B~t2*����$���D��V��X�M��r9���/��Ǔ��|�_������-4&�kۼ�Ɨ.ț�/���L�n����,ϑ;���#�t������"zRa)��9r��,��>�ka �����=r�:���N�w��d��8�q��$���%b!�V��ĴC��ᴒ���^�q�-F��΁�J��� ���t��o|�J� �. ��o��M����N�0��~�9�P"P�� +qGm�H�bb)q�������ii��i坝�vn�C���c �X��d��yҦj� [c�V#ɖ��U>���͊��Q�aC�g�� ���Ao��A܆�1�o���nΏ���9*�a`�k�,ޓ�����6>�$�����5�\`��`���Ww�nV��v>;]>0 �� +k?���a��zz��cx������&X�8F N�֚�5�5_}��n�0��y�=TJ@4�K[J�8A�^�,�,ĪcGk��x��&I�=Yqf���M��e^F�hK.>r\#�fQ9� �fQ$�^���S!�~)�[�ΐ�@�����v�I�{�L��p���`kL�< gI��� ϲZt��b�u$��r��ꦰ�UR㖐]�Xr����k���6�՘{t��wZm����p�h`LmU�%-y�&�:������>6zx�6�:yI�@������#t�[�Fa� '�߾�u�<�#{6S�M)�gƄ)J����„1��X����c�8�L�W��o��,��|��Ī�jv�l�П�6��uK���tm�1uH� H��b2L��IMZ�P��Ůu1u�ҳOt^�e�5�`�8�or{+S�� +br�l�aˤRU ~�%B.1g� B��>x�ʧ1W��^5���c��o�&%�l����y�J����ڢ��I��N�H8|&C�0*�g@�`�XK�WB-!S�&�Xg*�,�B-��������t�q/�?Z�`4����7- ��|�ՙЃ���dr����K�� �)�׮&�jE�ʇ��޴�8� �&�FU���'��Fc�;6�#߷ѹ��������cCZ���ߙ���U�P�=�TWB��҈OUd`Q=�$�GP�f�y�Z��3�Ҏ�jL�������/�V]o�0}�W�I<$����nZ����j�'@�qn�5cG�ӵ���'��'P��(���s���|�&�H�=�P�_ > ����H.I���Dk�&9Gj��G�qS��j2i�a_���yq�^5�bO� ��&� g�\�;Ԗ��2�2���yi��a�"BA^4�FK�/4�R�(1=Q�l��8�0)��J��ʨ +k��S��4� �E��I��-� �Ǵ eM���fy?Մ�rr�R뇔�����q�8v�)4���FT�M�W��l�Cs�����&<���;�4�d��51G?g6n��TvT��� ���׳��p*3q8!,�㹚r۔�,��+G(�[W��0��aթ`/O;�I_��`�ڟ� %��A�ĮT���u+%��Zuve�C����]d5 �Q/}"�=1ֳ��°�U�c���Ė���aXJ�[�S�0;�'7�h��B��d�?ۖ[0|6(" ��S�W�с���������G��Xmo7 ��_�v��.u� X�$u�.i� i��!) ����Ȓ&�m���^}o�Zo�/�O$E>|H�z����=N�% �����y�����@Ȉ�p*RhT�1B]H� �F�M�2U��p?���Ӓ( J�U��0�.]�Ћ���EQ�(Q��㽺�F��RX��x�hӘ�� +�q(�6*��N����9� +�i��Mzs���ԳU���N���qpB������kWH"2��U[�1s%��] ��!J�_S����r3��L��!<�V?. LE�#��P�^*���\3�����߸U�gGG�|I�2�_���n�Lp���a�W��U0��*�N��+$�z�{�b��|a("�-,��~c)�m�F�X��`�<��k��&��q�)Tk4��w�^(��μ�^�wQ�}hd�ڶR�l�P2b3'���{�����C�9�z� m���$o���?c����PH�y� ���N,dJS��len�xr.=[��()t���́8��6{��̢<��F��Ʒ=3���D*��Wɞ~��~UP7��QM�8��׷qm͡l0�e����(�l+��z��[rm� ���q�z "�pނ�k�M����Sv���jKk����Vr�\�{0H ���d�$�̓Zz}p_~��ԍnU"�b�1�������E��IG��fV&)�Kt\ �������q� i��9ll��46�WbÆH+ ��| _C�f�m$ޢ��ff��ZY{1'؃�u&��n�&V��<�d Fn�W�z������t���1=�X�d�)�k� �d9� JF#�1c_���ݡ'۱�SJEfs��ܠ�j �Q���r*�4��Re 7�3:��v�q���`3�_8� ����Zݹ��i��4���5��aY0s�j�٬�S��F�u�hQ� +0��I��ׇ_.��<~��⏳�d4�xr�<8{ +u�LWGH��IHϤN��ho~��h�og"���+9�mHLre]8��m� }��i�ϰ(���btI��6lkc�se����|u Oộ9�����0x6QÓ��U���#��R� `r�Q�Z��`����y4ʹp4��1aC�C��5��������mA�1�S³<���8h�)���&���J��9�3,�zU/9���*�k�8g�D�����K�UuGTF��]�kM�a�4��|��, ��:�Ba�#��5� �<��%��� +��_ �-��ɪCˆ�Aǡ8}w�3f�M e��}oq���R�]�{:��V۶�!*������k}%��F�!n�b���Q�`�m�P&Jxd��$1�N?ĵ;䕲f�6%L����ܜ7x��y~�/��U�M@�����{J��U�(���ވ7:ZQr%^@[���?���7�# 7ūbw�-��0�����Zmo�8��_1-r�\�N��.p���b+�P��Jr�A���y�EI' v����_-����, g������>�F'gϟ��s��葑�����/���������~�Lp���M�}�N�փ�h s��?>���>႑۽ 4���H�՛["��v|Dl�2�?� �Q�l��$������1zO|��"b�aC��>��<�D��j��7 .�a�@7)(��v{.�a�H�آ[z/?�� +� ��Ėp�OQp�WP��{";��hHX�K�&b��{�]}��w8��)C�g�[�`�f<�5qjtA���KL�XI���*��3O�Ǽ(�)�z�[,�A�>eK���Q�!6���cF�Fw�E8݈�p�,u3aO�D�H�cҽ���8/h��M�½�mLV��ٜ38�wn��Z]��ǹ sk13l�� ��ҵ��k�<�0��j�,g�/���me�� ��ja3��m[_����\N뙹�8�󵫬i��0/Mט�k�bhF�`�.�Ұ�s}�����t��� �]J�� :�t�5��nKN������3ә.t�Ҙ��\��㳱t���E��`�Eu%�s�~�0bq�k���1u�f��ԜKW_��YSS>����n_+ -[�1~]K��0�/���Z�@��I��m�R�.$+g}�v �hY3eyǰ?�S�y �Q�[;�f������m]���V>��S�O�2��a��kZ�!̭+�a�T_;�L��Z*�ݹa�ג�4���\� wn�`.U(��ӥ9�6�n�Ҳ��l��,,�� 󣱜�%]��1T�e���1c�W�5Xk�����cďw�����gS����KX�c&�c]�����+�C��o��e�� (��{�[��r뷣|ȣZ���>��!W�Ν,�1޳e"�ap����}Ma�''r� �aJ� �K�ɿ��{$0��kO�����.�c�����aOPF0� �У6l��{�� $���ά�;h����IB/�+�X���8�F�&��{����aKV{Iΰ��m��o%�F*��,1�#L`�^,��E��S@B|�0�x�5|�h��y��0]���zt�C��� z@lw�D�3ԉR�=r�k��(h�uɐ�K@@0���|$�e;�d�{�� ��Jz��"��+�~i-�6���[��0� +x�����:&0h0G|�w���5��9�� H��wH����r%��$���~���Q��M@�=~�}�tWZHR��j6��S��͍GC.���.�����G6R���l�Żr�*x�Dm#�����n,&� �����`��k���1���g{��nIxV�M?ڕ�X���V&-ZTNyP_�󢵵��-"�S���vWV°س�QV_e3V� h��{2��uu@�(���62���G�T�W�6� �d��!AL ��Bb;�^ju��. .r{���)��h(M��\ؽ���w3���0Qg4��o`0tB8;���wX�r��૒ ��4�.����!����@V�5�{�N�=S+�-르��{ #� �{��L���O�Y��i���-pf*n6l��˳ �[�#Ys,�8�K7�$l�#OS¾�b��Ôca�%�I�ۂ=ςS¾�S1�ػ���Z��>$#U.T�p���l��,��Dƹ] �����ċ��m�n�0pzƿb����r�Z�a�xkeC�Z+ǔ�m�V�uڍ�q]�ӎ��zZ׃�/�����jP�;r��sWr � � �;̈W8�g�@#�V�N�BW�[�`.؍L���05plH~��Ty�T�1����΀���3�¯�s_s:��,�L�Z��D�6����5��W?�ޯ�Z���5O +nY^X\ `�N��G0���/�ʾ���S��>v��&Y~�%�ޜ��:���q�8��)��f�����;�4�98#I�$�-�:�gh���CE�3m��;-��A<�ن�Pg(�����GI�e��ͱ�B/QF�TZ�H�-� +�EO���n�9J��!B;-(�� ���CA� �c�CYA�΁�TP-������Gp� �E�� �{RV�Ȋ��@��\�*Bi�r�@A�J�I���;���X����;''��\� ��X/���M���{0�6M�a!���J��x�i6K������E��e!%z������h���mnx�7 ��1����j�}��:�:���� o��N?J�#g�������H=�������`ؘ�-����� �Q@|�ݩ{x#-k��/� �F���7y�.�2�}'�ո�4�8դ�z޽Q����a����}�F=�3�ů������� -��1?{�Wb��P�Oj�Y�pʅ�7���΀_:�tٷI|����(�k;$��suӗ��v��u���׺� 5�ݪ���-֕��-ʦWt��و�:�º�P|�j�&ײZvAۂ?���ӵ���U� ���1�yɻ�����G� ��U�(�*���Os�C��'���p� vi�W��@`"A�%����i�܌g�H~6窎g�q� �(J +"Zq�,R�,�^-��3��@v�E ���v{�[pR�wv>�%,Ā��B�GPِVsմ����D�]J�b�������O�[�V�q��Z�j���8��|��V�`�@�Z���SVy�c����]]�]�V&*��栐�IBB��p� t'�%�������� %߁җ=7����=�(�R��F�za���[e +z$/�J�(u~h�N�ɵv�\~�SOkV@t��/IH��g�����!H;���#�!��R��{�"�]F{E�7}H�2R*��y��@7����}����O'�6�a�m�qկ�q�Q�AE���6���_���;��E9f�r6�21ȓ�4a +�a���)3�И{viQ�t��;�i�/�+��;�n�jp��D} b���� D���4�����ۿi�n��n����6�bc�Ľ�5�u�Y�����:-J�� ���U:B�1U���|����"{�ʴR��z1� �h�PCx��(�~�JSa7�޵V������ɏ��V]o�6}��� +,N��u�Ú��,�1Y�D*���"]�DeQ#�A��>��%����`���s�=����?�Y�9>8�����S����~ޅW''�:y�;x�B�Y����6S�_7�G���^Y��֠P����8�Bh����YAV���-*Gw�ZT����Ts݃[af ��� sY���3 г��B�Qͅ1X@��(�3� ��T�����eU��]�͛/��ANR�,� m@��D�`�kyc5JU҈{K43J���i��'� +��2sTGϰU[��M�d����"�^ �/�X'z�UűT � �3�Jd�~���n5�n1B�rmL���{�J><�+�K,�tCd���5Z#`$`UH��z�Vr. �R&��@%n�����"ZN�m��kl�k̭ϠVºOY{UK�i��)���� �I|I@|H��Ǔ�^ 9 �0 / +��#��~����ǀ�=�x��h��8!�A��CJ��ċ8%�4��4��E�)wj�B:����ޒْ �F$�^Ľ> )����#[q'���K8���K,�8M�1#`[ (�C��Hp4�(rI"l��֎!N��Z�>��z��,�EhB|n;{8�i@"�=`c�S{Bޓ�8����0N�������S/��y������c�� Y���B���8�)'pǁS������B3'\�H�{=�|���٩=琉Z�,�8I�t�iua_�K��樓N�8r=�!���ŵb�Q��jH��$@#�[�<+� �y;2N�� o5 ���|b���2�u�J(�1tY�ʛ@�����RF��-;��h�� .�%� v���+��`�?\�߬��:˿f_�.+oad)����f&�[n?�F9��~"��� �ȱҎQ�./;pp��=��r>C��>6��{�cS����Ť2�RfZ���;(�.�*��� �n�9(�{!�Uf�oEXe�'B8�c����o�W�ms����Q+q��.>ڜn>+��tKn�7Xn{�m�� S*���cץ�a��r����s.+m�"7���^QB݃�u�F�����������]�{gk��m�m8���&�~7�Z�����ũgp��T*�����L����;x1%>�����/���Tt��kJ.��n�kS�ey�����F�}����k=�gz[����~�F��ȇe֧���oQ-�G4�@;��N;�a����v�l�6�����˗� [U�� ^�l��̔�� +o��u�-�ڒ����W�p��T��2��� ��3���z?�|�K9ʪ����!u�����h���������9�X��l��&�g�lYJ뒛^�T`���6g,i|���ȶncnv��mGƒ/�y ����`Uh��p���p�2�4��o�]��s���Y[o�8~ϯ8S�p/�>m3�V��XXE�Hr2�tl�E I%�m���DG�d�}X����|�;Wʿ��X'޾=��0f����+��������O������ߐK���{��}�J��2��8 +���? bJ�������@�J�@s�� �_�iN�,߈!��+�M��%�̍ +��:�F0�kw�L`t �́q0� ��Y ���8a�?�q�ǡ;Z�A�;7z��ǟ��߂��������\?v�p1����,�q����"r&�ׁ�m�gN�*\� �!�̜x����+���Q�㸹3!¸a,�Υ�^:��Q���q#g�S+t#�ǭ��ط,��:h�ȩ>6�y�C ��ɵ��W�uJQ���L!Z�g��Me|)H�P��]J�1���I)׌�r;�D���Վ��;��KF̅f4�&�U_+�Nt�)H��1B�w�6|?9QB�2k�1��?E ���^q�$+St��oS�I�DN$�g��$#B��h9�C��Ym^o���0f���?�~.8} �4�{�^"�_�Q[��� ܵ]yWq4<�u��M`Y扞�J����y8U-��E��/�� ���r�%�a�GQZ�{��fP�����,Mh�3�������X �C|��jG�Pzl�Bn ��Ϋ��S��``���w�hywA��%Ey�R�?�9���>�vw���w8��M;;��V����'D`�{G�a�z�%X�tty99��0�mV ��w1���I�4_�b�J]�� �[��[w-:�~�7�� +�h���q��4�� �e���ӥ�!���2j�4C��mI3��Q!�U���D�I�$pe.��K���� +�W5�b��{��N8'O}d������6 ��X�#��pl��Y���`�n:���0{R�]41ː��n��RH���X��YS��t��QF�o�����K���VW��pP]��z&VԷ5��1m������epZI��h[ӱ�Zo�jh8�8\8��*j�U6�OE楪���-��݅@YK�}���KV_��`ԓVF���x�n'�Tߝ��MF�o����Иfdeݩ�$��č��>���|�n�<��wtT��go5fZwF�N�8n���xhR�j��>�f�~"rMi��cf�5"�45 {�`ˋ��- �v����F9���0+�3&���oͅ$�1��GH%'�}B���@��W�Z�#tC��_H�� +Q���+F�{������S �M�V�g7���S˨��]�T����wE��+����0� :}��!�[� �5OThr�Y�=�wp��Q'P�MTCu�P���K �%����J��{���vK�z����ժ�R��k#�'�� E���WG�1�~��>�gzk�F}uo +���u��Ƣjbֶѵ;��=�R�{�m�}����� _S�-�[�������S֛K��9�_���_!e��������Z}'�=��iw�R��B���_X߹�ʁH���>�Ԓ{CUnDbL7��Sv\U��w�� ��wGڣ9�8�o=j�^� ڞ�?�]NU�y�zjr�$+����2����6�S�,��8�7�R����?��X�3������#�y`��LҴ:�<��'ܥn��><��<���U�o�6~�_q(��jdO[�.�D�d�#���D�D%�#�A��} m�n�}�^�w�}�ݑ|��n�]�};����ݳ՛��qu�WW��������L�}V�;x/m�nW���V�7j���r�>��b��Z;o����t �z�@w�Lo+WV����ƶ.�'��`l���Ckj�֕ I��V�N�V{�j�Y�kU��J~�`m��<�n��j�\tj���� �����R�����`�����re�֠Tg��T�G�[�������]��U�]�H�*{�ltw���fgM�W��"�\kS���|}�]}i,�UZ�ղq���E�DNS,������� +�^uXg���@|�e����V*4R ހ�jc� +=���5^�^&�VV?��ִ{E�Y�'i�6��۩*���gC{u�Fs�$1#8���0 F�I�3�,A�0�t�d�n&`F� 3�� ��`dR +�8�AO�*��?.�(2_�g��C� �'@�4/3R�%0)ET� +�ɜ���ɞ��3�)�1Kg�hBr"�1ꔈ"D�R� ��9biQ��B��i��g@ +((�{\�3����(;M7N0�Mr�W,!# �"dv�$Å@y|�S�#�/rĖ1Cʂ��Y�B�C���s��:�Hi��<���� D���YT�cvOR�o �< +Wr�@�J����)�&�'%'A��E +�+���f��c)*9΢ִ�9��lp�� <̰�a��Wq9�`$�����L�$ ���.Rviz ���b�����@ː~,Z��~x��I,-�)���{���srh:^�����ɸ���ܨp�P�Mcd�e�����s/���V� ��G/�6�R���&<�|?���[f'+b��R���6|��S�H� 7����Z�vV?J����^+���� ��:|Q+[X�|�V'�G�~�� +�}W�7�ӧ�t�۾�����Q�,\r�> ��7����~�1��Y�������va��h)^��k��{�>�|o����_����U�Ƈ` �Ͽa�2z��S�n�@�����6�nkk�������L�Q6�e3�k���A¥�v��{oO/:с�=���+�O"^͝�� c8�H��@6� �R{�(��x\�a �d0���-��Ť:k��#��5� Ȍ�a4��)� +� cC�� ���u�B_�ڇS N��@5LPF��#�Ў'�n��b�f���h����Ɔj�������:�O�a(#KS�h��p2�F:p<��4u �ڙf���ND�*� � ��F�TYʱ6Ԭs>�f�p��Ɗai��P1�xb�uSdq��������.h#�~TG���p(�tCd�0Ԕ� 7:��f�} 9˾���:��a̱������z6*�9�P7P����:�4e�L����*� ����Pϐp�Q��c�Ҭ���]pɛ��Q�� u� nb�(�������h�y�ߏ'���C\��R c2�4}ԆS���Q5��LLu�e��8�֩�������NU�T5@qS��SP�eh}K�� �t�����a�}PG}[uD�I3�6W-C3F�����>A���ML5�*�s�O-h'� >jH|�UB7M-V��I�4�~�2ޯl�ƾ&���ux�=���u�(_n�y���U۠G���{�u��8E������eog�ۘ����$�~N��/;;�eoƧcx�} N�\�!;#s�'��5� E��o^7;p���͛�p��zA�R�f� _x�G��qﯹ����GpgәHCq��r�NO&�����e��h�jD)�@�Z +W$�`B��䬊0�&c����hg�x6c�q;���Z�/?w=�b��OUո�Vԕ��㟜hGܹ˕G�'o4?$t�R���uo�@cI�E0;�W��R�����k\����+8��>���`�o^o�*��!^�j(���zY�۳�}�T��}��nn�O���O�V�G 7�E�T�J�~R������ ���ܐ���._j��+��>1��S}��6�1ЇC��]��dž�W��!���#�…+R�n�t v�r"�A�Ze����$Qjl��Պ���s�،h>#>sC��Hn�� �)�RT��٭4�G2���qk��O�z�/��%�)�\E�CrK� +4���h+k��k&�� fdE��7��HɌ8�M�(3����\�k���t�> �� [�9��tMD�O���we��']�f�����l�vo��]z��ۤid�s:�-J�R�g:���M�%��9� �����+��?%���ak/��*%�}h���?�x/�%6��[�^��{ #���-���=�7a��Ka�'��#9��9n-",�^_���p�i�����t��U�a����D6Nyn��؞7]3B���[��F��p!s����~䙉m�O�D��mA:�h�;rSЖ���d&�����UX$qտ|y$�{�K8x��$���|n�tGs�c�����͠D�Xr�B2-�f���^����%�Ilg��)��R,���،�x��;7t�j�����J`3���~����:�����өfE9�aŠ�\Qb�ȝ����e�Zz�E�Z�(^�#�M[��qp)g�Q��5��:V �ҍ�H���}:��,u40kI(�(̈� I5>��h� ^w�'���7�<��\⫩ov�uDs���SR��%좹����-%,�n� ���¶��j6�Q�缪��s=#s{�u� +4��"I��;d����V���y��-|�Q�M(��O��naf�q\;� 9����'�+�|��������O�Չ��-Ԋd�˦.O�b��+|昦>����!�_k�;E �*�Uٵb���# �y�"�Ͷ���D��矓�$?l��ۗQ�L]z��$�K ����fi�'�l���HL��q���P��[Y�N�]�_�nJkb�v�u�s��IV#����j� 4*�^L�� a�ҍ,ɥ P�'�Hz����X���cI}��� ۯl��zXI�i���WD=E����::>���.;F��j���W$D$��B��o-����������Hq�������������ٓNOٓ�҈����9�?g$�{�G�,F��.K�����a��D�ߒ.��=`凩]�s}~�v���u���Rw� ���� �PmK1���?EкI��{t�HeD<D ����@�g�NO+�n޼G�� \�l�RZ��6D$���]JZƫ?G�(W[���T��C=��l<U���� a�5�T�/�~A�K���� i��E�@�#��LK�2E��zF%���{�x +��"��r�sB��,8j�הS!�ּ�̳����!�>Ӹ�G ����ރ��9�h��5��G +�Ң��alr2k͊�[n�b�v��w�Pݞx�mK����ǧٿbȱ�ы�ם�Z��~R�땬O�JN��yD��& �ME�o7�q�[�(I�[J&a����3ME��OŎS�����LE��8�Ēs�+]Uq��4|Ƣ�]�[�NT�{�D%C�x-�D��&#��4�#��zY��˞ںq�h����~�{�'�+ ���#"�'*��_��k�/�s��Ҳ�"�-� μ��a�������G�v׷�SB��6׹l+�������N����2X���5�'��m�~�n�+ۦH��w�J�v-��եPZU?}��� M~Z���Z�W�Ǔ6&*�1L�!��T�Uɮ��애`Q�/�M��b�_w� �Q1N1���)(�%HI +$�%�Ʒ��plk�N@����%R��V�3;3^<�>�@{�D����Y�W%G��3k�ϗ:�0�� Gt.��Ђ?���h�s�[XO��8�ܻ��'h,b���xI%�Q0�Jc�)|�dł$�r{�-�W���Kܮ��AL��u]!�,n��̸�Y���Ku�t鿍�T�C5�;w�P ��2z�.E�Ύ'�B.kM�rDOTg�e"�=65�ǁ��O�f�Me�EW���?u{}�nn~�/�R�n�0��+� Ĕ�Əc6m�қk CG$R�]� ��{@ٖ��Q^4�.ggfu}S�U�Ti�R�������� ���=�t���Nq~��J�b��Z8����0��/ +��{�l;�E�+�/� �O�f��HP��M$ٗ� �;b��U���%��{�޸i]�ca5�j��z�1�H���;�F�\�\)j� {É9���G�{� hTQ)��$W��,�=��N�K�φo}Yz�[���vB�ڱkH@J �)�h�Fg5��P]0�)��m6�٨�lr�����*�ez��whv�U��=��D;.2���x��%p��f����v =�*���0t����0NĴJ�Ay�-��e+����-��-\7�p���j�-H�8��������+O⸆`1l�&�(�C\�'�XQo�8~ϯ�9�)���" \�I+[LL�#�D9٠-ZDŽeR ���"��@ٲe[��N/���Ǚo>����K>��>~�P���|����B+9��O��}t���7�D�d\NQ[p-��z>N�B� J �'L��c�I��0V�Qa���e +�A�*t�囑�\�a��̴�Y� (]����L�b,��.�9꙰Sȵz)�`'܂� �U��g!!Q2n�)'�Оm���5�LT�0+��� Y��zrC�RY�`{�`'�@&�uXu'd��a*L�q1C��^�gB���<˵J�����2�T%� �-R�2��4(;A 3nQ ��ur�Ė���BP��V�:'�T)�z�,�X`*m�N��F�D��U�2Uڠ�W��LY�u�@�Zt�!�膃��^�b�}�D ���n�� �0b��c@��� |��{  "��ЛA��(��t�C��m� ��0�>��1�!�kI�WpC�n� b�C�4�/W��q�V� +#�`�E1��^T� �� d\�>eݾGo� 4� rK�X�����0��^�v����Ų�=�4"��E���R���o�.u7�/r3�{��*�0r�2�! b����n�k �E�[.q�aDn\�Udž�x�C��#�-�v���Di���^ۍ�������w��:>+<�$������!��;rK"�zCF���0(�{$���#�LO�z$�h�*��L����v�uAFq-p�u�^��K�h���(#�+�E�9;�p�λ�p��(�9ddq[�|�L9�+��[�X��2F�� +�� ��eF�w�ל'S����+��OWc���˧_�d_���f���|�5 JSz�a>���1|�++W��x� Q�B���J+�(�1_hL��s�p)^�/�$7�;��R���r-��E�7՜��D�r�e�j����LE�7�aØ���Bc<����t�@�����a��)�\i�)�C�'�f�������'pq �Qgg��~>i�ڝ���ewr�{���-��+� ��Q&2)C����"���t�r�^�{o���Z����.�Z3[�8��|���A�]K�F��ڟ�_q�&�����e6��ڷ�|g1��;!i-Mڋ?LqnZ#�z8<�ƃ�$��3H|��T>�L��~,�7�g���3�]&�B�q�`� +8�����{w䂩T��4���'�g�AoB˳��n��Ru���̧�nt�.���������i����jG��rm;�x�Z���+Fh�-F�#�;�������a��������Y����|�B�(���)���P5�v�pwv��J�^�����YkY�� ��#���7��.���#ڇt1T���I^X�,ʺX���b4Ź+ +�ž�W?�u�ld􍈫��&[�w[ �mQ��z�fs����is�� ?Vy2i�7�zݨ�X��+�fԢ�V��fw<�|x�IM�^�j��̀k"�78'�n�0 o�Wͺ����n+��q'���jE�!� 5�����Y5�BKx��~k���f��>���-��7۵hKD�� ��.S�:�..��QѲ>k��k>6� v6zps�[Z���q�6��66/� ��K������;W����-dȍ��L�e�SX��:9�Y6���}���m����.�D�Ǎ��x� ��Ow�������_=�� +�@D���-,��/P�NL�@8�Ir���=B�]���f�1�?�!�/�h�Pcs�8�M�9�)Q��p� �[!�DMy .҅?�(�;{G]�>��:iʫl +M�^$Ɍ��Wɭ�1̣����;Hf���.�׳f��(7�`ID'�$�b8rc��m��7���<"q a�f�S���En�P��S���z �Eb� �� M�I8n���^� ��37H� �irg�^�$0��\��QB� ߍ���"��1�G���� �|#A�����C��5'|�N|Ҹ ����&&��Ӕz$H\ �L�y �����Fw6�02D��_ $���so�k��C�;&I�EDn ��ʘ��8��"!p��e>&�7:%�9�al�[�d ���c�?��+����y�����آAB�h1Oh�`ޒo$�����g�s2#atg�2l*�p;#ɌD@;� q��#N":M��aI%�`! �>�&�������1�Ҋhldh��ֽ�pa·I[Ĥy��ئ���7j�7¶$�8�m��W/�����3�V,��д�[kQ��uV땐���w�|�N�wh��:��k�3,�E4�=��|8 쌩X���0F���@ß��Q��#��5�y�+Pf$f �Kl��R������K-���fZ�ڝ��JD����am��߬`JAlC���Q����2W/�{�h�0����s��v�qT���Պ��k0l��[�/d�Qo�l1�\�NO���#5âB�3]���`Y�����i&J�e�i�Iɞ��rb�9>������Ea�3��>;�[����>6���˾=��� ?��N�Z����u�x���ĊI��6��=��d�h��`8L�� +��+�}q�KNo�`L�9V� 3��'�nn�ؓ8������F3_1y#rt��AX�F;u��0�^�4W5ss1W�RQfb��[yg�;��0ʨ��:V�����^l|�[]���U���%�Z�����5��X�K�=S�q��p�����^~v5w���F�������I�� T� �4���(M���'GC8���Ξ ����ٚ���V8�i�ÜKs�8izE}��#8�!��0M=��y�q=x��{�m&���ΜY +�,[u0��z�m~gL��Ҁ8��5���[��(68gz崚?�(�*���BL�(�������D�޲�39�F�M�g���3����a�'��nI�5*��|�_:��F�8:�&g>������P�ޜ�����1�Y��w�󶊻HB?tۘ/.�.�Ms L�}g���-�ch��-�Dx;��vk�LfD��̄��u�)��O�_�u�)���0G�Z���9�Q�� ����ͫzA�$��[|��n)}�J���?�Li����zC�B�Ȼ�M�9������t�h�\my������ʬ�f +�:&���}�â':@꜒h�@�_2=�����'���O1�v +"�U&[we@��aa����j �����v����0��?��O�������C��nl��X?�,�O�� q�ߚê߬���X/�7�V�N�8}�+�F�ZF- �i�a�4q���tm��J(M\jMG�[����ӆ�����%�}}|ι7v��Uͫ#8���y�|]=�8��f'�8?��q~�v���q���ʑϞ���{�Ƴy�L��!����'1Ǐ�� +�_�u;�^,ܭׯ��sYgFM%���xJ�k���2�_�r�:+�E���x�u�&�%G�'Ήc�5��L�A��Ø/�Fߎ��i�������N�r��5�||WI�N5��):8�����N�A�O�_���߬�S+��Ig8}��6]������������u��j�0 ��y +zH`�Е�K3��4= �&VR��3Y���>��R��ϒ>��n�%�)<�^�V��V�C�:��d,)���X�4�JKd�7L�@��[r�JHCc,�-h� �6���b|d���Ԛ �i����]:�� +!���rL%��P�9���ى��3��r@�E�bV_�� b�ZTժ�v�8��y�!9�t0� �$>���L$'��I�޲k9���0E��EU,�-"�Z��n�?��%/6��y����f[l��2���������]�Mk�0 ���:b�X���Y���5I�1���Ր:��0��^�|�= =Hz��hN�8ӡ|��� �|W�KK �qЭW�����1��m�8/�j� +����u�����+�/�M k�]�D�ʴ�ˢnq��t�B��pކ�>��W_�)��^V�~e�rV~ �J��I��z��|�V��=,foE.w�O>�r��="�M^�<�?�XLxX�9  +�ΰ� nӲ���E�W���u��J�@ ����X��}��hݭP(VlA$ �lw`���T�w�Z�?�2_��!����}�Z�]�q]-�i�:��L��Li�?�UG�E��L�&� ��A|G�X i�Kp ڃ���D���b�D�^����-"Ƌ֪���(1-LM +���]�1���� �±�T�Y}�G2"nʬ�ˢn�Ȓ��MR���"���H�9��{O�C0} �i\w����xFċ�y�! +���p�+�L� B///�����M���#"&��;�u��j�0 ��y +vH`�Е�K=�.4= �&VR��3[��w^�� �?}�>���c�,�X;�m[��Jz��:O���?��}��- �b�92>�:�� {v-Y�IA� �(�1�Ҝ��`�d�T�Ձɧ @�ۚ���Mmd|��cK�5 �����,G�H�p5��1��{��Nh "��uUm�ꀈ��m��W�@<�߆�{6�{�赧�xZWbS�q�5w�n����k.�C�CıX�E)v��j��r%aU�]��J�0��y�9M@���EJ l�Adݬ�mL҃Ⱦ��J���c�����o �!���'t�C��%�k��;z��=���u�2�F�<��I'��@op7����ٵtN~��Aļ��,���lׅW{��d�w��;�5h� �鶛 "�}V!b�>_ˮ�� ͹h���������ؙ�DD��S�8�dfb)A�ge���E%�(K�/u��J�0E��� l �~��h�V(+�� 2�f� ��&SAd�]bٺ +;O��p��f܏�z ��S���UҫP �y�׆�� +�A^4#��sd��tiAN���%��^�+P�c �9�'۱v1n���w��^tF���WX��`>R�-�ɘ��=��`;��0ΉJz/?Ӆ�AĢ΅�+�"�²���d�����yy�N�<�O�S���E������{H�h0d�T�iЁɧ��M�.�]�R��m�< b�9�Ҫ�u�A +�@ ���"�~@P�z[�!�t(Iٍ}��x�y`��}��0���VK�157�t����iT�G%���b�ځ �^ŤQâ����<@X#!��q�剈'�h�>��/�(P��W(��,V�,VH�SH,-�OO�K-J,IMQH��IU�UH�W��/QHM�,኏�w��q�� ���犏�p� �w�� ��q �д���{v)�[��)��Iy� &�5޵yv]�)�Ӗ����Pa��C���������o�3n3�O*�GBMB \ No newline at end of file diff --git a/tools/phpstan b/tools/phpstan new file mode 120000 index 00000000000..5e15af476e4 --- /dev/null +++ b/tools/phpstan @@ -0,0 +1 @@ +.phpstan/vendor/bin/phpstan \ No newline at end of file diff --git a/tools/psalm b/tools/psalm deleted file mode 100755 index 86f33cc6bec..00000000000 Binary files a/tools/psalm and /dev/null differ