PK (M .travis.ymlnu W+A language: php
sudo: false
addons:
apt:
packages:
- libxml2-utils
php:
- 7.0
- 7.1
- 7.2
- master
matrix:
allow_failures:
- php: master
fast_finish: true
env:
matrix:
- DEPENDENCIES="high"
- DEPENDENCIES="low"
global:
- DEFAULT_COMPOSER_FLAGS="--no-interaction --no-ansi --no-progress --no-suggest"
before_install:
- composer self-update
- composer clear-cache
install:
- if [[ "$DEPENDENCIES" = 'high' ]]; then travis_retry composer update $DEFAULT_COMPOSER_FLAGS; fi
- if [[ "$DEPENDENCIES" = 'low' ]]; then travis_retry composer update $DEFAULT_COMPOSER_FLAGS --prefer-lowest; fi
before_script:
- echo 'zend.assertions=1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- echo 'assert.exception=On' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
script:
- ./phpunit --coverage-clover=coverage.xml
- ./phpunit --configuration ./build/travis-ci-fail.xml > /dev/null; if [ $? -eq 0 ]; then echo "SHOULD FAIL"; false; else echo "fail checked"; fi;
- xmllint --noout --schema phpunit.xsd phpunit.xml
- xmllint --noout --schema phpunit.xsd tests/_files/configuration.xml
- xmllint --noout --schema phpunit.xsd tests/_files/configuration_empty.xml
- xmllint --noout --schema phpunit.xsd tests/_files/configuration_xinclude.xml -xinclude
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
email: false
PK (M9
phive.xmlnu W+A
PK (M[IQ README.mdnu W+A # PHPUnit
PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks.
[![Latest Stable Version](https://img.shields.io/packagist/v/phpunit/phpunit.svg?style=flat-square)](https://packagist.org/packages/phpunit/phpunit)
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg?style=flat-square)](https://php.net/)
[![Build Status](https://img.shields.io/travis/sebastianbergmann/phpunit/6.5.svg?style=flat-square)](https://phpunit.de/build-status.html)
## Installation
We distribute a [PHP Archive (PHAR)](https://php.net/phar) that has all required (as well as some optional) dependencies of PHPUnit 6.5 bundled in a single file:
```bash
$ wget https://phar.phpunit.de/phpunit-6.5.phar
$ php phpunit-6.5.phar --version
```
Alternatively, you may use [Composer](https://getcomposer.org/) to download and install PHPUnit as well as its dependencies. Please refer to the "[Getting Started](https://phpunit.de/getting-started-with-phpunit.html)" guide for details on how to install PHPUnit.
## Contribute
Please refer to [CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/.github/CONTRIBUTING.md) for information on how to contribute to PHPUnit and its related projects.
## List of Contributors
Thanks to everyone who has contributed to PHPUnit! You can find a detailed list of contributors on every PHPUnit related package on GitHub. This list shows only the major components:
* [PHPUnit](https://github.com/sebastianbergmann/phpunit/graphs/contributors)
* [php-code-coverage](https://github.com/sebastianbergmann/php-code-coverage/graphs/contributors)
* [phpunit-mock-objects](https://github.com/sebastianbergmann/phpunit-mock-objects/graphs/contributors)
A very special thanks to everyone who has contributed to the documentation and helps maintain the translations:
* [English](https://github.com/sebastianbergmann/phpunit-documentation-english/graphs/contributors)
* [Spanish](https://github.com/sebastianbergmann/phpunit-documentation-spanish/graphs/contributors)
* [French](https://github.com/sebastianbergmann/phpunit-documentation-french/graphs/contributors)
* [Japanese](https://github.com/sebastianbergmann/phpunit-documentation-japanese/graphs/contributors)
* [Brazilian Portuguese](https://github.com/sebastianbergmann/phpunit-documentation-brazilian-portuguese/graphs/contributors)
* [Simplified Chinese](https://github.com/sebastianbergmann/phpunit-documentation-chinese/graphs/contributors)
PK (MrUzf f
.gitignorenu W+A /.ant_targets
/.idea
/.php_cs
/.php_cs.cache
/.phpunit.result.cache
/build/documentation
/build/logfiles
/build/phar
/build/phpdox
/build/*.phar
/build/*.phar.asc
/build/binary-phar-autoload.php
/cache.properties
/composer.lock
/tests/end-to-end/*.diff
/tests/end-to-end/*.exp
/tests/end-to-end/*.log
/tests/end-to-end/*.out
/tests/end-to-end/*.php
/vendor
PK (Mޖ phpunit.xmlnu W+A
tests/unit
tests/end-to-end
src
src/Framework/Assert/Functions.php
src/Util/PHP/eval-stdin.php
PK (MpE E src/TextUI/ResultPrinter.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use PHP_Timer;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\InvalidArgumentHelper;
use PHPUnit\Util\Printer;
use SebastianBergmann\Environment\Console;
/**
* Prints the result of a TextUI TestRunner run.
*/
class ResultPrinter extends Printer implements TestListener
{
const EVENT_TEST_START = 0;
const EVENT_TEST_END = 1;
const EVENT_TESTSUITE_START = 2;
const EVENT_TESTSUITE_END = 3;
const COLOR_NEVER = 'never';
const COLOR_AUTO = 'auto';
const COLOR_ALWAYS = 'always';
const COLOR_DEFAULT = self::COLOR_NEVER;
/**
* @var array
*/
private static $ansiCodes = [
'bold' => 1,
'fg-black' => 30,
'fg-red' => 31,
'fg-green' => 32,
'fg-yellow' => 33,
'fg-blue' => 34,
'fg-magenta' => 35,
'fg-cyan' => 36,
'fg-white' => 37,
'bg-black' => 40,
'bg-red' => 41,
'bg-green' => 42,
'bg-yellow' => 43,
'bg-blue' => 44,
'bg-magenta' => 45,
'bg-cyan' => 46,
'bg-white' => 47
];
/**
* @var int
*/
protected $column = 0;
/**
* @var int
*/
protected $maxColumn;
/**
* @var bool
*/
protected $lastTestFailed = false;
/**
* @var int
*/
protected $numAssertions = 0;
/**
* @var int
*/
protected $numTests = -1;
/**
* @var int
*/
protected $numTestsRun = 0;
/**
* @var int
*/
protected $numTestsWidth;
/**
* @var bool
*/
protected $colors = false;
/**
* @var bool
*/
protected $debug = false;
/**
* @var bool
*/
protected $verbose = false;
/**
* @var int
*/
private $numberOfColumns;
/**
* @var bool
*/
private $reverse;
/**
* @var bool
*/
private $defectListPrinted = false;
/**
* Constructor.
*
* @param mixed $out
* @param bool $verbose
* @param string $colors
* @param bool $debug
* @param int|string $numberOfColumns
* @param bool $reverse
*
* @throws Exception
*/
public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80, $reverse = false)
{
parent::__construct($out);
if (!\is_bool($verbose)) {
throw InvalidArgumentHelper::factory(2, 'boolean');
}
$availableColors = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS];
if (!\in_array($colors, $availableColors)) {
throw InvalidArgumentHelper::factory(
3,
\vsprintf('value from "%s", "%s" or "%s"', $availableColors)
);
}
if (!\is_bool($debug)) {
throw InvalidArgumentHelper::factory(4, 'boolean');
}
if (!\is_int($numberOfColumns) && $numberOfColumns !== 'max') {
throw InvalidArgumentHelper::factory(5, 'integer or "max"');
}
if (!\is_bool($reverse)) {
throw InvalidArgumentHelper::factory(6, 'boolean');
}
$console = new Console;
$maxNumberOfColumns = $console->getNumberOfColumns();
if ($numberOfColumns === 'max' || ($numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns)) {
$numberOfColumns = $maxNumberOfColumns;
}
$this->numberOfColumns = $numberOfColumns;
$this->verbose = $verbose;
$this->debug = $debug;
$this->reverse = $reverse;
if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) {
$this->colors = true;
} else {
$this->colors = (self::COLOR_ALWAYS === $colors);
}
}
/**
* @param TestResult $result
*/
public function printResult(TestResult $result)
{
$this->printHeader();
$this->printErrors($result);
$this->printWarnings($result);
$this->printFailures($result);
$this->printRisky($result);
if ($this->verbose) {
$this->printIncompletes($result);
$this->printSkipped($result);
}
$this->printFooter($result);
}
/**
* @param array $defects
* @param string $type
*/
protected function printDefects(array $defects, $type)
{
$count = \count($defects);
if ($count == 0) {
return;
}
if ($this->defectListPrinted) {
$this->write("\n--\n\n");
}
$this->write(
\sprintf(
"There %s %d %s%s:\n",
($count == 1) ? 'was' : 'were',
$count,
$type,
($count == 1) ? '' : 's'
)
);
$i = 1;
if ($this->reverse) {
$defects = \array_reverse($defects);
}
foreach ($defects as $defect) {
$this->printDefect($defect, $i++);
}
$this->defectListPrinted = true;
}
/**
* @param TestFailure $defect
* @param int $count
*/
protected function printDefect(TestFailure $defect, $count)
{
$this->printDefectHeader($defect, $count);
$this->printDefectTrace($defect);
}
/**
* @param TestFailure $defect
* @param int $count
*/
protected function printDefectHeader(TestFailure $defect, $count)
{
$this->write(
\sprintf(
"\n%d) %s\n",
$count,
$defect->getTestName()
)
);
}
/**
* @param TestFailure $defect
*/
protected function printDefectTrace(TestFailure $defect)
{
$e = $defect->thrownException();
$this->write((string) $e);
while ($e = $e->getPrevious()) {
$this->write("\nCaused by\n" . $e);
}
}
/**
* @param TestResult $result
*/
protected function printErrors(TestResult $result)
{
$this->printDefects($result->errors(), 'error');
}
/**
* @param TestResult $result
*/
protected function printFailures(TestResult $result)
{
$this->printDefects($result->failures(), 'failure');
}
/**
* @param TestResult $result
*/
protected function printWarnings(TestResult $result)
{
$this->printDefects($result->warnings(), 'warning');
}
/**
* @param TestResult $result
*/
protected function printIncompletes(TestResult $result)
{
$this->printDefects($result->notImplemented(), 'incomplete test');
}
/**
* @param TestResult $result
*/
protected function printRisky(TestResult $result)
{
$this->printDefects($result->risky(), 'risky test');
}
/**
* @param TestResult $result
*/
protected function printSkipped(TestResult $result)
{
$this->printDefects($result->skipped(), 'skipped test');
}
protected function printHeader()
{
$this->write("\n\n" . PHP_Timer::resourceUsage() . "\n\n");
}
/**
* @param TestResult $result
*/
protected function printFooter(TestResult $result)
{
if (\count($result) === 0) {
$this->writeWithColor(
'fg-black, bg-yellow',
'No tests executed!'
);
return;
}
if ($result->wasSuccessful() &&
$result->allHarmless() &&
$result->allCompletelyImplemented() &&
$result->noneSkipped()) {
$this->writeWithColor(
'fg-black, bg-green',
\sprintf(
'OK (%d test%s, %d assertion%s)',
\count($result),
(\count($result) == 1) ? '' : 's',
$this->numAssertions,
($this->numAssertions == 1) ? '' : 's'
)
);
} else {
if ($result->wasSuccessful()) {
$color = 'fg-black, bg-yellow';
if ($this->verbose || !$result->allHarmless()) {
$this->write("\n");
}
$this->writeWithColor(
$color,
'OK, but incomplete, skipped, or risky tests!'
);
} else {
$this->write("\n");
if ($result->errorCount()) {
$color = 'fg-white, bg-red';
$this->writeWithColor(
$color,
'ERRORS!'
);
} elseif ($result->failureCount()) {
$color = 'fg-white, bg-red';
$this->writeWithColor(
$color,
'FAILURES!'
);
} elseif ($result->warningCount()) {
$color = 'fg-black, bg-yellow';
$this->writeWithColor(
$color,
'WARNINGS!'
);
}
}
$this->writeCountString(\count($result), 'Tests', $color, true);
$this->writeCountString($this->numAssertions, 'Assertions', $color, true);
$this->writeCountString($result->errorCount(), 'Errors', $color);
$this->writeCountString($result->failureCount(), 'Failures', $color);
$this->writeCountString($result->warningCount(), 'Warnings', $color);
$this->writeCountString($result->skippedCount(), 'Skipped', $color);
$this->writeCountString($result->notImplementedCount(), 'Incomplete', $color);
$this->writeCountString($result->riskyCount(), 'Risky', $color);
$this->writeWithColor($color, '.', true);
}
}
public function printWaitPrompt()
{
$this->write("\n to continue\n");
}
/**
* An error occurred.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(Test $test, \Exception $e, $time)
{
$this->writeProgressWithColor('fg-red, bold', 'E');
$this->lastTestFailed = true;
}
/**
* A failure occurred.
*
* @param Test $test
* @param AssertionFailedError $e
* @param float $time
*/
public function addFailure(Test $test, AssertionFailedError $e, $time)
{
$this->writeProgressWithColor('bg-red, fg-white', 'F');
$this->lastTestFailed = true;
}
/**
* A warning occurred.
*
* @param Test $test
* @param Warning $e
* @param float $time
*/
public function addWarning(Test $test, Warning $e, $time)
{
$this->writeProgressWithColor('fg-yellow, bold', 'W');
$this->lastTestFailed = true;
}
/**
* Incomplete test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(Test $test, \Exception $e, $time)
{
$this->writeProgressWithColor('fg-yellow, bold', 'I');
$this->lastTestFailed = true;
}
/**
* Risky test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addRiskyTest(Test $test, \Exception $e, $time)
{
$this->writeProgressWithColor('fg-yellow, bold', 'R');
$this->lastTestFailed = true;
}
/**
* Skipped test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addSkippedTest(Test $test, \Exception $e, $time)
{
$this->writeProgressWithColor('fg-cyan, bold', 'S');
$this->lastTestFailed = true;
}
/**
* A testsuite started.
*
* @param TestSuite $suite
*/
public function startTestSuite(TestSuite $suite)
{
if ($this->numTests == -1) {
$this->numTests = \count($suite);
$this->numTestsWidth = \strlen((string) $this->numTests);
$this->maxColumn = $this->numberOfColumns - \strlen(' / (XXX%)') - (2 * $this->numTestsWidth);
}
}
/**
* A testsuite ended.
*
* @param TestSuite $suite
*/
public function endTestSuite(TestSuite $suite)
{
}
/**
* A test started.
*
* @param Test $test
*/
public function startTest(Test $test)
{
if ($this->debug) {
$this->write(
\sprintf(
"Test '%s' started\n",
\PHPUnit\Util\Test::describe($test)
)
);
}
}
/**
* A test ended.
*
* @param Test $test
* @param float $time
*/
public function endTest(Test $test, $time)
{
if ($this->debug) {
$this->write(
\sprintf(
"Test '%s' ended\n",
\PHPUnit\Util\Test::describe($test)
)
);
}
if (!$this->lastTestFailed) {
$this->writeProgress('.');
}
if ($test instanceof TestCase) {
$this->numAssertions += $test->getNumAssertions();
} elseif ($test instanceof PhptTestCase) {
$this->numAssertions++;
}
$this->lastTestFailed = false;
if ($test instanceof TestCase) {
if (!$test->hasExpectationOnOutput()) {
$this->write($test->getActualOutput());
}
}
}
/**
* @param string $progress
*/
protected function writeProgress($progress)
{
if ($this->debug) {
return;
}
$this->write($progress);
$this->column++;
$this->numTestsRun++;
if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) {
if ($this->numTestsRun == $this->numTests) {
$this->write(\str_repeat(' ', $this->maxColumn - $this->column));
}
$this->write(
\sprintf(
' %' . $this->numTestsWidth . 'd / %' .
$this->numTestsWidth . 'd (%3s%%)',
$this->numTestsRun,
$this->numTests,
\floor(($this->numTestsRun / $this->numTests) * 100)
)
);
if ($this->column == $this->maxColumn) {
$this->writeNewLine();
}
}
}
protected function writeNewLine()
{
$this->column = 0;
$this->write("\n");
}
/**
* Formats a buffer with a specified ANSI color sequence if colors are
* enabled.
*
* @param string $color
* @param string $buffer
*
* @return string
*/
protected function formatWithColor($color, $buffer)
{
if (!$this->colors) {
return $buffer;
}
$codes = \array_map('trim', \explode(',', $color));
$lines = \explode("\n", $buffer);
$padding = \max(\array_map('strlen', $lines));
$styles = [];
foreach ($codes as $code) {
$styles[] = self::$ansiCodes[$code];
}
$style = \sprintf("\x1b[%sm", \implode(';', $styles));
$styledLines = [];
foreach ($lines as $line) {
$styledLines[] = $style . \str_pad($line, $padding) . "\x1b[0m";
}
return \implode("\n", $styledLines);
}
/**
* Writes a buffer out with a color sequence if colors are enabled.
*
* @param string $color
* @param string $buffer
* @param bool $lf
*/
protected function writeWithColor($color, $buffer, $lf = true)
{
$this->write($this->formatWithColor($color, $buffer));
if ($lf) {
$this->write("\n");
}
}
/**
* Writes progress with a color sequence if colors are enabled.
*
* @param string $color
* @param string $buffer
*/
protected function writeProgressWithColor($color, $buffer)
{
$buffer = $this->formatWithColor($color, $buffer);
$this->writeProgress($buffer);
}
/**
* @param int $count
* @param string $name
* @param string $color
* @param bool $always
*/
private function writeCountString($count, $name, $color, $always = false)
{
static $first = true;
if ($always || $count > 0) {
$this->writeWithColor(
$color,
\sprintf(
'%s%s: %d',
!$first ? ', ' : '',
$name,
$count
),
false
);
$first = false;
}
}
}
PK (MnK src/TextUI/TestRunner.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator;
use PHPUnit\Runner\Filter\Factory;
use PHPUnit\Runner\Filter\IncludeGroupFilterIterator;
use PHPUnit\Runner\Filter\NameFilterIterator;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Runner\TestSuiteLoader;
use PHPUnit\Runner\Version;
use PHPUnit\Util\Configuration;
use PHPUnit\Util\Log\JUnit;
use PHPUnit\Util\Log\TeamCity;
use PHPUnit\Util\Printer;
use PHPUnit\Util\TestDox\HtmlResultPrinter;
use PHPUnit\Util\TestDox\TextResultPrinter;
use PHPUnit\Util\TestDox\XmlResultPrinter;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Environment\Runtime;
/**
* A TestRunner for the Command Line Interface (CLI)
* PHP SAPI Module.
*/
class TestRunner extends BaseTestRunner
{
const SUCCESS_EXIT = 0;
const FAILURE_EXIT = 1;
const EXCEPTION_EXIT = 2;
/**
* @var CodeCoverageFilter
*/
protected $codeCoverageFilter;
/**
* @var TestSuiteLoader
*/
protected $loader;
/**
* @var ResultPrinter
*/
protected $printer;
/**
* @var bool
*/
protected static $versionStringPrinted = false;
/**
* @var Runtime
*/
private $runtime;
/**
* @var bool
*/
private $messagePrinted = false;
/**
* @param TestSuiteLoader $loader
* @param CodeCoverageFilter $filter
*/
public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null)
{
if ($filter === null) {
$filter = new CodeCoverageFilter;
}
$this->codeCoverageFilter = $filter;
$this->loader = $loader;
$this->runtime = new Runtime;
}
/**
* @param Test|ReflectionClass $test
* @param array $arguments
* @param bool $exit
*
* @return TestResult
*
* @throws Exception
*/
public static function run($test, array $arguments = [], $exit = true)
{
if ($test instanceof ReflectionClass) {
$test = new TestSuite($test);
}
if ($test instanceof Test) {
$aTestRunner = new self;
return $aTestRunner->doRun(
$test,
$arguments,
$exit
);
}
throw new Exception('No test case or test suite found.');
}
/**
* @return TestResult
*/
protected function createTestResult()
{
return new TestResult;
}
/**
* @param TestSuite $suite
* @param array $arguments
*/
private function processSuiteFilters(TestSuite $suite, array $arguments)
{
if (!$arguments['filter'] &&
empty($arguments['groups']) &&
empty($arguments['excludeGroups'])) {
return;
}
$filterFactory = new Factory;
if (!empty($arguments['excludeGroups'])) {
$filterFactory->addFilter(
new ReflectionClass(ExcludeGroupFilterIterator::class),
$arguments['excludeGroups']
);
}
if (!empty($arguments['groups'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
$arguments['groups']
);
}
if ($arguments['filter']) {
$filterFactory->addFilter(
new ReflectionClass(NameFilterIterator::class),
$arguments['filter']
);
}
$suite->injectFilter($filterFactory);
}
/**
* @param Test $suite
* @param array $arguments
* @param bool $exit
*
* @return TestResult
*/
public function doRun(Test $suite, array $arguments = [], $exit = true)
{
if (isset($arguments['configuration'])) {
$GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration'];
}
$this->handleConfiguration($arguments);
if (isset($arguments['bootstrap'])) {
$GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap'];
}
if ($arguments['backupGlobals'] === true) {
$suite->setBackupGlobals(true);
}
if ($arguments['backupStaticAttributes'] === true) {
$suite->setBackupStaticAttributes(true);
}
if ($arguments['beStrictAboutChangesToGlobalState'] === true) {
$suite->setBeStrictAboutChangesToGlobalState(true);
}
if (\is_int($arguments['repeat']) && $arguments['repeat'] > 0) {
$_suite = new TestSuite;
foreach (\range(1, $arguments['repeat']) as $step) {
$_suite->addTest($suite);
}
$suite = $_suite;
unset($_suite);
}
$result = $this->createTestResult();
if (!$arguments['convertErrorsToExceptions']) {
$result->convertErrorsToExceptions(false);
}
if (!$arguments['convertDeprecationsToExceptions']) {
Deprecated::$enabled = false;
}
if (!$arguments['convertNoticesToExceptions']) {
Notice::$enabled = false;
}
if (!$arguments['convertWarningsToExceptions']) {
Warning::$enabled = false;
}
if ($arguments['stopOnError']) {
$result->stopOnError(true);
}
if ($arguments['stopOnFailure']) {
$result->stopOnFailure(true);
}
if ($arguments['stopOnWarning']) {
$result->stopOnWarning(true);
}
if ($arguments['stopOnIncomplete']) {
$result->stopOnIncomplete(true);
}
if ($arguments['stopOnRisky']) {
$result->stopOnRisky(true);
}
if ($arguments['stopOnSkipped']) {
$result->stopOnSkipped(true);
}
if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) {
$result->setRegisterMockObjectsFromTestArgumentsRecursively(true);
}
if ($this->printer === null) {
if (isset($arguments['printer']) &&
$arguments['printer'] instanceof Printer) {
$this->printer = $arguments['printer'];
} else {
$printerClass = ResultPrinter::class;
if (isset($arguments['printer']) && \is_string($arguments['printer']) && \class_exists($arguments['printer'], false)) {
$class = new ReflectionClass($arguments['printer']);
if ($class->isSubclassOf(ResultPrinter::class)) {
$printerClass = $arguments['printer'];
}
}
$this->printer = new $printerClass(
(isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null,
$arguments['verbose'],
$arguments['colors'],
$arguments['debug'],
$arguments['columns'],
$arguments['reverseList']
);
}
}
$this->printer->write(
Version::getVersionString() . "\n"
);
self::$versionStringPrinted = true;
if ($arguments['verbose']) {
$runtime = $this->runtime->getNameWithVersion();
if ($this->runtime->hasXdebug()) {
$runtime .= \sprintf(
' with Xdebug %s',
\phpversion('xdebug')
);
}
$this->writeMessage('Runtime', $runtime);
if (isset($arguments['configuration'])) {
$this->writeMessage(
'Configuration',
$arguments['configuration']->getFilename()
);
}
foreach ($arguments['loadedExtensions'] as $extension) {
$this->writeMessage(
'Extension',
$extension
);
}
foreach ($arguments['notLoadedExtensions'] as $extension) {
$this->writeMessage(
'Extension',
$extension
);
}
}
if ($this->runtime->discardsComments()) {
$this->writeMessage('Warning', 'opcache.save_comments=0 set; annotations will not work');
}
foreach ($arguments['listeners'] as $listener) {
$result->addListener($listener);
}
$result->addListener($this->printer);
$codeCoverageReports = 0;
if (!isset($arguments['noLogging'])) {
if (isset($arguments['testdoxHTMLFile'])) {
$result->addListener(
new HtmlResultPrinter(
$arguments['testdoxHTMLFile'],
$arguments['testdoxGroups'],
$arguments['testdoxExcludeGroups']
)
);
}
if (isset($arguments['testdoxTextFile'])) {
$result->addListener(
new TextResultPrinter(
$arguments['testdoxTextFile'],
$arguments['testdoxGroups'],
$arguments['testdoxExcludeGroups']
)
);
}
if (isset($arguments['testdoxXMLFile'])) {
$result->addListener(
new XmlResultPrinter(
$arguments['testdoxXMLFile']
)
);
}
if (isset($arguments['teamcityLogfile'])) {
$result->addListener(
new TeamCity($arguments['teamcityLogfile'])
);
}
if (isset($arguments['junitLogfile'])) {
$result->addListener(
new JUnit(
$arguments['junitLogfile'],
$arguments['reportUselessTests']
)
);
}
if (isset($arguments['coverageClover'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageCrap4J'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageHtml'])) {
$codeCoverageReports++;
}
if (isset($arguments['coveragePHP'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageText'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageXml'])) {
$codeCoverageReports++;
}
}
if (isset($arguments['noCoverage'])) {
$codeCoverageReports = 0;
}
if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) {
$this->writeMessage('Error', 'No code coverage driver is available');
$codeCoverageReports = 0;
}
if ($codeCoverageReports > 0) {
$codeCoverage = new CodeCoverage(
null,
$this->codeCoverageFilter
);
$codeCoverage->setUnintentionallyCoveredSubclassesWhitelist(
[Comparator::class]
);
$codeCoverage->setCheckForUnintentionallyCoveredCode(
$arguments['strictCoverage']
);
$codeCoverage->setCheckForMissingCoversAnnotation(
$arguments['strictCoverage']
);
if (isset($arguments['forceCoversAnnotation'])) {
$codeCoverage->setForceCoversAnnotation(
$arguments['forceCoversAnnotation']
);
}
if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) {
$codeCoverage->setIgnoreDeprecatedCode(
$arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']
);
}
if (isset($arguments['disableCodeCoverageIgnore'])) {
$codeCoverage->setDisableIgnoredLines(true);
}
$whitelistFromConfigurationFile = false;
$whitelistFromOption = false;
if (isset($arguments['whitelist'])) {
$this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']);
$whitelistFromOption = true;
}
if (isset($arguments['configuration'])) {
$filterConfiguration = $arguments['configuration']->getFilterConfiguration();
if (!empty($filterConfiguration['whitelist'])) {
$whitelistFromConfigurationFile = true;
}
if (!empty($filterConfiguration['whitelist'])) {
$codeCoverage->setAddUncoveredFilesFromWhitelist(
$filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']
);
$codeCoverage->setProcessUncoveredFilesFromWhitelist(
$filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']
);
foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) {
$this->codeCoverageFilter->addDirectoryToWhitelist(
$dir['path'],
$dir['suffix'],
$dir['prefix']
);
}
foreach ($filterConfiguration['whitelist']['include']['file'] as $file) {
$this->codeCoverageFilter->addFileToWhitelist($file);
}
foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) {
$this->codeCoverageFilter->removeDirectoryFromWhitelist(
$dir['path'],
$dir['suffix'],
$dir['prefix']
);
}
foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) {
$this->codeCoverageFilter->removeFileFromWhitelist($file);
}
}
}
if (isset($codeCoverage) && !$this->codeCoverageFilter->hasWhitelist()) {
if (!$whitelistFromConfigurationFile && !$whitelistFromOption) {
$this->writeMessage('Error', 'No whitelist is configured, no code coverage will be generated.');
} else {
$this->writeMessage('Error', 'Incorrect whitelist config, no code coverage will be generated.');
}
$codeCoverageReports = 0;
unset($codeCoverage);
}
}
$this->printer->write("\n");
if (isset($codeCoverage)) {
$result->setCodeCoverage($codeCoverage);
if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) {
$codeCoverage->setCacheTokens($arguments['cacheTokens']);
}
}
$result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']);
$result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']);
$result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']);
$result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']);
$result->enforceTimeLimit($arguments['enforceTimeLimit']);
$result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']);
$result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']);
$result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']);
if ($suite instanceof TestSuite) {
$this->processSuiteFilters($suite, $arguments);
$suite->setRunTestInSeparateProcess($arguments['processIsolation']);
}
$suite->run($result);
unset($suite);
$result->flushListeners();
if ($this->printer instanceof ResultPrinter) {
$this->printer->printResult($result);
}
if (isset($codeCoverage)) {
if (isset($arguments['coverageClover'])) {
$this->printer->write(
"\nGenerating code coverage report in Clover XML format ..."
);
try {
$writer = new CloverReport;
$writer->process($codeCoverage, $arguments['coverageClover']);
$this->printer->write(" done\n");
unset($writer);
} catch (CodeCoverageException $e) {
$this->printer->write(
" failed\n" . $e->getMessage() . "\n"
);
}
}
if (isset($arguments['coverageCrap4J'])) {
$this->printer->write(
"\nGenerating Crap4J report XML file ..."
);
try {
$writer = new Crap4jReport($arguments['crap4jThreshold']);
$writer->process($codeCoverage, $arguments['coverageCrap4J']);
$this->printer->write(" done\n");
unset($writer);
} catch (CodeCoverageException $e) {
$this->printer->write(
" failed\n" . $e->getMessage() . "\n"
);
}
}
if (isset($arguments['coverageHtml'])) {
$this->printer->write(
"\nGenerating code coverage report in HTML format ..."
);
try {
$writer = new HtmlReport(
$arguments['reportLowUpperBound'],
$arguments['reportHighLowerBound'],
\sprintf(
' and PHPUnit %s',
Version::id()
)
);
$writer->process($codeCoverage, $arguments['coverageHtml']);
$this->printer->write(" done\n");
unset($writer);
} catch (CodeCoverageException $e) {
$this->printer->write(
" failed\n" . $e->getMessage() . "\n"
);
}
}
if (isset($arguments['coveragePHP'])) {
$this->printer->write(
"\nGenerating code coverage report in PHP format ..."
);
try {
$writer = new PhpReport;
$writer->process($codeCoverage, $arguments['coveragePHP']);
$this->printer->write(" done\n");
unset($writer);
} catch (CodeCoverageException $e) {
$this->printer->write(
" failed\n" . $e->getMessage() . "\n"
);
}
}
if (isset($arguments['coverageText'])) {
if ($arguments['coverageText'] == 'php://stdout') {
$outputStream = $this->printer;
$colors = $arguments['colors'] && $arguments['colors'] != ResultPrinter::COLOR_NEVER;
} else {
$outputStream = new Printer($arguments['coverageText']);
$colors = false;
}
$processor = new TextReport(
$arguments['reportLowUpperBound'],
$arguments['reportHighLowerBound'],
$arguments['coverageTextShowUncoveredFiles'],
$arguments['coverageTextShowOnlySummary']
);
$outputStream->write(
$processor->process($codeCoverage, $colors)
);
}
if (isset($arguments['coverageXml'])) {
$this->printer->write(
"\nGenerating code coverage report in PHPUnit XML format ..."
);
try {
$writer = new XmlReport(Version::id());
$writer->process($codeCoverage, $arguments['coverageXml']);
$this->printer->write(" done\n");
unset($writer);
} catch (CodeCoverageException $e) {
$this->printer->write(
" failed\n" . $e->getMessage() . "\n"
);
}
}
}
if ($exit) {
if ($result->wasSuccessful()) {
if ($arguments['failOnRisky'] && !$result->allHarmless()) {
exit(self::FAILURE_EXIT);
}
if ($arguments['failOnWarning'] && $result->warningCount() > 0) {
exit(self::FAILURE_EXIT);
}
exit(self::SUCCESS_EXIT);
}
if ($result->errorCount() > 0) {
exit(self::EXCEPTION_EXIT);
}
if ($result->failureCount() > 0) {
exit(self::FAILURE_EXIT);
}
}
return $result;
}
/**
* @param ResultPrinter $resultPrinter
*/
public function setPrinter(ResultPrinter $resultPrinter)
{
$this->printer = $resultPrinter;
}
/**
* Override to define how to handle a failed loading of
* a test suite.
*
* @param string $message
*/
protected function runFailed($message)
{
$this->write($message . PHP_EOL);
exit(self::FAILURE_EXIT);
}
/**
* @param string $buffer
*/
protected function write($buffer)
{
if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
$buffer = \htmlspecialchars($buffer);
}
if ($this->printer !== null) {
$this->printer->write($buffer);
} else {
print $buffer;
}
}
/**
* Returns the loader to be used.
*
* @return TestSuiteLoader
*/
public function getLoader()
{
if ($this->loader === null) {
$this->loader = new StandardTestSuiteLoader;
}
return $this->loader;
}
/**
* @param array $arguments
*/
protected function handleConfiguration(array &$arguments)
{
if (isset($arguments['configuration']) &&
!$arguments['configuration'] instanceof Configuration) {
$arguments['configuration'] = Configuration::getInstance(
$arguments['configuration']
);
}
$arguments['debug'] = $arguments['debug'] ?? false;
$arguments['filter'] = $arguments['filter'] ?? false;
$arguments['listeners'] = $arguments['listeners'] ?? [];
if (isset($arguments['configuration'])) {
$arguments['configuration']->handlePHPConfiguration();
$phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration();
if (isset($phpunitConfiguration['backupGlobals']) && !isset($arguments['backupGlobals'])) {
$arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals'];
}
if (isset($phpunitConfiguration['backupStaticAttributes']) && !isset($arguments['backupStaticAttributes'])) {
$arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes'];
}
if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) && !isset($arguments['beStrictAboutChangesToGlobalState'])) {
$arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState'];
}
if (isset($phpunitConfiguration['bootstrap']) && !isset($arguments['bootstrap'])) {
$arguments['bootstrap'] = $phpunitConfiguration['bootstrap'];
}
if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) {
$arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens'];
}
if (isset($phpunitConfiguration['colors']) && !isset($arguments['colors'])) {
$arguments['colors'] = $phpunitConfiguration['colors'];
}
if (isset($phpunitConfiguration['convertDeprecationsToExceptions']) && !isset($arguments['convertDeprecationsToExceptions'])) {
$arguments['convertDeprecationsToExceptions'] = $phpunitConfiguration['convertDeprecationsToExceptions'];
}
if (isset($phpunitConfiguration['convertErrorsToExceptions']) && !isset($arguments['convertErrorsToExceptions'])) {
$arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions'];
}
if (isset($phpunitConfiguration['convertNoticesToExceptions']) && !isset($arguments['convertNoticesToExceptions'])) {
$arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions'];
}
if (isset($phpunitConfiguration['convertWarningsToExceptions']) && !isset($arguments['convertWarningsToExceptions'])) {
$arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions'];
}
if (isset($phpunitConfiguration['processIsolation']) && !isset($arguments['processIsolation'])) {
$arguments['processIsolation'] = $phpunitConfiguration['processIsolation'];
}
if (isset($phpunitConfiguration['stopOnError']) && !isset($arguments['stopOnError'])) {
$arguments['stopOnError'] = $phpunitConfiguration['stopOnError'];
}
if (isset($phpunitConfiguration['stopOnFailure']) && !isset($arguments['stopOnFailure'])) {
$arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure'];
}
if (isset($phpunitConfiguration['stopOnWarning']) && !isset($arguments['stopOnWarning'])) {
$arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning'];
}
if (isset($phpunitConfiguration['stopOnIncomplete']) && !isset($arguments['stopOnIncomplete'])) {
$arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete'];
}
if (isset($phpunitConfiguration['stopOnRisky']) && !isset($arguments['stopOnRisky'])) {
$arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky'];
}
if (isset($phpunitConfiguration['stopOnSkipped']) && !isset($arguments['stopOnSkipped'])) {
$arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped'];
}
if (isset($phpunitConfiguration['failOnWarning']) && !isset($arguments['failOnWarning'])) {
$arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning'];
}
if (isset($phpunitConfiguration['failOnRisky']) && !isset($arguments['failOnRisky'])) {
$arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky'];
}
if (isset($phpunitConfiguration['timeoutForSmallTests']) && !isset($arguments['timeoutForSmallTests'])) {
$arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests'];
}
if (isset($phpunitConfiguration['timeoutForMediumTests']) && !isset($arguments['timeoutForMediumTests'])) {
$arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests'];
}
if (isset($phpunitConfiguration['timeoutForLargeTests']) && !isset($arguments['timeoutForLargeTests'])) {
$arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests'];
}
if (isset($phpunitConfiguration['reportUselessTests']) && !isset($arguments['reportUselessTests'])) {
$arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests'];
}
if (isset($phpunitConfiguration['strictCoverage']) && !isset($arguments['strictCoverage'])) {
$arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage'];
}
if (isset($phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage']) && !isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) {
$arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage'];
}
if (isset($phpunitConfiguration['disallowTestOutput']) && !isset($arguments['disallowTestOutput'])) {
$arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput'];
}
if (isset($phpunitConfiguration['enforceTimeLimit']) && !isset($arguments['enforceTimeLimit'])) {
$arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit'];
}
if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) && !isset($arguments['disallowTodoAnnotatedTests'])) {
$arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests'];
}
if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) && !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) {
$arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests'];
}
if (isset($phpunitConfiguration['verbose']) && !isset($arguments['verbose'])) {
$arguments['verbose'] = $phpunitConfiguration['verbose'];
}
if (isset($phpunitConfiguration['reverseDefectList']) && !isset($arguments['reverseList'])) {
$arguments['reverseList'] = $phpunitConfiguration['reverseDefectList'];
}
if (isset($phpunitConfiguration['forceCoversAnnotation']) && !isset($arguments['forceCoversAnnotation'])) {
$arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation'];
}
if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) && !isset($arguments['disableCodeCoverageIgnore'])) {
$arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore'];
}
if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) && !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) {
$arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively'];
}
$groupCliArgs = [];
if (!empty($arguments['groups'])) {
$groupCliArgs = $arguments['groups'];
}
$groupConfiguration = $arguments['configuration']->getGroupConfiguration();
if (!empty($groupConfiguration['include']) && !isset($arguments['groups'])) {
$arguments['groups'] = $groupConfiguration['include'];
}
if (!empty($groupConfiguration['exclude']) && !isset($arguments['excludeGroups'])) {
$arguments['excludeGroups'] = \array_diff($groupConfiguration['exclude'], $groupCliArgs);
}
foreach ($arguments['configuration']->getListenerConfiguration() as $listener) {
if (!\class_exists($listener['class'], false) &&
$listener['file'] !== '') {
require_once $listener['file'];
}
if (!\class_exists($listener['class'])) {
throw new Exception(
\sprintf(
'Class "%s" does not exist',
$listener['class']
)
);
}
$listenerClass = new ReflectionClass($listener['class']);
if (!$listenerClass->implementsInterface(TestListener::class)) {
throw new Exception(
\sprintf(
'Class "%s" does not implement the PHPUnit\Framework\TestListener interface',
$listener['class']
)
);
}
if (\count($listener['arguments']) == 0) {
$listener = new $listener['class'];
} else {
$listener = $listenerClass->newInstanceArgs(
$listener['arguments']
);
}
$arguments['listeners'][] = $listener;
}
$loggingConfiguration = $arguments['configuration']->getLoggingConfiguration();
if (isset($loggingConfiguration['coverage-clover']) && !isset($arguments['coverageClover'])) {
$arguments['coverageClover'] = $loggingConfiguration['coverage-clover'];
}
if (isset($loggingConfiguration['coverage-crap4j']) && !isset($arguments['coverageCrap4J'])) {
$arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j'];
if (isset($loggingConfiguration['crap4jThreshold']) && !isset($arguments['crap4jThreshold'])) {
$arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold'];
}
}
if (isset($loggingConfiguration['coverage-html']) && !isset($arguments['coverageHtml'])) {
if (isset($loggingConfiguration['lowUpperBound']) && !isset($arguments['reportLowUpperBound'])) {
$arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound'];
}
if (isset($loggingConfiguration['highLowerBound']) && !isset($arguments['reportHighLowerBound'])) {
$arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound'];
}
$arguments['coverageHtml'] = $loggingConfiguration['coverage-html'];
}
if (isset($loggingConfiguration['coverage-php']) && !isset($arguments['coveragePHP'])) {
$arguments['coveragePHP'] = $loggingConfiguration['coverage-php'];
}
if (isset($loggingConfiguration['coverage-text']) && !isset($arguments['coverageText'])) {
$arguments['coverageText'] = $loggingConfiguration['coverage-text'];
if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) {
$arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles'];
} else {
$arguments['coverageTextShowUncoveredFiles'] = false;
}
if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) {
$arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary'];
} else {
$arguments['coverageTextShowOnlySummary'] = false;
}
}
if (isset($loggingConfiguration['coverage-xml']) && !isset($arguments['coverageXml'])) {
$arguments['coverageXml'] = $loggingConfiguration['coverage-xml'];
}
if (isset($loggingConfiguration['plain'])) {
$arguments['listeners'][] = new ResultPrinter(
$loggingConfiguration['plain'],
true
);
}
if (isset($loggingConfiguration['teamcity']) && !isset($arguments['teamcityLogfile'])) {
$arguments['teamcityLogfile'] = $loggingConfiguration['teamcity'];
}
if (isset($loggingConfiguration['junit']) && !isset($arguments['junitLogfile'])) {
$arguments['junitLogfile'] = $loggingConfiguration['junit'];
}
if (isset($loggingConfiguration['testdox-html']) && !isset($arguments['testdoxHTMLFile'])) {
$arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html'];
}
if (isset($loggingConfiguration['testdox-text']) && !isset($arguments['testdoxTextFile'])) {
$arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text'];
}
if (isset($loggingConfiguration['testdox-xml']) && !isset($arguments['testdoxXMLFile'])) {
$arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml'];
}
$testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration();
if (isset($testdoxGroupConfiguration['include']) &&
!isset($arguments['testdoxGroups'])) {
$arguments['testdoxGroups'] = $testdoxGroupConfiguration['include'];
}
if (isset($testdoxGroupConfiguration['exclude']) &&
!isset($arguments['testdoxExcludeGroups'])) {
$arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude'];
}
}
$arguments['addUncoveredFilesFromWhitelist'] = $arguments['addUncoveredFilesFromWhitelist'] ?? true;
$arguments['backupGlobals'] = $arguments['backupGlobals'] ?? null;
$arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? null;
$arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? null;
$arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? false;
$arguments['cacheTokens'] = $arguments['cacheTokens'] ?? false;
$arguments['colors'] = $arguments['colors'] ?? ResultPrinter::COLOR_DEFAULT;
$arguments['columns'] = $arguments['columns'] ?? 80;
$arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? true;
$arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? true;
$arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? true;
$arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? true;
$arguments['crap4jThreshold'] = $arguments['crap4jThreshold'] ?? 30;
$arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? false;
$arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? false;
$arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? false;
$arguments['excludeGroups'] = $arguments['excludeGroups'] ?? [];
$arguments['failOnRisky'] = $arguments['failOnRisky'] ?? false;
$arguments['failOnWarning'] = $arguments['failOnWarning'] ?? false;
$arguments['groups'] = $arguments['groups'] ?? [];
$arguments['processIsolation'] = $arguments['processIsolation'] ?? false;
$arguments['processUncoveredFilesFromWhitelist'] = $arguments['processUncoveredFilesFromWhitelist'] ?? false;
$arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? false;
$arguments['repeat'] = $arguments['repeat'] ?? false;
$arguments['reportHighLowerBound'] = $arguments['reportHighLowerBound'] ?? 90;
$arguments['reportLowUpperBound'] = $arguments['reportLowUpperBound'] ?? 50;
$arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? true;
$arguments['reverseList'] = $arguments['reverseList'] ?? false;
$arguments['stopOnError'] = $arguments['stopOnError'] ?? false;
$arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? false;
$arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? false;
$arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? false;
$arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? false;
$arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? false;
$arguments['strictCoverage'] = $arguments['strictCoverage'] ?? false;
$arguments['testdoxExcludeGroups'] = $arguments['testdoxExcludeGroups'] ?? [];
$arguments['testdoxGroups'] = $arguments['testdoxGroups'] ?? [];
$arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? 60;
$arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? 10;
$arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? 1;
$arguments['verbose'] = $arguments['verbose'] ?? false;
}
/**
* @param string $type
* @param string $message
*/
private function writeMessage($type, $message)
{
if (!$this->messagePrinted) {
$this->write("\n");
}
$this->write(
\sprintf(
"%-15s%s\n",
$type . ':',
$message
)
);
$this->messagePrinted = true;
}
}
PK (M& src/TextUI/Command.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use File_Iterator_Facade;
use PharIo\Manifest\ApplicationName;
use PharIo\Manifest\Exception as ManifestException;
use PharIo\Manifest\ManifestLoader;
use PharIo\Version\Version as PharIoVersion;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Runner\TestSuiteLoader;
use PHPUnit\Runner\Version;
use PHPUnit\Util\Configuration;
use PHPUnit\Util\ConfigurationGenerator;
use PHPUnit\Util\Fileloader;
use PHPUnit\Util\Filesystem;
use PHPUnit\Util\Getopt;
use PHPUnit\Util\Log\TeamCity;
use PHPUnit\Util\Printer;
use PHPUnit\Util\TestDox\TextResultPrinter;
use PHPUnit\Util\TextTestListRenderer;
use PHPUnit\Util\XmlTestListRenderer;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\Report\PHP;
use Throwable;
/**
* A TestRunner for the Command Line Interface (CLI)
* PHP SAPI Module.
*/
class Command
{
/**
* @var array
*/
protected $arguments = [
'listGroups' => false,
'listSuites' => false,
'listTests' => false,
'listTestsXml' => false,
'loader' => null,
'useDefaultConfiguration' => true,
'loadedExtensions' => [],
'notLoadedExtensions' => []
];
/**
* @var array
*/
protected $options = [];
/**
* @var array
*/
protected $longOptions = [
'atleast-version=' => null,
'bootstrap=' => null,
'check-version' => null,
'colors==' => null,
'columns=' => null,
'configuration=' => null,
'coverage-clover=' => null,
'coverage-crap4j=' => null,
'coverage-html=' => null,
'coverage-php=' => null,
'coverage-text==' => null,
'coverage-xml=' => null,
'debug' => null,
'disallow-test-output' => null,
'disallow-resource-usage' => null,
'disallow-todo-tests' => null,
'enforce-time-limit' => null,
'exclude-group=' => null,
'filter=' => null,
'generate-configuration' => null,
'globals-backup' => null,
'group=' => null,
'help' => null,
'include-path=' => null,
'list-groups' => null,
'list-suites' => null,
'list-tests' => null,
'list-tests-xml=' => null,
'loader=' => null,
'log-junit=' => null,
'log-teamcity=' => null,
'no-configuration' => null,
'no-coverage' => null,
'no-logging' => null,
'no-extensions' => null,
'printer=' => null,
'process-isolation' => null,
'repeat=' => null,
'dont-report-useless-tests' => null,
'reverse-list' => null,
'static-backup' => null,
'stderr' => null,
'stop-on-error' => null,
'stop-on-failure' => null,
'stop-on-warning' => null,
'stop-on-incomplete' => null,
'stop-on-risky' => null,
'stop-on-skipped' => null,
'fail-on-warning' => null,
'fail-on-risky' => null,
'strict-coverage' => null,
'disable-coverage-ignore' => null,
'strict-global-state' => null,
'teamcity' => null,
'testdox' => null,
'testdox-group=' => null,
'testdox-exclude-group=' => null,
'testdox-html=' => null,
'testdox-text=' => null,
'testdox-xml=' => null,
'test-suffix=' => null,
'testsuite=' => null,
'verbose' => null,
'version' => null,
'whitelist=' => null
];
/**
* @var bool
*/
private $versionStringPrinted = false;
/**
* @param bool $exit
*/
public static function main($exit = true)
{
$command = new static;
return $command->run($_SERVER['argv'], $exit);
}
/**
* @param array $argv
* @param bool $exit
*
* @return int
*/
public function run(array $argv, $exit = true)
{
$this->handleArguments($argv);
$runner = $this->createRunner();
if ($this->arguments['test'] instanceof Test) {
$suite = $this->arguments['test'];
} else {
$suite = $runner->getTest(
$this->arguments['test'],
$this->arguments['testFile'],
$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->doRun($suite, $this->arguments, $exit);
} catch (Exception $e) {
print $e->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.
*
* @return TestRunner
*/
protected function createRunner()
{
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
*
*
* 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);
* }
*
* }
*
*
* @param array $argv
*/
protected function handleArguments(array $argv)
{
try {
$this->options = Getopt::getopt(
$argv,
'd:c:hv',
\array_keys($this->longOptions)
);
} catch (Exception $t) {
$this->exitWithErrorMessage($t->getMessage());
}
foreach ($this->options[0] as $option) {
switch ($option[0]) {
case '--colors':
$this->arguments['colors'] = $option[1] ?: ResultPrinter::COLOR_AUTO;
break;
case '--bootstrap':
$this->arguments['bootstrap'] = $option[1];
break;
case '--columns':
if (\is_numeric($option[1])) {
$this->arguments['columns'] = (int) $option[1];
} elseif ($option[1] === 'max') {
$this->arguments['columns'] = 'max';
}
break;
case 'c':
case '--configuration':
$this->arguments['configuration'] = $option[1];
break;
case '--coverage-clover':
$this->arguments['coverageClover'] = $option[1];
break;
case '--coverage-crap4j':
$this->arguments['coverageCrap4J'] = $option[1];
break;
case '--coverage-html':
$this->arguments['coverageHtml'] = $option[1];
break;
case '--coverage-php':
$this->arguments['coveragePHP'] = $option[1];
break;
case '--coverage-text':
if ($option[1] === null) {
$option[1] = 'php://stdout';
}
$this->arguments['coverageText'] = $option[1];
$this->arguments['coverageTextShowUncoveredFiles'] = false;
$this->arguments['coverageTextShowOnlySummary'] = false;
break;
case '--coverage-xml':
$this->arguments['coverageXml'] = $option[1];
break;
case 'd':
$ini = \explode('=', $option[1]);
if (isset($ini[0])) {
if (isset($ini[1])) {
\ini_set($ini[0], $ini[1]);
} else {
\ini_set($ini[0], true);
}
}
break;
case '--debug':
$this->arguments['debug'] = true;
break;
case 'h':
case '--help':
$this->showHelp();
exit(TestRunner::SUCCESS_EXIT);
break;
case '--filter':
$this->arguments['filter'] = $option[1];
break;
case '--testsuite':
$this->arguments['testsuite'] = $option[1];
break;
case '--generate-configuration':
$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));
if ($bootstrapScript === '') {
$bootstrapScript = 'vendor/autoload.php';
}
if ($testsDirectory === '') {
$testsDirectory = 'tests';
}
if ($src === '') {
$src = 'src';
}
$generator = new ConfigurationGenerator;
\file_put_contents(
'phpunit.xml',
$generator->generateDefaultConfiguration(
Version::series(),
$bootstrapScript,
$testsDirectory,
$src
)
);
print PHP_EOL . 'Generated phpunit.xml in ' . \getcwd() . PHP_EOL;
exit(TestRunner::SUCCESS_EXIT);
break;
case '--group':
$this->arguments['groups'] = \explode(',', $option[1]);
break;
case '--exclude-group':
$this->arguments['excludeGroups'] = \explode(
',',
$option[1]
);
break;
case '--test-suffix':
$this->arguments['testSuffixes'] = \explode(
',',
$option[1]
);
break;
case '--include-path':
$includePath = $option[1];
break;
case '--list-groups':
$this->arguments['listGroups'] = true;
break;
case '--list-suites':
$this->arguments['listSuites'] = true;
break;
case '--list-tests':
$this->arguments['listTests'] = true;
break;
case '--list-tests-xml':
$this->arguments['listTestsXml'] = $option[1];
break;
case '--printer':
$this->arguments['printer'] = $option[1];
break;
case '--loader':
$this->arguments['loader'] = $option[1];
break;
case '--log-junit':
$this->arguments['junitLogfile'] = $option[1];
break;
case '--log-teamcity':
$this->arguments['teamcityLogfile'] = $option[1];
break;
case '--process-isolation':
$this->arguments['processIsolation'] = true;
break;
case '--repeat':
$this->arguments['repeat'] = (int) $option[1];
break;
case '--stderr':
$this->arguments['stderr'] = true;
break;
case '--stop-on-error':
$this->arguments['stopOnError'] = true;
break;
case '--stop-on-failure':
$this->arguments['stopOnFailure'] = true;
break;
case '--stop-on-warning':
$this->arguments['stopOnWarning'] = true;
break;
case '--stop-on-incomplete':
$this->arguments['stopOnIncomplete'] = true;
break;
case '--stop-on-risky':
$this->arguments['stopOnRisky'] = true;
break;
case '--stop-on-skipped':
$this->arguments['stopOnSkipped'] = true;
break;
case '--fail-on-warning':
$this->arguments['failOnWarning'] = true;
break;
case '--fail-on-risky':
$this->arguments['failOnRisky'] = true;
break;
case '--teamcity':
$this->arguments['printer'] = TeamCity::class;
break;
case '--testdox':
$this->arguments['printer'] = TextResultPrinter::class;
break;
case '--testdox-group':
$this->arguments['testdoxGroups'] = \explode(
',',
$option[1]
);
break;
case '--testdox-exclude-group':
$this->arguments['testdoxExcludeGroups'] = \explode(
',',
$option[1]
);
break;
case '--testdox-html':
$this->arguments['testdoxHTMLFile'] = $option[1];
break;
case '--testdox-text':
$this->arguments['testdoxTextFile'] = $option[1];
break;
case '--testdox-xml':
$this->arguments['testdoxXMLFile'] = $option[1];
break;
case '--no-configuration':
$this->arguments['useDefaultConfiguration'] = false;
break;
case '--no-extensions':
$this->arguments['noExtensions'] = true;
break;
case '--no-coverage':
$this->arguments['noCoverage'] = true;
break;
case '--no-logging':
$this->arguments['noLogging'] = true;
break;
case '--globals-backup':
$this->arguments['backupGlobals'] = true;
break;
case '--static-backup':
$this->arguments['backupStaticAttributes'] = true;
break;
case 'v':
case '--verbose':
$this->arguments['verbose'] = true;
break;
case '--atleast-version':
if (\version_compare(Version::id(), $option[1], '>=')) {
exit(TestRunner::SUCCESS_EXIT);
}
exit(TestRunner::FAILURE_EXIT);
break;
case '--version':
$this->printVersionString();
exit(TestRunner::SUCCESS_EXIT);
break;
case '--dont-report-useless-tests':
$this->arguments['reportUselessTests'] = false;
break;
case '--strict-coverage':
$this->arguments['strictCoverage'] = true;
break;
case '--disable-coverage-ignore':
$this->arguments['disableCodeCoverageIgnore'] = true;
break;
case '--strict-global-state':
$this->arguments['beStrictAboutChangesToGlobalState'] = true;
break;
case '--disallow-test-output':
$this->arguments['disallowTestOutput'] = true;
break;
case '--disallow-resource-usage':
$this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true;
break;
case '--enforce-time-limit':
$this->arguments['enforceTimeLimit'] = true;
break;
case '--disallow-todo-tests':
$this->arguments['disallowTodoAnnotatedTests'] = true;
break;
case '--reverse-list':
$this->arguments['reverseList'] = true;
break;
case '--check-version':
$this->handleVersionCheck();
break;
case '--whitelist':
$this->arguments['whitelist'] = $option[1];
break;
default:
$optionName = \str_replace('--', '', $option[0]);
$handler = null;
if (isset($this->longOptions[$optionName])) {
$handler = $this->longOptions[$optionName];
} elseif (isset($this->longOptions[$optionName . '='])) {
$handler = $this->longOptions[$optionName . '='];
}
if (isset($handler) && \is_callable([$this, $handler])) {
$this->$handler($option[1]);
}
}
}
$this->handleCustomTestSuite();
if (!isset($this->arguments['test'])) {
if (isset($this->options[1][0])) {
$this->arguments['test'] = $this->options[1][0];
}
if (isset($this->options[1][1])) {
$this->arguments['testFile'] = \realpath($this->options[1][1]);
} else {
$this->arguments['testFile'] = '';
}
if (isset($this->arguments['test']) &&
\is_file($this->arguments['test']) &&
\substr($this->arguments['test'], -5, 5) != '.phpt') {
$this->arguments['testFile'] = \realpath($this->arguments['test']);
$this->arguments['test'] = \substr($this->arguments['test'], 0, \strrpos($this->arguments['test'], '.'));
}
}
if (!isset($this->arguments['testSuffixes'])) {
$this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
}
if (isset($includePath)) {
\ini_set(
'include_path',
$includePath . PATH_SEPARATOR . \ini_get('include_path')
);
}
if ($this->arguments['loader'] !== null) {
$this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
}
if (isset($this->arguments['configuration']) &&
\is_dir($this->arguments['configuration'])) {
$configurationFile = $this->arguments['configuration'] . '/phpunit.xml';
if (\file_exists($configurationFile)) {
$this->arguments['configuration'] = \realpath(
$configurationFile
);
} elseif (\file_exists($configurationFile . '.dist')) {
$this->arguments['configuration'] = \realpath(
$configurationFile . '.dist'
);
}
} elseif (!isset($this->arguments['configuration']) &&
$this->arguments['useDefaultConfiguration']) {
if (\file_exists('phpunit.xml')) {
$this->arguments['configuration'] = \realpath('phpunit.xml');
} elseif (\file_exists('phpunit.xml.dist')) {
$this->arguments['configuration'] = \realpath(
'phpunit.xml.dist'
);
}
}
if (isset($this->arguments['configuration'])) {
try {
$configuration = Configuration::getInstance(
$this->arguments['configuration']
);
} catch (Throwable $t) {
print $t->getMessage() . PHP_EOL;
exit(TestRunner::FAILURE_EXIT);
}
$phpunitConfiguration = $configuration->getPHPUnitConfiguration();
$configuration->handlePHPConfiguration();
/*
* Issue #1216
*/
if (isset($this->arguments['bootstrap'])) {
$this->handleBootstrap($this->arguments['bootstrap']);
} elseif (isset($phpunitConfiguration['bootstrap'])) {
$this->handleBootstrap($phpunitConfiguration['bootstrap']);
}
/*
* Issue #657
*/
if (isset($phpunitConfiguration['stderr']) && !isset($this->arguments['stderr'])) {
$this->arguments['stderr'] = $phpunitConfiguration['stderr'];
}
if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && \extension_loaded('phar')) {
$this->handleExtensions($phpunitConfiguration['extensionsDirectory']);
}
if (isset($phpunitConfiguration['columns']) && !isset($this->arguments['columns'])) {
$this->arguments['columns'] = $phpunitConfiguration['columns'];
}
if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) {
if (isset($phpunitConfiguration['printerFile'])) {
$file = $phpunitConfiguration['printerFile'];
} else {
$file = '';
}
$this->arguments['printer'] = $this->handlePrinter(
$phpunitConfiguration['printerClass'],
$file
);
}
if (isset($phpunitConfiguration['testSuiteLoaderClass'])) {
if (isset($phpunitConfiguration['testSuiteLoaderFile'])) {
$file = $phpunitConfiguration['testSuiteLoaderFile'];
} else {
$file = '';
}
$this->arguments['loader'] = $this->handleLoader(
$phpunitConfiguration['testSuiteLoaderClass'],
$file
);
}
if (!isset($this->arguments['testsuite']) && isset($phpunitConfiguration['defaultTestSuite'])) {
$this->arguments['testsuite'] = $phpunitConfiguration['defaultTestSuite'];
}
if (!isset($this->arguments['test'])) {
$testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? null);
if ($testSuite !== null) {
$this->arguments['test'] = $testSuite;
}
}
} 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['test']) && \is_string($this->arguments['test']) && \substr($this->arguments['test'], -5, 5) == '.phpt') {
$test = new PhptTestCase($this->arguments['test']);
$this->arguments['test'] = new TestSuite;
$this->arguments['test']->addTest($test);
}
if (!isset($this->arguments['test'])) {
$this->showHelp();
exit(TestRunner::EXCEPTION_EXIT);
}
}
/**
* Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation.
*
* @param string $loaderClass
* @param string $loaderFile
*
* @return TestSuiteLoader|null
*/
protected function handleLoader($loaderClass, $loaderFile = '')
{
if (!\class_exists($loaderClass, false)) {
if ($loaderFile == '') {
$loaderFile = Filesystem::classNameToFilename(
$loaderClass
);
}
$loaderFile = \stream_resolve_include_path($loaderFile);
if ($loaderFile) {
require $loaderFile;
}
}
if (\class_exists($loaderClass, false)) {
$class = new ReflectionClass($loaderClass);
if ($class->implementsInterface(TestSuiteLoader::class) &&
$class->isInstantiable()) {
return $class->newInstance();
}
}
if ($loaderClass == StandardTestSuiteLoader::class) {
return;
}
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as loader.',
$loaderClass
)
);
}
/**
* Handles the loading of the PHPUnit\Util\Printer implementation.
*
* @param string $printerClass
* @param string $printerFile
*
* @return Printer|string|null
*/
protected function handlePrinter($printerClass, $printerFile = '')
{
if (!\class_exists($printerClass, false)) {
if ($printerFile == '') {
$printerFile = Filesystem::classNameToFilename(
$printerClass
);
}
$printerFile = \stream_resolve_include_path($printerFile);
if ($printerFile) {
require $printerFile;
}
}
if (!\class_exists($printerClass)) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class does not exist',
$printerClass
)
);
}
$class = new ReflectionClass($printerClass);
if (!$class->implementsInterface(TestListener::class)) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class does not implement %s',
$printerClass,
TestListener::class
)
);
}
if (!$class->isSubclassOf(Printer::class)) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class does not extend %s',
$printerClass,
Printer::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.
*
* @param string $filename
*/
protected function handleBootstrap($filename)
{
try {
Fileloader::checkAndLoad($filename);
} catch (Exception $e) {
$this->exitWithErrorMessage($e->getMessage());
}
}
protected function handleVersionCheck()
{
$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()
{
$this->printVersionString();
print <<
Code Coverage Options:
--coverage-clover Generate code coverage report in Clover XML format.
--coverage-crap4j Generate code coverage report in Crap4J XML format.
--coverage-html Generate code coverage report in HTML format.
--coverage-php Export PHP_CodeCoverage object to file.
--coverage-text= Generate code coverage report in text format.
Default: Standard output.
--coverage-xml Generate code coverage report in PHPUnit XML format.
--whitelist Whitelist for code coverage analysis.
--disable-coverage-ignore Disable annotations for ignoring code coverage.
--no-coverage Ignore code coverage configuration.
Logging Options:
--log-junit Log test execution in JUnit XML format to file.
--log-teamcity Log test execution in TeamCity format to file.
--testdox-html Write agile documentation in HTML format to file.
--testdox-text Write agile documentation in Text format to file.
--testdox-xml Write agile documentation in XML format to file.
--reverse-list Print defects in reverse order
Test Selection Options:
--filter Filter which tests to run.
--testsuite Filter which testsuite to run.
--group ... Only runs tests from the specified group(s).
--exclude-group ... Exclude tests from the specified group(s).
--list-groups List available test groups.
--list-suites List available test suites.
--list-tests List available tests.
--list-tests-xml List available tests in XML format.
--test-suffix ... Only search for test in files with specified
suffix(es). Default: Test.php,.phpt
Test Execution Options:
--dont-report-useless-tests Do not report tests that do not test anything.
--strict-coverage Be strict about @covers annotation usage.
--strict-global-state Be strict about changes to global state
--disallow-test-output Be strict about output during tests.
--disallow-resource-usage Be strict about resource usage during small tests.
--enforce-time-limit Enforce time limit based on test size.
--disallow-todo-tests Disallow @todo-annotated tests.
--process-isolation Run each test in a separate PHP process.
--globals-backup Backup and restore \$GLOBALS for each test.
--static-backup Backup and restore static attributes for each test.
--colors= Use colors in output ("never", "auto" or "always").
--columns Number of columns to use for progress output.
--columns max Use maximum number of columns for progress output.
--stderr Write to STDERR instead of STDOUT.
--stop-on-error Stop execution upon first error.
--stop-on-failure Stop execution upon first error or failure.
--stop-on-warning Stop execution upon first warning.
--stop-on-risky Stop execution upon first risky test.
--stop-on-skipped Stop execution upon first skipped test.
--stop-on-incomplete Stop execution upon first incomplete test.
--fail-on-warning Treat tests with warnings as failures.
--fail-on-risky Treat risky tests as failures.
-v|--verbose Output more verbose information.
--debug Display debugging information.
--loader TestSuiteLoader implementation to use.
--repeat Runs the test(s) repeatedly.
--teamcity Report test execution progress in TeamCity format.
--testdox Report test execution progress in TestDox format.
--testdox-group Only include tests from the specified group(s).
--testdox-exclude-group Exclude tests from the specified group(s).
--printer TestListener implementation to use.
Configuration Options:
--bootstrap A "bootstrap" PHP file that is run before the tests.
-c|--configuration Read configuration from XML file.
--no-configuration Ignore default configuration file (phpunit.xml).
--no-logging Ignore logging configuration.
--no-extensions Do not load PHPUnit extensions.
--include-path Prepend PHP's include_path with given path(s).
-d key[=value] Sets a php.ini value.
--generate-configuration Generate configuration file with suggested settings.
Miscellaneous Options:
-h|--help Prints this usage information.
--version Prints the version and exits.
--atleast-version Checks that version is greater than min and exits.
--check-version Check whether PHPUnit is the latest version.
EOT;
}
/**
* Custom callback for test suite discovery.
*/
protected function handleCustomTestSuite()
{
}
private function printVersionString()
{
if ($this->versionStringPrinted) {
return;
}
print Version::getVersionString() . PHP_EOL . PHP_EOL;
$this->versionStringPrinted = true;
}
/**
* @param string $message
*/
private function exitWithErrorMessage($message)
{
$this->printVersionString();
print $message . PHP_EOL;
exit(TestRunner::FAILURE_EXIT);
}
/**
* @param string $directory
*/
private function handleExtensions($directory)
{
$facade = new File_Iterator_Facade;
foreach ($facade->getFilesAsArray($directory, '.phar') as $file) {
if (!\file_exists('phar://' . $file . '/manifest.xml')) {
$this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
continue;
}
try {
$applicationName = new ApplicationName('phpunit/phpunit');
$version = new PharIoVersion(Version::series());
$manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml');
if (!$manifest->isExtensionFor($applicationName)) {
$this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
continue;
}
if (!$manifest->isExtensionFor($applicationName, $version)) {
$this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit';
continue;
}
} catch (ManifestException $e) {
$this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage();
continue;
}
require $file;
$this->arguments['loadedExtensions'][] = $manifest->getName() . ' ' . $manifest->getVersion()->getVersionString();
}
}
private function handleListGroups(TestSuite $suite, bool $exit): int
{
$this->printVersionString();
print 'Available test group(s):' . PHP_EOL;
$groups = $suite->getGroups();
\sort($groups);
foreach ($groups as $group) {
\printf(
' - %s' . PHP_EOL,
$group
);
}
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
private function handleListSuites(bool $exit): int
{
$this->printVersionString();
print 'Available test suite(s):' . PHP_EOL;
$configuration = Configuration::getInstance(
$this->arguments['configuration']
);
$suiteNames = $configuration->getTestSuiteNames();
foreach ($suiteNames as $suiteName) {
\printf(
' - %s' . PHP_EOL,
$suiteName
);
}
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
private function handleListTests(TestSuite $suite, bool $exit): int
{
$this->printVersionString();
$renderer = new TextTestListRenderer;
print $renderer->render($suite);
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int
{
$this->printVersionString();
$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;
}
}
PK (M>;U U src/Util/Type.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* Utility class for textual type (and value) representation.
*/
class Type
{
/**
* @param string $type
*
* @return bool
*/
public static function isType($type)
{
return \in_array(
$type,
[
'numeric',
'integer',
'int',
'float',
'string',
'boolean',
'bool',
'null',
'array',
'object',
'resource',
'scalar'
]
);
}
}
PK (MGO` src/Util/Getopt.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* Command-line options parsing class.
*/
class Getopt
{
public static function getopt(array $args, $short_options, $long_options = null)
{
if (empty($args)) {
return [[], []];
}
$opts = [];
$non_opts = [];
if ($long_options) {
\sort($long_options);
}
if (isset($args[0][0]) && $args[0][0] != '-') {
\array_shift($args);
}
\reset($args);
$args = \array_map('trim', $args);
while (false !== $arg = \current($args)) {
$i = \key($args);
\next($args);
if ($arg == '') {
continue;
}
if ($arg == '--') {
$non_opts = \array_merge($non_opts, \array_slice($args, $i + 1));
break;
}
if ($arg[0] != '-' || (\strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) {
$non_opts[] = $args[$i];
continue;
} elseif (\strlen($arg) > 1 && $arg[1] == '-') {
self::parseLongOption(
\substr($arg, 2),
$long_options,
$opts,
$args
);
} else {
self::parseShortOption(
\substr($arg, 1),
$short_options,
$opts,
$args
);
}
}
return [$opts, $non_opts];
}
protected static function parseShortOption($arg, $short_options, &$opts, &$args)
{
$argLen = \strlen($arg);
for ($i = 0; $i < $argLen; $i++) {
$opt = $arg[$i];
$opt_arg = null;
if (($spec = \strstr($short_options, $opt)) === false || $arg[$i] == ':') {
throw new Exception(
"unrecognized option -- $opt"
);
}
if (\strlen($spec) > 1 && $spec[1] == ':') {
if ($i + 1 < $argLen) {
$opts[] = [$opt, \substr($arg, $i + 1)];
break;
}
if (!(\strlen($spec) > 2 && $spec[2] == ':')) {
if (false === $opt_arg = \current($args)) {
throw new Exception(
"option requires an argument -- $opt"
);
}
\next($args);
}
}
$opts[] = [$opt, $opt_arg];
}
}
protected static function parseLongOption($arg, $long_options, &$opts, &$args)
{
$count = \count($long_options);
$list = \explode('=', $arg);
$opt = $list[0];
$opt_arg = null;
if (\count($list) > 1) {
$opt_arg = $list[1];
}
$opt_len = \strlen($opt);
for ($i = 0; $i < $count; $i++) {
$long_opt = $long_options[$i];
$opt_start = \substr($long_opt, 0, $opt_len);
if ($opt_start != $opt) {
continue;
}
$opt_rest = \substr($long_opt, $opt_len);
if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count &&
$opt == \substr($long_options[$i + 1], 0, $opt_len)) {
throw new Exception(
"option --$opt is ambiguous"
);
}
if (\substr($long_opt, -1) == '=') {
if (\substr($long_opt, -2) != '==') {
if (!\strlen($opt_arg)) {
if (false === $opt_arg = \current($args)) {
throw new Exception(
"option --$opt requires an argument"
);
}
\next($args);
}
}
} elseif ($opt_arg) {
throw new Exception(
"option --$opt doesn't allow an argument"
);
}
$full_option = '--' . \preg_replace('/={1,2}$/', '', $long_opt);
$opts[] = [$full_option, $opt_arg];
return;
}
throw new Exception("unrecognized option --$opt");
}
}
PK (MA " src/Util/PHP/DefaultPhpProcess.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\PHP;
use PHPUnit\Framework\Exception;
/**
* Default utility for PHP sub-processes.
*/
class DefaultPhpProcess extends AbstractPhpProcess
{
/**
* @var string
*/
protected $tempFile;
/**
* @var bool
*/
protected $useTempFile = false;
/**
* Runs a single job (PHP code) using a separate PHP process.
*
* @param string $job
* @param array $settings
*
* @return array
*
* @throws Exception
*/
public function runJob($job, array $settings = [])
{
if ($this->useTempFile || $this->stdin) {
if (!($this->tempFile = \tempnam(\sys_get_temp_dir(), 'PHPUnit')) ||
\file_put_contents($this->tempFile, $job) === false) {
throw new Exception(
'Unable to write temporary file'
);
}
$job = $this->stdin;
}
return $this->runProcess($job, $settings);
}
/**
* Returns an array of file handles to be used in place of pipes
*
* @return array
*/
protected function getHandles()
{
return [];
}
/**
* Handles creating the child process and returning the STDOUT and STDERR
*
* @param string $job
* @param array $settings
*
* @return array
*
* @throws Exception
*/
protected function runProcess($job, $settings)
{
$handles = $this->getHandles();
$env = null;
if ($this->env) {
$env = $_SERVER ?? [];
unset($env['argv'], $env['argc']);
$env = \array_merge($env, $this->env);
foreach ($env as $envKey => $envVar) {
if (\is_array($envVar)) {
unset($env[$envKey]);
}
}
}
$pipeSpec = [
0 => $handles[0] ?? ['pipe', 'r'],
1 => $handles[1] ?? ['pipe', 'w'],
2 => $handles[2] ?? ['pipe', 'w'],
];
$process = \proc_open(
$this->getCommand($settings, $this->tempFile),
$pipeSpec,
$pipes,
null,
$env
);
if (!\is_resource($process)) {
throw new Exception(
'Unable to spawn worker process'
);
}
if ($job) {
$this->process($pipes[0], $job);
}
\fclose($pipes[0]);
if ($this->timeout) {
$stderr = $stdout = '';
unset($pipes[0]);
while (true) {
$r = $pipes;
$w = null;
$e = null;
$n = @\stream_select($r, $w, $e, $this->timeout);
if ($n === false) {
break;
} elseif ($n === 0) {
\proc_terminate($process, 9);
throw new Exception(
\sprintf(
'Job execution aborted after %d seconds',
$this->timeout
)
);
} elseif ($n > 0) {
foreach ($r as $pipe) {
$pipeOffset = 0;
foreach ($pipes as $i => $origPipe) {
if ($pipe == $origPipe) {
$pipeOffset = $i;
break;
}
}
if (!$pipeOffset) {
break;
}
$line = \fread($pipe, 8192);
if (\strlen($line) == 0) {
\fclose($pipes[$pipeOffset]);
unset($pipes[$pipeOffset]);
} else {
if ($pipeOffset == 1) {
$stdout .= $line;
} else {
$stderr .= $line;
}
}
}
if (empty($pipes)) {
break;
}
}
}
} else {
if (isset($pipes[1])) {
$stdout = \stream_get_contents($pipes[1]);
\fclose($pipes[1]);
}
if (isset($pipes[2])) {
$stderr = \stream_get_contents($pipes[2]);
\fclose($pipes[2]);
}
}
if (isset($handles[1])) {
\rewind($handles[1]);
$stdout = \stream_get_contents($handles[1]);
\fclose($handles[1]);
}
if (isset($handles[2])) {
\rewind($handles[2]);
$stderr = \stream_get_contents($handles[2]);
\fclose($handles[2]);
}
\proc_close($process);
$this->cleanup();
return ['stdout' => $stdout, 'stderr' => $stderr];
}
/**
* @param resource $pipe
* @param string $job
*
* @throws Exception
*/
protected function process($pipe, $job)
{
\fwrite($pipe, $job);
}
protected function cleanup()
{
if ($this->tempFile) {
\unlink($this->tempFile);
}
}
}
PK (M.lw) ) # src/Util/PHP/AbstractPhpProcess.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\PHP;
use __PHP_Incomplete_Class;
use ErrorException;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use PHPUnit\Util\InvalidArgumentHelper;
use SebastianBergmann\Environment\Runtime;
/**
* Utility methods for PHP sub-processes.
*/
abstract class AbstractPhpProcess
{
/**
* @var Runtime
*/
protected $runtime;
/**
* @var bool
*/
protected $stderrRedirection = false;
/**
* @var string
*/
protected $stdin = '';
/**
* @var string
*/
protected $args = '';
/**
* @var array
*/
protected $env = [];
/**
* @var int
*/
protected $timeout = 0;
/**
* Creates internal Runtime instance.
*/
public function __construct()
{
$this->runtime = new Runtime();
}
/**
* Defines if should use STDERR redirection or not.
*
* Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT.
*
* @throws Exception
*
* @param bool $stderrRedirection
*/
public function setUseStderrRedirection($stderrRedirection)
{
if (!\is_bool($stderrRedirection)) {
throw InvalidArgumentHelper::factory(1, 'boolean');
}
$this->stderrRedirection = $stderrRedirection;
}
/**
* Returns TRUE if uses STDERR redirection or FALSE if not.
*
* @return bool
*/
public function useStderrRedirection()
{
return $this->stderrRedirection;
}
/**
* Sets the input string to be sent via STDIN
*
* @param string $stdin
*/
public function setStdin($stdin)
{
$this->stdin = (string) $stdin;
}
/**
* Returns the input string to be sent via STDIN
*
* @return string
*/
public function getStdin()
{
return $this->stdin;
}
/**
* Sets the string of arguments to pass to the php job
*
* @param string $args
*/
public function setArgs($args)
{
$this->args = (string) $args;
}
/**
* Returns the string of arguments to pass to the php job
*
* @retrun string
*/
public function getArgs()
{
return $this->args;
}
/**
* Sets the array of environment variables to start the child process with
*
* @param array $env
*/
public function setEnv(array $env)
{
$this->env = $env;
}
/**
* Returns the array of environment variables to start the child process with
*
* @return array
*/
public function getEnv()
{
return $this->env;
}
/**
* Sets the amount of seconds to wait before timing out
*
* @param int $timeout
*/
public function setTimeout($timeout)
{
$this->timeout = (int) $timeout;
}
/**
* Returns the amount of seconds to wait before timing out
*
* @return int
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* @return AbstractPhpProcess
*/
public static function factory()
{
if (DIRECTORY_SEPARATOR == '\\') {
return new WindowsPhpProcess;
}
return new DefaultPhpProcess;
}
/**
* Runs a single test in a separate PHP process.
*
* @param string $job
* @param Test $test
* @param TestResult $result
*
* @throws Exception
*/
public function runTestJob($job, Test $test, TestResult $result)
{
$result->startTest($test);
$_result = $this->runJob($job);
$this->processChildResult(
$test,
$result,
$_result['stdout'],
$_result['stderr']
);
}
/**
* Returns the command based into the configurations.
*
* @param array $settings
* @param string|null $file
*
* @return string
*/
public function getCommand(array $settings, $file = null)
{
$command = $this->runtime->getBinary();
$command .= $this->settingsToParameters($settings);
if ('phpdbg' === PHP_SAPI) {
$command .= ' -qrr ';
if ($file) {
$command .= '-e ' . \escapeshellarg($file);
} else {
$command .= \escapeshellarg(__DIR__ . '/eval-stdin.php');
}
} elseif ($file) {
$command .= ' -f ' . \escapeshellarg($file);
}
if ($this->args) {
$command .= ' -- ' . $this->args;
}
if (true === $this->stderrRedirection) {
$command .= ' 2>&1';
}
return $command;
}
/**
* Runs a single job (PHP code) using a separate PHP process.
*
* @param string $job
* @param array $settings
*
* @return array
*
* @throws Exception
*/
abstract public function runJob($job, array $settings = []);
/**
* @param array $settings
*
* @return string
*/
protected function settingsToParameters(array $settings)
{
$buffer = '';
foreach ($settings as $setting) {
$buffer .= ' -d ' . \escapeshellarg($setting);
}
return $buffer;
}
/**
* Processes the TestResult object from an isolated process.
*
* @param Test $test
* @param TestResult $result
* @param string $stdout
* @param string $stderr
*/
private function processChildResult(Test $test, TestResult $result, $stdout, $stderr)
{
$time = 0;
if (!empty($stderr)) {
$result->addError(
$test,
new Exception(\trim($stderr)),
$time
);
} else {
\set_error_handler(function ($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
});
try {
if (\strpos($stdout, "#!/usr/bin/env php\n") === 0) {
$stdout = \substr($stdout, 19);
}
$childResult = \unserialize(\str_replace("#!/usr/bin/env php\n", '', $stdout));
\restore_error_handler();
} catch (ErrorException $e) {
\restore_error_handler();
$childResult = false;
$result->addError(
$test,
new Exception(\trim($stdout), 0, $e),
$time
);
}
if ($childResult !== false) {
if (!empty($childResult['output'])) {
$output = $childResult['output'];
}
$test->setResult($childResult['testResult']);
$test->addToAssertionCount($childResult['numAssertions']);
/** @var TestResult $childResult */
$childResult = $childResult['result'];
if ($result->getCollectCodeCoverageInformation()) {
$result->getCodeCoverage()->merge(
$childResult->getCodeCoverage()
);
}
$time = $childResult->time();
$notImplemented = $childResult->notImplemented();
$risky = $childResult->risky();
$skipped = $childResult->skipped();
$errors = $childResult->errors();
$warnings = $childResult->warnings();
$failures = $childResult->failures();
if (!empty($notImplemented)) {
$result->addError(
$test,
$this->getException($notImplemented[0]),
$time
);
} elseif (!empty($risky)) {
$result->addError(
$test,
$this->getException($risky[0]),
$time
);
} elseif (!empty($skipped)) {
$result->addError(
$test,
$this->getException($skipped[0]),
$time
);
} elseif (!empty($errors)) {
$result->addError(
$test,
$this->getException($errors[0]),
$time
);
} elseif (!empty($warnings)) {
$result->addWarning(
$test,
$this->getException($warnings[0]),
$time
);
} elseif (!empty($failures)) {
$result->addFailure(
$test,
$this->getException($failures[0]),
$time
);
}
}
}
$result->endTest($test, $time);
if (!empty($output)) {
print $output;
}
}
/**
* Gets the thrown exception from a PHPUnit\Framework\TestFailure.
*
* @param TestFailure $error
*
* @return Exception
*
* @see https://github.com/sebastianbergmann/phpunit/issues/74
*/
private function getException(TestFailure $error)
{
$exception = $error->thrownException();
if ($exception instanceof __PHP_Incomplete_Class) {
$exceptionArray = [];
foreach ((array) $exception as $key => $value) {
$key = \substr($key, \strrpos($key, "\0") + 1);
$exceptionArray[$key] = $value;
}
$exception = new SyntheticError(
\sprintf(
'%s: %s',
$exceptionArray['_PHP_Incomplete_Class_Name'],
$exceptionArray['message']
),
$exceptionArray['code'],
$exceptionArray['file'],
$exceptionArray['line'],
$exceptionArray['trace']
);
}
return $exception;
}
}
PK (M^ src/Util/PHP/eval-stdin.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
eval('?>' . \file_get_contents('php://stdin'));
PK (M"F< < - src/Util/PHP/Template/TestCaseMethod.tpl.distnu W+A setCodeCoverage(
new CodeCoverage(
null,
unserialize('{codeCoverageFilter}')
)
);
}
$result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything});
$result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests});
$result->enforceTimeLimit({enforcesTimeLimit});
$result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests});
$result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests});
/** @var TestCase $test */
$test = new {className}('{methodName}', unserialize('{data}'), '{dataName}');
$test->setDependencyInput(unserialize('{dependencyInput}'));
$test->setInIsolation(TRUE);
ob_end_clean();
$test->run($result);
$output = '';
if (!$test->hasExpectationOnOutput()) {
$output = $test->getActualOutput();
}
@rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */
if ($stdout = stream_get_contents(STDOUT)) {
$output = $stdout . $output;
$streamMetaData = stream_get_meta_data(STDOUT);
if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) {
@ftruncate(STDOUT, 0);
@rewind(STDOUT);
}
}
print serialize(
array(
'testResult' => $test->getResult(),
'numAssertions' => $test->getNumAssertions(),
'result' => $result,
'output' => $output
)
);
}
$configurationFilePath = '{configurationFilePath}';
if ('' !== $configurationFilePath) {
$configuration = PHPUnit\Util\Configuration::getInstance($configurationFilePath);
$configuration->handlePHPConfiguration();
unset($configuration);
}
function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
{
return true;
}
set_error_handler('__phpunit_error_handler');
{constants}
{included_files}
{globals}
restore_error_handler();
if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
require_once $GLOBALS['__PHPUNIT_BOOTSTRAP'];
unset($GLOBALS['__PHPUNIT_BOOTSTRAP']);
}
__phpunit_run_isolated_test();
PK (M8 , src/Util/PHP/Template/TestCaseClass.tpl.distnu W+A setCodeCoverage(
new CodeCoverage(
null,
unserialize('{codeCoverageFilter}')
)
);
}
$result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything});
$result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests});
$result->enforceTimeLimit({enforcesTimeLimit});
$result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests});
$result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests});
$test = new {className}('{name}', unserialize('{data}'), '{dataName}');
$test->setDependencyInput(unserialize('{dependencyInput}'));
$test->setInIsolation(TRUE);
ob_end_clean();
$test->run($result);
$output = '';
if (!$test->hasExpectationOnOutput()) {
$output = $test->getActualOutput();
}
@rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */
if ($stdout = stream_get_contents(STDOUT)) {
$output = $stdout . $output;
$streamMetaData = stream_get_meta_data(STDOUT);
if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) {
@ftruncate(STDOUT, 0);
@rewind(STDOUT);
}
}
print serialize(
array(
'testResult' => $test->getResult(),
'numAssertions' => $test->getNumAssertions(),
'result' => $result,
'output' => $output
)
);
}
$configurationFilePath = '{configurationFilePath}';
if ('' !== $configurationFilePath) {
$configuration = PHPUnit\Util\Configuration::getInstance($configurationFilePath);
$configuration->handlePHPConfiguration();
unset($configuration);
}
function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
{
return true;
}
set_error_handler('__phpunit_error_handler');
{constants}
{included_files}
{globals}
restore_error_handler();
if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
require_once $GLOBALS['__PHPUNIT_BOOTSTRAP'];
unset($GLOBALS['__PHPUNIT_BOOTSTRAP']);
}
__phpunit_run_isolated_test();
PK (M'F F + src/Util/PHP/Template/PhptTestCase.tpl.distnu W+A start(__FILE__);
}
register_shutdown_function(function() use ($coverage, $autoPrependFile) {
$output = null;
if ($coverage) {
$output = $coverage->stop();
}
file_put_contents('{coverageFile}', serialize($output));
});
ob_end_clean();
if ($autoPrependFile) {
require $autoPrependFile;
$includes = get_included_files();
$GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'][] = array_pop($includes);
unset($includes);
}
PK (M " src/Util/PHP/WindowsPhpProcess.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\PHP;
use PHPUnit\Framework\Exception;
/**
* Windows utility for PHP sub-processes.
*
* Reading from STDOUT or STDERR hangs forever on Windows if the output is
* too large.
*
* @see https://bugs.php.net/bug.php?id=51800
*/
class WindowsPhpProcess extends DefaultPhpProcess
{
protected $useTempFile = true;
protected function getHandles()
{
if (false === $stdout_handle = \tmpfile()) {
throw new Exception(
'A temporary file could not be created; verify that your TEMP environment variable is writable'
);
}
return [
1 => $stdout_handle
];
}
public function getCommand(array $settings, $file = null)
{
return '"' . parent::getCommand($settings, $file) . '"';
}
}
PK (MD src/Util/XmlTestListRenderer.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
class XmlTestListRenderer
{
public function render(TestSuite $suite): string
{
$writer = new \XmlWriter;
$writer->openMemory();
$writer->setIndent(true);
$writer->startDocument();
$writer->startElement('tests');
$currentTestCase = null;
foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) {
if ($test instanceof TestCase) {
if (\get_class($test) !== $currentTestCase) {
if ($currentTestCase !== null) {
$writer->endElement();
}
$writer->startElement('testCaseClass');
$writer->writeAttribute('name', \get_class($test));
$currentTestCase = \get_class($test);
}
$writer->startElement('testCaseMethod');
$writer->writeAttribute('name', $test->getName(false));
$writer->writeAttribute('groups', \implode(',', $test->getGroups()));
if (!empty($test->getDataSetAsString(false))) {
$writer->writeAttribute(
'dataSet',
\str_replace(
' with data set ',
'',
$test->getDataSetAsString(false)
)
);
}
$writer->endElement();
} elseif ($test instanceof PhptTestCase) {
if ($currentTestCase !== null) {
$writer->endElement();
$currentTestCase = null;
}
$writer->startElement('phptFile');
$writer->writeAttribute('path', $test->getName());
$writer->endElement();
} else {
continue;
}
}
if ($currentTestCase !== null) {
$writer->endElement();
}
$writer->endElement();
return $writer->outputMemory();
}
}
PK (MQt>
src/Util/Printer.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* Utility class that can print to STDOUT or write to a file.
*/
class Printer
{
/**
* If true, flush output after every write.
*
* @var bool
*/
protected $autoFlush = false;
/**
* @var resource
*/
protected $out;
/**
* @var string
*/
protected $outTarget;
/**
* Constructor.
*
* @param mixed $out
*
* @throws Exception
*/
public function __construct($out = null)
{
if ($out !== null) {
if (\is_string($out)) {
if (\strpos($out, 'socket://') === 0) {
$out = \explode(':', \str_replace('socket://', '', $out));
if (\count($out) != 2) {
throw new Exception;
}
$this->out = \fsockopen($out[0], $out[1]);
} else {
if (\strpos($out, 'php://') === false && !\is_dir(\dirname($out))) {
$this->createDirectory(\dirname($out));
}
$this->out = \fopen($out, 'wt');
}
$this->outTarget = $out;
} else {
$this->out = $out;
}
}
}
/**
* Flush buffer and close output if it's not to a PHP stream
*/
public function flush()
{
if ($this->out && \strncmp($this->outTarget, 'php://', 6) !== 0) {
\fclose($this->out);
}
}
/**
* Performs a safe, incremental flush.
*
* Do not confuse this function with the flush() function of this class,
* since the flush() function may close the file being written to, rendering
* the current object no longer usable.
*/
public function incrementalFlush()
{
if ($this->out) {
\fflush($this->out);
} else {
\flush();
}
}
/**
* @param string $buffer
*/
public function write($buffer)
{
if ($this->out) {
\fwrite($this->out, $buffer);
if ($this->autoFlush) {
$this->incrementalFlush();
}
} else {
if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
$buffer = \htmlspecialchars($buffer, ENT_SUBSTITUTE);
}
print $buffer;
if ($this->autoFlush) {
$this->incrementalFlush();
}
}
}
/**
* Check auto-flush mode.
*
* @return bool
*/
public function getAutoFlush()
{
return $this->autoFlush;
}
/**
* Set auto-flushing mode.
*
* If set, *incremental* flushes will be done after each write. This should
* not be confused with the different effects of this class' flush() method.
*
* @param bool $autoFlush
*/
public function setAutoFlush($autoFlush)
{
if (\is_bool($autoFlush)) {
$this->autoFlush = $autoFlush;
} else {
throw InvalidArgumentHelper::factory(1, 'boolean');
}
}
private function createDirectory(string $directory): bool
{
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
}
}
PK (M1kfw w src/Util/Filter.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
/**
* Utility class for code filtering.
*/
class Filter
{
/**
* Filters stack frames from PHPUnit classes.
*
* @param \Throwable $e
* @param bool $asString
*
* @return string|string[]
*/
public static function getFilteredStacktrace($e, $asString = true)
{
$prefix = false;
$script = \realpath($GLOBALS['_SERVER']['SCRIPT_NAME']);
if (\defined('__PHPUNIT_PHAR_ROOT__')) {
$prefix = __PHPUNIT_PHAR_ROOT__;
}
if ($asString === true) {
$filteredStacktrace = '';
} else {
$filteredStacktrace = [];
}
if ($e instanceof SyntheticError) {
$eTrace = $e->getSyntheticTrace();
$eFile = $e->getSyntheticFile();
$eLine = $e->getSyntheticLine();
} elseif ($e instanceof Exception) {
$eTrace = $e->getSerializableTrace();
$eFile = $e->getFile();
$eLine = $e->getLine();
} else {
if ($e->getPrevious()) {
$e = $e->getPrevious();
}
$eTrace = $e->getTrace();
$eFile = $e->getFile();
$eLine = $e->getLine();
}
if (!self::frameExists($eTrace, $eFile, $eLine)) {
\array_unshift(
$eTrace,
['file' => $eFile, 'line' => $eLine]
);
}
$blacklist = new Blacklist;
foreach ($eTrace as $frame) {
if (isset($frame['file']) && \is_file($frame['file']) &&
!$blacklist->isBlacklisted($frame['file']) &&
($prefix === false || \strpos($frame['file'], $prefix) !== 0) &&
$frame['file'] !== $script) {
if ($asString === true) {
$filteredStacktrace .= \sprintf(
"%s:%s\n",
$frame['file'],
$frame['line'] ?? '?'
);
} else {
$filteredStacktrace[] = $frame;
}
}
}
return $filteredStacktrace;
}
/**
* @param array $trace
* @param string $file
* @param int $line
*
* @return bool
*/
private static function frameExists(array $trace, $file, $line)
{
foreach ($trace as $frame) {
if (isset($frame['file']) && $frame['file'] == $file &&
isset($frame['line']) && $frame['line'] == $line) {
return true;
}
}
return false;
}
}
PK (M0å, , src/Util/Log/TeamCity.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\Log;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\TextUI\ResultPrinter;
use PHPUnit\Util\Filter;
use ReflectionClass;
use SebastianBergmann\Comparator\ComparisonFailure;
/**
* A TestListener that generates a logfile of the test execution using the
* TeamCity format (for use with PhpStorm, for instance).
*/
class TeamCity extends ResultPrinter
{
/**
* @var bool
*/
private $isSummaryTestCountPrinted = false;
/**
* @var string
*/
private $startedTestName;
/**
* @var int|false
*/
private $flowId;
/**
* @param string $progress
*/
protected function writeProgress($progress)
{
}
/**
* @param TestResult $result
*/
public function printResult(TestResult $result)
{
$this->printHeader();
$this->printFooter($result);
}
/**
* An error occurred.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(Test $test, \Exception $e, $time)
{
$this->printEvent(
'testFailed',
[
'name' => $test->getName(),
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* A warning occurred.
*
* @param Test $test
* @param Warning $e
* @param float $time
*/
public function addWarning(Test $test, Warning $e, $time)
{
$this->printEvent(
'testFailed',
[
'name' => $test->getName(),
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* A failure occurred.
*
* @param Test $test
* @param AssertionFailedError $e
* @param float $time
*/
public function addFailure(Test $test, AssertionFailedError $e, $time)
{
$parameters = [
'name' => $test->getName(),
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
];
if ($e instanceof ExpectationFailedException) {
$comparisonFailure = $e->getComparisonFailure();
if ($comparisonFailure instanceof ComparisonFailure) {
$expectedString = $comparisonFailure->getExpectedAsString();
if (null === $expectedString || empty($expectedString)) {
$expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
}
$actualString = $comparisonFailure->getActualAsString();
if (null === $actualString || empty($actualString)) {
$actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
}
if (null !== $actualString && null !== $expectedString) {
$parameters['type'] = 'comparisonFailure';
$parameters['actual'] = $actualString;
$parameters['expected'] = $expectedString;
}
}
}
$this->printEvent('testFailed', $parameters);
}
/**
* Incomplete test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(Test $test, \Exception $e, $time)
{
$this->printIgnoredTest($test->getName(), $e, $time);
}
/**
* Risky test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addRiskyTest(Test $test, \Exception $e, $time)
{
$this->addError($test, $e, $time);
}
/**
* Skipped test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addSkippedTest(Test $test, \Exception $e, $time)
{
$testName = $test->getName();
if ($this->startedTestName != $testName) {
$this->startTest($test);
$this->printIgnoredTest($testName, $e, $time);
$this->endTest($test, $time);
} else {
$this->printIgnoredTest($testName, $e, $time);
}
}
public function printIgnoredTest($testName, \Exception $e, $time)
{
$this->printEvent(
'testIgnored',
[
'name' => $testName,
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* A testsuite started.
*
* @param TestSuite $suite
*/
public function startTestSuite(TestSuite $suite)
{
if (\stripos(\ini_get('disable_functions'), 'getmypid') === false) {
$this->flowId = \getmypid();
} else {
$this->flowId = false;
}
if (!$this->isSummaryTestCountPrinted) {
$this->isSummaryTestCountPrinted = true;
$this->printEvent(
'testCount',
['count' => \count($suite)]
);
}
$suiteName = $suite->getName();
if (empty($suiteName)) {
return;
}
$parameters = ['name' => $suiteName];
if (\class_exists($suiteName, false)) {
$fileName = self::getFileName($suiteName);
$parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
} else {
$split = \preg_split('/::/', $suiteName);
if (\count($split) == 2 && \method_exists($split[0], $split[1])) {
$fileName = self::getFileName($split[0]);
$parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
$parameters['name'] = $split[1];
}
}
$this->printEvent('testSuiteStarted', $parameters);
}
/**
* A testsuite ended.
*
* @param TestSuite $suite
*/
public function endTestSuite(TestSuite $suite)
{
$suiteName = $suite->getName();
if (empty($suiteName)) {
return;
}
$parameters = ['name' => $suiteName];
if (!\class_exists($suiteName, false)) {
$split = \preg_split('/::/', $suiteName);
if (\count($split) == 2 && \method_exists($split[0], $split[1])) {
$parameters['name'] = $split[1];
}
}
$this->printEvent('testSuiteFinished', $parameters);
}
/**
* A test started.
*
* @param Test $test
*/
public function startTest(Test $test)
{
$testName = $test->getName();
$this->startedTestName = $testName;
$params = ['name' => $testName];
if ($test instanceof TestCase) {
$className = \get_class($test);
$fileName = self::getFileName($className);
$params['locationHint'] = "php_qn://$fileName::\\$className::$testName";
}
$this->printEvent('testStarted', $params);
}
/**
* A test ended.
*
* @param Test $test
* @param float $time
*/
public function endTest(Test $test, $time)
{
parent::endTest($test, $time);
$this->printEvent(
'testFinished',
[
'name' => $test->getName(),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* @param string $eventName
* @param array $params
*/
private function printEvent($eventName, $params = [])
{
$this->write("\n##teamcity[$eventName");
if ($this->flowId) {
$params['flowId'] = $this->flowId;
}
foreach ($params as $key => $value) {
$escapedValue = self::escapeValue($value);
$this->write(" $key='$escapedValue'");
}
$this->write("]\n");
}
/**
* @param \Exception $e
*
* @return string
*/
private static function getMessage(\Exception $e)
{
$message = '';
if ($e instanceof ExceptionWrapper) {
if (\strlen($e->getClassName()) != 0) {
$message .= $e->getClassName();
}
if (\strlen($message) != 0 && \strlen($e->getMessage()) != 0) {
$message .= ' : ';
}
}
return $message . $e->getMessage();
}
/**
* @param \Exception $e
*
* @return string
*/
private static function getDetails(\Exception $e)
{
$stackTrace = Filter::getFilteredStacktrace($e);
$previous = $e instanceof ExceptionWrapper ?
$e->getPreviousWrapped() : $e->getPrevious();
while ($previous) {
$stackTrace .= "\nCaused by\n" .
TestFailure::exceptionToString($previous) . "\n" .
Filter::getFilteredStacktrace($previous);
$previous = $previous instanceof ExceptionWrapper ?
$previous->getPreviousWrapped() : $previous->getPrevious();
}
return ' ' . \str_replace("\n", "\n ", $stackTrace);
}
/**
* @param mixed $value
*
* @return string
*/
private static function getPrimitiveValueAsString($value)
{
if (null === $value) {
return 'null';
}
if (\is_bool($value)) {
return $value == true ? 'true' : 'false';
}
if (\is_scalar($value)) {
return \print_r($value, true);
}
}
/**
* @param $text
*
* @return string
*/
private static function escapeValue($text)
{
$text = \str_replace('|', '||', $text);
$text = \str_replace("'", "|'", $text);
$text = \str_replace("\n", '|n', $text);
$text = \str_replace("\r", '|r', $text);
$text = \str_replace(']', '|]', $text);
$text = \str_replace('[', '|[', $text);
return $text;
}
/**
* @param string $className
*
* @return string
*/
private static function getFileName($className)
{
$reflectionClass = new ReflectionClass($className);
return $reflectionClass->getFileName();
}
/**
* @param float $time microseconds
*
* @return int
*/
private static function toMilliseconds($time)
{
return \round($time * 1000);
}
}
PK (M
_. _. src/Util/Log/JUnit.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\Log;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Filter;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Xml;
use ReflectionClass;
use ReflectionException;
/**
* A TestListener that generates a logfile of the test execution in XML markup.
*
* The XML markup used is the same as the one that is used by the JUnit Ant task.
*/
class JUnit extends Printer implements TestListener
{
/**
* @var DOMDocument
*/
protected $document;
/**
* @var DOMElement
*/
protected $root;
/**
* @var bool
*/
protected $reportUselessTests = false;
/**
* @var bool
*/
protected $writeDocument = true;
/**
* @var DOMElement[]
*/
protected $testSuites = [];
/**
* @var int[]
*/
protected $testSuiteTests = [0];
/**
* @var int[]
*/
protected $testSuiteAssertions = [0];
/**
* @var int[]
*/
protected $testSuiteErrors = [0];
/**
* @var int[]
*/
protected $testSuiteFailures = [0];
/**
* @var int[]
*/
protected $testSuiteSkipped = [0];
/**
* @var int[]
*/
protected $testSuiteTimes = [0];
/**
* @var int
*/
protected $testSuiteLevel = 0;
/**
* @var ?DOMElement
*/
protected $currentTestCase;
/**
* Constructor.
*
* @param mixed $out
* @param bool $reportUselessTests
*/
public function __construct($out = null, $reportUselessTests = false)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('testsuites');
$this->document->appendChild($this->root);
parent::__construct($out);
$this->reportUselessTests = $reportUselessTests;
}
/**
* Flush buffer and close output.
*/
public function flush()
{
if ($this->writeDocument === true) {
$this->write($this->getXML());
}
parent::flush();
}
/**
* An error occurred.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(Test $test, \Exception $e, $time)
{
$this->doAddFault($test, $e, $time, 'error');
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* A warning occurred.
*
* @param Test $test
* @param Warning $e
* @param float $time
*/
public function addWarning(Test $test, Warning $e, $time)
{
$this->doAddFault($test, $e, $time, 'warning');
$this->testSuiteFailures[$this->testSuiteLevel]++;
}
/**
* A failure occurred.
*
* @param Test $test
* @param AssertionFailedError $e
* @param float $time
*/
public function addFailure(Test $test, AssertionFailedError $e, $time)
{
$this->doAddFault($test, $e, $time, 'failure');
$this->testSuiteFailures[$this->testSuiteLevel]++;
}
/**
* Incomplete test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(Test $test, \Exception $e, $time)
{
$this->doAddSkipped($test);
}
/**
* Risky test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addRiskyTest(Test $test, \Exception $e, $time)
{
if (!$this->reportUselessTests || $this->currentTestCase === null) {
return;
}
$error = $this->document->createElement(
'error',
Xml::prepareString(
"Risky Test\n" .
Filter::getFilteredStacktrace($e)
)
);
$error->setAttribute('type', \get_class($e));
$this->currentTestCase->appendChild($error);
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* Skipped test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addSkippedTest(Test $test, \Exception $e, $time)
{
$this->doAddSkipped($test);
}
/**
* A testsuite started.
*
* @param TestSuite $suite
*/
public function startTestSuite(TestSuite $suite)
{
$testSuite = $this->document->createElement('testsuite');
$testSuite->setAttribute('name', $suite->getName());
if (\class_exists($suite->getName(), false)) {
try {
$class = new ReflectionClass($suite->getName());
$testSuite->setAttribute('file', $class->getFileName());
} catch (ReflectionException $e) {
}
}
if ($this->testSuiteLevel > 0) {
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
} else {
$this->root->appendChild($testSuite);
}
$this->testSuiteLevel++;
$this->testSuites[$this->testSuiteLevel] = $testSuite;
$this->testSuiteTests[$this->testSuiteLevel] = 0;
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
}
/**
* A testsuite ended.
*
* @param TestSuite $suite
*/
public function endTestSuite(TestSuite $suite)
{
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'tests',
$this->testSuiteTests[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'assertions',
$this->testSuiteAssertions[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'errors',
$this->testSuiteErrors[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'failures',
$this->testSuiteFailures[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'skipped',
$this->testSuiteSkipped[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'time',
\sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
);
if ($this->testSuiteLevel > 1) {
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
}
$this->testSuiteLevel--;
}
/**
* A test started.
*
* @param Test $test
*/
public function startTest(Test $test)
{
$testCase = $this->document->createElement('testcase');
$testCase->setAttribute('name', $test->getName());
if ($test instanceof TestCase) {
$class = new ReflectionClass($test);
$methodName = $test->getName(!$test->usesDataProvider());
if ($class->hasMethod($methodName)) {
$method = $class->getMethod($methodName);
$testCase->setAttribute('class', $class->getName());
$testCase->setAttribute('classname', \str_replace('\\', '.', $class->getName()));
$testCase->setAttribute('file', $class->getFileName());
$testCase->setAttribute('line', $method->getStartLine());
}
}
$this->currentTestCase = $testCase;
}
/**
* A test ended.
*
* @param Test $test
* @param float $time
*/
public function endTest(Test $test, $time)
{
if ($test instanceof TestCase) {
$numAssertions = $test->getNumAssertions();
$this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
$this->currentTestCase->setAttribute(
'assertions',
$numAssertions
);
}
$this->currentTestCase->setAttribute(
'time',
\sprintf('%F', $time)
);
$this->testSuites[$this->testSuiteLevel]->appendChild(
$this->currentTestCase
);
$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
if (\method_exists($test, 'hasOutput') && $test->hasOutput()) {
$systemOut = $this->document->createElement(
'system-out',
Xml::prepareString($test->getActualOutput())
);
$this->currentTestCase->appendChild($systemOut);
}
$this->currentTestCase = null;
}
/**
* Returns the XML as a string.
*
* @return string
*/
public function getXML()
{
return $this->document->saveXML();
}
/**
* Enables or disables the writing of the document
* in flush().
*
* This is a "hack" needed for the integration of
* PHPUnit with Phing.
*
* @return string
*/
public function setWriteDocument($flag)
{
if (\is_bool($flag)) {
$this->writeDocument = $flag;
}
}
/**
* Method which generalizes addError() and addFailure()
*
* @param Test $test
* @param \Exception $e
* @param float $time
* @param string $type
*/
private function doAddFault(Test $test, \Exception $e, $time, $type)
{
if ($this->currentTestCase === null) {
return;
}
if ($test instanceof SelfDescribing) {
$buffer = $test->toString() . "\n";
} else {
$buffer = '';
}
$buffer .= TestFailure::exceptionToString($e) . "\n" .
Filter::getFilteredStacktrace($e);
$fault = $this->document->createElement(
$type,
Xml::prepareString($buffer)
);
if ($e instanceof ExceptionWrapper) {
$fault->setAttribute('type', $e->getClassName());
} else {
$fault->setAttribute('type', \get_class($e));
}
$this->currentTestCase->appendChild($fault);
}
private function doAddSkipped(Test $test)
{
if ($this->currentTestCase === null) {
return;
}
$skipped = $this->document->createElement('skipped');
$this->currentTestCase->appendChild($skipped);
$this->testSuiteSkipped[$this->testSuiteLevel]++;
}
}
PK (Mwz| | src/Util/Test.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use Iterator;
use PharIo\Version\VersionConstraintParser;
use PHPUnit\Framework\CodeCoverageException;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\InvalidCoversTargetException;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\SkippedTestError;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\Version;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionMethod;
use SebastianBergmann\Environment\OperatingSystem;
use Traversable;
/**
* Test helpers.
*/
class Test
{
const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
const REGEX_TEST_WITH = '/@testWith\s+/';
const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\t -.|~^]+)[ \t]*\r?$/m';
const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m';
const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^ ]+?))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m';
const UNKNOWN = -1;
const SMALL = 0;
const MEDIUM = 1;
const LARGE = 2;
private static $annotationCache = [];
private static $hookMethods = [];
/**
* @param \PHPUnit\Framework\Test $test
* @param bool $asString
*
* @return mixed
*/
public static function describe(\PHPUnit\Framework\Test $test, $asString = true)
{
if ($asString) {
if ($test instanceof SelfDescribing) {
return $test->toString();
}
return \get_class($test);
}
if ($test instanceof TestCase) {
return [
\get_class($test), $test->getName()
];
}
if ($test instanceof SelfDescribing) {
return ['', $test->toString()];
}
return ['', \get_class($test)];
}
/**
* @param string $className
* @param string $methodName
*
* @return array|bool
*
* @throws CodeCoverageException
*/
public static function getLinesToBeCovered($className, $methodName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
return false;
}
return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
}
/**
* Returns lines of code specified with the @uses annotation.
*
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getLinesToBeUsed($className, $methodName)
{
return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
}
/**
* @param string $className
* @param string $methodName
* @param string $mode
*
* @return array
*
* @throws CodeCoverageException
*/
private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$classShortcut = null;
if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
if (\count($annotations['class'][$mode . 'DefaultClass']) > 1) {
throw new CodeCoverageException(
\sprintf(
'More than one @%sClass annotation in class or interface "%s".',
$mode,
$className
)
);
}
$classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
}
$list = [];
if (isset($annotations['class'][$mode])) {
$list = $annotations['class'][$mode];
}
if (isset($annotations['method'][$mode])) {
$list = \array_merge($list, $annotations['method'][$mode]);
}
$codeList = [];
foreach (\array_unique($list) as $element) {
if ($classShortcut && \strncmp($element, '::', 2) === 0) {
$element = $classShortcut . $element;
}
$element = \preg_replace('/[\s()]+$/', '', $element);
$element = \explode(' ', $element);
$element = $element[0];
$codeList = \array_merge(
$codeList,
self::resolveElementToReflectionObjects($element)
);
}
return self::resolveReflectionObjectsToLines($codeList);
}
/**
* Returns the requirements for a test.
*
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getRequirements($className, $methodName)
{
$reflector = new ReflectionClass($className);
$docComment = $reflector->getDocComment();
$reflector = new ReflectionMethod($className, $methodName);
$docComment .= "\n" . $reflector->getDocComment();
$requires = [];
if ($count = \preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) {
foreach (\range(0, $count - 1) as $i) {
$requires[$matches['name'][$i]] = $matches['value'][$i];
}
}
if ($count = \preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) {
foreach (\range(0, $count - 1) as $i) {
$requires[$matches['name'][$i]] = [
'version' => $matches['version'][$i],
'operator' => $matches['operator'][$i]
];
}
}
if ($count = \preg_match_all(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $docComment, $matches)) {
foreach (\range(0, $count - 1) as $i) {
if (!empty($requires[$matches['name'][$i]])) {
continue;
}
try {
$versionConstraintParser = new VersionConstraintParser;
$requires[$matches['name'][$i] . '_constraint'] = [
'constraint' => $versionConstraintParser->parse(\trim($matches['constraint'][$i]))
];
} catch (\PharIo\Version\Exception $e) {
throw new Warning($e->getMessage(), $e->getCode(), $e);
}
}
}
if ($count = \preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
foreach (\range(0, $count - 1) as $i) {
$name = $matches['name'][$i] . 's';
if (!isset($requires[$name])) {
$requires[$name] = [];
}
$requires[$name][] = $matches['value'][$i];
if (empty($matches['version'][$i]) || $name != 'extensions') {
continue;
}
$requires['extension_versions'][$matches['value'][$i]] = [
'version' => $matches['version'][$i],
'operator' => $matches['operator'][$i]
];
}
}
return $requires;
}
/**
* Returns the missing requirements for a test.
*
* @param string $className
* @param string $methodName
*
* @return string[]
*/
public static function getMissingRequirements($className, $methodName)
{
$required = static::getRequirements($className, $methodName);
$missing = [];
if (!empty($required['PHP'])) {
$operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator'];
if (!\version_compare(PHP_VERSION, $required['PHP']['version'], $operator)) {
$missing[] = \sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']);
}
} elseif (!empty($required['PHP_constraint'])) {
$version = new \PharIo\Version\Version(self::sanitizeVersionNumber(PHP_VERSION));
if (!$required['PHP_constraint']['constraint']->complies($version)) {
$missing[] = \sprintf(
'PHP version does not match the required constraint %s.',
$required['PHP_constraint']['constraint']->asString()
);
}
}
if (!empty($required['PHPUnit'])) {
$phpunitVersion = Version::id();
$operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator'];
if (!\version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) {
$missing[] = \sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']);
}
} elseif (!empty($required['PHPUnit_constraint'])) {
$phpunitVersion = new \PharIo\Version\Version(self::sanitizeVersionNumber(Version::id()));
if (!$required['PHPUnit_constraint']['constraint']->complies($phpunitVersion)) {
$missing[] = \sprintf(
'PHPUnit version does not match the required constraint %s.',
$required['PHPUnit_constraint']['constraint']->asString()
);
}
}
if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem())->getFamily()) {
$missing[] = \sprintf('Operating system %s is required.', $required['OSFAMILY']);
}
if (!empty($required['OS'])) {
$requiredOsPattern = \sprintf('/%s/i', \addcslashes($required['OS'], '/'));
if (!\preg_match($requiredOsPattern, PHP_OS)) {
$missing[] = \sprintf('Operating system matching %s is required.', $requiredOsPattern);
}
}
if (!empty($required['functions'])) {
foreach ($required['functions'] as $function) {
$pieces = \explode('::', $function);
if (2 === \count($pieces) && \method_exists($pieces[0], $pieces[1])) {
continue;
}
if (\function_exists($function)) {
continue;
}
$missing[] = \sprintf('Function %s is required.', $function);
}
}
if (!empty($required['extensions'])) {
foreach ($required['extensions'] as $extension) {
if (isset($required['extension_versions'][$extension])) {
continue;
}
if (!\extension_loaded($extension)) {
$missing[] = \sprintf('Extension %s is required.', $extension);
}
}
}
if (!empty($required['extension_versions'])) {
foreach ($required['extension_versions'] as $extension => $required) {
$actualVersion = \phpversion($extension);
$operator = empty($required['operator']) ? '>=' : $required['operator'];
if (false === $actualVersion || !\version_compare($actualVersion, $required['version'], $operator)) {
$missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']);
}
}
}
return $missing;
}
/**
* Returns the expected exception for a test.
*
* @param string $className
* @param string $methodName
*
* @return array|false
*/
public static function getExpectedException($className, $methodName)
{
$reflector = new ReflectionMethod($className, $methodName);
$docComment = $reflector->getDocComment();
$docComment = \substr($docComment, 3, -2);
if (\preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$class = $matches[1];
$code = null;
$message = '';
$messageRegExp = '';
if (isset($matches[2])) {
$message = \trim($matches[2]);
} elseif (isset($annotations['method']['expectedExceptionMessage'])) {
$message = self::parseAnnotationContent(
$annotations['method']['expectedExceptionMessage'][0]
);
}
if (isset($annotations['method']['expectedExceptionMessageRegExp'])) {
$messageRegExp = self::parseAnnotationContent(
$annotations['method']['expectedExceptionMessageRegExp'][0]
);
}
if (isset($matches[3])) {
$code = $matches[3];
} elseif (isset($annotations['method']['expectedExceptionCode'])) {
$code = self::parseAnnotationContent(
$annotations['method']['expectedExceptionCode'][0]
);
}
if (\is_numeric($code)) {
$code = (int) $code;
} elseif (\is_string($code) && \defined($code)) {
$code = (int) \constant($code);
}
return [
'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp
];
}
return false;
}
/**
* Parse annotation content to use constant/class constant values
*
* Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
*
* If the constant is not found the string is used as is to ensure maximum BC.
*
* @param string $message
*
* @return string
*/
private static function parseAnnotationContent($message)
{
if ((\strpos($message, '::') !== false && \count(\explode('::', $message)) == 2) && \defined($message)) {
$message = \constant($message);
}
return $message;
}
/**
* Returns the provided data for a method.
*
* @param string $className
* @param string $methodName
*
* @return array When a data provider is specified and exists
* null When no data provider is specified
*
* @throws Exception
*/
public static function getProvidedData($className, $methodName)
{
$reflector = new ReflectionMethod($className, $methodName);
$docComment = $reflector->getDocComment();
$data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName);
if ($data === null) {
$data = self::getDataFromTestWithAnnotation($docComment);
}
if (\is_array($data) && empty($data)) {
throw new SkippedTestError;
}
if ($data !== null) {
foreach ($data as $key => $value) {
if (!\is_array($value)) {
throw new Exception(
\sprintf(
'Data set %s is invalid.',
\is_int($key) ? '#' . $key : '"' . $key . '"'
)
);
}
}
}
return $data;
}
/**
* Returns the provided data for a method.
*
* @param string $docComment
* @param string $className
* @param string $methodName
*
* @return array|Iterator when a data provider is specified and exists
* null when no data provider is specified
*
* @throws Exception
*/
private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName)
{
if (\preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
$result = [];
foreach ($matches[1] as $match) {
$dataProviderMethodNameNamespace = \explode('\\', $match);
$leaf = \explode('::', \array_pop($dataProviderMethodNameNamespace));
$dataProviderMethodName = \array_pop($leaf);
if (!empty($dataProviderMethodNameNamespace)) {
$dataProviderMethodNameNamespace = \implode('\\', $dataProviderMethodNameNamespace) . '\\';
} else {
$dataProviderMethodNameNamespace = '';
}
if (!empty($leaf)) {
$dataProviderClassName = $dataProviderMethodNameNamespace . \array_pop($leaf);
} else {
$dataProviderClassName = $className;
}
$dataProviderClass = new ReflectionClass($dataProviderClassName);
$dataProviderMethod = $dataProviderClass->getMethod(
$dataProviderMethodName
);
if ($dataProviderMethod->isStatic()) {
$object = null;
} else {
$object = $dataProviderClass->newInstance();
}
if ($dataProviderMethod->getNumberOfParameters() == 0) {
$data = $dataProviderMethod->invoke($object);
} else {
$data = $dataProviderMethod->invoke($object, $methodName);
}
if ($data instanceof Traversable) {
$origData = $data;
$data = [];
foreach ($origData as $key => $value) {
if (\is_int($key)) {
$data[] = $value;
} else {
$data[$key] = $value;
}
}
}
if (\is_array($data)) {
$result = \array_merge($result, $data);
}
}
return $result;
}
}
/**
* @param string $docComment full docComment string
*
* @return array|null array when @testWith annotation is defined,
* null when @testWith annotation is omitted
*
* @throws Exception when @testWith annotation is defined but cannot be parsed
*/
public static function getDataFromTestWithAnnotation($docComment)
{
$docComment = self::cleanUpMultiLineAnnotation($docComment);
if (\preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
$offset = \strlen($matches[0][0]) + $matches[0][1];
$annotationContent = \substr($docComment, $offset);
$data = [];
foreach (\explode("\n", $annotationContent) as $candidateRow) {
$candidateRow = \trim($candidateRow);
if ($candidateRow[0] !== '[') {
break;
}
$dataSet = \json_decode($candidateRow, true);
if (\json_last_error() != JSON_ERROR_NONE) {
throw new Exception(
'The dataset for the @testWith annotation cannot be parsed: ' . \json_last_error_msg()
);
}
$data[] = $dataSet;
}
if (!$data) {
throw new Exception('The dataset for the @testWith annotation cannot be parsed.');
}
return $data;
}
}
private static function cleanUpMultiLineAnnotation($docComment)
{
//removing initial ' * ' for docComment
$docComment = \str_replace("\r\n", "\n", $docComment);
$docComment = \preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
$docComment = \substr($docComment, 0, -1);
$docComment = \rtrim($docComment, "\n");
return $docComment;
}
/**
* @param string $className
* @param string $methodName
*
* @return array
*
* @throws ReflectionException
*/
public static function parseTestMethodAnnotations($className, $methodName = '')
{
if (!isset(self::$annotationCache[$className])) {
$class = new ReflectionClass($className);
$traits = $class->getTraits();
$annotations = [];
foreach ($traits as $trait) {
$annotations = \array_merge(
$annotations,
self::parseAnnotations($trait->getDocComment())
);
}
self::$annotationCache[$className] = \array_merge(
$annotations,
self::parseAnnotations($class->getDocComment())
);
}
if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
try {
$method = new ReflectionMethod($className, $methodName);
$annotations = self::parseAnnotations($method->getDocComment());
} catch (ReflectionException $e) {
$annotations = [];
}
self::$annotationCache[$className . '::' . $methodName] = $annotations;
}
return [
'class' => self::$annotationCache[$className],
'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : []
];
}
/**
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getInlineAnnotations($className, $methodName)
{
$method = new ReflectionMethod($className, $methodName);
$code = \file($method->getFileName());
$lineNumber = $method->getStartLine();
$startLine = $method->getStartLine() - 1;
$endLine = $method->getEndLine() - 1;
$methodLines = \array_slice($code, $startLine, $endLine - $startLine + 1);
$annotations = [];
foreach ($methodLines as $line) {
if (\preg_match('#/\*\*?\s*@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?\*/$#m', $line, $matches)) {
$annotations[\strtolower($matches['name'])] = [
'line' => $lineNumber,
'value' => $matches['value']
];
}
$lineNumber++;
}
return $annotations;
}
/**
* @param string $docblock
*
* @return array
*/
private static function parseAnnotations($docblock)
{
$annotations = [];
// Strip away the docblock header and footer to ease parsing of one line annotations
$docblock = \substr($docblock, 3, -2);
if (\preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
$numMatches = \count($matches[0]);
for ($i = 0; $i < $numMatches; ++$i) {
$annotations[$matches['name'][$i]][] = (string) $matches['value'][$i];
}
}
return $annotations;
}
/**
* Returns the backup settings for a test.
*
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getBackupSettings($className, $methodName)
{
return [
'backupGlobals' => self::getBooleanAnnotationSetting(
$className,
$methodName,
'backupGlobals'
),
'backupStaticAttributes' => self::getBooleanAnnotationSetting(
$className,
$methodName,
'backupStaticAttributes'
)
];
}
/**
* Returns the dependencies for a test class or method.
*
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getDependencies($className, $methodName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$dependencies = [];
if (isset($annotations['class']['depends'])) {
$dependencies = $annotations['class']['depends'];
}
if (isset($annotations['method']['depends'])) {
$dependencies = \array_merge(
$dependencies,
$annotations['method']['depends']
);
}
return \array_unique($dependencies);
}
/**
* Returns the error handler settings for a test.
*
* @param string $className
* @param string $methodName
*
* @return ?bool
*/
public static function getErrorHandlerSettings($className, $methodName)
{
return self::getBooleanAnnotationSetting(
$className,
$methodName,
'errorHandler'
);
}
/**
* Returns the groups for a test class or method.
*
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getGroups($className, $methodName = '')
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$groups = [];
if (isset($annotations['method']['author'])) {
$groups = $annotations['method']['author'];
} elseif (isset($annotations['class']['author'])) {
$groups = $annotations['class']['author'];
}
if (isset($annotations['class']['group'])) {
$groups = \array_merge($groups, $annotations['class']['group']);
}
if (isset($annotations['method']['group'])) {
$groups = \array_merge($groups, $annotations['method']['group']);
}
if (isset($annotations['class']['ticket'])) {
$groups = \array_merge($groups, $annotations['class']['ticket']);
}
if (isset($annotations['method']['ticket'])) {
$groups = \array_merge($groups, $annotations['method']['ticket']);
}
foreach (['method', 'class'] as $element) {
foreach (['small', 'medium', 'large'] as $size) {
if (isset($annotations[$element][$size])) {
$groups[] = $size;
break 2;
}
}
}
return \array_unique($groups);
}
/**
* Returns the size of the test.
*
* @param string $className
* @param string $methodName
*
* @return int
*/
public static function getSize($className, $methodName)
{
$groups = \array_flip(self::getGroups($className, $methodName));
$class = new ReflectionClass($className);
if (isset($groups['large']) ||
(\class_exists('PHPUnit\DbUnit\TestCase', false) && $class->isSubclassOf('PHPUnit\DbUnit\TestCase'))) {
return self::LARGE;
}
if (isset($groups['medium'])) {
return self::MEDIUM;
}
if (isset($groups['small'])) {
return self::SMALL;
}
return self::UNKNOWN;
}
/**
* Returns the process isolation settings for a test.
*
* @param string $className
* @param string $methodName
*
* @return bool
*/
public static function getProcessIsolationSettings($className, $methodName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
return isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess']);
}
public static function getClassProcessIsolationSettings($className, $methodName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
return isset($annotations['class']['runClassInSeparateProcess']);
}
/**
* Returns the preserve global state settings for a test.
*
* @param string $className
* @param string $methodName
*
* @return ?bool
*/
public static function getPreserveGlobalStateSettings($className, $methodName)
{
return self::getBooleanAnnotationSetting(
$className,
$methodName,
'preserveGlobalState'
);
}
/**
* @param string $className
*
* @return array
*/
public static function getHookMethods($className)
{
if (!\class_exists($className, false)) {
return self::emptyHookMethodsArray();
}
if (!isset(self::$hookMethods[$className])) {
self::$hookMethods[$className] = self::emptyHookMethodsArray();
try {
$class = new ReflectionClass($className);
foreach ($class->getMethods() as $method) {
if (self::isBeforeClassMethod($method)) {
\array_unshift(
self::$hookMethods[$className]['beforeClass'],
$method->getName()
);
}
if (self::isBeforeMethod($method)) {
\array_unshift(
self::$hookMethods[$className]['before'],
$method->getName()
);
}
if (self::isAfterMethod($method)) {
self::$hookMethods[$className]['after'][] = $method->getName();
}
if (self::isAfterClassMethod($method)) {
self::$hookMethods[$className]['afterClass'][] = $method->getName();
}
}
} catch (ReflectionException $e) {
}
}
return self::$hookMethods[$className];
}
/**
* @return array
*/
private static function emptyHookMethodsArray()
{
return [
'beforeClass' => ['setUpBeforeClass'],
'before' => ['setUp'],
'after' => ['tearDown'],
'afterClass' => ['tearDownAfterClass']
];
}
/**
* @param string $className
* @param string $methodName
* @param string $settingName
*
* @return ?bool
*/
private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
if (isset($annotations['method'][$settingName])) {
if ($annotations['method'][$settingName][0] === 'enabled') {
return true;
}
if ($annotations['method'][$settingName][0] === 'disabled') {
return false;
}
}
if (isset($annotations['class'][$settingName])) {
if ($annotations['class'][$settingName][0] === 'enabled') {
return true;
}
if ($annotations['class'][$settingName][0] === 'disabled') {
return false;
}
}
}
/**
* @param string $element
*
* @return array
*
* @throws InvalidCoversTargetException
*/
private static function resolveElementToReflectionObjects($element)
{
$codeToCoverList = [];
if (\strpos($element, '\\') !== false && \function_exists($element)) {
$codeToCoverList[] = new ReflectionFunction($element);
} elseif (\strpos($element, '::') !== false) {
list($className, $methodName) = \explode('::', $element);
if (isset($methodName[0]) && $methodName[0] == '<') {
$classes = [$className];
foreach ($classes as $className) {
if (!\class_exists($className) &&
!\interface_exists($className) &&
!\trait_exists($className)) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover or @use not existing class or ' .
'interface "%s".',
$className
)
);
}
$class = new ReflectionClass($className);
$methods = $class->getMethods();
$inverse = isset($methodName[1]) && $methodName[1] == '!';
if (\strpos($methodName, 'protected')) {
$visibility = 'isProtected';
} elseif (\strpos($methodName, 'private')) {
$visibility = 'isPrivate';
} elseif (\strpos($methodName, 'public')) {
$visibility = 'isPublic';
}
foreach ($methods as $method) {
if ($inverse && !$method->$visibility()) {
$codeToCoverList[] = $method;
} elseif (!$inverse && $method->$visibility()) {
$codeToCoverList[] = $method;
}
}
}
} else {
$classes = [$className];
foreach ($classes as $className) {
if ($className == '' && \function_exists($methodName)) {
$codeToCoverList[] = new ReflectionFunction(
$methodName
);
} else {
if (!((\class_exists($className) || \interface_exists($className) || \trait_exists($className)) &&
\method_exists($className, $methodName))) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover or @use not existing method "%s::%s".',
$className,
$methodName
)
);
}
$codeToCoverList[] = new ReflectionMethod(
$className,
$methodName
);
}
}
}
} else {
$extended = false;
if (\strpos($element, '') !== false) {
$element = \str_replace('', '', $element);
$extended = true;
}
$classes = [$element];
if ($extended) {
$classes = \array_merge(
$classes,
\class_implements($element),
\class_parents($element)
);
}
foreach ($classes as $className) {
if (!\class_exists($className) &&
!\interface_exists($className) &&
!\trait_exists($className)) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover or @use not existing class or ' .
'interface "%s".',
$className
)
);
}
$codeToCoverList[] = new ReflectionClass($className);
}
}
return $codeToCoverList;
}
/**
* @param array $reflectors
*
* @return array
*/
private static function resolveReflectionObjectsToLines(array $reflectors)
{
$result = [];
foreach ($reflectors as $reflector) {
$filename = $reflector->getFileName();
if (!isset($result[$filename])) {
$result[$filename] = [];
}
$result[$filename] = \array_merge(
$result[$filename],
\range($reflector->getStartLine(), $reflector->getEndLine())
);
}
foreach ($result as $filename => $lineNumbers) {
$result[$filename] = \array_keys(\array_flip($lineNumbers));
}
return $result;
}
/**
* @param ReflectionMethod $method
*
* @return bool
*/
private static function isBeforeClassMethod(ReflectionMethod $method)
{
return $method->isStatic() && \strpos($method->getDocComment(), '@beforeClass') !== false;
}
/**
* @param ReflectionMethod $method
*
* @return bool
*/
private static function isBeforeMethod(ReflectionMethod $method)
{
return \preg_match('/@before\b/', $method->getDocComment()) > 0;
}
/**
* @param ReflectionMethod $method
*
* @return bool
*/
private static function isAfterClassMethod(ReflectionMethod $method)
{
return $method->isStatic() && \strpos($method->getDocComment(), '@afterClass') !== false;
}
/**
* @param ReflectionMethod $method
*
* @return bool
*/
private static function isAfterMethod(ReflectionMethod $method)
{
return \preg_match('/@after\b/', $method->getDocComment()) > 0;
}
/**
* Trims any extensions from version string that follows after
* the .[.] format
*
* @param $version (Optional)
*
* @return mixed
*/
private static function sanitizeVersionNumber($version)
{
return \preg_replace(
'/^(\d+\.\d+(?:.\d+)?).*$/',
'$1',
$version
);
}
}
PK (M03-x x src/Util/RegularExpression.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* Error handler that converts PHP errors and warnings to exceptions.
*/
class RegularExpression
{
/**
* @param string $pattern
* @param string $subject
* @param null $matches
* @param int $flags
* @param int $offset
*
* @return int
*/
public static function safeMatch($pattern, $subject, $matches = null, $flags = 0, $offset = 0)
{
$handler_terminator = ErrorHandler::handleErrorOnce(E_WARNING);
$match = \preg_match($pattern, $subject, $matches, $flags, $offset);
$handler_terminator(); // cleaning
return $match;
}
}
PK (M ح) ) " src/Util/InvalidArgumentHelper.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* Factory for PHPUnit\Framework\Exception objects that are used to describe
* invalid arguments passed to a function or method.
*/
class InvalidArgumentHelper
{
/**
* @param int $argument
* @param string $type
* @param mixed $value
*
* @return Exception
*/
public static function factory($argument, $type, $value = null)
{
$stack = \debug_backtrace();
return new Exception(
\sprintf(
'Argument #%d%sof %s::%s() must be a %s',
$argument,
$value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ',
$stack[1]['class'],
$stack[1]['function'],
$type
)
);
}
}
PK (M=- - src/Util/Filesystem.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* Filesystem helpers.
*/
class Filesystem
{
/**
* @var array
*/
protected static $buffer = [];
/**
* Maps class names to source file names:
* - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php
* - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php
*
* @param string $className
*
* @return string
*/
public static function classNameToFilename($className)
{
return \str_replace(
['_', '\\'],
DIRECTORY_SEPARATOR,
$className
) . '.php';
}
}
PK (M(?! ! src/Util/Xml.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use DOMCharacterData;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMText;
use PHPUnit\Framework\Exception;
use ReflectionClass;
/**
* XML helpers.
*/
class Xml
{
/**
* Load an $actual document into a DOMDocument. This is called
* from the selector assertions.
*
* If $actual is already a DOMDocument, it is returned with
* no changes. Otherwise, $actual is loaded into a new DOMDocument
* as either HTML or XML, depending on the value of $isHtml. If $isHtml is
* false and $xinclude is true, xinclude is performed on the loaded
* DOMDocument.
*
* Note: prior to PHPUnit 3.3.0, this method loaded a file and
* not a string as it currently does. To load a file into a
* DOMDocument, use loadFile() instead.
*
* @param string|DOMDocument $actual
* @param bool $isHtml
* @param string $filename
* @param bool $xinclude
* @param bool $strict
*
* @return DOMDocument
*/
public static function load($actual, $isHtml = false, $filename = '', $xinclude = false, $strict = false)
{
if ($actual instanceof DOMDocument) {
return $actual;
}
if (!\is_string($actual)) {
throw new Exception('Could not load XML from ' . \gettype($actual));
}
if ($actual === '') {
throw new Exception('Could not load XML from empty string');
}
// Required for XInclude on Windows.
if ($xinclude) {
$cwd = \getcwd();
@\chdir(\dirname($filename));
}
$document = new DOMDocument;
$document->preserveWhiteSpace = false;
$internal = \libxml_use_internal_errors(true);
$message = '';
$reporting = \error_reporting(0);
if ('' !== $filename) {
// Necessary for xinclude
$document->documentURI = $filename;
}
if ($isHtml) {
$loaded = $document->loadHTML($actual);
} else {
$loaded = $document->loadXML($actual);
}
if (!$isHtml && $xinclude) {
$document->xinclude();
}
foreach (\libxml_get_errors() as $error) {
$message .= "\n" . $error->message;
}
\libxml_use_internal_errors($internal);
\error_reporting($reporting);
if (isset($cwd)) {
@\chdir($cwd);
}
if ($loaded === false || ($strict && $message !== '')) {
if ($filename !== '') {
throw new Exception(
\sprintf(
'Could not load "%s".%s',
$filename,
$message != '' ? "\n" . $message : ''
)
);
}
if ($message === '') {
$message = 'Could not load XML for unknown reason';
}
throw new Exception($message);
}
return $document;
}
/**
* Loads an XML (or HTML) file into a DOMDocument object.
*
* @param string $filename
* @param bool $isHtml
* @param bool $xinclude
* @param bool $strict
*
* @return DOMDocument
*/
public static function loadFile($filename, $isHtml = false, $xinclude = false, $strict = false)
{
$reporting = \error_reporting(0);
$contents = \file_get_contents($filename);
\error_reporting($reporting);
if ($contents === false) {
throw new Exception(
\sprintf(
'Could not read "%s".',
$filename
)
);
}
return self::load($contents, $isHtml, $filename, $xinclude, $strict);
}
/**
* @param DOMNode $node
*/
public static function removeCharacterDataNodes(DOMNode $node)
{
if ($node->hasChildNodes()) {
for ($i = $node->childNodes->length - 1; $i >= 0; $i--) {
if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) {
$node->removeChild($child);
}
}
}
}
/**
* Escapes a string for the use in XML documents
* Any Unicode character is allowed, excluding the surrogate blocks, FFFE,
* and FFFF (not even as character reference).
* See http://www.w3.org/TR/xml/#charsets
*
* @param string $string
*
* @return string
*/
public static function prepareString($string)
{
return \preg_replace(
'/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/',
'',
\htmlspecialchars(
self::convertToUtf8($string),
ENT_QUOTES,
'UTF-8'
)
);
}
/**
* "Convert" a DOMElement object into a PHP variable.
*
* @param DOMElement $element
*
* @return mixed
*/
public static function xmlToVariable(DOMElement $element)
{
$variable = null;
switch ($element->tagName) {
case 'array':
$variable = [];
foreach ($element->childNodes as $entry) {
if (!$entry instanceof DOMElement || $entry->tagName !== 'element') {
continue;
}
$item = $entry->childNodes->item(0);
if ($item instanceof DOMText) {
$item = $entry->childNodes->item(1);
}
$value = self::xmlToVariable($item);
if ($entry->hasAttribute('key')) {
$variable[(string) $entry->getAttribute('key')] = $value;
} else {
$variable[] = $value;
}
}
break;
case 'object':
$className = $element->getAttribute('class');
if ($element->hasChildNodes()) {
$arguments = $element->childNodes->item(0)->childNodes;
$constructorArgs = [];
foreach ($arguments as $argument) {
if ($argument instanceof DOMElement) {
$constructorArgs[] = self::xmlToVariable($argument);
}
}
$class = new ReflectionClass($className);
$variable = $class->newInstanceArgs($constructorArgs);
} else {
$variable = new $className;
}
break;
case 'boolean':
$variable = $element->textContent == 'true';
break;
case 'integer':
case 'double':
case 'string':
$variable = $element->textContent;
\settype($variable, $element->tagName);
break;
}
return $variable;
}
/**
* Converts a string to UTF-8 encoding.
*
* @param string $string
*
* @return string
*/
private static function convertToUtf8($string)
{
if (!self::isUtf8($string)) {
if (\function_exists('mb_convert_encoding')) {
return \mb_convert_encoding($string, 'UTF-8');
}
return \utf8_encode($string);
}
return $string;
}
/**
* Checks a string for UTF-8 encoding.
*
* @param string $string
*
* @return bool
*/
private static function isUtf8($string)
{
$length = \strlen($string);
for ($i = 0; $i < $length; $i++) {
if (\ord($string[$i]) < 0x80) {
$n = 0;
} elseif ((\ord($string[$i]) & 0xE0) == 0xC0) {
$n = 1;
} elseif ((\ord($string[$i]) & 0xF0) == 0xE0) {
$n = 2;
} elseif ((\ord($string[$i]) & 0xF0) == 0xF0) {
$n = 3;
} else {
return false;
}
for ($j = 0; $j < $n; $j++) {
if ((++$i == $length) || ((\ord($string[$i]) & 0xC0) != 0x80)) {
return false;
}
}
}
return true;
}
}
PK (M81 1 # src/Util/ConfigurationGenerator.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
class ConfigurationGenerator
{
/**
* @var string
*/
private $defaultTemplate = <<
{tests_directory}
{src_directory}
EOT;
/**
* @param string $phpunitVersion
* @param string $bootstrapScript
* @param string $testsDirectory
* @param string $srcDirectory
*
* @return string
*/
public function generateDefaultConfiguration($phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory)
{
return \str_replace(
[
'{phpunit_version}',
'{bootstrap_script}',
'{tests_directory}',
'{src_directory}'
],
[
$phpunitVersion,
$bootstrapScript,
$testsDirectory,
$srcDirectory
],
$this->defaultTemplate
);
}
}
PK (MȐ Ȑ src/Util/Configuration.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use DOMElement;
use DOMXPath;
use File_Iterator_Facade;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestSuite;
use PHPUnit\TextUI\ResultPrinter;
/**
* Wrapper for the PHPUnit XML configuration file.
*
* Example XML configuration file:
*
*
*
*
*
*
* /path/to/files
* /path/to/MyTest.php
* /path/to/files/exclude
*
*
*
*
*
* name
*
*
* name
*
*
*
*
*
* name
*
*
* name
*
*
*
*
*
* /path/to/files
* /path/to/file
*
* /path/to/files
* /path/to/file
*
*
*
*
*
*
*
*
*
* Sebastian
*
*
* 22
* April
* 19.78
*
*
* MyRelativeFile.php
* MyRelativeDir
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* .
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
class Configuration
{
const TEST_SUITE_FILTER_SEPARATOR = ',';
private static $instances = [];
protected $document;
protected $xpath;
protected $filename;
/**
* Loads a PHPUnit configuration file.
*
* @param string $filename
*/
protected function __construct($filename)
{
$this->filename = $filename;
$this->document = Xml::loadFile($filename, false, true, true);
$this->xpath = new DOMXPath($this->document);
}
/**
* @codeCoverageIgnore
*/
final private function __clone()
{
}
/**
* Returns a PHPUnit configuration object.
*
* @param string $filename
*
* @return Configuration
*/
public static function getInstance($filename)
{
$realpath = \realpath($filename);
if ($realpath === false) {
throw new Exception(
\sprintf(
'Could not read "%s".',
$filename
)
);
}
if (!isset(self::$instances[$realpath])) {
self::$instances[$realpath] = new self($realpath);
}
return self::$instances[$realpath];
}
/**
* Returns the realpath to the configuration file.
*
* @return string
*/
public function getFilename()
{
return $this->filename;
}
/**
* Returns the configuration for SUT filtering.
*
* @return array
*/
public function getFilterConfiguration()
{
$addUncoveredFilesFromWhitelist = true;
$processUncoveredFilesFromWhitelist = false;
$includeDirectory = [];
$includeFile = [];
$excludeDirectory = [];
$excludeFile = [];
$tmp = $this->xpath->query('filter/whitelist');
if ($tmp->length === 1) {
if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) {
$addUncoveredFilesFromWhitelist = $this->getBoolean(
(string) $tmp->item(0)->getAttribute(
'addUncoveredFilesFromWhitelist'
),
true
);
}
if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) {
$processUncoveredFilesFromWhitelist = $this->getBoolean(
(string) $tmp->item(0)->getAttribute(
'processUncoveredFilesFromWhitelist'
),
false
);
}
$includeDirectory = $this->readFilterDirectories(
'filter/whitelist/directory'
);
$includeFile = $this->readFilterFiles(
'filter/whitelist/file'
);
$excludeDirectory = $this->readFilterDirectories(
'filter/whitelist/exclude/directory'
);
$excludeFile = $this->readFilterFiles(
'filter/whitelist/exclude/file'
);
}
return [
'whitelist' => [
'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist,
'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist,
'include' => [
'directory' => $includeDirectory,
'file' => $includeFile
],
'exclude' => [
'directory' => $excludeDirectory,
'file' => $excludeFile
]
]
];
}
/**
* Returns the configuration for groups.
*
* @return array
*/
public function getGroupConfiguration()
{
return $this->parseGroupConfiguration('groups');
}
/**
* Returns the configuration for testdox groups.
*
* @return array
*/
public function getTestdoxGroupConfiguration()
{
return $this->parseGroupConfiguration('testdoxGroups');
}
/**
* @param string $root
*
* @return array
*/
private function parseGroupConfiguration($root)
{
$groups = [
'include' => [],
'exclude' => []
];
foreach ($this->xpath->query($root . '/include/group') as $group) {
$groups['include'][] = (string) $group->textContent;
}
foreach ($this->xpath->query($root . '/exclude/group') as $group) {
$groups['exclude'][] = (string) $group->textContent;
}
return $groups;
}
/**
* Returns the configuration for listeners.
*
* @return array
*/
public function getListenerConfiguration()
{
$result = [];
foreach ($this->xpath->query('listeners/listener') as $listener) {
$class = (string) $listener->getAttribute('class');
$file = '';
$arguments = [];
if ($listener->getAttribute('file')) {
$file = $this->toAbsolutePath(
(string) $listener->getAttribute('file'),
true
);
}
foreach ($listener->childNodes as $node) {
if ($node instanceof DOMElement && $node->tagName == 'arguments') {
foreach ($node->childNodes as $argument) {
if ($argument instanceof DOMElement) {
if ($argument->tagName == 'file' || $argument->tagName == 'directory') {
$arguments[] = $this->toAbsolutePath((string) $argument->textContent);
} else {
$arguments[] = Xml::xmlToVariable($argument);
}
}
}
}
}
$result[] = [
'class' => $class,
'file' => $file,
'arguments' => $arguments
];
}
return $result;
}
/**
* Returns the logging configuration.
*
* @return array
*/
public function getLoggingConfiguration()
{
$result = [];
foreach ($this->xpath->query('logging/log') as $log) {
$type = (string) $log->getAttribute('type');
$target = (string) $log->getAttribute('target');
if (!$target) {
continue;
}
$target = $this->toAbsolutePath($target);
if ($type == 'coverage-html') {
if ($log->hasAttribute('lowUpperBound')) {
$result['lowUpperBound'] = $this->getInteger(
(string) $log->getAttribute('lowUpperBound'),
50
);
}
if ($log->hasAttribute('highLowerBound')) {
$result['highLowerBound'] = $this->getInteger(
(string) $log->getAttribute('highLowerBound'),
90
);
}
} elseif ($type == 'coverage-crap4j') {
if ($log->hasAttribute('threshold')) {
$result['crap4jThreshold'] = $this->getInteger(
(string) $log->getAttribute('threshold'),
30
);
}
} elseif ($type == 'coverage-text') {
if ($log->hasAttribute('showUncoveredFiles')) {
$result['coverageTextShowUncoveredFiles'] = $this->getBoolean(
(string) $log->getAttribute('showUncoveredFiles'),
false
);
}
if ($log->hasAttribute('showOnlySummary')) {
$result['coverageTextShowOnlySummary'] = $this->getBoolean(
(string) $log->getAttribute('showOnlySummary'),
false
);
}
}
$result[$type] = $target;
}
return $result;
}
/**
* Returns the PHP configuration.
*
* @return array
*/
public function getPHPConfiguration()
{
$result = [
'include_path' => [],
'ini' => [],
'const' => [],
'var' => [],
'env' => [],
'post' => [],
'get' => [],
'cookie' => [],
'server' => [],
'files' => [],
'request' => []
];
foreach ($this->xpath->query('php/includePath') as $includePath) {
$path = (string) $includePath->textContent;
if ($path) {
$result['include_path'][] = $this->toAbsolutePath($path);
}
}
foreach ($this->xpath->query('php/ini') as $ini) {
$name = (string) $ini->getAttribute('name');
$value = (string) $ini->getAttribute('value');
$result['ini'][$name]['value'] = $value;
}
foreach ($this->xpath->query('php/const') as $const) {
$name = (string) $const->getAttribute('name');
$value = (string) $const->getAttribute('value');
$result['const'][$name]['value'] = $this->getBoolean($value, $value);
}
foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
foreach ($this->xpath->query('php/' . $array) as $var) {
$name = (string) $var->getAttribute('name');
$value = (string) $var->getAttribute('value');
$verbatim = false;
$force = false;
if ($var->hasAttribute('verbatim')) {
$verbatim = $this->getBoolean($var->getAttribute('verbatim'), false);
$result[$array][$name]['verbatim'] = $verbatim;
}
if ($var->hasAttribute('force')) {
$force = $this->getBoolean($var->getAttribute('force'), false);
$result[$array][$name]['force'] = $force;
}
if (!$verbatim) {
$value = $this->getBoolean($value, $value);
}
$result[$array][$name]['value'] = $value;
}
}
return $result;
}
/**
* Handles the PHP configuration.
*/
public function handlePHPConfiguration()
{
$configuration = $this->getPHPConfiguration();
if (!empty($configuration['include_path'])) {
\ini_set(
'include_path',
\implode(PATH_SEPARATOR, $configuration['include_path']) .
PATH_SEPARATOR .
\ini_get('include_path')
);
}
foreach ($configuration['ini'] as $name => $data) {
$value = $data['value'];
if (\defined($value)) {
$value = (string) \constant($value);
}
\ini_set($name, $value);
}
foreach ($configuration['const'] as $name => $data) {
$value = $data['value'];
if (!\defined($name)) {
\define($name, $value);
}
}
foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
// See https://github.com/sebastianbergmann/phpunit/issues/277
switch ($array) {
case 'var':
$target = &$GLOBALS;
break;
case 'server':
$target = &$_SERVER;
break;
default:
$target = &$GLOBALS['_' . \strtoupper($array)];
break;
}
foreach ($configuration[$array] as $name => $data) {
$target[$name] = $data['value'];
}
}
foreach ($configuration['env'] as $name => $data) {
$value = $data['value'];
$force = isset($data['force']) ? $data['force'] : false;
if ($force || false === \getenv($name)) {
\putenv("{$name}={$value}");
}
$value = \getenv($name);
if (!isset($_ENV[$name])) {
$_ENV[$name] = $value;
}
if (true === $force) {
$_ENV[$name] = $value;
}
}
}
/**
* Returns the PHPUnit configuration.
*
* @return array
*/
public function getPHPUnitConfiguration()
{
$result = [];
$root = $this->document->documentElement;
if ($root->hasAttribute('cacheTokens')) {
$result['cacheTokens'] = $this->getBoolean(
(string) $root->getAttribute('cacheTokens'),
false
);
}
if ($root->hasAttribute('columns')) {
$columns = (string) $root->getAttribute('columns');
if ($columns == 'max') {
$result['columns'] = 'max';
} else {
$result['columns'] = $this->getInteger($columns, 80);
}
}
if ($root->hasAttribute('colors')) {
/* only allow boolean for compatibility with previous versions
'always' only allowed from command line */
if ($this->getBoolean($root->getAttribute('colors'), false)) {
$result['colors'] = ResultPrinter::COLOR_AUTO;
} else {
$result['colors'] = ResultPrinter::COLOR_NEVER;
}
}
/*
* Issue #657
*/
if ($root->hasAttribute('stderr')) {
$result['stderr'] = $this->getBoolean(
(string) $root->getAttribute('stderr'),
false
);
}
if ($root->hasAttribute('backupGlobals')) {
$result['backupGlobals'] = $this->getBoolean(
(string) $root->getAttribute('backupGlobals'),
false
);
}
if ($root->hasAttribute('backupStaticAttributes')) {
$result['backupStaticAttributes'] = $this->getBoolean(
(string) $root->getAttribute('backupStaticAttributes'),
false
);
}
if ($root->getAttribute('bootstrap')) {
$result['bootstrap'] = $this->toAbsolutePath(
(string) $root->getAttribute('bootstrap')
);
}
if ($root->hasAttribute('convertDeprecationsToExceptions')) {
$result['convertDeprecationsToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertDeprecationsToExceptions'),
true
);
}
if ($root->hasAttribute('convertErrorsToExceptions')) {
$result['convertErrorsToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertErrorsToExceptions'),
true
);
}
if ($root->hasAttribute('convertNoticesToExceptions')) {
$result['convertNoticesToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertNoticesToExceptions'),
true
);
}
if ($root->hasAttribute('convertWarningsToExceptions')) {
$result['convertWarningsToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertWarningsToExceptions'),
true
);
}
if ($root->hasAttribute('forceCoversAnnotation')) {
$result['forceCoversAnnotation'] = $this->getBoolean(
(string) $root->getAttribute('forceCoversAnnotation'),
false
);
}
if ($root->hasAttribute('disableCodeCoverageIgnore')) {
$result['disableCodeCoverageIgnore'] = $this->getBoolean(
(string) $root->getAttribute('disableCodeCoverageIgnore'),
false
);
}
if ($root->hasAttribute('processIsolation')) {
$result['processIsolation'] = $this->getBoolean(
(string) $root->getAttribute('processIsolation'),
false
);
}
if ($root->hasAttribute('stopOnError')) {
$result['stopOnError'] = $this->getBoolean(
(string) $root->getAttribute('stopOnError'),
false
);
}
if ($root->hasAttribute('stopOnFailure')) {
$result['stopOnFailure'] = $this->getBoolean(
(string) $root->getAttribute('stopOnFailure'),
false
);
}
if ($root->hasAttribute('stopOnWarning')) {
$result['stopOnWarning'] = $this->getBoolean(
(string) $root->getAttribute('stopOnWarning'),
false
);
}
if ($root->hasAttribute('stopOnIncomplete')) {
$result['stopOnIncomplete'] = $this->getBoolean(
(string) $root->getAttribute('stopOnIncomplete'),
false
);
}
if ($root->hasAttribute('stopOnRisky')) {
$result['stopOnRisky'] = $this->getBoolean(
(string) $root->getAttribute('stopOnRisky'),
false
);
}
if ($root->hasAttribute('stopOnSkipped')) {
$result['stopOnSkipped'] = $this->getBoolean(
(string) $root->getAttribute('stopOnSkipped'),
false
);
}
if ($root->hasAttribute('failOnWarning')) {
$result['failOnWarning'] = $this->getBoolean(
(string) $root->getAttribute('failOnWarning'),
false
);
}
if ($root->hasAttribute('failOnRisky')) {
$result['failOnRisky'] = $this->getBoolean(
(string) $root->getAttribute('failOnRisky'),
false
);
}
if ($root->hasAttribute('testSuiteLoaderClass')) {
$result['testSuiteLoaderClass'] = (string) $root->getAttribute(
'testSuiteLoaderClass'
);
}
if ($root->hasAttribute('defaultTestSuite')) {
$result['defaultTestSuite'] = (string) $root->getAttribute(
'defaultTestSuite'
);
}
if ($root->getAttribute('testSuiteLoaderFile')) {
$result['testSuiteLoaderFile'] = $this->toAbsolutePath(
(string) $root->getAttribute('testSuiteLoaderFile')
);
}
if ($root->hasAttribute('printerClass')) {
$result['printerClass'] = (string) $root->getAttribute(
'printerClass'
);
}
if ($root->getAttribute('printerFile')) {
$result['printerFile'] = $this->toAbsolutePath(
(string) $root->getAttribute('printerFile')
);
}
if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) {
$result['beStrictAboutChangesToGlobalState'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutChangesToGlobalState'),
false
);
}
if ($root->hasAttribute('beStrictAboutOutputDuringTests')) {
$result['disallowTestOutput'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutOutputDuringTests'),
false
);
}
if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) {
$result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'),
false
);
}
if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) {
$result['reportUselessTests'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'),
true
);
}
if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) {
$result['disallowTodoAnnotatedTests'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'),
false
);
}
if ($root->hasAttribute('beStrictAboutCoversAnnotation')) {
$result['strictCoverage'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutCoversAnnotation'),
false
);
}
if ($root->hasAttribute('enforceTimeLimit')) {
$result['enforceTimeLimit'] = $this->getBoolean(
(string) $root->getAttribute('enforceTimeLimit'),
false
);
}
if ($root->hasAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage')) {
$result['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $this->getBoolean(
(string) $root->getAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage'),
false
);
}
if ($root->hasAttribute('timeoutForSmallTests')) {
$result['timeoutForSmallTests'] = $this->getInteger(
(string) $root->getAttribute('timeoutForSmallTests'),
1
);
}
if ($root->hasAttribute('timeoutForMediumTests')) {
$result['timeoutForMediumTests'] = $this->getInteger(
(string) $root->getAttribute('timeoutForMediumTests'),
10
);
}
if ($root->hasAttribute('timeoutForLargeTests')) {
$result['timeoutForLargeTests'] = $this->getInteger(
(string) $root->getAttribute('timeoutForLargeTests'),
60
);
}
if ($root->hasAttribute('reverseDefectList')) {
$result['reverseDefectList'] = $this->getBoolean(
(string) $root->getAttribute('reverseDefectList'),
false
);
}
if ($root->hasAttribute('verbose')) {
$result['verbose'] = $this->getBoolean(
(string) $root->getAttribute('verbose'),
false
);
}
if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) {
$result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean(
(string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'),
false
);
}
if ($root->hasAttribute('extensionsDirectory')) {
$result['extensionsDirectory'] = $this->toAbsolutePath(
(string) $root->getAttribute(
'extensionsDirectory'
)
);
}
return $result;
}
/**
* Returns the test suite configuration.
*
* @param string|null $testSuiteFilter
*
* @return TestSuite
*/
public function getTestSuiteConfiguration($testSuiteFilter = null)
{
$testSuiteNodes = $this->xpath->query('testsuites/testsuite');
if ($testSuiteNodes->length == 0) {
$testSuiteNodes = $this->xpath->query('testsuite');
}
if ($testSuiteNodes->length == 1) {
return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter);
}
//if ($testSuiteNodes->length > 1) { there cannot be a negative number of Nodes
$suite = new TestSuite;
foreach ($testSuiteNodes as $testSuiteNode) {
$suite->addTestSuite(
$this->getTestSuite($testSuiteNode, $testSuiteFilter)
);
}
return $suite;
}
/**
* Returns the test suite names from the configuration.
*
* @return array
*/
public function getTestSuiteNames()
{
$names = [];
$nodes = $this->xpath->query('*/testsuite');
foreach ($nodes as $node) {
$names[] = $node->getAttribute('name');
}
return $names;
}
/**
* @param DOMElement $testSuiteNode
* @param string|null $testSuiteFilter
*
* @return TestSuite
*/
protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter = null)
{
if ($testSuiteNode->hasAttribute('name')) {
$suite = new TestSuite(
(string) $testSuiteNode->getAttribute('name')
);
} else {
$suite = new TestSuite;
}
$exclude = [];
foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) {
$excludeFile = (string) $excludeNode->textContent;
if ($excludeFile) {
$exclude[] = $this->toAbsolutePath($excludeFile);
}
}
$fileIteratorFacade = new File_Iterator_Facade;
$testSuiteFilter = $testSuiteFilter ? \explode(self::TEST_SUITE_FILTER_SEPARATOR, $testSuiteFilter) : [];
foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) {
if (!empty($testSuiteFilter) && !\in_array($directoryNode->parentNode->getAttribute('name'), $testSuiteFilter)) {
continue;
}
$directory = (string) $directoryNode->textContent;
if (empty($directory)) {
continue;
}
if ($directoryNode->hasAttribute('phpVersion')) {
$phpVersion = (string) $directoryNode->getAttribute('phpVersion');
} else {
$phpVersion = PHP_VERSION;
}
if ($directoryNode->hasAttribute('phpVersionOperator')) {
$phpVersionOperator = (string) $directoryNode->getAttribute('phpVersionOperator');
} else {
$phpVersionOperator = '>=';
}
if (!\version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) {
continue;
}
if ($directoryNode->hasAttribute('prefix')) {
$prefix = (string) $directoryNode->getAttribute('prefix');
} else {
$prefix = '';
}
if ($directoryNode->hasAttribute('suffix')) {
$suffix = (string) $directoryNode->getAttribute('suffix');
} else {
$suffix = 'Test.php';
}
$files = $fileIteratorFacade->getFilesAsArray(
$this->toAbsolutePath($directory),
$suffix,
$prefix,
$exclude
);
$suite->addTestFiles($files);
}
foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) {
if (!empty($testSuiteFilter) && !\in_array($fileNode->parentNode->getAttribute('name'), $testSuiteFilter)) {
continue;
}
$file = (string) $fileNode->textContent;
if (empty($file)) {
continue;
}
// Get the absolute path to the file
$file = $fileIteratorFacade->getFilesAsArray(
$this->toAbsolutePath($file)
);
if (!isset($file[0])) {
continue;
}
$file = $file[0];
if ($fileNode->hasAttribute('phpVersion')) {
$phpVersion = (string) $fileNode->getAttribute('phpVersion');
} else {
$phpVersion = PHP_VERSION;
}
if ($fileNode->hasAttribute('phpVersionOperator')) {
$phpVersionOperator = (string) $fileNode->getAttribute('phpVersionOperator');
} else {
$phpVersionOperator = '>=';
}
if (!\version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) {
continue;
}
$suite->addTestFile($file);
}
return $suite;
}
/**
* if $value is 'false' or 'true', this returns the value that $value represents.
* Otherwise, returns $default, which may be a string in rare cases.
* See PHPUnit\Util\ConfigurationTest::testPHPConfigurationIsReadCorrectly
*
* @param string $value
* @param string|bool $default
*
* @return string|bool
*/
protected function getBoolean($value, $default)
{
if (\strtolower($value) == 'false') {
return false;
}
if (\strtolower($value) == 'true') {
return true;
}
return $default;
}
/**
* @param string $value
* @param int $default
*
* @return int
*/
protected function getInteger($value, $default)
{
if (\is_numeric($value)) {
return (int) $value;
}
return $default;
}
/**
* @param string $query
*
* @return array
*/
protected function readFilterDirectories($query)
{
$directories = [];
foreach ($this->xpath->query($query) as $directory) {
$directoryPath = (string) $directory->textContent;
if (!$directoryPath) {
continue;
}
if ($directory->hasAttribute('prefix')) {
$prefix = (string) $directory->getAttribute('prefix');
} else {
$prefix = '';
}
if ($directory->hasAttribute('suffix')) {
$suffix = (string) $directory->getAttribute('suffix');
} else {
$suffix = '.php';
}
if ($directory->hasAttribute('group')) {
$group = (string) $directory->getAttribute('group');
} else {
$group = 'DEFAULT';
}
$directories[] = [
'path' => $this->toAbsolutePath($directoryPath),
'prefix' => $prefix,
'suffix' => $suffix,
'group' => $group
];
}
return $directories;
}
/**
* @param string $query
*
* @return array
*/
protected function readFilterFiles($query)
{
$files = [];
foreach ($this->xpath->query($query) as $file) {
$filePath = (string) $file->textContent;
if ($filePath) {
$files[] = $this->toAbsolutePath($filePath);
}
}
return $files;
}
/**
* @param string $path
* @param bool $useIncludePath
*
* @return string
*/
protected function toAbsolutePath($path, $useIncludePath = false)
{
$path = \trim($path);
if ($path[0] === '/') {
return $path;
}
// Matches the following on Windows:
// - \\NetworkComputer\Path
// - \\.\D:
// - \\.\c:
// - C:\Windows
// - C:\windows
// - C:/windows
// - c:/windows
if (\defined('PHP_WINDOWS_VERSION_BUILD') &&
($path[0] === '\\' || (\strlen($path) >= 3 && \preg_match('#^[A-Z]\:[/\\\]#i', \substr($path, 0, 3))))) {
return $path;
}
// Stream
if (\strpos($path, '://') !== false) {
return $path;
}
$file = \dirname($this->filename) . DIRECTORY_SEPARATOR . $path;
if ($useIncludePath && !\file_exists($file)) {
$includePathFile = \stream_resolve_include_path($path);
if ($includePathFile) {
$file = $includePathFile;
}
}
return $file;
}
}
PK (Ms-7
7
src/Util/Blacklist.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use ReflectionClass;
/**
* Utility class for blacklisting PHPUnit's own source code files.
*/
class Blacklist
{
/**
* @var array
*/
public static $blacklistedClassNames = [
'File_Iterator' => 1,
'PHP_Invoker' => 1,
'PHP_Timer' => 1,
'PHP_Token' => 1,
'PHPUnit\Framework\TestCase' => 2,
'PHPUnit\DbUnit\TestCase' => 2,
'PHPUnit\Framework\MockObject\Generator' => 1,
'Text_Template' => 1,
'Symfony\Component\Yaml\Yaml' => 1,
'SebastianBergmann\CodeCoverage\CodeCoverage' => 1,
'SebastianBergmann\Diff\Diff' => 1,
'SebastianBergmann\Environment\Runtime' => 1,
'SebastianBergmann\Comparator\Comparator' => 1,
'SebastianBergmann\Exporter\Exporter' => 1,
'SebastianBergmann\GlobalState\Snapshot' => 1,
'SebastianBergmann\RecursionContext\Context' => 1,
'SebastianBergmann\Version' => 1,
'Composer\Autoload\ClassLoader' => 1,
'Doctrine\Instantiator\Instantiator' => 1,
'phpDocumentor\Reflection\DocBlock' => 1,
'Prophecy\Prophet' => 1,
'DeepCopy\DeepCopy' => 1
];
/**
* @var string[]
*/
private static $directories;
/**
* @return string[]
*/
public function getBlacklistedDirectories()
{
$this->initialize();
return self::$directories;
}
/**
* @param string $file
*
* @return bool
*/
public function isBlacklisted($file)
{
if (\defined('PHPUNIT_TESTSUITE')) {
return false;
}
$this->initialize();
foreach (self::$directories as $directory) {
if (\strpos($file, $directory) === 0) {
return true;
}
}
return false;
}
private function initialize()
{
if (self::$directories === null) {
self::$directories = [];
foreach (self::$blacklistedClassNames as $className => $parent) {
if (!\class_exists($className)) {
continue;
}
$reflector = new ReflectionClass($className);
$directory = $reflector->getFileName();
for ($i = 0; $i < $parent; $i++) {
$directory = \dirname($directory);
}
self::$directories[] = $directory;
}
// Hide process isolation workaround on Windows.
if (DIRECTORY_SEPARATOR === '\\') {
// tempnam() prefix is limited to first 3 chars.
// @see http://php.net/manual/en/function.tempnam.php
self::$directories[] = \sys_get_temp_dir() . '\\PHP';
}
}
}
}
PK (M;| | ! src/Util/TextTestListRenderer.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
class TextTestListRenderer
{
public function render(TestSuite $suite): string
{
$buffer = 'Available test(s):' . PHP_EOL;
foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) {
if ($test instanceof TestCase) {
$name = \sprintf(
'%s::%s',
\get_class($test),
\str_replace(' with data set ', '', $test->getName())
);
} elseif ($test instanceof PhptTestCase) {
$name = $test->getName();
} else {
continue;
}
$buffer .= \sprintf(
' - %s' . PHP_EOL,
$name
);
}
return $buffer;
}
}
PK (M:H src/Util/Json.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
class Json
{
/**
* Prettify json string
*
* @param string $json
*
* @return string
*
* @throws \PHPUnit\Framework\Exception
*/
public static function prettify(string $json)
{
$decodedJson = \json_decode($json, true);
if (\json_last_error()) {
throw new Exception(
'Cannot prettify invalid json'
);
}
return \json_encode($decodedJson, JSON_PRETTY_PRINT);
}
/*
* To allow comparison of JSON strings, first process them into a consistent
* format so that they can be compared as strings.
* @return array ($error, $canonicalized_json) The $error parameter is used
* to indicate an error decoding the json. This is used to avoid ambiguity
* with JSON strings consisting entirely of 'null' or 'false'.
*/
public static function canonicalize(string $json)
{
$decodedJson = \json_decode($json, true);
if (\json_last_error()) {
return [true, null];
}
self::recursiveSort($decodedJson);
$reencodedJson = \json_encode($decodedJson);
return [false, $reencodedJson];
}
/*
* JSON object keys are unordered while PHP array keys are ordered.
* Sort all array keys to ensure both the expected and actual values have
* their keys in the same order.
*/
private static function recursiveSort(&$json)
{
if (false === \is_array($json)) {
return;
}
\ksort($json);
foreach ($json as $key => &$value) {
self::recursiveSort($value);
}
}
}
PK (Mhj src/Util/ErrorHandler.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;
/**
* Error handler that converts PHP errors and warnings to exceptions.
*/
class ErrorHandler
{
protected static $errorStack = [];
/**
* Returns the error stack.
*
* @return array
*/
public static function getErrorStack()
{
return self::$errorStack;
}
/**
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
*
* @return false
*
* @throws Error
*/
public static function handleError($errno, $errstr, $errfile, $errline)
{
if (!($errno & \error_reporting())) {
return false;
}
self::$errorStack[] = [$errno, $errstr, $errfile, $errline];
$trace = \debug_backtrace();
\array_shift($trace);
foreach ($trace as $frame) {
if ($frame['function'] == '__toString') {
return false;
}
}
if ($errno == E_NOTICE || $errno == E_USER_NOTICE || $errno == E_STRICT) {
if (Notice::$enabled !== true) {
return false;
}
$exception = Notice::class;
} elseif ($errno == E_WARNING || $errno == E_USER_WARNING) {
if (Warning::$enabled !== true) {
return false;
}
$exception = Warning::class;
} elseif ($errno == E_DEPRECATED || $errno == E_USER_DEPRECATED) {
if (Deprecated::$enabled !== true) {
return false;
}
$exception = Deprecated::class;
} else {
$exception = Error::class;
}
throw new $exception($errstr, $errno, $errfile, $errline);
}
/**
* Registers an error handler and returns a function that will restore
* the previous handler when invoked
*
* @param int $severity PHP predefined error constant
*
* @return \Closure
*
* @throws \Exception if event of specified severity is emitted
*/
public static function handleErrorOnce($severity = E_WARNING)
{
$terminator = function () {
static $expired = false;
if (!$expired) {
$expired = true;
// cleans temporary error handler
return \restore_error_handler();
}
};
\set_error_handler(function ($errno, $errstr) use ($severity) {
if ($errno === $severity) {
return;
}
return false;
});
return $terminator;
}
}
PK (ML9e# # " src/Util/TestDox/ResultPrinter.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Util\Printer;
/**
* Base class for printers of TestDox documentation.
*/
abstract class ResultPrinter extends Printer implements TestListener
{
/**
* @var NamePrettifier
*/
protected $prettifier;
/**
* @var string
*/
protected $testClass = '';
/**
* @var int
*/
protected $testStatus;
/**
* @var array
*/
protected $tests = [];
/**
* @var int
*/
protected $successful = 0;
/**
* @var int
*/
protected $warned = 0;
/**
* @var int
*/
protected $failed = 0;
/**
* @var int
*/
protected $risky = 0;
/**
* @var int
*/
protected $skipped = 0;
/**
* @var int
*/
protected $incomplete = 0;
/**
* @var string|null
*/
protected $currentTestClassPrettified;
/**
* @var string|null
*/
protected $currentTestMethodPrettified;
/**
* @var array
*/
private $groups;
/**
* @var array
*/
private $excludeGroups;
/**
* @param resource $out
* @param array $groups
* @param array $excludeGroups
*/
public function __construct($out = null, array $groups = [], array $excludeGroups = [])
{
parent::__construct($out);
$this->groups = $groups;
$this->excludeGroups = $excludeGroups;
$this->prettifier = new NamePrettifier;
$this->startRun();
}
/**
* Flush buffer and close output.
*/
public function flush()
{
$this->doEndClass();
$this->endRun();
parent::flush();
}
/**
* An error occurred.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(Test $test, \Exception $e, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_ERROR;
$this->failed++;
}
/**
* A warning occurred.
*
* @param Test $test
* @param Warning $e
* @param float $time
*/
public function addWarning(Test $test, Warning $e, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_WARNING;
$this->warned++;
}
/**
* A failure occurred.
*
* @param Test $test
* @param AssertionFailedError $e
* @param float $time
*/
public function addFailure(Test $test, AssertionFailedError $e, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_FAILURE;
$this->failed++;
}
/**
* Incomplete test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(Test $test, \Exception $e, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_INCOMPLETE;
$this->incomplete++;
}
/**
* Risky test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addRiskyTest(Test $test, \Exception $e, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_RISKY;
$this->risky++;
}
/**
* Skipped test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addSkippedTest(Test $test, \Exception $e, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_SKIPPED;
$this->skipped++;
}
/**
* A testsuite started.
*
* @param TestSuite $suite
*/
public function startTestSuite(TestSuite $suite)
{
}
/**
* A testsuite ended.
*
* @param TestSuite $suite
*/
public function endTestSuite(TestSuite $suite)
{
}
/**
* A test started.
*
* @param Test $test
*/
public function startTest(Test $test)
{
if (!$this->isOfInterest($test)) {
return;
}
$class = \get_class($test);
if ($this->testClass != $class) {
if ($this->testClass != '') {
$this->doEndClass();
}
$classAnnotations = \PHPUnit\Util\Test::parseTestMethodAnnotations($class);
if (isset($classAnnotations['class']['testdox'][0])) {
$this->currentTestClassPrettified = $classAnnotations['class']['testdox'][0];
} else {
$this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class);
}
$this->startClass($class);
$this->testClass = $class;
$this->tests = [];
}
if ($test instanceof TestCase) {
$annotations = $test->getAnnotations();
if (isset($annotations['method']['testdox'][0])) {
$this->currentTestMethodPrettified = $annotations['method']['testdox'][0];
} else {
$this->currentTestMethodPrettified = $this->prettifier->prettifyTestMethod($test->getName(false));
}
if ($test->usesDataProvider()) {
$this->currentTestMethodPrettified .= ' ' . $test->dataDescription();
}
}
$this->testStatus = BaseTestRunner::STATUS_PASSED;
}
/**
* A test ended.
*
* @param Test $test
* @param float $time
*/
public function endTest(Test $test, $time)
{
if (!$this->isOfInterest($test)) {
return;
}
if (!isset($this->tests[$this->currentTestMethodPrettified])) {
if ($this->testStatus == BaseTestRunner::STATUS_PASSED) {
$this->tests[$this->currentTestMethodPrettified]['success'] = 1;
$this->tests[$this->currentTestMethodPrettified]['failure'] = 0;
} else {
$this->tests[$this->currentTestMethodPrettified]['success'] = 0;
$this->tests[$this->currentTestMethodPrettified]['failure'] = 1;
}
} else {
if ($this->testStatus == BaseTestRunner::STATUS_PASSED) {
$this->tests[$this->currentTestMethodPrettified]['success']++;
} else {
$this->tests[$this->currentTestMethodPrettified]['failure']++;
}
}
$this->currentTestClassPrettified = null;
$this->currentTestMethodPrettified = null;
}
protected function doEndClass()
{
foreach ($this->tests as $name => $data) {
$this->onTest($name, $data['failure'] == 0);
}
$this->endClass($this->testClass);
}
/**
* Handler for 'start run' event.
*/
protected function startRun()
{
}
/**
* Handler for 'start class' event.
*
* @param string $name
*/
protected function startClass($name)
{
}
/**
* Handler for 'on test' event.
*
* @param string $name
* @param bool $success
*/
protected function onTest($name, $success = true)
{
}
/**
* Handler for 'end class' event.
*
* @param string $name
*/
protected function endClass($name)
{
}
/**
* Handler for 'end run' event.
*/
protected function endRun()
{
}
/**
* @param Test $test
*
* @return bool
*/
private function isOfInterest(Test $test)
{
if (!$test instanceof TestCase) {
return false;
}
if ($test instanceof WarningTestCase) {
return false;
}
if (!empty($this->groups)) {
foreach ($test->getGroups() as $group) {
if (\in_array($group, $this->groups)) {
return true;
}
}
return false;
}
if (!empty($this->excludeGroups)) {
foreach ($test->getGroups() as $group) {
if (\in_array($group, $this->excludeGroups)) {
return false;
}
}
return true;
}
return true;
}
}
PK (M F F # src/Util/TestDox/NamePrettifier.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
/**
* Prettifies class and method names for use in TestDox documentation.
*/
class NamePrettifier
{
/**
* @var string
*/
protected $prefix = 'Test';
/**
* @var string
*/
protected $suffix = 'Test';
/**
* @var array
*/
protected $strings = [];
/**
* Prettifies the name of a test class.
*
* @param string $name
*
* @return string
*/
public function prettifyTestClass($name)
{
$title = $name;
if ($this->suffix !== null &&
$this->suffix == \substr($name, -1 * \strlen($this->suffix))) {
$title = \substr($title, 0, \strripos($title, $this->suffix));
}
if ($this->prefix !== null &&
$this->prefix == \substr($name, 0, \strlen($this->prefix))) {
$title = \substr($title, \strlen($this->prefix));
}
if (\substr($title, 0, 1) == '\\') {
$title = \substr($title, 1);
}
return $title;
}
/**
* Prettifies the name of a test method.
*
* @param string $name
*
* @return string
*/
public function prettifyTestMethod($name)
{
$buffer = '';
if (!\is_string($name) || \strlen($name) == 0) {
return $buffer;
}
$string = \preg_replace('#\d+$#', '', $name, -1, $count);
if (\in_array($string, $this->strings)) {
$name = $string;
} elseif ($count == 0) {
$this->strings[] = $string;
}
if (\substr($name, 0, 4) == 'test') {
$name = \substr($name, 4);
}
if (\strlen($name) == 0) {
return $buffer;
}
$name[0] = \strtoupper($name[0]);
if (\strpos($name, '_') !== false) {
return \trim(\str_replace('_', ' ', $name));
}
$max = \strlen($name);
$wasNumeric = false;
for ($i = 0; $i < $max; $i++) {
if ($i > 0 && \ord($name[$i]) >= 65 && \ord($name[$i]) <= 90) {
$buffer .= ' ' . \strtolower($name[$i]);
} else {
$isNumeric = \is_numeric($name[$i]);
if (!$wasNumeric && $isNumeric) {
$buffer .= ' ';
$wasNumeric = true;
}
if ($wasNumeric && !$isNumeric) {
$wasNumeric = false;
}
$buffer .= $name[$i];
}
}
return $buffer;
}
/**
* Sets the prefix of test names.
*
* @param string $prefix
*/
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
/**
* Sets the suffix of test names.
*
* @param string $suffix
*/
public function setSuffix($suffix)
{
$this->suffix = $suffix;
}
}
PK (MWi i % src/Util/TestDox/XmlResultPrinter.phpnu W+A
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Printer;
use ReflectionClass;
class XmlResultPrinter extends Printer implements TestListener
{
/**
* @var DOMDocument
*/
private $document;
/**
* @var DOMElement
*/
private $root;
/**
* @var NamePrettifier
*/
private $prettifier;
/**
* @var \Exception|null
*/
private $exception;
/**
* @param string|resource $out
*/
public function __construct($out = null)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('tests');
$this->document->appendChild($this->root);
$this->prettifier = new NamePrettifier;
parent::__construct($out);
}
/**
* Flush buffer and close output.
*/
public function flush()
{
$this->write($this->document->saveXML());
parent::flush();
}
/**
* An error occurred.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addError(Test $test, \Exception $e, $time)
{
$this->exception = $e;
}
/**
* A warning occurred.
*
* @param Test $test
* @param Warning $e
* @param float $time
*/
public function addWarning(Test $test, Warning $e, $time)
{
}
/**
* A failure occurred.
*
* @param Test $test
* @param AssertionFailedError $e
* @param float $time
*/
public function addFailure(Test $test, AssertionFailedError $e, $time)
{
$this->exception = $e;
}
/**
* Incomplete test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(Test $test, \Exception $e, $time)
{
}
/**
* Risky test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addRiskyTest(Test $test, \Exception $e, $time)
{
}
/**
* Skipped test.
*
* @param Test $test
* @param \Exception $e
* @param float $time
*/
public function addSkippedTest(Test $test, \Exception $e, $time)
{
}
/**
* A test suite started.
*
* @param TestSuite $suite
*/
public function startTestSuite(TestSuite $suite)
{
}
/**
* A test suite ended.
*
* @param TestSuite $suite
*/
public function endTestSuite(TestSuite $suite)
{
}
/**
* A test started.
*
* @param Test $test
*/
public function startTest(Test $test)
{
$this->exception = null;
}
/**
* A test ended.
*
* @param Test $test
* @param float $time
*/
public function endTest(Test $test, $time)
{
if (!$test instanceof TestCase) {
return;
}
/* @var TestCase $test */
$groups = \array_filter(
$test->getGroups(),
function ($group) {
if ($group == 'small' || $group == 'medium' || $group == 'large') {
return false;
}
return true;
}
);
$node = $this->document->createElement('test');
$node->setAttribute('className', \get_class($test));
$node->setAttribute('methodName', $test->getName());
$node->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(\get_class($test)));
$node->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestMethod($test->getName()));
$node->setAttribute('status', $test->getStatus());
$node->setAttribute('time', $time);
$node->setAttribute('size', $test->getSize());
$node->setAttribute('groups', \implode(',', $groups));
$inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(\get_class($test), $test->getName());
if (isset($inlineAnnotations['given']) && isset($inlineAnnotations['when']) && isset($inlineAnnotations['then'])) {
$node->setAttribute('given', $inlineAnnotations['given']['value']);
$node->setAttribute('givenStartLine', $inlineAnnotations['given']['line']);
$node->setAttribute('when', $inlineAnnotations['when']['value']);
$node->setAttribute('whenStartLine', $inlineAnnotations['when']['line']);
$node->setAttribute('then', $inlineAnnotations['then']['value']);
$node->setAttribute('thenStartLine', $inlineAnnotations['then']['line']);
}
if ($this->exception !== null) {
if ($this->exception instanceof Exception) {
$steps = $this->exception->getSerializableTrace();
} else {
$steps = $this->exception->getTrace();
}
$class = new ReflectionClass($test);
$file = $class->getFileName();
foreach ($steps as $step) {
if (isset($step['file']) && $step['file'] == $file) {
$node->setAttribute('exceptionLine', $step['line']);
break;
}
}
$node->setAttribute('exceptionMessage', $this->exception->getMessage());
}
$this->root->appendChild($node);
}
}
PK (MM#i
&