Skip to content

Commit d34d3ea

Browse files
Refactor
1 parent 833fa23 commit d34d3ea

File tree

8 files changed

+579
-515
lines changed

8 files changed

+579
-515
lines changed

.psalm/baseline.xml

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@
658658
</file>
659659
<file src="src/Runner/PhptTestCase.php">
660660
<ArgumentTypeCoercion>
661+
<code><![CDATA[$arguments]]></code>
661662
<code><![CDATA[$message]]></code>
662663
</ArgumentTypeCoercion>
663664
<InternalClass>
@@ -668,9 +669,25 @@
668669
<code><![CDATA[RawCodeCoverageData::fromXdebugWithoutPathCoverage([])]]></code>
669670
<code><![CDATA[RawCodeCoverageData::fromXdebugWithoutPathCoverage([])]]></code>
670671
</InternalMethod>
672+
<LessSpecificReturnStatement>
673+
<code><![CDATA[str_replace(
674+
[
675+
'__DIR__',
676+
'__FILE__',
677+
],
678+
[
679+
"'" . dirname($this->filename) . "'",
680+
"'" . $this->filename . "'",
681+
],
682+
$code,
683+
)]]></code>
684+
</LessSpecificReturnStatement>
671685
<MissingThrowsDocblock>
672686
<code><![CDATA[bootstrap]]></code>
673687
</MissingThrowsDocblock>
688+
<MoreSpecificReturnType>
689+
<code><![CDATA[non-empty-string]]></code>
690+
</MoreSpecificReturnType>
674691
<PossiblyInvalidArgument>
675692
<code><![CDATA[$sections['FILEEOF']]]></code>
676693
</PossiblyInvalidArgument>
@@ -994,24 +1011,13 @@
9941011
<code><![CDATA[self::$directories]]></code>
9951012
</InvalidPropertyAssignmentValue>
9961013
</file>
997-
<file src="src/Util/PHP/AbstractPhpProcess.php">
998-
<RiskyTruthyFalsyComparison>
999-
<code><![CDATA[!$file]]></code>
1000-
<code><![CDATA[!$file]]></code>
1001-
<code><![CDATA[$file]]></code>
1002-
</RiskyTruthyFalsyComparison>
1003-
</file>
1004-
<file src="src/Util/PHP/DefaultPhpProcess.php">
1014+
<file src="src/Util/PHP/DefaultPhpJobRunner.php">
10051015
<DocblockTypeContradiction>
1006-
<code><![CDATA[is_array($envVar)]]></code>
1016+
<code><![CDATA[is_array($value)]]></code>
10071017
</DocblockTypeContradiction>
10081018
<RedundantCondition>
10091019
<code><![CDATA[$_SERVER]]></code>
10101020
</RedundantCondition>
1011-
<RiskyTruthyFalsyComparison>
1012-
<code><![CDATA[!($this->tempFile = tempnam(sys_get_temp_dir(), 'phpunit_'))]]></code>
1013-
<code><![CDATA[$this->tempFile]]></code>
1014-
</RiskyTruthyFalsyComparison>
10151021
<TypeDoesNotContainNull>
10161022
<code><![CDATA[[]]]></code>
10171023
</TypeDoesNotContainNull>

src/Framework/TestRunner.php

Lines changed: 143 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,37 @@
1414
use function class_exists;
1515
use function defined;
1616
use function extension_loaded;
17+
use function file_exists;
18+
use function file_get_contents;
1719
use function get_include_path;
1820
use function hrtime;
21+
use function restore_error_handler;
1922
use function serialize;
23+
use function set_error_handler;
2024
use function sprintf;
2125
use function sys_get_temp_dir;
2226
use function tempnam;
27+
use function trim;
2328
use function unlink;
29+
use function unserialize;
2430
use function var_export;
2531
use AssertionError;
26-
use PHPUnit\Event;
32+
use ErrorException;
33+
use PHPUnit\Event\Code\TestMethodBuilder;
34+
use PHPUnit\Event\Code\ThrowableBuilder;
35+
use PHPUnit\Event\Facade;
2736
use PHPUnit\Event\NoPreviousThrowableException;
2837
use PHPUnit\Metadata\Api\CodeCoverage as CodeCoverageMetadataApi;
2938
use PHPUnit\Metadata\Parser\Registry as MetadataRegistry;
3039
use PHPUnit\Runner\CodeCoverage;
3140
use PHPUnit\Runner\ErrorHandler;
41+
use PHPUnit\TestRunner\TestResult\PassedTests;
3242
use PHPUnit\TextUI\Configuration\Configuration;
3343
use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry;
3444
use PHPUnit\Util\GlobalState;
35-
use PHPUnit\Util\PHP\AbstractPhpProcess;
45+
use PHPUnit\Util\PHP\DefaultPhpJobRunner;
46+
use PHPUnit\Util\PHP\PhpJob;
47+
use PHPUnit\Util\PHP\PhpProcessException;
3648
use ReflectionClass;
3749
use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException;
3850
use SebastianBergmann\CodeCoverage\InvalidArgumentException;
@@ -137,7 +149,7 @@ public function run(TestCase $test): void
137149
if (!$error && !$failure && !$incomplete && !$skipped && !$risky &&
138150
$this->configuration->requireCoverageMetadata() &&
139151
!$this->hasCoverageMetadata($test::class, $test->name())) {
140-
Event\Facade::emitter()->testConsideredRisky(
152+
Facade::emitter()->testConsideredRisky(
141153
$test->valueObjectForEvents(),
142154
'This test does not define a code coverage target but is expected to do so',
143155
);
@@ -162,7 +174,7 @@ public function run(TestCase $test): void
162174
$test->name(),
163175
);
164176
} catch (InvalidCoversTargetException $cce) {
165-
Event\Facade::emitter()->testTriggeredPhpunitWarning(
177+
Facade::emitter()->testTriggeredPhpunitWarning(
166178
$test->valueObjectForEvents(),
167179
$cce->getMessage(),
168180
);
@@ -178,7 +190,7 @@ public function run(TestCase $test): void
178190
$linesToBeUsed,
179191
);
180192
} catch (UnintentionallyCoveredCodeException $cce) {
181-
Event\Facade::emitter()->testConsideredRisky(
193+
Facade::emitter()->testConsideredRisky(
182194
$test->valueObjectForEvents(),
183195
'This test executed code that is not listed as code to be covered or used:' .
184196
PHP_EOL .
@@ -199,15 +211,15 @@ public function run(TestCase $test): void
199211
$this->configuration->reportUselessTests() &&
200212
!$test->doesNotPerformAssertions() &&
201213
$test->numberOfAssertionsPerformed() === 0) {
202-
Event\Facade::emitter()->testConsideredRisky(
214+
Facade::emitter()->testConsideredRisky(
203215
$test->valueObjectForEvents(),
204216
'This test did not perform any assertions',
205217
);
206218
}
207219

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

220232
if ($test->hasUnexpectedOutput()) {
221-
Event\Facade::emitter()->testPrintedUnexpectedOutput($test->output());
233+
Facade::emitter()->testPrintedUnexpectedOutput($test->output());
222234
}
223235

224236
if ($this->configuration->disallowTestOutput() && $test->hasUnexpectedOutput()) {
225-
Event\Facade::emitter()->testConsideredRisky(
237+
Facade::emitter()->testConsideredRisky(
226238
$test->valueObjectForEvents(),
227239
sprintf(
228240
'This test printed output: %s',
@@ -232,7 +244,7 @@ public function run(TestCase $test): void
232244
}
233245

234246
if ($test->wasPrepared()) {
235-
Event\Facade::emitter()->testFinished(
247+
Facade::emitter()->testFinished(
236248
$test->valueObjectForEvents(),
237249
$test->numberOfAssertionsPerformed(),
238250
);
@@ -337,8 +349,11 @@ public function runInSeparateProcess(TestCase $test, bool $runEntireClass, bool
337349

338350
$template->setVar($var);
339351

340-
$php = AbstractPhpProcess::factory();
341-
$php->runTestJob($template->render(), $test, $processResultFile);
352+
$code = $template->render();
353+
354+
assert($code !== '');
355+
356+
$this->runTestJob($code, $test, $processResultFile);
342357

343358
@unlink($serializedConfiguration);
344359
}
@@ -431,7 +446,7 @@ private function runTestWithTimeout(TestCase $test): bool
431446
try {
432447
(new Invoker)->invoke([$test, 'runBare'], [], $_timeout);
433448
} catch (TimeoutException) {
434-
Event\Facade::emitter()->testConsideredRisky(
449+
Facade::emitter()->testConsideredRisky(
435450
$test->valueObjectForEvents(),
436451
sprintf(
437452
'This test was aborted after %d second%s',
@@ -472,4 +487,119 @@ private function shouldErrorHandlerBeUsed(TestCase $test): bool
472487

473488
return true;
474489
}
490+
491+
/**
492+
* @psalm-param non-empty-string $code
493+
*
494+
* @throws Exception
495+
* @throws NoPreviousThrowableException
496+
* @throws PhpProcessException
497+
*/
498+
private function runTestJob(string $code, Test $test, string $processResultFile): void
499+
{
500+
$_result = (new DefaultPhpJobRunner)->run(new PhpJob($code));
501+
502+
$processResult = '';
503+
504+
if (file_exists($processResultFile)) {
505+
$processResult = file_get_contents($processResultFile);
506+
507+
@unlink($processResultFile);
508+
}
509+
510+
$this->processChildResult(
511+
$test,
512+
$processResult,
513+
$_result['stderr'],
514+
);
515+
}
516+
517+
/**
518+
* @throws Exception
519+
* @throws NoPreviousThrowableException
520+
*/
521+
private function processChildResult(Test $test, string $stdout, string $stderr): void
522+
{
523+
if (!empty($stderr)) {
524+
$exception = new Exception(trim($stderr));
525+
526+
assert($test instanceof TestCase);
527+
528+
Facade::emitter()->testErrored(
529+
TestMethodBuilder::fromTestCase($test),
530+
ThrowableBuilder::from($exception),
531+
);
532+
533+
return;
534+
}
535+
536+
set_error_handler(
537+
/**
538+
* @throws ErrorException
539+
*/
540+
static function (int $errno, string $errstr, string $errfile, int $errline): never
541+
{
542+
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
543+
},
544+
);
545+
546+
try {
547+
$childResult = unserialize($stdout);
548+
549+
restore_error_handler();
550+
551+
if ($childResult === false) {
552+
$exception = new AssertionFailedError('Test was run in child process and ended unexpectedly');
553+
554+
assert($test instanceof TestCase);
555+
556+
Facade::emitter()->testErrored(
557+
TestMethodBuilder::fromTestCase($test),
558+
ThrowableBuilder::from($exception),
559+
);
560+
561+
Facade::emitter()->testFinished(
562+
TestMethodBuilder::fromTestCase($test),
563+
0,
564+
);
565+
}
566+
} catch (ErrorException $e) {
567+
restore_error_handler();
568+
569+
$childResult = false;
570+
571+
$exception = new Exception(trim($stdout), 0, $e);
572+
573+
assert($test instanceof TestCase);
574+
575+
Facade::emitter()->testErrored(
576+
TestMethodBuilder::fromTestCase($test),
577+
ThrowableBuilder::from($exception),
578+
);
579+
}
580+
581+
if ($childResult !== false) {
582+
if (!empty($childResult['output'])) {
583+
$output = $childResult['output'];
584+
}
585+
586+
Facade::instance()->forward($childResult['events']);
587+
PassedTests::instance()->import($childResult['passedTests']);
588+
589+
assert($test instanceof TestCase);
590+
591+
$test->setResult($childResult['testResult']);
592+
$test->addToAssertionCount($childResult['numAssertions']);
593+
594+
if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) {
595+
CodeCoverage::instance()->codeCoverage()->merge(
596+
$childResult['codeCoverage'],
597+
);
598+
}
599+
}
600+
601+
if (!empty($output)) {
602+
print $output;
603+
}
604+
}
475605
}

0 commit comments

Comments
 (0)