Skip to content

Commit

Permalink
Reimplement coverage:exclude option for PHPUnit 11 (#6739)
Browse files Browse the repository at this point in the history
* Reimplement coverage:exclude for PHPUnit 11

* CodeCoverage: Fix excluding of files when whildcard patterns are used
  • Loading branch information
Naktibalda committed Feb 23, 2024
1 parent 86121cf commit dfe90fa
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 32 deletions.
120 changes: 95 additions & 25 deletions src/Codeception/Coverage/Filter.php
Expand Up @@ -7,41 +7,42 @@
use Codeception\Configuration;
use Codeception\Exception\ConfigurationException;
use Codeception\Exception\ModuleException;
use Codeception\Lib\Notification;
use PHPUnit\Runner\Version as PHPUnitVersion;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Filter as PhpUnitFilter;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Finder;

use function array_pop;
use function explode;
use function implode;
use function is_array;
use function method_exists;
use function iterator_to_array;
use function str_replace;

class Filter
{
protected static ?Filter $c3 = null;
protected static ?self $codeceptionFilter = null;

protected ?\SebastianBergmann\CodeCoverage\Filter $filter = null;
protected ?PhpUnitFilter $phpUnitFilter = null;

public function __construct(protected ?CodeCoverage $phpCodeCoverage)
{
$this->filter = $this->phpCodeCoverage->filter();
$this->phpUnitFilter = $this->phpCodeCoverage->filter();
}

public static function setup(CodeCoverage $phpCoverage): self
{
self::$c3 = new self($phpCoverage);
return self::$c3;
self::$codeceptionFilter = new self($phpCoverage);
return self::$codeceptionFilter;
}

/**
* @throws ConfigurationException
*/
public function whiteList(array $config): self
{
$filter = $this->filter;
$filter = $this->phpUnitFilter;
if (!isset($config['coverage'])) {
return $this;
}
Expand All @@ -56,6 +57,10 @@ public function whiteList(array $config): self
}
}

if (PHPUnitVersion::series() >= 11) {
return $this->newWhiteList($coverage['whitelist']);
}

if (isset($coverage['whitelist']['include'])) {
if (!is_array($coverage['whitelist']['include'])) {
throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array');
Expand All @@ -75,27 +80,77 @@ public function whiteList(array $config): self
if (!is_array($coverage['whitelist']['exclude'])) {
throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array');
}
if (method_exists($filter, 'excludeFile')) {
foreach ($coverage['whitelist']['exclude'] as $fileOrDir) {
try {
$finder = !str_contains($fileOrDir, '*')
? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]
: $this->matchWildcardPattern($fileOrDir);

foreach ($finder as $file) {
$filter->excludeFile((string)$file);
}
} catch (DirectoryNotFoundException) {
continue;

foreach ($coverage['whitelist']['exclude'] as $fileOrDir) {
try {
$finder = !str_contains($fileOrDir, '*')
? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]
: $this->matchWildcardPattern($fileOrDir);

foreach ($finder as $file) {
$filter->excludeFile((string)$file);
}
} catch (DirectoryNotFoundException) {
continue;
}
} else {
Notification::warning('`coverage: whitelist: exclude` option has no effect since PHPUnit 11.0', '');
}
}
return $this;
}

private function newWhiteList(array $whitelist): self
{
$include = $whitelist['include'] ?? [];
$exclude = $whitelist['exclude'] ?? [];

if (!is_array($include)) {
throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array');
}
if (!is_array($exclude)) {
throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array');
}

if (count($exclude) === 0 && count($include) === 0) {
return $this;
}

if (count($include) === 0) {
$include = [
Configuration::projectDir() . DIRECTORY_SEPARATOR . '*'
];
}

$allIncludedFiles = [];
foreach ($include as $fileOrDir) {
$finder = !str_contains($fileOrDir, '*')
? $this->matchFileOrDirectory($fileOrDir)
: $this->matchWildcardPattern($fileOrDir);

$allIncludedFiles += iterator_to_array($finder->getIterator());
}

$allExcludedFiles = [];
foreach ($exclude as $fileOrDir) {
try {
$finder = !str_contains($fileOrDir, '*')
? $this->matchFileOrDirectory($fileOrDir)
: $this->matchWildcardPattern($fileOrDir);

$allExcludedFiles += iterator_to_array($finder->getIterator());
} catch (DirectoryNotFoundException) {
continue;
}
}

$coveredFiles = array_diff($allIncludedFiles, $allExcludedFiles);

foreach ($coveredFiles as $coveredFile) {
$this->phpUnitFilter->includeFile((string)$coveredFile);
}

return $this;
}

/**
* @throws ModuleException
*/
Expand All @@ -108,6 +163,21 @@ public function blackList(array $config): self
return $this;
}

private function matchFileOrDirectory(string $fileOrDir): Finder
{
$fullPath = Configuration::projectDir() . $fileOrDir;
$finder = Finder::create();
if (is_dir($fullPath)) {
$finder->in($fullPath);
$finder->name('*.php');
} else {
$finder->in(dirname($fullPath));
$finder->name(basename($fullPath));
}
$finder->ignoreVCS(true)->files();
return $finder;
}

protected function matchWildcardPattern(string $pattern): Finder
{
$finder = Finder::create();
Expand All @@ -123,15 +193,15 @@ protected function matchWildcardPattern(string $pattern): Finder
if ($lastPath === '*') {
$finder->in(Configuration::projectDir() . implode('/', $parts));
} else {
$finder->in(Configuration::projectDir() . implode('/', $parts) . '/' . $lastPath);
$finder->in(Configuration::projectDir() . implode('/', [...$parts, $lastPath]));
}
}
$finder->ignoreVCS(true)->files();
return $finder;
}

public function getFilter(): \SebastianBergmann\CodeCoverage\Filter
public function getFilter(): PhpUnitFilter
{
return $this->filter;
return $this->phpUnitFilter;
}
}
7 changes: 0 additions & 7 deletions tests/unit/Codeception/Coverage/FilterTest.php
Expand Up @@ -23,9 +23,6 @@ protected function _before()

public function testWhitelistFilterApplied()
{
if (version_compare(VersionAlias::id(), '11.0', '>=')) {
$this->markTestSkipped('Whitelist exclude option has no effect since PHPUnit 11.0');
}
$config = [
'coverage' => [
'whitelist' => [
Expand Down Expand Up @@ -57,10 +54,6 @@ public function testWhitelistFilterApplied()

public function testShortcutFilter()
{
if (version_compare(VersionAlias::id(), '11.0', '>=')) {
$this->markTestSkipped('Whitelist exclude option has no effect since PHPUnit 11.0');
}

$config = ['coverage' => [
'include' => ['tests/*'],
'exclude' => ['tests/support/CodeGuy.php']
Expand Down

0 comments on commit dfe90fa

Please sign in to comment.