Files
farmOS-dev/vendor/brianium/paratest/src/Parser/Parser.php
2024-12-10 15:08:16 +01:00

132 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace ParaTest\Parser;
use InvalidArgumentException;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\Exception;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Util\Test as TestUtil;
use ReflectionClass;
use ReflectionMethod;
use function array_diff;
use function assert;
use function get_declared_classes;
use function is_file;
use function realpath;
/** @internal */
final class Parser
{
/** @var ReflectionClass<TestCase> */
private $refl;
/** @var ReflectionClass<TestCase>[] */
private static $alreadyLoadedSources = [];
/** @var class-string[] */
private static $externalClassesFound = [];
public function __construct(string $srcPath)
{
if (! is_file($srcPath)) {
throw new InvalidArgumentException('file not found: ' . $srcPath);
}
$srcPath = realpath($srcPath);
assert($srcPath !== false);
if (! isset(self::$alreadyLoadedSources[$srcPath])) {
$declaredClasses = get_declared_classes();
try {
$refClass = (new StandardTestSuiteLoader())->load($srcPath);
if (! $refClass->isSubclassOf(TestCase::class)) {
throw new NoClassInFileException($srcPath);
}
self::$alreadyLoadedSources[$srcPath] = $refClass;
self::$externalClassesFound += array_diff(
get_declared_classes(),
$declaredClasses,
[self::$alreadyLoadedSources[$srcPath]->getName()],
);
} catch (Exception $exception) {
self::$externalClassesFound += array_diff(get_declared_classes(), $declaredClasses);
$reflFound = null;
foreach (self::$externalClassesFound as $newClass) {
$refClass = new ReflectionClass($newClass);
if ($refClass->getFileName() !== $srcPath) {
continue;
}
$reflFound = $refClass;
break;
}
if ($reflFound === null || ! $reflFound->isSubclassOf(TestCase::class) || $reflFound->isAbstract()) {
throw new NoClassInFileException($srcPath, 0, $exception);
}
self::$alreadyLoadedSources[$srcPath] = $reflFound;
}
}
$this->refl = self::$alreadyLoadedSources[$srcPath];
}
/**
* Returns the fully constructed class
* with methods or null if the class is abstract.
*/
public function getClass(): ParsedClass
{
$parentsCount = 0;
$class = $this->refl;
while (($parent = $class->getParentClass()) !== false) {
++$parentsCount;
$class = $parent;
}
return new ParsedClass(
$this->refl->getName(),
$this->getMethods(),
$parentsCount,
);
}
/**
* Return all test methods present in the file.
*
* @return ReflectionMethod[]
* @psalm-return list<ReflectionMethod>
*/
private function getMethods(): array
{
$methods = [];
// @see \PHPUnit\Framework\TestSuite::__construct
foreach ($this->refl->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() === Assert::class) {
continue;
}
if ($method->getDeclaringClass()->getName() === TestCase::class) {
continue;
}
if (! TestUtil::isTestMethod($method)) {
continue;
}
$methods[] = $method;
}
return $methods;
}
}