Skip to content

Commit

Permalink
Handle unexpected output (paratestphp#769)
Browse files Browse the repository at this point in the history
  • Loading branch information
Slamdunk authored Jun 13, 2023
1 parent 56ba24a commit e67e9ed
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 56 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/.idea
/coverage
/test/fixtures/generated_tests
/test/fixtures/wrapper_batchsize_suite/tmp
/vendor
/.phpunit.cache
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ code-coverage: coverage/junit.xml
--ignore-msi-with-no-mutations \
--min-msi=100 \
$(INFECTION_ARGS)

.PHONY: clean
clean:
rm -r test/tmp/*

.PHONY: regenerate-fixture-results
regenerate-fixture-results: vendor
Expand Down
3 changes: 3 additions & 0 deletions bin/phpunit-wrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
$getopt = getopt('', [
'status-file:',
'progress-file:',
'unexpected-output-file:',
'testresult-file:',
'teamcity-file:',
'testdox-file:',
Expand Down Expand Up @@ -36,6 +37,7 @@
assert(is_resource($statusFile));

assert(isset($getopt['progress-file']) && is_string($getopt['progress-file']));
assert(isset($getopt['unexpected-output-file']) && is_string($getopt['unexpected-output-file']));
assert(isset($getopt['testresult-file']) && is_string($getopt['testresult-file']));
assert(!isset($getopt['teamcity-file']) || is_string($getopt['teamcity-file']));
assert(!isset($getopt['testdox-file']) || is_string($getopt['testdox-file']));
Expand All @@ -47,6 +49,7 @@
$application = new ApplicationForWrapperWorker(
$phpunitArgv,
$getopt['progress-file'],
$getopt['unexpected-output-file'],
$getopt['testresult-file'],
$getopt['teamcity-file'] ?? null,
$getopt['testdox-file'] ?? null,
Expand Down
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ parameters:
count: 1
path: src/Options.php

-
message: "#^Match expression does not handle remaining value\\: string$#"
count: 1
path: src/WrapperRunner/ResultPrinter.php

-
message: "#^Property ParaTest\\\\WrapperRunner\\\\SuiteLoader\\:\\:\\$files \\(array\\<int, non\\-empty\\-string\\>\\) does not accept array\\<int, int\\|string\\>\\.$#"
count: 1
Expand Down
6 changes: 5 additions & 1 deletion src/WrapperRunner/ApplicationForWrapperWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ final class ApplicationForWrapperWorker
public function __construct(
private readonly array $argv,
private readonly string $progressFile,
private readonly string $unexpectedOutputFile,
private readonly string $testresultFile,
private readonly ?string $teamcityFile,
private readonly ?string $testdoxFile,
Expand Down Expand Up @@ -140,7 +141,10 @@ private function bootstrap(): void
}

new ProgressPrinter(
DefaultPrinter::from($this->progressFile),
new ProgressPrinterOutput(
DefaultPrinter::from($this->progressFile),
DefaultPrinter::from($this->unexpectedOutputFile),
),
EventFacade::instance(),
false,
120,
Expand Down
42 changes: 42 additions & 0 deletions src/WrapperRunner/ProgressPrinterOutput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace ParaTest\WrapperRunner;

use PHPUnit\TextUI\Output\Printer;

use function preg_match;

/** @internal */
final class ProgressPrinterOutput implements Printer
{
public function __construct(
private readonly Printer $progressPrinter,
private readonly Printer $outputPrinter,
) {
}

public function print(string $buffer): void
{
// Skip anything in \PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter::printProgress except $progress
if (
$buffer === "\n"
|| preg_match('/^ +$/', $buffer) === 1
|| preg_match('/^ \\d+ \\/ \\d+ \\(...%\\)$/', $buffer) === 1
) {
return;
}

match ($buffer) {
'E', 'F', 'I', 'N', 'D', 'R', 'W', 'S', '.' => $this->progressPrinter->print($buffer),
default => $this->outputPrinter->print($buffer),
};
}

public function flush(): void
{
$this->progressPrinter->flush();
$this->outputPrinter->flush();
}
}
18 changes: 11 additions & 7 deletions src/WrapperRunner/ResultPrinter.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
use function fseek;
use function ftell;
use function fwrite;
use function preg_replace;
use function sprintf;
use function str_repeat;
use function strlen;
Expand Down Expand Up @@ -129,8 +128,11 @@ public function start(): void
}

/** @param list<SplFileInfo> $teamcityFiles */
public function printFeedback(SplFileInfo $progressFile, array $teamcityFiles): void
{
public function printFeedback(
SplFileInfo $progressFile,
SplFileInfo $outputFile,
array $teamcityFiles
): void {
if ($this->options->needsTeamcity) {
$teamcityProgress = $this->tailMultiple($teamcityFiles);

Expand All @@ -150,14 +152,16 @@ public function printFeedback(SplFileInfo $progressFile, array $teamcityFiles):
return;
}

$unexpectedOutput = $this->tail($outputFile);
if ($unexpectedOutput !== '') {
$this->output->write($unexpectedOutput);
}

$feedbackItems = $this->tail($progressFile);
if ($feedbackItems === '') {
return;
}

$feedbackItems = preg_replace('/ +\\d+ \\/ \\d+ \\( *\\d+%\\)\\s*/', '', $feedbackItems);
assert($feedbackItems !== null);

$actualTestCount = strlen($feedbackItems);
for ($index = 0; $index < $actualTestCount; ++$index) {
$this->printFeedbackItem($feedbackItems[$index]);
Expand Down Expand Up @@ -249,7 +253,7 @@ private function printFeedbackItemColor(string $item): void
'F' => $this->colorizeTextBox('bg-red, fg-white', $item),
'I', 'N', 'D', 'R', 'W' => $this->colorizeTextBox('fg-yellow, bold', $item),
'S' => $this->colorizeTextBox('fg-cyan, bold', $item),
default => $item,
'.' => $item,
};
$this->output->write($buffer);
}
Expand Down
11 changes: 8 additions & 3 deletions src/WrapperRunner/WrapperRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ final class WrapperRunner implements RunnerInterface
/** @var list<SplFileInfo> */
private array $progressFiles = [];
/** @var list<SplFileInfo> */
private array $unexpectedOutputFiles = [];
/** @var list<SplFileInfo> */
private array $testresultFiles = [];
/** @var list<SplFileInfo> */
private array $coverageFiles = [];
Expand Down Expand Up @@ -165,6 +167,7 @@ private function flushWorker(WrapperWorker $worker): void
$this->exitcode = max($this->exitcode, $worker->getExitCode());
$this->printer->printFeedback(
$worker->progressFile,
$worker->unexpectedOutputFile,
$this->teamcityFiles,
);
$worker->reset();
Expand Down Expand Up @@ -208,9 +211,10 @@ private function startWorker(int $token): WrapperWorker
$worker->start();
$this->batches[$token] = 0;

$this->statusFiles[] = $worker->statusFile;
$this->progressFiles[] = $worker->progressFile;
$this->testresultFiles[] = $worker->testresultFile;
$this->statusFiles[] = $worker->statusFile;
$this->progressFiles[] = $worker->progressFile;
$this->unexpectedOutputFiles[] = $worker->unexpectedOutputFile;
$this->testresultFiles[] = $worker->testresultFile;

if (isset($worker->junitFile)) {
$this->junitFiles[] = $worker->junitFile;
Expand Down Expand Up @@ -300,6 +304,7 @@ private function complete(TestResult $testResultSum): int

$this->clearFiles($this->statusFiles);
$this->clearFiles($this->progressFiles);
$this->clearFiles($this->unexpectedOutputFiles);
$this->clearFiles($this->testresultFiles);
$this->clearFiles($this->coverageFiles);
$this->clearFiles($this->junitFiles);
Expand Down
5 changes: 5 additions & 0 deletions src/WrapperRunner/WrapperWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ final class WrapperWorker

public readonly SplFileInfo $statusFile;
public readonly SplFileInfo $progressFile;
public readonly SplFileInfo $unexpectedOutputFile;
public readonly SplFileInfo $testresultFile;
public readonly SplFileInfo $junitFile;
public readonly SplFileInfo $coverageFile;
Expand Down Expand Up @@ -62,6 +63,8 @@ public function __construct(
touch($this->statusFile->getPathname());
$this->progressFile = new SplFileInfo($commonTmpFilePath . 'progress');
touch($this->progressFile->getPathname());
$this->unexpectedOutputFile = new SplFileInfo($commonTmpFilePath . 'unexpected_output');
touch($this->unexpectedOutputFile->getPathname());
$this->testresultFile = new SplFileInfo($commonTmpFilePath . 'testresult');
if ($options->configuration->hasLogfileJunit()) {
$this->junitFile = new SplFileInfo($commonTmpFilePath . 'junit');
Expand All @@ -83,6 +86,8 @@ public function __construct(
$parameters[] = $this->statusFile->getPathname();
$parameters[] = '--progress-file';
$parameters[] = $this->progressFile->getPathname();
$parameters[] = '--unexpected-output-file';
$parameters[] = $this->unexpectedOutputFile->getPathname();
$parameters[] = '--testresult-file';
$parameters[] = $this->testresultFile->getPathname();
if (isset($this->teamcityFile)) {
Expand Down
37 changes: 37 additions & 0 deletions test/MemoryPrinter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace ParaTest\Tests;

use PHPUnit\TextUI\Output\Printer;

/** @internal */
final class MemoryPrinter implements Printer
{
private string $memory = '';
private bool $flushed = false;

public function print(string $buffer): void
{
$this->memory .= $buffer;
}

public function tail(): string
{
$memory = $this->memory;
$this->memory = '';

return $memory;
}

public function flush(): void
{
$this->flushed = true;
}

public function hasBeenFlushed(): bool
{
return $this->flushed;
}
}
81 changes: 81 additions & 0 deletions test/Unit/WrapperRunner/ProgressPrinterOutputTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace ParaTest\Tests\Unit\WrapperRunner;

use ParaTest\Tests\MemoryPrinter;
use ParaTest\WrapperRunner\ProgressPrinterOutput;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;

/** @internal */
#[CoversClass(ProgressPrinterOutput::class)]
final class ProgressPrinterOutputTest extends TestCase
{
private MemoryPrinter $progress;
private MemoryPrinter $output;
private ProgressPrinterOutput $printer;

protected function setUp(): void
{
$this->progress = new MemoryPrinter();
$this->output = new MemoryPrinter();

$this->printer = new ProgressPrinterOutput(
$this->progress,
$this->output,
);
}

public function testSkipProgressRelatedContents(): void
{
$this->printer->print("\n");
$this->printer->print(' ');
$this->printer->print(' ');
$this->printer->print(' 65 / 75 ( 86%)');
$this->printer->print(' 2484 / 2484 (100%)');

self::assertSame('', $this->progress->tail());
self::assertSame('', $this->output->tail());
}

public function testAProgressGoesIntoProgressTheRestInOutput(): void
{
foreach (['E', 'F', 'I', 'N', 'D', 'R', 'W', 'S', '.'] as $progress) {
$this->printer->print($progress);
}

$this->printer->print('var_dump');

self::assertSame('EFINDRWS.', $this->progress->tail());
self::assertSame('var_dump', $this->output->tail());

$this->printer->print('a ');
self::assertSame('', $this->progress->tail());
self::assertSame('a ', $this->output->tail());

$this->printer->print(' z');
self::assertSame('', $this->progress->tail());
self::assertSame(' z', $this->output->tail());

$this->printer->print(' 65 / 75 ( 86%)');
self::assertSame('', $this->progress->tail());
self::assertSame(' 65 / 75 ( 86%)', $this->output->tail());

$this->printer->print(' 2484 / 2484 (100%) ');
self::assertSame('', $this->progress->tail());
self::assertSame(' 2484 / 2484 (100%) ', $this->output->tail());
}

public function testFlushBoth(): void
{
self::assertFalse($this->progress->hasBeenFlushed());
self::assertFalse($this->output->hasBeenFlushed());

$this->printer->flush();

self::assertTrue($this->progress->hasBeenFlushed());
self::assertTrue($this->output->hasBeenFlushed());
}
}
Loading

0 comments on commit e67e9ed

Please sign in to comment.