Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbergmann committed Apr 18, 2024
1 parent 833fa23 commit d34d3ea
Show file tree
Hide file tree
Showing 8 changed files with 579 additions and 515 deletions.
32 changes: 19 additions & 13 deletions .psalm/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@
</file>
<file src="src/Runner/PhptTestCase.php">
<ArgumentTypeCoercion>
<code><![CDATA[$arguments]]></code>
<code><![CDATA[$message]]></code>
</ArgumentTypeCoercion>
<InternalClass>
Expand All @@ -668,9 +669,25 @@
<code><![CDATA[RawCodeCoverageData::fromXdebugWithoutPathCoverage([])]]></code>
<code><![CDATA[RawCodeCoverageData::fromXdebugWithoutPathCoverage([])]]></code>
</InternalMethod>
<LessSpecificReturnStatement>
<code><![CDATA[str_replace(
[
'__DIR__',
'__FILE__',
],
[
"'" . dirname($this->filename) . "'",
"'" . $this->filename . "'",
],
$code,
)]]></code>
</LessSpecificReturnStatement>
<MissingThrowsDocblock>
<code><![CDATA[bootstrap]]></code>
</MissingThrowsDocblock>
<MoreSpecificReturnType>
<code><![CDATA[non-empty-string]]></code>
</MoreSpecificReturnType>
<PossiblyInvalidArgument>
<code><![CDATA[$sections['FILEEOF']]]></code>
</PossiblyInvalidArgument>
Expand Down Expand Up @@ -994,24 +1011,13 @@
<code><![CDATA[self::$directories]]></code>
</InvalidPropertyAssignmentValue>
</file>
<file src="src/Util/PHP/AbstractPhpProcess.php">
<RiskyTruthyFalsyComparison>
<code><![CDATA[!$file]]></code>
<code><![CDATA[!$file]]></code>
<code><![CDATA[$file]]></code>
</RiskyTruthyFalsyComparison>
</file>
<file src="src/Util/PHP/DefaultPhpProcess.php">
<file src="src/Util/PHP/DefaultPhpJobRunner.php">
<DocblockTypeContradiction>
<code><![CDATA[is_array($envVar)]]></code>
<code><![CDATA[is_array($value)]]></code>
</DocblockTypeContradiction>
<RedundantCondition>
<code><![CDATA[$_SERVER]]></code>
</RedundantCondition>
<RiskyTruthyFalsyComparison>
<code><![CDATA[!($this->tempFile = tempnam(sys_get_temp_dir(), 'phpunit_'))]]></code>
<code><![CDATA[$this->tempFile]]></code>
</RiskyTruthyFalsyComparison>
<TypeDoesNotContainNull>
<code><![CDATA[[]]]></code>
</TypeDoesNotContainNull>
Expand Down
156 changes: 143 additions & 13 deletions src/Framework/TestRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,37 @@
use function class_exists;
use function defined;
use function extension_loaded;
use function file_exists;
use function file_get_contents;
use function get_include_path;
use function hrtime;
use function restore_error_handler;
use function serialize;
use function set_error_handler;
use function sprintf;
use function sys_get_temp_dir;
use function tempnam;
use function trim;
use function unlink;
use function unserialize;
use function var_export;
use AssertionError;
use PHPUnit\Event;
use ErrorException;
use PHPUnit\Event\Code\TestMethodBuilder;
use PHPUnit\Event\Code\ThrowableBuilder;
use PHPUnit\Event\Facade;
use PHPUnit\Event\NoPreviousThrowableException;
use PHPUnit\Metadata\Api\CodeCoverage as CodeCoverageMetadataApi;
use PHPUnit\Metadata\Parser\Registry as MetadataRegistry;
use PHPUnit\Runner\CodeCoverage;
use PHPUnit\Runner\ErrorHandler;
use PHPUnit\TestRunner\TestResult\PassedTests;
use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry;
use PHPUnit\Util\GlobalState;
use PHPUnit\Util\PHP\AbstractPhpProcess;
use PHPUnit\Util\PHP\DefaultPhpJobRunner;
use PHPUnit\Util\PHP\PhpJob;
use PHPUnit\Util\PHP\PhpProcessException;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException;
use SebastianBergmann\CodeCoverage\InvalidArgumentException;
Expand Down Expand Up @@ -137,7 +149,7 @@ public function run(TestCase $test): void
if (!$error && !$failure && !$incomplete && !$skipped && !$risky &&
$this->configuration->requireCoverageMetadata() &&
!$this->hasCoverageMetadata($test::class, $test->name())) {
Event\Facade::emitter()->testConsideredRisky(
Facade::emitter()->testConsideredRisky(
$test->valueObjectForEvents(),
'This test does not define a code coverage target but is expected to do so',
);
Expand All @@ -162,7 +174,7 @@ public function run(TestCase $test): void
$test->name(),
);
} catch (InvalidCoversTargetException $cce) {
Event\Facade::emitter()->testTriggeredPhpunitWarning(
Facade::emitter()->testTriggeredPhpunitWarning(
$test->valueObjectForEvents(),
$cce->getMessage(),
);
Expand All @@ -178,7 +190,7 @@ public function run(TestCase $test): void
$linesToBeUsed,
);
} catch (UnintentionallyCoveredCodeException $cce) {
Event\Facade::emitter()->testConsideredRisky(
Facade::emitter()->testConsideredRisky(
$test->valueObjectForEvents(),
'This test executed code that is not listed as code to be covered or used:' .
PHP_EOL .
Expand All @@ -199,15 +211,15 @@ public function run(TestCase $test): void
$this->configuration->reportUselessTests() &&
!$test->doesNotPerformAssertions() &&
$test->numberOfAssertionsPerformed() === 0) {
Event\Facade::emitter()->testConsideredRisky(
Facade::emitter()->testConsideredRisky(
$test->valueObjectForEvents(),
'This test did not perform any assertions',
);
}

if ($test->doesNotPerformAssertions() &&
$test->numberOfAssertionsPerformed() > 0) {
Event\Facade::emitter()->testConsideredRisky(
Facade::emitter()->testConsideredRisky(
$test->valueObjectForEvents(),
sprintf(
'This test is not expected to perform assertions but performed %d assertion%s',
Expand All @@ -218,11 +230,11 @@ public function run(TestCase $test): void
}

if ($test->hasUnexpectedOutput()) {
Event\Facade::emitter()->testPrintedUnexpectedOutput($test->output());
Facade::emitter()->testPrintedUnexpectedOutput($test->output());
}

if ($this->configuration->disallowTestOutput() && $test->hasUnexpectedOutput()) {
Event\Facade::emitter()->testConsideredRisky(
Facade::emitter()->testConsideredRisky(
$test->valueObjectForEvents(),
sprintf(
'This test printed output: %s',
Expand All @@ -232,7 +244,7 @@ public function run(TestCase $test): void
}

if ($test->wasPrepared()) {
Event\Facade::emitter()->testFinished(
Facade::emitter()->testFinished(
$test->valueObjectForEvents(),
$test->numberOfAssertionsPerformed(),
);
Expand Down Expand Up @@ -337,8 +349,11 @@ public function runInSeparateProcess(TestCase $test, bool $runEntireClass, bool

$template->setVar($var);

$php = AbstractPhpProcess::factory();
$php->runTestJob($template->render(), $test, $processResultFile);
$code = $template->render();

assert($code !== '');

$this->runTestJob($code, $test, $processResultFile);

@unlink($serializedConfiguration);
}
Expand Down Expand Up @@ -431,7 +446,7 @@ private function runTestWithTimeout(TestCase $test): bool
try {
(new Invoker)->invoke([$test, 'runBare'], [], $_timeout);
} catch (TimeoutException) {
Event\Facade::emitter()->testConsideredRisky(
Facade::emitter()->testConsideredRisky(
$test->valueObjectForEvents(),
sprintf(
'This test was aborted after %d second%s',
Expand Down Expand Up @@ -472,4 +487,119 @@ private function shouldErrorHandlerBeUsed(TestCase $test): bool

return true;
}

/**
* @psalm-param non-empty-string $code
*
* @throws Exception
* @throws NoPreviousThrowableException
* @throws PhpProcessException
*/
private function runTestJob(string $code, Test $test, string $processResultFile): void
{
$_result = (new DefaultPhpJobRunner)->run(new PhpJob($code));

$processResult = '';

if (file_exists($processResultFile)) {
$processResult = file_get_contents($processResultFile);

@unlink($processResultFile);
}

$this->processChildResult(
$test,
$processResult,
$_result['stderr'],
);
}

/**
* @throws Exception
* @throws NoPreviousThrowableException
*/
private function processChildResult(Test $test, string $stdout, string $stderr): void
{
if (!empty($stderr)) {
$exception = new Exception(trim($stderr));

assert($test instanceof TestCase);

Facade::emitter()->testErrored(
TestMethodBuilder::fromTestCase($test),
ThrowableBuilder::from($exception),
);

return;
}

set_error_handler(
/**
* @throws ErrorException
*/
static function (int $errno, string $errstr, string $errfile, int $errline): never
{
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
},
);

try {
$childResult = unserialize($stdout);

restore_error_handler();

if ($childResult === false) {
$exception = new AssertionFailedError('Test was run in child process and ended unexpectedly');

assert($test instanceof TestCase);

Facade::emitter()->testErrored(
TestMethodBuilder::fromTestCase($test),
ThrowableBuilder::from($exception),
);

Facade::emitter()->testFinished(
TestMethodBuilder::fromTestCase($test),
0,
);
}
} catch (ErrorException $e) {
restore_error_handler();

$childResult = false;

$exception = new Exception(trim($stdout), 0, $e);

assert($test instanceof TestCase);

Facade::emitter()->testErrored(
TestMethodBuilder::fromTestCase($test),
ThrowableBuilder::from($exception),
);
}

if ($childResult !== false) {
if (!empty($childResult['output'])) {
$output = $childResult['output'];
}

Facade::instance()->forward($childResult['events']);
PassedTests::instance()->import($childResult['passedTests']);

assert($test instanceof TestCase);

$test->setResult($childResult['testResult']);
$test->addToAssertionCount($childResult['numAssertions']);

if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) {
CodeCoverage::instance()->codeCoverage()->merge(
$childResult['codeCoverage'],
);
}
}

if (!empty($output)) {
print $output;
}
}
}
Loading

0 comments on commit d34d3ea

Please sign in to comment.