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.0.6
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.0
Choose a head ref
Loading
Showing with 3,941 additions and 1,849 deletions.
  1. +4 −0 .gitattributes
  2. +43 −1 CHANGELOG.md
  3. +37 −11 ClassExistsMock.php
  4. +32 −8 ClockMock.php
  5. +9 −5 ConstraintTrait.php
  6. +102 −9 CoverageListener.php
  7. +179 −127 DeprecationErrorHandler.php
  8. +243 −66 DeprecationErrorHandler/Configuration.php
  9. +166 −100 DeprecationErrorHandler/Deprecation.php
  10. +57 −0 DeprecationErrorHandler/DeprecationGroup.php
  11. +49 −0 DeprecationErrorHandler/DeprecationNotice.php
  12. +21 −21 DnsMock.php
  13. +30 −0 ExpectDeprecationTrait.php
  14. +1 −1 LICENSE
  15. +0 −57 Legacy/CommandForV5.php
  16. +2 −7 Legacy/{CommandForV6.php → CommandForV7.php}
  17. +9 −8 Legacy/CommandForV9.php
  18. +62 −0 Legacy/ConstraintLogicTrait.php
  19. +0 −103 Legacy/ConstraintTraitForV6.php
  20. +21 −25 Legacy/ConstraintTraitForV7.php
  21. +53 −0 Legacy/ConstraintTraitForV8.php
  22. +50 −0 Legacy/ConstraintTraitForV9.php
  23. +0 −35 Legacy/CoverageListenerForV5.php
  24. +0 −41 Legacy/CoverageListenerForV6.php
  25. +0 −41 Legacy/CoverageListenerForV7.php
  26. +0 −119 Legacy/CoverageListenerTrait.php
  27. +45 −0 Legacy/ExpectDeprecationTraitBeforeV8_4.php
  28. +65 −0 Legacy/ExpectDeprecationTraitForV8_4.php
  29. +24 −313 Legacy/PolyfillAssertTrait.php
  30. +53 −46 Legacy/PolyfillTestCaseTrait.php
  31. +0 −70 Legacy/SetUpTearDownTraitForV5.php
  32. +0 −58 Legacy/SetUpTearDownTraitForV8.php
  33. +0 −54 Legacy/SymfonyTestsListenerForV5.php
  34. +0 −58 Legacy/SymfonyTestsListenerForV6.php
  35. +94 −69 Legacy/SymfonyTestsListenerTrait.php
  36. +7 −6 README.md
  37. +0 −28 SetUpTearDownTrait.php
  38. +1 −7 SymfonyTestsListener.php
  39. +46 −0 Tests/ClassExistsMockTest.php
  40. +10 −0 Tests/ClockMockTest.php
  41. +20 −5 Tests/CoverageListenerTest.php
  42. +526 −101 Tests/DeprecationErrorHandler/ConfigurationTest.php
  43. +30 −0 Tests/DeprecationErrorHandler/DeprecationGroupTest.php
  44. +35 −0 Tests/DeprecationErrorHandler/DeprecationNoticeTest.php
  45. +43 −30 Tests/DeprecationErrorHandler/DeprecationTest.php
  46. +81 −0 Tests/DeprecationErrorHandler/baseline.phpt
  47. +76 −0 Tests/DeprecationErrorHandler/baseline2.phpt
  48. +79 −0 Tests/DeprecationErrorHandler/baseline3.phpt
  49. +41 −0 Tests/DeprecationErrorHandler/debug_class_loader_autoload.phpt
  50. +41 −0 Tests/DeprecationErrorHandler/debug_class_loader_deprecation.phpt
  51. +1 −1 Tests/DeprecationErrorHandler/deprecation/deprecation.php
  52. +37 −0 Tests/DeprecationErrorHandler/disabled_1.phpt
  53. +49 −0 Tests/DeprecationErrorHandler/fake_app/AppService.php
  54. +13 −0 Tests/DeprecationErrorHandler/fake_app/BarService.php
  55. +9 −0 Tests/DeprecationErrorHandler/fake_app/ExtendsDeprecatedFromVendor.php
  56. +10 −0 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/ExtendsDeprecatedClassFromOtherVendor.php
  57. +15 −0 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/PhpDeprecation.php
  58. +14 −5 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php
  59. +7 −7 Tests/DeprecationErrorHandler/fake_vendor/acme/lib/deprecation_riddled.php
  60. +16 −0 Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php
  61. +39 −2 Tests/DeprecationErrorHandler/fake_vendor/composer/autoload_real.php
  62. +10 −0 Tests/DeprecationErrorHandler/fake_vendor/fcy/lib/DeprecatedClass.php
  63. +5 −0 Tests/DeprecationErrorHandler/fake_vendor_bis/autoload.php
  64. +47 −0 Tests/DeprecationErrorHandler/fake_vendor_bis/composer/autoload_real.php
  65. +1 −0 Tests/DeprecationErrorHandler/fake_vendor_bis/composer/installed.json
  66. +14 −0 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. +1 −1 Tests/DeprecationErrorHandler/lagging_vendor.phpt
  71. +58 −0 Tests/DeprecationErrorHandler/log_file.phpt
  72. +45 −0 Tests/DeprecationErrorHandler/multiple_autoloads.phpt
  73. +35 −0 Tests/DeprecationErrorHandler/partially_quiet.phpt
  74. +36 −0 Tests/DeprecationErrorHandler/partially_quiet2.phpt
  75. +43 −0 Tests/DeprecationErrorHandler/php_deprecation_from_vendor_class.phpt
  76. +1 −1 Tests/DeprecationErrorHandler/quiet.phpt
  77. +39 −0 Tests/DeprecationErrorHandler/quiet_but_failing.phpt
  78. +58 −0 Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt
  79. +58 −0 Tests/DeprecationErrorHandler/trigger_error_types.phpt
  80. +3 −5 Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt
  81. +3 −3 Tests/DnsMockTest.php
  82. +82 −0 Tests/EnumExistsMockTest.php
  83. +89 −0 Tests/ExpectDeprecationTraitTest.php
  84. +3 −3 Tests/ExpectedDeprecationAnnotationTest.php
  85. +50 −0 Tests/FailTests/ExpectDeprecationTraitTestFail.php
  86. +32 −0 Tests/FailTests/NoAssertionsTestNotRisky.php
  87. +44 −0 Tests/FailTests/NoAssertionsTestRisky.php
  88. +16 −0 Tests/Fixtures/ExistingEnum.php
  89. +16 −0 Tests/Fixtures/ExistingEnumReal.php
  90. +1 −1 Tests/Fixtures/coverage/tests/BarCovTest.php
  91. 0 Tests/Fixtures/coverage/tests/{SutNotFindTest.php → SutNotFoundTest.php}
  92. +0 −10 Tests/Fixtures/coverage/tests/bootstrap.php
  93. +12 −3 Tests/ProcessIsolationTest.php
  94. +37 −0 Tests/expectdeprecationfail.phpt
  95. +18 −0 Tests/expectnotrisky.phpt
  96. +24 −0 Tests/expectrisky.phpt
  97. +2 −4 TextUI/Command.php
  98. +177 −55 bin/simple-phpunit.php
  99. +18 −97 bootstrap.php
  100. +10 −10 composer.json
  101. +9 −9 phpunit.xml.dist
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
44 changes: 43 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,48 @@
CHANGELOG
=========

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

* ignore verbosity settings when the build fails because of deprecations
* added per-group verbosity
* added `ExpectDeprecationTrait` to be able to define an expected deprecation from inside a test
* deprecated the `@expectedDeprecation` annotation, use the `ExpectDeprecationTrait::expectDeprecation()` method instead

5.0.0
-----

@@ -17,7 +59,7 @@ CHANGELOG
-----

* added `ClassExistsMock`
* bumped PHP version from 5.3.3 to 5.5.9
* bumped PHP version from 5.3.3 to 5.5.9
* split simple-phpunit bin into php file with code and a shell script

4.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;
}
40 changes: 32 additions & 8 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) {
@@ -72,7 +75,7 @@ public static function microtime($asFloat = false)
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
);
}
14 changes: 9 additions & 5 deletions ConstraintTrait.php
Original file line number Diff line number Diff line change
@@ -12,17 +12,21 @@
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()) {
$r = new \ReflectionClass(Constraint::class);
if ($r->getProperty('exporter')->isProtected()) {
trait ConstraintTrait
{
use Legacy\ConstraintTraitForV6;
use Legacy\ConstraintTraitForV7;
}
} elseif (!$r->getMethod('evaluate')->hasReturnType()) {
trait ConstraintTrait
{
use Legacy\ConstraintTraitForV8;
}
} else {
trait ConstraintTrait
{
use Legacy\ConstraintTraitForV7;
use Legacy\ConstraintTraitForV9;
}
}
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,
]));
}
}
Loading