Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | 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 &&
807
            $test->getNumAssertions() === 0) {
808
            $risky = true;
809
        }
810
 
811
        if ($this->forceCoversAnnotation && !$error && !$failure && !$warning && !$incomplete && !$skipped && !$risky) {
812
            $annotations = TestUtil::parseTestMethodAnnotations(
813
                get_class($test),
814
                $test->getName(false)
815
            );
816
 
817
            if (!isset($annotations['class']['covers']) &&
818
                !isset($annotations['method']['covers']) &&
819
                !isset($annotations['class']['coversNothing']) &&
820
                !isset($annotations['method']['coversNothing'])) {
821
                $this->addFailure(
822
                    $test,
823
                    new MissingCoversAnnotationException(
824
                        'This test does not have a @covers annotation but is expected to have one'
825
                    ),
826
                    $time
827
                );
828
 
829
                $risky = true;
830
            }
831
        }
832
 
833
        if ($collectCodeCoverage) {
834
            $append           = !$risky && !$incomplete && !$skipped;
835
            $linesToBeCovered = [];
836
            $linesToBeUsed    = [];
837
 
838
            if ($append && $test instanceof TestCase) {
839
                try {
840
                    $linesToBeCovered = TestUtil::getLinesToBeCovered(
841
                        get_class($test),
842
                        $test->getName(false)
843
                    );
844
 
845
                    $linesToBeUsed = TestUtil::getLinesToBeUsed(
846
                        get_class($test),
847
                        $test->getName(false)
848
                    );
849
                } catch (InvalidCoversTargetException $cce) {
850
                    $this->addWarning(
851
                        $test,
852
                        new Warning(
853
                            $cce->getMessage()
854
                        ),
855
                        $time
856
                    );
857
                }
858
            }
859
 
860
            try {
861
                $this->codeCoverage->stop(
862
                    $append,
863
                    $linesToBeCovered,
864
                    $linesToBeUsed
865
                );
866
            } catch (UnintentionallyCoveredCodeException $cce) {
867
                $unintentionallyCoveredCodeError = new UnintentionallyCoveredCodeError(
868
                    'This test executed code that is not listed as code to be covered or used:' .
869
                    PHP_EOL . $cce->getMessage()
870
                );
871
            } catch (OriginalCodeCoverageException $cce) {
872
                $error = true;
873
 
874
                $e = $e ?? $cce;
875
            }
876
        }
877
 
878
        if (isset($errorHandler)) {
879
            $errorHandler->unregister();
880
 
881
            unset($errorHandler);
882
        }
883
 
884
        if ($error) {
885
            $this->addError($test, $e, $time);
886
        } elseif ($failure) {
887
            $this->addFailure($test, $e, $time);
888
        } elseif ($warning) {
889
            $this->addWarning($test, $e, $time);
890
        } elseif (isset($unintentionallyCoveredCodeError)) {
891
            $this->addFailure(
892
                $test,
893
                $unintentionallyCoveredCodeError,
894
                $time
895
            );
896
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
897
            !$test->doesNotPerformAssertions() &&
898
            $test->getNumAssertions() === 0) {
899
            try {
900
                $reflected = new ReflectionClass($test);
901
                // @codeCoverageIgnoreStart
902
            } catch (ReflectionException $e) {
903
                throw new Exception(
904
                    $e->getMessage(),
905
                    $e->getCode(),
906
                    $e
907
                );
908
            }
909
            // @codeCoverageIgnoreEnd
910
 
911
            $name = $test->getName(false);
912
 
913
            if ($name && $reflected->hasMethod($name)) {
914
                try {
915
                    $reflected = $reflected->getMethod($name);
916
                    // @codeCoverageIgnoreStart
917
                } catch (ReflectionException $e) {
918
                    throw new Exception(
919
                        $e->getMessage(),
920
                        $e->getCode(),
921
                        $e
922
                    );
923
                }
924
                // @codeCoverageIgnoreEnd
925
            }
926
 
927
            $this->addFailure(
928
                $test,
929
                new RiskyTestError(
930
                    sprintf(
931
                        "This test did not perform any assertions\n\n%s:%d",
932
                        $reflected->getFileName(),
933
                        $reflected->getStartLine()
934
                    )
935
                ),
936
                $time
937
            );
938
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
939
            $test->doesNotPerformAssertions() &&
940
            $test->getNumAssertions() > 0) {
941
            $this->addFailure(
942
                $test,
943
                new RiskyTestError(
944
                    sprintf(
945
                        'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions',
946
                        $test->getNumAssertions()
947
                    )
948
                ),
949
                $time
950
            );
951
        } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
952
            $this->addFailure(
953
                $test,
954
                new OutputError(
955
                    sprintf(
956
                        'This test printed output: %s',
957
                        $test->getActualOutput()
958
                    )
959
                ),
960
                $time
961
            );
962
        } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) {
963
            $annotations = TestUtil::parseTestMethodAnnotations(
964
                get_class($test),
965
                $test->getName(false)
966
            );
967
 
968
            if (isset($annotations['method']['todo'])) {
969
                $this->addFailure(
970
                    $test,
971
                    new RiskyTestError(
972
                        'Test method is annotated with @todo'
973
                    ),
974
                    $time
975
                );
976
            }
977
        }
978
 
979
        $this->endTest($test, $time);
980
    }
981
 
982
    /**
983
     * Gets the number of run tests.
984
     */
985
    public function count(): int
986
    {
987
        return $this->runTests;
988
    }
989
 
990
    /**
991
     * Checks whether the test run should stop.
992
     */
993
    public function shouldStop(): bool
994
    {
995
        return $this->stop;
996
    }
997
 
998
    /**
999
     * Marks that the test run should stop.
1000
     */
1001
    public function stop(): void
1002
    {
1003
        $this->stop = true;
1004
    }
1005
 
1006
    /**
1007
     * Returns the code coverage object.
1008
     */
1009
    public function getCodeCoverage(): ?CodeCoverage
1010
    {
1011
        return $this->codeCoverage;
1012
    }
1013
 
1014
    /**
1015
     * Sets the code coverage object.
1016
     */
1017
    public function setCodeCoverage(CodeCoverage $codeCoverage): void
1018
    {
1019
        $this->codeCoverage = $codeCoverage;
1020
    }
1021
 
1022
    /**
1023
     * Enables or disables the deprecation-to-exception conversion.
1024
     */
1025
    public function convertDeprecationsToExceptions(bool $flag): void
1026
    {
1027
        $this->convertDeprecationsToExceptions = $flag;
1028
    }
1029
 
1030
    /**
1031
     * Returns the deprecation-to-exception conversion setting.
1032
     */
1033
    public function getConvertDeprecationsToExceptions(): bool
1034
    {
1035
        return $this->convertDeprecationsToExceptions;
1036
    }
1037
 
1038
    /**
1039
     * Enables or disables the error-to-exception conversion.
1040
     */
1041
    public function convertErrorsToExceptions(bool $flag): void
1042
    {
1043
        $this->convertErrorsToExceptions = $flag;
1044
    }
1045
 
1046
    /**
1047
     * Returns the error-to-exception conversion setting.
1048
     */
1049
    public function getConvertErrorsToExceptions(): bool
1050
    {
1051
        return $this->convertErrorsToExceptions;
1052
    }
1053
 
1054
    /**
1055
     * Enables or disables the notice-to-exception conversion.
1056
     */
1057
    public function convertNoticesToExceptions(bool $flag): void
1058
    {
1059
        $this->convertNoticesToExceptions = $flag;
1060
    }
1061
 
1062
    /**
1063
     * Returns the notice-to-exception conversion setting.
1064
     */
1065
    public function getConvertNoticesToExceptions(): bool
1066
    {
1067
        return $this->convertNoticesToExceptions;
1068
    }
1069
 
1070
    /**
1071
     * Enables or disables the warning-to-exception conversion.
1072
     */
1073
    public function convertWarningsToExceptions(bool $flag): void
1074
    {
1075
        $this->convertWarningsToExceptions = $flag;
1076
    }
1077
 
1078
    /**
1079
     * Returns the warning-to-exception conversion setting.
1080
     */
1081
    public function getConvertWarningsToExceptions(): bool
1082
    {
1083
        return $this->convertWarningsToExceptions;
1084
    }
1085
 
1086
    /**
1087
     * Enables or disables the stopping when an error occurs.
1088
     */
1089
    public function stopOnError(bool $flag): void
1090
    {
1091
        $this->stopOnError = $flag;
1092
    }
1093
 
1094
    /**
1095
     * Enables or disables the stopping when a failure occurs.
1096
     */
1097
    public function stopOnFailure(bool $flag): void
1098
    {
1099
        $this->stopOnFailure = $flag;
1100
    }
1101
 
1102
    /**
1103
     * Enables or disables the stopping when a warning occurs.
1104
     */
1105
    public function stopOnWarning(bool $flag): void
1106
    {
1107
        $this->stopOnWarning = $flag;
1108
    }
1109
 
1110
    public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void
1111
    {
1112
        $this->beStrictAboutTestsThatDoNotTestAnything = $flag;
1113
    }
1114
 
1115
    public function isStrictAboutTestsThatDoNotTestAnything(): bool
1116
    {
1117
        return $this->beStrictAboutTestsThatDoNotTestAnything;
1118
    }
1119
 
1120
    public function beStrictAboutOutputDuringTests(bool $flag): void
1121
    {
1122
        $this->beStrictAboutOutputDuringTests = $flag;
1123
    }
1124
 
1125
    public function isStrictAboutOutputDuringTests(): bool
1126
    {
1127
        return $this->beStrictAboutOutputDuringTests;
1128
    }
1129
 
1130
    public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void
1131
    {
1132
        $this->beStrictAboutResourceUsageDuringSmallTests = $flag;
1133
    }
1134
 
1135
    public function isStrictAboutResourceUsageDuringSmallTests(): bool
1136
    {
1137
        return $this->beStrictAboutResourceUsageDuringSmallTests;
1138
    }
1139
 
1140
    public function enforceTimeLimit(bool $flag): void
1141
    {
1142
        $this->enforceTimeLimit = $flag;
1143
    }
1144
 
1145
    public function enforcesTimeLimit(): bool
1146
    {
1147
        return $this->enforceTimeLimit;
1148
    }
1149
 
1150
    public function beStrictAboutTodoAnnotatedTests(bool $flag): void
1151
    {
1152
        $this->beStrictAboutTodoAnnotatedTests = $flag;
1153
    }
1154
 
1155
    public function isStrictAboutTodoAnnotatedTests(): bool
1156
    {
1157
        return $this->beStrictAboutTodoAnnotatedTests;
1158
    }
1159
 
1160
    public function forceCoversAnnotation(): void
1161
    {
1162
        $this->forceCoversAnnotation = true;
1163
    }
1164
 
1165
    public function forcesCoversAnnotation(): bool
1166
    {
1167
        return $this->forceCoversAnnotation;
1168
    }
1169
 
1170
    /**
1171
     * Enables or disables the stopping for risky tests.
1172
     */
1173
    public function stopOnRisky(bool $flag): void
1174
    {
1175
        $this->stopOnRisky = $flag;
1176
    }
1177
 
1178
    /**
1179
     * Enables or disables the stopping for incomplete tests.
1180
     */
1181
    public function stopOnIncomplete(bool $flag): void
1182
    {
1183
        $this->stopOnIncomplete = $flag;
1184
    }
1185
 
1186
    /**
1187
     * Enables or disables the stopping for skipped tests.
1188
     */
1189
    public function stopOnSkipped(bool $flag): void
1190
    {
1191
        $this->stopOnSkipped = $flag;
1192
    }
1193
 
1194
    /**
1195
     * Enables or disables the stopping for defects: error, failure, warning.
1196
     */
1197
    public function stopOnDefect(bool $flag): void
1198
    {
1199
        $this->stopOnDefect = $flag;
1200
    }
1201
 
1202
    /**
1203
     * Returns the time spent running the tests.
1204
     */
1205
    public function time(): float
1206
    {
1207
        return $this->time;
1208
    }
1209
 
1210
    /**
1211
     * Returns whether the entire test was successful or not.
1212
     */
1213
    public function wasSuccessful(): bool
1214
    {
1215
        return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings);
1216
    }
1217
 
1218
    public function wasSuccessfulIgnoringWarnings(): bool
1219
    {
1220
        return empty($this->errors) && empty($this->failures);
1221
    }
1222
 
1223
    public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete(): bool
1224
    {
1225
        return $this->wasSuccessful() && $this->allHarmless() && $this->allCompletelyImplemented() && $this->noneSkipped();
1226
    }
1227
 
1228
    /**
1229
     * Sets the default timeout for tests.
1230
     */
1231
    public function setDefaultTimeLimit(int $timeout): void
1232
    {
1233
        $this->defaultTimeLimit = $timeout;
1234
    }
1235
 
1236
    /**
1237
     * Sets the timeout for small tests.
1238
     */
1239
    public function setTimeoutForSmallTests(int $timeout): void
1240
    {
1241
        $this->timeoutForSmallTests = $timeout;
1242
    }
1243
 
1244
    /**
1245
     * Sets the timeout for medium tests.
1246
     */
1247
    public function setTimeoutForMediumTests(int $timeout): void
1248
    {
1249
        $this->timeoutForMediumTests = $timeout;
1250
    }
1251
 
1252
    /**
1253
     * Sets the timeout for large tests.
1254
     */
1255
    public function setTimeoutForLargeTests(int $timeout): void
1256
    {
1257
        $this->timeoutForLargeTests = $timeout;
1258
    }
1259
 
1260
    /**
1261
     * Returns the set timeout for large tests.
1262
     */
1263
    public function getTimeoutForLargeTests(): int
1264
    {
1265
        return $this->timeoutForLargeTests;
1266
    }
1267
 
1268
    public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
1269
    {
1270
        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
1271
    }
1272
 
1273
    private function recordError(Test $test, Throwable $t): void
1274
    {
1275
        $this->errors[] = new TestFailure($test, $t);
1276
    }
1277
 
1278
    private function recordNotImplemented(Test $test, Throwable $t): void
1279
    {
1280
        $this->notImplemented[] = new TestFailure($test, $t);
1281
    }
1282
 
1283
    private function recordRisky(Test $test, Throwable $t): void
1284
    {
1285
        $this->risky[] = new TestFailure($test, $t);
1286
    }
1287
 
1288
    private function recordSkipped(Test $test, Throwable $t): void
1289
    {
1290
        $this->skipped[] = new TestFailure($test, $t);
1291
    }
1292
 
1293
    private function recordWarning(Test $test, Throwable $t): void
1294
    {
1295
        $this->warnings[] = new TestFailure($test, $t);
1296
    }
1297
 
1298
    private function shouldTimeLimitBeEnforced(int $size): bool
1299
    {
1300
        if (!$this->enforceTimeLimit) {
1301
            return false;
1302
        }
1303
 
1304
        if (!(($this->defaultTimeLimit || $size !== TestUtil::UNKNOWN))) {
1305
            return false;
1306
        }
1307
 
1308
        if (!extension_loaded('pcntl')) {
1309
            return false;
1310
        }
1311
 
1312
        if (!class_exists(Invoker::class)) {
1313
            return false;
1314
        }
1315
 
1316
        if (extension_loaded('xdebug') && xdebug_is_debugger_active()) {
1317
            return false;
1318
        }
1319
 
1320
        return true;
1321
    }
1322
}