14
14
use function class_exists ;
15
15
use function defined ;
16
16
use function extension_loaded ;
17
+ use function file_exists ;
18
+ use function file_get_contents ;
17
19
use function get_include_path ;
18
20
use function hrtime ;
21
+ use function restore_error_handler ;
19
22
use function serialize ;
23
+ use function set_error_handler ;
20
24
use function sprintf ;
21
25
use function sys_get_temp_dir ;
22
26
use function tempnam ;
27
+ use function trim ;
23
28
use function unlink ;
29
+ use function unserialize ;
24
30
use function var_export ;
25
31
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 ;
27
36
use PHPUnit \Event \NoPreviousThrowableException ;
28
37
use PHPUnit \Metadata \Api \CodeCoverage as CodeCoverageMetadataApi ;
29
38
use PHPUnit \Metadata \Parser \Registry as MetadataRegistry ;
30
39
use PHPUnit \Runner \CodeCoverage ;
31
40
use PHPUnit \Runner \ErrorHandler ;
41
+ use PHPUnit \TestRunner \TestResult \PassedTests ;
32
42
use PHPUnit \TextUI \Configuration \Configuration ;
33
43
use PHPUnit \TextUI \Configuration \Registry as ConfigurationRegistry ;
34
44
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 ;
36
48
use ReflectionClass ;
37
49
use SebastianBergmann \CodeCoverage \Exception as OriginalCodeCoverageException ;
38
50
use SebastianBergmann \CodeCoverage \InvalidArgumentException ;
@@ -137,7 +149,7 @@ public function run(TestCase $test): void
137
149
if (!$ error && !$ failure && !$ incomplete && !$ skipped && !$ risky &&
138
150
$ this ->configuration ->requireCoverageMetadata () &&
139
151
!$ this ->hasCoverageMetadata ($ test ::class, $ test ->name ())) {
140
- Event \ Facade::emitter ()->testConsideredRisky (
152
+ Facade::emitter ()->testConsideredRisky (
141
153
$ test ->valueObjectForEvents (),
142
154
'This test does not define a code coverage target but is expected to do so ' ,
143
155
);
@@ -162,7 +174,7 @@ public function run(TestCase $test): void
162
174
$ test ->name (),
163
175
);
164
176
} catch (InvalidCoversTargetException $ cce ) {
165
- Event \ Facade::emitter ()->testTriggeredPhpunitWarning (
177
+ Facade::emitter ()->testTriggeredPhpunitWarning (
166
178
$ test ->valueObjectForEvents (),
167
179
$ cce ->getMessage (),
168
180
);
@@ -178,7 +190,7 @@ public function run(TestCase $test): void
178
190
$ linesToBeUsed ,
179
191
);
180
192
} catch (UnintentionallyCoveredCodeException $ cce ) {
181
- Event \ Facade::emitter ()->testConsideredRisky (
193
+ Facade::emitter ()->testConsideredRisky (
182
194
$ test ->valueObjectForEvents (),
183
195
'This test executed code that is not listed as code to be covered or used: ' .
184
196
PHP_EOL .
@@ -199,15 +211,15 @@ public function run(TestCase $test): void
199
211
$ this ->configuration ->reportUselessTests () &&
200
212
!$ test ->doesNotPerformAssertions () &&
201
213
$ test ->numberOfAssertionsPerformed () === 0 ) {
202
- Event \ Facade::emitter ()->testConsideredRisky (
214
+ Facade::emitter ()->testConsideredRisky (
203
215
$ test ->valueObjectForEvents (),
204
216
'This test did not perform any assertions ' ,
205
217
);
206
218
}
207
219
208
220
if ($ test ->doesNotPerformAssertions () &&
209
221
$ test ->numberOfAssertionsPerformed () > 0 ) {
210
- Event \ Facade::emitter ()->testConsideredRisky (
222
+ Facade::emitter ()->testConsideredRisky (
211
223
$ test ->valueObjectForEvents (),
212
224
sprintf (
213
225
'This test is not expected to perform assertions but performed %d assertion%s ' ,
@@ -218,11 +230,11 @@ public function run(TestCase $test): void
218
230
}
219
231
220
232
if ($ test ->hasUnexpectedOutput ()) {
221
- Event \ Facade::emitter ()->testPrintedUnexpectedOutput ($ test ->output ());
233
+ Facade::emitter ()->testPrintedUnexpectedOutput ($ test ->output ());
222
234
}
223
235
224
236
if ($ this ->configuration ->disallowTestOutput () && $ test ->hasUnexpectedOutput ()) {
225
- Event \ Facade::emitter ()->testConsideredRisky (
237
+ Facade::emitter ()->testConsideredRisky (
226
238
$ test ->valueObjectForEvents (),
227
239
sprintf (
228
240
'This test printed output: %s ' ,
@@ -232,7 +244,7 @@ public function run(TestCase $test): void
232
244
}
233
245
234
246
if ($ test ->wasPrepared ()) {
235
- Event \ Facade::emitter ()->testFinished (
247
+ Facade::emitter ()->testFinished (
236
248
$ test ->valueObjectForEvents (),
237
249
$ test ->numberOfAssertionsPerformed (),
238
250
);
@@ -337,8 +349,11 @@ public function runInSeparateProcess(TestCase $test, bool $runEntireClass, bool
337
349
338
350
$ template ->setVar ($ var );
339
351
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 );
342
357
343
358
@unlink ($ serializedConfiguration );
344
359
}
@@ -431,7 +446,7 @@ private function runTestWithTimeout(TestCase $test): bool
431
446
try {
432
447
(new Invoker )->invoke ([$ test , 'runBare ' ], [], $ _timeout );
433
448
} catch (TimeoutException ) {
434
- Event \ Facade::emitter ()->testConsideredRisky (
449
+ Facade::emitter ()->testConsideredRisky (
435
450
$ test ->valueObjectForEvents (),
436
451
sprintf (
437
452
'This test was aborted after %d second%s ' ,
@@ -472,4 +487,119 @@ private function shouldErrorHandlerBeUsed(TestCase $test): bool
472
487
473
488
return true ;
474
489
}
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
+ }
475
605
}
0 commit comments