Skip to content

Commit a02470b

Browse files
committed
Refactor to allow easier testing, and add a bunch of tests.
1 parent bde983e commit a02470b

16 files changed

+735
-89
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Coverage Path Fixer
2+
======
3+
4+
This simple command line app rewrites the paths inside PHP .cov files so that you're able to merge and upload code
5+
coverage reports to services like [coveralls.io](https://coveralls.io)

composer.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,24 @@
1111
],
1212

1313
"require": {
14+
"php": "^7.1",
1415
"phpunit/php-code-coverage": "^8.0.0",
1516
"symfony/console": "^v5.0.4"
1617
},
1718
"require-dev": {
18-
"phpunit/phpunit": "9.0.1"
19+
"phpunit/phpunit": "9.0.1",
20+
"mikey179/vfsstream": "v1.6.8",
21+
"vimeo/psalm": "^3.9"
1922
},
2023
"autoload": {
2124
"psr-4": {
2225
"CoveragePathFixer\\": "src/"
2326
}
2427
},
28+
"autoload-dev": {
29+
"psr-4": {
30+
"CoveragePathFixerTest\\": "tests/unit/"
31+
}
32+
},
2533
"bin": [ "covpf" ]
2634
}

phpunit.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.0/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
executionOrder="depends,defects"
6+
forceCoversAnnotation="true"
7+
beStrictAboutCoversAnnotation="true"
8+
beStrictAboutOutputDuringTests="true"
9+
beStrictAboutTodoAnnotatedTests="true"
10+
verbose="true">
11+
<testsuites>
12+
<testsuite name="default">
13+
<directory suffix="Test.php">tests</directory>
14+
</testsuite>
15+
</testsuites>
16+
17+
<filter>
18+
<whitelist processUncoveredFilesFromWhitelist="true">
19+
<directory suffix=".php">src</directory>
20+
</whitelist>
21+
</filter>
22+
</phpunit>

psalm.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0"?>
2+
<psalm
3+
totallyTyped="false"
4+
errorLevel="1"
5+
resolveFromConfigFile="true"
6+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7+
xmlns="https://getpsalm.org/schema/config"
8+
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
9+
>
10+
<projectFiles>
11+
<directory name="src" />
12+
<ignoreFiles>
13+
<directory name="vendor" />
14+
</ignoreFiles>
15+
</projectFiles>
16+
</psalm>

src/Application.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
namespace CoveragePathFixer;
66

77
use CoveragePathFixer\Command\ChangePathPrefix;
8+
use CoveragePathFixer\Service\CoverageLoader;
9+
use CoveragePathFixer\Service\FileFinder;
10+
use CoveragePathFixer\Service\FileWriter;
811
use Symfony\Component\Console\Application as ConsoleApplication;
912

1013
class Application extends ConsoleApplication
@@ -13,8 +16,12 @@ public function __construct(string $name = 'CoveragePathFixer', string $version
1316
{
1417
parent::__construct($name, $version);
1518

16-
$command = new ChangePathPrefix();
19+
$finder = new FileFinder();
20+
$loader = new CoverageLoader();
21+
$writer = new FileWriter();
22+
23+
$command = new ChangePathPrefix($finder, $loader, $writer);
1724
$this->add($command);
18-
$this->setDefaultCommand($command->getName(), true);
25+
$this->setDefaultCommand($command->getName() ?? '', true);
1926
}
2027
}

src/Command/ChangePathPrefix.php

Lines changed: 53 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,45 @@
44

55
namespace CoveragePathFixer\Command;
66

7-
use RecursiveDirectoryIterator;
8-
use RecursiveIteratorIterator;
9-
use RecursiveRegexIterator;
10-
use RegexIterator;
11-
use SebastianBergmann\CodeCoverage\CodeCoverage;
12-
use SebastianBergmann\CodeCoverage\Filter;
13-
use SebastianBergmann\CodeCoverage\Report\Clover;
14-
use SebastianBergmann\CodeCoverage\Report\PHP;
15-
use Symfony\Component\Console\Command\Command;
16-
use Symfony\Component\Console\Input\InputArgument;
17-
use Symfony\Component\Console\Input\InputInterface;
18-
use Symfony\Component\Console\Input\InputOption;
19-
use Symfony\Component\Console\Output\OutputInterface;
7+
use CoveragePathFixer\Service\{CoverageLoader, FileFinder, FileWriter, PathFixer};
8+
use SebastianBergmann\CodeCoverage\{CodeCoverage, Filter};
9+
use Symfony\Component\Console\{Command\Command,
10+
Input\InputArgument,
11+
Input\InputInterface,
12+
Input\InputOption,
13+
Output\OutputInterface};
2014

2115
class ChangePathPrefix extends Command
2216
{
17+
/**
18+
* @var string Name of the command
19+
*/
2320
protected static $defaultName = 'fix';
2421

22+
/**
23+
* @var FileFinder
24+
*/
25+
private $finder;
26+
27+
/**
28+
* @var CoverageLoader
29+
*/
30+
private $loader;
31+
32+
/**
33+
* @var FileWriter
34+
*/
35+
private $writer;
36+
37+
public function __construct(FileFinder $finder, CoverageLoader $loader, FileWriter $writer)
38+
{
39+
parent::__construct(self::$defaultName);
40+
41+
$this->finder = $finder;
42+
$this->loader = $loader;
43+
$this->writer = $writer;
44+
}
45+
2546
protected function configure(): void
2647
{
2748
$this->setDescription('Swaps a given path prefix with another in your coverage files')
@@ -66,46 +87,42 @@ protected function configure(): void
6687

6788
protected function execute(InputInterface $input, OutputInterface $output): int
6889
{
90+
$fixer = new PathFixer(
91+
$input->getArgument('original_prefix'),
92+
$input->getArgument('replacement_prefix')
93+
);
94+
6995
try {
70-
$files = $this->findCoverageFiles($input->getArgument('directory_to_search'));
96+
$files = $this->finder->findCoverage($input->getArgument('directory_to_search'));
97+
$output->writeln(sprintf('%d .cov files found', count($files)));
7198

7299
$files = $this->iterateCoverageFiles(
73100
$files,
74-
$input->getArgument('original_prefix'),
75-
$input->getArgument('replacement_prefix')
101+
$fixer
76102
);
77103

104+
$this->writer->setFiles($files);
105+
78106
if ($path = $input->getOption('merge')) {
79-
$files = $this->mergeCoverageFiles($files, $path);
107+
$this->writer->merge($path);
80108
}
81109

82-
$this->outputCoverageFiles($files, $input->getOption('clover'));
110+
$this->writer->write($input->getOption('clover'));
83111
} catch (\Exception $ex) {
84-
return $ex->getCode();
112+
$output->writeln($ex->getMessage());
113+
return 1;
85114
}
86115

87116
return 0;
88117
}
89118

90-
private function changePrefix(array $data, string $originalPrefix, string $replacementPrefix): array
119+
protected function iterateCoverageFiles(array $files, PathFixer $fixer): array
91120
{
92-
return array_combine(array_map(function($el) use ($originalPrefix, $replacementPrefix) {
93-
$el = preg_replace('#^' . $originalPrefix . '#', $replacementPrefix, $el);
94-
return $el;
95-
}, array_keys($data)), array_values($data));
96-
}
121+
return array_map(function(array $file) use ($fixer) {
122+
$coverage = $this->loader->loadCoverage($file[0]);
97123

98-
private function iterateCoverageFiles(array $files, string $originalPrefix, string $replacementPrefix): array
99-
{
100-
return array_map(function(array $file) use ($originalPrefix, $replacementPrefix) {
101-
$coverage = $this->loadCoverageFile($file[0]);
102-
103-
$data = $this->changePrefix($coverage->getData(), $originalPrefix, $replacementPrefix);
104-
$whiteList = $this->changePrefix(
105-
$coverage->filter()->getWhitelistedFiles(),
106-
$originalPrefix,
107-
$replacementPrefix
108-
);
124+
$data = $fixer->fix($coverage->getData());
125+
$whiteList = $fixer->fix( $coverage->filter()->getWhitelistedFiles());
109126

110127
$filter = new Filter();
111128
$filter->setWhitelistedFiles($whiteList);
@@ -116,54 +133,4 @@ private function iterateCoverageFiles(array $files, string $originalPrefix, stri
116133
return $coverage;
117134
}, $files);
118135
}
119-
120-
private function findCoverageFiles(string $directory): array
121-
{
122-
$path = realpath($directory);
123-
124-
$directory = new RecursiveDirectoryIterator($path);
125-
$iterator = new RecursiveIteratorIterator($directory);
126-
$filtered = new RegexIterator($iterator, '/^.+\.cov$/i', RecursiveRegexIterator::GET_MATCH);
127-
128-
return iterator_to_array($filtered);
129-
}
130-
131-
private function loadCoverageFile(string $file): CodeCoverage
132-
{
133-
$coverage = include $file;
134-
135-
if (!($coverage instanceof CodeCoverage)) {
136-
unset($coverage);
137-
throw new \Exception('File with coverage extension not resolved to CodeCoverage class');
138-
}
139-
140-
return $coverage;
141-
}
142-
143-
private function mergeCoverageFiles(array $files, string $path): array
144-
{
145-
$coverage = new CodeCoverage();
146-
147-
foreach ($files as $file => $coverage) {
148-
$coverage->merge($coverage);
149-
}
150-
151-
return [$path => $coverage];
152-
}
153-
154-
private function outputCoverageFiles(array $files, bool $asClover = false): void
155-
{
156-
array_walk($files, function($coverage, $path) use ($asClover) {
157-
if ($asClover) {
158-
$filename = basename($path, '.cov');
159-
$directory = dirname($path);
160-
161-
$reportWriter = new Clover();
162-
$reportWriter->process($coverage, $directory . DIRECTORY_SEPARATOR . $filename . '.xml');
163-
}
164-
165-
$reportWriter = new PHP();
166-
$reportWriter->process($coverage, $path);
167-
});
168-
}
169136
}

src/Service/CoverageLoader.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CoveragePathFixer\Service;
6+
7+
use SebastianBergmann\CodeCoverage\CodeCoverage;
8+
9+
class CoverageLoader
10+
{
11+
/**
12+
* @param string $file
13+
* @return CodeCoverage
14+
* @throws \Exception
15+
*/
16+
public function loadCoverage(string $file): CodeCoverage
17+
{
18+
$coverage = include $file;
19+
20+
if (!($coverage instanceof CodeCoverage)) {
21+
unset($coverage);
22+
throw new \Exception('File with coverage extension not resolved to CodeCoverage class');
23+
}
24+
25+
return $coverage;
26+
}
27+
}

src/Service/FileFinder.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CoveragePathFixer\Service;
6+
7+
use RecursiveDirectoryIterator;
8+
use RecursiveIteratorIterator;
9+
use RecursiveRegexIterator;
10+
use RegexIterator;
11+
12+
class FileFinder
13+
{
14+
public function findCoverage(string $directory): array
15+
{
16+
$path = realpath($directory);
17+
18+
$directory = new RecursiveDirectoryIterator($path);
19+
$iterator = new RecursiveIteratorIterator($directory);
20+
$filtered = new RegexIterator($iterator, '/^.+\.cov$/i', RecursiveRegexIterator::GET_MATCH);
21+
22+
return iterator_to_array($filtered);
23+
}
24+
}

0 commit comments

Comments
 (0)