Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sebastianbergmann/phpunit
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 11.5.14
Choose a base ref
...
head repository: sebastianbergmann/phpunit
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 12.0.0
Choose a head ref

Commits on Oct 6, 2024

  1. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    688df78 View commit details
  2. Closes #5978

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    b2f75a1 View commit details
  3. Closes #5247

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    a79078c View commit details
  4. Closes #5314

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    593df34 View commit details
  5. Closes #5249

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    3b2c9bb View commit details
  6. Closes #5313

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    9edff8d View commit details
  7. Closes #5250

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    769cf22 View commit details
  8. Closes #5248

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    efa5bf8 View commit details
  9. Closes #5246

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    26d505b View commit details
  10. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    4adaf9b View commit details
  11. Closes #5321

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    4b851e1 View commit details
  12. Closes #5215

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    8d269d2 View commit details
  13. Closes #5217

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    3ad870b View commit details
  14. Fix code coverage metadata

    sebastianbergmann committed Oct 6, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    e2aebfd View commit details

Commits on Oct 7, 2024

  1. Closes #5311

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    66b1faf View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    1f77a2c View commit details
  3. Fix CS/WS issue

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    1ed6f17 View commit details
  4. Closes #5316

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    4c3f4b8 View commit details
  5. Closes #5310

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    bccb9c4 View commit details
  6. Delete unused code

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    14dd103 View commit details
  7. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    da9b33e View commit details
  8. Closes #5312

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    4a325be View commit details
  9. Delete unused code

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    d0f8ab9 View commit details
  10. Closes #5473

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    da4c344 View commit details
  11. Closes #5424

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    6738627 View commit details
  12. Closes #5710

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    ddfbf3b View commit details
  13. Closes #5872

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    cc9dfca View commit details
  14. Closes #5952

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    1964ae3 View commit details
  15. Closes #5801

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    8367f93 View commit details
  16. Closes #5961

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    f460b5c View commit details
  17. Closes #5959

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    309f6f0 View commit details
  18. Closes #5416

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    98cc111 View commit details
  19. Closes #5536

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    a98e393 View commit details
  20. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    307c404 View commit details
  21. Merge branch '11.5'

    sebastianbergmann committed Oct 7, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    38e016c View commit details

Commits on Oct 8, 2024

  1. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    6f7c3a3 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    e6d1658 View commit details
  3. Closes #5541

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    377c89c View commit details
  4. Merge branch '11.5'

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    3b09509 View commit details
  5. Delete unused code

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    d66ed45 View commit details
  6. Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    cbb48ab View commit details
  7. Delete unused code

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    3d77725 View commit details
  8. Closes #5756

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    231ede8 View commit details
  9. Merge branch '11.5'

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    6ae5dc5 View commit details
  10. Merge branch '11.5'

    sebastianbergmann committed Oct 8, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    eb49da8 View commit details

Commits on Oct 9, 2024

  1. Merge branch '11.5'

    sebastianbergmann committed Oct 9, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    2530d93 View commit details
  2. Merge branch '11.5'

    sebastianbergmann committed Oct 9, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    84bc668 View commit details
  3. Merge branch '11.5'

    sebastianbergmann committed Oct 9, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    50089e6 View commit details
  4. Merge branch '11.5'

    sebastianbergmann committed Oct 9, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    fa1595a View commit details

Commits on Oct 11, 2024

  1. Delete unused code

    sebastianbergmann committed Oct 11, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    sebastianbergmann Sebastian Bergmann
    Copy the full SHA
    a43f67f View commit details
Showing 441 changed files with 4,278 additions and 13,130 deletions.
Binary file added .github/img/bubble-shooter.png

Unable to render rich display

Invalid image source.

39 changes: 39 additions & 0 deletions .github/img/in2it.svg

Unable to render rich display

Invalid image source.

45 changes: 45 additions & 0 deletions .github/img/phpunit.svg

Unable to render rich display

Invalid image source.

30 changes: 30 additions & 0 deletions .github/img/roave.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions .github/img/route4me.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions .github/img/testmo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions .github/img/tideways.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .github/img/typo3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions .github/img/vema.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 12 additions & 15 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ on:
name: CI

env:
COMPOSER_ROOT_VERSION: "11.5.x-dev"
COMPOSER_ROOT_VERSION: "12.0.x-dev"

permissions:
contents: read
@@ -29,7 +29,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: 8.4
extensions: none, ctype, curl, dom, json, libxml, mbstring, openssl, phar, soap, tokenizer, xml, xmlwriter
extensions: none, ctype, curl, dom, json, libxml, mbstring, openssl, phar, tokenizer, xml, xmlwriter
coverage: none
tools: none

@@ -100,8 +100,8 @@ jobs:
timeout-minutes: 5

env:
PHP_EXTENSIONS: none, ctype, 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
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
@@ -111,7 +111,6 @@ jobs:
- windows-latest

php-version:
- "8.2"
- "8.3"
- "8.4"
- "8.5"
@@ -148,8 +147,8 @@ jobs:
timeout-minutes: 5

env:
PHP_EXTENSIONS: none, ctype, curl, dom, json, libxml, mbstring, openssl, pdo, 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
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
@@ -159,7 +158,6 @@ jobs:
- windows-latest

php-version:
- "8.2"
- "8.3"
- "8.4"
- "8.5"
@@ -206,8 +204,8 @@ jobs:
with:
php-version: 8.4
coverage: xdebug
extensions: none, ctype, curl, dom, json, libxml, mbstring, pdo, phar, soap, tokenizer, xml, xmlwriter
ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On
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
@@ -239,8 +237,8 @@ jobs:
timeout-minutes: 5

env:
PHP_EXTENSIONS: none, ctype, dom, json, fileinfo, iconv, libxml, mbstring, phar, soap, tokenizer, xml, xmlwriter
PHP_INI_VALUES: assert.exception=1, phar.readonly=0, zend.assertions=1, error_reporting=0
PHP_EXTENSIONS: none, ctype, dom, json, fileinfo, iconv, libxml, mbstring, phar, tokenizer, xml, xmlwriter
PHP_INI_VALUES: phar.readonly=0, zend.assertions=1, error_reporting=0

steps:
- name: Checkout
@@ -287,14 +285,13 @@ jobs:
timeout-minutes: 5

env:
PHP_EXTENSIONS: none, ctype, curl, dom, json, fileinfo, iconv, libxml, mbstring, phar, soap, tokenizer, xml, xmlwriter
PHP_INI_VALUES: assert.exception=1, phar.readonly=0, zend.assertions=1
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.2"
- "8.3"
- "8.4"
- "8.5"
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ jobs:
tag: ${{ env.RELEASE_TAG }}
name: PHPUnit ${{ env.RELEASE_TAG }}
bodyFile: release-notes.md
commit: "11.5"
commit: "12.0"

- name: Announce release
id: mastodon
107 changes: 0 additions & 107 deletions ChangeLog-11.5.md

This file was deleted.

51 changes: 51 additions & 0 deletions ChangeLog-12.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Changes in PHPUnit 12.0

All notable changes of the PHPUnit 12.0 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.

## [12.0.0] - 2025-02-07

### Added

* [#5984](https://github.com/sebastianbergmann/phpunit/issues/5984): `#[CoversClassesThatExtendClass]` and `#[UsesClassesThatExtendClass]`
* [#5985](https://github.com/sebastianbergmann/phpunit/issues/5985): `#[CoversClassesThatImplementInterface]` and `#[UsesClassesThatImplementInterface]`
* [#6073](https://github.com/sebastianbergmann/phpunit/issues/6073): `#[CoversNamespace]` and `#[UsesNamespace]`
* [#6074](https://github.com/sebastianbergmann/phpunit/pull/6074): `#[RequiresEnvironmentVariable]`

### Changed

* [#5872](https://github.com/sebastianbergmann/phpunit/issues/5872): The default value for `shortenArraysForExportThreshold` is now `10` (limit export of arrays to 10 levels) instead of `0` (do not limit export of arrays)

### Deprecated

* [#6053](https://github.com/sebastianbergmann/phpunit/issues/6053): `Assert::isType()` (was soft-deprecated in PHPUnit 11.5)
* [#6056](https://github.com/sebastianbergmann/phpunit/issues/6056): `assertContainsOnly()` (was soft-deprecated in PHPUnit 11.5)
* [#6056](https://github.com/sebastianbergmann/phpunit/issues/6056): `assertNotContainsOnly()` (was soft-deprecated in PHPUnit 11.5)
* [#6060](https://github.com/sebastianbergmann/phpunit/issues/6060): `containsOnly()` (was soft-deprecated in PHPUnit 11.5)

### Removed

* [#5215](https://github.com/sebastianbergmann/phpunit/issues/5215): `TestCase::iniSet()`
* [#5217](https://github.com/sebastianbergmann/phpunit/issues/5217): `TestCase::setLocale()`
* [#5246](https://github.com/sebastianbergmann/phpunit/issues/5246): `TestCase::createTestProxy()`
* [#5247](https://github.com/sebastianbergmann/phpunit/issues/5247): `TestCase::getMockForAbstractClass()`
* [#5248](https://github.com/sebastianbergmann/phpunit/issues/5248): `TestCase::getMockFromWsdl()`
* [#5249](https://github.com/sebastianbergmann/phpunit/issues/5249): `TestCase::getMockForTrait()`
* [#5250](https://github.com/sebastianbergmann/phpunit/issues/5250): `TestCase::getObjectForTrait()`
* [#5310](https://github.com/sebastianbergmann/phpunit/issues/5310): `MockBuilder::enableAutoload()` and `MockBuilder::disableAutoload()`
* [#5311](https://github.com/sebastianbergmann/phpunit/issues/5311): `MockBuilder::allowMockingUnknownTypes()` and `MockBuilder::disallowMockingUnknownTypes()`
* [#5312](https://github.com/sebastianbergmann/phpunit/issues/5312): `MockBuilder::enableProxyingToOriginalMethods()`, `MockBuilder::disableProxyingToOriginalMethods()`, and `MockBuilder::setProxyTarget()`
* [#5313](https://github.com/sebastianbergmann/phpunit/issues/5313): `MockBuilder::getMockForTrait()`
* [#5314](https://github.com/sebastianbergmann/phpunit/issues/5314): `MockBuilder::getMockForAbstractClass()`
* [#5316](https://github.com/sebastianbergmann/phpunit/issues/5316): `MockBuilder::enableArgumentCloning()` and `MockBuilder::disableArgumentCloning()`
* [#5321](https://github.com/sebastianbergmann/phpunit/issues/5321): `MockBuilder::addMethods()`
* [#5416](https://github.com/sebastianbergmann/phpunit/issues/5416): Support for doubling interfaces (or classes) that have a method named `method`
* [#5424](https://github.com/sebastianbergmann/phpunit/issues/5424): `TestCase` methods for creating return stub configuration objects
* [#5473](https://github.com/sebastianbergmann/phpunit/issues/5473): `assertStringNotMatchesFormat()` and `assertStringNotMatchesFormatFile()`
* [#5536](https://github.com/sebastianbergmann/phpunit/issues/5536): Support for configuring expectations using `expects()` on test stubs
* [#5541](https://github.com/sebastianbergmann/phpunit/issues/5541): Support for metadata in doc-comments
* [#5710](https://github.com/sebastianbergmann/phpunit/issues/5710): Support for using comma-separated values with the `--group`, `--exclude-group`, `--covers`, `--uses`, and `--test-suffix` CLI options
* [#5756](https://github.com/sebastianbergmann/phpunit/issues/5756): Support for the `restrictDeprecations` attribute on the `<source>` element of the XML configuration file
* [#5801](https://github.com/sebastianbergmann/phpunit/issues/5801): Support for targeting traits with `#[CoversClass]` and `#[UsesClass]` attributes
* [#5978](https://github.com/sebastianbergmann/phpunit/issues/5978): Support for PHP 8.2

[12.0.0]: https://github.com/sebastianbergmann/phpunit/compare/11.5...12.0.0
59 changes: 0 additions & 59 deletions DEPRECATIONS.md
Original file line number Diff line number Diff line change
@@ -8,65 +8,6 @@ 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) | `Assert::assertStringNotMatchesFormat()` | 10.4.0 | |
| [#5472](https://github.com/sebastianbergmann/phpunit/issues/5472) | `Assert::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 | |
| [#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 |

### Running Tests

| Issue | Description | Since | Replacement |
|-------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|--------|----------------------------------------------------------------------------------------------------|
| [#5689](https://github.com/sebastianbergmann/phpunit/issues/5689) | `restrictDeprecations` attribute on the `<source>` element of the XML configuration file | 11.1.0 | Use `ignoreSelfDeprecations`, `ignoreDirectDeprecations`, and `ignoreIndirectDeprecations` instead |
| [#5709](https://github.com/sebastianbergmann/phpunit/issues/5709) | Support for using comma-separated values with the `--group`, `--exclude-group`, `--covers`, `--uses`, and `--test-suffix` CLI options | 11.1.0 | Use `--group foo --group bar` instead of `--group foo,bar`, for example |

#### 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 | |
| [#5800](https://github.com/sebastianbergmann/phpunit/issues/5800) | Targeting traits with `#[CoversClass]` and `#[UsesClass]` | 11.2.0 | `#[CoversClass]` and `#[UsesClass]` also target the traits used by the targeted classes |

## Soft Deprecations

This functionality is currently [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation):

### Writing Tests

#### Assertions, Constraints, and Expectations

| 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 |
97 changes: 86 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
# PHPUnit
[![PHPUnit](.github/img/phpunit.svg)](https://phpunit.de/?ref=github)

[![Latest Stable Version](https://poser.pugx.org/phpunit/phpunit/v)](https://packagist.org/packages/phpunit/phpunit)
[![CI Status](https://github.com/sebastianbergmann/phpunit/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/phpunit/actions)
[![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
@@ -18,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 [documentation](https://phpunit.de/documentation.html) 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:
<table>
<tbody>
<tr>
<td style="width: 30%; vertical-align: middle;"><a href="https://www.bubbleshooter.net/"><img alt="Bubble Shooter" src=".github/img/bubble-shooter.png" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://www.in2it.be/phpunit-supporter/"><img alt="in2it vof" src=".github/img/in2it.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://roave.com/"><img alt="Roave" src=".github/img/roave.svg" style="width: 200px;"/></a></td>
</tr>
<tr>
<td style="width: 30%; vertical-align: middle;"><a href="https://route4me.com/"><img alt="Route4Me" src=".github/img/route4me.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://testmo.com/"><img alt="Testmo GmbH" src=".github/img/testmo.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://tideways.com/"><img alt="Tideways GmbH" src=".github/img/tideways.svg" style="width: 200px;"/></a></td>
</tr>
<tr>
<td style="width: 30%; vertical-align: middle;"><a href="https://typo3.com/"><img alt="TYPO3 GmbH" src=".github/img/typo3.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://vema-eg.de/"><img alt="VEMA Versicherungsmakler Genossenschaft eG" src=".github/img/vema.svg" style="width: 200px;"/></a></td>
</tr>
</tbody>
</table>

* [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).
14 changes: 0 additions & 14 deletions build.xml
Original file line number Diff line number Diff line change
@@ -174,20 +174,6 @@
</fileset>
</copy>

<copy file="${basedir}/vendor/sebastian/code-unit/LICENSE" tofile="${basedir}/build/tmp/phar/sebastian-code-unit/LICENSE"/>
<copy todir="${basedir}/build/tmp/phar/sebastian-code-unit">
<fileset dir="${basedir}/vendor/sebastian/code-unit/src">
<include name="**/*.php" />
</fileset>
</copy>

<copy file="${basedir}/vendor/sebastian/code-unit-reverse-lookup/LICENSE" tofile="${basedir}/build/tmp/phar/sebastian-code-unit-reverse-lookup/LICENSE"/>
<copy todir="${basedir}/build/tmp/phar/sebastian-code-unit-reverse-lookup">
<fileset dir="${basedir}/vendor/sebastian/code-unit-reverse-lookup/src">
<include name="**/*.php" />
</fileset>
</copy>

<copy file="${basedir}/vendor/sebastian/comparator/LICENSE" tofile="${basedir}/build/tmp/phar/sebastian-comparator/LICENSE"/>
<copy todir="${basedir}/build/tmp/phar/sebastian-comparator">
<fileset dir="${basedir}/vendor/sebastian/comparator/src">
55 changes: 0 additions & 55 deletions build/scripts/generate-global-assert-wrappers.php
Original file line number Diff line number Diff line change
@@ -200,68 +200,13 @@ 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')) {
/**
* @param array<mixed> $valueMap
*/
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')) {
/**
* @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);
4 changes: 2 additions & 2 deletions build/templates/binary-phar-autoload.php.in
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion build/test-extension/manifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<phar xmlns="https://phar.io/xml/manifest/1.0">
<contains name="phpunit/phpunit-test-extension" version="1.0.0" type="extension">
<extension for="phpunit/phpunit" compatible="^11.0"/>
<extension for="phpunit/phpunit" compatible="^12.0"/>
</contains>

<requires>
40 changes: 17 additions & 23 deletions composer.json
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
},
"prefer-stable": true,
"require": {
"php": ">=8.2",
"php": ">=8.3",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
@@ -32,33 +32,29 @@
"myclabs/deep-copy": "^1.12.1",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"phpunit/php-code-coverage": "^11.0.8",
"phpunit/php-file-iterator": "^5.1.0",
"phpunit/php-invoker": "^5.0.1",
"phpunit/php-text-template": "^4.0.1",
"phpunit/php-timer": "^7.0.1",
"sebastian/cli-parser": "^3.0.2",
"sebastian/code-unit": "^3.0.2",
"sebastian/comparator": "^6.3.0",
"sebastian/diff": "^6.0.2",
"sebastian/environment": "^7.2.0",
"sebastian/exporter": "^6.3.0",
"sebastian/global-state": "^7.0.2",
"sebastian/object-enumerator": "^6.0.1",
"sebastian/type": "^5.1.0",
"sebastian/version": "^5.0.2",
"phpunit/php-code-coverage": "^12.0.0",
"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.0",
"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.0",
"sebastian/version": "^6.0.0",
"staabm/side-effects-detector": "^1.0.5"
},
"config": {
"platform": {
"php": "8.2.0"
"php": "8.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"suggest": {
"ext-soap": "To be able to generate mocks based on WSDL files"
},
"bin": [
"phpunit"
],
@@ -78,7 +74,6 @@
"tests/_files/deprecation-trigger/trigger_deprecation.php",
"tests/unit/Event/AbstractEventTestCase.php",
"tests/unit/Framework/MockObject/TestDoubleTestCase.php",
"tests/unit/Metadata/Parser/AnnotationParserTestCase.php",
"tests/unit/Metadata/Parser/AttributeParserTestCase.php",
"tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php",
"tests/unit/Framework/Assert/assertContainsOnlyBoolTest.php",
@@ -107,15 +102,14 @@
"tests/unit/Framework/Assert/assertNullTest.php",
"tests/unit/Framework/Assert/assertSameSizeTest.php",
"tests/unit/Framework/Assert/assertSameTest.php",
"tests/_files/CoverageNamespacedFunctionTest.php",
"tests/_files/CoveredFunction.php",
"tests/_files/Generator.php",
"tests/_files/NamespaceCoveredFunction.php"
]
},
"extra": {
"branch-alias": {
"dev-main": "11.5-dev"
"dev-main": "12.0-dev"
}
}
}
472 changes: 179 additions & 293 deletions composer.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions phpunit
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions phpunit.xsd
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:annotation>
<xs:documentation source="https://phpunit.de/documentation.html">
This Schema file defines the rules by which the XML configuration file of PHPUnit 11.5 may be structured.
This Schema file defines the rules by which the XML configuration file of PHPUnit 12.0 may be structured.
</xs:documentation>
<xs:appinfo source="https://phpunit.de/documentation.html"/>
</xs:annotation>
@@ -219,7 +219,7 @@
<xs:attribute name="displayDetailsOnTestsThatTriggerErrors" type="xs:boolean" default="false"/>
<xs:attribute name="displayDetailsOnTestsThatTriggerNotices" type="xs:boolean" default="false"/>
<xs:attribute name="displayDetailsOnTestsThatTriggerWarnings" type="xs:boolean" default="false"/>
<xs:attribute name="shortenArraysForExportThreshold" type="xs:integer" default="0"/>
<xs:attribute name="shortenArraysForExportThreshold" type="xs:integer" default="10"/>
</xs:attributeGroup>
<xs:group name="configGroup">
<xs:all>
334 changes: 334 additions & 0 deletions schema/11.5.xsd

Large diffs are not rendered by default.

75 changes: 0 additions & 75 deletions src/Event/Emitter/DispatchingEmitter.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@
use PHPUnit\Event\TestSuite\Started as TestSuiteStarted;
use PHPUnit\Event\TestSuite\TestSuite;
use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\Util\Exporter;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
@@ -555,62 +554,6 @@ public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfac
);
}

/**
* @param trait-string $traitName
*
* @throws InvalidArgumentException
* @throws UnknownEventTypeException
*/
public function testCreatedMockObjectForTrait(string $traitName): void
{
$this->dispatcher->dispatch(
new Test\MockObjectForTraitCreated(
$this->telemetryInfo(),
$traitName,
),
);
}

/**
* @param class-string $className
*
* @throws InvalidArgumentException
* @throws UnknownEventTypeException
*/
public function testCreatedMockObjectForAbstractClass(string $className): void
{
$this->dispatcher->dispatch(
new Test\MockObjectForAbstractClassCreated(
$this->telemetryInfo(),
$className,
),
);
}

/**
* @param class-string $originalClassName
* @param class-string $mockClassName
* @param list<string> $methods
* @param list<mixed> $options
*
* @throws InvalidArgumentException
* @throws UnknownEventTypeException
*/
public function testCreatedMockObjectFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void
{
$this->dispatcher->dispatch(
new Test\MockObjectFromWsdlCreated(
$this->telemetryInfo(),
$wsdlFile,
$originalClassName,
$mockClassName,
$methods,
$callOriginalConstructor,
$options,
),
);
}

/**
* @param class-string $className
*
@@ -628,24 +571,6 @@ public function testCreatedPartialMockObject(string $className, string ...$metho
);
}

/**
* @param class-string $className
* @param list<mixed> $constructorArguments
*
* @throws InvalidArgumentException
* @throws UnknownEventTypeException
*/
public function testCreatedTestProxy(string $className, array $constructorArguments): void
{
$this->dispatcher->dispatch(
new Test\TestProxyCreated(
$this->telemetryInfo(),
$className,
Exporter::export($constructorArguments),
),
);
}

/**
* @param class-string $className
*
27 changes: 0 additions & 27 deletions src/Event/Emitter/Emitter.php
Original file line number Diff line number Diff line change
@@ -127,35 +127,11 @@ public function testCreatedMockObject(string $className): void;
*/
public function testCreatedMockObjectForIntersectionOfInterfaces(array $interfaces): void;

/**
* @param trait-string $traitName
*/
public function testCreatedMockObjectForTrait(string $traitName): void;

/**
* @param class-string $className
*/
public function testCreatedMockObjectForAbstractClass(string $className): void;

/**
* @param class-string $originalClassName
* @param class-string $mockClassName
* @param list<string> $methods
* @param list<mixed> $options
*/
public function testCreatedMockObjectFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void;

/**
* @param class-string $className
*/
public function testCreatedPartialMockObject(string $className, string ...$methodNames): void;

/**
* @param class-string $className
* @param list<mixed> $constructorArguments
*/
public function testCreatedTestProxy(string $className, array $constructorArguments): void;

/**
* @param class-string $className
*/
@@ -179,9 +155,6 @@ 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;

/**

This file was deleted.

59 changes: 0 additions & 59 deletions src/Event/Events/Test/TestDouble/MockObjectForTraitCreated.php

This file was deleted.

This file was deleted.

118 changes: 0 additions & 118 deletions src/Event/Events/Test/TestDouble/MockObjectFromWsdlCreated.php

This file was deleted.

This file was deleted.

66 changes: 0 additions & 66 deletions src/Event/Events/Test/TestDouble/TestProxyCreated.php

This file was deleted.

20 changes: 0 additions & 20 deletions src/Event/Events/Test/TestDouble/TestProxyCreatedSubscriber.php

This file was deleted.

24 changes: 3 additions & 21 deletions src/Event/Facade.php
Original file line number Diff line number Diff line change
@@ -9,13 +9,10 @@
*/
namespace PHPUnit\Event;

use const PHP_VERSION;
use function assert;
use function interface_exists;
use function version_compare;
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
@@ -99,7 +96,7 @@ public function initForIsolation(HRTime $offset): CollectingDispatcher
new Telemetry\System(
new Telemetry\SystemStopWatchWithOffset($offset),
new Telemetry\SystemMemoryMeter,
$this->garbageCollectorStatusProvider(),
new SystemGarbageCollectorStatusProvider,
),
);

@@ -139,7 +136,7 @@ private function createTelemetrySystem(): Telemetry\System
return new Telemetry\System(
new Telemetry\SystemStopWatch,
new Telemetry\SystemMemoryMeter,
$this->garbageCollectorStatusProvider(),
new SystemGarbageCollectorStatusProvider,
);
}

@@ -217,12 +214,8 @@ private function registerDefaultTypes(TypeMap $typeMap): void
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,

@@ -260,15 +253,4 @@ private function registerDefaultTypes(TypeMap $typeMap): void
$typeMap->addMapping($subscriberInterface, $eventClass);
}
}

private function garbageCollectorStatusProvider(): Telemetry\GarbageCollectorStatusProvider
{
if (version_compare(PHP_VERSION, '8.3.0', '>=')) {
return new Php83GarbageCollectorStatusProvider;
}

// @codeCoverageIgnoreStart
return new Php81GarbageCollectorStatusProvider;
// @codeCoverageIgnoreEnd
}
}
93 changes: 10 additions & 83 deletions src/Event/Value/Telemetry/GarbageCollectorStatus.php
Original file line number Diff line number Diff line change
@@ -9,8 +9,6 @@
*/
namespace PHPUnit\Event\Telemetry;

use PHPUnit\Event\RuntimeException;

/**
* @immutable
*
@@ -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;
}

/**
* @phpstan-assert-if-true !null $this->applicationTime
* @phpstan-assert-if-true !null $this->collectorTime
* @phpstan-assert-if-true !null $this->destructorTime
* @phpstan-assert-if-true !null $this->freeTime
* @phpstan-assert-if-true !null $this->running
* @phpstan-assert-if-true !null $this->protected
* @phpstan-assert-if-true !null $this->full
* @phpstan-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;
}
}
48 changes: 0 additions & 48 deletions src/Event/Value/Telemetry/Php83GarbageCollectorStatusProvider.php

This file was deleted.

Original file line number Diff line number Diff line change
@@ -15,10 +15,8 @@
* @no-named-arguments Parameter 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 Php81GarbageCollectorStatusProvider implements GarbageCollectorStatusProvider
final readonly class SystemGarbageCollectorStatusProvider implements GarbageCollectorStatusProvider
{
public function status(): GarbageCollectorStatus
{
@@ -29,14 +27,14 @@ public function status(): GarbageCollectorStatus
$status['collected'],
$status['threshold'],
$status['roots'],
null,
null,
null,
null,
null,
null,
null,
null,
$status['application_time'],
$status['collector_time'],
$status['destructor_time'],
$status['free_time'],
$status['running'],
$status['protected'],
$status['full'],
$status['buffer_size'],
);
}
}
475 changes: 287 additions & 188 deletions src/Framework/Assert.php

Large diffs are not rendered by default.

98 changes: 2 additions & 96 deletions src/Framework/Assert/Functions.php
Original file line number Diff line number Diff line change
@@ -59,13 +59,7 @@
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;

@@ -287,7 +281,7 @@ function assertNotContainsEquals(mixed $needle, iterable $haystack, string $mess
* @throws Exception
* @throws ExpectationFailedException
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/6055
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/6056
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
@@ -563,7 +557,7 @@ function assertContainsOnlyInstancesOf(string $className, iterable $haystack, st
* @throws Exception
* @throws ExpectationFailedException
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/6055
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/6056
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
@@ -2474,24 +2468,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
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @see Assert::assertStringNotMatchesFormat
*/
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.
@@ -2508,24 +2484,6 @@ 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
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/5472
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @see Assert::assertStringNotMatchesFormatFile
*/
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.
@@ -3448,61 +3406,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')) {
/**
* @param array<mixed> $valueMap
*/
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);
}
}
Original file line number Diff line number Diff line change
@@ -7,21 +7,18 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Event\Test;
namespace PHPUnit\Framework\Attributes;

use function sprintf;
use PHPUnit\Event\Event;
use PHPUnit\Event\Telemetry;
use Attribute;

/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
final readonly class MockObjectForAbstractClassCreated implements Event
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final readonly class CoversClassesThatExtendClass
{
private Telemetry\Info $telemetryInfo;

/**
* @var class-string
*/
@@ -30,15 +27,9 @@
/**
* @param class-string $className
*/
public function __construct(Telemetry\Info $telemetryInfo, string $className)
{
$this->telemetryInfo = $telemetryInfo;
$this->className = $className;
}

public function telemetryInfo(): Telemetry\Info
public function __construct(string $className)
{
return $this->telemetryInfo;
$this->className = $className;
}

/**
@@ -48,12 +39,4 @@ public function className(): string
{
return $this->className;
}

public function asString(): string
{
return sprintf(
'Mock Object Created (%s)',
$this->className,
);
}
}
42 changes: 42 additions & 0 deletions src/Framework/Attributes/CoversClassesThatImplementInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
42 changes: 42 additions & 0 deletions src/Framework/Attributes/CoversNamespace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
40 changes: 40 additions & 0 deletions src/Framework/Attributes/RequiresEnvironmentVariable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
42 changes: 42 additions & 0 deletions src/Framework/Attributes/UsesClassesThatExtendClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
42 changes: 42 additions & 0 deletions src/Framework/Attributes/UsesClassesThatImplementInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
42 changes: 42 additions & 0 deletions src/Framework/Attributes/UsesNamespace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
2 changes: 1 addition & 1 deletion src/Framework/Constraint/Operator/LogicalXor.php
Original file line number Diff line number Diff line change
@@ -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),
);
}
25 changes: 14 additions & 11 deletions src/Framework/Constraint/Traversable/TraversableContainsOnly.php
Original file line number Diff line number Diff line change
@@ -9,8 +9,8 @@
*/
namespace PHPUnit\Framework\Constraint;

use PHPUnit\Framework\Exception;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\NativeType;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
@@ -20,20 +20,23 @@ final class TraversableContainsOnly extends Constraint
private readonly Constraint $constraint;
private readonly string $type;

public static function forNativeType(NativeType $type): self
{
return new self(new IsType($type), $type->value);
}

/**
* @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string'|class-string $type
*
* @throws 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;
}

/**
138 changes: 17 additions & 121 deletions src/Framework/Constraint/Type/IsType.php
Original file line number Diff line number Diff line change
@@ -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';

/**
* @var array<string,bool>
*/
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,
];

/**
* @var 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string'
*/
private readonly string $type;
private readonly NativeType $type;

/**
* @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $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:
Original file line number Diff line number Diff line change
@@ -16,14 +16,14 @@
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class UnknownTypeException extends InvalidArgumentException
final class UnknownNativeTypeException extends InvalidArgumentException
{
public function __construct(string $name)
public function __construct(string $type)
{
parent::__construct(
sprintf(
'Type "%s" is not known',
$name,
'Native type "%s" is not known',
$type,
),
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final readonly class MockClass implements MockType
final readonly class DoubledClass
{
private string $classCode;

Original file line number Diff line number Diff line change
@@ -16,7 +16,6 @@
use function is_string;
use function preg_match;
use function preg_replace;
use function sprintf;
use function str_contains;
use function strlen;
use function strpos;
@@ -35,7 +34,7 @@
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MockMethod
final class DoubledMethod
{
use TemplateLoader;

@@ -48,13 +47,11 @@ final class MockMethod
* @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;

@@ -72,7 +69,7 @@ final class MockMethod
* @throws ReflectionException
* @throws RuntimeException
*/
public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self
public static function fromReflection(ReflectionMethod $method): self
{
if ($method->isPrivate()) {
$modifier = 'private';
@@ -104,15 +101,13 @@ public static function fromReflection(ReflectionMethod $method, bool $callOrigin
return new self(
$method->getDeclaringClass()->getName(),
$method->getName(),
$cloneArguments,
$modifier,
self::methodParametersForDeclaration($method),
self::methodParametersForCall($method),
self::methodParametersDefaultValues($method),
count($method->getParameters()),
(new ReflectionMapper)->fromReturnType($method),
$reference,
$callOriginalMethod,
$method->isStatic(),
$deprecation,
);
@@ -122,12 +117,11 @@ public static function fromReflection(ReflectionMethod $method, bool $callOrigin
* @param class-string $className
* @param non-empty-string $methodName
*/
public static function fromName(string $className, string $methodName, bool $cloneArguments): self
public static function fromName(string $className, string $methodName): self
{
return new self(
$className,
$methodName,
$cloneArguments,
'public',
'',
'',
@@ -136,7 +130,6 @@ public static function fromName(string $className, string $methodName, bool $clo
new UnknownType,
'',
false,
false,
null,
);
}
@@ -147,19 +140,17 @@ public static function fromName(string $className, string $methodName, bool $clo
* @param array<int, mixed> $defaultParameterValues
* @param non-negative-int $numberOfParameters
*/
private function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, array $defaultParameterValues, int $numberOfParameters, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation)
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->cloneArguments = $cloneArguments;
$this->modifier = $modifier;
$this->argumentsForDeclaration = $argumentsForDeclaration;
$this->argumentsForCall = $argumentsForCall;
$this->defaultParameterValues = $defaultParameterValues;
$this->numberOfParameters = $numberOfParameters;
$this->returnType = $returnType;
$this->reference = $reference;
$this->callOriginalMethod = $callOriginalMethod;
$this->static = $static;
$this->deprecation = $deprecation;
}
@@ -180,10 +171,7 @@ public function generateCode(): string
if ($this->static) {
$templateFile = 'doubled_static_method.tpl';
} else {
$templateFile = sprintf(
'%s_method.tpl',
$this->callOriginalMethod ? 'proxied' : 'doubled',
);
$templateFile = 'doubled_method.tpl';
}

$deprecation = $this->deprecation;
@@ -231,8 +219,7 @@ public function generateCode(): string
'method_name' => $this->methodName,
'modifier' => $this->modifier,
'reference' => $this->reference,
'clone_arguments' => $this->cloneArguments ? 'true' : 'false',
'deprecation' => $deprecation ?? '',
'deprecation' => $deprecation,
'return_result' => $returnResult,
],
);
Original file line number Diff line number Diff line change
@@ -18,22 +18,22 @@
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MockMethodSet
final class DoubledMethodSet
{
/**
* @var array<string,MockMethod>
* @var array<string,DoubledMethod>
*/
private array $methods = [];

public function addMethods(MockMethod ...$methods): void
public function addMethods(DoubledMethod ...$methods): void
{
foreach ($methods as $method) {
$this->methods[strtolower($method->methodName())] = $method;
}
}

/**
* @return list<MockMethod>
* @return list<DoubledMethod>
*/
public function asArray(): array
{

This file was deleted.

Original file line number Diff line number Diff line change
@@ -10,14 +10,12 @@
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
*/
interface MockType
final class MethodNamedMethodException extends \PHPUnit\Framework\Exception implements Exception
{
/**
* @return class-string
*/
public function generate(): string;
public function __construct()
{
parent::__construct('Doubling interfaces (or classes) that have a method named "method" is not supported.');
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

602 changes: 103 additions & 499 deletions src/Framework/MockObject/Generator/Generator.php

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -21,8 +21,6 @@ final class HookedPropertyGenerator
/**
* @param class-string $className
* @param list<HookedProperty> $properties
*
* @return non-empty-string
*/
public function generate(string $className, array $properties): string
{
@@ -45,7 +43,7 @@ public function generate(string $className, array $properties): string
get {
return $this->__phpunit_getInvocationHandler()->invoke(
new \PHPUnit\Framework\MockObject\Invocation(
'%s', '$%s::get', [], '%s', $this, false
'%s', '$%s::get', [], '%s', $this
)
);
}
@@ -64,7 +62,7 @@ public function generate(string $className, array $properties): string
set (%s $value) {
$this->__phpunit_getInvocationHandler()->invoke(
new \PHPUnit\Framework\MockObject\Invocation(
'%s', '$%s::set', [$value], 'void', $this, false
'%s', '$%s::set', [$value], 'void', $this
)
);
}
50 changes: 0 additions & 50 deletions src/Framework/MockObject/Generator/MockTrait.php

This file was deleted.

Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@

$__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}
}
37 changes: 0 additions & 37 deletions src/Framework/MockObject/Generator/templates/proxied_method.tpl

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
declare(strict_types=1);

{prologue}{class_declaration}
{class_declaration}
{
{use_statements}{property_hooks}{methods}}{epilogue}
{use_statements}{property_hooks}{methods}}
6 changes: 0 additions & 6 deletions src/Framework/MockObject/Generator/templates/trait_class.tpl

This file was deleted.

9 changes: 0 additions & 9 deletions src/Framework/MockObject/Generator/templates/wsdl_class.tpl

This file was deleted.

4 changes: 0 additions & 4 deletions src/Framework/MockObject/Generator/templates/wsdl_method.tpl

This file was deleted.

339 changes: 4 additions & 335 deletions src/Framework/MockObject/MockBuilder.php

Large diffs are not rendered by default.

25 changes: 0 additions & 25 deletions src/Framework/MockObject/Runtime/Api/ErrorCloneMethod.php

This file was deleted.

23 changes: 0 additions & 23 deletions src/Framework/MockObject/Runtime/Api/GeneratedAsMockObject.php

This file was deleted.

23 changes: 0 additions & 23 deletions src/Framework/MockObject/Runtime/Api/GeneratedAsTestStub.php

This file was deleted.

16 changes: 7 additions & 9 deletions src/Framework/MockObject/Runtime/Api/Method.php
Original file line number Diff line number Diff line change
@@ -9,10 +9,10 @@
*/
namespace PHPUnit\Framework\MockObject;

use function call_user_func_array;
use function func_get_args;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\MockObject\Builder\InvocationMocker;
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
@@ -23,13 +23,11 @@ trait Method
{
abstract public function __phpunit_getInvocationHandler(): InvocationHandler;

public function method(): InvocationMocker
public function method(Constraint|PropertyHook|string $constraint): InvocationMocker
{
$expects = $this->__phpunit_getInvocationHandler()->expects(new AnyInvokedCount);

return call_user_func_array(
[$expects, 'method'],
func_get_args(),
);
return $this
->__phpunit_getInvocationHandler()
->expects(new AnyInvokedCount)
->method($constraint);
}
}
27 changes: 0 additions & 27 deletions src/Framework/MockObject/Runtime/Api/MockObjectApi.php
Original file line number Diff line number Diff line change
@@ -9,10 +9,6 @@
*/
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;

@@ -47,29 +43,6 @@ abstract public function __phpunit_unsetInvocationMocker(): void;

public function expects(InvocationOrder $matcher): InvocationMockerBuilder
{
assert($this instanceof StubInternal);

if (!$this->__phpunit_wasGeneratedAsMockObject()) {
$message = 'Expectations configured on test doubles that are created as test stubs are no longer verified since PHPUnit 10. Test doubles that are created as test stubs will no longer have the expects() method in PHPUnit 12. Update your test code to use createMock() instead of createStub(), for example.';

try {
$test = TestMethodBuilder::fromCallStack();

if (!$this->__phpunit_state()->wasDeprecationAlreadyEmittedFor($test->id())) {
EventFacade::emitter()->testTriggeredPhpunitDeprecation(
$test,
$message,
);

$this->__phpunit_state()->deprecationWasEmittedFor($test->id());
}
// @codeCoverageIgnoreStart
} catch (NoTestCaseObjectOnCallStackException) {
EventFacade::emitter()->testRunnerTriggeredDeprecation($message);
// @codeCoverageIgnoreEnd
}
}

return $this->__phpunit_getInvocationHandler()->expects($matcher);
}
}
39 changes: 0 additions & 39 deletions src/Framework/MockObject/Runtime/Api/MutableStubApi.php

This file was deleted.

36 changes: 0 additions & 36 deletions src/Framework/MockObject/Runtime/Api/TestDoubleState.php
Original file line number Diff line number Diff line change
@@ -9,27 +9,19 @@
*/
namespace PHPUnit\Framework\MockObject;

use function assert;

/**
* @no-named-arguments Parameter names are 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 array<non-empty-string, true>
*/
private static array $deprecationEmittedForTest = [];

/**
* @var list<ConfigurableMethod>
*/
private readonly array $configurableMethods;
private readonly bool $generateReturnValues;
private ?InvocationHandler $invocationHandler = null;
private ?object $proxyTarget = null;

/**
* @param list<ConfigurableMethod> $configurableMethods
@@ -68,34 +60,6 @@ public function unsetInvocationHandler(): void
$this->invocationHandler = null;
}

public function setProxyTarget(object $proxyTarget): void
{
$this->proxyTarget = $proxyTarget;
}

public function proxyTarget(): object
{
assert($this->proxyTarget !== null);

return $this->proxyTarget;
}

/**
* @param non-empty-string $testId
*/
public function deprecationWasEmittedFor(string $testId): void
{
self::$deprecationEmittedForTest[$testId] = true;
}

/**
* @param non-empty-string $testId
*/
public function wasDeprecationAlreadyEmittedFor(string $testId): bool
{
return isset(self::$deprecationEmittedForTest[$testId]);
}

/**
* @return list<ConfigurableMethod>
*/
2 changes: 0 additions & 2 deletions src/Framework/MockObject/Runtime/Interface/MockObject.php
Original file line number Diff line number Diff line change
@@ -13,8 +13,6 @@
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
5 changes: 3 additions & 2 deletions src/Framework/MockObject/Runtime/Interface/Stub.php
Original file line number Diff line number Diff line change
@@ -9,13 +9,14 @@
*/
namespace PHPUnit\Framework\MockObject;

use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\MockObject\Builder\InvocationStubber;
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;
}
2 changes: 0 additions & 2 deletions src/Framework/MockObject/Runtime/Interface/StubInternal.php
Original file line number Diff line number Diff line change
@@ -21,6 +21,4 @@ public function __phpunit_state(): TestDoubleState;
public function __phpunit_getInvocationHandler(): InvocationHandler;

public function __phpunit_unsetInvocationMocker(): void;

public function __phpunit_wasGeneratedAsMockObject(): bool;
}
29 changes: 6 additions & 23 deletions src/Framework/MockObject/Runtime/Invocation.php
Original file line number Diff line number Diff line change
@@ -11,13 +11,11 @@

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 PHPUnit\Util\Exporter;

/**
@@ -43,20 +41,19 @@
private array $parameters;
private string $returnType;
private bool $isReturnTypeNullable;
private bool $proxiedCall;
private MockObjectInternal|StubInternal $object;

/**
* @param class-string $className
* @param non-empty-string $methodName
* @param array<mixed> $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';
@@ -70,20 +67,6 @@ 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;
}

/**
@@ -122,7 +105,7 @@ public function generateReturnValue(): mixed
);
}

if ($this->isReturnTypeNullable || $this->proxiedCall) {
if ($this->isReturnTypeNullable) {
return null;
}

2 changes: 1 addition & 1 deletion src/Framework/MockObject/Runtime/ReturnValueGenerator.php
Original file line number Diff line number Diff line change
@@ -218,7 +218,7 @@ private function newInstanceOf(StubInternal $testStub, string $className, string
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(
2 changes: 1 addition & 1 deletion src/Framework/TestBuilder.php
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ public function build(ReflectionClass $theClass, string $methodName, array $grou
/**
* @param non-empty-string $methodName
* @param class-string<TestCase> $className
* @param array<list<mixed>> $data
* @param array<array<mixed>> $data
* @param array{backupGlobals: ?bool, backupGlobalsExcludeList: list<string>, backupStaticProperties: ?bool, backupStaticPropertiesExcludeList: array<string,list<string>>} $backupSettings
* @param list<non-empty-string> $groups
*/
486 changes: 23 additions & 463 deletions src/Framework/TestCase.php

Large diffs are not rendered by default.

25 changes: 16 additions & 9 deletions src/Framework/TestRunner/SeparateProcessTestRunner.php
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
use function file_get_contents;
use function get_include_path;
use function hrtime;
use function is_array;
use function is_file;
use function restore_error_handler;
use function serialize;
@@ -248,18 +249,24 @@ static function (int $errno, string $errstr, string $errfile, int $errline): nev
}

if ($childResult !== false) {
Facade::instance()->forward($childResult['events']);
PassedTests::instance()->import($childResult['passedTests']);
if (!is_array($childResult)) {
$childResult = [$childResult];
}

assert($test instanceof TestCase);
foreach ($childResult as $result) {
Facade::instance()->forward($result->events);
PassedTests::instance()->import($result->passedTests);

$test->setResult($childResult['testResult']);
$test->addToAssertionCount($childResult['numAssertions']);
assert($test instanceof TestCase);

if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) {
CodeCoverage::instance()->codeCoverage()->merge(
$childResult['codeCoverage'],
);
$test->setResult($result->testResult);
$test->addToAssertionCount($result->numAssertions);

if (CodeCoverage::instance()->isActive() && $result->codeCoverage instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) {
CodeCoverage::instance()->codeCoverage()->merge(
$result->codeCoverage,
);
}
}
}
}
53 changes: 28 additions & 25 deletions src/Framework/TestRunner/TestRunner.php
Original file line number Diff line number Diff line change
@@ -135,36 +135,31 @@ public function run(TestCase $test): void
}

if ($collectCodeCoverage) {
$append = !$risky && !$incomplete && !$skipped;
$linesToBeCovered = [];
$linesToBeUsed = [];
$append = !$risky && !$incomplete && !$skipped;
$covers = null;
$uses = null;

if (!$append) {
$covers = false;
}

if ($append) {
try {
$linesToBeCovered = $codeCoverageMetadataApi->linesToBeCovered(
$test::class,
$test->name(),
);

$linesToBeUsed = $codeCoverageMetadataApi->linesToBeUsed(
$test::class,
$test->name(),
);
} catch (InvalidCoversTargetException $cce) {
Facade::emitter()->testTriggeredPhpunitWarning(
$test->valueObjectForEvents(),
$cce->getMessage(),
);

$append = false;
}
$covers = $codeCoverageMetadataApi->coversTargets(
$test::class,
$test->name(),
);

$uses = $codeCoverageMetadataApi->usesTargets(
$test::class,
$test->name(),
);
}

try {
CodeCoverage::instance()->stop(
$append,
$linesToBeCovered,
$linesToBeUsed,
$covers,
$uses,
);
} catch (UnintentionallyCoveredCodeException $cce) {
Facade::emitter()->testConsideredRisky(
@@ -235,15 +230,23 @@ public function run(TestCase $test): void
private function hasCoverageMetadata(string $className, string $methodName): bool
{
foreach (MetadataRegistry::parser()->forClassAndMethod($className, $methodName) as $metadata) {
if ($metadata->isCovers()) {
if ($metadata->isCoversNamespace()) {
return true;
}

if ($metadata->isCoversTrait()) {
return true;
}

if ($metadata->isCoversClass()) {
return true;
}

if ($metadata->isCoversTrait()) {
if ($metadata->isCoversClassesThatExtendClass()) {
return true;
}

if ($metadata->isCoversClassesThatImplementInterface()) {
return true;
}

2 changes: 1 addition & 1 deletion src/Framework/TestRunner/templates/class.tpl
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ function __phpunit_run_isolated_test()
file_put_contents(
'{processResultFile}',
serialize(
[
(object)[
'testResult' => $test->result(),
'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null,
'numAssertions' => $test->numberOfAssertionsPerformed(),
2 changes: 1 addition & 1 deletion src/Framework/TestRunner/templates/method.tpl
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ function __phpunit_run_isolated_test()
file_put_contents(
'{processResultFile}',
serialize(
[
(object)[
'testResult' => $test->result(),
'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null,
'numAssertions' => $test->numberOfAssertionsPerformed(),
2 changes: 1 addition & 1 deletion src/Logging/EventLogger.php
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ public function trace(Event $event): void
{
$telemetryInfo = $this->telemetryInfo($event);
$indentation = PHP_EOL . str_repeat(' ', strlen($telemetryInfo));
$lines = preg_split('/\r\n|\r|\n/', $event->asString());
$lines = preg_split('/\r\n|\r|\n/', $event->asString()) ?: [];

$flags = FILE_APPEND;

4 changes: 2 additions & 2 deletions src/Logging/JUnit/JunitXmlLogger.php
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ public function __construct(Printer $printer, Facade $facade)

public function flush(): void
{
$this->printer->print($this->document->saveXML());
$this->printer->print($this->document->saveXML() ?: '');

$this->printer->flush();
}
@@ -285,7 +285,7 @@ private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerfor
);

$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
$this->testSuiteTimes[$this->testSuiteLevel] += (int) $time;

$this->currentTestCase = null;
$this->time = null;
23 changes: 4 additions & 19 deletions src/Logging/TestDox/HtmlRenderer.php
Original file line number Diff line number Diff line change
@@ -18,10 +18,7 @@
*/
final readonly class HtmlRenderer
{
/**
* @var string
*/
private const PAGE_HEADER = <<<'EOT'
private const string PAGE_HEADER = <<<'EOT'
<!doctype html>
<html lang="en">
<head>
@@ -76,28 +73,16 @@
</head>
<body>
EOT;

/**
* @var string
*/
private const CLASS_HEADER = <<<'EOT'
private const string CLASS_HEADER = <<<'EOT'
<h2>%s</h2>
<ul>

EOT;

/**
* @var string
*/
private const CLASS_FOOTER = <<<'EOT'
private const string CLASS_FOOTER = <<<'EOT'
</ul>
EOT;

/**
* @var string
*/
private const PAGE_FOOTER = <<<'EOT'
private const string PAGE_FOOTER = <<<'EOT'
</body>
</html>
2 changes: 1 addition & 1 deletion src/Metadata/After.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
private int $priority;

/**
* @param 0|1 $level
* @param int<0, 1> $level
*/
protected function __construct(int $level, int $priority)
{
2 changes: 1 addition & 1 deletion src/Metadata/AfterClass.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
private int $priority;

/**
* @param 0|1 $level
* @param int<0, 1> $level
*/
protected function __construct(int $level, int $priority)
{
310 changes: 81 additions & 229 deletions src/Metadata/Api/CodeCoverage.php
Original file line number Diff line number Diff line change
@@ -10,32 +10,23 @@
namespace PHPUnit\Metadata\Api;

use function assert;
use function class_exists;
use function count;
use function interface_exists;
use function sprintf;
use function str_starts_with;
use function trait_exists;
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 PHPUnit\Metadata\UsesMethod;
use PHPUnit\Metadata\UsesNamespace;
use PHPUnit\Metadata\UsesTrait;
use ReflectionClass;
use SebastianBergmann\CodeUnit\CodeUnitCollection;
use SebastianBergmann\CodeUnit\Exception as CodeUnitException;
use SebastianBergmann\CodeUnit\InvalidCodeUnitException;
use SebastianBergmann\CodeUnit\Mapper;
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
@@ -44,162 +35,114 @@
*/
final class CodeCoverage
{
/**
* @var array<class-string, non-empty-list<class-string>>
*/
private array $withParents = [];

/**
* @param class-string $className
* @param non-empty-string $methodName
*
* @throws CodeCoverageException
*
* @return array<string,list<int>>|false
*/
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 = [];

$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,
),
);
foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) {
if ($metadata->isCoversNamespace()) {
assert($metadata instanceof CoversNamespace);

$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->isCoversTrait() && !$metadata->isCoversMethod() && !$metadata->isCoversFunction() && !$metadata->isCovers()) {
continue;
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());
}

/** @phpstan-ignore booleanOr.alwaysTrue */
assert($metadata instanceof CoversClass || $metadata instanceof CoversTrait || $metadata instanceof CoversMethod || $metadata instanceof CoversFunction || $metadata instanceof Covers);

if ($metadata->isCoversClass() || $metadata->isCoversTrait() || $metadata->isCoversMethod() || $metadata->isCoversFunction()) {
$codeUnits = $codeUnits->mergeWith($this->mapToCodeUnits($metadata));
} 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->isCoversTrait()) {
assert($metadata instanceof CoversTrait);

$targets[] = Target::forTrait($metadata->traitName());
}
}

return $mapper->codeUnitsToSourceLines($codeUnits);
return TargetCollection::fromArray($targets);
}

/**
* @param class-string $className
* @param non-empty-string $methodName
*
* @throws CodeCoverageException
*
* @return array<string,list<int>>
*/
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->isUsesTrait() && !$metadata->isUsesMethod() && !$metadata->isUsesFunction() && !$metadata->isUses()) {
continue;
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());
}

/** @phpstan-ignore booleanOr.alwaysTrue */
assert($metadata instanceof UsesClass || $metadata instanceof UsesTrait || $metadata instanceof UsesMethod || $metadata instanceof UsesFunction || $metadata instanceof Uses);

if ($metadata->isUsesClass() || $metadata->isUsesTrait() || $metadata->isUsesMethod() || $metadata->isUsesFunction()) {
$codeUnits = $codeUnits->mergeWith($this->mapToCodeUnits($metadata));
} 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->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);
}

/**
@@ -215,101 +158,10 @@ 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;
}

return true;
}

/**
* @throws InvalidCoversTargetException
*/
private function mapToCodeUnits(CoversClass|CoversFunction|CoversMethod|CoversTrait|UsesClass|UsesFunction|UsesMethod|UsesTrait $metadata): CodeUnitCollection
{
$mapper = new Mapper;
$names = $this->names($metadata);

try {
if (count($names) === 1) {
return $mapper->stringToCodeUnits($names[0]);
}

$codeUnits = CodeUnitCollection::fromList();

foreach ($names as $name) {
$codeUnits = $codeUnits->mergeWith(
$mapper->stringToCodeUnits($name),
);
}

return $codeUnits;
} catch (CodeUnitException $e) {
throw new InvalidCoversTargetException(
sprintf(
'%s is not a valid target for code coverage',
$metadata->asStringForCodeUnitMapper(),
),
$e->getCode(),
$e,
);
}
}

/**
* @throws InvalidCoversTargetException
*
* @return non-empty-list<non-empty-string>
*/
private function names(CoversClass|CoversFunction|CoversMethod|CoversTrait|UsesClass|UsesFunction|UsesMethod|UsesTrait $metadata): array
{
$name = $metadata->asStringForCodeUnitMapper();
$names = [$name];

if ($metadata->isCoversClass() || $metadata->isUsesClass()) {
if (isset($this->withParents[$name])) {
return $this->withParents[$name];
}

if (interface_exists($name)) {
throw new InvalidCoversTargetException(
sprintf(
'Interface "%s" is not a valid target for code coverage',
$name,
),
);
}

if (!(class_exists($name) || trait_exists($name))) {
throw new InvalidCoversTargetException(
sprintf(
'"%s" is not a valid target for code coverage',
$name,
),
);
}

assert(class_exists($names[0]) || trait_exists($names[0]));

$reflector = new ReflectionClass($name);

while ($reflector = $reflector->getParentClass()) {
if (!$reflector->isUserDefined()) {
break;
}

$names[] = $reflector->getName();
}

$this->withParents[$name] = $names;
}

return $names;
}
}
73 changes: 3 additions & 70 deletions src/Metadata/Api/DataProvider.php
Original file line number Diff line number Diff line change
@@ -9,34 +9,20 @@
*/
namespace PHPUnit\Metadata\Api;

use const JSON_ERROR_NONE;
use const PREG_OFFSET_CAPTURE;
use function array_key_exists;
use function assert;
use function explode;
use function get_debug_type;
use function is_array;
use function is_int;
use function is_string;
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 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;
use PHPUnit\Metadata\MetadataCollection;
use PHPUnit\Metadata\Parser\Registry as MetadataRegistry;
use PHPUnit\Metadata\TestWith;
use ReflectionClass;
use ReflectionMethod;
use Throwable;

/**
@@ -60,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()) {
@@ -186,12 +172,14 @@ private function dataProvidedByMethods(string $className, string $methodName, Me

$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
}
}
}
@@ -234,59 +222,4 @@ private function dataProvidedByMetadata(MetadataCollection $testWith): array

return $result;
}

/**
* @param class-string $className
*
* @throws InvalidDataProviderException
*
* @return ?array<array<mixed>>
*/
private function dataProvidedByTestWithAnnotation(string $className, string $methodName): ?array
{
$docComment = (new ReflectionMethod($className, $methodName))->getDocComment();

if ($docComment === false) {
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;
}

$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 === '' || $candidateRow[0] !== '[') {
break;
}

$dataSet = json_decode($candidateRow, true);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidDataProviderException(
'The data set for the @testWith annotation cannot be parsed: ' . json_last_error_msg(),
);
}

$data[] = $dataSet;
}

if (!$data) {
throw new InvalidDataProviderException(
'The data set for the @testWith annotation cannot be parsed.',
);
}

return $data;
}
}
35 changes: 15 additions & 20 deletions src/Metadata/Api/Groups.php
Original file line number Diff line number Diff line change
@@ -13,17 +13,14 @@
use function array_key_exists;
use function array_unique;
use function assert;
use function ltrim;
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\RequiresPhpExtension;
use PHPUnit\Metadata\Uses;
use PHPUnit\Metadata\UsesClass;
use PHPUnit\Metadata\UsesFunction;

@@ -66,36 +63,36 @@ public function groups(string $className, string $methodName, bool $includeVirtu
}

foreach (Registry::parser()->forClassAndMethod($className, $methodName) as $metadata) {
if ($metadata->isCoversClass() || $metadata->isCoversFunction()) {
/** @phpstan-ignore booleanOr.alwaysTrue */
assert($metadata instanceof CoversClass || $metadata instanceof CoversFunction);
if ($metadata->isCoversClass()) {
assert($metadata instanceof CoversClass);

$groups[] = '__phpunit_covers_' . $this->canonicalizeName(ltrim($metadata->asStringForCodeUnitMapper(), ':'));
$groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->className());

continue;
}

if ($metadata->isCovers()) {
assert($metadata instanceof Covers);
if ($metadata->isCoversFunction()) {
assert($metadata instanceof CoversFunction);

$groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->target());
$groups[] = '__phpunit_covers_' . $this->canonicalizeName($metadata->functionName());

continue;
}

if ($metadata->isUsesClass() || $metadata->isUsesFunction()) {
/** @phpstan-ignore booleanOr.alwaysTrue */
assert($metadata instanceof UsesClass || $metadata instanceof UsesFunction);
if ($metadata->isUsesClass()) {
assert($metadata instanceof UsesClass);

$groups[] = '__phpunit_uses_' . $this->canonicalizeName(ltrim($metadata->asStringForCodeUnitMapper(), ':'));
$groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->className());

continue;
}

if ($metadata->isUses()) {
assert($metadata instanceof Uses);
if ($metadata->isUsesFunction()) {
assert($metadata instanceof UsesFunction);

$groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->target());
$groups[] = '__phpunit_uses_' . $this->canonicalizeName($metadata->functionName());

continue;
}

if ($metadata->isRequiresPhpExtension()) {
@@ -105,9 +102,7 @@ public function groups(string $className, string $methodName, bool $includeVirtu
}
}

self::$groupCache[$key] = array_unique($groups);

return self::$groupCache[$key];
return self::$groupCache[$key] = array_unique($groups);
}

/**
23 changes: 22 additions & 1 deletion src/Metadata/Api/Requirements.php
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
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;
@@ -24,6 +25,7 @@
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;
@@ -70,7 +72,7 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam

if (!extension_loaded($metadata->extension()) ||
($metadata->hasVersionRequirement() &&
!$metadata->versionRequirement()->isSatisfiedBy(phpversion($metadata->extension())))) {
!$metadata->versionRequirement()->isSatisfiedBy(phpversion($metadata->extension()) ?: ''))) {
$notSatisfied[] = sprintf(
'PHP extension %s%s is required.',
$metadata->extension(),
@@ -105,6 +107,25 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam
}
}

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);

2 changes: 1 addition & 1 deletion src/Metadata/BackupGlobals.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
private bool $enabled;

/**
* @param 0|1 $level
* @param int<0, 1> $level
*/
protected function __construct(int $level, bool $enabled)
{
2 changes: 1 addition & 1 deletion src/Metadata/BackupStaticProperties.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
private bool $enabled;

/**
* @param 0|1 $level
* @param int<0, 1> $level
*/
protected function __construct(int $level, bool $enabled)
{
2 changes: 1 addition & 1 deletion src/Metadata/Before.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
private int $priority;

/**
* @param 0|1 $level
* @param int<0, 1> $level
*/
protected function __construct(int $level, int $priority)
{
2 changes: 1 addition & 1 deletion src/Metadata/BeforeClass.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
private int $priority;

/**
* @param 0|1 $level
* @param int<0, 1> $level
*/
protected function __construct(int $level, int $priority)
{
12 changes: 1 addition & 11 deletions src/Metadata/CoversClass.php
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
private string $className;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
*/
protected function __construct(int $level, string $className)
@@ -44,14 +44,4 @@ public function className(): string
{
return $this->className;
}

/**
* @return class-string
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function asStringForCodeUnitMapper(): string
{
return $this->className;
}
}
Original file line number Diff line number Diff line change
@@ -14,15 +14,15 @@
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
final readonly class UsesDefaultClass extends Metadata
final readonly class CoversClassesThatExtendClass extends Metadata
{
/**
* @var class-string
*/
private string $className;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
*/
protected function __construct(int $level, string $className)
@@ -32,7 +32,7 @@ protected function __construct(int $level, string $className)
$this->className = $className;
}

public function isUsesDefaultClass(): true
public function isCoversClassesThatExtendClass(): true
{
return true;
}
47 changes: 47 additions & 0 deletions src/Metadata/CoversClassesThatImplementInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
10 changes: 1 addition & 9 deletions src/Metadata/CoversFunction.php
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
private string $functionName;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param non-empty-string $functionName
*/
protected function __construct(int $level, string $functionName)
@@ -44,12 +44,4 @@ 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;
}
}
12 changes: 1 addition & 11 deletions src/Metadata/CoversMethod.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@
private string $methodName;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
* @param non-empty-string $methodName
*/
@@ -59,14 +59,4 @@ public function methodName(): string
{
return $this->methodName;
}

/**
* @return non-empty-string
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function asStringForCodeUnitMapper(): string
{
return $this->className . '::' . $this->methodName;
}
}
20 changes: 10 additions & 10 deletions src/Metadata/Covers.php → src/Metadata/CoversNamespace.php
Original file line number Diff line number Diff line change
@@ -14,34 +14,34 @@
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
final readonly class Covers extends Metadata
final readonly class CoversNamespace extends Metadata
{
/**
* @var non-empty-string
*/
private string $target;
private string $namespace;

/**
* @param 0|1 $level
* @param non-empty-string $target
* @param int<0, 1> $level
* @param non-empty-string $namespace
*/
protected function __construct(int $level, string $target)
protected function __construct(int $level, string $namespace)
{
parent::__construct($level);

$this->target = $target;
$this->namespace = $namespace;
}

public function isCovers(): true
public function isCoversNamespace(): true
{
return true;
}

/**
* @return non-empty-string
* @return class-string
*/
public function target(): string
public function namespace(): string
{
return $this->target;
return $this->namespace;
}
}
10 changes: 0 additions & 10 deletions src/Metadata/CoversTrait.php
Original file line number Diff line number Diff line change
@@ -44,14 +44,4 @@ public function traitName(): string
{
return $this->traitName;
}

/**
* @return trait-string
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function asStringForCodeUnitMapper(): string
{
return $this->traitName;
}
}
2 changes: 1 addition & 1 deletion src/Metadata/DataProvider.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@
private string $methodName;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
* @param non-empty-string $methodName
*/
2 changes: 1 addition & 1 deletion src/Metadata/DependsOnClass.php
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
private bool $shallowClone;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
*/
protected function __construct(int $level, string $className, bool $deepClone, bool $shallowClone)
2 changes: 1 addition & 1 deletion src/Metadata/DependsOnMethod.php
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@
private bool $shallowClone;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
* @param non-empty-string $methodName
*/

This file was deleted.

22 changes: 0 additions & 22 deletions src/Metadata/Exception/ReflectionException.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Metadata/ExcludeGlobalVariableFromBackup.php
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
private string $globalVariableName;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param non-empty-string $globalVariableName
*/
protected function __construct(int $level, string $globalVariableName)
2 changes: 1 addition & 1 deletion src/Metadata/ExcludeStaticPropertyFromBackup.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@
private string $propertyName;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param class-string $className
* @param non-empty-string $propertyName
*/
2 changes: 1 addition & 1 deletion src/Metadata/Group.php
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
private string $groupName;

/**
* @param 0|1 $level
* @param int<0, 1> $level
* @param non-empty-string $groupName
*/
protected function __construct(int $level, string $groupName)
Loading