Skip to content

Commit ab1bf62

Browse files
Extract class
1 parent e465e8b commit ab1bf62

File tree

3 files changed

+160
-88
lines changed

3 files changed

+160
-88
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework;
11+
12+
use function assert;
13+
use function trim;
14+
use function unserialize;
15+
use PHPUnit\Event\Code\TestMethodBuilder;
16+
use PHPUnit\Event\Code\ThrowableBuilder;
17+
use PHPUnit\Event\Emitter;
18+
use PHPUnit\Event\Facade;
19+
use PHPUnit\Runner\CodeCoverage;
20+
use PHPUnit\TestRunner\TestResult\PassedTests;
21+
22+
final readonly class ChildProcessResultProcessor
23+
{
24+
private Facade $eventFacade;
25+
private Emitter $emitter;
26+
private PassedTests $passedTests;
27+
private CodeCoverage $codeCoverage;
28+
29+
public function __construct(Facade $eventFacade, Emitter $emitter, PassedTests $passedTests, CodeCoverage $codeCoverage)
30+
{
31+
$this->eventFacade = $eventFacade;
32+
$this->emitter = $emitter;
33+
$this->passedTests = $passedTests;
34+
$this->codeCoverage = $codeCoverage;
35+
}
36+
37+
public function process(Test $test, string $serializedProcessResult, string $stderr): void
38+
{
39+
if (!empty($stderr)) {
40+
$exception = new Exception(trim($stderr));
41+
42+
assert($test instanceof TestCase);
43+
44+
$this->emitter->testErrored(
45+
TestMethodBuilder::fromTestCase($test),
46+
ThrowableBuilder::from($exception),
47+
);
48+
49+
return;
50+
}
51+
52+
$childResult = @unserialize($serializedProcessResult);
53+
54+
if ($childResult === false) {
55+
$exception = new AssertionFailedError('Test was run in child process and ended unexpectedly');
56+
57+
assert($test instanceof TestCase);
58+
59+
$this->emitter->testErrored(
60+
TestMethodBuilder::fromTestCase($test),
61+
ThrowableBuilder::from($exception),
62+
);
63+
64+
$this->emitter->testFinished(
65+
TestMethodBuilder::fromTestCase($test),
66+
0,
67+
);
68+
69+
return;
70+
}
71+
72+
$this->eventFacade->forward($childResult['events']);
73+
$this->passedTests->import($childResult['passedTests']);
74+
75+
assert($test instanceof TestCase);
76+
77+
$test->setResult($childResult['testResult']);
78+
$test->addToAssertionCount($childResult['numAssertions']);
79+
80+
if (!$this->codeCoverage->isActive()) {
81+
return;
82+
}
83+
84+
// @codeCoverageIgnoreStart
85+
if (!$childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) {
86+
return;
87+
}
88+
89+
CodeCoverage::instance()->codeCoverage()->merge(
90+
$childResult['codeCoverage'],
91+
);
92+
// @codeCoverageIgnoreEnd
93+
}
94+
}

src/Framework/TestRunner/SeparateProcessTestRunner.php

Lines changed: 8 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,12 @@
1515
use function get_include_path;
1616
use function hrtime;
1717
use function is_file;
18-
use function restore_error_handler;
1918
use function serialize;
20-
use function set_error_handler;
2119
use function sys_get_temp_dir;
2220
use function tempnam;
23-
use function trim;
2421
use function unlink;
2522
use function unserialize;
2623
use function var_export;
27-
use ErrorException;
28-
use PHPUnit\Event\Code\TestMethodBuilder;
29-
use PHPUnit\Event\Code\ThrowableBuilder;
3024
use PHPUnit\Event\Facade;
3125
use PHPUnit\Event\NoPreviousThrowableException;
3226
use PHPUnit\Runner\CodeCoverage;
@@ -176,94 +170,20 @@ private function runTestJob(string $code, Test $test, string $processResultFile)
176170
@unlink($processResultFile);
177171
}
178172

179-
$this->processChildResult(
173+
$processor = new ChildProcessResultProcessor(
174+
Facade::instance(),
175+
Facade::emitter(),
176+
PassedTests::instance(),
177+
CodeCoverage::instance(),
178+
);
179+
180+
$processor->process(
180181
$test,
181182
$processResult,
182183
$result->stderr(),
183184
);
184185
}
185186

186-
/**
187-
* @throws Exception
188-
* @throws NoPreviousThrowableException
189-
*/
190-
private function processChildResult(Test $test, string $stdout, string $stderr): void
191-
{
192-
if (!empty($stderr)) {
193-
$exception = new Exception(trim($stderr));
194-
195-
assert($test instanceof TestCase);
196-
197-
Facade::emitter()->testErrored(
198-
TestMethodBuilder::fromTestCase($test),
199-
ThrowableBuilder::from($exception),
200-
);
201-
202-
return;
203-
}
204-
205-
set_error_handler(
206-
/**
207-
* @throws ErrorException
208-
*/
209-
static function (int $errno, string $errstr, string $errfile, int $errline): never
210-
{
211-
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
212-
},
213-
);
214-
215-
try {
216-
$childResult = unserialize($stdout);
217-
218-
restore_error_handler();
219-
220-
if ($childResult === false) {
221-
$exception = new AssertionFailedError('Test was run in child process and ended unexpectedly');
222-
223-
assert($test instanceof TestCase);
224-
225-
Facade::emitter()->testErrored(
226-
TestMethodBuilder::fromTestCase($test),
227-
ThrowableBuilder::from($exception),
228-
);
229-
230-
Facade::emitter()->testFinished(
231-
TestMethodBuilder::fromTestCase($test),
232-
0,
233-
);
234-
}
235-
} catch (ErrorException $e) {
236-
restore_error_handler();
237-
238-
$childResult = false;
239-
240-
$exception = new Exception(trim($stdout), 0, $e);
241-
242-
assert($test instanceof TestCase);
243-
244-
Facade::emitter()->testErrored(
245-
TestMethodBuilder::fromTestCase($test),
246-
ThrowableBuilder::from($exception),
247-
);
248-
}
249-
250-
if ($childResult !== false) {
251-
Facade::instance()->forward($childResult['events']);
252-
PassedTests::instance()->import($childResult['passedTests']);
253-
254-
assert($test instanceof TestCase);
255-
256-
$test->setResult($childResult['testResult']);
257-
$test->addToAssertionCount($childResult['numAssertions']);
258-
259-
if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) {
260-
CodeCoverage::instance()->codeCoverage()->merge(
261-
$childResult['codeCoverage'],
262-
);
263-
}
264-
}
265-
}
266-
267187
/**
268188
* @throws ProcessIsolationException
269189
*/
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework;
11+
12+
use PHPUnit\Event\Emitter;
13+
use PHPUnit\Event\Facade;
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\Attributes\Small;
16+
use PHPUnit\Framework\Attributes\TestDox;
17+
use PHPUnit\Runner\CodeCoverage;
18+
use PHPUnit\TestFixture\Success;
19+
use PHPUnit\TestRunner\TestResult\PassedTests;
20+
21+
#[CoversClass(ChildProcessResultProcessor::class)]
22+
#[Small]
23+
final class ChildProcessResultProcessorTest extends TestCase
24+
{
25+
#[TestDox('Emits Test\Errored event when standard output is not empty')]
26+
public function testEmitsErrorEventWhenStderrIsNotEmpty(): void
27+
{
28+
$emitter = $this->createMock(Emitter::class);
29+
30+
$emitter
31+
->expects($this->once())
32+
->method('testErrored');
33+
34+
$this->processor($emitter)->process(new Success('testOne'), '', 'error');
35+
}
36+
37+
#[TestDox('Emits Test\Errored event when process result cannot be unserialized')]
38+
public function testEmitsErrorEventWhenProcessResultCannotBeUnserialized(): void
39+
{
40+
$emitter = $this->createMock(Emitter::class);
41+
42+
$emitter
43+
->expects($this->once())
44+
->method('testErrored');
45+
46+
$this->processor($emitter)->process(new Success('testOne'), '', '');
47+
}
48+
49+
private function processor(Emitter $emitter): ChildProcessResultProcessor
50+
{
51+
return new ChildProcessResultProcessor(
52+
new Facade,
53+
$emitter,
54+
new PassedTests,
55+
new CodeCoverage,
56+
);
57+
}
58+
}

0 commit comments

Comments
 (0)