%PDF- %PDF-
Direktori : /home/silvzytp/dsr_code/vendor/phpunit/phpunit/src/Framework/ |
Current File : //home/silvzytp/dsr_code/vendor/phpunit/phpunit/src/Framework/TestSuite.php |
<?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 const PHP_EOL; use function array_keys; use function array_map; use function array_merge; use function array_slice; use function array_unique; use function basename; use function call_user_func; use function class_exists; use function count; use function dirname; use function get_declared_classes; use function implode; use function is_bool; use function is_callable; use function is_file; use function is_object; use function is_string; use function method_exists; use function preg_match; use function preg_quote; use function sprintf; use function strpos; use function substr; use Iterator; use IteratorAggregate; use PHPUnit\Runner\BaseTestRunner; use PHPUnit\Runner\Filter\Factory; use PHPUnit\Runner\PhptTestCase; use PHPUnit\Util\FileLoader; use PHPUnit\Util\Reflection; use PHPUnit\Util\Test as TestUtil; use ReflectionClass; use ReflectionException; use ReflectionMethod; use Throwable; /** * @template-implements IteratorAggregate<int, Test> * * @internal This class is not covered by the backward compatibility promise for PHPUnit */ class TestSuite implements IteratorAggregate, Reorderable, SelfDescribing, Test { /** * Enable or disable the backup and restoration of the $GLOBALS array. * * @var bool */ protected $backupGlobals; /** * Enable or disable the backup and restoration of static attributes. * * @var bool */ protected $backupStaticAttributes; /** * @var bool */ protected $runTestInSeparateProcess = false; /** * The name of the test suite. * * @var string */ protected $name = ''; /** * The test groups of the test suite. * * @psalm-var array<string,list<Test>> */ protected $groups = []; /** * The tests in the test suite. * * @var Test[] */ protected $tests = []; /** * The number of tests in the test suite. * * @var int */ protected $numTests = -1; /** * @var bool */ protected $testCase = false; /** * @var string[] */ protected $foundClasses = []; /** * @var null|list<ExecutionOrderDependency> */ protected $providedTests; /** * @var null|list<ExecutionOrderDependency> */ protected $requiredTests; /** * @var bool */ private $beStrictAboutChangesToGlobalState; /** * @var Factory */ private $iteratorFilter; /** * @var int */ private $declaredClassesPointer; /** * @psalm-var array<int,string> */ private $warnings = []; /** * Constructs a new TestSuite. * * - PHPUnit\Framework\TestSuite() constructs an empty TestSuite. * * - PHPUnit\Framework\TestSuite(ReflectionClass) constructs a * TestSuite from the given class. * * - PHPUnit\Framework\TestSuite(ReflectionClass, String) * constructs a TestSuite from the given class with the given * name. * * - PHPUnit\Framework\TestSuite(String) either constructs a * TestSuite from the given class (if the passed string is the * name of an existing class) or constructs an empty TestSuite * with the given name. * * @param ReflectionClass|string $theClass * * @throws Exception */ public function __construct($theClass = '', string $name = '') { if (!is_string($theClass) && !$theClass instanceof ReflectionClass) { throw InvalidArgumentException::create( 1, 'ReflectionClass object or string' ); } $this->declaredClassesPointer = count(get_declared_classes()); if (!$theClass instanceof ReflectionClass) { if (class_exists($theClass, true)) { if ($name === '') { $name = $theClass; } try { $theClass = new ReflectionClass($theClass); } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } else { $this->setName($theClass); return; } } if (!$theClass->isSubclassOf(TestCase::class)) { $this->setName((string) $theClass); return; } if ($name !== '') { $this->setName($name); } else { $this->setName($theClass->getName()); } $constructor = $theClass->getConstructor(); if ($constructor !== null && !$constructor->isPublic()) { $this->addTest( new WarningTestCase( sprintf( 'Class "%s" has no public constructor.', $theClass->getName() ) ) ); return; } foreach ((new Reflection)->publicMethodsInTestClass($theClass) as $method) { if (!TestUtil::isTestMethod($method)) { continue; } $this->addTestMethod($theClass, $method); } if (empty($this->tests)) { $this->addTest( new WarningTestCase( sprintf( 'No tests found in class "%s".', $theClass->getName() ) ) ); } $this->testCase = true; } /** * Returns a string representation of the test suite. */ public function toString(): string { return $this->getName(); } /** * Adds a test to the suite. * * @param array $groups */ public function addTest(Test $test, $groups = []): void { try { $class = new ReflectionClass($test); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd if (!$class->isAbstract()) { $this->tests[] = $test; $this->clearCaches(); if ($test instanceof self && empty($groups)) { $groups = $test->getGroups(); } if ($this->containsOnlyVirtualGroups($groups)) { $groups[] = 'default'; } foreach ($groups as $group) { if (!isset($this->groups[$group])) { $this->groups[$group] = [$test]; } else { $this->groups[$group][] = $test; } } if ($test instanceof TestCase) { $test->setGroups($groups); } } } /** * Adds the tests from the given class to the suite. * * @psalm-param object|class-string $testClass * * @throws Exception */ public function addTestSuite($testClass): void { if (!(is_object($testClass) || (is_string($testClass) && class_exists($testClass)))) { throw InvalidArgumentException::create( 1, 'class name or object' ); } if (!is_object($testClass)) { try { $testClass = new ReflectionClass($testClass); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } if ($testClass instanceof self) { $this->addTest($testClass); } elseif ($testClass instanceof ReflectionClass) { $suiteMethod = false; if (!$testClass->isAbstract() && $testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { try { $method = $testClass->getMethod( BaseTestRunner::SUITE_METHODNAME ); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd if ($method->isStatic()) { $this->addTest( $method->invoke(null, $testClass->getName()) ); $suiteMethod = true; } } if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(TestCase::class)) { $this->addTest(new self($testClass)); } } else { throw new Exception; } } public function addWarning(string $warning): void { $this->warnings[] = $warning; } /** * Wraps both <code>addTest()</code> and <code>addTestSuite</code> * as well as the separate import statements for the user's convenience. * * If the named file cannot be read or there are no new tests that can be * added, a <code>PHPUnit\Framework\WarningTestCase</code> will be created instead, * leaving the current test run untouched. * * @throws Exception */ public function addTestFile(string $filename): void { if (is_file($filename) && substr($filename, -5) === '.phpt') { $this->addTest(new PhptTestCase($filename)); $this->declaredClassesPointer = count(get_declared_classes()); return; } $numTests = count($this->tests); // The given file may contain further stub classes in addition to the // test class itself. Figure out the actual test class. $filename = FileLoader::checkAndLoad($filename); $newClasses = array_slice(get_declared_classes(), $this->declaredClassesPointer); // The diff is empty in case a parent class (with test methods) is added // AFTER a child class that inherited from it. To account for that case, // accumulate all discovered classes, so the parent class may be found in // a later invocation. if (!empty($newClasses)) { // On the assumption that test classes are defined first in files, // process discovered classes in approximate LIFO order, so as to // avoid unnecessary reflection. $this->foundClasses = array_merge($newClasses, $this->foundClasses); $this->declaredClassesPointer = count(get_declared_classes()); } // The test class's name must match the filename, either in full, or as // a PEAR/PSR-0 prefixed short name ('NameSpace_ShortName'), or as a // PSR-1 local short name ('NameSpace\ShortName'). The comparison must be // anchored to prevent false-positive matches (e.g., 'OtherShortName'). $shortName = basename($filename, '.php'); $shortNameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortName, '/') . '$/'; foreach ($this->foundClasses as $i => $className) { if (preg_match($shortNameRegEx, $className)) { try { $class = new ReflectionClass($className); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd if ($class->getFileName() == $filename) { $newClasses = [$className]; unset($this->foundClasses[$i]); break; } } } foreach ($newClasses as $className) { try { $class = new ReflectionClass($className); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd if (dirname($class->getFileName()) === __DIR__) { continue; } if (!$class->isAbstract()) { if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { try { $method = $class->getMethod( BaseTestRunner::SUITE_METHODNAME ); // @codeCoverageIgnoreStart } catch (ReflectionException $e) { throw new Exception( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd if ($method->isStatic()) { $this->addTest($method->invoke(null, $className)); } } elseif ($class->implementsInterface(Test::class)) { // Do we have modern namespacing ('Foo\Bar\WhizBangTest') or old-school namespacing ('Foo_Bar_WhizBangTest')? $isPsr0 = (!$class->inNamespace()) && (strpos($class->getName(), '_') !== false); $expectedClassName = $isPsr0 ? $className : $shortName; if (($pos = strpos($expectedClassName, '.')) !== false) { $expectedClassName = substr( $expectedClassName, 0, $pos ); } if ($class->getShortName() !== $expectedClassName) { $this->addWarning( sprintf( "Test case class not matching filename is deprecated\n in %s\n Class name was '%s', expected '%s'", $filename, $class->getShortName(), $expectedClassName ) ); } $this->addTestSuite($class); } } } if (count($this->tests) > ++$numTests) { $this->addWarning( sprintf( "Multiple test case classes per file is deprecated\n in %s", $filename ) ); } $this->numTests = -1; } /** * Wrapper for addTestFile() that adds multiple test files. * * @throws Exception */ public function addTestFiles(iterable $fileNames): void { foreach ($fileNames as $filename) { $this->addTestFile((string) $filename); } } /** * Counts the number of test cases that will be run by this test. * * @todo refactor usage of numTests in DefaultResultPrinter */ public function count(): int { $this->numTests = 0; foreach ($this as $test) { $this->numTests += count($test); } return $this->numTests; } /** * Returns the name of the suite. */ public function getName(): string { return $this->name; } /** * Returns the test groups of the suite. * * @psalm-return list<string> */ public function getGroups(): array { return array_map( static function ($key): string { return (string) $key; }, array_keys($this->groups) ); } public function getGroupDetails(): array { return $this->groups; } /** * Set tests groups of the test case. */ public function setGroupDetails(array $groups): void { $this->groups = $groups; } /** * Runs the tests and collects their result in a TestResult. * * @throws \PHPUnit\Framework\CodeCoverageException * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Warning */ public function run(TestResult $result = null): TestResult { if ($result === null) { $result = $this->createResult(); } if (count($this) === 0) { return $result; } /** @psalm-var class-string $className */ $className = $this->name; $hookMethods = TestUtil::getHookMethods($className); $result->startTestSuite($this); $test = null; if ($this->testCase && class_exists($this->name, false)) { try { foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { if (method_exists($this->name, $beforeClassMethod)) { if ($missingRequirements = TestUtil::getMissingRequirements($this->name, $beforeClassMethod)) { $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements)); } call_user_func([$this->name, $beforeClassMethod]); } } } catch (SkippedTestSuiteError $error) { foreach ($this->tests() as $test) { $result->startTest($test); $result->addFailure($test, $error, 0); $result->endTest($test, 0); } $result->endTestSuite($this); return $result; } catch (Throwable $t) { $errorAdded = false; foreach ($this->tests() as $test) { if ($result->shouldStop()) { break; } $result->startTest($test); if (!$errorAdded) { $result->addError($test, $t, 0); $errorAdded = true; } else { $result->addFailure( $test, new SkippedTestError('Test skipped because of an error in hook method'), 0 ); } $result->endTest($test, 0); } $result->endTestSuite($this); return $result; } } foreach ($this as $test) { if ($result->shouldStop()) { break; } if ($test instanceof TestCase || $test instanceof self) { $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); $test->setBackupGlobals($this->backupGlobals); $test->setBackupStaticAttributes($this->backupStaticAttributes); $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); } $test->run($result); } if ($this->testCase && class_exists($this->name, false)) { foreach ($hookMethods['afterClass'] as $afterClassMethod) { if (method_exists($this->name, $afterClassMethod)) { try { call_user_func([$this->name, $afterClassMethod]); } catch (Throwable $t) { $message = "Exception in {$this->name}::{$afterClassMethod}" . PHP_EOL . $t->getMessage(); $error = new SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace()); $placeholderTest = clone $test; $placeholderTest->setName($afterClassMethod); $result->startTest($placeholderTest); $result->addFailure($placeholderTest, $error, 0); $result->endTest($placeholderTest, 0); } } } } $result->endTestSuite($this); return $result; } public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void { $this->runTestInSeparateProcess = $runTestInSeparateProcess; } public function setName(string $name): void { $this->name = $name; } /** * Returns the tests as an enumeration. * * @return Test[] */ public function tests(): array { return $this->tests; } /** * Set tests of the test suite. * * @param Test[] $tests */ public function setTests(array $tests): void { $this->tests = $tests; } /** * Mark the test suite as skipped. * * @param string $message * * @throws SkippedTestSuiteError * * @psalm-return never-return */ public function markTestSuiteSkipped($message = ''): void { throw new SkippedTestSuiteError($message); } /** * @param bool $beStrictAboutChangesToGlobalState */ public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState): void { if (null === $this->beStrictAboutChangesToGlobalState && is_bool($beStrictAboutChangesToGlobalState)) { $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } } /** * @param bool $backupGlobals */ public function setBackupGlobals($backupGlobals): void { if (null === $this->backupGlobals && is_bool($backupGlobals)) { $this->backupGlobals = $backupGlobals; } } /** * @param bool $backupStaticAttributes */ public function setBackupStaticAttributes($backupStaticAttributes): void { if (null === $this->backupStaticAttributes && is_bool($backupStaticAttributes)) { $this->backupStaticAttributes = $backupStaticAttributes; } } /** * Returns an iterator for this test suite. */ public function getIterator(): Iterator { $iterator = new TestSuiteIterator($this); if ($this->iteratorFilter !== null) { $iterator = $this->iteratorFilter->factory($iterator, $this); } return $iterator; } public function injectFilter(Factory $filter): void { $this->iteratorFilter = $filter; foreach ($this as $test) { if ($test instanceof self) { $test->injectFilter($filter); } } } /** * @psalm-return array<int,string> */ public function warnings(): array { return array_unique($this->warnings); } /** * @return list<ExecutionOrderDependency> */ public function provides(): array { if ($this->providedTests === null) { $this->providedTests = []; if (is_callable($this->sortId(), true)) { $this->providedTests[] = new ExecutionOrderDependency($this->sortId()); } foreach ($this->tests as $test) { if (!($test instanceof Reorderable)) { // @codeCoverageIgnoreStart continue; // @codeCoverageIgnoreEnd } $this->providedTests = ExecutionOrderDependency::mergeUnique($this->providedTests, $test->provides()); } } return $this->providedTests; } /** * @return list<ExecutionOrderDependency> */ public function requires(): array { if ($this->requiredTests === null) { $this->requiredTests = []; foreach ($this->tests as $test) { if (!($test instanceof Reorderable)) { // @codeCoverageIgnoreStart continue; // @codeCoverageIgnoreEnd } $this->requiredTests = ExecutionOrderDependency::mergeUnique( ExecutionOrderDependency::filterInvalid($this->requiredTests), $test->requires() ); } $this->requiredTests = ExecutionOrderDependency::diff($this->requiredTests, $this->provides()); } return $this->requiredTests; } public function sortId(): string { return $this->getName() . '::class'; } /** * Creates a default TestResult object. */ protected function createResult(): TestResult { return new TestResult; } /** * @throws Exception */ protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method): void { $methodName = $method->getName(); $test = (new TestBuilder)->build($class, $methodName); if ($test instanceof TestCase || $test instanceof DataProviderTestSuite) { $test->setDependencies( TestUtil::getDependencies($class->getName(), $methodName) ); } $this->addTest( $test, TestUtil::getGroups($class->getName(), $methodName) ); } private function clearCaches(): void { $this->numTests = -1; $this->providedTests = null; $this->requiredTests = null; } private function containsOnlyVirtualGroups(array $groups): bool { foreach ($groups as $group) { if (strpos($group, '__phpunit_') !== 0) { return false; } } return true; } }