%PDF- %PDF-
Direktori : /home/silvzytp/dsr_code/vendor/phpunit/phpunit/src/TextUI/ |
Current File : /home/silvzytp/dsr_code/vendor/phpunit/phpunit/src/TextUI/Command.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\TextUI; use const PATH_SEPARATOR; use const PHP_EOL; use const STDIN; use function array_keys; use function assert; use function class_exists; use function copy; use function extension_loaded; use function fgets; use function file_get_contents; use function file_put_contents; use function get_class; use function getcwd; use function ini_get; use function ini_set; use function is_array; use function is_callable; use function is_dir; use function is_file; use function is_string; use function printf; use function realpath; use function sort; use function sprintf; use function stream_resolve_include_path; use function strpos; use function trim; use function version_compare; use PHPUnit\Framework\TestSuite; use PHPUnit\Runner\Extension\PharLoader; use PHPUnit\Runner\StandardTestSuiteLoader; use PHPUnit\Runner\TestSuiteLoader; use PHPUnit\Runner\Version; use PHPUnit\TextUI\CliArguments\Builder; use PHPUnit\TextUI\CliArguments\Configuration; use PHPUnit\TextUI\CliArguments\Exception as ArgumentsException; use PHPUnit\TextUI\CliArguments\Mapper; use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper; use PHPUnit\TextUI\XmlConfiguration\Generator; use PHPUnit\TextUI\XmlConfiguration\Loader; use PHPUnit\TextUI\XmlConfiguration\Migrator; use PHPUnit\TextUI\XmlConfiguration\PhpHandler; use PHPUnit\Util\FileLoader; use PHPUnit\Util\Filesystem; use PHPUnit\Util\Printer; use PHPUnit\Util\TextTestListRenderer; use PHPUnit\Util\Xml\SchemaDetector; use PHPUnit\Util\XmlTestListRenderer; use ReflectionClass; use SebastianBergmann\CodeCoverage\Filter; use SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer; use SebastianBergmann\Timer\Timer; use Throwable; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ class Command { /** * @var array<string,mixed> */ protected $arguments = []; /** * @var array<string,mixed> */ protected $longOptions = []; /** * @var bool */ private $versionStringPrinted = false; /** * @psalm-var list<string> */ private $warnings = []; /** * @throws Exception */ public static function main(bool $exit = true): int { try { return (new static)->run($_SERVER['argv'], $exit); } catch (Throwable $t) { throw new RuntimeException( $t->getMessage(), (int) $t->getCode(), $t ); } } /** * @throws Exception */ public function run(array $argv, bool $exit = true): int { $this->handleArguments($argv); $runner = $this->createRunner(); if ($this->arguments['test'] instanceof TestSuite) { $suite = $this->arguments['test']; } else { $suite = $runner->getTest( $this->arguments['test'], $this->arguments['testSuffixes'] ); } if ($this->arguments['listGroups']) { return $this->handleListGroups($suite, $exit); } if ($this->arguments['listSuites']) { return $this->handleListSuites($exit); } if ($this->arguments['listTests']) { return $this->handleListTests($suite, $exit); } if ($this->arguments['listTestsXml']) { return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit); } unset($this->arguments['test'], $this->arguments['testFile']); try { $result = $runner->run($suite, $this->arguments, $this->warnings, $exit); } catch (Throwable $t) { print $t->getMessage() . PHP_EOL; } $return = TestRunner::FAILURE_EXIT; if (isset($result) && $result->wasSuccessful()) { $return = TestRunner::SUCCESS_EXIT; } elseif (!isset($result) || $result->errorCount() > 0) { $return = TestRunner::EXCEPTION_EXIT; } if ($exit) { exit($return); } return $return; } /** * Create a TestRunner, override in subclasses. */ protected function createRunner(): TestRunner { return new TestRunner($this->arguments['loader']); } /** * Handles the command-line arguments. * * A child class of PHPUnit\TextUI\Command can hook into the argument * parsing by adding the switch(es) to the $longOptions array and point to a * callback method that handles the switch(es) in the child class like this * * <code> * <?php * class MyCommand extends PHPUnit\TextUI\Command * { * public function __construct() * { * // my-switch won't accept a value, it's an on/off * $this->longOptions['my-switch'] = 'myHandler'; * // my-secondswitch will accept a value - note the equals sign * $this->longOptions['my-secondswitch='] = 'myOtherHandler'; * } * * // --my-switch -> myHandler() * protected function myHandler() * { * } * * // --my-secondswitch foo -> myOtherHandler('foo') * protected function myOtherHandler ($value) * { * } * * // You will also need this - the static keyword in the * // PHPUnit\TextUI\Command will mean that it'll be * // PHPUnit\TextUI\Command that gets instantiated, * // not MyCommand * public static function main($exit = true) * { * $command = new static; * * return $command->run($_SERVER['argv'], $exit); * } * * } * </code> * * @throws Exception */ protected function handleArguments(array $argv): void { try { $arguments = (new Builder)->fromParameters($argv, array_keys($this->longOptions)); } catch (ArgumentsException $e) { $this->exitWithErrorMessage($e->getMessage()); } assert(isset($arguments) && $arguments instanceof Configuration); if ($arguments->hasGenerateConfiguration() && $arguments->generateConfiguration()) { $this->generateConfiguration(); } if ($arguments->hasAtLeastVersion()) { if (version_compare(Version::id(), $arguments->atLeastVersion(), '>=')) { exit(TestRunner::SUCCESS_EXIT); } exit(TestRunner::FAILURE_EXIT); } if ($arguments->hasVersion() && $arguments->version()) { $this->printVersionString(); exit(TestRunner::SUCCESS_EXIT); } if ($arguments->hasCheckVersion() && $arguments->checkVersion()) { $this->handleVersionCheck(); } if ($arguments->hasHelp()) { $this->showHelp(); exit(TestRunner::SUCCESS_EXIT); } if ($arguments->hasUnrecognizedOrderBy()) { $this->exitWithErrorMessage( sprintf( 'unrecognized --order-by option: %s', $arguments->unrecognizedOrderBy() ) ); } if ($arguments->hasIniSettings()) { foreach ($arguments->iniSettings() as $name => $value) { ini_set($name, $value); } } if ($arguments->hasIncludePath()) { ini_set( 'include_path', $arguments->includePath() . PATH_SEPARATOR . ini_get('include_path') ); } $this->arguments = (new Mapper)->mapToLegacyArray($arguments); $this->handleCustomOptions($arguments->unrecognizedOptions()); $this->handleCustomTestSuite(); if (!isset($this->arguments['testSuffixes'])) { $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; } if (!isset($this->arguments['test']) && $arguments->hasArgument()) { $this->arguments['test'] = realpath($arguments->argument()); if ($this->arguments['test'] === false) { $this->exitWithErrorMessage( sprintf( 'Cannot open file "%s".', $arguments->argument() ) ); } } if ($this->arguments['loader'] !== null) { $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); } if (isset($this->arguments['configuration'])) { if (is_dir($this->arguments['configuration'])) { $candidate = $this->configurationFileInDirectory($this->arguments['configuration']); if ($candidate !== null) { $this->arguments['configuration'] = $candidate; } } } elseif ($this->arguments['useDefaultConfiguration']) { $candidate = $this->configurationFileInDirectory(getcwd()); if ($candidate !== null) { $this->arguments['configuration'] = $candidate; } } if ($arguments->hasMigrateConfiguration() && $arguments->migrateConfiguration()) { if (!isset($this->arguments['configuration'])) { print 'No configuration file found to migrate.' . PHP_EOL; exit(TestRunner::EXCEPTION_EXIT); } $this->migrateConfiguration(realpath($this->arguments['configuration'])); } if (isset($this->arguments['configuration'])) { try { $this->arguments['configurationObject'] = (new Loader)->load($this->arguments['configuration']); } catch (Throwable $e) { print $e->getMessage() . PHP_EOL; exit(TestRunner::FAILURE_EXIT); } $phpunitConfiguration = $this->arguments['configurationObject']->phpunit(); (new PhpHandler)->handle($this->arguments['configurationObject']->php()); if (isset($this->arguments['bootstrap'])) { $this->handleBootstrap($this->arguments['bootstrap']); } elseif ($phpunitConfiguration->hasBootstrap()) { $this->handleBootstrap($phpunitConfiguration->bootstrap()); } if (!isset($this->arguments['stderr'])) { $this->arguments['stderr'] = $phpunitConfiguration->stderr(); } if (!isset($this->arguments['noExtensions']) && $phpunitConfiguration->hasExtensionsDirectory() && extension_loaded('phar')) { $result = (new PharLoader)->loadPharExtensionsInDirectory($phpunitConfiguration->extensionsDirectory()); $this->arguments['loadedExtensions'] = $result['loadedExtensions']; $this->arguments['notLoadedExtensions'] = $result['notLoadedExtensions']; unset($result); } if (!isset($this->arguments['columns'])) { $this->arguments['columns'] = $phpunitConfiguration->columns(); } if (!isset($this->arguments['printer']) && $phpunitConfiguration->hasPrinterClass()) { $file = $phpunitConfiguration->hasPrinterFile() ? $phpunitConfiguration->printerFile() : ''; $this->arguments['printer'] = $this->handlePrinter( $phpunitConfiguration->printerClass(), $file ); } if ($phpunitConfiguration->hasTestSuiteLoaderClass()) { $file = $phpunitConfiguration->hasTestSuiteLoaderFile() ? $phpunitConfiguration->testSuiteLoaderFile() : ''; $this->arguments['loader'] = $this->handleLoader( $phpunitConfiguration->testSuiteLoaderClass(), $file ); } if (!isset($this->arguments['testsuite']) && $phpunitConfiguration->hasDefaultTestSuite()) { $this->arguments['testsuite'] = $phpunitConfiguration->defaultTestSuite(); } if (!isset($this->arguments['test'])) { try { $this->arguments['test'] = (new TestSuiteMapper)->map( $this->arguments['configurationObject']->testSuite(), $this->arguments['testsuite'] ?? '' ); } catch (Exception $e) { $this->printVersionString(); print $e->getMessage() . PHP_EOL; exit(TestRunner::EXCEPTION_EXIT); } } } elseif (isset($this->arguments['bootstrap'])) { $this->handleBootstrap($this->arguments['bootstrap']); } if (isset($this->arguments['printer']) && is_string($this->arguments['printer'])) { $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); } if (isset($this->arguments['configurationObject'], $this->arguments['warmCoverageCache'])) { $this->handleWarmCoverageCache($this->arguments['configurationObject']); } if (!isset($this->arguments['test'])) { $this->showHelp(); exit(TestRunner::EXCEPTION_EXIT); } } /** * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. * * @deprecated see https://github.com/sebastianbergmann/phpunit/issues/4039 */ protected function handleLoader(string $loaderClass, string $loaderFile = ''): ?TestSuiteLoader { $this->warnings[] = 'Using a custom test suite loader is deprecated'; if (!class_exists($loaderClass, false)) { if ($loaderFile == '') { $loaderFile = Filesystem::classNameToFilename( $loaderClass ); } $loaderFile = stream_resolve_include_path($loaderFile); if ($loaderFile) { /** * @noinspection PhpIncludeInspection * * @psalm-suppress UnresolvableInclude */ require $loaderFile; } } if (class_exists($loaderClass, false)) { try { $class = new ReflectionClass($loaderClass); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd if ($class->implementsInterface(TestSuiteLoader::class) && $class->isInstantiable()) { $object = $class->newInstance(); assert($object instanceof TestSuiteLoader); return $object; } } if ($loaderClass == StandardTestSuiteLoader::class) { return null; } $this->exitWithErrorMessage( sprintf( 'Could not use "%s" as loader.', $loaderClass ) ); return null; } /** * Handles the loading of the PHPUnit\Util\Printer implementation. * * @return null|Printer|string */ protected function handlePrinter(string $printerClass, string $printerFile = '') { if (!class_exists($printerClass, false)) { if ($printerFile === '') { $printerFile = Filesystem::classNameToFilename( $printerClass ); } $printerFile = stream_resolve_include_path($printerFile); if ($printerFile) { /** * @noinspection PhpIncludeInspection * * @psalm-suppress UnresolvableInclude */ require $printerFile; } } if (!class_exists($printerClass)) { $this->exitWithErrorMessage( sprintf( 'Could not use "%s" as printer: class does not exist', $printerClass ) ); } try { $class = new ReflectionClass($printerClass); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); // @codeCoverageIgnoreEnd } if (!$class->implementsInterface(ResultPrinter::class)) { $this->exitWithErrorMessage( sprintf( 'Could not use "%s" as printer: class does not implement %s', $printerClass, ResultPrinter::class ) ); } if (!$class->isInstantiable()) { $this->exitWithErrorMessage( sprintf( 'Could not use "%s" as printer: class cannot be instantiated', $printerClass ) ); } if ($class->isSubclassOf(ResultPrinter::class)) { return $printerClass; } $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; return $class->newInstance($outputStream); } /** * Loads a bootstrap file. */ protected function handleBootstrap(string $filename): void { try { FileLoader::checkAndLoad($filename); } catch (Throwable $t) { if ($t instanceof \PHPUnit\Exception) { $this->exitWithErrorMessage($t->getMessage()); } $this->exitWithErrorMessage( sprintf( 'Error in bootstrap script: %s:%s%s%s%s', get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, $t->getTraceAsString() ) ); } } protected function handleVersionCheck(): void { $this->printVersionString(); $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); $isOutdated = version_compare($latestVersion, Version::id(), '>'); if ($isOutdated) { printf( 'You are not using the latest version of PHPUnit.' . PHP_EOL . 'The latest version is PHPUnit %s.' . PHP_EOL, $latestVersion ); } else { print 'You are using the latest version of PHPUnit.' . PHP_EOL; } exit(TestRunner::SUCCESS_EXIT); } /** * Show the help message. */ protected function showHelp(): void { $this->printVersionString(); (new Help)->writeToConsole(); } /** * Custom callback for test suite discovery. */ protected function handleCustomTestSuite(): void { } private function printVersionString(): void { if ($this->versionStringPrinted) { return; } print Version::getVersionString() . PHP_EOL . PHP_EOL; $this->versionStringPrinted = true; } private function exitWithErrorMessage(string $message): void { $this->printVersionString(); print $message . PHP_EOL; exit(TestRunner::FAILURE_EXIT); } private function handleListGroups(TestSuite $suite, bool $exit): int { $this->printVersionString(); $this->warnAboutConflictingOptions( 'listGroups', [ 'filter', 'groups', 'excludeGroups', 'testsuite', ] ); print 'Available test group(s):' . PHP_EOL; $groups = $suite->getGroups(); sort($groups); foreach ($groups as $group) { if (strpos($group, '__phpunit_') === 0) { continue; } printf( ' - %s' . PHP_EOL, $group ); } if ($exit) { exit(TestRunner::SUCCESS_EXIT); } return TestRunner::SUCCESS_EXIT; } /** * @throws \PHPUnit\Framework\Exception * @throws \PHPUnit\TextUI\XmlConfiguration\Exception */ private function handleListSuites(bool $exit): int { $this->printVersionString(); $this->warnAboutConflictingOptions( 'listSuites', [ 'filter', 'groups', 'excludeGroups', 'testsuite', ] ); print 'Available test suite(s):' . PHP_EOL; foreach ($this->arguments['configurationObject']->testSuite() as $testSuite) { printf( ' - %s' . PHP_EOL, $testSuite->name() ); } if ($exit) { exit(TestRunner::SUCCESS_EXIT); } return TestRunner::SUCCESS_EXIT; } /** * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ private function handleListTests(TestSuite $suite, bool $exit): int { $this->printVersionString(); $this->warnAboutConflictingOptions( 'listTests', [ 'filter', 'groups', 'excludeGroups', 'testsuite', ] ); $renderer = new TextTestListRenderer; print $renderer->render($suite); if ($exit) { exit(TestRunner::SUCCESS_EXIT); } return TestRunner::SUCCESS_EXIT; } /** * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int { $this->printVersionString(); $this->warnAboutConflictingOptions( 'listTestsXml', [ 'filter', 'groups', 'excludeGroups', 'testsuite', ] ); $renderer = new XmlTestListRenderer; file_put_contents($target, $renderer->render($suite)); printf( 'Wrote list of tests that would have been run to %s' . PHP_EOL, $target ); if ($exit) { exit(TestRunner::SUCCESS_EXIT); } return TestRunner::SUCCESS_EXIT; } private function generateConfiguration(): void { $this->printVersionString(); print 'Generating phpunit.xml in ' . getcwd() . PHP_EOL . PHP_EOL; print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; $bootstrapScript = trim(fgets(STDIN)); print 'Tests directory (relative to path shown above; default: tests): '; $testsDirectory = trim(fgets(STDIN)); print 'Source directory (relative to path shown above; default: src): '; $src = trim(fgets(STDIN)); print 'Cache directory (relative to path shown above; default: .phpunit.cache): '; $cacheDirectory = trim(fgets(STDIN)); if ($bootstrapScript === '') { $bootstrapScript = 'vendor/autoload.php'; } if ($testsDirectory === '') { $testsDirectory = 'tests'; } if ($src === '') { $src = 'src'; } if ($cacheDirectory === '') { $cacheDirectory = '.phpunit.cache'; } $generator = new Generator; file_put_contents( 'phpunit.xml', $generator->generateDefaultConfiguration( Version::series(), $bootstrapScript, $testsDirectory, $src, $cacheDirectory ) ); print PHP_EOL . 'Generated phpunit.xml in ' . getcwd() . '.' . PHP_EOL; print 'Make sure to exclude the ' . $cacheDirectory . ' directory from version control.' . PHP_EOL; exit(TestRunner::SUCCESS_EXIT); } private function migrateConfiguration(string $filename): void { $this->printVersionString(); if (!(new SchemaDetector)->detect($filename)->detected()) { print $filename . ' does not need to be migrated.' . PHP_EOL; exit(TestRunner::EXCEPTION_EXIT); } copy($filename, $filename . '.bak'); print 'Created backup: ' . $filename . '.bak' . PHP_EOL; try { file_put_contents( $filename, (new Migrator)->migrate($filename) ); print 'Migrated configuration: ' . $filename . PHP_EOL; } catch (Throwable $t) { print 'Migration failed: ' . $t->getMessage() . PHP_EOL; exit(TestRunner::EXCEPTION_EXIT); } exit(TestRunner::SUCCESS_EXIT); } private function handleCustomOptions(array $unrecognizedOptions): void { foreach ($unrecognizedOptions as $name => $value) { if (isset($this->longOptions[$name])) { $handler = $this->longOptions[$name]; } $name .= '='; if (isset($this->longOptions[$name])) { $handler = $this->longOptions[$name]; } if (isset($handler) && is_callable([$this, $handler])) { $this->{$handler}($value); unset($handler); } } } private function handleWarmCoverageCache(XmlConfiguration\Configuration $configuration): void { $this->printVersionString(); if (isset($this->arguments['coverageCacheDirectory'])) { $cacheDirectory = $this->arguments['coverageCacheDirectory']; } elseif ($configuration->codeCoverage()->hasCacheDirectory()) { $cacheDirectory = $configuration->codeCoverage()->cacheDirectory()->path(); } else { print 'Cache for static analysis has not been configured' . PHP_EOL; exit(TestRunner::EXCEPTION_EXIT); } $filter = new Filter; if ($configuration->codeCoverage()->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) { (new FilterMapper)->map( $filter, $configuration->codeCoverage() ); } elseif (isset($this->arguments['coverageFilter'])) { if (!is_array($this->arguments['coverageFilter'])) { $coverageFilterDirectories = [$this->arguments['coverageFilter']]; } else { $coverageFilterDirectories = $this->arguments['coverageFilter']; } foreach ($coverageFilterDirectories as $coverageFilterDirectory) { $filter->includeDirectory($coverageFilterDirectory); } } else { print 'Filter for code coverage has not been configured' . PHP_EOL; exit(TestRunner::EXCEPTION_EXIT); } $timer = new Timer; $timer->start(); print 'Warming cache for static analysis ... '; (new CacheWarmer)->warmCache( $cacheDirectory, !$configuration->codeCoverage()->disableCodeCoverageIgnore(), $configuration->codeCoverage()->ignoreDeprecatedCodeUnits(), $filter ); print 'done [' . $timer->stop()->asString() . ']' . PHP_EOL; exit(TestRunner::SUCCESS_EXIT); } private function configurationFileInDirectory(string $directory): ?string { $candidates = [ $directory . '/phpunit.xml', $directory . '/phpunit.xml.dist', ]; foreach ($candidates as $candidate) { if (is_file($candidate)) { return realpath($candidate); } } return null; } /** * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key * @psalm-param list<"listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite"> $keys */ private function warnAboutConflictingOptions(string $key, array $keys): void { $warningPrinted = false; foreach ($keys as $_key) { if (!empty($this->arguments[$_key])) { printf( 'The %s and %s options cannot be combined, %s is ignored' . PHP_EOL, $this->mapKeyToOptionForWarning($_key), $this->mapKeyToOptionForWarning($key), $this->mapKeyToOptionForWarning($_key) ); $warningPrinted = true; } } if ($warningPrinted) { print PHP_EOL; } } /** * @psalm-param "listGroups"|"listSuites"|"listTests"|"listTestsXml"|"filter"|"groups"|"excludeGroups"|"testsuite" $key */ private function mapKeyToOptionForWarning(string $key): string { switch ($key) { case 'listGroups': return '--list-groups'; case 'listSuites': return '--list-suites'; case 'listTests': return '--list-tests'; case 'listTestsXml': return '--list-tests-xml'; case 'filter': return '--filter'; case 'groups': return '--group'; case 'excludeGroups': return '--exclude-group'; case 'testsuite': return '--testsuite'; } } }