Skip to content

Commit

Permalink
Warm code coverage cache by default (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
Slamdunk committed Aug 24, 2020
1 parent 2de13e1 commit d47136e
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 33 deletions.
8 changes: 3 additions & 5 deletions README.md
Expand Up @@ -138,12 +138,10 @@ See [Logging docs](docs/logging.md) for further information.

### Generating code coverage

Beginning from PHPUnit 9.3.4, it is strongly advised to warm the coverage cache before running any code-coverage
analysis, see [PHPUnit Changlog @ 9.3.4](https://github.com/sebastianbergmann/phpunit/blob/master/ChangeLog-9.3.md#934---2020-08-10):
Beginning from PHPUnit 9.3.4, it is strongly advised to set a coverage cache directory,
see [PHPUnit Changlog @ 9.3.4](https://github.com/sebastianbergmann/phpunit/blob/master/ChangeLog-9.3.md#934---2020-08-10).

```
vendor/bin/phpunit --warm-coverage-cache
```
The cache is always warmed by ParaTest before executing the test suite.

Examples assume your tests are located under `./test/unit`.
````
Expand Down
2 changes: 1 addition & 1 deletion src/Runners/PHPUnit/BaseRunner.php
Expand Up @@ -69,7 +69,7 @@ public function __construct(Options $options, OutputInterface $output)

final public function run(): void
{
$this->load(new SuiteLoader($this->options));
$this->load(new SuiteLoader($this->options, $this->output));
$this->printer->start();

$this->doRun();
Expand Down
37 changes: 36 additions & 1 deletion src/Runners/PHPUnit/SuiteLoader.php
Expand Up @@ -9,13 +9,18 @@
use ParaTest\Parser\ParsedFunction;
use ParaTest\Parser\Parser;
use PHPUnit\Framework\ExecutionOrderDependency;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper;
use PHPUnit\TextUI\XmlConfiguration\Configuration;
use PHPUnit\TextUI\XmlConfiguration\PhpHandler;
use PHPUnit\TextUI\XmlConfiguration\TestSuite;
use PHPUnit\Util\FileLoader;
use PHPUnit\Util\Test;
use RuntimeException;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer;
use SebastianBergmann\FileIterator\Facade;
use SebastianBergmann\Timer\Timer;
use Symfony\Component\Console\Output\OutputInterface;

use function array_intersect;
use function array_map;
Expand Down Expand Up @@ -66,11 +71,14 @@ final class SuiteLoader

/** @var Options */
private $options;
/** @var OutputInterface */
private $output;

public function __construct(Options $options)
public function __construct(Options $options, OutputInterface $output)
{
$this->options = $options;
$this->configuration = $options->configuration();
$this->output = $output;
}

/**
Expand Down Expand Up @@ -160,6 +168,7 @@ public function load(): void
$this->files = array_unique($this->files); // remove duplicates

$this->initSuites();
$this->warmCoverageCache();
}

/**
Expand Down Expand Up @@ -440,4 +449,30 @@ private function loadConfiguration(): void

FileLoader::checkAndLoad($bootstrap);
}

private function warmCoverageCache(): void
{
if (($configuration = $this->options->configuration()) === null || ! $configuration->codeCoverage()->hasCacheDirectory()) {
return;
}

$filter = new Filter();
(new FilterMapper())->map(
$filter,
$configuration->codeCoverage()
);
$timer = new Timer();
$timer->start();

$this->output->write('Warming cache for static analysis ... ');

(new CacheWarmer())->warmCache(
$configuration->codeCoverage()->cacheDirectory()->path(),
! $configuration->codeCoverage()->disableCodeCoverageIgnore(),
$configuration->codeCoverage()->ignoreDeprecatedCodeUnits(),
$filter
);

$this->output->writeln('done [' . $timer->stop()->asString() . ']');
}
}
72 changes: 46 additions & 26 deletions test/Unit/Runners/PHPUnit/SuiteLoaderTest.php
Expand Up @@ -13,6 +13,7 @@
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RuntimeException;
use Symfony\Component\Console\Output\BufferedOutput;

use function array_keys;
use function array_shift;
Expand All @@ -28,9 +29,17 @@
*/
final class SuiteLoaderTest extends TestBase
{
/** @var BufferedOutput */
private $output;

protected function setUpTest(): void
{
$this->output = new BufferedOutput();
}

public function testLoadThrowsExceptionWithInvalidPath(): void
{
$loader = new SuiteLoader($this->createOptionsFromArgv(['--path' => '/path/to/nowhere']));
$loader = new SuiteLoader($this->createOptionsFromArgv(['--path' => '/path/to/nowhere']), $this->output);

$this->expectException(RuntimeException::class);

Expand All @@ -39,7 +48,7 @@ public function testLoadThrowsExceptionWithInvalidPath(): void

public function testLoadBarePathWithNoPathAndNoConfiguration(): void
{
$loader = new SuiteLoader($this->createOptionsFromArgv([], __DIR__));
$loader = new SuiteLoader($this->createOptionsFromArgv([], __DIR__), $this->output);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('No path or configuration provided (tests must end with Test.php)');
Expand All @@ -52,7 +61,7 @@ public function testLoadTestsuiteFileFromConfig(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-file.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -65,7 +74,7 @@ public function testLoadTestsuiteFilesFromConfigWhileIgnoringExcludeTag(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-excluded-including-file.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -78,7 +87,7 @@ public function testLoadTestsuiteFilesFromDirFromConfigWhileRespectingExcludeTag
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-excluded-including-dir.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -92,7 +101,7 @@ public function testLoadTestsuiteFilesFromConfig(): void
'--configuration' => $this->fixture('phpunit-multifile.xml'),
'--group' => 'fixtures,group4',
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -104,7 +113,7 @@ public function testLoadTestsuiteWithDirectory(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-passing.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -117,7 +126,7 @@ public function testLoadTestsuiteWithDirectories(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-multidir.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -131,7 +140,7 @@ public function testLoadTestsuiteWithFilesDirsMixed(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-files-dirs-mix.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -144,7 +153,7 @@ public function testLoadTestsuiteWithDuplicateFilesDirMixed(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-files-dirs-mix-duplicates.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -158,7 +167,7 @@ public function testLoadSomeTestsuite(): void
'--configuration' => $this->fixture('phpunit-parallel-suite.xml'),
'--testsuite' => 'Suite 1',
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -171,7 +180,7 @@ public function testLoadSuiteFromConfig(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-passing.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -184,7 +193,7 @@ public function testLoadSuiteFromConfigWithMultipleDirs(): void
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-multidir.xml'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$files = $this->getObjectValue($loader, 'files');

Expand All @@ -199,7 +208,7 @@ public function testLoadSuiteFromConfigWithBadSuitePath(): void
'--configuration' => $this->fixture('phpunit-non-existent-testsuite-dir.xml'),
'--testsuite' => uniqid(),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessageMatches('/Suite path \w+ could not be found/');
Expand All @@ -219,7 +228,7 @@ public function testLoadFileGetsPathOfFile(): void
*/
private function getLoadedPaths(string $path, ?SuiteLoader $loader = null): array
{
$loader = $loader ?? new SuiteLoader($this->createOptionsFromArgv(['--path' => $path]));
$loader = $loader ?? new SuiteLoader($this->createOptionsFromArgv(['--path' => $path]), $this->output);
$loader->load();
$loaded = $this->getObjectValue($loader, 'loadedSuites');

Expand All @@ -241,7 +250,7 @@ public function testLoadDirGetsPathOfAllTestsWithKeys(): array
$fixturePath = $this->fixture('passing_tests');
$files = $this->findTests($fixturePath);

$loader = new SuiteLoader($this->createOptionsFromArgv(['--path' => $fixturePath]));
$loader = new SuiteLoader($this->createOptionsFromArgv(['--path' => $fixturePath]), $this->output);
$loader->load();
$loaded = $this->getObjectValue($loader, 'loadedSuites');
foreach ($loaded as $path => $test) {
Expand Down Expand Up @@ -302,7 +311,7 @@ public function testGetTestMethodsOnlyReturnsMethodsOfGroupIfOptionIsSpecified()
'--group' => 'group1',
'--path' => $this->fixture('passing_tests/GroupsTest.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$methods = $loader->getTestMethods();
static::assertCount(2, $methods);
Expand All @@ -316,7 +325,7 @@ public function testGetTestMethodsOnlyReturnsMethodsOfClassGroup(): void
'--group' => 'group4',
'--path' => $this->fixture('passing_tests/GroupsTest.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
$methods = $loader->getTestMethods();
static::assertCount(1, $loader->getSuites());
Expand All @@ -329,7 +338,7 @@ public function testGetSuitesForNonMatchingGroups(): void
'--group' => 'non-existent',
'--path' => $this->fixture('passing_tests/GroupsTest.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
static::assertCount(0, $loader->getSuites());
static::assertCount(0, $loader->getTestMethods());
Expand All @@ -341,7 +350,7 @@ public function testLoadIgnoresFilesWithoutClasses(): void
'--group' => 'non-existent',
'--path' => $this->fixture('special_classes/FileWithoutClass.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
static::assertCount(0, $loader->getTestMethods());
}
Expand All @@ -352,7 +361,7 @@ public function testExcludeGroupSwitchDontExecuteThatGroup(): void
'--exclude-group' => 'group1',
'--path' => $this->fixture('passing_tests/GroupsTest.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
static::assertCount(3, $loader->getTestMethods());
}
Expand All @@ -363,7 +372,7 @@ public function testGroupsSwitchExecutesMultipleGroups(): void
'--group' => 'group1,group3',
'--path' => $this->fixture('passing_tests/GroupsTest.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();
static::assertCount(3, $loader->getTestMethods());
}
Expand All @@ -372,7 +381,7 @@ public function testExecutableTestsForFunctionalModeUse(): void
{
$loader = new SuiteLoader($this->createOptionsFromArgv([
'--path' => $this->fixture('passing_tests/DependsOnChain.php'),
]));
]), $this->output);
$loader->load();
$tests = $loader->getTestMethods();
static::assertCount(2, $tests);
Expand All @@ -388,7 +397,7 @@ public function testParallelSuite(): void
'--configuration' => $this->fixture('phpunit-parallel-suite.xml'),
'--parallel-suite' => true,
'--processes' => 2,
]));
]), $this->output);
$loader->load();

$suites = $loader->getSuites();
Expand All @@ -408,7 +417,7 @@ public function testBatches(): void
'--functional' => true,
'--max-batch-size' => 50,
], __DIR__);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);
$loader->load();

$suites = $loader->getSuites();
Expand All @@ -426,13 +435,24 @@ public function testRunWithFatalParseErrors(): void
$options = $this->createOptionsFromArgv([
'--path' => $this->fixture('fatal_tests' . DS . 'UnitTestWithFatalParseErrorTest.php'),
]);
$loader = new SuiteLoader($options);
$loader = new SuiteLoader($options, $this->output);

self::expectException(ParseError::class);

$loader->load();
}

public function testCacheIsWarmedWhenSpecified(): void
{
$options = $this->createOptionsFromArgv([
'--configuration' => $this->fixture('phpunit-coverage-cache.xml'),
]);
$loader = new SuiteLoader($options, $this->output);
$loader->load();

static::assertStringContainsString('Warming cache', $this->output->fetch());
}

/**
* @return string[]
*/
Expand Down
17 changes: 17 additions & 0 deletions test/fixtures/phpunit-coverage-cache.xml
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
colors="true"
>
<testsuites>
<testsuite name="ParaTest Fixtures">
<file>./passing_tests/GroupsTest.php</file>
</testsuite>
</testsuites>
<coverage cacheDirectory="../tmp/">
<include>
<directory suffix=".php">passing_tests</directory>
</include>
</coverage>
</phpunit>

0 comments on commit d47136e

Please sign in to comment.