panther icon indicating copy to clipboard operation
panther copied to clipboard

Code coverage compliance?

Open soullivaneuh opened this issue 7 years ago • 14 comments

Your project is very interesting. :+1:

What about code coverage? Does it work like for a regular Symfony WebTestCase? If not, is that a panned thing or is it not feasible?

Thanks!

soullivaneuh avatar Apr 04 '18 12:04 soullivaneuh

As it browses the app through a web server, coverage will not work by default. It's probably possible to hack something, but it's not on my todo list for now. I'll be glad to merge a PR anyway.

dunglas avatar Apr 04 '18 14:04 dunglas

Actually, it would be possible to use a hack similar to this one: https://github.com/api-platform/core/blob/master/features/bootstrap/CoverageContext.php

dunglas avatar Jun 29 '18 12:06 dunglas

I've tried the above, but I'm not really familiar with this kind of stuff

<?php

namespace App\Tests;

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\Report\PHP;
use Symfony\Component\Panther\PantherTestCase;

class NewsControllerPantherTest extends PantherTestCase
{

    /** @var CodeCoverage */
    private static $coverage;

    protected function setup()
    {
        $filter = new Filter();
        $filter->addDirectoryToWhitelist(__DIR__ . '/../../src');
        self::$coverage = new CodeCoverage(null, $filter);
        self::$coverage->start("test1", true);
    }

    public function testJavaScript()
    {
        $client = static::createPantherClient();
        $crawler = $client->request('GET', '/');

        // do some asserts
        self::assertFalse(false);
    }

    protected function tearDown()
    {
        // what does this actually do?
         $feature = 'test';
        (new PHP())->process(self::$coverage, __DIR__ . "/../../build/cov/coverage-$feature.cov");

        self::$coverage->stop();
    }
}

So there will be created a coverage-test.cov, however PhpStorm does not show covered code. I'm not sure what is missing.

baris1892 avatar Nov 01 '18 18:11 baris1892

I found a way to use code coverage with Panter after inspecting selenium method.

It's a small hack and it can be improved but it work you need to

  • patch public/index.php for saving usage of code (2 patches in this file)
  • overriding 'run()' method in all tests class to include code coverage usage at the end of each tests.
// public/index.php
// add method to start code coverage just in top of file 

require dirname(__DIR__).'/config/bootstrap.php';

// code coverage in tests
if($_SERVER['APP_ENV'] == 'test' ) {
    xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
}
// public/index.php
// add method to strop and srtore code coverage information at the end of /public/index.php

// code coverage in tests
if($_SERVER['APP_ENV'] == 'test' || 1) {
    // get code coverage information.
    $data = xdebug_get_code_coverage();
    xdebug_stop_code_coverage();

    $jsonCodeCoverageFile = __DIR__ . '/..'. DIRECTORY_SEPARATOR . 'var'. DIRECTORY_SEPARATOR. md5(uniqid(rand(), TRUE)).'.code_coverage';

    file_put_contents($jsonCodeCoverageFile, serialize($data));
}

Overriding each tests files

// tests/myFonctionnalTest.php

    public function run(TestResult $result = NULL)
    {

        if ($result === NULL) {
            $result = $this->createResult();
        }

        // run original method.
        parent::run($result);


        $it = new \RecursiveDirectoryIterator("var/");
        // Loop through files
        foreach(new \RecursiveIteratorIterator($it) as $file) {
            if ($file->getExtension() == 'code_coverage') {
                $content = file_get_contents($file);
                $content = unserialize($content);
                // append in current test result.
                $result->getCodeCoverage()->append(
                    $content, $this
                );
                // remove code_coverage_file
                unlink($file);
            }
        }

        return $result;
    }

Now we got covered code with Panther :)

code_coverage

alebec avatar Mar 28 '19 09:03 alebec

I was running into this issue as well. The hack provided by alebec worked like a charm for me. A small change I needed to make:

$content = file_get_contents($file->getRealPath());

unlink($file->getRealPath());

petermanders89 avatar Aug 23 '19 08:08 petermanders89

If one of you guys can add it to the docs, it would be awesome!

dunglas avatar Aug 23 '19 08:08 dunglas

@alebec an improvement to your patch would be to put the code coverage in a dedicated subfolder of var to iterate only that folder when reading them (instead of iterating the full Symfony caches too)

stof avatar Aug 23 '19 08:08 stof

Hi @alebec, thanks for the hack! Just wanted to add that if you're using PHP 7.3, you need to cast rand() as a string.

    $jsonCodeCoverageFile = __DIR__ . '/..'. DIRECTORY_SEPARATOR . 'var'. DIRECTORY_SEPARATOR. md5(uniqid((string)rand(), TRUE)).'.code_coverage';

The error was silently ignored and it took me hours to figure it out.

AcelisWeaven avatar Oct 30 '19 21:10 AcelisWeaven

Hi, have you got some hack for symfony 5.3 and php 8?

tidall87 avatar Nov 08 '21 22:11 tidall87

Version for Symfony 5.4:

Kernel.php:

<?php

namespace App;

use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use function file_put_contents;
use function is_dir;
use function md5;
use function mkdir;
use function mt_rand;
use function serialize;
use function uniqid;
use function xdebug_get_code_coverage;
use function xdebug_start_code_coverage;
use function xdebug_stop_code_coverage;

class Kernel extends BaseKernel
{
	public const COVERAGE_PANTHER_DIR = __DIR__ . '/../var/build/coverage-panther/';
	public const COVERAGE_PANTHER_ENV = 'panther';

	use MicroKernelTrait;

	/**
	 * @inheritDoc
	 */
	public function handle(Request $request, int $type = HttpKernelInterface::MAIN_REQUEST, bool $catch = true)
	{
		// code coverage in tests
		if ($this->environment === $this::COVERAGE_PANTHER_ENV) {
			xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
		}

		$response = parent::handle($request, $type, $catch);

		if ($this->environment === $this::COVERAGE_PANTHER_ENV) {
			// get code coverage information.
			$data = xdebug_get_code_coverage();
			xdebug_stop_code_coverage();

			$jsonCodeCoverageFile = md5(uniqid((string) mt_rand(), true)) . '.code_coverage';

			if (!is_dir($this::COVERAGE_PANTHER_DIR)) {
				mkdir($this::COVERAGE_PANTHER_DIR, 0777, true);
			}

			file_put_contents($this::COVERAGE_PANTHER_DIR . $jsonCodeCoverageFile, serialize($data));
		}

		return $response;
	}
}

ExtendedPantherTestCase.php: ...

	/**
	 * @inheritDoc
	 */
	public function run(TestResult $result = null): TestResult
	{
		$result = parent::run($result);

		if (!is_dir(Kernel::COVERAGE_PANTHER_DIR)) {
			return $result;
		}

		$it = new \RecursiveDirectoryIterator(Kernel::COVERAGE_PANTHER_DIR);

		/** @var SplFileInfo $file */
		foreach (new \RecursiveIteratorIterator($it) as $file) {
			if ($file->getExtension() === 'code_coverage') {
				$content = file_get_contents($file->getRealPath());
				$content = unserialize($content);

				if (!empty($content)) {
					// append in current test result.
					$result->getCodeCoverage()->append(RawCodeCoverageData::fromXdebugWithoutPathCoverage($content), $this);
				}

				// remove code_coverage_file
				unlink($file->getRealPath());
			}
		}

		return $result;
	}

...

Dukecz avatar Jan 20 '22 19:01 Dukecz

@Dukecz Thanks! This works great!

DigitalTimK avatar Feb 04 '22 12:02 DigitalTimK

Codeception has a very own router c3 for this task, maybe you can adopt something from it? I think it makes more sense than fiddeling around in the application kernel.

Additionally I'd prefer a kernel decorator, like the former CacheKernel, to decorate in a specific environment, only.

Basster avatar Sep 14 '22 06:09 Basster

Anyone tried to use pcov instead of xdebug ?

vasilvestre avatar Feb 17 '23 11:02 vasilvestre