diff --git a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md index 1cb07da782b6d..39ae3a881b3b1 100644 --- a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md +++ b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.4.0 +----- + + * added a `CoverageListener` to enhance the code coverage report + 3.3.0 ----- diff --git a/src/Symfony/Bridge/PhpUnit/CoverageListener.php b/src/Symfony/Bridge/PhpUnit/CoverageListener.php new file mode 100644 index 0000000000000..e6b4e7ec98b8b --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/CoverageListener.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +use PHPUnit\Framework\BaseTestListener; +use PHPUnit\Framework\Test; +use Symfony\Bridge\PhpUnit\Legacy\CoverageListenerTrait; + +if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { + class_alias('Symfony\Bridge\PhpUnit\Legacy\CoverageListener', 'Symfony\Bridge\PhpUnit\CoverageListener'); +// Using an early return instead of a else does not work when using the PHPUnit +// phar due to some weird PHP behavior (the class gets defined without executing +// the code before it and so the definition is not properly conditional) +} else { + /** + * CoverageListener adds `@covers ` on each test suite when possible + * to make the code coverage more accurate. + * + * @author Grégoire Pineau + */ + class CoverageListener extends BaseTestListener + { + private $trait; + + public function __construct(callable $sutFqcnResolver = null, $warningOnSutNotFound = false) + { + $this->trait = new CoverageListenerTrait($sutFqcnResolver, $warningOnSutNotFound); + } + + public function startTest(Test $test) + { + $this->trait->startTest($test); + } + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListener.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListener.php new file mode 100644 index 0000000000000..0227828515760 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListener.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +/** + * CoverageListener adds `@covers ` on each test suite when possible + * to make the code coverage more accurate. + * + * @author Grégoire Pineau + * + * @internal + */ +class CoverageListener extends \PHPUnit_Framework_BaseTestListener +{ + private $trait; + + public function __construct(callable $sutFqcnResolver = null, $warningOnSutNotFound = false) + { + $this->trait = new CoverageListenerTrait($sutFqcnResolver, $warningOnSutNotFound); + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + $this->trait->startTest($test); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php new file mode 100644 index 0000000000000..0a1603e646cbf --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\Framework\Test; +use PHPUnit\Framework\Warning; + +/** + * PHP 5.3 compatible trait-like shared implementation. + * + * @author Grégoire Pineau + * + * @internal + */ +class CoverageListenerTrait +{ + private $sutFqcnResolver; + private $warningOnSutNotFound; + private $warnings; + + public function __construct(callable $sutFqcnResolver = null, $warningOnSutNotFound = false) + { + $this->sutFqcnResolver = $sutFqcnResolver; + $this->warningOnSutNotFound = $warningOnSutNotFound; + $this->warnings = array(); + } + + public function startTest($test) + { + $annotations = $test->getAnnotations(); + + $ignoredAnnotations = array('covers', 'coversDefaultClass', 'coversNothing'); + + foreach ($ignoredAnnotations as $annotation) { + if (isset($annotations['class'][$annotation]) || isset($annotations['method'][$annotation])) { + return; + } + } + + $sutFqcn = $this->findSutFqcn($test); + if (!$sutFqcn) { + if ($this->warningOnSutNotFound) { + $message = 'Could not find the tested class.'; + // addWarning does not exist on old PHPUnit version + if (method_exists($test->getTestResultObject(), 'addWarning') && class_exists(Warning::class)) { + $test->getTestResultObject()->addWarning($test, new Warning($message), 0); + } else { + $this->warnings[] = sprintf("%s::%s\n%s", get_class($test), $test->getName(), $message); + } + } + + return; + } + + $testClass = \PHPUnit\Util\Test::class; + if (!class_exists($testClass, false)) { + $testClass = \PHPUnit_Util_Test::class; + } + + $r = new \ReflectionProperty($testClass, 'annotationCache'); + $r->setAccessible(true); + + $cache = $r->getValue(); + $cache = array_replace_recursive($cache, array( + get_class($test) => array( + 'covers' => array($sutFqcn), + ), + )); + $r->setValue($testClass, $cache); + } + + private function findSutFqcn($test) + { + if ($this->sutFqcnResolver) { + $resolver = $this->sutFqcnResolver; + + return $resolver($test); + } + + $class = get_class($test); + + $sutFqcn = str_replace('\\Tests\\', '\\', $class); + $sutFqcn = preg_replace('{Test$}', '', $sutFqcn); + + if (!class_exists($sutFqcn)) { + return; + } + + return $sutFqcn; + } + + public function __destruct() + { + if (!$this->warnings) { + return; + } + + echo "\n"; + + foreach ($this->warnings as $key => $warning) { + echo sprintf("%d) %s\n", ++$key, $warning); + } + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php new file mode 100644 index 0000000000000..216b860a4bc2c --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php @@ -0,0 +1,35 @@ +markTestSkipped('This test cannot be run on Windows.'); + } + + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('This test cannot be run on HHVM.'); + } + + $dir = __DIR__.'/../Tests/Fixtures/coverage'; + $php = PHP_BINARY; + $phpunit = $_SERVER['argv'][0]; + + exec("$php -d zend_extension=xdebug.so $phpunit -c $dir/phpunit-without-listener.xml.dist $dir/tests/ --coverage-text", $output); + $output = implode("\n", $output); + $this->assertContains('Foo', $output); + + exec("$php -d zend_extension=xdebug.so $phpunit -c $dir/phpunit-with-listener.xml.dist $dir/tests/ --coverage-text", $output); + $output = implode("\n", $output); + $this->assertNotContains('Foo', $output); + $this->assertContains("SutNotFoundTest::test\nCould not find the tested class.", $output); + $this->assertNotContains("CoversTest::test\nCould not find the tested class.", $output); + $this->assertNotContains("CoversDefaultClassTest::test\nCould not find the tested class.", $output); + $this->assertNotContains("CoversNothingTest::test\nCould not find the tested class.", $output); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/phpunit-with-listener.xml.dist b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/phpunit-with-listener.xml.dist new file mode 100644 index 0000000000000..1984359ebdd78 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/phpunit-with-listener.xml.dist @@ -0,0 +1,32 @@ + + + + + + + tests + + + + + + src + + + + + + + + true + + + + diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/phpunit-without-listener.xml.dist b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/phpunit-without-listener.xml.dist new file mode 100644 index 0000000000000..6201535933767 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/phpunit-without-listener.xml.dist @@ -0,0 +1,23 @@ + + + + + + + tests + + + + + + src + + + diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/src/Bar.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/src/Bar.php new file mode 100644 index 0000000000000..b50305a402634 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/src/Bar.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpUnitCoverageTest; + +class Bar +{ + private $foo; + + public function __construct(Foo $foo) + { + $this->foo = $foo; + } + + public function barZ() + { + $this->foo->fooZ(); + + return 'bar'; + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/src/Foo.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/src/Foo.php new file mode 100644 index 0000000000000..f811ae70b10a0 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/src/Foo.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpUnitCoverageTest; + +class Foo +{ + public function fooZ() + { + return 'foo'; + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/BarTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/BarTest.php new file mode 100644 index 0000000000000..b49fc706a9cfa --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/BarTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PhpUnitCoverageTest\Tests; + +use PHPUnit\Framework\TestCase; + +class BarTest extends TestCase +{ + public function testBar() + { + if (!class_exists('PhpUnitCoverageTest\Foo')) { + $this->markTestSkipped('This test is not part of the main Symfony test suite. It\'s here to test the CoverageListener.'); + } + + $foo = new \PhpUnitCoverageTest\Foo(); + $bar = new \PhpUnitCoverageTest\Bar($foo); + + $this->assertSame('bar', $bar->barZ()); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversDefaultClassTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversDefaultClassTest.php new file mode 100644 index 0000000000000..d764638d04958 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversDefaultClassTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\TestCase; + +/** + * @coversDefaultClass \DateTime + */ +class CoversDefaultClassTest extends TestCase +{ + public function test() + { + $this->assertTrue(true); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversNothingTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversNothingTest.php new file mode 100644 index 0000000000000..e60ea97e57bbd --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversNothingTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\TestCase; + +/** + * @coversNothing + */ +class CoversNothingTest extends TestCase +{ + public function test() + { + $this->assertTrue(true); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversTest.php new file mode 100644 index 0000000000000..f6d3406046d86 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/CoversTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\TestCase; + +class CoversTest extends TestCase +{ + /** + * @covers \DateTime + */ + public function test() + { + $this->assertTrue(true); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php new file mode 100644 index 0000000000000..934ee77dc1873 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\TestCase; + +class SutNotFoundTest extends TestCase +{ + public function test() + { + $this->assertTrue(true); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php new file mode 100644 index 0000000000000..9647a8658d212 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/../src/Bar.php'; +require __DIR__.'/../src/Foo.php'; + +require __DIR__.'/../../../../Legacy/CoverageListenerTrait.php'; +if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { + require __DIR__.'/../../../../Legacy/CoverageListener.php'; +} +require __DIR__.'/../../../../CoverageListener.php'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy