Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of PHPUnit.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
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 const PHP_EOL;
13
use function class_exists;
14
use function count;
15
use function extension_loaded;
16
use function function_exists;
17
use function get_class;
18
use function sprintf;
19
use function xdebug_get_monitored_functions;
20
use function xdebug_is_debugger_active;
21
use function xdebug_start_function_monitor;
22
use function xdebug_stop_function_monitor;
23
use AssertionError;
24
use Countable;
25
use Error;
26
use PHPUnit\Util\ErrorHandler;
27
use PHPUnit\Util\ExcludeList;
28
use PHPUnit\Util\Printer;
29
use PHPUnit\Util\Test as TestUtil;
30
use ReflectionClass;
31
use ReflectionException;
32
use SebastianBergmann\CodeCoverage\CodeCoverage;
33
use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException;
34
use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
35
use SebastianBergmann\Invoker\Invoker;
36
use SebastianBergmann\Invoker\TimeoutException;
37
use SebastianBergmann\ResourceOperations\ResourceOperations;
38
use SebastianBergmann\Timer\Timer;
39
use Throwable;
40
 
41
/**
42
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
43
 */
44
final class TestResult implements Countable
45
{
46
    /**
47
     * @var array
48
     */
49
    private $passed = [];
50
 
51
    /**
52
     * @var array<string>
53
     */
54
    private $passedTestClasses = [];
55
 
56
    /**
57
     * @var bool
58
     */
59
    private $currentTestSuiteFailed = false;
60
 
61
    /**
62
     * @var TestFailure[]
63
     */
64
    private $errors = [];
65
 
66
    /**
67
     * @var TestFailure[]
68
     */
69
    private $failures = [];
70
 
71
    /**
72
     * @var TestFailure[]
73
     */
74
    private $warnings = [];
75
 
76
    /**
77
     * @var TestFailure[]
78
     */
79
    private $notImplemented = [];
80
 
81
    /**
82
     * @var TestFailure[]
83
     */
84
    private $risky = [];
85
 
86
    /**
87
     * @var TestFailure[]
88
     */
89
    private $skipped = [];
90
 
91
    /**
92
     * @deprecated Use the `TestHook` interfaces instead
93
     *
94
     * @var TestListener[]
95
     */
96
    private $listeners = [];
97
 
98
    /**
99
     * @var int
100
     */
101
    private $runTests = 0;
102
 
103
    /**
104
     * @var float
105
     */
106
    private $time = 0;
107
 
108
    /**
109
     * Code Coverage information.
110
     *
111
     * @var CodeCoverage
112
     */
113
    private $codeCoverage;
114
 
115
    /**
116
     * @var bool
117
     */
118
    private $convertDeprecationsToExceptions = false;
119
 
120
    /**
121
     * @var bool
122
     */
123
    private $convertErrorsToExceptions = true;
124
 
125
    /**
126
     * @var bool
127
     */
128
    private $convertNoticesToExceptions = true;
129
 
130
    /**
131
     * @var bool
132
     */
133
    private $convertWarningsToExceptions = true;
134
 
135
    /**
136
     * @var bool
137
     */
138
    private $stop = false;
139
 
140
    /**
141
     * @var bool
142
     */
143
    private $stopOnError = false;
144
 
145
    /**
146
     * @var bool
147
     */
148
    private $stopOnFailure = false;
149
 
150
    /**
151
     * @var bool
152
     */
153
    private $stopOnWarning = false;
154
 
155
    /**
156
     * @var bool
157
     */
158
    private $beStrictAboutTestsThatDoNotTestAnything = true;
159
 
160
    /**
161
     * @var bool
162
     */
163
    private $beStrictAboutOutputDuringTests = false;
164
 
165
    /**
166
     * @var bool
167
     */
168
    private $beStrictAboutTodoAnnotatedTests = false;
169
 
170
    /**
171
     * @var bool
172
     */
173
    private $beStrictAboutResourceUsageDuringSmallTests = false;
174
 
175
    /**
176
     * @var bool
177
     */
178
    private $enforceTimeLimit = false;
179
 
180
    /**
181
     * @var bool
182
     */
183
    private $forceCoversAnnotation = false;
184
 
185
    /**
186
     * @var int
187
     */
188
    private $timeoutForSmallTests = 1;
189
 
190
    /**
191
     * @var int
192
     */
193
    private $timeoutForMediumTests = 10;
194
 
195
    /**
196
     * @var int
197
     */
198
    private $timeoutForLargeTests = 60;
199
 
200
    /**
201
     * @var bool
202
     */
203
    private $stopOnRisky = false;
204
 
205
    /**
206
     * @var bool
207
     */
208
    private $stopOnIncomplete = false;
209
 
210
    /**
211
     * @var bool
212
     */
213
    private $stopOnSkipped = false;
214
 
215
    /**
216
     * @var bool
217
     */
218
    private $lastTestFailed = false;
219
 
220
    /**
221
     * @var int
222
     */
223
    private $defaultTimeLimit = 0;
224
 
225
    /**
226
     * @var bool
227
     */
228
    private $stopOnDefect = false;
229
 
230
    /**
231
     * @var bool
232
     */
233
    private $registerMockObjectsFromTestArgumentsRecursively = false;
234
 
235
    /**
236
     * @deprecated Use the `TestHook` interfaces instead
237
     *
238
     * @codeCoverageIgnore
239
     *
240
     * Registers a TestListener.
241
     */
242
    public function addListener(TestListener $listener): void
243
    {
244
        $this->listeners[] = $listener;
245
    }
246
 
247
    /**
248
     * @deprecated Use the `TestHook` interfaces instead
249
     *
250
     * @codeCoverageIgnore
251
     *
252
     * Unregisters a TestListener.
253
     */
254
    public function removeListener(TestListener $listener): void
255
    {
256
        foreach ($this->listeners as $key => $_listener) {
257
            if ($listener === $_listener) {
258
                unset($this->listeners[$key]);
259
            }
260
        }
261
    }
262
 
263
    /**
264
     * @deprecated Use the `TestHook` interfaces instead
265
     *
266
     * @codeCoverageIgnore
267
     *
268
     * Flushes all flushable TestListeners.
269
     */
270
    public function flushListeners(): void
271
    {
272
        foreach ($this->listeners as $listener) {
273
            if ($listener instanceof Printer) {
274
                $listener->flush();
275
            }
276
        }
277
    }
278
 
279
    /**
280
     * Adds an error to the list of errors.
281
     */
282
    public function addError(Test $test, Throwable $t, float $time): void
283
    {
284
        if ($t instanceof RiskyTestError) {
285
            $this->recordRisky($test, $t);
286
 
287
            $notifyMethod = 'addRiskyTest';
288
 
289
            if ($test instanceof TestCase) {
290
                $test->markAsRisky();
291
            }
292
 
293
            if ($this->stopOnRisky || $this->stopOnDefect) {
294
                $this->stop();
295
            }
296
        } elseif ($t instanceof IncompleteTest) {
297
            $this->recordNotImplemented($test, $t);
298
 
299
            $notifyMethod = 'addIncompleteTest';
300
 
301
            if ($this->stopOnIncomplete) {
302
                $this->stop();
303
            }
304
        } elseif ($t instanceof SkippedTest) {
305
            $this->recordSkipped($test, $t);
306
 
307
            $notifyMethod = 'addSkippedTest';
308
 
309
            if ($this->stopOnSkipped) {
310
                $this->stop();
311
            }
312
        } else {
313
            $this->recordError($test, $t);
314
 
315
            $notifyMethod = 'addError';
316
 
317
            if ($this->stopOnError || $this->stopOnFailure) {
318
                $this->stop();
319
            }
320
        }
321
 
322
        // @see https://github.com/sebastianbergmann/phpunit/issues/1953
323
        if ($t instanceof Error) {
324
            $t = new ExceptionWrapper($t);
325
        }
326
 
327
        foreach ($this->listeners as $listener) {
328
            $listener->{$notifyMethod}($test, $t, $time);
329
        }
330
 
331
        $this->lastTestFailed = true;
332
        $this->time += $time;
333
    }
334
 
335
    /**
336
     * Adds a warning to the list of warnings.
337
     * The passed in exception caused the warning.
338
     */
339
    public function addWarning(Test $test, Warning $e, float $time): void
340
    {
341
        if ($this->stopOnWarning || $this->stopOnDefect) {
342
            $this->stop();
343
        }
344
 
345
        $this->recordWarning($test, $e);
346
 
347
        foreach ($this->listeners as $listener) {
348
            $listener->addWarning($test, $e, $time);
349
        }
350
 
351
        $this->time += $time;
352
    }
353
 
354
    /**
355
     * Adds a failure to the list of failures.
356
     * The passed in exception caused the failure.
357
     */
358
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
359
    {
360
        if ($e instanceof RiskyTestError || $e instanceof OutputError) {
361
            $this->recordRisky($test, $e);
362
 
363
            $notifyMethod = 'addRiskyTest';
364
 
365
            if ($test instanceof TestCase) {
366
                $test->markAsRisky();
367
            }
368
 
369
            if ($this->stopOnRisky || $this->stopOnDefect) {
370
                $this->stop();
371
            }
372
        } elseif ($e instanceof IncompleteTest) {
373
            $this->recordNotImplemented($test, $e);
374
 
375
            $notifyMethod = 'addIncompleteTest';
376
 
377
            if ($this->stopOnIncomplete) {
378
                $this->stop();
379
            }
380
        } elseif ($e instanceof SkippedTest) {
381
            $this->recordSkipped($test, $e);
382
 
383
            $notifyMethod = 'addSkippedTest';
384
 
385
            if ($this->stopOnSkipped) {
386
                $this->stop();
387
            }
388
        } else {
389
            $this->failures[] = new TestFailure($test, $e);
390
            $notifyMethod     = 'addFailure';
391
 
392
            if ($this->stopOnFailure || $this->stopOnDefect) {
393
                $this->stop();
394
            }
395
        }
396
 
397
        foreach ($this->listeners as $listener) {
398
            $listener->{$notifyMethod}($test, $e, $time);
399
        }
400
 
401
        $this->lastTestFailed = true;
402
        $this->time += $time;
403
    }
404
 
405
    /**
406
     * Informs the result that a test suite will be started.
407
     */
408
    public function startTestSuite(TestSuite $suite): void
409
    {
410
        $this->currentTestSuiteFailed = false;
411
 
412
        foreach ($this->listeners as $listener) {
413
            $listener->startTestSuite($suite);
414
        }
415
    }
416
 
417
    /**
418
     * Informs the result that a test suite was completed.
419
     */
420
    public function endTestSuite(TestSuite $suite): void
421
    {
422
        if (!$this->currentTestSuiteFailed) {
423
            $this->passedTestClasses[] = $suite->getName();
424
        }
425
 
426
        foreach ($this->listeners as $listener) {
427
            $listener->endTestSuite($suite);
428
        }
429
    }
430
 
431
    /**
432
     * Informs the result that a test will be started.
433
     */
434
    public function startTest(Test $test): void
435
    {
436
        $this->lastTestFailed = false;
437
        $this->runTests += count($test);
438
 
439
        foreach ($this->listeners as $listener) {
440
            $listener->startTest($test);
441
        }
442
    }
443
 
444
    /**
445
     * Informs the result that a test was completed.
446
     *
447
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
448
     */
449
    public function endTest(Test $test, float $time): void
450
    {
451
        foreach ($this->listeners as $listener) {
452
            $listener->endTest($test, $time);
453
        }
454
 
455
        if (!$this->lastTestFailed && $test instanceof TestCase) {
456
            $class = get_class($test);
457
            $key   = $class . '::' . $test->getName();
458
 
459
            $this->passed[$key] = [
460
                'result' => $test->getResult(),
461
                'size'   => TestUtil::getSize(
462
                    $class,
463
                    $test->getName(false)
464
                ),
465
            ];
466
 
467
            $this->time += $time;
468
        }
469
 
470
        if ($this->lastTestFailed && $test instanceof TestCase) {
471
            $this->currentTestSuiteFailed = true;
472
        }
473
    }
474
 
475
    /**
476
     * Returns true if no risky test occurred.
477
     */
478
    public function allHarmless(): bool
479
    {
480
        return $this->riskyCount() === 0;
481
    }
482
 
483
    /**
484
     * Gets the number of risky tests.
485
     */
486
    public function riskyCount(): int
487
    {
488
        return count($this->risky);
489
    }
490
 
491
    /**
492
     * Returns true if no incomplete test occurred.
493
     */
494
    public function allCompletelyImplemented(): bool
495
    {
496
        return $this->notImplementedCount() === 0;
497
    }
498
 
499
    /**
500
     * Gets the number of incomplete tests.
501
     */
502
    public function notImplementedCount(): int
503
    {
504
        return count($this->notImplemented);
505
    }
506
 
507
    /**
508
     * Returns an array of TestFailure objects for the risky tests.
509
     *
510
     * @return TestFailure[]
511
     */
512
    public function risky(): array
513
    {
514
        return $this->risky;
515
    }
516
 
517
    /**
518
     * Returns an array of TestFailure objects for the incomplete tests.
519
     *
520
     * @return TestFailure[]
521
     */
522
    public function notImplemented(): array
523
    {
524
        return $this->notImplemented;
525
    }
526
 
527
    /**
528
     * Returns true if no test has been skipped.
529
     */
530
    public function noneSkipped(): bool
531
    {
532
        return $this->skippedCount() === 0;
533
    }
534
 
535
    /**
536
     * Gets the number of skipped tests.
537
     */
538
    public function skippedCount(): int
539
    {
540
        return count($this->skipped);
541
    }
542
 
543
    /**
544
     * Returns an array of TestFailure objects for the skipped tests.
545
     *
546
     * @return TestFailure[]
547
     */
548
    public function skipped(): array
549
    {
550
        return $this->skipped;
551
    }
552
 
553
    /**
554
     * Gets the number of detected errors.
555
     */
556
    public function errorCount(): int
557
    {
558
        return count($this->errors);
559
    }
560
 
561
    /**
562
     * Returns an array of TestFailure objects for the errors.
563
     *
564
     * @return TestFailure[]
565
     */
566
    public function errors(): array
567
    {
568
        return $this->errors;
569
    }
570
 
571
    /**
572
     * Gets the number of detected failures.
573
     */
574
    public function failureCount(): int
575
    {
576
        return count($this->failures);
577
    }
578
 
579
    /**
580
     * Returns an array of TestFailure objects for the failures.
581
     *
582
     * @return TestFailure[]
583
     */
584
    public function failures(): array
585
    {
586
        return $this->failures;
587
    }
588
 
589
    /**
590
     * Gets the number of detected warnings.
591
     */
592
    public function warningCount(): int
593
    {
594
        return count($this->warnings);
595
    }
596
 
597
    /**
598
     * Returns an array of TestFailure objects for the warnings.
599
     *
600
     * @return TestFailure[]
601
     */
602
    public function warnings(): array
603
    {
604
        return $this->warnings;
605
    }
606
 
607
    /**
608
     * Returns the names of the tests that have passed.
609
     */
610
    public function passed(): array
611
    {
612
        return $this->passed;
613
    }
614
 
615
    /**
616
     * Returns the names of the TestSuites that have passed.
617
     *
618
     * This enables @depends-annotations for TestClassName::class
619
     */
620
    public function passedClasses(): array
621
    {
622
        return $this->passedTestClasses;
623
    }
624
 
625
    /**
626
     * Returns whether code coverage information should be collected.
627
     */
628
    public function getCollectCodeCoverageInformation(): bool
629
    {
630
        return $this->codeCoverage !== null;
631
    }
632
 
633
    /**
634
     * Runs a TestCase.
635
     *
636
     * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
637
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
638
     * @throws CodeCoverageException
639
     * @throws UnintentionallyCoveredCodeException
640
     */
641
    public function run(Test $test): void
642
    {
643
        Assert::resetCount();
644
 
645
        $size = TestUtil::UNKNOWN;
646
 
647
        if ($test instanceof TestCase) {
648
            $test->setRegisterMockObjectsFromTestArgumentsRecursively(
649
                $this->registerMockObjectsFromTestArgumentsRecursively
650
            );
651
 
652
            $isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test);
653
            $size                  = $test->getSize();
654
        }
655
 
656
        $error      = false;
657
        $failure    = false;
658
        $warning    = false;
659
        $incomplete = false;
660
        $risky      = false;
661
        $skipped    = false;
662
 
663
        $this->startTest($test);
664
 
665
        if ($this->convertDeprecationsToExceptions || $this->convertErrorsToExceptions || $this->convertNoticesToExceptions || $this->convertWarningsToExceptions) {
666
            $errorHandler = new ErrorHandler(
667
                $this->convertDeprecationsToExceptions,
668
                $this->convertErrorsToExceptions,
669
                $this->convertNoticesToExceptions,
670
                $this->convertWarningsToExceptions
671
            );
672
 
673
            $errorHandler->register();
674
        }
675
 
676
        $collectCodeCoverage = $this->codeCoverage !== null &&
677
                               !$test instanceof ErrorTestCase &&
678
                               !$test instanceof WarningTestCase &&
679
                               $isAnyCoverageRequired;
680
 
681
        if ($collectCodeCoverage) {
682
            $this->codeCoverage->start($test);
683
        }
684
 
685
        $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests &&
686
                            !$test instanceof ErrorTestCase &&
687
                            !$test instanceof WarningTestCase &&
688
                            $size === TestUtil::SMALL &&
689
                            function_exists('xdebug_start_function_monitor');
690
 
691
        if ($monitorFunctions) {
692
            /* @noinspection ForgottenDebugOutputInspection */
693
            xdebug_start_function_monitor(ResourceOperations::getFunctions());
694
        }
695
 
696
        $timer = new Timer;
697
        $timer->start();
698
 
699
        try {
700
            $invoker = new Invoker;
701
 
702
            if (!$test instanceof ErrorTestCase &&
703
                !$test instanceof WarningTestCase &&
704
                $this->shouldTimeLimitBeEnforced($size) &&
705
                $invoker->canInvokeWithTimeout()) {
706
                switch ($size) {
707
                    case TestUtil::SMALL:
708
                        $_timeout = $this->timeoutForSmallTests;
709
 
710
                        break;
711
 
712
                    case TestUtil::MEDIUM:
713
                        $_timeout = $this->timeoutForMediumTests;
714
 
715
                        break;
716
 
717
                    case TestUtil::LARGE:
718
                        $_timeout = $this->timeoutForLargeTests;
719
 
720
                        break;
721
 
722
                    default:
723
                        $_timeout = $this->defaultTimeLimit;
724
                }
725
 
726
                $invoker->invoke([$test, 'runBare'], [], $_timeout);
727
            } else {
728
                $test->runBare();
729
            }
730
        } catch (TimeoutException $e) {
731
            $this->addFailure(
732
                $test,
733
                new RiskyTestError(
734
                    $e->getMessage()
735
                ),
736
                $_timeout
737
            );
738
 
739
            $risky = true;
740
        } catch (AssertionFailedError $e) {
741
            $failure = true;
742
 
743
            if ($e instanceof RiskyTestError) {
744
                $risky = true;
745
            } elseif ($e instanceof IncompleteTestError) {
746
                $incomplete = true;
747
            } elseif ($e instanceof SkippedTestError) {
748
                $skipped = true;
749
            }
750
        } catch (AssertionError $e) {
751
            $test->addToAssertionCount(1);
752
 
753
            $failure = true;
754
            $frame   = $e->getTrace()[0];
755
 
756
            $e = new AssertionFailedError(
757
                sprintf(
758
                    '%s in %s:%s',
759
                    $e->getMessage(),
760
                    $frame['file'] ?? $e->getFile(),
761
                    $frame['line'] ?? $e->getLine()
762
                ),
763
                0,
764
                $e
765
            );
766
        } catch (Warning $e) {
767
            $warning = true;
768
        } catch (Exception $e) {
769
            $error = true;
770
        } catch (Throwable $e) {
771
            $e     = new ExceptionWrapper($e);
772
            $error = true;
773
        }
774
 
775
        $time = $timer->stop()->asSeconds();
776
 
777
        $test->addToAssertionCount(Assert::getCount());
778
 
779
        if ($monitorFunctions) {
780
            $excludeList = new ExcludeList;
781
 
782
            /** @noinspection ForgottenDebugOutputInspection */
783
            $functions = xdebug_get_monitored_functions();
784
 
785
            /* @noinspection ForgottenDebugOutputInspection */
786
            xdebug_stop_function_monitor();
787
 
788
            foreach ($functions as $function) {
789
                if (!$excludeList->isExcluded($function['filename'])) {
790
                    $this->addFailure(
791
                        $test,
792
                        new RiskyTestError(
793
                            sprintf(
794
                                '%s() used in %s:%s',
795
                                $function['function'],
796
                                $function['filename'],
797
                                $function['lineno']
798
                            )
799
                        ),
800
                        $time
801
                    );
802
                }
803
            }
804
        }
805
 
806
        if ($this->beStrictAboutTestsThatDoNotTestAnything &&
1464 lars 807
            !$test->doesNotPerformAssertions() &&
148 lars 808
            $test->getNumAssertions() === 0) {
809
            $risky = true;
810
        }
811
 
812
        if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) {
813
            $annotations = TestUtil::parseTestMethodAnnotations(
814
                get_class($test),
815
                $test->getName(false)
816
            );
817
 
818
            if (!isset($annotations['class']['covers']) &&
819
                !isset($annotations['method']['covers']) &&
820
                !isset($annotations['class']['coversNothing']) &&
821
                !isset($annotations['method']['coversNothing'])) {
822
                $this->addFailure(
823
                    $test,
824
                    new MissingCoversAnnotationException(
825
                        'This test does not have a @covers annotation but is expected to have one'
826
                    ),
827
                    $time
828
                );
829
 
830
                $risky = true;
831
            }
832
        }
833
 
834
        if ($collectCodeCoverage) {
835
            $append           = !$risky && !$incomplete && !$skipped;
836
            $linesToBeCovered = [];
837
            $linesToBeUsed    = [];
838
 
839
            if ($append && $test instanceof TestCase) {
840
                try {
841
                    $linesToBeCovered = TestUtil::getLinesToBeCovered(
842
                        get_class($test),
843
                        $test->getName(false)
844
                    );
845
 
846
                    $linesToBeUsed = TestUtil::getLinesToBeUsed(
847
                        get_class($test),
848
                        $test->getName(false)
849
                    );
850
                } catch (InvalidCoversTargetException $cce) {
851
                    $this->addWarning(
852
                        $test,
853
                        new Warning(
854
                            $cce->getMessage()
855
                        ),
856
                        $time
857
                    );
858
                }
859
            }
860
 
861
            try {
862
                $this->codeCoverage->stop(
863
                    $append,
864
                    $linesToBeCovered,
865
                    $linesToBeUsed
866
                );
867
            } catch (UnintentionallyCoveredCodeException $cce) {
868
                $unintentionallyCoveredCodeError = new UnintentionallyCoveredCodeError(
869
                    'This test executed code that is not listed as code to be covered or used:' .
870
                    PHP_EOL . $cce->getMessage()
871
                );
872
            } catch (OriginalCodeCoverageException $cce) {
873
                $error = true;
874
 
875
                $e = $e ?? $cce;
876
            }
877
        }
878
 
879
        if (isset($errorHandler)) {
880
            $errorHandler->unregister();
881
 
882
            unset($errorHandler);
883
        }
884
 
885
        if ($error) {
886
            $this->addError($test, $e, $time);
887
        } elseif ($failure) {
888
            $this->addFailure($test, $e, $time);
889
        } elseif ($warning) {
890
            $this->addWarning($test, $e, $time);
891
        } elseif (isset($unintentionallyCoveredCodeError)) {
892
            $this->addFailure(
893
                $test,
894
                $unintentionallyCoveredCodeError,
895
                $time
896
            );
897
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
898
            !$test->doesNotPerformAssertions() &&
899
            $test->getNumAssertions() === 0) {
900
            try {
901
                $reflected = new ReflectionClass($test);
902
                // @codeCoverageIgnoreStart
903
            } catch (ReflectionException $e) {
904
                throw new Exception(
905
                    $e->getMessage(),
906
                    $e->getCode(),
907
                    $e
908
                );
909
            }
910
            // @codeCoverageIgnoreEnd
911
 
912
            $name = $test->getName(false);
913
 
914
            if ($name && $reflected->hasMethod($name)) {
915
                try {
916
                    $reflected = $reflected->getMethod($name);
917
                    // @codeCoverageIgnoreStart
918
                } catch (ReflectionException $e) {
919
                    throw new Exception(
920
                        $e->getMessage(),
921
                        $e->getCode(),
922
                        $e
923
                    );
924
                }
925
                // @codeCoverageIgnoreEnd
926
            }
927
 
928
            $this->addFailure(
929
                $test,
930
                new RiskyTestError(
931
                    sprintf(
932
                        "This test did not perform any assertions\n\n%s:%d",
933
                        $reflected->getFileName(),
934
                        $reflected->getStartLine()
935
                    )
936
                ),
937
                $time
938
            );
939
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
940
            $test->doesNotPerformAssertions() &&
941
            $test->getNumAssertions() > 0) {
942
            $this->addFailure(
943
                $test,
944
                new RiskyTestError(
945
                    sprintf(
946
                        'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions',
947
                        $test->getNumAssertions()
948
                    )
949
                ),
950
                $time
951
            );
952
        } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
953
            $this->addFailure(
954
                $test,
955
                new OutputError(
956
                    sprintf(
957
                        'This test printed output: %s',
958
                        $test->getActualOutput()
959
                    )
960
                ),
961
                $time
962
            );
963
        } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) {
964
            $annotations = TestUtil::parseTestMethodAnnotations(
965
                get_class($test),
966
                $test->getName(false)
967
            );
968
 
969
            if (isset($annotations['method']['todo'])) {
970
                $this->addFailure(
971
                    $test,
972
                    new RiskyTestError(
973
                        'Test method is annotated with @todo'
974
                    ),
975
                    $time
976
                );
977
            }
978
        }
979
 
980
        $this->endTest($test, $time);
981
    }
982
 
983
    /**
984
     * Gets the number of run tests.
985
     */
986
    public function count(): int
987
    {
988
        return $this->runTests;
989
    }
990
 
991
    /**
992
     * Checks whether the test run should stop.
993
     */
994
    public function shouldStop(): bool
995
    {
996
        return $this->stop;
997
    }
998
 
999
    /**
1000
     * Marks that the test run should stop.
1001
     */
1002
    public function stop(): void
1003
    {
1004
        $this->stop = true;
1005
    }
1006
 
1007
    /**
1008
     * Returns the code coverage object.
1009
     */
1010
    public function getCodeCoverage(): ?CodeCoverage
1011
    {
1012
        return $this->codeCoverage;
1013
    }
1014
 
1015
    /**
1016
     * Sets the code coverage object.
1017
     */
1018
    public function setCodeCoverage(CodeCoverage $codeCoverage): void
1019
    {
1020
        $this->codeCoverage = $codeCoverage;
1021
    }
1022
 
1023
    /**
1024
     * Enables or disables the deprecation-to-exception conversion.
1025
     */
1026
    public function convertDeprecationsToExceptions(bool $flag): void
1027
    {
1028
        $this->convertDeprecationsToExceptions = $flag;
1029
    }
1030
 
1031
    /**
1032
     * Returns the deprecation-to-exception conversion setting.
1033
     */
1034
    public function getConvertDeprecationsToExceptions(): bool
1035
    {
1036
        return $this->convertDeprecationsToExceptions;
1037
    }
1038
 
1039
    /**
1040
     * Enables or disables the error-to-exception conversion.
1041
     */
1042
    public function convertErrorsToExceptions(bool $flag): void
1043
    {
1044
        $this->convertErrorsToExceptions = $flag;
1045
    }
1046
 
1047
    /**
1048
     * Returns the error-to-exception conversion setting.
1049
     */
1050
    public function getConvertErrorsToExceptions(): bool
1051
    {
1052
        return $this->convertErrorsToExceptions;
1053
    }
1054
 
1055
    /**
1056
     * Enables or disables the notice-to-exception conversion.
1057
     */
1058
    public function convertNoticesToExceptions(bool $flag): void
1059
    {
1060
        $this->convertNoticesToExceptions = $flag;
1061
    }
1062
 
1063
    /**
1064
     * Returns the notice-to-exception conversion setting.
1065
     */
1066
    public function getConvertNoticesToExceptions(): bool
1067
    {
1068
        return $this->convertNoticesToExceptions;
1069
    }
1070
 
1071
    /**
1072
     * Enables or disables the warning-to-exception conversion.
1073
     */
1074
    public function convertWarningsToExceptions(bool $flag): void
1075
    {
1076
        $this->convertWarningsToExceptions = $flag;
1077
    }
1078
 
1079
    /**
1080
     * Returns the warning-to-exception conversion setting.
1081
     */
1082
    public function getConvertWarningsToExceptions(): bool
1083
    {
1084
        return $this->convertWarningsToExceptions;
1085
    }
1086
 
1087
    /**
1088
     * Enables or disables the stopping when an error occurs.
1089
     */
1090
    public function stopOnError(bool $flag): void
1091
    {
1092
        $this->stopOnError = $flag;
1093
    }
1094
 
1095
    /**
1096
     * Enables or disables the stopping when a failure occurs.
1097
     */
1098
    public function stopOnFailure(bool $flag): void
1099
    {
1100
        $this->stopOnFailure = $flag;
1101
    }
1102
 
1103
    /**
1104
     * Enables or disables the stopping when a warning occurs.
1105
     */
1106
    public function stopOnWarning(bool $flag): void
1107
    {
1108
        $this->stopOnWarning = $flag;
1109
    }
1110
 
1111
    public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void
1112
    {
1113
        $this->beStrictAboutTestsThatDoNotTestAnything = $flag;
1114
    }
1115
 
1116
    public function isStrictAboutTestsThatDoNotTestAnything(): bool
1117
    {
1118
        return $this->beStrictAboutTestsThatDoNotTestAnything;
1119
    }
1120
 
1121
    public function beStrictAboutOutputDuringTests(bool $flag): void
1122
    {
1123
        $this->beStrictAboutOutputDuringTests = $flag;
1124
    }
1125
 
1126
    public function isStrictAboutOutputDuringTests(): bool
1127
    {
1128
        return $this->beStrictAboutOutputDuringTests;
1129
    }
1130
 
1131
    public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void
1132
    {
1133
        $this->beStrictAboutResourceUsageDuringSmallTests = $flag;
1134
    }
1135
 
1136
    public function isStrictAboutResourceUsageDuringSmallTests(): bool
1137
    {
1138
        return $this->beStrictAboutResourceUsageDuringSmallTests;
1139
    }
1140
 
1141
    public function enforceTimeLimit(bool $flag): void
1142
    {
1143
        $this->enforceTimeLimit = $flag;
1144
    }
1145
 
1146
    public function enforcesTimeLimit(): bool
1147
    {
1148
        return $this->enforceTimeLimit;
1149
    }
1150
 
1151
    public function beStrictAboutTodoAnnotatedTests(bool $flag): void
1152
    {
1153
        $this->beStrictAboutTodoAnnotatedTests = $flag;
1154
    }
1155
 
1156
    public function isStrictAboutTodoAnnotatedTests(): bool
1157
    {
1158
        return $this->beStrictAboutTodoAnnotatedTests;
1159
    }
1160
 
1161
    public function forceCoversAnnotation(): void
1162
    {
1163
        $this->forceCoversAnnotation = true;
1164
    }
1165
 
1166
    public function forcesCoversAnnotation(): bool
1167
    {
1168
        return $this->forceCoversAnnotation;
1169
    }
1170
 
1171
    /**
1172
     * Enables or disables the stopping for risky tests.
1173
     */
1174
    public function stopOnRisky(bool $flag): void
1175
    {
1176
        $this->stopOnRisky = $flag;
1177
    }
1178
 
1179
    /**
1180
     * Enables or disables the stopping for incomplete tests.
1181
     */
1182
    public function stopOnIncomplete(bool $flag): void
1183
    {
1184
        $this->stopOnIncomplete = $flag;
1185
    }
1186
 
1187
    /**
1188
     * Enables or disables the stopping for skipped tests.
1189
     */
1190
    public function stopOnSkipped(bool $flag): void
1191
    {
1192
        $this->stopOnSkipped = $flag;
1193
    }
1194
 
1195
    /**
1196
     * Enables or disables the stopping for defects: error, failure, warning.
1197
     */
1198
    public function stopOnDefect(bool $flag): void
1199
    {
1200
        $this->stopOnDefect = $flag;
1201
    }
1202
 
1203
    /**
1204
     * Returns the time spent running the tests.
1205
     */
1206
    public function time(): float
1207
    {
1208
        return $this->time;
1209
    }
1210
 
1211
    /**
1212
     * Returns whether the entire test was successful or not.
1213
     */
1214
    public function wasSuccessful(): bool
1215
    {
1216
        return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings);
1217
    }
1218
 
1219
    public function wasSuccessfulIgnoringWarnings(): bool
1220
    {
1221
        return empty($this->errors) && empty($this->failures);
1222
    }
1223
 
1224
    public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete(): bool
1225
    {
1226
        return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped();
1227
    }
1228
 
1229
    /**
1230
     * Sets the default timeout for tests.
1231
     */
1232
    public function setDefaultTimeLimit(int $timeout): void
1233
    {
1234
        $this->defaultTimeLimit = $timeout;
1235
    }
1236
 
1237
    /**
1238
     * Sets the timeout for small tests.
1239
     */
1240
    public function setTimeoutForSmallTests(int $timeout): void
1241
    {
1242
        $this->timeoutForSmallTests = $timeout;
1243
    }
1244
 
1245
    /**
1246
     * Sets the timeout for medium tests.
1247
     */
1248
    public function setTimeoutForMediumTests(int $timeout): void
1249
    {
1250
        $this->timeoutForMediumTests = $timeout;
1251
    }
1252
 
1253
    /**
1254
     * Sets the timeout for large tests.
1255
     */
1256
    public function setTimeoutForLargeTests(int $timeout): void
1257
    {
1258
        $this->timeoutForLargeTests = $timeout;
1259
    }
1260
 
1261
    /**
1262
     * Returns the set timeout for large tests.
1263
     */
1264
    public function getTimeoutForLargeTests(): int
1265
    {
1266
        return $this->timeoutForLargeTests;
1267
    }
1268
 
1269
    public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
1270
    {
1271
        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
1272
    }
1273
 
1274
    private function recordError(Test $test, Throwable $t): void
1275
    {
1276
        $this->errors[] = new TestFailure($test, $t);
1277
    }
1278
 
1279
    private function recordNotImplemented(Test $test, Throwable $t): void
1280
    {
1281
        $this->notImplemented[] = new TestFailure($test, $t);
1282
    }
1283
 
1284
    private function recordRisky(Test $test, Throwable $t): void
1285
    {
1286
        $this->risky[] = new TestFailure($test, $t);
1287
    }
1288
 
1289
    private function recordSkipped(Test $test, Throwable $t): void
1290
    {
1291
        $this->skipped[] = new TestFailure($test, $t);
1292
    }
1293
 
1294
    private function recordWarning(Test $test, Throwable $t): void
1295
    {
1296
        $this->warnings[] = new TestFailure($test, $t);
1297
    }
1298
 
1299
    private function shouldTimeLimitBeEnforced(int $size): bool
1300
    {
1301
        if (!$this->enforceTimeLimit) {
1302
            return false;
1303
        }
1304
 
1305
        if (!(($this->defaultTimeLimit || $size !== TestUtil::UNKNOWN))) {
1306
            return false;
1307
        }
1308
 
1309
        if (!extension_loaded('pcntl')) {
1310
            return false;
1311
        }
1312
 
1313
        if (!class_exists(Invoker::class)) {
1314
            return false;
1315
        }
1316
 
1317
        if (extension_loaded('xdebug') && xdebug_is_debugger_active()) {
1318
            return false;
1319
        }
1320
 
1321
        return true;
1322
    }
1323
}