Skip to content

Commit

Permalink
Request coverage reports with command line args (#1375)
Browse files Browse the repository at this point in the history
* Request coverage reports with command line args

* Remove coverage reports from PHPUnit 9.3+ format
  • Loading branch information
sanmai committed Oct 26, 2020
1 parent cc299b1 commit 0db4bce
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 112 deletions.
12 changes: 5 additions & 7 deletions src/TestFramework/AbstractTestFrameworkAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ abstract class AbstractTestFrameworkAdapter implements TestFrameworkAdapter
private $mutationConfigBuilder;
private $versionParser;
private $commandLineBuilder;

/**
* @var string|null
*/
private $version;

public function __construct(
Expand All @@ -65,14 +61,16 @@ public function __construct(
MutationConfigBuilder $mutationConfigBuilder,
CommandLineArgumentsAndOptionsBuilder $argumentsAndOptionsBuilder,
VersionParser $versionParser,
CommandLineBuilder $commandLineBuilder
CommandLineBuilder $commandLineBuilder,
?string $version = null
) {
$this->testFrameworkExecutable = $testFrameworkExecutable;
$this->initialConfigBuilder = $initialConfigBuilder;
$this->mutationConfigBuilder = $mutationConfigBuilder;
$this->argumentsAndOptionsBuilder = $argumentsAndOptionsBuilder;
$this->versionParser = $versionParser;
$this->commandLineBuilder = $commandLineBuilder;
$this->version = $version;
}

abstract public function testsPass(string $output): bool;
Expand All @@ -82,7 +80,7 @@ abstract public function getName(): string;
abstract public function hasJUnitReport(): bool;

/**
* Returns array of arguments to pass them into the Initial Run Symfony Process
* Returns array of arguments to pass them into the Initial Run Process
*
* @param string[] $phpExtraArgs
*
Expand All @@ -97,7 +95,7 @@ public function getInitialTestRunCommandLine(
}

/**
* Returns array of arguments to pass them into the Mutant Symfony Process
* Returns array of arguments to pass them into the Mutant Process
*
* @param TestLocation[] $tests
*
Expand Down
3 changes: 2 additions & 1 deletion src/TestFramework/CommandLineBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@

/**
* @internal
* @final
*/
final class CommandLineBuilder
class CommandLineBuilder
{
/**
* @var string[]|null
Expand Down
50 changes: 50 additions & 0 deletions src/TestFramework/PhpUnit/Adapter/PhpUnitAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@
use Infection\AbstractTestFramework\MemoryUsageAware;
use Infection\PhpParser\Visitor\IgnoreNode\PhpUnitCodeCoverageAnnotationIgnorer;
use Infection\TestFramework\AbstractTestFrameworkAdapter;
use Infection\TestFramework\CommandLineArgumentsAndOptionsBuilder;
use Infection\TestFramework\CommandLineBuilder;
use Infection\TestFramework\Config\InitialConfigBuilder;
use Infection\TestFramework\Config\MutationConfigBuilder;
use Infection\TestFramework\IgnoresAdditionalNodes;
use Infection\TestFramework\ProvidesInitialRunOnlyOptions;
use Infection\TestFramework\VersionParser;
use function Safe\preg_match;
use function Safe\sprintf;
use function version_compare;
Expand All @@ -52,11 +57,56 @@ class PhpUnitAdapter extends AbstractTestFrameworkAdapter implements IgnoresAddi
{
public const COVERAGE_DIR = 'coverage-xml';

private $tmpDir;

private $jUnitFilePath;

public function __construct(
string $testFrameworkExecutable,
string $tmpDir,
string $jUnitFilePath,
InitialConfigBuilder $initialConfigBuilder,
MutationConfigBuilder $mutationConfigBuilder,
CommandLineArgumentsAndOptionsBuilder $argumentsAndOptionsBuilder,
VersionParser $versionParser,
CommandLineBuilder $commandLineBuilder,
?string $version = null
) {
parent::__construct($testFrameworkExecutable, $initialConfigBuilder, $mutationConfigBuilder, $argumentsAndOptionsBuilder, $versionParser, $commandLineBuilder, $version);

$this->tmpDir = $tmpDir;
$this->jUnitFilePath = $jUnitFilePath;
}

public function hasJUnitReport(): bool
{
return true;
}

/**
* Returns array of arguments to pass them into the Initial Run Process
*
* @param string[] $phpExtraArgs
*
* @return string[]
*/
public function getInitialTestRunCommandLine(
string $extraOptions,
array $phpExtraArgs,
bool $skipCoverage
): array {
if ($skipCoverage === false) {
$extraOptions = trim(sprintf(
'%s --coverage-xml=%s --log-junit=%s',
$extraOptions,
$this->tmpDir . '/' . self::COVERAGE_DIR,
$this->jUnitFilePath // escapeshellarg() is done up the stack in ArgumentsAndOptionsBuilder
));
}

return parent::getInitialTestRunCommandLine($extraOptions, $phpExtraArgs, $skipCoverage);
}

public function testsPass(string $output): bool
{
if (preg_match('/failures!/i', $output)) {
Expand Down
6 changes: 3 additions & 3 deletions src/TestFramework/PhpUnit/Adapter/PhpUnitAdapterFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ public static function create(

return new PhpUnitAdapter(
$testFrameworkExecutable,
$tmpDir,
$jUnitFilePath,
new InitialConfigBuilder(
$tmpDir,
$testFrameworkConfigContent,
$configManipulator,
$jUnitFilePath,
$sourceDirectories,
$skipCoverage
$sourceDirectories
),
new MutationConfigBuilder(
$tmpDir,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function build(string $configPath, string $extraOptions): array
'--configuration',
$configPath,
],
explode(' ', $extraOptions)
explode(' ', $extraOptions) // FIXME might break space-containing paths
);

return array_filter($options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
use DOMElement;
use DOMNode;
use Infection\TestFramework\Config\InitialConfigBuilder as ConfigBuilder;
use Infection\TestFramework\PhpUnit\Adapter\PhpUnitAdapter;
use Infection\TestFramework\PhpUnit\Config\XmlConfigurationManipulator;
use Infection\TestFramework\SafeDOMXPath;
use function Safe\file_put_contents;
Expand All @@ -55,9 +54,7 @@ class InitialConfigBuilder implements ConfigBuilder
private $tmpDir;
private $originalXmlConfigContent;
private $configManipulator;
private $jUnitFilePath;
private $srcDirs;
private $skipCoverage;

/**
* @param string[] $srcDirs
Expand All @@ -66,9 +63,7 @@ public function __construct(
string $tmpDir,
string $originalXmlConfigContent,
XmlConfigurationManipulator $configManipulator,
string $jUnitFilePath,
array $srcDirs,
bool $skipCoverage
array $srcDirs
) {
Assert::notEmpty(
$originalXmlConfigContent,
Expand All @@ -78,9 +73,7 @@ public function __construct(
$this->tmpDir = $tmpDir;
$this->originalXmlConfigContent = $originalXmlConfigContent;
$this->configManipulator = $configManipulator;
$this->jUnitFilePath = $jUnitFilePath;
$this->srcDirs = $srcDirs;
$this->skipCoverage = $skipCoverage;
}

public function build(string $version): string
Expand Down Expand Up @@ -109,11 +102,6 @@ public function build(string $version): string
$this->configManipulator->removeExistingLoggers($xPath);
$this->configManipulator->removeExistingPrinters($xPath);

if (!$this->skipCoverage) {
$this->addCodeCoverageLogger($xPath);
$this->addJUnitLogger($xPath);
}

file_put_contents($path, $dom->saveXML());

return $path;
Expand All @@ -124,28 +112,6 @@ private function buildPath(): string
return $this->tmpDir . '/phpunitConfiguration.initial.infection.xml';
}

private function addJUnitLogger(SafeDOMXPath $xPath): void
{
$logging = $this->getOrCreateNode($xPath, 'logging');

$junitLog = $xPath->document->createElement('log');
$junitLog->setAttribute('type', 'junit');
$junitLog->setAttribute('target', $this->jUnitFilePath);

$logging->appendChild($junitLog);
}

private function addCodeCoverageLogger(SafeDOMXPath $xPath): void
{
$logging = $this->getOrCreateNode($xPath, 'logging');

$coverageXmlLog = $xPath->document->createElement('log');
$coverageXmlLog->setAttribute('type', 'coverage-xml');
$coverageXmlLog->setAttribute('target', $this->tmpDir . '/' . PhpUnitAdapter::COVERAGE_DIR);

$logging->appendChild($coverageXmlLog);
}

private function addCoverageFilterWhitelistIfDoesNotExist(SafeDOMXPath $xPath): void
{
$filterNode = $this->getNode($xPath, 'filter');
Expand All @@ -168,18 +134,6 @@ private function addCoverageFilterWhitelistIfDoesNotExist(SafeDOMXPath $xPath):
}
}

private function getOrCreateNode(SafeDOMXPath $xPath, string $nodeName): DOMElement
{
$node = $this->getNode($xPath, $nodeName);

if (!$node) {
$node = $this->createNode($xPath->document, $nodeName);
}
Assert::isInstanceOf($node, DOMElement::class);

return $node;
}

private function getNode(SafeDOMXPath $xPath, string $nodeName): ?DOMNode
{
$nodeList = $xPath->query(sprintf('/phpunit/%s', $nodeName));
Expand Down
11 changes: 8 additions & 3 deletions src/TestFramework/PhpUnit/Config/XmlConfigurationManipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,17 @@ public function replaceWithAbsolutePaths(SafeDOMXPath $xPath): void
}
}

/**
* Removes existing loggers to improve throughput during MT. Initial test loggers are added through CLI arguments.
*/
public function removeExistingLoggers(SafeDOMXPath $xPath): void
{
foreach ($xPath->query('/phpunit/logging') as $node) {
$document = $xPath->document->documentElement;
Assert::isInstanceOf($document, DOMElement::class);
$document->removeChild($node);
$node->parentNode->removeChild($node);
}

foreach ($xPath->query('/phpunit/coverage/report') as $node) {
$node->parentNode->removeChild($node);
}
}

Expand Down
93 changes: 86 additions & 7 deletions tests/phpunit/TestFramework/PhpUnit/Adapter/PhpUnitAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,28 @@ final class PhpUnitAdapterTest extends TestCase
*/
private $adapter;

private $initialConfigBuilder;
private $mutationConfigBuilder;
private $cliArgumentsBuilder;
private $commandLineBuilder;

protected function setUp(): void
{
$initialConfigBuilder = $this->createMock(InitialConfigBuilder::class);
$mutationConfigBuilder = $this->createMock(MutationConfigBuilder::class);
$cliArgumentsBuilder = $this->createMock(CommandLineArgumentsAndOptionsBuilder::class);
$this->initialConfigBuilder = $this->createMock(InitialConfigBuilder::class);
$this->mutationConfigBuilder = $this->createMock(MutationConfigBuilder::class);
$this->cliArgumentsBuilder = $this->createMock(CommandLineArgumentsAndOptionsBuilder::class);
$this->commandLineBuilder = $this->createMock(CommandLineBuilder::class);

$this->adapter = new PhpUnitAdapter(
'/path/to/phpunit',
$initialConfigBuilder,
$mutationConfigBuilder,
$cliArgumentsBuilder,
'/tmp',
'/tmp/infection/junit.xml',
$this->initialConfigBuilder,
$this->mutationConfigBuilder,
$this->cliArgumentsBuilder,
new VersionParser(),
new CommandLineBuilder()
$this->commandLineBuilder,
'9.0'
);
}

Expand Down Expand Up @@ -120,6 +129,76 @@ public function test_it_provides_node_ignorers(): void
);
}

/**
* @group integration
*/
public function test_it_provides_initial_test_run_command_line_when_no_coverage_is_expected(): void
{
$this->cliArgumentsBuilder
->expects($this->once())
->method('build')
->with('', '--group=default')
;

$this->commandLineBuilder
->expects($this->once())
->method('build')
->with('/path/to/phpunit', ['-d', 'memory_limit=-1'], [])
->willReturn(['/path/to/phpunit', '--dummy-argument'])
;

$initialTestRunCommandLine = $this->adapter->getInitialTestRunCommandLine('--group=default', ['-d', 'memory_limit=-1'], true);

$this->assertSame(
[
'/path/to/phpunit',
'--dummy-argument',
],
$initialTestRunCommandLine
);
}

/**
* @group integration
*/
public function test_it_provides_initial_test_run_command_line_when_coverage_report_is_requested(): void
{
$this->cliArgumentsBuilder
->expects($this->once())
->method('build')
->with('', '--group=default --coverage-xml=/tmp/coverage-xml --log-junit=/tmp/infection/junit.xml')
->willReturn([
'--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml',
])
;

$this->commandLineBuilder
->expects($this->once())
->method('build')
->with('/path/to/phpunit', ['-d', 'memory_limit=-1'], [
'--group=default', '--coverage-xml=/tmp/coverage-xml', '--log-junit=/tmp/infection/junit.xml',
])
->willReturn([
'/path/to/phpunit',
'--group=default',
'--coverage-xml=/tmp/coverage-xml',
'--log-junit=/tmp/infection/junit.xml',
])
;

$initialTestRunCommandLine = $this->adapter->getInitialTestRunCommandLine('--group=default', ['-d', 'memory_limit=-1'], false);

$this->assertSame(
[
'/path/to/phpunit',
'--group=default',
'--coverage-xml=/tmp/coverage-xml',
'--log-junit=/tmp/infection/junit.xml',
],
$initialTestRunCommandLine
);
}

public function outputProvider(): iterable
{
yield ['OK, but incomplete, skipped, or risky tests!', true];
Expand Down

0 comments on commit 0db4bce

Please sign in to comment.