diff --git a/phpunit.xml b/phpunit.xml
index 92b3e855560..673e3c9396b 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -12,6 +12,7 @@
tests/end-to-end
+ tests/end-to-end/_files
diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php
index 453abd9bbdc..4b08f1161e8 100644
--- a/src/Framework/Assert.php
+++ b/src/Framework/Assert.php
@@ -3000,6 +3000,12 @@ public static function markTestIncomplete(string $message = ''): void
*/
public static function markTestSkipped(string $message = ''): void
{
+ if ($hint = self::detectLocationHint($message)) {
+ $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
+
+ throw new SyntheticSkippedError($hint['message'], 0, $hint['file'], $hint['line'], $trace);
+ }
+
throw new SkippedTestError($message);
}
@@ -3019,6 +3025,30 @@ public static function resetCount(): void
self::$count = 0;
}
+ private static function detectLocationHint(string $message): ?array
+ {
+ $hint = null;
+ $lines = \preg_split('/\r\n|\r|\n/', $message);
+
+ while (\strpos($lines[0], '__OFFSET') !== false) {
+ $offset = \explode('=', \array_shift($lines));
+
+ if ($offset[0] === '__OFFSET_FILE') {
+ $hint['file'] = $offset[1];
+ }
+
+ if ($offset[0] === '__OFFSET_LINE') {
+ $hint['line'] = $offset[1];
+ }
+ }
+
+ if ($hint) {
+ $hint['message'] = \implode(\PHP_EOL, $lines);
+ }
+
+ return $hint;
+ }
+
private static function isValidAttributeName(string $attributeName): bool
{
return \preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName);
diff --git a/src/Framework/PHPTAssertionFailedError.php b/src/Framework/PHPTAssertionFailedError.php
new file mode 100644
index 00000000000..e1cdfff97af
--- /dev/null
+++ b/src/Framework/PHPTAssertionFailedError.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace PHPUnit\Framework;
+
+class PHPTAssertionFailedError extends SyntheticError
+{
+}
diff --git a/src/Framework/SyntheticSkippedError.php b/src/Framework/SyntheticSkippedError.php
new file mode 100644
index 00000000000..186ca0ccd8b
--- /dev/null
+++ b/src/Framework/SyntheticSkippedError.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace PHPUnit\Framework;
+
+class SyntheticSkippedError extends SyntheticError implements SkippedTest
+{
+}
diff --git a/src/Runner/PhptTestCase.php b/src/Runner/PhptTestCase.php
index bbc1a239750..5341c677178 100644
--- a/src/Runner/PhptTestCase.php
+++ b/src/Runner/PhptTestCase.php
@@ -11,9 +11,11 @@
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\AssertionFailedError;
+use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\IncompleteTestError;
+use PHPUnit\Framework\PHPTAssertionFailedError;
use PHPUnit\Framework\SelfDescribing;
-use PHPUnit\Framework\SkippedTestError;
+use PHPUnit\Framework\SyntheticSkippedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestResult;
use PHPUnit\Util\PHP\AbstractPhpProcess;
@@ -175,7 +177,16 @@ public function run(TestResult $result = null): TestResult
if ($xfail !== false) {
$failure = new IncompleteTestError($xfail, 0, $e);
+ } else {
+ if ($e instanceof ExpectationFailedException && $e->getComparisonFailure()) {
+ /** @var ExpectationFailedException $e */
+ $hint = $this->getLocationHintFromDiff($e->getComparisonFailure()->getDiff(), $sections);
+ $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
+ \array_unshift($trace, $hint);
+ $failure = new PHPTAssertionFailedError($e->getMessage(), 0, $trace[0]['file'], $trace[0]['line'], $trace);
+ }
}
+
$result->addFailure($this, $failure, $time);
} catch (Throwable $t) {
$result->addError($this, $t, $time);
@@ -328,7 +339,10 @@ private function runSkip(array &$sections, TestResult $result, array $settings):
$message = \substr($skipMatch[1], 2);
}
- $result->addFailure($this, new SkippedTestError($message), 0);
+ $hint = $this->getLocationHint($message, $sections, 'SKIPIF');
+ $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
+ \array_unshift($trace, $hint);
+ $result->addFailure($this, new SyntheticSkippedError($message, 0, $trace[0]['file'], $trace[0]['line'], $trace), 0);
$result->endTest($this, 0);
return true;
@@ -374,10 +388,15 @@ private function parse(): array
'PHPDBG',
];
+ $lineNr = 0;
+
foreach (\file($this->filename) as $line) {
+ $lineNr++;
+
if (\preg_match('/^--([_A-Z]+)--/', $line, $result)) {
- $section = $result[1];
- $sections[$section] = '';
+ $section = $result[1];
+ $sections[$section] = '';
+ $sections[$section . '_offset'] = $lineNr;
continue;
}
@@ -440,8 +459,6 @@ private function parseExternal(array &$sections): void
}
$sections[$section] = \file_get_contents($testDirectory . $externalFilename);
-
- unset($sections[$section . '_EXTERNAL']);
}
}
}
@@ -587,4 +604,82 @@ private function stringifyIni(array $ini): array
return $settings;
}
+
+ private function getLocationHintFromDiff(string $message, array $sections): array
+ {
+ $needle = '';
+
+ if (\preg_match("/--- Expected[^']+^-'([^']*)'/m", $message, $matches)) {
+ $needle = $matches[1];
+ }
+
+ return $this->getLocationHint($needle, $sections);
+ }
+
+ private function getLocationHint(string $needle, array $sections, ?string $sectionName = null): array
+ {
+ $needle = \trim($needle);
+
+ if (empty($needle)) {
+ return [
+ 'file' => \realpath($this->filename),
+ 'line' => 0,
+ ];
+ }
+
+ if ($sectionName) {
+ $search = [$sectionName];
+ } else {
+ $search = [
+ // 'FILE',
+ 'EXPECT',
+ 'EXPECTF',
+ 'EXPECTREGEX',
+ ];
+ }
+
+ foreach ($search as $section) {
+ if (!isset($sections[$section])) {
+ continue;
+ }
+
+ if (isset($sections[$section . '_EXTERNAL'])) {
+ $file = \trim($sections[$section . '_EXTERNAL']);
+
+ return [
+ 'file' => \realpath(\dirname($this->filename) . \DIRECTORY_SEPARATOR . $file),
+ 'line' => 1,
+ ];
+ }
+
+ $sectionOffset = $sections[$section . '_offset'] ?? 0;
+ $offset = $sectionOffset + 1;
+
+ $lines = \preg_split('/\r\n|\r|\n/', $sections[$section]);
+
+ foreach ($lines as $line) {
+ if (\strpos($line, $needle) !== false) {
+ return [
+ 'file' => \realpath($this->filename),
+ 'line' => $offset,
+ ];
+ }
+ $offset++;
+ }
+ }
+
+ if ($sectionName) {
+ // String not found in specified section, show user the start of the named section
+ return [
+ 'file' => \realpath($this->filename),
+ 'line' => $sectionOffset,
+ ];
+ }
+
+ // No section specified, show user start of code
+ return [
+ 'file' => \realpath($this->filename),
+ 'line' => 0,
+ ];
+ }
}
diff --git a/src/Util/Color.php b/src/Util/Color.php
index dc88e4019c1..83e368385d7 100644
--- a/src/Util/Color.php
+++ b/src/Util/Color.php
@@ -93,8 +93,7 @@ public static function colorizePath(string $path, ?string $prevPath = null, bool
$last = \count($path) - 1;
$path[$last] = \preg_replace_callback(
'/([\-_\.]+|phpt$)/',
- function ($matches)
- {
+ function ($matches) {
return self::dim($matches[0]);
},
$path[$last]
diff --git a/src/Util/Test.php b/src/Util/Test.php
index 7ff953629ec..c233af86604 100644
--- a/src/Util/Test.php
+++ b/src/Util/Test.php
@@ -160,71 +160,84 @@ public static function getLinesToBeUsed(string $className, string $methodName):
public static function getRequirements(string $className, string $methodName): array
{
$reflector = new ReflectionClass($className);
- $docComment = $reflector->getDocComment();
+ $requires = [
+ '__OFFSET' => [
+ '__FILE' => \realpath($reflector->getFileName()),
+ ],
+ ];
+ $requires = self::parseRequirements($reflector->getDocComment(), $reflector->getStartLine(), $requires);
+
$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];
+ return self::parseRequirements($reflector->getDocComment(), $reflector->getStartLine(), $requires);
+ }
+
+ public static function parseRequirements(string $docComment, int $offset = 0, array $requires = []): array
+ {
+ // Split docblock into lines and rewind offset to start of docblock
+ $lines = \preg_split('/\r\n|\r|\n/', $docComment);
+ $offset -= \count($lines);
+
+ foreach ($lines as $line) {
+ if (\preg_match(self::REGEX_REQUIRES_OS, $line, $matches)) {
+ $requires[$matches['name']] = $matches['value'];
+ $requires['__OFFSET'][$matches['name']] = $offset;
}
- }
- 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 (\preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) {
+ $requires[$matches['name']] = [
+ 'version' => $matches['version'],
+ 'operator' => $matches['operator'],
];
+ $requires['__OFFSET'][$matches['name']] = $offset;
}
- }
- 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]])) {
+ if (\preg_match(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $line, $matches)) {
+ if (!empty($requires[$matches['name']])) {
+ $offset++;
+
continue;
}
try {
$versionConstraintParser = new VersionConstraintParser;
- $requires[$matches['name'][$i] . '_constraint'] = [
- 'constraint' => $versionConstraintParser->parse(\trim($matches['constraint'][$i])),
+ $requires[$matches['name'] . '_constraint'] = [
+ 'constraint' => $versionConstraintParser->parse(\trim($matches['constraint'])),
];
+ $requires['__OFFSET'][$matches['name'] . '_constraint'] = $offset;
} catch (\PharIo\Version\Exception $e) {
throw new Warning($e->getMessage(), $e->getCode(), $e);
}
}
- }
- if ($count = \preg_match_all(self::REGEX_REQUIRES_SETTING, $docComment, $matches)) {
- $requires['setting'] = [];
-
- foreach (\range(0, $count - 1) as $i) {
- $requires['setting'][$matches['setting'][$i]] = $matches['value'][$i];
+ if (\preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) {
+ if (!isset($requires['setting'])) {
+ $requires['setting'] = [];
+ }
+ $requires['setting'][$matches['setting']] = $matches['value'];
+ $requires['__OFFSET']['__SETTING_' . $matches['setting']] = $offset;
}
- }
- if ($count = \preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
- foreach (\range(0, $count - 1) as $i) {
- $name = $matches['name'][$i] . 's';
+ if (\preg_match(self::REGEX_REQUIRES, $line, $matches)) {
+ $name = $matches['name'] . 's';
if (!isset($requires[$name])) {
$requires[$name] = [];
}
- $requires[$name][] = $matches['value'][$i];
+ $requires[$name][] = $matches['value'];
+ $requires['__OFFSET'][$matches['name'] . '_' . $matches['value']] = $offset;
- if ($name !== 'extensions' || empty($matches['version'][$i])) {
- continue;
+ if ($name === 'extensions' && !empty($matches['version'])) {
+ $requires['extension_versions'][$matches['value']] = [
+ 'version' => $matches['version'],
+ 'operator' => $matches['operator'],
+ ];
}
-
- $requires['extension_versions'][$matches['value'][$i]] = [
- 'version' => $matches['version'][$i],
- 'operator' => $matches['operator'][$i],
- ];
}
+
+ $offset++;
}
return $requires;
@@ -241,12 +254,14 @@ public static function getMissingRequirements(string $className, string $methodN
{
$required = static::getRequirements($className, $methodName);
$missing = [];
+ $hint = null;
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']);
+ $hint = $hint ?? 'PHP';
}
} elseif (!empty($required['PHP_constraint'])) {
$version = new \PharIo\Version\Version(self::sanitizeVersionNumber(\PHP_VERSION));
@@ -256,6 +271,7 @@ public static function getMissingRequirements(string $className, string $methodN
'PHP version does not match the required constraint %s.',
$required['PHP_constraint']['constraint']->asString()
);
+ $hint = $hint ?? 'PHP_constraint';
}
}
@@ -266,6 +282,7 @@ public static function getMissingRequirements(string $className, string $methodN
if (!\version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) {
$missing[] = \sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']);
+ $hint = $hint ?? 'PHPUnit';
}
} elseif (!empty($required['PHPUnit_constraint'])) {
$phpunitVersion = new \PharIo\Version\Version(self::sanitizeVersionNumber(Version::id()));
@@ -275,11 +292,13 @@ public static function getMissingRequirements(string $className, string $methodN
'PHPUnit version does not match the required constraint %s.',
$required['PHPUnit_constraint']['constraint']->asString()
);
+ $hint = $hint ?? 'PHPUnit_constraint';
}
}
if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem)->getFamily()) {
$missing[] = \sprintf('Operating system %s is required.', $required['OSFAMILY']);
+ $hint = $hint ?? 'OSFAMILY';
}
if (!empty($required['OS'])) {
@@ -287,6 +306,7 @@ public static function getMissingRequirements(string $className, string $methodN
if (!\preg_match($requiredOsPattern, \PHP_OS)) {
$missing[] = \sprintf('Operating system matching %s is required.', $requiredOsPattern);
+ $hint = $hint ?? 'OS';
}
}
@@ -303,6 +323,7 @@ public static function getMissingRequirements(string $className, string $methodN
}
$missing[] = \sprintf('Function %s is required.', $function);
+ $hint = $hint ?? 'function_' . $function;
}
}
@@ -310,6 +331,7 @@ public static function getMissingRequirements(string $className, string $methodN
foreach ($required['setting'] as $setting => $value) {
if (\ini_get($setting) != $value) {
$missing[] = \sprintf('Setting "%s" must be "%s".', $setting, $value);
+ $hint = $hint ?? '__SETTING_' . $setting;
}
}
}
@@ -322,22 +344,29 @@ public static function getMissingRequirements(string $className, string $methodN
if (!\extension_loaded($extension)) {
$missing[] = \sprintf('Extension %s is required.', $extension);
+ $hint = $hint ?? 'extension_' . $extension;
}
}
}
if (!empty($required['extension_versions'])) {
- foreach ($required['extension_versions'] as $extension => $required) {
+ foreach ($required['extension_versions'] as $extension => $req) {
$actualVersion = \phpversion($extension);
- $operator = empty($required['operator']) ? '>=' : $required['operator'];
+ $operator = empty($req['operator']) ? '>=' : $req['operator'];
- if ($actualVersion === false || !\version_compare($actualVersion, $required['version'], $operator)) {
- $missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']);
+ if ($actualVersion === false || !\version_compare($actualVersion, $req['version'], $operator)) {
+ $missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator, $req['version']);
+ $hint = $hint ?? 'extension_' . $extension;
}
}
}
+ if ($hint && isset($required['__OFFSET'])) {
+ \array_unshift($missing, '__OFFSET_FILE=' . $required['__OFFSET']['__FILE']);
+ \array_unshift($missing, '__OFFSET_LINE=' . $required['__OFFSET'][$hint] ?? 1);
+ }
+
return $missing;
}
diff --git a/src/Util/TestDox/CliTestDoxPrinter.php b/src/Util/TestDox/CliTestDoxPrinter.php
index cba54800ede..b11f5f676d6 100644
--- a/src/Util/TestDox/CliTestDoxPrinter.php
+++ b/src/Util/TestDox/CliTestDoxPrinter.php
@@ -177,9 +177,9 @@ protected function colorizeMessageAndDiff(string $style, string $message): strin
$throwable[] = $line;
} else {
if (\substr($line, 0, 1) === '-') {
- $line = Color::colorize('fg-red', $line);
+ $line = Color::colorize('fg-red', Color::visualizeWhitespace($line, true));
} elseif (\substr($line, 0, 1) === '+') {
- $line = Color::colorize('fg-green', $line);
+ $line = Color::colorize('fg-green', Color::visualizeWhitespace($line, true));
} elseif ($line === '@@ @@') {
$line = Color::colorize('fg-cyan', $line);
}
@@ -259,7 +259,7 @@ private function formatRuntime(float $time, string $color = ''): string
$color = 'fg-magenta';
}
- return Color::colorize($color, \sprintf(' %.2f ms', $time * 1000));
+ return Color::colorize($color, \sprintf(' %d ms', \ceil($time * 1000)));
}
private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests): void
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 00000000000..ef8a96feab7
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,32 @@
+# PHPUnit self-tests
+
+This document contains the notes of projects contributors about testing the PHPUnit core and its integration with the most important dependencies.
+
+## Quick start
+
+There are two main ways to self-test PHPUnit. The first is to test _everything_ using the default configuration. Here's how to do that with pretty colors in a human-readable format:
+
+```
+cd /path/to/phpunit
+./phpunit --testdox --colors=always --verbose
+```
+
+If you want to do a very quick check health-check of most basic use cases you can use the `basic` test collection:
+
+```
+./phpunit --testdox --colors=always --verbose -c tests/basic/configuration.basic.xml
+```
+
+The `basic` suite of tests puts the core system through its paces and covers most of the basic use cases of PHPUnit including happy flows and common exceptions.
+
+## Structure of the self-test collection
+
+Note: this section will change often while `tests/` is being refactored.
+
+- `configuration.xml`: the global configuration file which defines the internal `unit` and `end-to-end` tests suites
+- `tests/`
+ - `_files`: specialized helper files; input/output samples
+ - `basic/`: fast tests covering all basics
+ - `configuration.basic.xml`: configuration file tailored for the `basic` suite
+ - `end-to-end/`: run PHPUnit as a seperate process and observe its behaviour via console messages and the filesystem
+ - `unit/`: unit tests for individual smallest components and integration tests of common usa cases
diff --git a/tests/_files/RequirementsTest.php b/tests/_files/RequirementsTest.php
index cdff0db56f4..d3615595c24 100644
--- a/tests/_files/RequirementsTest.php
+++ b/tests/_files/RequirementsTest.php
@@ -183,6 +183,7 @@ public function testSpecificExtensionVersion(): void
}
/**
+ * @testdox PHP version operator less than
* @requires PHP < 5.4
*/
public function testPHPVersionOperatorLessThan(): void
@@ -190,6 +191,7 @@ public function testPHPVersionOperatorLessThan(): void
}
/**
+ * @testdox PHP version operator less than or equals
* @requires PHP <= 5.4
*/
public function testPHPVersionOperatorLessThanEquals(): void
@@ -197,6 +199,7 @@ public function testPHPVersionOperatorLessThanEquals(): void
}
/**
+ * @testdox PHP version operator greater than
* @requires PHP > 99
*/
public function testPHPVersionOperatorGreaterThan(): void
@@ -204,6 +207,7 @@ public function testPHPVersionOperatorGreaterThan(): void
}
/**
+ * @testdox PHP version operator greater than or equals
* @requires PHP >= 99
*/
public function testPHPVersionOperatorGreaterThanEquals(): void
@@ -211,6 +215,7 @@ public function testPHPVersionOperatorGreaterThanEquals(): void
}
/**
+ * @testdox PHP version operator equals
* @requires PHP = 5.4
*/
public function testPHPVersionOperatorEquals(): void
@@ -218,6 +223,7 @@ public function testPHPVersionOperatorEquals(): void
}
/**
+ * @testdox PHP version operator double equals
* @requires PHP == 5.4
*/
public function testPHPVersionOperatorDoubleEquals(): void
@@ -225,6 +231,7 @@ public function testPHPVersionOperatorDoubleEquals(): void
}
/**
+ * @testdox PHP version operator bang equals
* @requires PHP != 99
*/
public function testPHPVersionOperatorBangEquals(): void
@@ -232,6 +239,7 @@ public function testPHPVersionOperatorBangEquals(): void
}
/**
+ * @testdox PHP version operator not equals
* @requires PHP <> 99
*/
public function testPHPVersionOperatorNotEquals(): void
@@ -239,6 +247,7 @@ public function testPHPVersionOperatorNotEquals(): void
}
/**
+ * @testdox PHP version operator no space
* @requires PHP >=99
*/
public function testPHPVersionOperatorNoSpace(): void
@@ -246,6 +255,7 @@ public function testPHPVersionOperatorNoSpace(): void
}
/**
+ * @testdox PHPUnit version operator less than
* @requires PHPUnit < 1.0
*/
public function testPHPUnitVersionOperatorLessThan(): void
@@ -253,6 +263,7 @@ public function testPHPUnitVersionOperatorLessThan(): void
}
/**
+ * @testdox PHPUnit version operator less than equals
* @requires PHPUnit <= 1.0
*/
public function testPHPUnitVersionOperatorLessThanEquals(): void
@@ -260,6 +271,7 @@ public function testPHPUnitVersionOperatorLessThanEquals(): void
}
/**
+ * @testdox PHPUnit version operator greater than
* @requires PHPUnit > 99
*/
public function testPHPUnitVersionOperatorGreaterThan(): void
@@ -267,6 +279,7 @@ public function testPHPUnitVersionOperatorGreaterThan(): void
}
/**
+ * @testdox PHPUnit version operator greater than or equals
* @requires PHPUnit >= 99
*/
public function testPHPUnitVersionOperatorGreaterThanEquals(): void
@@ -274,6 +287,7 @@ public function testPHPUnitVersionOperatorGreaterThanEquals(): void
}
/**
+ * @testdox PHPUnit version operator equals
* @requires PHPUnit = 1.0
*/
public function testPHPUnitVersionOperatorEquals(): void
@@ -281,6 +295,7 @@ public function testPHPUnitVersionOperatorEquals(): void
}
/**
+ * @testdox PHPUnit version operator double equals
* @requires PHPUnit == 1.0
*/
public function testPHPUnitVersionOperatorDoubleEquals(): void
@@ -288,6 +303,7 @@ public function testPHPUnitVersionOperatorDoubleEquals(): void
}
/**
+ * @testdox PHPUnit version operator bang equals
* @requires PHPUnit != 99
*/
public function testPHPUnitVersionOperatorBangEquals(): void
@@ -295,6 +311,7 @@ public function testPHPUnitVersionOperatorBangEquals(): void
}
/**
+ * @testdox PHPUnit version operator not equals
* @requires PHPUnit <> 99
*/
public function testPHPUnitVersionOperatorNotEquals(): void
@@ -302,6 +319,7 @@ public function testPHPUnitVersionOperatorNotEquals(): void
}
/**
+ * @testdox PHPUnit version operator no space
* @requires PHPUnit >=99
*/
public function testPHPUnitVersionOperatorNoSpace(): void
diff --git a/tests/basic/README.md b/tests/basic/README.md
new file mode 100644
index 00000000000..e41cf58faa1
--- /dev/null
+++ b/tests/basic/README.md
@@ -0,0 +1,16 @@
+# PHPUnit Basic Training
+
+The `basic` test suite is designed to put PHPUnit through its paces:
+- basic test runner functionality
+ - configuration options
+ - test selection, grouping
+ - execution reordering and dependecy resolution are noticeable
+- all test result statuses
+- common fixture errors and exceptions
+- common dataprovider errors and exceptions
+- out-of-sequence test events are processed correctly
+- events from isolated sandboxes are reported to the user
+- execution order and reporting order are decoupled
+
+## References
+- https://github.com/sebastianbergmann/phpunit/issues/3453
diff --git a/tests/basic/configuration.basic.xml b/tests/basic/configuration.basic.xml
new file mode 100644
index 00000000000..b366eae49bf
--- /dev/null
+++ b/tests/basic/configuration.basic.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ unit
+ integration
+
+
+
+
+
+
+
diff --git a/tests/basic/unit/StatusTest.php b/tests/basic/unit/StatusTest.php
new file mode 100644
index 00000000000..036ea1f93c4
--- /dev/null
+++ b/tests/basic/unit/StatusTest.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace vendor\project;
+
+use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\Warning;
+
+/**
+ * @testdox Test result status with and without message
+ */
+class StatusTest extends TestCase
+{
+ public function testSuccess(): void
+ {
+ $this->assertTrue(true);
+ }
+
+ public function testFailure(): void
+ {
+ $this->assertTrue(false);
+ }
+
+ public function testError(): void
+ {
+ throw new \RuntimeException;
+ }
+
+ public function testIncomplete(): void
+ {
+ $this->markTestIncomplete();
+ }
+
+ public function testSkipped(): void
+ {
+ $this->markTestSkipped();
+ }
+
+ public function testRisky(): void
+ {
+ }
+
+ public function testWarning(): void
+ {
+ throw new Warning;
+ }
+
+ public function testSuccessWithMessage(): void
+ {
+ $this->assertTrue(true, '"success with custom message"');
+ }
+
+ public function testFailureWithMessage(): void
+ {
+ $this->assertTrue(false, 'failure with custom message');
+ }
+
+ public function testErrorWithMessage(): void
+ {
+ throw new \RuntimeException('error with custom message');
+ }
+
+ public function testIncompleteWithMessage(): void
+ {
+ $this->markTestIncomplete('incomplete with custom message');
+ }
+
+ public function testSkippedWithMessage(): void
+ {
+ $this->markTestSkipped('skipped with custom message');
+ }
+
+ public function testRiskyWithMessage(): void
+ {
+ // Custom messages not implemented for risky status
+ }
+
+ public function testWarningWithMessage(): void
+ {
+ throw new Warning('warning with custom message');
+ }
+}
diff --git a/tests/end-to-end/_files/expect_external.txt b/tests/end-to-end/_files/expect_external.txt
index 5e1c309dae7..4e8779e82dc 100644
--- a/tests/end-to-end/_files/expect_external.txt
+++ b/tests/end-to-end/_files/expect_external.txt
@@ -1 +1,3 @@
-Hello World
\ No newline at end of file
+Hello World
+This is line two
+and this is line three
diff --git a/tests/end-to-end/_files/phpt-expect-external-location-hint-example.phpt b/tests/end-to-end/_files/phpt-expect-external-location-hint-example.phpt
new file mode 100644
index 00000000000..8475cb4de14
--- /dev/null
+++ b/tests/end-to-end/_files/phpt-expect-external-location-hint-example.phpt
@@ -0,0 +1,10 @@
+--TEST--
+PHPT skip condition results in correct code location hint
+--FILE--
+
+--EXPECT_EXTERNAL--
+../_files/expect_external.txt
diff --git a/tests/end-to-end/_files/phpt-expect-location-hint-example.phpt b/tests/end-to-end/_files/phpt-expect-location-hint-example.phpt
new file mode 100644
index 00000000000..87f49c75826
--- /dev/null
+++ b/tests/end-to-end/_files/phpt-expect-location-hint-example.phpt
@@ -0,0 +1,10 @@
+--TEST--
+PHPT skip condition results in correct code location hint
+--FILE--
+
+--EXPECT--
+Nothing to see here, move along
diff --git a/tests/end-to-end/_files/phpt-skipif-location-hint-example.phpt b/tests/end-to-end/_files/phpt-skipif-location-hint-example.phpt
new file mode 100644
index 00000000000..1437dcc6609
--- /dev/null
+++ b/tests/end-to-end/_files/phpt-skipif-location-hint-example.phpt
@@ -0,0 +1,12 @@
+--TEST--
+PHPT skip condition results in correct code location hint
+--FILE--
+
+--SKIPIF--
+
+--EXPECT--
+Nothing to see here, move along
diff --git a/tests/end-to-end/_files/phpt_external.php b/tests/end-to-end/_files/phpt_external.php
index f0bab63409f..3219572a232 100644
--- a/tests/end-to-end/_files/phpt_external.php
+++ b/tests/end-to-end/_files/phpt_external.php
@@ -7,4 +7,6 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
-print 'Hello World';
+print 'Hello World' . \PHP_EOL;
+print 'This is line two' . \PHP_EOL;
+print 'and this is line three' . \PHP_EOL;
diff --git a/tests/end-to-end/loggers/_files/StatusTest.php b/tests/end-to-end/loggers/_files/StatusTest.php
deleted file mode 100644
index 8f891dffc1d..00000000000
--- a/tests/end-to-end/loggers/_files/StatusTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-namespace vendor\project;
-
-use PHPUnit\Framework\TestCase;
-use PHPUnit\Framework\Warning;
-
-class StatusTest extends TestCase
-{
- public function testSuccess(): void
- {
- $this->assertTrue(true);
- }
-
- public function testFailure(): void
- {
- $this->assertTrue(false);
- }
-
- public function testError(): void
- {
- throw new \RuntimeException;
- }
-
- public function testIncomplete(): void
- {
- $this->markTestIncomplete();
- }
-
- public function testSkipped(): void
- {
- $this->markTestSkipped();
- }
-
- public function testRisky(): void
- {
- }
-
- public function testWarning(): void
- {
- throw new Warning;
- }
-}
diff --git a/tests/end-to-end/loggers/_files/raw_output_StatusTest.txt b/tests/end-to-end/loggers/_files/raw_output_StatusTest.txt
index 42ace20c301..4af93ec882e 100644
--- a/tests/end-to-end/loggers/_files/raw_output_StatusTest.txt
+++ b/tests/end-to-end/loggers/_files/raw_output_StatusTest.txt
@@ -1,48 +1,94 @@
PHPUnit %s Sebastian Bergmann and contributors.
Runtime: %s
+Configuration: %stests%ebasic%econfiguration.basic.xml
-[4mvendor\project\Status[0m
+[4mTest result status with and without message[0m
[32m✔[0m Success [32m %f ms[0m
[31m✘[0m Failure [31m %f ms[0m
[31m│[0m
[31m│[0m [41;37mFailed asserting that false is true.[0m
[31m│[0m
- [31m│[0m %stests[2m%e[22mend-to-end[2m%e[22mloggers[2m%e[22m_files[2m%e[22mStatusTest.php[2m:[22m[34m24[0m
+ [31m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
[31m│[0m
[33m✘[0m Error [33m %f ms[0m
[33m│[0m
[33m│[0m [43;30mRuntimeException:[0m
[33m│[0m
- [33m│[0m %stests[2m%e[22mend-to-end[2m%e[22mloggers[2m%e[22m_files[2m%e[22mStatusTest.php[2m:[22m[34m29[0m
+ [33m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
[33m│[0m
[33m∅[0m Incomplete [33m %f ms[0m
[33m│[0m
- [33m│[0m %stests[2m%e[22mend-to-end[2m%e[22mloggers[2m%e[22m_files[2m%e[22mStatusTest.php[2m:[22m[34m34[0m
+ [33m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
[33m│[0m
[36m↩[0m Skipped [36m %f ms[0m
[36m│[0m
- [36m│[0m %stests[2m%e[22mend-to-end[2m%e[22mloggers[2m%e[22m_files[2m%e[22mStatusTest.php[2m:[22m[34m39[0m
+ [36m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
[36m│[0m
[33m☢[0m Risky [33m %f ms[0m
[33m│[0m
[33m│[0m [33mThis test did not perform any assertions%w[0m
[33m│[0m%w
- [33m│[0m [33m%stests%eend-to-end%eloggers%e_files%eStatusTest.php:42[0m%w
+ [33m│[0m [33m%stests%ebasic%eunit%eStatusTest.php:%d[0m%w
[33m│[0m%w
[33m│[0m
[33m⚠[0m Warning [33m %f ms[0m
[33m│[0m
- [33m│[0m %stests[2m%e[22mend-to-end[2m%e[22mloggers[2m%e[22m_files[2m%e[22mStatusTest.php[2m:[22m[34m48[0m
+ [33m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
+ [33m│[0m
+
+ [32m✔[0m Success with message [32m %f ms[0m
+ [31m✘[0m Failure with message [31m %f ms[0m
+ [31m│[0m
+ [31m│[0m [41;37mfailure with custom message%w[0m
+ [31m│[0m [41;37mFailed asserting that false is true.[0m
+ [31m│[0m
+ [31m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
+ [31m│[0m
+
+ [33m✘[0m Error with message [33m %f ms[0m
+ [33m│[0m
+ [33m│[0m [43;30mRuntimeException: error with custom message[0m
+ [33m│[0m
+ [33m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
+ [33m│[0m
+
+ [33m∅[0m Incomplete with message [33m %f ms[0m
+ [33m│[0m
+ [33m│[0m [33mincomplete with custom message[0m
+ [33m│[0m
+ [33m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
+ [33m│[0m
+
+ [36m↩[0m Skipped with message [36m %f ms[0m
+ [36m│[0m
+ [36m│[0m [36mskipped with custom message[0m
+ [36m│[0m
+ [36m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
+ [36m│[0m
+
+ [33m☢[0m Risky with message [33m %f ms[0m
+ [33m│[0m
+ [33m│[0m [33mThis test did not perform any assertions%w[0m
+ [33m│[0m%w
+ [33m│[0m [33m%stests%ebasic%eunit%eStatusTest.php:%d[0m%w
+ [33m│[0m%w
+ [33m│[0m
+
+ [33m⚠[0m Warning with message [33m %f ms[0m
+ [33m│[0m
+ [33m│[0m [33mwarning with custom message[0m
+ [33m│[0m
+ [33m│[0m %stests[2m%e[22mbasic[2m%e[22munit[2m%e[22mStatusTest.php[2m:[22m[34m%d[0m
[33m│[0m
Time: %s, Memory: %s
[37;41mERRORS![0m
-[37;41mTests: 7[0m[37;41m, Assertions: 2[0m[37;41m, Errors: 1[0m[37;41m, Failures: 1[0m[37;41m, Warnings: 1[0m[37;41m, Skipped: 1[0m[37;41m, Incomplete: 1[0m[37;41m, Risky: 1[0m[37;41m.[0m
+[37;41mTests: 14[0m[37;41m, Assertions: 4[0m[37;41m, Errors: 2[0m[37;41m, Failures: 2[0m[37;41m, Warnings: 2[0m[37;41m, Skipped: 2[0m[37;41m, Incomplete: 2[0m[37;41m, Risky: 2[0m[37;41m.[0m
diff --git a/tests/end-to-end/loggers/debug.phpt b/tests/end-to-end/loggers/debug.phpt
index 92c92ca9a0c..ab20977edb7 100644
--- a/tests/end-to-end/loggers/debug.phpt
+++ b/tests/end-to-end/loggers/debug.phpt
@@ -1,28 +1,105 @@
--TEST--
-phpunit --debug BankAccountTest ../../_files/BankAccountTest.php
+phpunit --debug -c tests/basic/configuration.basic.xml
--FILE--
+.FEISRW.FEISRW 14 / 14 (100%)
-
-
-
+
+
+
vendor\project\StatusTest::testFailure
Failed asserting that false is true.
%s%eStatusTest.php:%d
-
+
vendor\project\StatusTest::testError
RuntimeException:%w
%s%eStatusTest.php:%d
-
+
-
+
-
+
Risky Test
-
+
vendor\project\StatusTest::testWarning
+%s%eStatusTest.php:%d
+
+
+
+
+ vendor\project\StatusTest::testFailureWithMessage
+failure with custom message
+Failed asserting that false is true.
+
+%s%eStatusTest.php:%d
+
+
+
+ vendor\project\StatusTest::testErrorWithMessage
+RuntimeException: error with custom message
+
+%s%eStatusTest.php:%d
+
+
+
+
+
+
+
+
+
+ Risky Test
+
+
+
+ vendor\project\StatusTest::testWarningWithMessage
+warning with custom message
+
%s%eStatusTest.php:%d
@@ -57,38 +90,59 @@ RuntimeException:%w
Time: %s, Memory: %s
-There was 1 error:
+There were 2 errors:
1) vendor\project\StatusTest::testError
RuntimeException:%w
%s%eStatusTest.php:%d
+2) vendor\project\StatusTest::testErrorWithMessage
+RuntimeException: error with custom message
+
+%s%eStatusTest.php:%d
+
--
-There was 1 warning:
+There were 2 warnings:
1) vendor\project\StatusTest::testWarning
%s%eStatusTest.php:%d
+2) vendor\project\StatusTest::testWarningWithMessage
+warning with custom message
+
+%s%eStatusTest.php:%d
+
--
-There was 1 failure:
+There were 2 failures:
1) vendor\project\StatusTest::testFailure
Failed asserting that false is true.
%s%eStatusTest.php:%d
+2) vendor\project\StatusTest::testFailureWithMessage
+failure with custom message
+Failed asserting that false is true.
+
+%s%eStatusTest.php:%d
+
--
-There was 1 risky test:
+There were 2 risky tests:
1) vendor\project\StatusTest::testRisky
This test did not perform any assertions
-%s:42
+%s%eStatusTest.php:%d
+
+2) vendor\project\StatusTest::testRiskyWithMessage
+This test did not perform any assertions
+
+%s%eStatusTest.php:%d
ERRORS!
-Tests: 7, Assertions: 2, Errors: 1, Failures: 1, Warnings: 1, Skipped: 1, Incomplete: 1, Risky: 1.
+Tests: 14, Assertions: 4, Errors: 2, Failures: 2, Warnings: 2, Skipped: 2, Incomplete: 2, Risky: 2.
diff --git a/tests/end-to-end/loggers/testdox-status-colorization.phpt b/tests/end-to-end/loggers/testdox-colors-verbose.phpt
similarity index 68%
rename from tests/end-to-end/loggers/testdox-status-colorization.phpt
rename to tests/end-to-end/loggers/testdox-colors-verbose.phpt
index 208d839bda1..9c645f02cb7 100644
--- a/tests/end-to-end/loggers/testdox-status-colorization.phpt
+++ b/tests/end-to-end/loggers/testdox-colors-verbose.phpt
@@ -1,9 +1,10 @@
--TEST--
-phpunit --testdox --colors=always --verbose RouterTest ../_files/StatusTest.php
+phpunit --testdox --colors=always --verbose -c tests/basic/configuration.basic.xml
--FILE--
+.FEISRW.FEISRW 14 / 14 (100%)
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Time: %s, Memory: %s
-There was 1 error:
+There were 2 errors:
1) vendor\project\StatusTest::testError
-RuntimeException:%s
+RuntimeException:%w
+
+%s%eStatusTest.php:%d
+
+2) vendor\project\StatusTest::testErrorWithMessage
+RuntimeException: error with custom message
%s%eStatusTest.php:%d
--
-There was 1 warning:
+There were 2 warnings:
1) vendor\project\StatusTest::testWarning
%s%eStatusTest.php:%d
+2) vendor\project\StatusTest::testWarningWithMessage
+warning with custom message
+
+%s%eStatusTest.php:%d
+
--
-There was 1 failure:
+There were 2 failures:
1) vendor\project\StatusTest::testFailure
Failed asserting that false is true.
%s%eStatusTest.php:%d
+2) vendor\project\StatusTest::testFailureWithMessage
+failure with custom message
+Failed asserting that false is true.
+
+%s%eStatusTest.php:%d
+
--
-There was 1 risky test:
+There were 2 risky tests:
1) vendor\project\StatusTest::testRisky
This test did not perform any assertions
-%s:42
+%s%eStatusTest.php:%d
+
+2) vendor\project\StatusTest::testRiskyWithMessage
+This test did not perform any assertions
+
+%s%eStatusTest.php:%d
ERRORS!
-Tests: 7, Assertions: 2, Errors: 1, Failures: 1, Warnings: 1, Skipped: 1, Incomplete: 1, Risky: 1.
+Tests: 14, Assertions: 4, Errors: 2, Failures: 2, Warnings: 2, Skipped: 2, Incomplete: 2, Risky: 2.
diff --git a/tests/end-to-end/loggers/testdox.phpt b/tests/end-to-end/loggers/testdox.phpt
index 4198fc49ac4..652e9ba3b90 100644
--- a/tests/end-to-end/loggers/testdox.phpt
+++ b/tests/end-to-end/loggers/testdox.phpt
@@ -1,9 +1,10 @@
--TEST--
-phpunit --testdox php://stdout BankAccountTest ../../_files/BankAccountTest.php
+phpunit --testdox -c tests/basic/configuration.basic.xml
--FILE--
printer->endTest($this, 0.001);
$this->assertStringContainsString(Color::colorize('bg-red,fg-white', 'some message'), $this->printer->getBuffer());
- $this->assertStringContainsString(Color::colorize('fg-red', '--- Expected'), $this->printer->getBuffer());
- $this->assertStringContainsString(Color::colorize('fg-green', '+++ Actual'), $this->printer->getBuffer());
+ $this->assertStringContainsString(Color::colorize('fg-red', '---' . Color::dim('·') . 'Expected'), $this->printer->getBuffer());
+ $this->assertStringContainsString(Color::colorize('fg-green', '+++' . Color::dim('·') . 'Actual'), $this->printer->getBuffer());
$this->assertStringContainsString(Color::colorize('fg-cyan', '@@ @@'), $this->printer->getBuffer());
}
}
diff --git a/tests/unit/Util/TestTest.php b/tests/unit/Util/TestTest.php
index 40f220b1d0f..fd7ce355825 100644
--- a/tests/unit/Util/TestTest.php
+++ b/tests/unit/Util/TestTest.php
@@ -19,6 +19,12 @@
class TestTest extends TestCase
{
/**
+ * @var string
+ */
+ private $fileRequirementsTest;
+
+ /**
+ * @testdox Test::getRequirements() for $test
* @dataProvider requirementsProvider
*
* @throws Warning
@@ -36,33 +42,155 @@ public function testGetRequirements($test, $result): void
public function requirementsProvider(): array
{
return [
- ['testOne', []],
- ['testTwo', ['PHPUnit' => ['version' => '1.0', 'operator' => '']]],
- ['testThree', ['PHP' => ['version' => '2.0', 'operator' => '']]],
- ['testFour', [
- 'PHPUnit' => ['version' => '2.0', 'operator' => ''],
- 'PHP' => ['version' => '1.0', 'operator' => ''],
- ]],
- ['testFive', ['PHP' => ['version' => '5.4.0RC6', 'operator' => '']]],
- ['testSix', ['PHP' => ['version' => '5.4.0-alpha1', 'operator' => '']]],
- ['testSeven', ['PHP' => ['version' => '5.4.0beta2', 'operator' => '']]],
- ['testEight', ['PHP' => ['version' => '5.4-dev', 'operator' => '']]],
- ['testNine', ['functions' => ['testFunc']]],
- ['testTen', ['extensions' => ['testExt']]],
- ['testEleven', [
- 'OS' => 'SunOS',
- 'OSFAMILY' => 'Solaris',
- ]],
+ [
+ 'testOne',
+ ['__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ ]],
+ ],
+
+ [
+ 'testTwo',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 19,
+ ],
+ 'PHPUnit' => ['version' => '1.0', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testThree',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 26,
+ ],
+ 'PHP' => ['version' => '2.0', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testFour',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 33,
+ 'PHP' => 34,
+ ],
+ 'PHPUnit' => ['version' => '2.0', 'operator' => ''],
+ 'PHP' => ['version' => '1.0', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testFive',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 41,
+ ],
+ 'PHP' => ['version' => '5.4.0RC6', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testSix',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 48,
+ ],
+ 'PHP' => ['version' => '5.4.0-alpha1', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testSeven',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 55,
+ ],
+ 'PHP' => ['version' => '5.4.0beta2', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testEight',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 62,
+ ],
+ 'PHP' => ['version' => '5.4-dev', 'operator' => ''],
+ ],
+ ],
+
+ [
+ 'testNine',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'function_testFunc' => 69,
+ ],
+ 'functions' => ['testFunc'],
+ ],
+ ],
+
+ [
+ 'testTen',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExt' => 85,
+ ],
+ 'extensions' => ['testExt'],
+ ],
+ ],
+
+ [
+ 'testEleven',
+ [
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'OS' => 92,
+ 'OSFAMILY' => 93,
+ ],
+ 'OS' => 'SunOS',
+ 'OSFAMILY' => 'Solaris',
+ ],
+ ],
+
[
'testSpace',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_spl' => 171,
+ 'OS' => 172,
+ ],
'extensions' => ['spl'],
'OS' => '.*',
],
],
+
[
'testAllPossibleRequirements',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 100,
+ 'PHPUnit' => 101,
+ 'OS' => 102,
+ 'function_testFuncOne' => 103,
+ 'function_testFunc2' => 104,
+ 'extension_testExtOne' => 105,
+ 'extension_testExt2' => 106,
+ 'extension_testExtThree' => 107,
+ '__SETTING_not_a_setting' => 108,
+ ],
'PHP' => ['version' => '99-dev', 'operator' => ''],
'PHPUnit' => ['version' => '9-dev', 'operator' => ''],
'OS' => 'DOESNOTEXIST',
@@ -83,146 +211,255 @@ public function requirementsProvider(): array
],
],
],
+
['testSpecificExtensionVersion',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExt' => 179,
+ ],
'extension_versions' => ['testExt' => ['version' => '1.8.0', 'operator' => '']],
'extensions' => ['testExt'],
],
],
['testPHPVersionOperatorLessThan',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 187,
+ ],
'PHP' => ['version' => '5.4', 'operator' => '<'],
],
],
['testPHPVersionOperatorLessThanEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 195,
+ ],
'PHP' => ['version' => '5.4', 'operator' => '<='],
],
],
['testPHPVersionOperatorGreaterThan',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 203,
+ ],
'PHP' => ['version' => '99', 'operator' => '>'],
],
],
['testPHPVersionOperatorGreaterThanEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 211,
+ ],
'PHP' => ['version' => '99', 'operator' => '>='],
],
],
['testPHPVersionOperatorEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 219,
+ ],
'PHP' => ['version' => '5.4', 'operator' => '='],
],
],
['testPHPVersionOperatorDoubleEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 227,
+ ],
'PHP' => ['version' => '5.4', 'operator' => '=='],
],
],
['testPHPVersionOperatorBangEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 235,
+ ],
'PHP' => ['version' => '99', 'operator' => '!='],
],
],
['testPHPVersionOperatorNotEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 243,
+ ],
'PHP' => ['version' => '99', 'operator' => '<>'],
],
],
['testPHPVersionOperatorNoSpace',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHP' => 251,
+ ],
'PHP' => ['version' => '99', 'operator' => '>='],
],
],
['testPHPUnitVersionOperatorLessThan',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 259,
+ ],
'PHPUnit' => ['version' => '1.0', 'operator' => '<'],
],
],
['testPHPUnitVersionOperatorLessThanEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 267,
+ ],
'PHPUnit' => ['version' => '1.0', 'operator' => '<='],
],
],
['testPHPUnitVersionOperatorGreaterThan',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 275,
+ ],
'PHPUnit' => ['version' => '99', 'operator' => '>'],
],
],
['testPHPUnitVersionOperatorGreaterThanEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 283,
+ ],
'PHPUnit' => ['version' => '99', 'operator' => '>='],
],
],
['testPHPUnitVersionOperatorEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 291,
+ ],
'PHPUnit' => ['version' => '1.0', 'operator' => '='],
],
],
['testPHPUnitVersionOperatorDoubleEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 299,
+ ],
'PHPUnit' => ['version' => '1.0', 'operator' => '=='],
],
],
['testPHPUnitVersionOperatorBangEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 307,
+ ],
'PHPUnit' => ['version' => '99', 'operator' => '!='],
],
],
['testPHPUnitVersionOperatorNotEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 315,
+ ],
'PHPUnit' => ['version' => '99', 'operator' => '<>'],
],
],
['testPHPUnitVersionOperatorNoSpace',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'PHPUnit' => 323,
+ ],
'PHPUnit' => ['version' => '99', 'operator' => '>='],
],
],
['testExtensionVersionOperatorLessThanEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 337,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '1.0', 'operator' => '<=']],
],
],
['testExtensionVersionOperatorGreaterThan',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 344,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '99', 'operator' => '>']],
],
],
['testExtensionVersionOperatorGreaterThanEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 351,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '99', 'operator' => '>=']],
],
],
['testExtensionVersionOperatorEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 358,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '1.0', 'operator' => '=']],
],
],
['testExtensionVersionOperatorDoubleEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 365,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '1.0', 'operator' => '==']],
],
],
['testExtensionVersionOperatorBangEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 372,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '99', 'operator' => '!=']],
],
],
['testExtensionVersionOperatorNotEquals',
[
+ '__OFFSET' => [
+ '__FILE' => $this->getRequirementsTestClassFile(),
+ 'extension_testExtOne' => 379,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '99', 'operator' => '<>']],
],
],
['testExtensionVersionOperatorNoSpace',
[
+ '__OFFSET' => [
+ '__FILE' => $this->fileRequirementsTest,
+ 'extension_testExtOne' => 386,
+ ],
'extensions' => ['testExtOne'],
'extension_versions' => ['testExtOne' => ['version' => '99', 'operator' => '>=']],
],
@@ -231,6 +468,7 @@ public function requirementsProvider(): array
}
/**
+ * @testdox Test::getRequirements() with constraints for $test
* @dataProvider requirementsWithVersionConstraintsProvider
*
* @throws Exception
@@ -388,7 +626,20 @@ public function requirementsWithInvalidVersionConstraintsThrowsExceptionProvider
public function testGetRequirementsMergesClassAndMethodDocBlocks(): void
{
+ $reflector = new \ReflectionClass(\RequirementsClassDocBlockTest::class);
+ $file = $reflector->getFileName();
+
$expectedAnnotations = [
+ '__OFFSET' => [
+ '__FILE' => $file,
+ 'PHP' => 21,
+ 'PHPUnit' => 22,
+ 'OS' => 23,
+ 'function_testFuncClass' => 15,
+ 'extension_testExtClass' => 16,
+ 'function_testFuncMethod' => 24,
+ 'extension_testExtMethod' => 25,
+ ],
'PHP' => ['version' => '5.4', 'operator' => ''],
'PHPUnit' => ['version' => '3.7', 'operator' => ''],
'OS' => 'WINNT',
@@ -409,6 +660,7 @@ public function testGetRequirementsMergesClassAndMethodDocBlocks(): void
}
/**
+ * @testdox Test::getMissingRequirements() for $test
* @dataProvider missingRequirementsProvider
*
* @throws Warning
@@ -427,12 +679,34 @@ public function missingRequirementsProvider(): array
{
return [
['testOne', []],
- ['testNine', ['Function testFunc is required.']],
- ['testTen', ['Extension testExt is required.']],
- ['testAlwaysSkip', ['PHPUnit >= 1111111 is required.']],
- ['testAlwaysSkip2', ['PHP >= 9999999 is required.']],
- ['testAlwaysSkip3', ['Operating system matching /DOESNOTEXIST/i is required.']],
+ ['testNine', [
+ '__OFFSET_LINE=69',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Function testFunc is required.',
+ ]],
+ ['testTen', [
+ '__OFFSET_LINE=85',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExt is required.',
+ ]],
+ ['testAlwaysSkip', [
+ '__OFFSET_LINE=143',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit >= 1111111 is required.',
+ ]],
+ ['testAlwaysSkip2', [
+ '__OFFSET_LINE=150',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP >= 9999999 is required.',
+ ]],
+ ['testAlwaysSkip3', [
+ '__OFFSET_LINE=157',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Operating system matching /DOESNOTEXIST/i is required.',
+ ]],
['testAllPossibleRequirements', [
+ '__OFFSET_LINE=100',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
'PHP >= 99-dev is required.',
'PHPUnit >= 9-dev is required.',
'Operating system matching /DOESNOTEXIST/i is required.',
@@ -443,32 +717,120 @@ public function missingRequirementsProvider(): array
'Extension testExt2 is required.',
'Extension testExtThree >= 2.0 is required.',
]],
- ['testPHPVersionOperatorLessThan', ['PHP < 5.4 is required.']],
- ['testPHPVersionOperatorLessThanEquals', ['PHP <= 5.4 is required.']],
- ['testPHPVersionOperatorGreaterThan', ['PHP > 99 is required.']],
- ['testPHPVersionOperatorGreaterThanEquals', ['PHP >= 99 is required.']],
- ['testPHPVersionOperatorNoSpace', ['PHP >= 99 is required.']],
- ['testPHPVersionOperatorEquals', ['PHP = 5.4 is required.']],
- ['testPHPVersionOperatorDoubleEquals', ['PHP == 5.4 is required.']],
- ['testPHPUnitVersionOperatorLessThan', ['PHPUnit < 1.0 is required.']],
- ['testPHPUnitVersionOperatorLessThanEquals', ['PHPUnit <= 1.0 is required.']],
- ['testPHPUnitVersionOperatorGreaterThan', ['PHPUnit > 99 is required.']],
- ['testPHPUnitVersionOperatorGreaterThanEquals', ['PHPUnit >= 99 is required.']],
- ['testPHPUnitVersionOperatorEquals', ['PHPUnit = 1.0 is required.']],
- ['testPHPUnitVersionOperatorDoubleEquals', ['PHPUnit == 1.0 is required.']],
- ['testPHPUnitVersionOperatorNoSpace', ['PHPUnit >= 99 is required.']],
- ['testExtensionVersionOperatorLessThan', ['Extension testExtOne < 1.0 is required.']],
- ['testExtensionVersionOperatorLessThanEquals', ['Extension testExtOne <= 1.0 is required.']],
- ['testExtensionVersionOperatorGreaterThan', ['Extension testExtOne > 99 is required.']],
- ['testExtensionVersionOperatorGreaterThanEquals', ['Extension testExtOne >= 99 is required.']],
- ['testExtensionVersionOperatorEquals', ['Extension testExtOne = 1.0 is required.']],
- ['testExtensionVersionOperatorDoubleEquals', ['Extension testExtOne == 1.0 is required.']],
- ['testExtensionVersionOperatorNoSpace', ['Extension testExtOne >= 99 is required.']],
+ ['testPHPVersionOperatorLessThan', [
+ '__OFFSET_LINE=187',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP < 5.4 is required.',
+ ]],
+ ['testPHPVersionOperatorLessThanEquals', [
+ '__OFFSET_LINE=195',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP <= 5.4 is required.',
+ ]],
+ ['testPHPVersionOperatorGreaterThan', [
+ '__OFFSET_LINE=203',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP > 99 is required.',
+ ]],
+ ['testPHPVersionOperatorGreaterThanEquals', [
+ '__OFFSET_LINE=211',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP >= 99 is required.',
+ ]],
+ ['testPHPVersionOperatorNoSpace', [
+ '__OFFSET_LINE=251',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP >= 99 is required.',
+ ]],
+ ['testPHPVersionOperatorEquals', [
+ '__OFFSET_LINE=219',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP = 5.4 is required.',
+ ]],
+ ['testPHPVersionOperatorDoubleEquals', [
+ '__OFFSET_LINE=227',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHP == 5.4 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorLessThan', [
+ '__OFFSET_LINE=259',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit < 1.0 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorLessThanEquals', [
+ '__OFFSET_LINE=267',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit <= 1.0 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorGreaterThan', [
+ '__OFFSET_LINE=275',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit > 99 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorGreaterThanEquals', [
+ '__OFFSET_LINE=283',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit >= 99 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorEquals', [
+ '__OFFSET_LINE=291',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit = 1.0 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorDoubleEquals', [
+ '__OFFSET_LINE=299',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit == 1.0 is required.',
+ ]],
+ ['testPHPUnitVersionOperatorNoSpace', [
+ '__OFFSET_LINE=323',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'PHPUnit >= 99 is required.',
+ ]],
+ ['testExtensionVersionOperatorLessThan', [
+ '__OFFSET_LINE=330',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne < 1.0 is required.',
+ ]],
+ ['testExtensionVersionOperatorLessThanEquals', [
+ '__OFFSET_LINE=337',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne <= 1.0 is required.',
+ ]],
+ ['testExtensionVersionOperatorGreaterThan', [
+ '__OFFSET_LINE=344',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne > 99 is required.',
+ ]],
+ ['testExtensionVersionOperatorGreaterThanEquals', [
+ '__OFFSET_LINE=351',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne >= 99 is required.',
+ ]],
+ ['testExtensionVersionOperatorEquals', [
+ '__OFFSET_LINE=358',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne = 1.0 is required.',
+ ]],
+ ['testExtensionVersionOperatorDoubleEquals', [
+ '__OFFSET_LINE=365',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne == 1.0 is required.',
+ ]],
+ ['testExtensionVersionOperatorNoSpace', [
+ '__OFFSET_LINE=386',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
+ 'Extension testExtOne >= 99 is required.',
+ ]],
['testVersionConstraintTildeMajor', [
+ '__OFFSET_LINE=393',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
'PHP version does not match the required constraint ~1.0.',
'PHPUnit version does not match the required constraint ~2.0.',
]],
['testVersionConstraintCaretMajor', [
+ '__OFFSET_LINE=401',
+ '__OFFSET_FILE=' . $this->getRequirementsTestClassFile(),
'PHP version does not match the required constraint ^1.0.',
'PHPUnit version does not match the required constraint ^2.0.',
]],
@@ -960,4 +1322,14 @@ public function testCoversAnnotationIncludesTraitsUsedByClass(): void
)
);
}
+
+ private function getRequirementsTestClassFile(): string
+ {
+ if (!$this->fileRequirementsTest) {
+ $reflector = new \ReflectionClass(\RequirementsTest::class);
+ $this->fileRequirementsTest = \realpath($reflector->getFileName());
+ }
+
+ return $this->fileRequirementsTest;
+ }
}