Skip to content

Commit 5787f67

Browse files
milodg
authored andcommitted
PhpInterpreter: list code coverage engines and let others to choose one (#400)
1 parent 359ddb1 commit 5787f67

File tree

8 files changed

+70
-42
lines changed

8 files changed

+70
-42
lines changed

src/CodeCoverage/Collector.php

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,26 @@
1515
*/
1616
class Collector
1717
{
18+
public const
19+
ENGINE_PHPDBG = 'PHPDBG',
20+
ENGINE_XDEBUG = 'Xdebug';
21+
1822
/** @var resource */
1923
private static $file;
2024

2125
/** @var string */
2226
private static $collector;
2327

2428

29+
public static function detectEngines(): array
30+
{
31+
return array_filter([
32+
defined('PHPDBG_VERSION') ? self::ENGINE_PHPDBG : null,
33+
extension_loaded('xdebug') ? self::ENGINE_XDEBUG : null,
34+
]);
35+
}
36+
37+
2538
public static function isStarted(): bool
2639
{
2740
return self::$file !== null;
@@ -32,23 +45,26 @@ public static function isStarted(): bool
3245
* Starts gathering the information for code coverage.
3346
* @throws \LogicException
3447
*/
35-
public static function start(string $file): void
48+
public static function start(string $file, string $engine): void
3649
{
3750
if (self::isStarted()) {
3851
throw new \LogicException('Code coverage collector has been already started.');
3952
}
4053
self::$file = fopen($file, 'c+');
4154

42-
if (defined('PHPDBG_VERSION')) {
43-
phpdbg_start_oplog();
44-
self::$collector = 'collectPhpDbg';
55+
switch ($engine) {
56+
case self::ENGINE_PHPDBG:
57+
phpdbg_start_oplog();
58+
self::$collector = 'collectPhpDbg';
59+
break;
4560

46-
} elseif (extension_loaded('xdebug')) {
47-
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
48-
self::$collector = 'collectXdebug';
61+
case self::ENGINE_XDEBUG:
62+
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
63+
self::$collector = 'collectXdebug';
64+
break;
4965

50-
} else {
51-
throw new \LogicException('Code coverage functionality requires Xdebug extension or phpdbg SAPI.');
66+
default:
67+
throw new \LogicException("Code coverage engine '$engine' is not supported.");
5268
}
5369

5470
register_shutdown_function(function (): void {

src/Framework/Environment.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class Environment
2121
/** Test is run by Runner */
2222
public const RUNNER = 'NETTE_TESTER_RUNNER';
2323

24+
/** Code coverage engine */
25+
public const COVERAGE_ENGINE = 'NETTE_TESTER_COVERAGE_ENGINE';
26+
2427
/** Code coverage file */
2528
public const COVERAGE = 'NETTE_TESTER_COVERAGE';
2629

@@ -53,8 +56,8 @@ class_exists('Tester\Assert');
5356
$annotations = self::getTestAnnotations();
5457
self::$checkAssertions = !isset($annotations['outputmatch']) && !isset($annotations['outputmatchfile']);
5558

56-
if (getenv(self::COVERAGE)) {
57-
CodeCoverage\Collector::start(getenv(self::COVERAGE));
59+
if (getenv(self::COVERAGE) && getenv(self::COVERAGE_ENGINE)) {
60+
CodeCoverage\Collector::start(getenv(self::COVERAGE), getenv(self::COVERAGE_ENGINE));
5861
}
5962
}
6063

src/Runner/CliTester.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,12 @@ public function run(): ?int
5858
return null;
5959
}
6060

61-
if ($this->options['--coverage']) {
62-
$coverageFile = $this->prepareCodeCoverage();
63-
}
64-
6561
$runner = $this->createRunner();
6662
$runner->setEnvironmentVariable(Environment::RUNNER, '1');
6763
$runner->setEnvironmentVariable(Environment::COLORS, (string) (int) Environment::$useColors);
68-
if (isset($coverageFile)) {
69-
$runner->setEnvironmentVariable(Environment::COVERAGE, $coverageFile);
64+
65+
if ($this->options['--coverage']) {
66+
$coverageFile = $this->prepareCodeCoverage($runner);
7067
}
7168

7269
if ($this->options['-o'] !== null) {
@@ -228,14 +225,20 @@ private function createRunner(): Runner
228225
}
229226

230227

231-
private function prepareCodeCoverage(): string
228+
private function prepareCodeCoverage(Runner $runner): string
232229
{
233-
if (!$this->interpreter->canMeasureCodeCoverage()) {
230+
$engines = $this->interpreter->getCodeCoverageEngines();
231+
if (count($engines) < 1) {
234232
throw new \Exception("Code coverage functionality requires Xdebug extension or phpdbg SAPI (used {$this->interpreter->getCommandLine()})");
235233
}
234+
236235
file_put_contents($this->options['--coverage'], '');
237236
$file = realpath($this->options['--coverage']);
238-
echo "Code coverage: {$file}\n";
237+
238+
$runner->setEnvironmentVariable(Environment::COVERAGE, $file);
239+
$runner->setEnvironmentVariable(Environment::COVERAGE_ENGINE, $engine = reset($engines));
240+
241+
echo "Code coverage by $engine: $file\n";
239242
return $file;
240243
}
241244

src/Runner/PhpInterpreter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ public function getVersion(): string
107107
}
108108

109109

110-
public function canMeasureCodeCoverage(): bool
110+
public function getCodeCoverageEngines(): array
111111
{
112-
return $this->info->canMeasureCodeCoverage;
112+
return $this->info->codeCoverageEngines;
113113
}
114114

115115

src/Runner/info.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
declare(strict_types=1);
88

9+
require __DIR__ . '/../CodeCoverage/Collector.php';
10+
911
$isPhpDbg = defined('PHPDBG_VERSION');
1012
$extensions = get_loaded_extensions();
1113
natcasesort($extensions);
@@ -21,7 +23,7 @@
2123
),
2224
'extensions' => $extensions,
2325
'tempDir' => sys_get_temp_dir(),
24-
'canMeasureCodeCoverage' => $isPhpDbg || in_array('xdebug', $extensions, true),
26+
'codeCoverageEngines' => Tester\CodeCoverage\Collector::detectEngines(),
2527
];
2628

2729
if (isset($_SERVER['argv'][1])) {
@@ -34,6 +36,7 @@
3436
'PHP version' . ($isPhpDbg ? '; PHPDBG version' : '')
3537
=> "$info->version ($info->sapi)" . ($isPhpDbg ? "; $info->phpDbgVersion" : ''),
3638
'Loaded php.ini files' => count($info->iniFiles) ? implode(', ', $info->iniFiles) : '(none)',
39+
'Code coverage engines' => count($info->codeCoverageEngines) ? implode(', ', $info->codeCoverageEngines) : '(not available)',
3740
'PHP temporary directory' => $info->tempDir == '' ? '(empty)' : $info->tempDir,
3841
'Loaded extensions' => count($info->extensions) ? implode(', ', $info->extensions) : '(none)',
3942
] as $title => $value) {

tests/CodeCoverage/Collector.phpt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ use Tester\FileMock;
99
require __DIR__ . '/../bootstrap.php';
1010

1111

12-
if (!extension_loaded('xdebug') && (!defined('PHPDBG_VERSION'))) {
12+
$engines = CodeCoverage\Collector::detectEngines();
13+
if (count($engines) < 1) {
1314
Tester\Environment::skip('Requires Xdebug or phpdbg SAPI.');
1415
}
16+
$engine = reset($engines);
1517

1618
if (CodeCoverage\Collector::isStarted()) {
1719
Tester\Environment::skip('Requires running without --coverage.');
@@ -20,11 +22,11 @@ if (CodeCoverage\Collector::isStarted()) {
2022
$outputFile = FileMock::create('');
2123

2224
Assert::false(CodeCoverage\Collector::isStarted());
23-
CodeCoverage\Collector::start($outputFile);
25+
CodeCoverage\Collector::start($outputFile, $engine);
2426
Assert::true(CodeCoverage\Collector::isStarted());
2527

26-
Assert::exception(function () use ($outputFile) {
27-
CodeCoverage\Collector::start($outputFile);
28+
Assert::exception(function () use ($outputFile, $engine) {
29+
CodeCoverage\Collector::start($outputFile, $engine);
2830
}, LogicException::class, 'Code coverage collector has been already started.');
2931

3032
$content = file_get_contents($outputFile);

tests/Runner/PhpInterpreter.Zend.phpt

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/Runner/PhpInterpreter.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,19 @@ $interpreter = createInterpreter();
1010

1111
Assert::true($interpreter->hasExtension('DaTe'));
1212
Assert::false($interpreter->hasExtension('foo-bar'));
13+
14+
Assert::contains(PHP_BINARY, $interpreter->getCommandLine());
15+
Assert::same(PHP_VERSION, $interpreter->getVersion());
16+
Assert::same(strpos(PHP_SAPI, 'cgi') !== false, $interpreter->isCgi());
17+
18+
$count = 0;
19+
$engines = $interpreter->getCodeCoverageEngines();
20+
if (defined('PHPDBG_VERSION')) {
21+
Assert::contains(Tester\CodeCoverage\Collector::ENGINE_PHPDBG, $engines);
22+
$count++;
23+
}
24+
if (extension_loaded('xdebug')) {
25+
Assert::contains(Tester\CodeCoverage\Collector::ENGINE_XDEBUG, $engines);
26+
$count++;
27+
}
28+
Assert::count($count, $engines);

0 commit comments

Comments
 (0)