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);
|
||||
}
|
||||
|
||||
$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;
|
||||
});
|
||||
|
@ -44,6 +44,8 @@ class Deprecation
|
||||
*/
|
||||
private static $internalPaths = [];
|
||||
|
||||
private $originalFilesStack;
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param string $file
|
||||
@ -64,6 +66,7 @@ class Deprecation
|
||||
$this->message = $parsedMsg['deprecation'];
|
||||
$this->originClass = $parsedMsg['class'];
|
||||
$this->originMethod = $parsedMsg['method'];
|
||||
$this->originalFilesStack = $parsedMsg['files_stack'];
|
||||
// If the deprecation has been triggered via
|
||||
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
* packages.
|
||||
@ -178,14 +199,8 @@ class Deprecation
|
||||
return self::TYPE_UNDETERMINED;
|
||||
}
|
||||
$erroringFile = $erroringPackage = null;
|
||||
foreach ($this->trace as $line) {
|
||||
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($line['file'])) {
|
||||
continue;
|
||||
}
|
||||
$file = $line['file'];
|
||||
|
||||
foreach ($this->getOriginalFilesStack() as $file) {
|
||||
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -11,12 +11,32 @@
|
||||
|
||||
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
|
||||
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
|
||||
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5;
|
||||
|
||||
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()
|
||||
{
|
||||
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
|
||||
@ -118,12 +138,135 @@ class DeprecationTest extends TestCase
|
||||
$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
|
||||
* triggering an error to the error handler
|
||||
* triggering an error to the error handler.
|
||||
*/
|
||||
public function debugBacktrace(): array
|
||||
{
|
||||
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