Skip to content

Commit

Permalink
feature #35271 [PHPUnitBridge] Improved deprecations display (greg0ire)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 5.1-dev branch (closes #35271).

Discussion
----------

[PHPUnitBridge] Improved deprecations display

| Q             | A
| ------------- | ---
| Branch?       | master for features
| Bug fix?      | no
| New feature?  | yes <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets       | n/a
| License       | MIT
| Doc PR        | todo

- [x] Ignore verbose when the build fails because of deprecations
- [x] Add per-group verbosity : it is now possible to make the report a lot quieter, by specifying which groups should become quiet, like this: `quiet[]=indirect&quiet[]=direct`

Might add more improvements to that branch if people suggest some.

Commits
-------

c55a89e [PHPUnitBridge] Improved deprecations display
  • Loading branch information
fabpot committed Jan 29, 2020
2 parents 5b38f70 + c55a89e commit 7ecb5aa
Show file tree
Hide file tree
Showing 10 changed files with 517 additions and 166 deletions.
6 changes: 6 additions & 0 deletions src/Symfony/Bridge/PhpUnit/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========

5.1.0
-----

* ignore verbosity settings when the build fails because of deprecations
* added per-group verbosity

5.0.0
-----

Expand Down
106 changes: 58 additions & 48 deletions src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
Expand Up @@ -15,6 +15,7 @@
use PHPUnit\Util\ErrorHandler;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\DeprecationGroup;
use Symfony\Component\ErrorHandler\DebugClassLoader;

/**
Expand All @@ -30,24 +31,20 @@ class DeprecationErrorHandler

private $mode;
private $configuration;
private $deprecations = [
'unsilencedCount' => 0,
'remaining selfCount' => 0,
'legacyCount' => 0,
'otherCount' => 0,
'remaining directCount' => 0,
'remaining indirectCount' => 0,
'unsilenced' => [],
'remaining self' => [],
'legacy' => [],
'other' => [],
'remaining direct' => [],
'remaining indirect' => [],
];

/**
* @var DeprecationGroup[]
*/
private $deprecationGroups = [];

private static $isRegistered = false;
private static $isAtLeastPhpUnit83;

public function __construct()
{
$this->resetDeprecationGroups();
}

/**
* Registers and configures the deprecation handler.
*
Expand Down Expand Up @@ -135,9 +132,9 @@ public function handleError($type, $msg, $file, $line, $context = [])
$group = 'legacy';
} else {
$group = [
Deprecation::TYPE_SELF => 'remaining self',
Deprecation::TYPE_DIRECT => 'remaining direct',
Deprecation::TYPE_INDIRECT => 'remaining indirect',
Deprecation::TYPE_SELF => 'self',
Deprecation::TYPE_DIRECT => 'direct',
Deprecation::TYPE_INDIRECT => 'indirect',
Deprecation::TYPE_UNDETERMINED => 'other',
][$deprecation->getType()];
}
Expand All @@ -148,18 +145,14 @@ public function handleError($type, $msg, $file, $line, $context = [])
exit(1);
}
if ('legacy' !== $group) {
$ref = &$this->deprecations[$group][$msg]['count'];
++$ref;
$ref = &$this->deprecations[$group][$msg][$class.'::'.$method];
++$ref;
$this->deprecationGroups[$group]->addNoticeFromObject($msg, $class, $method);
} else {
$this->deprecationGroups[$group]->addNotice();
}
} else {
$ref = &$this->deprecations[$group][$msg]['count'];
++$ref;
$this->deprecationGroups[$group]->addNoticeFromProceduralCode($msg);
}

++$this->deprecations[$group.'Count'];

return null;
}

Expand All @@ -184,34 +177,44 @@ public function shutdown()
echo "\n", self::colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n";
}

$groups = ['unsilenced', 'remaining self', 'remaining direct', 'remaining indirect', 'legacy', 'other'];

$this->displayDeprecations($groups, $configuration);
$groups = array_keys($this->deprecationGroups);

// store failing status
$isFailing = !$configuration->tolerates($this->deprecations);
$isFailing = !$configuration->tolerates($this->deprecationGroups);

// reset deprecations array
foreach ($this->deprecations as $group => $arrayOrInt) {
$this->deprecations[$group] = \is_int($arrayOrInt) ? 0 : [];
}
$this->displayDeprecations($groups, $configuration, $isFailing);

$this->resetDeprecationGroups();

register_shutdown_function(function () use ($isFailing, $groups, $configuration) {
foreach ($this->deprecations as $group => $arrayOrInt) {
if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) {
foreach ($this->deprecationGroups as $group) {
if ($group->count() > 0) {
echo "Shutdown-time deprecations:\n";
break;
}
}

$this->displayDeprecations($groups, $configuration);
$isFailingAtShutdown = !$configuration->tolerates($this->deprecationGroups);
$this->displayDeprecations($groups, $configuration, $isFailingAtShutdown);

if ($isFailing || !$configuration->tolerates($this->deprecations)) {
if ($isFailing || $isFailingAtShutdown) {
exit(1);
}
});
}

private function resetDeprecationGroups()
{
$this->deprecationGroups = [
'unsilenced' => new DeprecationGroup(),
'self' => new DeprecationGroup(),
'direct' => new DeprecationGroup(),
'indirect' => new DeprecationGroup(),
'legacy' => new DeprecationGroup(),
'other' => new DeprecationGroup(),
];
}

private function getConfiguration()
{
if (null !== $this->configuration) {
Expand Down Expand Up @@ -270,31 +273,38 @@ private static function colorize($str, $red)
/**
* @param string[] $groups
* @param Configuration $configuration
* @param bool $isFailing
*/
private function displayDeprecations($groups, $configuration)
private function displayDeprecations($groups, $configuration, $isFailing)
{
$cmp = function ($a, $b) {
return $b['count'] - $a['count'];
return $b->count() - $a->count();
};

foreach ($groups as $group) {
if ($this->deprecations[$group.'Count']) {
if ($this->deprecationGroups[$group]->count()) {
echo "\n", self::colorize(
sprintf('%s deprecation notices (%d)', ucfirst($group), $this->deprecations[$group.'Count']),
'legacy' !== $group && 'remaining indirect' !== $group
sprintf(
'%s deprecation notices (%d)',
\in_array($group, ['direct', 'indirect', 'self'], true) ? "Remaining $group" : ucfirst($group),
$this->deprecationGroups[$group]->count()
),
'legacy' !== $group && 'indirect' !== $group
), "\n";

if (!$configuration->verboseOutput()) {
if ('legacy' !== $group && !$configuration->verboseOutput($group) && !$isFailing) {
continue;
}
uasort($this->deprecations[$group], $cmp);
$notices = $this->deprecationGroups[$group]->notices();
uasort($notices, $cmp);

foreach ($this->deprecations[$group] as $msg => $notices) {
echo "\n ", $notices['count'], 'x: ', $msg, "\n";
foreach ($notices as $msg => $notice) {
echo "\n ", $notice->count(), 'x: ', $msg, "\n";

arsort($notices);
$countsByCaller = $notice->getCountsByCaller();
arsort($countsByCaller);

foreach ($notices as $method => $count) {
foreach ($countsByCaller as $method => $count) {
if ('count' !== $method) {
echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
}
Expand Down
Expand Up @@ -32,17 +32,17 @@ class Configuration
private $enabled = true;

/**
* @var bool
* @var bool[]
*/
private $verboseOutput = true;
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
* @param bool[] $verboseOutput Keyed by groups
*/
private function __construct(array $thresholds = [], $regex = '', $verboseOutput = true)
private function __construct(array $thresholds = [], $regex = '', $verboseOutput = [])
{
$groups = ['total', 'indirect', 'direct', 'self'];

Expand Down Expand Up @@ -72,7 +72,21 @@ private function __construct(array $thresholds = [], $regex = '', $verboseOutput
}
}
$this->regex = $regex;
$this->verboseOutput = $verboseOutput;

$this->verboseOutput = [
'unsilenced' => true,
'direct' => true,
'indirect' => true,
'self' => true,
'other' => true,
];

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))));
}
$this->verboseOutput[$group] = (bool) $status;
}
}

/**
Expand All @@ -84,24 +98,26 @@ public function isEnabled()
}

/**
* @param mixed[] $deprecations
* @param DeprecationGroup[] $deprecationGroups
*
* @return bool
*/
public function tolerates(array $deprecations)
public function tolerates(array $deprecationGroups)
{
$deprecationCounts = [];
foreach ($deprecations as $key => $deprecation) {
if (false !== strpos($key, 'Count') && false === strpos($key, 'legacy')) {
$deprecationCounts[$key] = $deprecation;
$grandTotal = 0;

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

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

foreach (['self', 'direct', 'indirect'] as $deprecationType) {
if ($deprecationCounts['remaining '.$deprecationType.'Count'] > $this->thresholds[$deprecationType]) {
if ($deprecationGroups[$deprecationType]->count() > $this->thresholds[$deprecationType]) {
return false;
}
}
Expand Down Expand Up @@ -130,9 +146,9 @@ public function isInRegexMode()
/**
* @return bool
*/
public function verboseOutput()
public function verboseOutput($group)
{
return $this->verboseOutput;
return $this->verboseOutput[$group];
}

/**
Expand All @@ -145,7 +161,7 @@ public static function fromUrlEncodedString($serializedConfiguration)
{
parse_str($serializedConfiguration, $normalizedConfiguration);
foreach (array_keys($normalizedConfiguration) as $key) {
if (!\in_array($key, ['max', 'disabled', 'verbose'], true)) {
if (!\in_array($key, ['max', 'disabled', 'verbose', 'quiet'], true)) {
throw new \InvalidArgumentException(sprintf('Unknown configuration option "%s"', $key));
}
}
Expand All @@ -154,9 +170,19 @@ public static function fromUrlEncodedString($serializedConfiguration)
return self::inDisabledMode();
}

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

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

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

return new self(
Expand Down Expand Up @@ -190,7 +216,12 @@ public static function inStrictMode()
*/
public static function inWeakMode()
{
return new self([], '', false);
$verboseOutput = [];
foreach (['unsilenced', 'direct', 'indirect', 'self', 'other'] as $group) {
$verboseOutput[$group] = false;
}

return new self([], '', $verboseOutput);
}

/**
Expand Down

0 comments on commit 7ecb5aa

Please sign in to comment.