![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/codeception/codeception/src/Codeception/Test/ |
<?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Event\FailEvent; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Exception\UselessTestException; use Codeception\PHPUnit\Wrapper\Test as TestWrapper; use Codeception\ResultAggregator; use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\TestInterface; use PHPUnit\Framework\Assert; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Exception; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SkippedTest; use PHPUnit\Framework\SkippedTestError; use PHPUnit\Runner\Version as PHPUnitVersion; use RuntimeException; use SebastianBergmann\Timer\Timer; use Symfony\Component\EventDispatcher\EventDispatcher; use Throwable; use function array_reverse; use function method_exists; // phpcs:disable if (PHPUnitVersion::series() < 10) { require_once __DIR__ . '/../../PHPUnit/Wrapper/PhpUnit9/Test.php'; } else { require_once __DIR__ . '/../../PHPUnit/Wrapper/PhpUnit10/Test.php'; } // phpcs:enable /** * The most simple testcase (with only one test in it) which can be executed by PHPUnit/Codeception. * It can be extended with included traits. Turning on/off a trait should not break class functionality. * * Class has exactly one method to be executed for testing, wrapped with before/after callbacks delivered from included traits. * A trait providing before/after callback should contain corresponding protected methods: `{traitName}Start` and `{traitName}End`, * then this trait should be enabled in `hooks` property. * * Inherited class must implement `test` method. */ abstract class Test extends TestWrapper implements TestInterface, Interfaces\Descriptive { use Feature\AssertionCounter; use Feature\CodeCoverage; use Feature\MetadataCollector; use Feature\IgnoreIfMetadataBlocked; private ?ResultAggregator $resultAggregator = null; private bool $ignored = false; private ?EventDispatcher $eventDispatcher = null; /** * Enabled traits with methods to be called before and after the test. */ protected array $hooks = [ 'ignoreIfMetadataBlocked', 'codeCoverage', 'assertionCounter', 'errorLogger' ]; /** * @var string */ public const STATUS_FAIL = 'fail'; /** * @var string */ public const STATUS_ERROR = 'error'; /** * @var string */ public const STATUS_OK = 'ok'; /** * @var string */ public const STATUS_PENDING = 'pending'; /** * @var string */ public const STATUS_USELESS = 'useless'; /** * @var string */ public const STATUS_INCOMPLETE = 'incomplete'; /** * @var string */ public const STATUS_SKIPPED = 'skipped'; protected bool $reportUselessTests = false; private bool $collectCodeCoverage = false; /** * Everything inside this method is treated as a test. * * @return mixed */ abstract public function test(); /** * Test representation */ abstract public function toString(): string; public function collectCodeCoverage(bool $enabled): void { $this->collectCodeCoverage = $enabled; } public function reportUselessTests(bool $enabled): void { $this->reportUselessTests = $enabled; } public function setEventDispatcher(EventDispatcher $eventDispatcher): void { $this->eventDispatcher = $eventDispatcher; } /** * Runs a test and collects its result in a TestResult instance. * Executes before/after hooks coming from traits. */ final public function realRun(ResultAggregator $result): void { $this->resultAggregator = $result; $status = self::STATUS_PENDING; $time = 0; $e = null; $timer = new Timer(); $result->addTest($this); try { $this->fire(Events::TEST_BEFORE, new TestEvent($this)); foreach ($this->hooks as $hook) { if ($hook === 'codeCoverage' && !$this->collectCodeCoverage) { continue; } if (method_exists($this, $hook . 'Start')) { $this->{$hook . 'Start'}(); } } $failedToStart = false; } catch (\Exception $e) { $failedToStart = true; $result->addError(new FailEvent($this, $e, $time)); $this->fire(Events::TEST_ERROR, new FailEvent($this, $e, $time)); } if (!$this->ignored && !$failedToStart) { Assert::resetCount(); $timer->start(); try { $this->test(); $status = self::STATUS_OK; $eventType = Events::TEST_SUCCESS; if (method_exists($this, 'getScenario')) { foreach ($this->getScenario()?->getSteps() ?? [] as $step) { if ($step->hasFailed()) { $lastFailure = $result->popLastFailure(); if ($lastFailure !== null) { throw $lastFailure->getFail(); } } } } } catch (UselessTestException $e) { $result->addUseless(new FailEvent($this, $e, $time)); $status = self::STATUS_USELESS; $eventType = Events::TEST_USELESS; } catch (IncompleteTestError $e) { $result->addIncomplete(new FailEvent($this, $e, $time)); $status = self::STATUS_INCOMPLETE; $eventType = Events::TEST_INCOMPLETE; } catch (SkippedTest | SkippedTestError $e) { $result->addSkipped(new FailEvent($this, $e, $time)); $status = self::STATUS_SKIPPED; $eventType = Events::TEST_SKIPPED; } catch (AssertionFailedError $e) { $result->addFailure(new FailEvent($this, $e, $time)); $status = self::STATUS_FAIL; $eventType = Events::TEST_FAIL; } catch (Exception $e) { $result->addError(new FailEvent($this, $e, $time)); $status = self::STATUS_ERROR; $eventType = Events::TEST_ERROR; } catch (Throwable $e) { $result->addError(new FailEvent($this, $e, $time)); $status = self::STATUS_ERROR; $eventType = Events::TEST_ERROR; } $time = $timer->stop()->asSeconds(); $this->callTestEndHooks($status, $time, $e); // We need to get the number of performed assertions _after_ calling the test end hooks because the // AssertionCounter needs to set the number of performed assertions first. $result->addToAssertionCount($this->numberOfAssertionsPerformed()); if ( $this->reportUselessTests && $this->numberOfAssertionsPerformed() === 0 && !$this->doesNotPerformAssertions() && $eventType === Events::TEST_SUCCESS ) { $eventType = Events::TEST_USELESS; $e = new UselessTestException('This test did not perform any assertions'); $result->addUseless(new FailEvent($this, $e, $time)); } if ($eventType === Events::TEST_SUCCESS) { $result->addSuccessful($this); $this->fire($eventType, new TestEvent($this, $time)); } else { $this->fire($eventType, new FailEvent($this, $e, $time)); } } else { $this->callTestEndHooks($status, $time, $e); } $this->fire(Events::TEST_AFTER, new TestEvent($this, $time)); $this->eventDispatcher->dispatch(new TestEvent($this, $time), Events::TEST_END); } /** * Return false by default, the Unit-specific TestCaseWrapper implements this properly as it supports the PHPUnit * test override `->expectNotToPerformAssertions()`. */ protected function doesNotPerformAssertions(): bool { return false; } public function getResultAggregator(): ResultAggregator { if ($this->resultAggregator === null) { throw new \LogicException('ResultAggregator is not set'); } return $this->resultAggregator; } /** * This class represents exactly one test */ public function count(): int { return 1; } /** * Should a test be skipped (can be set from hooks) */ protected function ignore(bool $ignored): void { $this->ignored = $ignored; } public function numberOfAssertionsPerformed(): int { return $this->getNumAssertions(); } protected function fire(string $eventType, TestEvent $event): void { if ($this->eventDispatcher === null) { throw new RuntimeException('EventDispatcher must be injected before running test'); } $test = $event->getTest(); if ($test instanceof TestInterface) { foreach ($test->getMetadata()->getGroups() as $group) { $this->eventDispatcher->dispatch($event, $eventType . '.' . $group); } } $this->eventDispatcher->dispatch($event, $eventType); } private function callTestEndHooks(string $status, float $time, ?Throwable $e): void { foreach (array_reverse($this->hooks) as $hook) { if ($hook === 'codeCoverage' && !$this->collectCodeCoverage) { continue; } if (method_exists($this, $hook . 'End')) { $this->{$hook . 'End'}($status, $time, $e); } } } }