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 -vendor\project\Status +Test result status with and without message ✔ Success  %f ms ✘ Failure  %f ms │ │ Failed asserting that false is true. │ - │ %stests%eend-to-end%eloggers%e_files%eStatusTest.php:24 + │ %stests%ebasic%eunit%eStatusTest.php:%d │ ✘ Error  %f ms │ │ RuntimeException: │ - │ %stests%eend-to-end%eloggers%e_files%eStatusTest.php:29 + │ %stests%ebasic%eunit%eStatusTest.php:%d │ ∅ Incomplete  %f ms │ - │ %stests%eend-to-end%eloggers%e_files%eStatusTest.php:34 + │ %stests%ebasic%eunit%eStatusTest.php:%d │ ↩ Skipped  %f ms │ - │ %stests%eend-to-end%eloggers%e_files%eStatusTest.php:39 + │ %stests%ebasic%eunit%eStatusTest.php:%d │ ☢ Risky  %f ms │ │ This test did not perform any assertions%w │%w - │ %stests%eend-to-end%eloggers%e_files%eStatusTest.php:42%w + │ %stests%ebasic%eunit%eStatusTest.php:%d%w │%w │ ⚠ Warning  %f ms │ - │ %stests%eend-to-end%eloggers%e_files%eStatusTest.php:48 + │ %stests%ebasic%eunit%eStatusTest.php:%d + │ + + ✔ Success with message  %f ms + ✘ Failure with message  %f ms + │ + │ failure with custom message%w + │ Failed asserting that false is true. + │ + │ %stests%ebasic%eunit%eStatusTest.php:%d + │ + + ✘ Error with message  %f ms + │ + │ RuntimeException: error with custom message + │ + │ %stests%ebasic%eunit%eStatusTest.php:%d + │ + + ∅ Incomplete with message  %f ms + │ + │ incomplete with custom message + │ + │ %stests%ebasic%eunit%eStatusTest.php:%d + │ + + ↩ Skipped with message  %f ms + │ + │ skipped with custom message + │ + │ %stests%ebasic%eunit%eStatusTest.php:%d + │ + + ☢ Risky with message  %f ms + │ + │ This test did not perform any assertions%w + │%w + │ %stests%ebasic%eunit%eStatusTest.php:%d%w + │%w + │ + + ⚠ Warning with message  %f ms + │ + │ warning with custom message + │ + │ %stests%ebasic%eunit%eStatusTest.php:%d │ Time: %s, Memory: %s 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/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; + } }