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: symfony/phpunit-bridge
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v5.1.8
Choose a base ref
...
head repository: symfony/phpunit-bridge
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7.2
Choose a head ref

Commits on May 16, 2020

  1. updated version to 5.2

    fabpot committed May 16, 2020
    Copy the full SHA
    52b17bc View commit details

Commits on May 19, 2020

  1. Merge branch '5.1'

    * 5.1:
      [PhpUnitBridge] fix bad detection of unsilenced deprecations
      [Security] Unserialize $parentData, if needed, to avoid errors
      [HttpKernel] Fix error logger when stderr is redirected to /dev/null (FPM)
    nicolas-grekas committed May 19, 2020
    Copy the full SHA
    0963625 View commit details

Commits on May 20, 2020

  1. Merge branch '5.1'

    * 5.1:
      [PhpUnitBridge] fix leftover
      [PhpUnitBridge] fix installing under PHP >= 8
      Use ">=" for the "php" requirement
      bump icu 67.1
      [DI] Remove preload primitive types
      [Validator] Add missing translations of nn locale
      [HttpKernel] Fix that the `Store` would not save responses with the X-Content-Digest header present
      [Intl] bump icu 67.1
      [Validator] allow passing a validator to Validation::createCallable()
    nicolas-grekas committed May 20, 2020
    Copy the full SHA
    1236ec8 View commit details
  2. Copy the full SHA
    57c5276 View commit details
  3. Merge branch '5.1'

    * 5.1:
      [PhpUnitBridge] fix installing on PHP 8 (ter)
      [PhpUnitBridge] fix installing on PHP 8 (bis)
      [PhpUnitBridge] fix installing on PHP 8
    nicolas-grekas committed May 20, 2020
    Copy the full SHA
    45210b5 View commit details
  4. Merge branch '5.1'

    * 5.1:
      fix merge
    nicolas-grekas committed May 20, 2020
    Copy the full SHA
    ede4a67 View commit details

Commits on May 23, 2020

  1. Merge branch '5.1'

    * 5.1: (33 commits)
      [Cache] $lifetime cannot be null
      [Serializer] minor cleanup
      fix merge
      Run PHP 8 as 7.4.99
      Remove calls to deprecated ReflectionParameter::getClass().
      [VarDumper] fix PHP 8 support
      Removed "services" prototype node from "custom_authenticator"
      Add php 8 to travis.
      [Cache] Accessing undefined constants raises an Error in php8
      [Cache] allow DBAL v3
      Skip Doctrine DBAL on php 8 until we have a compatible version.
      [DomCrawler] Catch expected ValueError.
      Made method signatures compatible with their corresponding traits.
      [ErrorHandler] Apply php8 fixes from Debug component.
      [DomCrawler] Catch expected ValueError.
      [Validator] Catch expected ValueError.
      [VarDumper] ReflectionFunction::isDisabled() is deprecated.
      [BrowserKit] Raw body with custom Content-Type header
      Revert symfony/symfony#34986
      Make ExpressionLanguageSyntax validator usable with annotation
      ...
    nicolas-grekas committed May 23, 2020
    Copy the full SHA
    671b8ad View commit details

Commits on Jun 7, 2020

  1. Merge branch '5.1'

    * 5.1: (36 commits)
      Fixed left-over debug statement
      set column length for mysql 5.6 compatibility
      [Mime] Remove unused var
      [HttpClient] fix monitoring timeouts when other streams are active
      [PhpUnitBridge] fix syntax on PHP 5.3
      [PhpUnitBridge] Fix undefined index when output of "composer show" cannot be parsed
      properly cascade validation to child forms
      [PropertyAccess] Fix getter call order BC
      [PhpUnitBridge] fix undefined var on version 3.4
      Fix invalid char in SQS Headers
      Move ajax clear event listener initialization on loadToolbar
      [HttpClient] Throw JsonException instead of TransportException on empty response in Response::toArray()
      Fix CS
      FrameworkBundle Serializer issue
      register event listeners depending on the installed packages
      take into account the context when preserving empty array objects
      Only register CSRF protection listener if CSRF is available
      [VarExporter] tfix: s/markAsSkipped/markTestSkipped/
      Also check PUBLIC_ACCESS for authenticated tokens
      Fix enabled_locales behavior
      ...
    nicolas-grekas committed Jun 7, 2020
    Copy the full SHA
    33bd9ae View commit details

Commits on Jun 11, 2020

  1. Merge branch '5.1'

    Tobion committed Jun 11, 2020
    Copy the full SHA
    32c0d13 View commit details

Commits on Jun 18, 2020

  1. Merge branch '5.1'

    * 5.1: (28 commits)
      [DI] fix
      Use "composer/package-versions-deprecated" when possible
      Fix
      Small update in our internal terminology
      Fix support for PHP8 union types
      [VarDumper] fix typo
      [Lock][Messenger] Fix precedence of DSN options for 5.1
      Fix support for PHP8 union types
      [FrameworkBundle] preserve dots in query-string when redirecting
      [3.4] Fix support for PHP8 union types
      [PhpUnitBridge] Streamline ansi/no-ansi of composer according to phpunit --colors option
      [3.4] Small update in our internal terminology
      [Cache] fix compat with DBAL v3
      Remove unnecessary null check
      [HttpFoundation] Allow `null` in InputBag@set
      [HttpClient] Convert CurlHttpClient::handlePush() to instance method
      Fix package rename when releasing
      bumped Symfony version to 5.1.3
      updated VERSION for 5.1.2
      updated CHANGELOG for 5.1.2
      ...
    nicolas-grekas committed Jun 18, 2020
    Copy the full SHA
    f3dc50d View commit details

Commits on Jun 28, 2020

  1. Merge branch '5.1'

    * 5.1:
      Fix test that fails on old distros
      Fix: compatibility with phpunit 9.3
      [DoctrineBridge] work around Connection::ping() deprecation
      [MimeType] Duplicated MimeType due to PHP Bug
      [HttpClient] fix casting TraceableResponse to php streams
      [DI] fix parsing of argument type=binary in xml
      fix guessing form types for DateTime types
      fix handling typed properties as constraint options
      Fix the 'supports' method argument type of the security voter
      Use the driverConnection executeUpdate method
    nicolas-grekas committed Jun 28, 2020
    Copy the full SHA
    7969846 View commit details

Commits on Jul 8, 2020

  1. Merge branch '5.1'

    * 5.1:
      [String] Added a help message when translation-contracts is not installed
      Add: ExcludeList usage for PHPUnit 9.4
      [Cache] Fix compat wth DBAL v3
      [String] throw when Alpine is used and translit fails
    nicolas-grekas committed Jul 8, 2020
    Copy the full SHA
    e87e70d View commit details

Commits on Jul 9, 2020

  1. Merge branch '5.1'

    * 5.1:
      Expect deprecations in isolation
    xabbuh committed Jul 9, 2020
    Copy the full SHA
    a27e61b View commit details
  2. Merge branch '5.1'

    * 5.1:
      consider traits imported in parent classes
    xabbuh committed Jul 9, 2020
    Copy the full SHA
    d7c549e View commit details

Commits on Jul 15, 2020

  1. Copy the full SHA
    2765c3f View commit details

Commits on Jul 23, 2020

  1. Merge branch '5.1'

    * 5.1:
      fix merge
      Require PHPUnit 9.3 on PHP 8
      [Cache] fix catching auth errors
      Fix CS
      [FrameworkBundle] set default session.handler alias if handler_id is not provided
      Fix CS
      Readability update
      Removed @internal from Composite
      Fix checks for phpunit releases on Composer 2 (resolves #37601)
      [Messenger] fix ignore account & endpoint options amazon sqs connection
      [Serializer] Support multiple levels of discriminator mapping
      Use hexadecimal numerals instead of hexadecimals in strings to represent error codes.
      [SCA] Minor fixes on tests
      [WebProfilerBundle] modified url generation to use absolute urls
      [Mailer] Fix reply-to functionality in the SendgridApiTransport
      [Mime] Fix compat with HTTP requests
      ticket_36879 - Fix mandrill raw http request setting from email/name
    nicolas-grekas committed Jul 23, 2020
    Copy the full SHA
    abb2cb7 View commit details
  2. Merge branch '5.1'

    * 5.1:
      [PhpUnitBridge] Fix tests on Windows
    nicolas-grekas committed Jul 23, 2020
    Copy the full SHA
    942013b View commit details

Commits on Aug 8, 2020

  1. Copy the full SHA
    d95e5d3 View commit details

Commits on Aug 23, 2020

  1. Copy the full SHA
    5a7a20f View commit details
  2. feature #37708 Allow Drupal to wrap the Symfony test listener (alexpott)

    This PR was squashed before being merged into the 5.2-dev branch.
    
    Discussion
    ----------
    
    Allow Drupal to wrap the Symfony test listener
    
    | Q             | A
    | ------------- | ---
    | Branch?       | 5.1
    | Bug fix?      | kinda
    | New feature?  | no <!-- please update src/**/CHANGELOG.md files -->
    | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
    | Tickets       | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
    | License       | MIT
    | Doc PR        | symfony/symfony-docs#... <!-- required for new features -->
    
    Drupal has a test listener that wraps the Symfony listener as we need to manipulate deprecation testing to skip specific deprecations. We've managed to remove some how custom stuff and reaching into Symfony's privates via reflection recently (and fix a bug or two) but now we're getting output like:
    ```
      2x: a:4:{s:11:"deprecation";s:191:"Calling Drupal\Tests\WebAssert::responseNotMatches with more than one argument is deprecated in drupal:9.1.0 and will throw an exception in drupal:10.0.0. See https://www.drupal.org/node/TODO";s:5:"class";s:52:"Drupal\Tests\comment\Functional\CommentAnonymousTest";s:6:"method";s:13:"testAnonymous";s:15:"triggering_file";s:74:"/Users/alex/dev/sites/drupal8alt.dev/core/tests/Drupal/Tests/WebAssert.php";}
        2x in DrupalListener::endTest from Drupal\Tests\Listeners
    ```
    instead of
    ```
      2x: Calling Drupal\Tests\WebAssert::responseNotMatches with more than one argument is deprecated in drupal:9.1.0 and will throw an exception in drupal:10.0.0. See https://www.drupal.org/node/TODO
        2x in CommentAnonymousTest::testAnonymous from Drupal\Tests\comment\Functional
    ```
    
    We can fix this by aliasing and copying the \Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation class but ideally that class could be a bit more liberal in its implementation.
    
    Commits
    -------
    
    91de46da3f Allow Drupal to wrap the Symfony test listener
    fabpot committed Aug 23, 2020
    Copy the full SHA
    80d662f View commit details

Commits on Aug 28, 2020

  1. Merge branch '5.1'

    * 5.1:
      [PhpUnitBridge] Create a predictable symlink pointing to the local install
      [PropertyInfo] Backport support for typed properties (PHP 7.4)
      [PhpUnitBridge] Polyfill new phpunit 9.1 assertions
      [PhpUnitBridge] Move assertMatchesRegularExpression in PolyfillAssertTrait
      [PhpUnit] Add polyfill for assertMatchesRegularExpression()
      Update Notifier bridge readme
      [TwigBridge] Fix #37931: BC break where filter method `trans` did not allow null values for `$message` parameter anymore
      [PropertyAccess] Fix accessing dynamic properties
    fabpot committed Aug 28, 2020
    Copy the full SHA
    69c3fdb View commit details

Commits on Sep 1, 2020

  1. Merge branch '5.1' into master

    * 5.1:
      Missed AbstractArgument
      swallow deprecations
    xabbuh committed Sep 1, 2020
    Copy the full SHA
    07ca5b4 View commit details
  2. [PHPUnitBridge] deprecations not enabled anymore when disabled=0

    Allow to pass 0 or 1 to "disabled" to be consistent with "verbose" key behavior
    l-vo committed Sep 1, 2020
    Copy the full SHA
    1e2cf16 View commit details
  3. feature #38017 [PHPUnitBridge] deprecations not disabled anymore when…

    … disabled=0 (l-vo)
    
    This PR was merged into the 5.2-dev branch.
    
    Discussion
    ----------
    
    [PHPUnitBridge] deprecations not disabled anymore when disabled=0
    
    | Q             | A
    | ------------- | ---
    | Branch?       | master
    | Bug fix?      | no
    | New feature?  | yes
    | Deprecations? |
    | Tickets       |
    | License       | MIT
    | Doc PR        |
    
    According to the [docs](https://symfony.com/doc/current/components/phpunit_bridge.html#disabling-the-deprecation-helper), `disabled=1` turns off deprecations mode on phpunit-bridge. It's not totally true since deprecations are disabled as soon as `disabled` key is present in `SYMFONY_DEPRECATIONS_HELPER`. So if `disabled=0` deprecations are still disabled.
    
    Instead of updating the doc, this PR suggest to make `disabled` behavior consistent with `verbose` behavior, so:
    - `disabled` => deprecations disabled
    - `disabled=0` => deprecations enabled
    - `disabled=1` => deprecations disabled
    
    Commits
    -------
    
    6908e3d156 [PHPUnitBridge] deprecations not enabled anymore when disabled=0
    fabpot committed Sep 1, 2020
    Copy the full SHA
    6c4454b View commit details

Commits on Sep 2, 2020

  1. Copy the full SHA
    3d5c0f5 View commit details
  2. Merge branch '5.1'

    * 5.1:
      [PHPUnitBridge] Fix deprecation type detection when trigger_deprecation is used
    nicolas-grekas committed Sep 2, 2020
    Copy the full SHA
    2515d98 View commit details
  3. Merge branch '5.1'

    * 5.1:
      Enable "native_constant_invocation" CS rule
      Make AbstractPhpFileCacheWarmer public
      Fix CS
      Add a warning comment on ldap empty password
      Bump Symfony version to 4.4.14
      Update VERSION for 4.4.13
      Update CHANGELOG for 4.4.13
      [PhpunitBridge] Fix deprecation type detection
    nicolas-grekas committed Sep 2, 2020
    Copy the full SHA
    cbf7f1a View commit details

Commits on Sep 3, 2020

  1. Merge branch '5.1' into master

    * 5.1:
      fix tests
    xabbuh committed Sep 3, 2020
    Copy the full SHA
    843cee2 View commit details

Commits on Sep 7, 2020

  1. Merge branch '5.1'

    * 5.1:
      Update UPGRADE-5.1.md
      [HttpKernel] Adjust tests to new "class not found" error message format.
      Don't call createMock with an array of interfaces.
      [FrameworkBundle] Fix Tests on PHPUnit 9.3.
      [WebProfilerBundle] Fix Tests on PHPUnit 9.3.
      esmtp error not being thrown properly
      [Yaml Parser] fixed Parser to skip comments when inlining sequences
      Update Connection.php
      [DI] fix generating preload file when cache_dir is outside project_dir
      Fix CacheCollectorPass with decorated cache pools
      [PhpUnitBridge] CoverageListenerTrait update for PHPUnit 8.5/9.x
      Remove invalid instantiation declaration
      [PropertyInfo] Fix typed collections in PHP 7.4
    fabpot committed Sep 7, 2020
    Copy the full SHA
    44d3363 View commit details

Commits on Sep 8, 2020

  1. Merge branch '5.1'

    * 5.1:
      [Debug] fix test
      consistently use same types for strict comparisons
      [PhpUnitBridge] Skip internal classes in CoverageListenerTrait
      [VarExporter] unserialize() might throw an Exception on php 8.
      [SecurityHttp] Don't call createMock() with multiple interfaces.
      [ErrorHandler] Parse "x not found" errors correctly on php 8.
      Prevent parsing invalid octal digits as octal numbers
      remove unnecessary check for  existing request
      [DI] fix ContainerBuilder on PHP8
      [Console] Make sure $maxAttempts is an int or null.
      [VarDumper] Fix caster for invalid SplFileInfo objects on php 8.
      [Intl] Skip test cases that produce a TypeError on php 8.
      [PhpUnitBridge] Adjust output parsing for PHPUnit 9.3.
      [PhpUnitBridge] CoverageListenerTrait update for PHPUnit 8.5/9.x
      add bosnian (bs) translation
      [Debug] Parse "x not found" errors correctly on php 8.
    nicolas-grekas committed Sep 8, 2020
    Copy the full SHA
    bd341a4 View commit details

Commits on Sep 13, 2020

  1. Merge branch '5.1'

    * 5.1:
      Internal classes are not legacy.
      [HttpFoundation] Skip the cookie_max_age fixture on PHP 8.
      add choice_translation_domain tests to prevent further regressions
      Update validators.tr.xlf
    fabpot committed Sep 13, 2020
    Copy the full SHA
    a8a6e0e View commit details

Commits on Sep 20, 2020

  1. Merge branch '5.1' into master

    * 5.1: (25 commits)
      stop using the deprecated at() PHPUnit matcher
      fix lowest allowed version of the HTTP client contracts
      fix lowest allowed version for the PHPUnit bridge
      fix merge
      fix merge
      drop logger mock in favor of using the BufferingLogger
      catch ValueError thrown on PHP 8
      [Yaml Parser] Fix edge cases when parsing multiple documents
      fix parsing comments not prefixed by a space
      [Translator] Make sure a null locale is handled properly
      deal with errors being thrown on PHP 8
      loadRoutes shoud receive RoutingPhpFileLoader
      [Cache] Allow cache tags to be objects implementing __toString()
      [HttpKernel] Do not override max_redirects option in HttpClientKernel
      Log notice when no entry point is configured
      remove superfluous cast
      [HttpClient] Support for CURLOPT_LOCALPORT.
      Upgrade PHPUnit to 8.5 (php 7.2) and 9.3 (php >= 7.3).
      Fixed exception message formatting
      [FrameworkBundle] Fix error in xsd which prevent to register more than one metadata
      ...
    xabbuh committed Sep 20, 2020
    Copy the full SHA
    fc9f2e6 View commit details

Commits on Sep 21, 2020

  1. fix tests

    xabbuh committed Sep 21, 2020
    Copy the full SHA
    4140574 View commit details

Commits on Sep 25, 2020

  1. Copy the full SHA
    c003c22 View commit details

Commits on Sep 26, 2020

  1. Fix CS

    fabpot committed Sep 26, 2020
    Copy the full SHA
    b6c713f View commit details

Commits on Sep 30, 2020

  1. Merge branch '5.1'

    * 5.1:
      [FrameworkBundle] Add Mailjet definition
      Revert "bug #38063 [FrameworkBundle] generate preload.php in src/ to make opcache.preload predictable (nicolas-grekas)"
      [PhpUnitBridge] Fix class_alias() for PHPUnit\Framework\Error\Error
    fabpot committed Sep 30, 2020
    Copy the full SHA
    0a67187 View commit details

Commits on Oct 2, 2020

  1. Merge branch '5.1'

    * 5.1:
      [5.1] Ignore more deprecations for Mockery mocks
      [PhpUnitBridge] Fix Deprecation file when it comes from the TestsListener
      disallow FrameworkBundle 4.4+
      propagate validation groups to subforms
      [Form] [Validator] Add failing testcase to demonstrate group sequence issue
    nicolas-grekas committed Oct 2, 2020
    Copy the full SHA
    6e7824c View commit details

Commits on Oct 6, 2020

  1. Merge branch '5.1' into 5.x

    * 5.1:
      fix merge
      [appveyor] fix checking for the .x branch
      Remove "branch-alias", populate "version"
    nicolas-grekas committed Oct 6, 2020
    Copy the full SHA
    5989fb1 View commit details
  2. Merge branch '5.1' into 5.x

    * 5.1:
      Update versions in composer.json
      [Mime] Fix serialization of RawMessage
    nicolas-grekas committed Oct 6, 2020
    Copy the full SHA
    ee2f15c View commit details

Commits on Oct 7, 2020

  1. Copy the full SHA
    9a963fc View commit details

Commits on Oct 12, 2020

  1. Fix test for PHP 8

    nicolas-grekas committed Oct 12, 2020
    Copy the full SHA
    19dded6 View commit details
  2. Merge branch '5.1' into 5.x

    * 5.1:
      Disable the PhpUnit bridge when testing it
      [PropertyInfo] Support for the mixed type.
      Don't unset the inflate resource on close as it might still be needed
      [HttpClient] Fix CurlHttpClient memory leak
      [Form] Add Bosnian (bs) validators translation
      [Form] Add missing Serbian (latn & cyrl) validators translation
      [Cache] skip igbinary < 3.1.6
      [Ldap] Bypass the use of `ldap_control_paged_result` on PHP >= 7.3
      [Form] [Validator] added pt_BR translations
      Estonian update
      Fix no collection in extract default value
      [PhpUnitBridge] fix running parallel tests with phpunit 9
      [VarDumper] fix truncating big arrays
    nicolas-grekas committed Oct 12, 2020
    Copy the full SHA
    e51eba2 View commit details

Commits on Oct 13, 2020

  1. Merge branch '5.1' into 5.x

    * 5.1:
      Remove "version" from composer.json files, use "branch-version" instead
      [String] fix "is too large" ValueError on PHP 8
    nicolas-grekas committed Oct 13, 2020
    Copy the full SHA
    b3c54c5 View commit details
  2. Merge branch '5.1' into 5.x

    * 5.1:
      Fix branch-version
    nicolas-grekas committed Oct 13, 2020
    Copy the full SHA
    1d56970 View commit details

Commits on Oct 14, 2020

  1. Fix test

    fabpot committed Oct 14, 2020
    Copy the full SHA
    d4e2c34 View commit details

Commits on Oct 22, 2020

  1. Copy the full SHA
    7638f15 View commit details

Commits on Oct 24, 2020

  1. Merge branch '5.1' into 5.x

    * 5.1:
      fix merge
      fix merge
      Remove branch-version (keep them for contracts only)
      [HttpClient] relax auth bearer format requirements
      [PHPUnitBridge] Silence errors from mkdir()
      [DependencyInjection] Preload classes with union types correctly.
      [Serializer] fix decoding float XML attributes starting with 0
      add missing dutch translations
      [TwigBridge] Remove "transchoice" from the code base
      Support PHPUnit 8 and PHPUnit 9 in constraint compatibility trait
      Add expectDeprecation, expectNotice, expectWarning, and expectError to TestCase polyfill
      [String] fix before/after[Last]() returning the empty string instead of the original one on non-match
      Add missing exporter function for PHPUnit 7
      [Validator] Add missing romanian translations
      [String] fix slicing in UnicodeString
      [Cache] Use correct expiry in ChainAdapter
      do not translate null placeholders or titles
    nicolas-grekas committed Oct 24, 2020
    Copy the full SHA
    0daa328 View commit details
  2. Merge branch '5.1' into 5.x

    * 5.1:
      Disable platform checks
      Put branch-version in the source for CI
      Bump default PHPUnit version for PHP 8 to 9.4.
    nicolas-grekas committed Oct 24, 2020
    Copy the full SHA
    debe16e View commit details

Commits on Oct 30, 2020

  1. Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    d9a7875 View commit details
  2. Merge branch '4.4' into 5.1

    derrabus committed Oct 30, 2020
    Copy the full SHA
    ae3552a View commit details
Showing with 3,481 additions and 1,991 deletions.
  1. +1 −2 .gitattributes
  2. +8 −0 .github/PULL_REQUEST_TEMPLATE.md
  3. +20 −0 .github/workflows/close-pull-request.yml
  4. +41 −0 CHANGELOG.md
  5. +37 −11 ClassExistsMock.php
  6. +33 −9 ClockMock.php
  7. +3 −9 ConstraintTrait.php
  8. +102 −9 CoverageListener.php
  9. +140 −93 DeprecationErrorHandler.php
  10. +210 −64 DeprecationErrorHandler/Configuration.php
  11. +153 −110 DeprecationErrorHandler/Deprecation.php
  12. +6 −23 DeprecationErrorHandler/DeprecationGroup.php
  13. +2 −2 DeprecationErrorHandler/DeprecationNotice.php
  14. +21 −21 DnsMock.php
  15. +30 −0 ExpectUserDeprecationMessageTrait.php
  16. +39 −0 Extension/EnableClockMockSubscriber.php
  17. +39 −0 Extension/RegisterClockMockSubscriber.php
  18. +39 −0 Extension/RegisterDnsMockSubscriber.php
  19. +1 −1 LICENSE
  20. +0 −57 Legacy/CommandForV5.php
  21. +2 −7 Legacy/{CommandForV6.php → CommandForV7.php}
  22. +1 −6 Legacy/CommandForV9.php
  23. +0 −130 Legacy/ConstraintTraitForV6.php
  24. +1 −1 Legacy/ConstraintTraitForV7.php
  25. +0 −35 Legacy/CoverageListenerForV5.php
  26. +0 −41 Legacy/CoverageListenerForV6.php
  27. +0 −41 Legacy/CoverageListenerForV7.php
  28. +0 −160 Legacy/CoverageListenerTrait.php
  29. +1 −3 Legacy/ExpectDeprecationTraitBeforeV8_4.php
  30. +4 −4 Legacy/ExpectDeprecationTraitForV8_4.php
  31. +0 −402 Legacy/PolyfillAssertTrait.php
  32. +3 −98 Legacy/PolyfillTestCaseTrait.php
  33. +0 −70 Legacy/SetUpTearDownTraitForV5.php
  34. +0 −58 Legacy/SetUpTearDownTraitForV8.php
  35. +0 −54 Legacy/SymfonyTestsListenerForV5.php
  36. +0 −58 Legacy/SymfonyTestsListenerForV6.php
  37. +48 −47 Legacy/SymfonyTestsListenerTrait.php
  38. +7 −6 README.md
  39. +0 −28 SetUpTearDownTrait.php
  40. +133 −0 SymfonyExtension.php
  41. +1 −7 SymfonyTestsListener.php
  42. +0 −40 Tests/BootstrapTest.php
  43. +46 −0 Tests/ClassExistsMockTest.php
  44. +10 −0 Tests/ClockMockTest.php
  45. +40 −16 Tests/CoverageListenerTest.php
  46. +391 −3 Tests/DeprecationErrorHandler/ConfigurationTest.php
  47. +19 −15 Tests/DeprecationErrorHandler/DeprecationTest.php
  48. +81 −0 Tests/DeprecationErrorHandler/baseline.phpt
  49. +76 −0 Tests/DeprecationErrorHandler/baseline2.phpt
  50. +79 −0 Tests/DeprecationErrorHandler/baseline3.phpt
  51. +41 −0 Tests/DeprecationErrorHandler/debug_class_loader_autoload.phpt
  52. +41 −0 Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt
  53. +1 −1 Tests/DeprecationErrorHandler/deprecation/deprecation.php
  54. +37 −0 Tests/DeprecationErrorHandler/disabled_1.phpt
  55. +1 −2 Tests/DeprecationErrorHandler/fake_app/AppService.php
  56. +13 −0 Tests/DeprecationErrorHandler/fake_app/BarService.php
  57. +9 −0 Tests/DeprecationErrorHandler/fake_app/ExtendsDeprecatedFromVendor.php
  58. +10 −0 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/ExtendsDeprecatedClassFromOtherVendor.php
  59. +15 −0 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/PhpDeprecation.php
  60. +1 −1 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php
  61. +7 −7 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php
  62. +1 −1 Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php
  63. +13 −3 Tests/DeprecationErrorHandler/fake_vendor/composer/autoload_real.php
  64. +10 −0 Tests/DeprecationErrorHandler/fake_vendor/fcy/lib/DeprecatedClass.php
  65. +1 −1 Tests/DeprecationErrorHandler/fake_vendor_bis/composer/autoload_real.php
  66. +1 −1 Tests/DeprecationErrorHandler/fake_vendor_bis/foo/lib/SomeOtherService.php
  67. +65 −0 Tests/DeprecationErrorHandler/generate_baseline.phpt
  68. +2 −2 Tests/DeprecationErrorHandler/generate_phar.php
  69. +80 −0 Tests/DeprecationErrorHandler/ignore.phpt
  70. +60 −0 Tests/DeprecationErrorHandler/log_file.phpt
  71. +1 −3 Tests/DeprecationErrorHandler/partially_quiet.phpt
  72. +36 −0 Tests/DeprecationErrorHandler/partially_quiet2.phpt
  73. +43 −0 Tests/DeprecationErrorHandler/php_deprecation_from_vendor_class.phpt
  74. +1 −1 Tests/DeprecationErrorHandler/quiet.phpt
  75. +3 −5 Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt
  76. +82 −0 Tests/EnumExistsMockTest.php
  77. +4 −0 Tests/ExpectDeprecationTraitTest.php
  78. +3 −0 Tests/ExpectedDeprecationAnnotationTest.php
  79. +3 −0 Tests/FailTests/ExpectDeprecationTraitTestFail.php
  80. +34 −0 Tests/FailTests/NoAssertionsTestNotRisky.php
  81. +46 −0 Tests/FailTests/NoAssertionsTestRisky.php
  82. +16 −0 Tests/Fixtures/ExistingEnum.php
  83. +16 −0 Tests/Fixtures/ExistingEnumReal.php
  84. +12 −16 Tests/Fixtures/coverage/phpunit-with-listener.xml.dist
  85. +11 −14 Tests/Fixtures/coverage/phpunit-without-listener.xml.dist
  86. +1 −1 Tests/Fixtures/coverage/tests/BarCovTest.php
  87. 0 Tests/Fixtures/coverage/tests/{SutNotFindTest.php → SutNotFoundTest.php}
  88. +0 −10 Tests/Fixtures/coverage/tests/bootstrap.php
  89. +29 −0 Tests/Fixtures/symfonyextension/phpunit-with-extension.xml.dist
  90. +22 −0 Tests/Fixtures/symfonyextension/phpunit-without-extension.xml.dist
  91. +16 −0 Tests/Fixtures/symfonyextension/src/ClassExtendingFinalClass.php
  92. +19 −0 Tests/Fixtures/symfonyextension/src/FinalClass.php
  93. +32 −0 Tests/Fixtures/symfonyextension/tests/bootstrap.php
  94. +12 −1 Tests/ProcessIsolationTest.php
  95. +140 −0 Tests/SymfonyExtension.php
  96. +64 −0 Tests/SymfonyExtensionWithManualRegister.php
  97. +4 −2 Tests/expectdeprecationfail.phpt
  98. +20 −0 Tests/expectnotrisky.phpt
  99. +26 −0 Tests/expectrisky.phpt
  100. +32 −0 Tests/symfonyextension.phpt
  101. +296 −0 Tests/symfonyextensionnotregistered.phpt
  102. +2 −4 TextUI/Command.php
  103. +130 −57 bin/simple-phpunit.php
  104. +27 −98 bootstrap.php
  105. +12 −10 composer.json
  106. +9 −9 phpunit.xml.dist
3 changes: 1 addition & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.git* export-ignore
8 changes: 8 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Please do not submit any Pull Requests here. They will be closed.
---

Please submit your PR here instead:
https://github.com/symfony/symfony

This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!
20 changes: 20 additions & 0 deletions .github/workflows/close-pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Close Pull Request

on:
pull_request_target:
types: [opened]

jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: |
Thanks for your Pull Request! We love contributions.
However, you should instead open your PR on the main repository:
https://github.com/symfony/symfony
This repository is what we call a "subtree split": a read-only subset of that main repository.
We're looking forward to your PR there!
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,47 @@
CHANGELOG
=========

7.2
---

* Add a PHPUnit extension that registers the clock mock and DNS mock and the `DebugClassLoader` from the ErrorHandler component if present
* Add `ExpectUserDeprecationMessageTrait` with a polyfill of PHPUnit's `expectUserDeprecationMessage()`
* Use `total` for asserting deprecation count when a group is not defined

6.4
---

* Allow setting the locale using `SYMFONY_PHPUNIT_LOCALE` env var

6.3
---

* Add support for mocking the `enum_exists` function
* Enable reporting of deprecations triggered by Doctrine by default

6.2
---

* Add support for mocking the `hrtime()` function

6.1
---

* Add option `ignoreFile` to configure a file that lists deprecation messages to ignore

6.0
---

* Remove `SetUpTearDownTrait`

5.3
---

* bumped the minimum PHP version to 7.1.3
* bumped the minimum PHPUnit version to 7.5
* deprecated the `SetUpTearDownTrait` trait, use original methods with "void" return typehint.
* added `logFile` option to write deprecations to a file instead of echoing them

5.1.0
-----

48 changes: 37 additions & 11 deletions ClassExistsMock.php
Original file line number Diff line number Diff line change
@@ -18,34 +18,60 @@ class ClassExistsMock
{
private static $classes = [];

private static $enums = [];

/**
* Configures the classes to be checked upon existence.
*
* @param array $classes Mocked class names as keys (case sensitive, without leading root namespace slash) and booleans as values
* @param array $classes Mocked class names as keys (case-sensitive, without leading root namespace slash) and booleans as values
*/
public static function withMockedClasses(array $classes)
public static function withMockedClasses(array $classes): void
{
self::$classes = $classes;
}

public static function class_exists($name, $autoload = true)
/**
* Configures the enums to be checked upon existence.
*
* @param array $enums Mocked enums names as keys (case-sensitive, without leading root namespace slash) and booleans as values
*/
public static function withMockedEnums(array $enums): void
{
self::$enums = $enums;
self::$classes += $enums;
}

public static function class_exists($name, $autoload = true): bool
{
return (bool) (self::$classes[ltrim($name, '\\')] ?? \class_exists($name, $autoload));
$name = ltrim($name, '\\');

return isset(self::$classes[$name]) ? (bool) self::$classes[$name] : \class_exists($name, $autoload);
}

public static function interface_exists($name, $autoload = true)
public static function interface_exists($name, $autoload = true): bool
{
return (bool) (self::$classes[ltrim($name, '\\')] ?? \interface_exists($name, $autoload));
$name = ltrim($name, '\\');

return isset(self::$classes[$name]) ? (bool) self::$classes[$name] : \interface_exists($name, $autoload);
}

public static function trait_exists($name, $autoload = true)
public static function trait_exists($name, $autoload = true): bool
{
return (bool) (self::$classes[ltrim($name, '\\')] ?? \trait_exists($name, $autoload));
$name = ltrim($name, '\\');

return isset(self::$classes[$name]) ? (bool) self::$classes[$name] : \trait_exists($name, $autoload);
}

public static function enum_exists($name, $autoload = true):bool
{
$name = ltrim($name, '\\');

return isset(self::$enums[$name]) ? (bool) self::$enums[$name] : \enum_exists($name, $autoload);
}

public static function register($class)
public static function register($class): void
{
$self = \get_called_class();
$self = static::class;

$mockedNs = [substr($class, 0, strrpos($class, '\\'))];
if (0 < strpos($class, '\\Tests\\')) {
@@ -55,7 +81,7 @@ public static function register($class)
$mockedNs[] = substr($class, 6, strrpos($class, '\\') - 6);
}
foreach ($mockedNs as $ns) {
foreach (['class', 'interface', 'trait'] as $type) {
foreach (['class', 'interface', 'trait', 'enum'] as $type) {
if (\function_exists($ns.'\\'.$type.'_exists')) {
continue;
}
42 changes: 33 additions & 9 deletions ClockMock.php
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ class ClockMock
{
private static $now;

public static function withClockMock($enable = null)
public static function withClockMock($enable = null): ?bool
{
if (null === $enable) {
return null !== self::$now;
@@ -30,7 +30,7 @@ public static function withClockMock($enable = null)
return null;
}

public static function time()
public static function time(): int
{
if (null === self::$now) {
return \time();
@@ -39,7 +39,7 @@ public static function time()
return (int) self::$now;
}

public static function sleep($s)
public static function sleep($s): int
{
if (null === self::$now) {
return \sleep($s);
@@ -50,7 +50,7 @@ public static function sleep($s)
return 0;
}

public static function usleep($us)
public static function usleep($us): void
{
if (null === self::$now) {
\usleep($us);
@@ -59,6 +59,9 @@ public static function usleep($us)
}
}

/**
* @return string|float
*/
public static function microtime($asFloat = false)
{
if (null === self::$now) {
@@ -69,10 +72,10 @@ public static function microtime($asFloat = false)
return self::$now;
}

return sprintf('%0.6f00 %d', self::$now - (int) self::$now, (int) self::$now);
return \sprintf('%0.6f00 %d', self::$now - (int) self::$now, (int) self::$now);
}

public static function date($format, $timestamp = null)
public static function date($format, $timestamp = null): string
{
if (null === $timestamp) {
$timestamp = self::time();
@@ -81,7 +84,7 @@ public static function date($format, $timestamp = null)
return \date($format, $timestamp);
}

public static function gmdate($format, $timestamp = null)
public static function gmdate($format, $timestamp = null): string
{
if (null === $timestamp) {
$timestamp = self::time();
@@ -90,9 +93,25 @@ public static function gmdate($format, $timestamp = null)
return \gmdate($format, $timestamp);
}

public static function register($class)
/**
* @return array|int|float
*/
public static function hrtime($asNumber = false)
{
$self = \get_called_class();
$ns = (self::$now - (int) self::$now) * 1000000000;

if ($asNumber) {
$number = \sprintf('%d%d', (int) self::$now, $ns);

return \PHP_INT_SIZE === 8 ? (int) $number : (float) $number;
}

return [(int) self::$now, (int) $ns];
}

public static function register($class): void
{
$self = static::class;

$mockedNs = [substr($class, 0, strrpos($class, '\\'))];
if (0 < strpos($class, '\\Tests\\')) {
@@ -137,6 +156,11 @@ function gmdate(\$format, \$timestamp = null)
{
return \\$self::gmdate(\$format, \$timestamp);
}
function hrtime(\$asNumber = false)
{
return \\$self::hrtime(\$asNumber);
}
EOPHP
);
}
12 changes: 3 additions & 9 deletions ConstraintTrait.php
Original file line number Diff line number Diff line change
@@ -12,20 +12,14 @@
namespace Symfony\Bridge\PhpUnit;

use PHPUnit\Framework\Constraint\Constraint;
use ReflectionClass;

$r = new ReflectionClass(Constraint::class);
if (\PHP_VERSION_ID < 70000 || !$r->getMethod('matches')->hasReturnType()) {
trait ConstraintTrait
{
use Legacy\ConstraintTraitForV6;
}
} elseif ($r->getProperty('exporter')->isProtected()) {
$r = new \ReflectionClass(Constraint::class);
if ($r->getProperty('exporter')->isProtected()) {
trait ConstraintTrait
{
use Legacy\ConstraintTraitForV7;
}
} elseif (\PHP_VERSION < 70100 || !$r->getMethod('evaluate')->hasReturnType()) {
} elseif (!$r->getMethod('evaluate')->hasReturnType()) {
trait ConstraintTrait
{
use Legacy\ConstraintTraitForV8;
111 changes: 102 additions & 9 deletions CoverageListener.php
Original file line number Diff line number Diff line change
@@ -11,16 +11,109 @@

namespace Symfony\Bridge\PhpUnit;

if (version_compare(\PHPUnit\Runner\Version::id(), '6.0.0', '<')) {
class_alias('Symfony\Bridge\PhpUnit\Legacy\CoverageListenerForV5', 'Symfony\Bridge\PhpUnit\CoverageListener');
} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) {
class_alias('Symfony\Bridge\PhpUnit\Legacy\CoverageListenerForV6', 'Symfony\Bridge\PhpUnit\CoverageListener');
} else {
class_alias('Symfony\Bridge\PhpUnit\Legacy\CoverageListenerForV7', 'Symfony\Bridge\PhpUnit\CoverageListener');
}
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Annotation\Registry;
use PHPUnit\Util\Test as TestUtil;

class CoverageListener implements TestListener
{
use TestListenerDefaultImplementation;

private $sutFqcnResolver;
private $warningOnSutNotFound;

public function __construct(?callable $sutFqcnResolver = null, bool $warningOnSutNotFound = false)
{
$this->sutFqcnResolver = $sutFqcnResolver ?? static function (Test $test): ?string {
$class = \get_class($test);

$sutFqcn = str_replace('\\Tests\\', '\\', $class);
$sutFqcn = preg_replace('{Test$}', '', $sutFqcn);

return class_exists($sutFqcn) ? $sutFqcn : null;
};

$this->warningOnSutNotFound = $warningOnSutNotFound;
}

public function startTest(Test $test): void
{
if (!$test instanceof TestCase) {
return;
}

$annotations = TestUtil::parseTestMethodAnnotations(\get_class($test), $test->getName(false));

$ignoredAnnotations = ['covers', 'coversDefaultClass', 'coversNothing'];

foreach ($ignoredAnnotations as $annotation) {
if (isset($annotations['class'][$annotation]) || isset($annotations['method'][$annotation])) {
return;
}
}

$sutFqcn = ($this->sutFqcnResolver)($test);
if (!$sutFqcn) {
if ($this->warningOnSutNotFound) {
$test->getTestResultObject()->addWarning($test, new Warning('Could not find the tested class.'), 0);
}

if (false) {
class CoverageListener
return;
}

$covers = $sutFqcn;
if (!\is_array($sutFqcn)) {
$covers = [$sutFqcn];
while ($parent = get_parent_class($sutFqcn)) {
$covers[] = $parent;
$sutFqcn = $parent;
}
}

if (class_exists(Registry::class)) {
$this->addCoversForDocBlockInsideRegistry($test, $covers);

return;
}

$this->addCoversForClassToAnnotationCache($test, $covers);
}

private function addCoversForClassToAnnotationCache(Test $test, array $covers): void
{
$r = new \ReflectionProperty(TestUtil::class, 'annotationCache');
$r->setAccessible(true);

$cache = $r->getValue();
$cache = array_replace_recursive($cache, [
\get_class($test) => [
'covers' => $covers,
],
]);

$r->setValue(TestUtil::class, $cache);
}

private function addCoversForDocBlockInsideRegistry(Test $test, array $covers): void
{
$docBlock = Registry::getInstance()->forClassName(\get_class($test));

$symbolAnnotations = new \ReflectionProperty($docBlock, 'symbolAnnotations');
$symbolAnnotations->setAccessible(true);

// Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException
$covers = array_filter($covers, function (string $class) {
$reflector = new \ReflectionClass($class);

return $reflector->isUserDefined();
});

$symbolAnnotations->setValue($docBlock, array_replace($docBlock->symbolAnnotations(), [
'covers' => $covers,
]));
}
}
233 changes: 140 additions & 93 deletions DeprecationErrorHandler.php

Large diffs are not rendered by default.

274 changes: 210 additions & 64 deletions DeprecationErrorHandler/Configuration.php
Original file line number Diff line number Diff line change
@@ -37,21 +37,49 @@ class Configuration
private $verboseOutput;

/**
* @param int[] $thresholds A hash associating groups to thresholds
* @param string $regex Will be matched against messages, to decide
* whether to display a stack trace
* @param bool[] $verboseOutput Keyed by groups
* @var string[]
*/
private function __construct(array $thresholds = [], $regex = '', $verboseOutput = [])
private $ignoreDeprecationPatterns = [];

/**
* @var bool
*/
private $generateBaseline = false;

/**
* @var string
*/
private $baselineFile = '';

/**
* @var array
*/
private $baselineDeprecations = [];

/**
* @var string|null
*/
private $logFile;

/**
* @param int[] $thresholds A hash associating groups to thresholds
* @param string $regex Will be matched against messages, to decide whether to display a stack trace
* @param bool[] $verboseOutput Keyed by groups
* @param string $ignoreFile The path to the ignore deprecation patterns file
* @param bool $generateBaseline Whether to generate or update the baseline file
* @param string $baselineFile The path to the baseline file
* @param string|null $logFile The path to the log file
*/
private function __construct(array $thresholds = [], string $regex = '', array $verboseOutput = [], string $ignoreFile = '', bool $generateBaseline = false, string $baselineFile = '', ?string $logFile = null)
{
$groups = ['total', 'indirect', 'direct', 'self'];

foreach ($thresholds as $group => $threshold) {
if (!\in_array($group, $groups, true)) {
throw new \InvalidArgumentException(sprintf('Unrecognized threshold "%s", expected one of "%s".', $group, implode('", "', $groups)));
throw new \InvalidArgumentException(\sprintf('Unrecognized threshold "%s", expected one of "%s".', $group, implode('", "', $groups)));
}
if (!is_numeric($threshold)) {
throw new \InvalidArgumentException(sprintf('Threshold for group "%s" has invalid value "%s".', $group, $threshold));
throw new \InvalidArgumentException(\sprintf('Threshold for group "%s" has invalid value "%s".', $group, $threshold));
}
$this->thresholds[$group] = (int) $threshold;
}
@@ -68,7 +96,7 @@ private function __construct(array $thresholds = [], $regex = '', $verboseOutput
}
foreach ($groups as $group) {
if (!isset($this->thresholds[$group])) {
$this->thresholds[$group] = 999999;
$this->thresholds[$group] = $this->thresholds['total'] ?? 999999;
}
}
$this->regex = $regex;
@@ -83,26 +111,58 @@ private function __construct(array $thresholds = [], $regex = '', $verboseOutput

foreach ($verboseOutput as $group => $status) {
if (!isset($this->verboseOutput[$group])) {
throw new \InvalidArgumentException(sprintf('Unsupported verbosity group "%s", expected one of "%s".', $group, implode('", "', array_keys($this->verboseOutput))));
throw new \InvalidArgumentException(\sprintf('Unsupported verbosity group "%s", expected one of "%s".', $group, implode('", "', array_keys($this->verboseOutput))));
}
$this->verboseOutput[$group] = $status;
}

if ($ignoreFile) {
if (!is_file($ignoreFile)) {
throw new \InvalidArgumentException(\sprintf('The ignoreFile "%s" does not exist.', $ignoreFile));
}
set_error_handler(static function ($t, $m) use ($ignoreFile, &$line) {
throw new \RuntimeException(\sprintf('Invalid pattern found in "%s" on line "%d"', $ignoreFile, 1 + $line).substr($m, 12));
});
try {
foreach (file($ignoreFile) as $line => $pattern) {
if ('#' !== (trim($pattern)[0] ?? '#')) {
preg_match($pattern, '');
$this->ignoreDeprecationPatterns[] = $pattern;
}
}
} finally {
restore_error_handler();
}
}

if ($generateBaseline && !$baselineFile) {
throw new \InvalidArgumentException('You cannot use the "generateBaseline" configuration option without providing a "baselineFile" configuration option.');
}
$this->generateBaseline = $generateBaseline;
$this->baselineFile = $baselineFile;
if ($this->baselineFile && !$this->generateBaseline) {
if (is_file($this->baselineFile)) {
$map = json_decode(file_get_contents($this->baselineFile));
foreach ($map as $baseline_deprecation) {
$this->baselineDeprecations[$baseline_deprecation->location][$baseline_deprecation->message] = $baseline_deprecation->count;
}
} else {
throw new \InvalidArgumentException(\sprintf('The baselineFile "%s" does not exist.', $this->baselineFile));
}
$this->verboseOutput[$group] = (bool) $status;
}

$this->logFile = $logFile;
}

/**
* @return bool
*/
public function isEnabled()
public function isEnabled(): bool
{
return $this->enabled;
}

/**
* @param DeprecationGroup[] $deprecationGroups
*
* @return bool
*/
public function tolerates(array $deprecationGroups)
public function tolerates(array $deprecationGroups): bool
{
$grandTotal = 0;

@@ -125,96 +185,188 @@ public function tolerates(array $deprecationGroups)
return true;
}

public function isIgnoredDeprecation(Deprecation $deprecation): bool
{
if (!$this->ignoreDeprecationPatterns) {
return false;
}
$result = @preg_filter($this->ignoreDeprecationPatterns, '$0', $deprecation->getMessage());
if (\PREG_NO_ERROR !== preg_last_error()) {
throw new \RuntimeException(preg_last_error_msg());
}

return (bool) $result;
}

/**
* @param string $message
* @param array<string,DeprecationGroup> $deprecationGroups
*
* @return bool
* @return bool true if the threshold is not reached for the deprecation type nor for the total
*/
public function shouldDisplayStackTrace($message)
public function toleratesForGroup(string $groupName, array $deprecationGroups): bool
{
$grandTotal = 0;

foreach ($deprecationGroups as $type => $group) {
if ('legacy' !== $type) {
$grandTotal += $group->count();
}
}

if ($grandTotal > $this->thresholds['total']) {
return false;
}

if (\in_array($groupName, ['self', 'direct', 'indirect'], true) && $deprecationGroups[$groupName]->count() > $this->thresholds[$groupName]) {
return false;
}

return true;
}

public function isBaselineDeprecation(Deprecation $deprecation): bool
{
if ($deprecation->isLegacy()) {
return false;
}

if ($deprecation->originatesFromDebugClassLoader()) {
$location = $deprecation->triggeringClass();
} elseif ($deprecation->originatesFromAnObject()) {
$location = $deprecation->originatingClass().'::'.$deprecation->originatingMethod();
} else {
$location = 'procedural code';
}

$message = $deprecation->getMessage();
$result = isset($this->baselineDeprecations[$location][$message]) && $this->baselineDeprecations[$location][$message] > 0;
if ($this->generateBaseline) {
if ($result) {
++$this->baselineDeprecations[$location][$message];
} else {
$this->baselineDeprecations[$location][$message] = 1;
$result = true;
}
} elseif ($result) {
--$this->baselineDeprecations[$location][$message];
}

return $result;
}

public function isGeneratingBaseline(): bool
{
return $this->generateBaseline;
}

public function getBaselineFile(): string
{
return $this->baselineFile;
}

public function writeBaseline(): void
{
$map = [];
foreach ($this->baselineDeprecations as $location => $messages) {
foreach ($messages as $message => $count) {
$map[] = [
'location' => $location,
'message' => $message,
'count' => $count,
];
}
}
file_put_contents($this->baselineFile, json_encode($map, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
}

public function shouldDisplayStackTrace(string $message): bool
{
return '' !== $this->regex && preg_match($this->regex, $message);
}

/**
* @return bool
*/
public function isInRegexMode()
public function isInRegexMode(): bool
{
return '' !== $this->regex;
}

/**
* @return bool
*/
public function verboseOutput($group)
public function verboseOutput($group): bool
{
return $this->verboseOutput[$group];
}

public function shouldWriteToLogFile(): bool
{
return null !== $this->logFile;
}

public function getLogFile(): ?string
{
return $this->logFile;
}

/**
* @param string $serializedConfiguration an encoded string, for instance
* max[total]=1234&max[indirect]=42
*
* @return self
* @param string $serializedConfiguration An encoded string, for instance max[total]=1234&max[indirect]=42
*/
public static function fromUrlEncodedString($serializedConfiguration)
public static function fromUrlEncodedString(string $serializedConfiguration): self
{
parse_str($serializedConfiguration, $normalizedConfiguration);
foreach (array_keys($normalizedConfiguration) as $key) {
if (!\in_array($key, ['max', 'disabled', 'verbose', 'quiet'], true)) {
throw new \InvalidArgumentException(sprintf('Unknown configuration option "%s".', $key));
if (!\in_array($key, ['max', 'disabled', 'verbose', 'quiet', 'ignoreFile', 'generateBaseline', 'baselineFile', 'logFile'], true)) {
throw new \InvalidArgumentException(\sprintf('Unknown configuration option "%s".', $key));
}
}

if (isset($normalizedConfiguration['disabled'])) {
$normalizedConfiguration += [
'max' => ['total' => 0],
'disabled' => false,
'verbose' => true,
'quiet' => [],
'ignoreFile' => '',
'generateBaseline' => false,
'baselineFile' => '',
'logFile' => null,
];

if ('' === $normalizedConfiguration['disabled'] || filter_var($normalizedConfiguration['disabled'], \FILTER_VALIDATE_BOOLEAN)) {
return self::inDisabledMode();
}

$verboseOutput = [];
if (!isset($normalizedConfiguration['verbose'])) {
$normalizedConfiguration['verbose'] = true;
}

foreach (['unsilenced', 'direct', 'indirect', 'self', 'other'] as $group) {
$verboseOutput[$group] = (bool) $normalizedConfiguration['verbose'];
$verboseOutput[$group] = filter_var($normalizedConfiguration['verbose'], \FILTER_VALIDATE_BOOLEAN);
}

if (isset($normalizedConfiguration['quiet']) && \is_array($normalizedConfiguration['quiet'])) {
if (\is_array($normalizedConfiguration['quiet'])) {
foreach ($normalizedConfiguration['quiet'] as $shushedGroup) {
$verboseOutput[$shushedGroup] = false;
}
}

return new self(
isset($normalizedConfiguration['max']) ? $normalizedConfiguration['max'] : [],
$normalizedConfiguration['max'],
'',
$verboseOutput
$verboseOutput,
$normalizedConfiguration['ignoreFile'],
filter_var($normalizedConfiguration['generateBaseline'], \FILTER_VALIDATE_BOOLEAN),
$normalizedConfiguration['baselineFile'],
$normalizedConfiguration['logFile']
);
}

/**
* @return self
*/
public static function inDisabledMode()
public static function inDisabledMode(): self
{
$configuration = new self();
$configuration->enabled = false;

return $configuration;
}

/**
* @return self
*/
public static function inStrictMode()
public static function inStrictMode(): self
{
return new self(['total' => 0]);
}

/**
* @return self
*/
public static function inWeakMode()
public static function inWeakMode(): self
{
$verboseOutput = [];
foreach (['unsilenced', 'direct', 'indirect', 'self', 'other'] as $group) {
@@ -224,18 +376,12 @@ public static function inWeakMode()
return new self([], '', $verboseOutput);
}

/**
* @return self
*/
public static function fromNumber($upperBound)
public static function fromNumber($upperBound): self
{
return new self(['total' => $upperBound]);
}

/**
* @return self
*/
public static function fromRegex($regex)
public static function fromRegex($regex): self
{
return new self([], $regex);
}
263 changes: 153 additions & 110 deletions DeprecationErrorHandler/Deprecation.php

Large diffs are not rendered by default.

29 changes: 6 additions & 23 deletions DeprecationErrorHandler/DeprecationGroup.php
Original file line number Diff line number Diff line change
@@ -23,21 +23,13 @@ final class DeprecationGroup
*/
private $deprecationNotices = [];

/**
* @param string $message
* @param string $class
* @param string $method
*/
public function addNoticeFromObject($message, $class, $method)
public function addNoticeFromObject(string $message, string $class, string $method): void
{
$this->deprecationNotice($message)->addObjectOccurrence($class, $method);
$this->addNotice();
}

/**
* @param string $message
*/
public function addNoticeFromProceduralCode($message)
public function addNoticeFromProceduralCode(string $message): void
{
$this->deprecationNotice($message)->addProceduralOccurrence();
$this->addNotice();
@@ -48,26 +40,17 @@ public function addNotice()
++$this->count;
}

/**
* @param string $message
*
* @return DeprecationNotice
*/
private function deprecationNotice($message)
private function deprecationNotice(string $message): DeprecationNotice
{
if (!isset($this->deprecationNotices[$message])) {
$this->deprecationNotices[$message] = new DeprecationNotice();
}

return $this->deprecationNotices[$message];
return $this->deprecationNotices[$message] ?? $this->deprecationNotices[$message] = new DeprecationNotice();
}

public function count()
public function count(): int
{
return $this->count;
}

public function notices()
public function notices(): array
{
return $this->deprecationNotices;
}
4 changes: 2 additions & 2 deletions DeprecationErrorHandler/DeprecationNotice.php
Original file line number Diff line number Diff line change
@@ -37,12 +37,12 @@ public function addProceduralOccurrence()
++$this->count;
}

public function getCountsByCaller()
public function getCountsByCaller(): array
{
return $this->countsByCaller;
}

public function count()
public function count(): int
{
return $this->count;
}
42 changes: 21 additions & 21 deletions DnsMock.php
Original file line number Diff line number Diff line change
@@ -18,31 +18,31 @@ class DnsMock
{
private static $hosts = [];
private static $dnsTypes = [
'A' => DNS_A,
'MX' => DNS_MX,
'NS' => DNS_NS,
'SOA' => DNS_SOA,
'PTR' => DNS_PTR,
'CNAME' => DNS_CNAME,
'AAAA' => DNS_AAAA,
'A6' => DNS_A6,
'SRV' => DNS_SRV,
'NAPTR' => DNS_NAPTR,
'TXT' => DNS_TXT,
'HINFO' => DNS_HINFO,
'A' => \DNS_A,
'MX' => \DNS_MX,
'NS' => \DNS_NS,
'SOA' => \DNS_SOA,
'PTR' => \DNS_PTR,
'CNAME' => \DNS_CNAME,
'AAAA' => \DNS_AAAA,
'A6' => \DNS_A6,
'SRV' => \DNS_SRV,
'NAPTR' => \DNS_NAPTR,
'TXT' => \DNS_TXT,
'HINFO' => \DNS_HINFO,
];

/**
* Configures the mock values for DNS queries.
*
* @param array $hosts Mocked hosts as keys, arrays of DNS records as returned by dns_get_record() as values
*/
public static function withMockedHosts(array $hosts)
public static function withMockedHosts(array $hosts): void
{
self::$hosts = $hosts;
}

public static function checkdnsrr($hostname, $type = 'MX')
public static function checkdnsrr($hostname, $type = 'MX'): bool
{
if (!self::$hosts) {
return \checkdnsrr($hostname, $type);
@@ -63,7 +63,7 @@ public static function checkdnsrr($hostname, $type = 'MX')
return false;
}

public static function getmxrr($hostname, &$mxhosts, &$weight = null)
public static function getmxrr($hostname, &$mxhosts, &$weight = null): bool
{
if (!self::$hosts) {
return \getmxrr($hostname, $mxhosts, $weight);
@@ -137,7 +137,7 @@ public static function gethostbynamel($hostname)
return $ips;
}

public static function dns_get_record($hostname, $type = DNS_ANY, &$authns = null, &$addtl = null, $raw = false)
public static function dns_get_record($hostname, $type = \DNS_ANY, &$authns = null, &$addtl = null, $raw = false)
{
if (!self::$hosts) {
return \dns_get_record($hostname, $type, $authns, $addtl, $raw);
@@ -146,13 +146,13 @@ public static function dns_get_record($hostname, $type = DNS_ANY, &$authns = nul
$records = false;

if (isset(self::$hosts[$hostname])) {
if (DNS_ANY === $type) {
$type = DNS_ALL;
if (\DNS_ANY === $type) {
$type = \DNS_ALL;
}
$records = [];

foreach (self::$hosts[$hostname] as $record) {
if (isset(self::$dnsTypes[$record['type']]) && (self::$dnsTypes[$record['type']] & $type)) {
if ((self::$dnsTypes[$record['type']] ?? 0) & $type) {
$records[] = array_merge(['host' => $hostname, 'class' => 'IN', 'ttl' => 1, 'type' => $record['type']], $record);
}
}
@@ -161,9 +161,9 @@ public static function dns_get_record($hostname, $type = DNS_ANY, &$authns = nul
return $records;
}

public static function register($class)
public static function register($class): void
{
$self = \get_called_class();
$self = static::class;

$mockedNs = [substr($class, 0, strrpos($class, '\\'))];
if (0 < strpos($class, '\\Tests\\')) {
30 changes: 30 additions & 0 deletions ExpectUserDeprecationMessageTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit;

use PHPUnit\Runner\Version;

if (version_compare(Version::id(), '11.0.0', '<')) {
trait ExpectUserDeprecationMessageTrait
{
use ExpectDeprecationTrait;

final protected function expectUserDeprecationMessage(string $expectedUserDeprecationMessage): void
{
$this->expectDeprecation(str_replace('%', '%%', $expectedUserDeprecationMessage));
}
}
} else {
trait ExpectUserDeprecationMessageTrait
{
}
}
39 changes: 39 additions & 0 deletions Extension/EnableClockMockSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit\Extension;

use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Test\PreparationStarted;
use PHPUnit\Event\Test\PreparationStartedSubscriber;
use PHPUnit\Metadata\Group;
use Symfony\Bridge\PhpUnit\ClockMock;

/**
* @internal
*/
class EnableClockMockSubscriber implements PreparationStartedSubscriber
{
public function notify(PreparationStarted $event): void
{
$test = $event->test();

if (!$test instanceof TestMethod) {
return;
}

foreach ($test->metadata() as $metadata) {
if ($metadata instanceof Group && 'time-sensitive' === $metadata->groupName()) {
ClockMock::withClockMock(true);
}
}
}
}
39 changes: 39 additions & 0 deletions Extension/RegisterClockMockSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit\Extension;

use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\TestSuite\Loaded;
use PHPUnit\Event\TestSuite\LoadedSubscriber;
use PHPUnit\Metadata\Group;
use Symfony\Bridge\PhpUnit\ClockMock;

/**
* @internal
*/
class RegisterClockMockSubscriber implements LoadedSubscriber
{
public function notify(Loaded $event): void
{
foreach ($event->testSuite()->tests() as $test) {
if (!$test instanceof TestMethod) {
continue;
}

foreach ($test->metadata() as $metadata) {
if ($metadata instanceof Group && 'time-sensitive' === $metadata->groupName()) {
ClockMock::register($test->className());
}
}
}
}
}
39 changes: 39 additions & 0 deletions Extension/RegisterDnsMockSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit\Extension;

use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\TestSuite\Loaded;
use PHPUnit\Event\TestSuite\LoadedSubscriber;
use PHPUnit\Metadata\Group;
use Symfony\Bridge\PhpUnit\DnsMock;

/**
* @internal
*/
class RegisterDnsMockSubscriber implements LoadedSubscriber
{
public function notify(Loaded $event): void
{
foreach ($event->testSuite()->tests() as $test) {
if (!$test instanceof TestMethod) {
continue;
}

foreach ($test->metadata() as $metadata) {
if ($metadata instanceof Group && 'dns-sensitive' === $metadata->groupName()) {
DnsMock::register($test->className());
}
}
}
}
}
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2014-2020 Fabien Potencier
Copyright (c) 2014-present Fabien Potencier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
57 changes: 0 additions & 57 deletions Legacy/CommandForV5.php

This file was deleted.

9 changes: 2 additions & 7 deletions Legacy/CommandForV6.php → Legacy/CommandForV7.php
Original file line number Diff line number Diff line change
@@ -17,18 +17,13 @@
use Symfony\Bridge\PhpUnit\SymfonyTestsListener;

/**
* {@inheritdoc}
*
* @internal
*/
class CommandForV6 extends BaseCommand
class CommandForV7 extends BaseCommand
{
/**
* {@inheritdoc}
*/
protected function createRunner(): BaseRunner
{
$this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : [];
$this->arguments['listeners'] ?? $this->arguments['listeners'] = [];

$registeredLocally = false;

7 changes: 1 addition & 6 deletions Legacy/CommandForV9.php
Original file line number Diff line number Diff line change
@@ -20,18 +20,13 @@
use Symfony\Bridge\PhpUnit\SymfonyTestsListener;

/**
* {@inheritdoc}
*
* @internal
*/
class CommandForV9 extends BaseCommand
{
/**
* {@inheritdoc}
*/
protected function createRunner(): BaseRunner
{
$this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : [];
$this->arguments['listeners'] ?? $this->arguments['listeners'] = [];

$registeredLocally = false;

130 changes: 0 additions & 130 deletions Legacy/ConstraintTraitForV6.php

This file was deleted.

2 changes: 1 addition & 1 deletion Legacy/ConstraintTraitForV7.php
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ protected function additionalFailureDescription($other): string

protected function exporter(): Exporter
{
if (null !== $this->exporter) {
if (null === $this->exporter) {
$this->exporter = new Exporter();
}

35 changes: 0 additions & 35 deletions Legacy/CoverageListenerForV5.php

This file was deleted.

41 changes: 0 additions & 41 deletions Legacy/CoverageListenerForV6.php

This file was deleted.

41 changes: 0 additions & 41 deletions Legacy/CoverageListenerForV7.php

This file was deleted.

160 changes: 0 additions & 160 deletions Legacy/CoverageListenerTrait.php

This file was deleted.

4 changes: 1 addition & 3 deletions Legacy/ExpectDeprecationTraitBeforeV8_4.php
Original file line number Diff line number Diff line change
@@ -18,10 +18,8 @@ trait ExpectDeprecationTraitBeforeV8_4
{
/**
* @param string $message
*
* @return void
*/
protected function expectDeprecation($message)
protected function expectDeprecation($message): void
{
// Expected deprecations set by isolated tests need to be written to a file
// so that the test running process can take account of them.
8 changes: 4 additions & 4 deletions Legacy/ExpectDeprecationTraitForV8_4.php
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
namespace Symfony\Bridge\PhpUnit\Legacy;

/**
* @internal use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait instead.
* @internal use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait instead
*/
trait ExpectDeprecationTraitForV8_4
{
@@ -21,7 +21,7 @@ trait ExpectDeprecationTraitForV8_4
*/
public function expectDeprecation(): void
{
if (1 > func_num_args() || !\is_string($message = func_get_arg(0))) {
if (1 > \func_num_args() || !\is_string($message = func_get_arg(0))) {
throw new \InvalidArgumentException(sprintf('The "%s()" method requires the string $message argument.', __FUNCTION__));
}

@@ -48,15 +48,15 @@ public function expectDeprecation(): void
}

/**
* @internal use expectDeprecation() instead.
* @internal use expectDeprecation() instead
*/
public function expectDeprecationMessage(string $message): void
{
throw new \BadMethodCallException(sprintf('The "%s()" method is not supported by Symfony\'s PHPUnit Bridge ExpectDeprecationTrait, pass the message to expectDeprecation() instead.', __FUNCTION__));
}

/**
* @internal use expectDeprecation() instead.
* @internal use expectDeprecation() instead
*/
public function expectDeprecationMessageMatches(string $regularExpression): void
{
402 changes: 0 additions & 402 deletions Legacy/PolyfillAssertTrait.php

Large diffs are not rendered by default.

101 changes: 3 additions & 98 deletions Legacy/PolyfillTestCaseTrait.php
Original file line number Diff line number Diff line change
@@ -14,88 +14,12 @@
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

/**
* This trait is @internal.
*/
trait PolyfillTestCaseTrait
{
/**
* @param string|string[] $originalClassName
*
* @return MockObject
*/
protected function createMock($originalClassName)
{
$mock = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning();

if (method_exists($mock, 'disallowMockingUnknownTypes')) {
$mock = $mock->disallowMockingUnknownTypes();
}

return $mock->getMock();
}

/**
* @param string|string[] $originalClassName
* @param string[] $methods
*
* @return MockObject
*/
protected function createPartialMock($originalClassName, array $methods)
{
$mock = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->setMethods(empty($methods) ? null : $methods);

if (method_exists($mock, 'disallowMockingUnknownTypes')) {
$mock = $mock->disallowMockingUnknownTypes();
}

return $mock->getMock();
}

/**
* @param string $exception
*
* @return void
*/
public function expectException($exception)
{
$this->doExpectException($exception);
}

/**
* @param int|string $code
*
* @return void
*/
public function expectExceptionCode($code)
{
$property = new \ReflectionProperty(TestCase::class, 'expectedExceptionCode');
$property->setAccessible(true);
$property->setValue($this, $code);
}

/**
* @param string $message
*
* @return void
*/
public function expectExceptionMessage($message)
{
$property = new \ReflectionProperty(TestCase::class, 'expectedExceptionMessage');
$property->setAccessible(true);
$property->setValue($this, $message);
}

/**
* @param string $messageRegExp
*
@@ -106,24 +30,12 @@ public function expectExceptionMessageMatches($messageRegExp)
$this->expectExceptionMessageRegExp($messageRegExp);
}

/**
* @param string $messageRegExp
*
* @return void
*/
public function expectExceptionMessageRegExp($messageRegExp)
{
$property = new \ReflectionProperty(TestCase::class, 'expectedExceptionMessageRegExp');
$property->setAccessible(true);
$property->setValue($this, $messageRegExp);
}

/**
* @return void
*/
public function expectNotice()
{
$this->doExpectException(Notice::class);
$this->expectException(Notice::class);
}

/**
@@ -151,7 +63,7 @@ public function expectNoticeMessageMatches($regularExpression)
*/
public function expectWarning()
{
$this->doExpectException(Warning::class);
$this->expectException(Warning::class);
}

/**
@@ -179,7 +91,7 @@ public function expectWarningMessageMatches($regularExpression)
*/
public function expectError()
{
$this->doExpectException(Error::class);
$this->expectException(Error::class);
}

/**
@@ -201,11 +113,4 @@ public function expectErrorMessageMatches($regularExpression)
{
$this->expectExceptionMessageMatches($regularExpression);
}

private function doExpectException($exception)
{
$property = new \ReflectionProperty(TestCase::class, 'expectedException');
$property->setAccessible(true);
$property->setValue($this, $exception);
}
}
70 changes: 0 additions & 70 deletions Legacy/SetUpTearDownTraitForV5.php

This file was deleted.

58 changes: 0 additions & 58 deletions Legacy/SetUpTearDownTraitForV8.php

This file was deleted.

54 changes: 0 additions & 54 deletions Legacy/SymfonyTestsListenerForV5.php

This file was deleted.

58 changes: 0 additions & 58 deletions Legacy/SymfonyTestsListenerForV6.php

This file was deleted.

95 changes: 48 additions & 47 deletions Legacy/SymfonyTestsListenerTrait.php
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@

use Doctrine\Common\Annotations\AnnotationRegistry;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\DataProviderTestSuite;
use PHPUnit\Framework\RiskyTestError;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
@@ -23,7 +24,6 @@
use Symfony\Bridge\PhpUnit\ClockMock;
use Symfony\Bridge\PhpUnit\DnsMock;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
use Symfony\Component\ErrorHandler\DebugClassLoader;

/**
@@ -51,6 +51,8 @@ class SymfonyTestsListenerTrait
*/
public function __construct(array $mockedNamespaces = [])
{
setlocale(\LC_ALL, $_ENV['SYMFONY_PHPUNIT_LOCALE'] ?? 'C');

if (class_exists(ExcludeList::class)) {
(new ExcludeList())->getExcludedDirectories();
ExcludeList::addDirectory(\dirname((new \ReflectionClass(__CLASS__))->getFileName(), 2));
@@ -61,7 +63,7 @@ public function __construct(array $mockedNamespaces = [])
Blacklist::$blacklistedClassNames[__CLASS__] = 2;
}

$enableDebugClassLoader = class_exists(DebugClassLoader::class) || class_exists(LegacyDebugClassLoader::class);
$enableDebugClassLoader = class_exists(DebugClassLoader::class);

foreach ($mockedNamespaces as $type => $namespaces) {
if (!\is_array($namespaces)) {
@@ -82,11 +84,7 @@ public function __construct(array $mockedNamespaces = [])
}
}
if ($enableDebugClassLoader) {
if (class_exists(DebugClassLoader::class)) {
DebugClassLoader::enable();
} else {
LegacyDebugClassLoader::enable();
}
DebugClassLoader::enable();
}
if (self::$globallyEnabled) {
$this->state = -2;
@@ -112,18 +110,18 @@ public function __destruct()
}
}

public function globalListenerDisabled()
public function globalListenerDisabled(): void
{
self::$globallyEnabled = false;
$this->state = -1;
}

public function startTestSuite($suite)
public function startTestSuite($suite): void
{
$suiteName = $suite->getName();

foreach ($suite->tests() as $test) {
if (!($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
if (!$test instanceof TestCase) {
continue;
}
if (null === Test::getPreserveGlobalStateSettings(\get_class($test), $test->getName(false))) {
@@ -135,10 +133,10 @@ public function startTestSuite($suite)
echo "Testing $suiteName\n";
$this->state = 0;

if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) {
if (method_exists('Doctrine\Common\Annotations\AnnotationRegistry', 'registerUniqueLoader')) {
if (!class_exists(AnnotationRegistry::class, false) && class_exists(AnnotationRegistry::class)) {
if (method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) {
AnnotationRegistry::registerUniqueLoader('class_exists');
} else {
} elseif (method_exists(AnnotationRegistry::class, 'registerLoader')) {
AnnotationRegistry::registerLoader('class_exists');
}
}
@@ -174,36 +172,41 @@ public function startTestSuite($suite)
}
}
} elseif (2 === $this->state) {
$suites = [$suite];
$skipped = [];
foreach ($suite->tests() as $test) {
if (!($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)
|| isset($this->wasSkipped[$suiteName]['*'])
|| isset($this->wasSkipped[$suiteName][$test->getName()])) {
$skipped[] = $test;
while ($s = array_shift($suites)) {
foreach ($s->tests() as $test) {
if ($test instanceof TestSuite) {
$suites[] = $test;
continue;
}
if ($test instanceof TestCase
&& isset($this->wasSkipped[\get_class($test)][$test->getName()])
) {
$skipped[] = $test;
}
}
}
$suite->setTests($skipped);
}
}

public function addSkippedTest($test, \Exception $e, $time)
public function addSkippedTest($test, \Exception $e, $time): void
{
if (0 < $this->state) {
if ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase) {
$class = \get_class($test);
$method = $test->getName();
if ($test instanceof DataProviderTestSuite) {
foreach ($test->tests() as $testWithDataProvider) {
$this->isSkipped[\get_class($testWithDataProvider)][$testWithDataProvider->getName()] = 1;
}
} else {
$class = $test->getName();
$method = '*';
$this->isSkipped[\get_class($test)][$test->getName()] = 1;
}

$this->isSkipped[$class][$method] = 1;
}
}

public function startTest($test)
public function startTest($test): void
{
if (-2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
if (-2 < $this->state && $test instanceof TestCase) {
// This event is triggered before the test is re-run in isolation
if ($this->willBeIsolated($test)) {
$this->runsInSeparateProcess = tempnam(sys_get_temp_dir(), 'deprec');
@@ -230,25 +233,25 @@ public function startTest($test)
$annotations = Test::parseTestMethodAnnotations(\get_class($test), $test->getName(false));

if (isset($annotations['class']['expectedDeprecation'])) {
$test->getTestResultObject()->addError($test, new AssertionFailedError('`@expectedDeprecation` annotations are not allowed at the class level.'), 0);
$test->getTestResultObject()->addError($test, new AssertionFailedError('"@expectedDeprecation" annotations are not allowed at the class level.'), 0);
}
if (isset($annotations['method']['expectedDeprecation']) || $this->checkNumAssertions = method_exists($test, 'expectDeprecation') && (new \ReflectionMethod($test, 'expectDeprecation'))->getFileName() === (new \ReflectionMethod(ExpectDeprecationTrait::class, 'expectDeprecation'))->getFileName()) {
if (isset($annotations['method']['expectedDeprecation'])) {
self::$expectedDeprecations = $annotations['method']['expectedDeprecation'];
self::$previousErrorHandler = set_error_handler([self::class, 'handleError']);
@trigger_error('Since symfony/phpunit-bridge 5.1: Using "@expectedDeprecation" annotations in tests is deprecated, use the "ExpectDeprecationTrait::expectDeprecation()" method instead.', E_USER_DEPRECATED);
@trigger_error('Since symfony/phpunit-bridge 5.1: Using "@expectedDeprecation" annotations in tests is deprecated, use the "ExpectDeprecationTrait::expectDeprecation()" method instead.', \E_USER_DEPRECATED);
}

if ($this->checkNumAssertions) {
$this->checkNumAssertions = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything() && !$test->doesNotPerformAssertions();
$this->checkNumAssertions = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything();
}

$test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false);
}
}
}

public function endTest($test, $time)
public function endTest($test, $time): void
{
if ($file = getenv('SYMFONY_EXPECTED_DEPRECATIONS_SERIALIZE')) {
putenv('SYMFONY_EXPECTED_DEPRECATIONS_SERIALIZE');
@@ -269,7 +272,10 @@ public function endTest($test, $time)
$groups = Test::getGroups($className, $test->getName(false));

if ($this->checkNumAssertions) {
if (!self::$expectedDeprecations && !$test->getNumAssertions() && $test->getTestResultObject()->noneSkipped()) {
$assertions = \count(self::$expectedDeprecations) + $test->getNumAssertions();
if ($test->doesNotPerformAssertions() && $assertions > 0) {
$test->getTestResultObject()->addFailure($test, new RiskyTestError(sprintf('This test is annotated with "@doesNotPerformAssertions", but performed %s assertions', $assertions)), $time);
} elseif ($assertions === 0 && !$test->doesNotPerformAssertions() && $test->getTestResultObject()->noneSkipped()) {
$test->getTestResultObject()->addFailure($test, new RiskyTestError('This test did not perform any assertions'), $time);
}

@@ -281,12 +287,12 @@ public function endTest($test, $time)
unlink($this->runsInSeparateProcess);
putenv('SYMFONY_DEPRECATIONS_SERIALIZE');
foreach ($deprecations ? unserialize($deprecations) : [] as $deprecation) {
$error = serialize(['deprecation' => $deprecation[1], 'class' => $className, 'method' => $test->getName(false), 'triggering_file' => isset($deprecation[2]) ? $deprecation[2] : null]);
$error = serialize(['deprecation' => $deprecation[1], 'class' => $className, 'method' => $test->getName(false), 'triggering_file' => $deprecation[2] ?? null, 'files_stack' => $deprecation[3] ?? []]);
if ($deprecation[0]) {
// unsilenced on purpose
trigger_error($error, E_USER_DEPRECATED);
trigger_error($error, \E_USER_DEPRECATED);
} else {
@trigger_error($error, E_USER_DEPRECATED);
@trigger_error($error, \E_USER_DEPRECATED);
}
}
$this->runsInSeparateProcess = false;
@@ -300,7 +306,7 @@ public function endTest($test, $time)
restore_error_handler();

if (!\in_array('legacy', $groups, true)) {
$test->getTestResultObject()->addError($test, new AssertionFailedError('Only tests with the `@group legacy` annotation can expect a deprecation.'), 0);
$test->getTestResultObject()->addError($test, new AssertionFailedError('Only tests with the "@group legacy" annotation can expect a deprecation.'), 0);
} elseif (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
try {
$prefix = "@expectedDeprecation:\n";
@@ -313,7 +319,7 @@ public function endTest($test, $time)
self::$expectedDeprecations = self::$gatheredDeprecations = [];
self::$previousErrorHandler = null;
}
if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
if (!$this->runsInSeparateProcess && -2 < $this->state && $test instanceof TestCase) {
if (\in_array('time-sensitive', $groups, true)) {
ClockMock::withClockMock(false);
}
@@ -325,7 +331,7 @@ public function endTest($test, $time)

public static function handleError($type, $msg, $file, $line, $context = [])
{
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
if (\E_USER_DEPRECATED !== $type && \E_DEPRECATED !== $type) {
$h = self::$previousErrorHandler;

return $h ? $h($type, $msg, $file, $line, $context) : false;
@@ -341,15 +347,10 @@ public static function handleError($type, $msg, $file, $line, $context = [])
}
self::$gatheredDeprecations[] = $msg;

return null;
return true;
}

/**
* @param TestCase $test
*
* @return bool
*/
private function willBeIsolated($test)
private function willBeIsolated(TestCase $test): bool
{
if ($test->isInIsolation()) {
return false;
@@ -358,6 +359,6 @@ private function willBeIsolated($test)
$r = new \ReflectionProperty($test, 'runTestInSeparateProcess');
$r->setAccessible(true);

return $r->getValue($test);
return $r->getValue($test) ?? false;
}
}
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
PHPUnit Bridge
==============

Provides utilities for PHPUnit, especially user deprecation notices management.
The PHPUnit bridge provides utilities for [PHPUnit](https://phpunit.de/),
especially user deprecation notices management.

Resources
---------

* [Documentation](https://symfony.com/doc/current/components/phpunit_bridge.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
* [Documentation](https://symfony.com/doc/current/components/phpunit_bridge.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
28 changes: 0 additions & 28 deletions SetUpTearDownTrait.php

This file was deleted.

133 changes: 133 additions & 0 deletions SymfonyExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit;

use PHPUnit\Event\Code\Test;
use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Test\BeforeTestMethodErrored;
use PHPUnit\Event\Test\BeforeTestMethodErroredSubscriber;
use PHPUnit\Event\Test\Errored;
use PHPUnit\Event\Test\ErroredSubscriber;
use PHPUnit\Event\Test\Finished;
use PHPUnit\Event\Test\FinishedSubscriber;
use PHPUnit\Event\Test\Skipped;
use PHPUnit\Event\Test\SkippedSubscriber;
use PHPUnit\Metadata\Group;
use PHPUnit\Runner\Extension\Extension;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;
use Symfony\Bridge\PhpUnit\Extension\EnableClockMockSubscriber;
use Symfony\Bridge\PhpUnit\Extension\RegisterClockMockSubscriber;
use Symfony\Bridge\PhpUnit\Extension\RegisterDnsMockSubscriber;
use Symfony\Component\ErrorHandler\DebugClassLoader;

class SymfonyExtension implements Extension
{
public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
{
if (class_exists(DebugClassLoader::class)) {
DebugClassLoader::enable();
}

if ($parameters->has('clock-mock-namespaces')) {
foreach (explode(',', $parameters->get('clock-mock-namespaces')) as $namespace) {
ClockMock::register($namespace.'\DummyClass');
}
}

$facade->registerSubscriber(new RegisterClockMockSubscriber());
$facade->registerSubscriber(new EnableClockMockSubscriber());
$facade->registerSubscriber(new class implements ErroredSubscriber {
public function notify(Errored $event): void
{
SymfonyExtension::disableClockMock($event->test());
SymfonyExtension::disableDnsMock($event->test());
}
});
$facade->registerSubscriber(new class implements FinishedSubscriber {
public function notify(Finished $event): void
{
SymfonyExtension::disableClockMock($event->test());
SymfonyExtension::disableDnsMock($event->test());
}
});
$facade->registerSubscriber(new class implements SkippedSubscriber {
public function notify(Skipped $event): void
{
SymfonyExtension::disableClockMock($event->test());
SymfonyExtension::disableDnsMock($event->test());
}
});

if (interface_exists(BeforeTestMethodErroredSubscriber::class)) {
$facade->registerSubscriber(new class implements BeforeTestMethodErroredSubscriber {
public function notify(BeforeTestMethodErrored $event): void
{
if (method_exists($event, 'test')) {
SymfonyExtension::disableClockMock($event->test());
SymfonyExtension::disableDnsMock($event->test());
} else {
ClockMock::withClockMock(false);
DnsMock::withMockedHosts([]);
}
}
});
}

if ($parameters->has('dns-mock-namespaces')) {
foreach (explode(',', $parameters->get('dns-mock-namespaces')) as $namespace) {
DnsMock::register($namespace.'\DummyClass');
}
}

$facade->registerSubscriber(new RegisterDnsMockSubscriber());
}

/**
* @internal
*/
public static function disableClockMock(Test $test): void
{
if (self::hasGroup($test, 'time-sensitive')) {
ClockMock::withClockMock(false);
}
}

/**
* @internal
*/
public static function disableDnsMock(Test $test): void
{
if (self::hasGroup($test, 'dns-sensitive')) {
DnsMock::withMockedHosts([]);
}
}

/**
* @internal
*/
public static function hasGroup(Test $test, string $groupName): bool
{
if (!$test instanceof TestMethod) {
return false;
}

foreach ($test->metadata() as $metadata) {
if ($metadata instanceof Group && $groupName === $metadata->groupName()) {
return true;
}
}

return false;
}
}
8 changes: 1 addition & 7 deletions SymfonyTestsListener.php
Original file line number Diff line number Diff line change
@@ -11,13 +11,7 @@

namespace Symfony\Bridge\PhpUnit;

if (version_compare(\PHPUnit\Runner\Version::id(), '6.0.0', '<')) {
class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) {
class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV6', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
} else {
class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
}
class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');

if (false) {
class SymfonyTestsListener
40 changes: 0 additions & 40 deletions Tests/BootstrapTest.php

This file was deleted.

46 changes: 46 additions & 0 deletions Tests/ClassExistsMockTest.php
Original file line number Diff line number Diff line change
@@ -31,6 +31,10 @@ protected function setUp(): void
ExistingTrait::class => false,
'NonExistingTrait' => true,
]);

ClassExistsMock::withMockedEnums([
'NonExistingEnum' => true,
]);
}

public function testClassExists()
@@ -53,6 +57,26 @@ public function testClassExists()
$this->assertFalse(class_exists('\\NonExistingClassReal', false));
}

public function testEnumExistsOnClasses()
{
$this->assertFalse(enum_exists(ExistingClass::class));
$this->assertFalse(enum_exists(ExistingClass::class, false));
$this->assertFalse(enum_exists('\\'.ExistingClass::class));
$this->assertFalse(enum_exists('\\'.ExistingClass::class, false));
$this->assertFalse(enum_exists('NonExistingClass'));
$this->assertFalse(enum_exists('NonExistingClass', false));
$this->assertFalse(enum_exists('\\NonExistingClass'));
$this->assertFalse(enum_exists('\\NonExistingClass', false));
$this->assertFalse(enum_exists(ExistingClassReal::class));
$this->assertFalse(enum_exists(ExistingClassReal::class, false));
$this->assertFalse(enum_exists('\\'.ExistingClassReal::class));
$this->assertFalse(enum_exists('\\'.ExistingClassReal::class, false));
$this->assertFalse(enum_exists('NonExistingClassReal'));
$this->assertFalse(enum_exists('NonExistingClassReal', false));
$this->assertFalse(enum_exists('\\NonExistingClassReal'));
$this->assertFalse(enum_exists('\\NonExistingClassReal', false));
}

public function testInterfaceExists()
{
$this->assertFalse(interface_exists(ExistingInterface::class));
@@ -92,6 +116,28 @@ public function testTraitExists()
$this->assertFalse(trait_exists('\\NonExistingTraitReal'));
$this->assertFalse(trait_exists('\\NonExistingTraitReal', false));
}

public function testEnumExists()
{
$this->assertTrue(enum_exists('NonExistingEnum'));
$this->assertTrue(enum_exists('NonExistingEnum', false));
$this->assertTrue(enum_exists('\\NonExistingEnum'));
$this->assertTrue(enum_exists('\\NonExistingEnum', false));
$this->assertFalse(enum_exists('NonExistingClassReal'));
$this->assertFalse(enum_exists('NonExistingClassReal', false));
$this->assertFalse(enum_exists('\\NonExistingEnumReal'));
$this->assertFalse(enum_exists('\\NonExistingEnumReal', false));
}

public function testClassExistsOnEnums()
{
$this->assertTrue(class_exists('NonExistingEnum'));
$this->assertTrue(class_exists('NonExistingEnum', false));
$this->assertTrue(class_exists('\\NonExistingEnum'));
$this->assertTrue(class_exists('\\NonExistingEnum', false));
$this->assertFalse(class_exists('\\NonExistingEnumReal'));
$this->assertFalse(class_exists('\\NonExistingEnumReal', false));
}
}

class ExistingClass
10 changes: 10 additions & 0 deletions Tests/ClockMockTest.php
Original file line number Diff line number Diff line change
@@ -69,4 +69,14 @@ public function testGmDate()

$this->assertSame('1555075769', gmdate('U'));
}

public function testHrTime()
{
$this->assertSame([1234567890, 125000000], hrtime());
}

public function testHrTimeAsNumber()
{
$this->assertSame(1234567890125000000, hrtime(true));
}
}
56 changes: 40 additions & 16 deletions Tests/CoverageListenerTest.php
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PhpUnit\Tests;

use PHPUnit\Framework\TestCase;

/**
* @requires PHPUnit < 10
*/
class CoverageListenerTest extends TestCase
{
public function test()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('This test cannot be run on Windows.');
}

exec('type phpdbg 2> /dev/null', $output, $returnCode);

if (\PHP_VERSION_ID >= 70000 && 0 === $returnCode) {
$php = 'phpdbg -qrr';
} else {
exec('php --ri xdebug -d zend_extension=xdebug.so 2> /dev/null', $output, $returnCode);
if (0 !== $returnCode) {
$this->markTestSkipped('Xdebug is required to run this test.');
}
$php = 'php -d zend_extension=xdebug.so';
}

$dir = __DIR__.'/../Tests/Fixtures/coverage';
$phpunit = $_SERVER['argv'][0];

$php = $this->findCoverageDriver();

$output = '';
exec("$php $phpunit -c $dir/phpunit-without-listener.xml.dist $dir/tests/ --coverage-text --colors=never 2> /dev/null", $output);
$output = implode("\n", $output);
$this->assertMatchesRegularExpression('/FooCov\n\s*Methods:\s+100.00%[^\n]+Lines:\s+100.00%/', $output);

$output = '';
exec("$php $phpunit -c $dir/phpunit-with-listener.xml.dist $dir/tests/ --coverage-text --colors=never 2> /dev/null", $output);
$output = implode("\n", $output);

@@ -45,4 +45,28 @@ public function test()
$this->assertStringNotContainsString("CoversDefaultClassTest::test\nCould not find the tested class.", $output);
$this->assertStringNotContainsString("CoversNothingTest::test\nCould not find the tested class.", $output);
}

private function findCoverageDriver(): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('This test cannot be run on Windows.');
}

exec('php --ri xdebug -d zend_extension=xdebug 2> /dev/null', $output, $returnCode);
if (0 === $returnCode) {
return 'php -d zend_extension=xdebug';
}

exec('php --ri pcov -d zend_extension=pcov 2> /dev/null', $output, $returnCode);
if (0 === $returnCode) {
return 'php -d zend_extension=pcov';
}

exec('type phpdbg 2> /dev/null', $output, $returnCode);
if (0 === $returnCode) {
return 'phpdbg -qrr';
}

$this->markTestSkipped('Xdebug or pvoc is required to run this test.');
}
}
394 changes: 391 additions & 3 deletions Tests/DeprecationErrorHandler/ConfigurationTest.php

Large diffs are not rendered by default.

34 changes: 19 additions & 15 deletions Tests/DeprecationErrorHandler/DeprecationTest.php
Original file line number Diff line number Diff line change
@@ -14,13 +14,10 @@
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5;
use Symfony\Bridge\PhpUnit\SetUpTearDownTrait;
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7;

class DeprecationTest extends TestCase
{
use SetUpTearDownTrait;

private static $vendorDir;
private static $prefixDirsPsr4;

@@ -33,7 +30,7 @@ private static function getVendorDir()
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$vendorDir = \dirname(\dirname($r->getFileName()));
$vendorDir = \dirname($r->getFileName(), 2);
if (file_exists($vendorDir.'/composer/installed.json') && @mkdir($vendorDir.'/myfakevendor/myfakepackage1', 0777, true)) {
break;
}
@@ -61,7 +58,7 @@ public function testItCanTellWhetherItIsInternal()
{
$r = new \ReflectionClass(Deprecation::class);

if (\dirname(\dirname($r->getFileName())) !== \dirname(\dirname(__DIR__))) {
if (\dirname($r->getFileName(), 2) !== \dirname(__DIR__, 2)) {
$this->markTestSkipped('Test case is not compatible with having the bridge in vendor/');
}

@@ -100,7 +97,7 @@ public function testItMutesOnlySpecificErrorMessagesWhenTheCallingCodeIsInPhpuni
$this->assertSame($muted, $deprecation->isMuted());
}

public function mutedProvider()
public static function mutedProvider(): iterable
{
yield 'not from phpunit, and not a whitelisted message' => [
false,
@@ -150,7 +147,7 @@ public function testItTakesMutesDeprecationFromPhpUnitFiles()
$this->assertTrue($deprecation->isMuted());
}

public function providerGetTypeDetectsSelf()
public static function providerGetTypeDetectsSelf(): array
{
return [
'not_from_vendors_file' => [Deprecation::TYPE_SELF, '', 'MyClass1', __FILE__],
@@ -164,7 +161,7 @@ public function providerGetTypeDetectsSelf()
'triggering_file' => 'dummy_vendor_path',
'files_stack' => [],
]),
SymfonyTestsListenerForV5::class,
SymfonyTestsListenerForV7::class,
'',
],
];
@@ -177,15 +174,22 @@ public function testGetTypeDetectsSelf(string $expectedType, string $message, st
{
$trace = [
['class' => 'MyClass1', 'function' => 'myMethod'],
['function' => 'trigger_error'],
['class' => SymfonyTestsListenerTrait::class, 'function' => 'endTest'],
['class' => $traceClass, 'function' => 'myMethod'],
];
$deprecation = new Deprecation($message, $trace, $file);
$this->assertSame($expectedType, $deprecation->getType());
}

public function providerGetTypeUsesRightTrace()
public static function providerGetTypeUsesRightTrace(): array
{
$vendorDir = self::getVendorDir();
$fakeTrace = [
['function' => 'trigger_error'],
['class' => SymfonyTestsListenerTrait::class, 'function' => 'endTest'],
['class' => SymfonyTestsListenerForV7::class, 'function' => 'endTest'],
];

return [
'no_file_in_stack' => [Deprecation::TYPE_DIRECT, '', [['function' => 'myfunc1'], ['function' => 'myfunc2']]],
@@ -208,7 +212,7 @@ public function providerGetTypeUsesRightTrace()
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile2.php',
],
]),
[['function' => 'myfunc1'], ['class' => SymfonyTestsListenerForV5::class, 'method' => 'mymethod']],
$fakeTrace,
],
'serialized_stack_files_from_various_packages' => [
Deprecation::TYPE_INDIRECT,
@@ -221,7 +225,7 @@ public function providerGetTypeUsesRightTrace()
$vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php',
],
]),
[['function' => 'myfunc1'], ['class' => SymfonyTestsListenerForV5::class, 'method' => 'mymethod']],
$fakeTrace,
],
];
}
@@ -261,12 +265,12 @@ private static function removeDir($dir)
rmdir($dir);
}

private static function doSetupBeforeClass()
public static function setUpBeforeClass(): void
{
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$v = \dirname(\dirname($r->getFileName()));
$v = \dirname($r->getFileName(), 2);
if (file_exists($v.'/composer/installed.json')) {
$loader = require $v.'/autoload.php';
$reflection = new \ReflectionClass($loader);
@@ -281,7 +285,7 @@ private static function doSetupBeforeClass()
}
}

private static function doTearDownAfterClass()
public static function tearDownAfterClass(): void
{
foreach (self::$prefixDirsPsr4 as [$prop, $loader, $prefixDirsPsr4]) {
$prop->setValue($loader, $prefixDirsPsr4);
81 changes: 81 additions & 0 deletions Tests/DeprecationErrorHandler/baseline.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
--TEST--
Test DeprecationErrorHandler in baseline mode
--FILE--
<?php
$filename = tempnam(sys_get_temp_dir(), 'sf-');
$baseline = [[
'location' => 'FooTestCase::testLegacyFoo',
'message' => 'silenced foo deprecation',
'count' => 1,
],
[
'location' => 'FooTestCase::testNonLegacyBar',
'message' => 'silenced bar deprecation',
'count' => 1,
],
[
'location' => 'procedural code',
'message' => 'root deprecation',
'count' => 1,
]];
file_put_contents($filename, json_encode($baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));

$k = 'SYMFONY_DEPRECATIONS_HELPER';
unset($_SERVER[$k], $_ENV[$k]);
putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'baselineFile=' . urlencode($filename));
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';

@trigger_error('root deprecation', E_USER_DEPRECATED);

eval(<<<'EOPHP'
namespace PHPUnit\Util;
class Test
{
public static function getGroups()
{
return array();
}
}
EOPHP
);

class PHPUnit_Util_Test
{
public static function getGroups()
{
return array();
}
}

class FooTestCase
{
public function testLegacyFoo()
{
@trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
}

public function testNonLegacyBar()
{
@trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
}
}

$foo = new FooTestCase();
$foo->testLegacyFoo();
$foo->testNonLegacyBar();
print "Cannot test baselineFile contents because it is generated in a shutdown function registered by another shutdown function."
?>
--EXPECT--
Cannot test baselineFile contents because it is generated in a shutdown function registered by another shutdown function.
Legacy deprecation notices (1)
76 changes: 76 additions & 0 deletions Tests/DeprecationErrorHandler/baseline2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
--TEST--
Test DeprecationErrorHandler in baseline mode
--FILE--
<?php
$filename = tempnam(sys_get_temp_dir(), 'sf-');
$baseline = [[
'location' => 'FooTestCase::testLegacyFoo',
'message' => 'silenced foo deprecation',
'count' => 1,
]];
file_put_contents($filename, json_encode($baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));

$k = 'SYMFONY_DEPRECATIONS_HELPER';
unset($_SERVER[$k], $_ENV[$k]);
putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'baselineFile=' . urlencode($filename));
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';

@trigger_error('root deprecation', E_USER_DEPRECATED);

eval(<<<'EOPHP'
namespace PHPUnit\Util;
class Test
{
public static function getGroups()
{
return array();
}
}
EOPHP
);

class PHPUnit_Util_Test
{
public static function getGroups()
{
return array();
}
}

class FooTestCase
{
public function testLegacyFoo()
{
@trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
}

public function testNonLegacyBar()
{
@trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
}
}

$foo = new FooTestCase();
$foo->testLegacyFoo();
$foo->testNonLegacyBar();
?>
--EXPECTF--
Legacy deprecation notices (1)

Other deprecation notices (2)

1x: root deprecation

1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
79 changes: 79 additions & 0 deletions Tests/DeprecationErrorHandler/baseline3.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
--TEST--
Test DeprecationErrorHandler in baseline mode
--FILE--
<?php
$filename = tempnam(sys_get_temp_dir(), 'sf-');
$baseline = [[
'location' => 'FooTestCase::testLegacyFoo',
'message' => 'silenced foo deprecation',
'count' => 1,
]];
file_put_contents($filename, json_encode($baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));

$k = 'SYMFONY_DEPRECATIONS_HELPER';
unset($_SERVER[$k], $_ENV[$k]);
putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'baselineFile=' . urlencode($filename));
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';

@trigger_error('root deprecation', E_USER_DEPRECATED);

eval(<<<'EOPHP'
namespace PHPUnit\Util;
class Test
{
public static function getGroups()
{
return array();
}
}
EOPHP
);

class PHPUnit_Util_Test
{
public static function getGroups()
{
return array();
}
}

class FooTestCase
{
public function testLegacyFoo()
{
@trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
// This will cause a deprecation because the baseline only expects 1
// deprecation.
@trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
}

public function testNonLegacyBar()
{
@trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
}
}

$foo = new FooTestCase();
$foo->testLegacyFoo();
$foo->testNonLegacyBar();
?>
--EXPECTF--
Legacy deprecation notices (2)

Other deprecation notices (2)

1x: root deprecation

1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
41 changes: 41 additions & 0 deletions Tests/DeprecationErrorHandler/debug_class_loader_autoload.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
Test that a deprecation from the DebugClassLoader on a vendor class autoload triggered by an app class is considered indirect.
--FILE--
<?php

$k = 'SYMFONY_DEPRECATIONS_HELPER';
putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'max[total]=0');
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';
eval(<<<'EOPHP'
namespace PHPUnit\Util;
class Test
{
public static function getGroups()
{
return array();
}
}
EOPHP
);
require __DIR__.'/fake_vendor/autoload.php';

\Symfony\Component\ErrorHandler\DebugClassLoader::enable();
new \App\Services\BarService();

?>
--EXPECTF--
Remaining indirect deprecation notices (1)

1x: The "acme\lib\ExtendsDeprecatedClassFromOtherVendor" class extends "fcy\lib\DeprecatedClass" that is deprecated.
1x in BarService::__construct from App\Services
41 changes: 41 additions & 0 deletions Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
Test that a deprecation from the DebugClassLoader triggered by an app class extending a vendor one is considered direct.
--FILE--
<?php

$k = 'SYMFONY_DEPRECATIONS_HELPER';
putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'max[total]=0');
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';
eval(<<<'EOPHP'
namespace PHPUnit\Util;
class Test
{
public static function getGroups()
{
return array();
}
}
EOPHP
);
require __DIR__.'/fake_vendor/autoload.php';

\Symfony\Component\ErrorHandler\DebugClassLoader::enable();
new \App\Services\ExtendsDeprecatedFromVendor();

?>
--EXPECTF--
Remaining direct deprecation notices (1)

1x: The "App\Services\ExtendsDeprecatedFromVendor" class extends "fcy\lib\DeprecatedClass" that is deprecated.
1x in DebugClassLoader::loadClass from Symfony\Component\ErrorHandler
2 changes: 1 addition & 1 deletion Tests/DeprecationErrorHandler/deprecation/deprecation.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?php

@trigger_error('I come from… afar! :D', E_USER_DEPRECATED);
@trigger_error('I come from… afar! :D', \E_USER_DEPRECATED);
37 changes: 37 additions & 0 deletions Tests/DeprecationErrorHandler/disabled_1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Test DeprecationErrorHandler in default mode
--FILE--
<?php

$k = 'SYMFONY_DEPRECATIONS_HELPER';
putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'disabled=1');
putenv($k);
putenv('ANSICON');
putenv('ConEmuANSI');
putenv('TERM');

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
require_once __DIR__.'/../../bootstrap.php';

@trigger_error('root deprecation', E_USER_DEPRECATED);

eval(<<<'EOPHP'
namespace PHPUnit\Util;
class Test
{
public static function getGroups()
{
return array();
}
}
EOPHP
);
?>
--EXPECTREGEX--
.{0}
3 changes: 1 addition & 2 deletions Tests/DeprecationErrorHandler/fake_app/AppService.php
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ public function selfDeprecation(bool $useContracts = false)
if ($useContracts) {
trigger_deprecation('App', '3.0', sprintf('%s is deprecated, use %s_new instead.', ...$args));
} else {
@trigger_error(sprintf('Since App 3.0: %s is deprecated, use %s_new instead.', ...$args), E_USER_DEPRECATED);
@trigger_error(sprintf('Since App 3.0: %s is deprecated, use %s_new instead.', ...$args), \E_USER_DEPRECATED);
}
}

@@ -47,4 +47,3 @@ public function directDeprecations()
$service2->deprecatedApi();
}
}

13 changes: 13 additions & 0 deletions Tests/DeprecationErrorHandler/fake_app/BarService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Services;

use acme\lib\ExtendsDeprecatedClassFromOtherVendor;

final class BarService
{
public function __construct()
{
ExtendsDeprecatedClassFromOtherVendor::FOO;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Services;

use fcy\lib\DeprecatedClass;

final class ExtendsDeprecatedFromVendor extends DeprecatedClass
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace acme\lib;

use fcy\lib\DeprecatedClass;

final class ExtendsDeprecatedClassFromOtherVendor extends DeprecatedClass
{
public const FOO = 'bar';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace acme\lib;

class PhpDeprecation implements \Serializable
{
public function serialize(): string
{
return serialize([]);
}

public function unserialize($data): void
{
}
}
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ public function deprecatedApi(bool $useContracts = false)
if ($useContracts) {
trigger_deprecation('acme/lib', '3.0', sprintf('%s is deprecated, use %s_new instead.', ...$args));
} else {
@trigger_error(sprintf('Since acme/lib 3.0: %s is deprecated, use %s_new instead.', ...$args), E_USER_DEPRECATED);
@trigger_error(sprintf('Since acme/lib 3.0: %s is deprecated, use %s_new instead.', ...$args), \E_USER_DEPRECATED);
}
}

Loading