Revert "Revert "bug #31730 [PhpUnitBridge] More accurate grouping (greg0ire)""

This reverts commit 4814fd3a4e.
This commit is contained in:
Nicolas Grekas 2019-06-26 14:14:14 +02:00
parent 4814fd3a4e
commit 5797e7b2f2
9 changed files with 117 additions and 72 deletions

View File

@ -139,10 +139,13 @@ class DeprecationErrorHandler
$group = 'unsilenced';
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
$group = 'legacy';
} elseif (!$deprecation->isSelf()) {
$group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct';
} else {
$group = 'remaining self';
$group = [
Deprecation::TYPE_SELF => 'remaining self',
Deprecation::TYPE_DIRECT => 'remaining direct',
Deprecation::TYPE_INDIRECT => 'remaining indirect',
Deprecation::TYPE_UNDETERMINED => 'other',
][$deprecation->getType()];
}
if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) {

View File

@ -18,6 +18,15 @@ use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor;
*/
class Deprecation
{
const PATH_TYPE_VENDOR = 'path_type_vendor';
const PATH_TYPE_SELF = 'path_type_internal';
const PATH_TYPE_UNDETERMINED = 'path_type_undetermined';
const TYPE_SELF = 'type_self';
const TYPE_DIRECT = 'type_direct';
const TYPE_INDIRECT = 'type_indirect';
const TYPE_UNDETERMINED = 'type_undetermined';
/**
* @var array
*/
@ -39,13 +48,21 @@ class Deprecation
private $originMethod;
/**
* @var bool
* @var string one of the PATH_TYPE_* constants
*/
private $self;
private $triggeringFilePathType;
/** @var string[] absolute paths to vendor directories */
private static $vendors;
/**
* @var string[] absolute paths to source or tests of the project. This
* excludes cache directories, because it is based on
* autoloading rules and cache systems typically do not use
* those.
*/
private static $internalPaths;
/**
* @param string $message
* @param string $file
@ -59,7 +76,7 @@ class Deprecation
// No-op
}
$line = $trace[$i];
$this->self = !$this->pathOriginatesFromVendor($file);
$this->trigerringFilePathType = $this->getPathType($file);
if (isset($line['object']) || isset($line['class'])) {
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
$parsedMsg = unserialize($this->message);
@ -70,8 +87,9 @@ class Deprecation
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
// then we need to use the serialized information to determine
// if the error has been triggered from vendor code.
$this->self = isset($parsedMsg['triggering_file'])
&& $this->pathOriginatesFromVendor($parsedMsg['triggering_file']);
if (isset($parsedMsg['triggering_file'])) {
$this->trigerringFilePathType = $this->getPathType($parsedMsg['triggering_file']);
}
return;
}
@ -101,14 +119,6 @@ class Deprecation
return isset($this->originClass);
}
/**
* @return bool
*/
public function isSelf()
{
return $this->self;
}
/**
* @return string
*/
@ -163,10 +173,16 @@ class Deprecation
* Tells whether both the calling package and the called package are vendor
* packages.
*
* @return bool
* @return string
*/
public function isIndirect()
public function getType()
{
if (self::PATH_TYPE_SELF === $this->trigerringFilePathType) {
return self::TYPE_SELF;
}
if (self::PATH_TYPE_UNDETERMINED === $this->trigerringFilePathType) {
return self::TYPE_UNDETERMINED;
}
$erroringFile = $erroringPackage = null;
foreach ($this->trace as $line) {
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
@ -179,13 +195,16 @@ class Deprecation
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
continue;
}
if (!$this->pathOriginatesFromVendor($file)) {
return false;
if (self::PATH_TYPE_SELF === $this->getPathType($file)) {
return self::TYPE_DIRECT;
}
if (self::PATH_TYPE_UNDETERMINED === $this->getPathType($file)) {
return self::TYPE_UNDETERMINED;
}
if (null !== $erroringFile && null !== $erroringPackage) {
$package = $this->getPackage($file);
if ('composer' !== $package && $package !== $erroringPackage) {
return true;
return self::TYPE_INDIRECT;
}
continue;
}
@ -193,11 +212,11 @@ class Deprecation
$erroringPackage = $this->getPackage($file);
}
return false;
return self::TYPE_DIRECT;
}
/**
* pathOriginatesFromVendor() should always be called prior to calling this method.
* getPathType() should always be called prior to calling this method.
*
* @param string $path
*
@ -237,6 +256,15 @@ class Deprecation
$v = \dirname(\dirname($r->getFileName()));
if (file_exists($v.'/composer/installed.json')) {
self::$vendors[] = $v;
$loader = require $v.'/autoload.php';
$paths = self::getSourcePathsFromPrefixes(array_merge($loader->getPrefixes(), $loader->getPrefixesPsr4()));
}
}
}
foreach ($paths as $path) {
foreach (self::$vendors as $vendor) {
if (0 !== strpos($path, $vendor)) {
self::$internalPaths[] = $path;
}
}
}
@ -245,24 +273,41 @@ class Deprecation
return self::$vendors;
}
private static function getSourcePathsFromPrefixes(array $prefixesByNamespace)
{
foreach ($prefixesByNamespace as $prefixes) {
foreach ($prefixes as $prefix) {
if (false !== realpath($prefix)) {
yield realpath($prefix);
}
}
}
}
/**
* @param string $path
*
* @return bool
* @return string
*/
private function pathOriginatesFromVendor($path)
private function getPathType($path)
{
$realPath = realpath($path);
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
return true;
return self::PATH_TYPE_UNDETERMINED;
}
foreach (self::getVendors() as $vendor) {
if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
return true;
return self::PATH_TYPE_VENDOR;
}
}
return false;
foreach (self::$internalPaths as $internalPath) {
if (0 === strpos($realPath, $internalPath)) {
return self::PATH_TYPE_SELF;
}
}
return self::PATH_TYPE_UNDETERMINED;
}
/**
@ -281,19 +326,4 @@ class Deprecation
"\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $exception->getTraceAsString()).
"\n";
}
private function getPackageFromLine(array $line)
{
if (!isset($line['file'])) {
return 'internal function';
}
if (!$this->pathOriginatesFromVendor($line['file'])) {
return 'source code';
}
try {
return $this->getPackage($line['file']);
} catch (\RuntimeException $e) {
return 'unknown';
}
}
}

View File

@ -33,7 +33,7 @@ class DeprecationTest extends TestCase
}
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
$this->assertTrue($deprecation->isSelf());
$this->assertSame(Deprecation::TYPE_SELF, $deprecation->getType());
}
public function testLegacyTestMethodIsDetectedAsSuch()
@ -52,7 +52,7 @@ class DeprecationTest extends TestCase
public function testItRulesOutFilesOutsideVendorsAsIndirect()
{
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
$this->assertFalse($deprecation->isIndirect());
$this->assertNotSame(Deprecation::TYPE_INDIRECT, $deprecation->getType());
}
/**

View File

@ -73,15 +73,13 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation
I get precedence over any exit statements inside the deprecation error handler.

View File

@ -1,3 +1,5 @@
<?php
require_once __DIR__.'/composer/autoload_real.php';
return ComposerAutoloaderInitFake::getLoader();

View File

@ -1,5 +1,22 @@
<?php
class ComposerLoaderFake
{
public function getPrefixes()
{
return [];
}
public function getPrefixesPsr4()
{
return [];
}
}
class ComposerAutoloaderInitFake
{
public static function getLoader()
{
return new ComposerLoaderFake();
}
}

View File

@ -61,14 +61,12 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation

View File

@ -73,17 +73,15 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation
Shutdown-time deprecations:
Other deprecation notices (1)

View File

@ -61,14 +61,13 @@ Unsilenced deprecation notices (3)
1x: unsilenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Remaining self deprecation notices (1)
Legacy deprecation notices (1)
Other deprecation notices (2)
1x: root deprecation
1x: silenced bar deprecation
1x in FooTestCase::testNonLegacyBar
Legacy deprecation notices (1)
Other deprecation notices (1)
1x: root deprecation