TestResult::wasSuccessful()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
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 AssertionError;
13
use Countable;
14
use Error;
15
use PHPUnit\Framework\MockObject\Exception as MockObjectException;
16
use PHPUnit\Util\Blacklist;
17
use PHPUnit\Util\ErrorHandler;
18
use PHPUnit\Util\Printer;
19
use SebastianBergmann\CodeCoverage\CodeCoverage;
20
use SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException as OriginalCoveredCodeNotExecutedException;
21
use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException;
22
use SebastianBergmann\CodeCoverage\MissingCoversAnnotationException as OriginalMissingCoversAnnotationException;
23
use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
24
use SebastianBergmann\Invoker\Invoker;
0 ignored issues
show
Bug introduced by
The type SebastianBergmann\Invoker\Invoker was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use SebastianBergmann\Invoker\TimeoutException;
0 ignored issues
show
Bug introduced by
The type SebastianBergmann\Invoker\TimeoutException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use SebastianBergmann\ResourceOperations\ResourceOperations;
27
use SebastianBergmann\Timer\Timer;
28
use Throwable;
29
30
/**
31
 * A TestResult collects the results of executing a test case.
32
 */
33
class TestResult implements Countable
34
{
35
    /**
36
     * @var array
37
     */
38
    protected $passed = [];
39
40
    /**
41
     * @var TestFailure[]
42
     */
43
    protected $errors = [];
44
45
    /**
46
     * @var TestFailure[]
47
     */
48
    protected $failures = [];
49
50
    /**
51
     * @var TestFailure[]
52
     */
53
    protected $warnings = [];
54
55
    /**
56
     * @var TestFailure[]
57
     */
58
    protected $notImplemented = [];
59
60
    /**
61
     * @var TestFailure[]
62
     */
63
    protected $risky = [];
64
65
    /**
66
     * @var TestFailure[]
67
     */
68
    protected $skipped = [];
69
70
    /**
71
     * @var TestListener[]
72
     */
73
    protected $listeners = [];
74
75
    /**
76
     * @var int
77
     */
78
    protected $runTests = 0;
79
80
    /**
81
     * @var float
82
     */
83
    protected $time = 0;
84
85
    /**
86
     * @var TestSuite
87
     */
88
    protected $topTestSuite;
89
90
    /**
91
     * Code Coverage information.
92
     *
93
     * @var CodeCoverage
94
     */
95
    protected $codeCoverage;
96
97
    /**
98
     * @var bool
99
     */
100
    protected $convertErrorsToExceptions = true;
101
102
    /**
103
     * @var bool
104
     */
105
    protected $stop = false;
106
107
    /**
108
     * @var bool
109
     */
110
    protected $stopOnError = false;
111
112
    /**
113
     * @var bool
114
     */
115
    protected $stopOnFailure = false;
116
117
    /**
118
     * @var bool
119
     */
120
    protected $stopOnWarning = false;
121
122
    /**
123
     * @var bool
124
     */
125
    protected $beStrictAboutTestsThatDoNotTestAnything = true;
126
127
    /**
128
     * @var bool
129
     */
130
    protected $beStrictAboutOutputDuringTests = false;
131
132
    /**
133
     * @var bool
134
     */
135
    protected $beStrictAboutTodoAnnotatedTests = false;
136
137
    /**
138
     * @var bool
139
     */
140
    protected $beStrictAboutResourceUsageDuringSmallTests = false;
141
142
    /**
143
     * @var bool
144
     */
145
    protected $enforceTimeLimit = false;
146
147
    /**
148
     * @var int
149
     */
150
    protected $timeoutForSmallTests = 1;
151
152
    /**
153
     * @var int
154
     */
155
    protected $timeoutForMediumTests = 10;
156
157
    /**
158
     * @var int
159
     */
160
    protected $timeoutForLargeTests = 60;
161
162
    /**
163
     * @var bool
164
     */
165
    protected $stopOnRisky = false;
166
167
    /**
168
     * @var bool
169
     */
170
    protected $stopOnIncomplete = false;
171
172
    /**
173
     * @var bool
174
     */
175
    protected $stopOnSkipped = false;
176
177
    /**
178
     * @var bool
179
     */
180
    protected $lastTestFailed = false;
181
182
    /**
183
     * @var int
184
     */
185
    private $defaultTimeLimit = 0;
186
187
    /**
188
     * @var bool
189
     */
190
    private $stopOnDefect = false;
191
192
    /**
193
     * @var bool
194
     */
195
    private $registerMockObjectsFromTestArgumentsRecursively = false;
196
197
    public static function isAnyCoverageRequired(TestCase $test)
198
    {
199
        $annotations = $test->getAnnotations();
200
201
        // If there is a @coversNothing annotation on the test method then code
202
        // coverage data does not need to be collected
203
        if (isset($annotations['method']['coversNothing'])) {
204
            return false;
205
        }
206
207
        // If any methods have covers, coverage must me generated
208
        if (isset($annotations['method']['covers'])) {
209
            return true;
210
        }
211
212
        // If there are no explicit covers, and the test class is
213
        // marked as covers nothing, all coverage can be skipped
214
        if (isset($annotations['class']['coversNothing'])) {
215
            return false;
216
        }
217
218
        // Otherwise each test method can generate coverage
219
        return true;
220
    }
221
222
    /**
223
     * Registers a TestListener.
224
     */
225
    public function addListener(TestListener $listener): void
226
    {
227
        $this->listeners[] = $listener;
228
    }
229
230
    /**
231
     * Unregisters a TestListener.
232
     */
233
    public function removeListener(TestListener $listener): void
234
    {
235
        foreach ($this->listeners as $key => $_listener) {
236
            if ($listener === $_listener) {
237
                unset($this->listeners[$key]);
238
            }
239
        }
240
    }
241
242
    /**
243
     * Flushes all flushable TestListeners.
244
     */
245
    public function flushListeners(): void
246
    {
247
        foreach ($this->listeners as $listener) {
248
            if ($listener instanceof Printer) {
249
                $listener->flush();
250
            }
251
        }
252
    }
253
254
    /**
255
     * Adds an error to the list of errors.
256
     */
257
    public function addError(Test $test, Throwable $t, float $time): void
258
    {
259
        if ($t instanceof RiskyTest) {
260
            $this->risky[] = new TestFailure($test, $t);
261
            $notifyMethod  = 'addRiskyTest';
262
263
            if ($test instanceof TestCase) {
264
                $test->markAsRisky();
265
            }
266
267
            if ($this->stopOnRisky || $this->stopOnDefect) {
268
                $this->stop();
269
            }
270
        } elseif ($t instanceof IncompleteTest) {
271
            $this->notImplemented[] = new TestFailure($test, $t);
272
            $notifyMethod           = 'addIncompleteTest';
273
274
            if ($this->stopOnIncomplete) {
275
                $this->stop();
276
            }
277
        } elseif ($t instanceof SkippedTest) {
278
            $this->skipped[] = new TestFailure($test, $t);
279
            $notifyMethod    = 'addSkippedTest';
280
281
            if ($this->stopOnSkipped) {
282
                $this->stop();
283
            }
284
        } else {
285
            $this->errors[] = new TestFailure($test, $t);
286
            $notifyMethod   = 'addError';
287
288
            if ($this->stopOnError || $this->stopOnFailure) {
289
                $this->stop();
290
            }
291
        }
292
293
        // @see https://github.com/sebastianbergmann/phpunit/issues/1953
294
        if ($t instanceof Error) {
295
            $t = new ExceptionWrapper($t);
296
        }
297
298
        foreach ($this->listeners as $listener) {
299
            $listener->$notifyMethod($test, $t, $time);
300
        }
301
302
        $this->lastTestFailed = true;
303
        $this->time += $time;
304
    }
305
306
    /**
307
     * Adds a warning to the list of warnings.
308
     * The passed in exception caused the warning.
309
     */
310
    public function addWarning(Test $test, Warning $e, float $time): void
311
    {
312
        if ($this->stopOnWarning || $this->stopOnDefect) {
313
            $this->stop();
314
        }
315
316
        $this->warnings[] = new TestFailure($test, $e);
317
318
        foreach ($this->listeners as $listener) {
319
            $listener->addWarning($test, $e, $time);
320
        }
321
322
        $this->time += $time;
323
    }
324
325
    /**
326
     * Adds a failure to the list of failures.
327
     * The passed in exception caused the failure.
328
     */
329
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
330
    {
331
        if ($e instanceof RiskyTest || $e instanceof OutputError) {
332
            $this->risky[] = new TestFailure($test, $e);
333
            $notifyMethod  = 'addRiskyTest';
334
335
            if ($test instanceof TestCase) {
336
                $test->markAsRisky();
337
            }
338
339
            if ($this->stopOnRisky || $this->stopOnDefect) {
340
                $this->stop();
341
            }
342
        } elseif ($e instanceof IncompleteTest) {
343
            $this->notImplemented[] = new TestFailure($test, $e);
344
            $notifyMethod           = 'addIncompleteTest';
345
346
            if ($this->stopOnIncomplete) {
347
                $this->stop();
348
            }
349
        } elseif ($e instanceof SkippedTest) {
350
            $this->skipped[] = new TestFailure($test, $e);
351
            $notifyMethod    = 'addSkippedTest';
352
353
            if ($this->stopOnSkipped) {
354
                $this->stop();
355
            }
356
        } else {
357
            $this->failures[] = new TestFailure($test, $e);
358
            $notifyMethod     = 'addFailure';
359
360
            if ($this->stopOnFailure || $this->stopOnDefect) {
361
                $this->stop();
362
            }
363
        }
364
365
        foreach ($this->listeners as $listener) {
366
            $listener->$notifyMethod($test, $e, $time);
367
        }
368
369
        $this->lastTestFailed = true;
370
        $this->time += $time;
371
    }
372
373
    /**
374
     * Informs the result that a test suite will be started.
375
     */
376
    public function startTestSuite(TestSuite $suite): void
377
    {
378
        if ($this->topTestSuite === null) {
379
            $this->topTestSuite = $suite;
380
        }
381
382
        foreach ($this->listeners as $listener) {
383
            $listener->startTestSuite($suite);
384
        }
385
    }
386
387
    /**
388
     * Informs the result that a test suite was completed.
389
     */
390
    public function endTestSuite(TestSuite $suite): void
391
    {
392
        foreach ($this->listeners as $listener) {
393
            $listener->endTestSuite($suite);
394
        }
395
    }
396
397
    /**
398
     * Informs the result that a test will be started.
399
     */
400
    public function startTest(Test $test): void
401
    {
402
        $this->lastTestFailed = false;
403
        $this->runTests += \count($test);
404
405
        foreach ($this->listeners as $listener) {
406
            $listener->startTest($test);
407
        }
408
    }
409
410
    /**
411
     * Informs the result that a test was completed.
412
     *
413
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
414
     */
415
    public function endTest(Test $test, float $time): void
416
    {
417
        foreach ($this->listeners as $listener) {
418
            $listener->endTest($test, $time);
419
        }
420
421
        if (!$this->lastTestFailed && $test instanceof TestCase) {
422
            $class = \get_class($test);
423
            $key   = $class . '::' . $test->getName();
424
425
            $this->passed[$key] = [
426
                'result' => $test->getResult(),
427
                'size'   => \PHPUnit\Util\Test::getSize(
428
                    $class,
429
                    $test->getName(false)
430
                ),
431
            ];
432
433
            $this->time += $time;
434
        }
435
    }
436
437
    /**
438
     * Returns true if no risky test occurred.
439
     */
440
    public function allHarmless(): bool
441
    {
442
        return $this->riskyCount() == 0;
443
    }
444
445
    /**
446
     * Gets the number of risky tests.
447
     */
448
    public function riskyCount(): int
449
    {
450
        return \count($this->risky);
451
    }
452
453
    /**
454
     * Returns true if no incomplete test occurred.
455
     */
456
    public function allCompletelyImplemented(): bool
457
    {
458
        return $this->notImplementedCount() == 0;
459
    }
460
461
    /**
462
     * Gets the number of incomplete tests.
463
     */
464
    public function notImplementedCount(): int
465
    {
466
        return \count($this->notImplemented);
467
    }
468
469
    /**
470
     * Returns an array of TestFailure objects for the risky tests
471
     *
472
     * @return TestFailure[]
473
     */
474
    public function risky(): array
475
    {
476
        return $this->risky;
477
    }
478
479
    /**
480
     * Returns an array of TestFailure objects for the incomplete tests
481
     *
482
     * @return TestFailure[]
483
     */
484
    public function notImplemented(): array
485
    {
486
        return $this->notImplemented;
487
    }
488
489
    /**
490
     * Returns true if no test has been skipped.
491
     */
492
    public function noneSkipped(): bool
493
    {
494
        return $this->skippedCount() == 0;
495
    }
496
497
    /**
498
     * Gets the number of skipped tests.
499
     */
500
    public function skippedCount(): int
501
    {
502
        return \count($this->skipped);
503
    }
504
505
    /**
506
     * Returns an array of TestFailure objects for the skipped tests
507
     *
508
     * @return TestFailure[]
509
     */
510
    public function skipped(): array
511
    {
512
        return $this->skipped;
513
    }
514
515
    /**
516
     * Gets the number of detected errors.
517
     */
518
    public function errorCount(): int
519
    {
520
        return \count($this->errors);
521
    }
522
523
    /**
524
     * Returns an array of TestFailure objects for the errors
525
     *
526
     * @return TestFailure[]
527
     */
528
    public function errors(): array
529
    {
530
        return $this->errors;
531
    }
532
533
    /**
534
     * Gets the number of detected failures.
535
     */
536
    public function failureCount(): int
537
    {
538
        return \count($this->failures);
539
    }
540
541
    /**
542
     * Returns an array of TestFailure objects for the failures
543
     *
544
     * @return TestFailure[]
545
     */
546
    public function failures(): array
547
    {
548
        return $this->failures;
549
    }
550
551
    /**
552
     * Gets the number of detected warnings.
553
     */
554
    public function warningCount(): int
555
    {
556
        return \count($this->warnings);
557
    }
558
559
    /**
560
     * Returns an array of TestFailure objects for the warnings
561
     *
562
     * @return TestFailure[]
563
     */
564
    public function warnings(): array
565
    {
566
        return $this->warnings;
567
    }
568
569
    /**
570
     * Returns the names of the tests that have passed.
571
     */
572
    public function passed(): array
573
    {
574
        return $this->passed;
575
    }
576
577
    /**
578
     * Returns the (top) test suite.
579
     */
580
    public function topTestSuite(): TestSuite
581
    {
582
        return $this->topTestSuite;
583
    }
584
585
    /**
586
     * Returns whether code coverage information should be collected.
587
     */
588
    public function getCollectCodeCoverageInformation(): bool
589
    {
590
        return $this->codeCoverage !== null;
591
    }
592
593
    /**
594
     * Runs a TestCase.
595
     *
596
     * @throws CodeCoverageException
597
     * @throws OriginalCoveredCodeNotExecutedException
598
     * @throws OriginalMissingCoversAnnotationException
599
     * @throws UnintentionallyCoveredCodeException
600
     * @throws \ReflectionException
601
     * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
602
     * @throws \SebastianBergmann\CodeCoverage\RuntimeException
603
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
604
     */
605
    public function run(Test $test): void
606
    {
607
        Assert::resetCount();
608
609
        $coversNothing = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $coversNothing is dead and can be removed.
Loading history...
610
611
        if ($test instanceof TestCase) {
612
            $test->setRegisterMockObjectsFromTestArgumentsRecursively(
613
                $this->registerMockObjectsFromTestArgumentsRecursively
614
            );
615
616
            $isAnyCoverageRequired = self::isAnyCoverageRequired($test);
617
        }
618
619
        $error      = false;
620
        $failure    = false;
621
        $warning    = false;
622
        $incomplete = false;
623
        $risky      = false;
624
        $skipped    = false;
625
626
        $this->startTest($test);
627
628
        $errorHandlerSet = false;
629
630
        if ($this->convertErrorsToExceptions) {
631
            $oldErrorHandler = \set_error_handler(
632
                [ErrorHandler::class, 'handleError'],
633
                \E_ALL | \E_STRICT
634
            );
635
636
            if ($oldErrorHandler === null) {
637
                $errorHandlerSet = true;
638
            } else {
639
                \restore_error_handler();
640
            }
641
        }
642
643
        $collectCodeCoverage = $this->codeCoverage !== null &&
644
                               !$test instanceof WarningTestCase &&
645
                               $isAnyCoverageRequired;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $isAnyCoverageRequired does not seem to be defined for all execution paths leading up to this point.
Loading history...
646
647
        if ($collectCodeCoverage) {
648
            $this->codeCoverage->start($test);
649
        }
650
651
        $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests &&
652
            !$test instanceof WarningTestCase &&
653
            $test->getSize() == \PHPUnit\Util\Test::SMALL &&
0 ignored issues
show
Bug introduced by
The method getSize() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

653
            $test->/** @scrutinizer ignore-call */ 
654
                   getSize() == \PHPUnit\Util\Test::SMALL &&
Loading history...
654
            \function_exists('xdebug_start_function_monitor');
655
656
        if ($monitorFunctions) {
657
            /* @noinspection ForgottenDebugOutputInspection */
658
            \xdebug_start_function_monitor(ResourceOperations::getFunctions());
659
        }
660
661
        Timer::start();
662
663
        try {
664
            if (!$test instanceof WarningTestCase &&
665
                $this->enforceTimeLimit &&
666
                ($this->defaultTimeLimit || $test->getSize() != \PHPUnit\Util\Test::UNKNOWN) &&
667
                \extension_loaded('pcntl') && \class_exists(Invoker::class)) {
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 = new Invoker;
691
                $invoker->invoke([$test, 'runBare'], [], $_timeout);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_timeout does not seem to be defined for all execution paths leading up to this point.
Loading history...
692
            } else {
693
                $test->runBare();
0 ignored issues
show
Bug introduced by
The method runBare() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

693
                $test->/** @scrutinizer ignore-call */ 
694
                       runBare();
Loading history...
694
            }
695
        } catch (TimeoutException $e) {
696
            $this->addFailure(
697
                $test,
698
                new RiskyTestError(
699
                    $e->getMessage()
700
                ),
701
                $_timeout
702
            );
703
704
            $risky = true;
705
        } catch (MockObjectException $e) {
706
            $e = new Warning(
707
                $e->getMessage()
708
            );
709
710
            $warning = true;
711
        } catch (AssertionFailedError $e) {
712
            $failure = true;
713
714
            if ($e instanceof RiskyTestError) {
715
                $risky = true;
716
            } elseif ($e instanceof IncompleteTestError) {
717
                $incomplete = true;
718
            } elseif ($e instanceof SkippedTestError) {
719
                $skipped = true;
720
            }
721
        } catch (AssertionError $e) {
722
            $test->addToAssertionCount(1);
0 ignored issues
show
Bug introduced by
The method addToAssertionCount() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

722
            $test->/** @scrutinizer ignore-call */ 
723
                   addToAssertionCount(1);
Loading history...
723
724
            $failure = true;
725
            $frame   = $e->getTrace()[0];
726
727
            $e = new AssertionFailedError(
728
                \sprintf(
729
                    '%s in %s:%s',
730
                    $e->getMessage(),
731
                    $frame['file'],
732
                    $frame['line']
733
                )
734
            );
735
        } catch (Warning $e) {
736
            $warning = true;
737
        } catch (Exception $e) {
738
            $error = true;
739
        } catch (Throwable $e) {
740
            $e     = new ExceptionWrapper($e);
741
            $error = true;
742
        }
743
744
        $time = Timer::stop();
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) {
0 ignored issues
show
Bug introduced by
The method getNumAssertions() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase or PHPUnit\Runner\PhptTestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

775
            $test->/** @scrutinizer ignore-call */ 
776
                   getNumAssertions() == 0) {
Loading history...
776
            $risky = true;
777
        }
778
779
        if ($collectCodeCoverage) {
780
            $append           = !$risky && !$incomplete && !$skipped;
781
            $linesToBeCovered = [];
782
            $linesToBeUsed    = [];
783
784
            if ($append && $test instanceof TestCase) {
785
                try {
786
                    $linesToBeCovered = \PHPUnit\Util\Test::getLinesToBeCovered(
787
                        \get_class($test),
788
                        $test->getName(false)
789
                    );
790
791
                    $linesToBeUsed = \PHPUnit\Util\Test::getLinesToBeUsed(
792
                        \get_class($test),
793
                        $test->getName(false)
794
                    );
795
                } catch (InvalidCoversTargetException $cce) {
796
                    $this->addWarning(
797
                        $test,
798
                        new Warning(
799
                            $cce->getMessage()
800
                        ),
801
                        $time
802
                    );
803
                }
804
            }
805
806
            try {
807
                $this->codeCoverage->stop(
808
                    $append,
809
                    $linesToBeCovered,
810
                    $linesToBeUsed
811
                );
812
            } catch (UnintentionallyCoveredCodeException $cce) {
813
                $this->addFailure(
814
                    $test,
815
                    new UnintentionallyCoveredCodeError(
816
                        'This test executed code that is not listed as code to be covered or used:' .
817
                        \PHP_EOL . $cce->getMessage()
818
                    ),
819
                    $time
820
                );
821
            } catch (OriginalCoveredCodeNotExecutedException $cce) {
822
                $this->addFailure(
823
                    $test,
824
                    new CoveredCodeNotExecutedException(
825
                        'This test did not execute all the code that is listed as code to be covered:' .
826
                        \PHP_EOL . $cce->getMessage()
827
                    ),
828
                    $time
829
                );
830
            } catch (OriginalMissingCoversAnnotationException $cce) {
831
                if ($linesToBeCovered !== false) {
832
                    $this->addFailure(
833
                        $test,
834
                        new MissingCoversAnnotationException(
835
                            'This test does not have a @covers annotation but is expected to have one'
836
                        ),
837
                        $time
838
                    );
839
                }
840
            } catch (OriginalCodeCoverageException $cce) {
841
                $error = true;
842
843
                $e = $e ?? $cce;
844
            }
845
        }
846
847
        if ($errorHandlerSet === true) {
848
            \restore_error_handler();
849
        }
850
851
        if ($error === true) {
852
            $this->addError($test, $e, $time);
853
        } elseif ($failure === true) {
854
            $this->addFailure($test, $e, $time);
855
        } elseif ($warning === true) {
856
            $this->addWarning($test, $e, $time);
857
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
858
            !$test->doesNotPerformAssertions() &&
0 ignored issues
show
Bug introduced by
The method doesNotPerformAssertions() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

858
            !$test->/** @scrutinizer ignore-call */ doesNotPerformAssertions() &&
Loading history...
859
            $test->getNumAssertions() == 0) {
860
            $reflected = new \ReflectionClass($test);
861
            $name      = $test->getName(false);
0 ignored issues
show
Bug introduced by
The method getName() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of said class. However, the method does not exist in DoubleTestCase or NotSelfDescribingTest. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

861
            /** @scrutinizer ignore-call */ 
862
            $name      = $test->getName(false);
Loading history...
862
863
            if ($name && $reflected->hasMethod($name)) {
864
                $reflected = $reflected->getMethod($name);
865
            }
866
867
            $this->addFailure(
868
                $test,
869
                new RiskyTestError(
870
                    \sprintf(
871
                        "This test did not perform any assertions\n\n%s:%d",
872
                        $reflected->getFileName(),
873
                        $reflected->getStartLine()
874
                    )
875
                ),
876
                $time
877
            );
878
        } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
879
            $test->doesNotPerformAssertions() &&
880
            $test->getNumAssertions() > 0) {
881
            $this->addFailure(
882
                $test,
883
                new RiskyTestError(
884
                    \sprintf(
885
                        'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions',
886
                        $test->getNumAssertions()
887
                    )
888
                ),
889
                $time
890
            );
891
        } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
0 ignored issues
show
Bug introduced by
The method hasOutput() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase or PHPUnit\Runner\PhptTestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

891
        } elseif ($this->beStrictAboutOutputDuringTests && $test->/** @scrutinizer ignore-call */ hasOutput()) {
Loading history...
892
            $this->addFailure(
893
                $test,
894
                new OutputError(
895
                    \sprintf(
896
                        'This test printed output: %s',
897
                        $test->getActualOutput()
0 ignored issues
show
Bug introduced by
The method getActualOutput() does not exist on PHPUnit\Framework\Test. It seems like you code against a sub-type of PHPUnit\Framework\Test such as PHPUnit\Framework\TestCase or PHPUnit\Runner\PhptTestCase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

897
                        $test->/** @scrutinizer ignore-call */ 
898
                               getActualOutput()
Loading history...
898
                    )
899
                ),
900
                $time
901
            );
902
        } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) {
903
            $annotations = $test->getAnnotations();
904
905
            if (isset($annotations['method']['todo'])) {
906
                $this->addFailure(
907
                    $test,
908
                    new RiskyTestError(
909
                        'Test method is annotated with @todo'
910
                    ),
911
                    $time
912
                );
913
            }
914
        }
915
916
        $this->endTest($test, $time);
917
    }
918
919
    /**
920
     * Gets the number of run tests.
921
     */
922
    public function count(): int
923
    {
924
        return $this->runTests;
925
    }
926
927
    /**
928
     * Checks whether the test run should stop.
929
     */
930
    public function shouldStop(): bool
931
    {
932
        return $this->stop;
933
    }
934
935
    /**
936
     * Marks that the test run should stop.
937
     */
938
    public function stop(): void
939
    {
940
        $this->stop = true;
941
    }
942
943
    /**
944
     * Returns the code coverage object.
945
     */
946
    public function getCodeCoverage(): ?CodeCoverage
947
    {
948
        return $this->codeCoverage;
949
    }
950
951
    /**
952
     * Sets the code coverage object.
953
     */
954
    public function setCodeCoverage(CodeCoverage $codeCoverage): void
955
    {
956
        $this->codeCoverage = $codeCoverage;
957
    }
958
959
    /**
960
     * Enables or disables the error-to-exception conversion.
961
     */
962
    public function convertErrorsToExceptions(bool $flag): void
963
    {
964
        $this->convertErrorsToExceptions = $flag;
965
    }
966
967
    /**
968
     * Returns the error-to-exception conversion setting.
969
     */
970
    public function getConvertErrorsToExceptions(): bool
971
    {
972
        return $this->convertErrorsToExceptions;
973
    }
974
975
    /**
976
     * Enables or disables the stopping when an error occurs.
977
     */
978
    public function stopOnError(bool $flag): void
979
    {
980
        $this->stopOnError = $flag;
981
    }
982
983
    /**
984
     * Enables or disables the stopping when a failure occurs.
985
     */
986
    public function stopOnFailure(bool $flag): void
987
    {
988
        $this->stopOnFailure = $flag;
989
    }
990
991
    /**
992
     * Enables or disables the stopping when a warning occurs.
993
     */
994
    public function stopOnWarning(bool $flag): void
995
    {
996
        $this->stopOnWarning = $flag;
997
    }
998
999
    public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void
1000
    {
1001
        $this->beStrictAboutTestsThatDoNotTestAnything = $flag;
1002
    }
1003
1004
    public function isStrictAboutTestsThatDoNotTestAnything(): bool
1005
    {
1006
        return $this->beStrictAboutTestsThatDoNotTestAnything;
1007
    }
1008
1009
    public function beStrictAboutOutputDuringTests(bool $flag): void
1010
    {
1011
        $this->beStrictAboutOutputDuringTests = $flag;
1012
    }
1013
1014
    public function isStrictAboutOutputDuringTests(): bool
1015
    {
1016
        return $this->beStrictAboutOutputDuringTests;
1017
    }
1018
1019
    public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void
1020
    {
1021
        $this->beStrictAboutResourceUsageDuringSmallTests = $flag;
1022
    }
1023
1024
    public function isStrictAboutResourceUsageDuringSmallTests(): bool
1025
    {
1026
        return $this->beStrictAboutResourceUsageDuringSmallTests;
1027
    }
1028
1029
    public function enforceTimeLimit(bool $flag): void
1030
    {
1031
        $this->enforceTimeLimit = $flag;
1032
    }
1033
1034
    public function enforcesTimeLimit(): bool
1035
    {
1036
        return $this->enforceTimeLimit;
1037
    }
1038
1039
    public function beStrictAboutTodoAnnotatedTests(bool $flag): void
1040
    {
1041
        $this->beStrictAboutTodoAnnotatedTests = $flag;
1042
    }
1043
1044
    public function isStrictAboutTodoAnnotatedTests(): bool
1045
    {
1046
        return $this->beStrictAboutTodoAnnotatedTests;
1047
    }
1048
1049
    /**
1050
     * Enables or disables the stopping for risky tests.
1051
     */
1052
    public function stopOnRisky(bool $flag): void
1053
    {
1054
        $this->stopOnRisky = $flag;
1055
    }
1056
1057
    /**
1058
     * Enables or disables the stopping for incomplete tests.
1059
     */
1060
    public function stopOnIncomplete(bool $flag): void
1061
    {
1062
        $this->stopOnIncomplete = $flag;
1063
    }
1064
1065
    /**
1066
     * Enables or disables the stopping for skipped tests.
1067
     */
1068
    public function stopOnSkipped(bool $flag): void
1069
    {
1070
        $this->stopOnSkipped = $flag;
1071
    }
1072
1073
    /**
1074
     * Enables or disables the stopping for defects: error, failure, warning
1075
     */
1076
    public function stopOnDefect(bool $flag): void
1077
    {
1078
        $this->stopOnDefect = $flag;
1079
    }
1080
1081
    /**
1082
     * Returns the time spent running the tests.
1083
     */
1084
    public function time(): float
1085
    {
1086
        return $this->time;
1087
    }
1088
1089
    /**
1090
     * Returns whether the entire test was successful or not.
1091
     */
1092
    public function wasSuccessful(): bool
1093
    {
1094
        return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings);
1095
    }
1096
1097
    public function wasSuccessfulIgnoringWarnings(): bool
1098
    {
1099
        return empty($this->errors) && empty($this->failures);
1100
    }
1101
1102
    /**
1103
     * Sets the default timeout for tests
1104
     */
1105
    public function setDefaultTimeLimit(int $timeout): void
1106
    {
1107
        $this->defaultTimeLimit = $timeout;
1108
    }
1109
1110
    /**
1111
     * Sets the timeout for small tests.
1112
     */
1113
    public function setTimeoutForSmallTests(int $timeout): void
1114
    {
1115
        $this->timeoutForSmallTests = $timeout;
1116
    }
1117
1118
    /**
1119
     * Sets the timeout for medium tests.
1120
     */
1121
    public function setTimeoutForMediumTests(int $timeout): void
1122
    {
1123
        $this->timeoutForMediumTests = $timeout;
1124
    }
1125
1126
    /**
1127
     * Sets the timeout for large tests.
1128
     */
1129
    public function setTimeoutForLargeTests(int $timeout): void
1130
    {
1131
        $this->timeoutForLargeTests = $timeout;
1132
    }
1133
1134
    /**
1135
     * Returns the set timeout for large tests.
1136
     */
1137
    public function getTimeoutForLargeTests(): int
1138
    {
1139
        return $this->timeoutForLargeTests;
1140
    }
1141
1142
    public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
1143
    {
1144
        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
1145
    }
1146
}
1147