Viewing file: NamePrettifier.php (8.73 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Logging\TestDox;
use function array_key_exists; use function array_keys; use function array_map; use function array_pop; use function array_values; use function assert; use function class_exists; use function explode; use function gettype; use function implode; use function is_bool; use function is_float; use function is_int; use function is_object; use function is_scalar; use function method_exists; use function preg_quote; use function preg_replace; use function rtrim; use function sprintf; use function str_contains; use function str_ends_with; use function str_replace; use function str_starts_with; use function strlen; use function strtolower; use function strtoupper; use function substr; use function trim; use PHPUnit\Framework\TestCase; use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; use PHPUnit\Metadata\TestDox; use PHPUnit\Util\Color; use ReflectionEnum; use ReflectionMethod; use ReflectionObject; use SebastianBergmann\Exporter\Exporter;
/** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class NamePrettifier { /** * @psalm-var array<string, int> */ private static array $strings = [];
/** * @psalm-param class-string $className */ public function prettifyTestClassName(string $className): string { if (class_exists($className)) { $classLevelTestDox = MetadataRegistry::parser()->forClass($className)->isTestDox();
if ($classLevelTestDox->isNotEmpty()) { $classLevelTestDox = $classLevelTestDox->asArray()[0];
assert($classLevelTestDox instanceof TestDox);
return $classLevelTestDox->text(); } }
$parts = explode('\\', $className); $className = array_pop($parts);
if (str_ends_with($className, 'Test')) { $className = substr($className, 0, strlen($className) - strlen('Test')); }
if (str_starts_with($className, 'Tests')) { $className = substr($className, strlen('Tests')); } elseif (str_starts_with($className, 'Test')) { $className = substr($className, strlen('Test')); }
if (empty($className)) { $className = 'UnnamedTests'; }
if (!empty($parts)) { $parts[] = $className; $fullyQualifiedName = implode('\\', $parts); } else { $fullyQualifiedName = $className; }
$result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className);
if ($fullyQualifiedName !== $className) { return $result . ' (' . $fullyQualifiedName . ')'; }
return $result; }
// NOTE: this method is on a hot path and very performance sensitive. change with care. public function prettifyTestMethodName(string $name): string { if ($name === '') { return ''; }
$string = rtrim($name, '0123456789');
if (array_key_exists($string, self::$strings)) { $name = $string; } elseif ($string === $name) { self::$strings[$string] = 1; }
if (str_starts_with($name, 'test_')) { $name = substr($name, 5); } elseif (str_starts_with($name, 'test')) { $name = substr($name, 4); }
if ($name === '') { return ''; }
$name[0] = strtoupper($name[0]);
$noUnderscore = str_replace('_', ' ', $name);
if ($noUnderscore !== $name) { return trim($noUnderscore); }
$wasNumeric = false;
$buffer = '';
$len = strlen($name);
for ($i = 0; $i < $len; $i++) { if ($i > 0 && $name[$i] >= 'A' && $name[$i] <= 'Z') { $buffer .= ' ' . strtolower($name[$i]); } else { $isNumeric = $name[$i] >= '0' && $name[$i] <= '9';
if (!$wasNumeric && $isNumeric) { $buffer .= ' '; $wasNumeric = true; }
if ($wasNumeric && !$isNumeric) { $wasNumeric = false; }
$buffer .= $name[$i]; } }
return $buffer; }
public function prettifyTestCase(TestCase $test, bool $colorize): string { $annotationWithPlaceholders = false; $methodLevelTestDox = MetadataRegistry::parser()->forMethod($test::class, $test->name())->isTestDox()->isMethodLevel();
if ($methodLevelTestDox->isNotEmpty()) { $methodLevelTestDox = $methodLevelTestDox->asArray()[0];
assert($methodLevelTestDox instanceof TestDox);
$result = $methodLevelTestDox->text();
if (str_contains($result, '$')) { $annotation = $result; $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test, $colorize);
$variables = array_map( static fn (string $variable): string => sprintf( '/%s(?=\b)/', preg_quote($variable, '/'), ), array_keys($providedData), );
$result = preg_replace($variables, $providedData, $annotation);
$annotationWithPlaceholders = true; } } else { $result = $this->prettifyTestMethodName($test->name()); }
if (!$annotationWithPlaceholders && $test->usesDataProvider()) { $result .= $this->prettifyDataSet($test, $colorize); }
return $result; }
public function prettifyDataSet(TestCase $test, bool $colorize): string { if (!$colorize) { return $test->dataSetAsString(); }
if (is_int($test->dataName())) { return Color::dim(' with data set ') . Color::colorize('fg-cyan', (string) $test->dataName()); }
return Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace($test->dataName())); }
private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test, bool $colorize): array { assert(method_exists($test, $test->name()));
/** @noinspection PhpUnhandledExceptionInspection */ $reflector = new ReflectionMethod($test::class, $test->name());
$providedData = []; $providedDataValues = array_values($test->providedData()); $i = 0;
$providedData['$_dataName'] = $test->dataName();
foreach ($reflector->getParameters() as $parameter) { if (!array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) { $providedDataValues[$i] = $parameter->getDefaultValue(); }
$value = $providedDataValues[$i++] ?? null;
if (is_object($value)) { $value = $this->objectToString($value); }
if (!is_scalar($value)) { $value = gettype($value);
if ($value === 'NULL') { $value = 'null'; } }
if (is_bool($value) || is_int($value) || is_float($value)) { $value = (new Exporter)->export($value); }
if ($value === '') { if ($colorize) { $value = Color::colorize('dim,underlined', 'empty'); } else { $value = "''"; } }
$providedData['$' . $parameter->getName()] = str_replace('$', '\\$', $value); }
if ($colorize) { $providedData = array_map( static fn ($value) => Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, true)), $providedData, ); }
return $providedData; }
/** * @return non-empty-string */ private function objectToString(object $value): string { $reflector = new ReflectionObject($value);
if ($reflector->isEnum()) { $enumReflector = new ReflectionEnum($value);
if ($enumReflector->isBacked()) { return (string) $value->value; }
return $value->name; }
if ($reflector->hasMethod('__toString')) { return $value->__toString(); }
return $value::class; } }
|