Passed
Pull Request — master (#1)
by Guillaume
04:03
created

TestResult::run()   F

Complexity

Conditions 76
Paths > 20000

Size

Total Lines 341
Code Lines 225

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 76
eloc 225
nc 260112384
nop 1
dl 0
loc 341
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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