Dan Copeland
Dan Copeland

Reputation: 571

PHPUnit HTML and Clover coverage reports differ due to codeCoverageIgnore

I'm using PHPUnit 3.5.14 and have a suite of tests which covers 100% of my PHP application excluding certain portions with // @codeCoverageIgnore[Start|End]. The HTML coverage report shows 100% coverage. But when I generate a Clover XML coverage report, which I'd like Jenkins to read to enforce the 100% coverage requirement, it shows all of my ignored code as not covered.

For example, I have a controller class with 20 methods, one of which looks like this:

// @codeCoverageIgnoreStart
/**
 * Gets an instance of Foo.  Abstracted for testing.
 *
 * @param array $options The constructor argument
 *
 * @return Foo
 */
protected function _getFoo(array $options)
{
    return new Foo($options);
}
// @codeCoverageIgnoreEnd

The HTML coverage report shows 20 methods covered including the one entirely ignored:

pic: coverage report excerpt

enter image description here https://i.sstatic.net/AvpN5.png

But the Clover XML report shows 19/20 methods covered and doesn't mention _getFoo:

<class name="CampaignController" namespace="global" (...)>
  <metrics methods="20" coveredmethods="19" conditionals="0" coveredconditionals="0" statements="532" coveredstatements="532" elements="552" coveredelements="551"/>

...

  <line num="592" type="stmt" count="1"/>
  <line num="593" type="stmt" count="1"/>
  <line num="615" type="method" name="createAction" crap="2" count="2"/>
  <line num="617" type="stmt" count="2"/>

(The _getFoo lines at the top are lines 596-608.)

The logging section of my PHPUnit configuration looks like this:

<logging>
    <log type="coverage-html" target="../public/build/coverage" charset="UTF-8"
        yui="true" highlight="true" lowUpperBound="90" highLowerBound="100"/>
    <log type="coverage-clover" target="../public/build/test-coverage.xml"/>
</logging>

Is there a way to configure the Clover coverage log entry, or change my coverage ignore comments, so that the Clover report indicates 100% coverage to match the HTML report?

Upvotes: 3

Views: 2994

Answers (1)

David Harkness
David Harkness

Reputation: 36532

The problem lies in PHP_CodeCoverage_Report_Clover::process(). While it correctly ignores marked lines while adding up the number of lines in a method, it gets the list of methods from PHP_Token_Stream which doesn't know about those code coverage comments. I created issue #54 at github which should be relatively easy to fix.

BTW, reading PHP_CodeCoverage::getLinesToBeIgnored() which PHP_CodeCoverage_Report_Clover uses, it appears that you can ignore an entire class or method by adding @codeCoverageIgnore to its docblock.

/**
 * Gets an instance of Foo.  Abstracted for testing.
 *
 * @param array $options The constructor argument
 * @return Foo
 *
 * @codeCoverageIgnore
 */

While this won't solve the problem, it is easier than using matching // comments.

Update: If you want to try out a fix, replace the foreach loop over the methods inside PHP_CodeCoverage_Report_Clover::process() with this modified version.

foreach ($_class['methods'] as $methodName => $method) {
    $methodCount        = 0;
    $methodLines        = 0;
    $methodLinesCovered = 0;

    for ($i  = $method['startLine'];
         $i <= $method['endLine'];
         $i++) {
        if (isset($ignoredLines[$i])) {
            continue;
        }

        $add   = TRUE;
        $count = 0;

        if (isset($files[$filename][$i])) {
            if ($files[$filename][$i] != -2) {
                $classStatistics['statements']++;
                $methodLines++;
            }

            if (is_array($files[$filename][$i])) {
                $classStatistics['coveredStatements']++;
                $methodLinesCovered++;
                $count = count($files[$filename][$i]);
            }

            else if ($files[$filename][$i] == -2) {
                $add = FALSE;
            }
        } else {
            $add = FALSE;
        }

        $methodCount = max($methodCount, $count);

        if ($add) {
            $lines[$i] = array(
              'count' => $count,
              'type'  => 'stmt'
            );
        }
    }

    if ($methodLines > 0) {
        $classStatistics['methods']++;

        if ($methodCount > 0) {
            $classStatistics['coveredMethods']++;
        }

        $lines[$method['startLine']] = array(
          'count' => $methodCount,
          'crap'  => PHP_CodeCoverage_Util::crap(
                       $method['ccn'],
                       PHP_CodeCoverage_Util::percent(
                         $methodLinesCovered,
                         $methodLines
                       )
                     ),
          'type'  => 'method',
          'name'  => $methodName
        );
    }
}

Upvotes: 2

Related Questions