bug #33820 [PhpUnitBridge] Fix some errors when using serialized deprecations (l-vo)
This PR was submitted for the 4.3 branch but it was squashed and merged into the 4.4 branch instead.
Discussion
----------
[PhpUnitBridge] Fix some errors when using serialized deprecations
| Q | A
| ------------- | ---
| Branch? | 4.3
| Bug fix? | yes
| New feature? | no <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets | n/a
| License | MIT
| Doc PR | n/a
This PR attempts to fix conflicts that arose in #31478
Creating as a draft for now as I think having separate test methods no longer make sense (`isSelf()` and `isIndirect()` have been replaced with `getType()`). @l-vo please review and confirm I did not loose anything valuable from your original contribution.
Commits
-------
056d59824f
[PhpUnitBridge] Fix some errors when using serialized deprecations
This commit is contained in:
commit
84d32aca73
@ -104,7 +104,19 @@ class DeprecationErrorHandler
|
|||||||
return \call_user_func(self::getPhpUnitErrorHandler(), $type, $msg, $file, $line, $context);
|
return \call_user_func(self::getPhpUnitErrorHandler(), $type, $msg, $file, $line, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
$deprecations[] = [error_reporting(), $msg, $file];
|
$trace = debug_backtrace();
|
||||||
|
$filesStack = [];
|
||||||
|
foreach ($trace as $line) {
|
||||||
|
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($line['file'])) {
|
||||||
|
$filesStack[] = $line['file'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$deprecations[] = [error_reporting(), $msg, $file, $filesStack];
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -44,6 +44,8 @@ class Deprecation
|
|||||||
*/
|
*/
|
||||||
private static $internalPaths = [];
|
private static $internalPaths = [];
|
||||||
|
|
||||||
|
private $originalFilesStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param string $file
|
* @param string $file
|
||||||
@ -64,6 +66,7 @@ class Deprecation
|
|||||||
$this->message = $parsedMsg['deprecation'];
|
$this->message = $parsedMsg['deprecation'];
|
||||||
$this->originClass = $parsedMsg['class'];
|
$this->originClass = $parsedMsg['class'];
|
||||||
$this->originMethod = $parsedMsg['method'];
|
$this->originMethod = $parsedMsg['method'];
|
||||||
|
$this->originalFilesStack = $parsedMsg['files_stack'];
|
||||||
// If the deprecation has been triggered via
|
// If the deprecation has been triggered via
|
||||||
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
||||||
// then we need to use the serialized information to determine
|
// then we need to use the serialized information to determine
|
||||||
@ -162,6 +165,24 @@ class Deprecation
|
|||||||
return false !== strpos($this->triggeringFile, \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR.'phpunit'.\DIRECTORY_SEPARATOR);
|
return false !== strpos($this->triggeringFile, \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR.'phpunit'.\DIRECTORY_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getOriginalFilesStack(): array
|
||||||
|
{
|
||||||
|
if (null === $this->originalFilesStack) {
|
||||||
|
$this->originalFilesStack = [];
|
||||||
|
foreach ($this->trace as $line) {
|
||||||
|
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!isset($line['file'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->originalFilesStack[] = $line['file'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->originalFilesStack;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells whether both the calling package and the called package are vendor
|
* Tells whether both the calling package and the called package are vendor
|
||||||
* packages.
|
* packages.
|
||||||
@ -178,14 +199,8 @@ class Deprecation
|
|||||||
return self::TYPE_UNDETERMINED;
|
return self::TYPE_UNDETERMINED;
|
||||||
}
|
}
|
||||||
$erroringFile = $erroringPackage = null;
|
$erroringFile = $erroringPackage = null;
|
||||||
foreach ($this->trace as $line) {
|
|
||||||
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
|
foreach ($this->getOriginalFilesStack() as $file) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!isset($line['file'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$file = $line['file'];
|
|
||||||
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
|
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,32 @@
|
|||||||
|
|
||||||
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
|
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
|
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
|
||||||
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
|
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
|
||||||
|
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5;
|
||||||
|
|
||||||
class DeprecationTest extends TestCase
|
class DeprecationTest extends TestCase
|
||||||
{
|
{
|
||||||
|
public static function setUpBeforeClass(): void
|
||||||
|
{
|
||||||
|
$vendorDir = self::getVendorDir();
|
||||||
|
|
||||||
|
mkdir($vendorDir.'/myfakevendor/myfakepackage1', 0777, true);
|
||||||
|
mkdir($vendorDir.'/myfakevendor/myfakepackage2');
|
||||||
|
touch($vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php');
|
||||||
|
touch($vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile2.php');
|
||||||
|
touch($vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getVendorDir(): string
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass(ClassLoader::class);
|
||||||
|
|
||||||
|
return \dirname($reflection->getFileName(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
public function testItCanDetermineTheClassWhereTheDeprecationHappened()
|
public function testItCanDetermineTheClassWhereTheDeprecationHappened()
|
||||||
{
|
{
|
||||||
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||||
@ -118,12 +138,135 @@ class DeprecationTest extends TestCase
|
|||||||
$this->assertTrue($deprecation->isMuted());
|
$this->assertTrue($deprecation->isMuted());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function providerGetTypeDetectsSelf(): array
|
||||||
|
{
|
||||||
|
foreach (get_declared_classes() as $class) {
|
||||||
|
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
|
||||||
|
$r = new \ReflectionClass($class);
|
||||||
|
$v = \dirname(\dirname($r->getFileName()));
|
||||||
|
if (file_exists($v.'/composer/installed.json')) {
|
||||||
|
$loader = require $v.'/autoload.php';
|
||||||
|
$reflection = new \ReflectionClass($loader);
|
||||||
|
$prop = $reflection->getProperty('prefixDirsPsr4');
|
||||||
|
$prop->setAccessible(true);
|
||||||
|
$currentValue = $prop->getValue($loader);
|
||||||
|
$currentValue['Symfony\\Bridge\\PhpUnit\\'] = [realpath(__DIR__.'/../..')];
|
||||||
|
$prop->setValue($loader, $currentValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'not_from_vendors_file' => [Deprecation::TYPE_SELF, '', 'MyClass1', ''],
|
||||||
|
'nonexistent_file' => [Deprecation::TYPE_UNDETERMINED, '', 'MyClass1', 'dummy_vendor_path'],
|
||||||
|
'serialized_trace_with_nonexistent_triggering_file' => [
|
||||||
|
Deprecation::TYPE_UNDETERMINED,
|
||||||
|
serialize([
|
||||||
|
'class' => '',
|
||||||
|
'method' => '',
|
||||||
|
'deprecation' => '',
|
||||||
|
'triggering_file' => 'dummy_vendor_path',
|
||||||
|
'files_stack' => [],
|
||||||
|
]),
|
||||||
|
SymfonyTestsListenerForV5::class,
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerGetTypeDetectsSelf
|
||||||
|
*/
|
||||||
|
public function testGetTypeDetectsSelf(string $expectedType, string $message, string $traceClass, string $file): void
|
||||||
|
{
|
||||||
|
$trace = [
|
||||||
|
['class' => 'MyClass1', 'function' => 'myMethod'],
|
||||||
|
['class' => $traceClass, 'function' => 'myMethod'],
|
||||||
|
];
|
||||||
|
$deprecation = new Deprecation($message, $trace, $file);
|
||||||
|
$this->assertEquals($expectedType, $deprecation->getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerGetTypeUsesRightTrace(): array
|
||||||
|
{
|
||||||
|
$vendorDir = self::getVendorDir();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'no_file_in_stack' => [Deprecation::TYPE_DIRECT, '', [['function' => 'myfunc1'], ['function' => 'myfunc2']]],
|
||||||
|
'files_in_stack_from_various_packages' => [
|
||||||
|
Deprecation::TYPE_INDIRECT,
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
['function' => 'myfunc1', 'file' => $vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php'],
|
||||||
|
['function' => 'myfunc2', 'file' => $vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'serialized_stack_files_from_same_package' => [
|
||||||
|
Deprecation::TYPE_DIRECT,
|
||||||
|
serialize([
|
||||||
|
'deprecation' => 'My deprecation message',
|
||||||
|
'class' => 'MyClass',
|
||||||
|
'method' => 'myMethod',
|
||||||
|
'files_stack' => [
|
||||||
|
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php',
|
||||||
|
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile2.php',
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
[['function' => 'myfunc1'], ['class' => SymfonyTestsListenerForV5::class, 'method' => 'mymethod']],
|
||||||
|
],
|
||||||
|
'serialized_stack_files_from_various_packages' => [
|
||||||
|
Deprecation::TYPE_INDIRECT,
|
||||||
|
serialize([
|
||||||
|
'deprecation' => 'My deprecation message',
|
||||||
|
'class' => 'MyClass',
|
||||||
|
'method' => 'myMethod',
|
||||||
|
'files_stack' => [
|
||||||
|
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php',
|
||||||
|
$vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php',
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
[['function' => 'myfunc1'], ['class' => SymfonyTestsListenerForV5::class, 'method' => 'mymethod']],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerGetTypeUsesRightTrace
|
||||||
|
*/
|
||||||
|
public function testGetTypeUsesRightTrace(string $expectedType, string $message, array $trace): void
|
||||||
|
{
|
||||||
|
$deprecation = new Deprecation(
|
||||||
|
$message,
|
||||||
|
$trace,
|
||||||
|
self::getVendorDir().'/myfakevendor/myfakepackage2/MyFakeFile.php'
|
||||||
|
);
|
||||||
|
$this->assertEquals($expectedType, $deprecation->getType());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is here to simulate the extra level from the piece of code
|
* This method is here to simulate the extra level from the piece of code
|
||||||
* triggering an error to the error handler
|
* triggering an error to the error handler.
|
||||||
*/
|
*/
|
||||||
public function debugBacktrace(): array
|
public function debugBacktrace(): array
|
||||||
{
|
{
|
||||||
return debug_backtrace();
|
return debug_backtrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function removeDir($dir): void
|
||||||
|
{
|
||||||
|
$files = glob($dir.'/*');
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if (is_file($file)) {
|
||||||
|
unlink($file);
|
||||||
|
} else {
|
||||||
|
self::removeDir($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rmdir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass(): void
|
||||||
|
{
|
||||||
|
self::removeDir(self::getVendorDir().'/myfakevendor');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user