You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
7.0 KiB
240 lines
7.0 KiB
<?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\Framework;
|
|
|
|
use function assert;
|
|
use function count;
|
|
use function get_class;
|
|
use function sprintf;
|
|
use function trim;
|
|
use PHPUnit\Util\Filter;
|
|
use PHPUnit\Util\InvalidDataSetException;
|
|
use PHPUnit\Util\Test as TestUtil;
|
|
use ReflectionClass;
|
|
use Throwable;
|
|
|
|
/**
|
|
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
|
*/
|
|
final class TestBuilder
|
|
{
|
|
public function build(ReflectionClass $theClass, string $methodName): Test
|
|
{
|
|
$className = $theClass->getName();
|
|
|
|
if (!$theClass->isInstantiable()) {
|
|
return new ErrorTestCase(
|
|
sprintf('Cannot instantiate class "%s".', $className),
|
|
);
|
|
}
|
|
|
|
$backupSettings = TestUtil::getBackupSettings(
|
|
$className,
|
|
$methodName,
|
|
);
|
|
|
|
$preserveGlobalState = TestUtil::getPreserveGlobalStateSettings(
|
|
$className,
|
|
$methodName,
|
|
);
|
|
|
|
$runTestInSeparateProcess = TestUtil::getProcessIsolationSettings(
|
|
$className,
|
|
$methodName,
|
|
);
|
|
|
|
$runClassInSeparateProcess = TestUtil::getClassProcessIsolationSettings(
|
|
$className,
|
|
$methodName,
|
|
);
|
|
|
|
$constructor = $theClass->getConstructor();
|
|
|
|
if ($constructor === null) {
|
|
throw new Exception('No valid test provided.');
|
|
}
|
|
|
|
$parameters = $constructor->getParameters();
|
|
|
|
// TestCase() or TestCase($name)
|
|
if (count($parameters) < 2) {
|
|
$test = $this->buildTestWithoutData($className);
|
|
} // TestCase($name, $data)
|
|
else {
|
|
try {
|
|
$data = TestUtil::getProvidedData(
|
|
$className,
|
|
$methodName,
|
|
);
|
|
} catch (IncompleteTestError $e) {
|
|
$message = sprintf(
|
|
"Test for %s::%s marked incomplete by data provider\n%s",
|
|
$className,
|
|
$methodName,
|
|
$this->throwableToString($e),
|
|
);
|
|
|
|
$data = new IncompleteTestCase($className, $methodName, $message);
|
|
} catch (SkippedTestError $e) {
|
|
$message = sprintf(
|
|
"Test for %s::%s skipped by data provider\n%s",
|
|
$className,
|
|
$methodName,
|
|
$this->throwableToString($e),
|
|
);
|
|
|
|
$data = new SkippedTestCase($className, $methodName, $message);
|
|
} catch (Throwable $t) {
|
|
$message = sprintf(
|
|
"The data provider specified for %s::%s is invalid.\n%s",
|
|
$className,
|
|
$methodName,
|
|
$this->throwableToString($t),
|
|
);
|
|
|
|
$data = new ErrorTestCase($message);
|
|
}
|
|
|
|
// Test method with @dataProvider.
|
|
if (isset($data)) {
|
|
$test = $this->buildDataProviderTestSuite(
|
|
$methodName,
|
|
$className,
|
|
$data,
|
|
$runTestInSeparateProcess,
|
|
$preserveGlobalState,
|
|
$runClassInSeparateProcess,
|
|
$backupSettings,
|
|
);
|
|
} else {
|
|
$test = $this->buildTestWithoutData($className);
|
|
}
|
|
}
|
|
|
|
if ($test instanceof TestCase) {
|
|
$test->setName($methodName);
|
|
$this->configureTestCase(
|
|
$test,
|
|
$runTestInSeparateProcess,
|
|
$preserveGlobalState,
|
|
$runClassInSeparateProcess,
|
|
$backupSettings,
|
|
);
|
|
}
|
|
|
|
return $test;
|
|
}
|
|
|
|
/** @psalm-param class-string $className */
|
|
private function buildTestWithoutData(string $className)
|
|
{
|
|
return new $className;
|
|
}
|
|
|
|
/** @psalm-param class-string $className */
|
|
private function buildDataProviderTestSuite(
|
|
string $methodName,
|
|
string $className,
|
|
$data,
|
|
bool $runTestInSeparateProcess,
|
|
?bool $preserveGlobalState,
|
|
bool $runClassInSeparateProcess,
|
|
array $backupSettings
|
|
): DataProviderTestSuite {
|
|
$dataProviderTestSuite = new DataProviderTestSuite(
|
|
$className . '::' . $methodName,
|
|
);
|
|
|
|
$groups = TestUtil::getGroups($className, $methodName);
|
|
|
|
if ($data instanceof ErrorTestCase ||
|
|
$data instanceof SkippedTestCase ||
|
|
$data instanceof IncompleteTestCase) {
|
|
$dataProviderTestSuite->addTest($data, $groups);
|
|
} else {
|
|
foreach ($data as $_dataName => $_data) {
|
|
$_test = new $className($methodName, $_data, $_dataName);
|
|
|
|
assert($_test instanceof TestCase);
|
|
|
|
$this->configureTestCase(
|
|
$_test,
|
|
$runTestInSeparateProcess,
|
|
$preserveGlobalState,
|
|
$runClassInSeparateProcess,
|
|
$backupSettings,
|
|
);
|
|
|
|
$dataProviderTestSuite->addTest($_test, $groups);
|
|
}
|
|
}
|
|
|
|
return $dataProviderTestSuite;
|
|
}
|
|
|
|
private function configureTestCase(
|
|
TestCase $test,
|
|
bool $runTestInSeparateProcess,
|
|
?bool $preserveGlobalState,
|
|
bool $runClassInSeparateProcess,
|
|
array $backupSettings
|
|
): void {
|
|
if ($runTestInSeparateProcess) {
|
|
$test->setRunTestInSeparateProcess(true);
|
|
|
|
if ($preserveGlobalState !== null) {
|
|
$test->setPreserveGlobalState($preserveGlobalState);
|
|
}
|
|
}
|
|
|
|
if ($runClassInSeparateProcess) {
|
|
$test->setRunClassInSeparateProcess(true);
|
|
|
|
if ($preserveGlobalState !== null) {
|
|
$test->setPreserveGlobalState($preserveGlobalState);
|
|
}
|
|
}
|
|
|
|
if ($backupSettings['backupGlobals'] !== null) {
|
|
$test->setBackupGlobals($backupSettings['backupGlobals']);
|
|
}
|
|
|
|
if ($backupSettings['backupStaticAttributes'] !== null) {
|
|
$test->setBackupStaticAttributes(
|
|
$backupSettings['backupStaticAttributes'],
|
|
);
|
|
}
|
|
}
|
|
|
|
private function throwableToString(Throwable $t): string
|
|
{
|
|
$message = $t->getMessage();
|
|
|
|
if (empty(trim($message))) {
|
|
$message = '<no message>';
|
|
}
|
|
|
|
if ($t instanceof InvalidDataSetException) {
|
|
return sprintf(
|
|
"%s\n%s",
|
|
$message,
|
|
Filter::getFilteredStacktrace($t),
|
|
);
|
|
}
|
|
|
|
return sprintf(
|
|
"%s: %s\n%s",
|
|
get_class($t),
|
|
$message,
|
|
Filter::getFilteredStacktrace($t),
|
|
);
|
|
}
|
|
}
|