Passed
Push — develop ( 30cf64...589229 )
by Guillaume
06:18 queued 04:10
created

TestCase   F

Complexity

Total Complexity 321

Size/Duplication

Total Lines 2373
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 828
dl 0
loc 2373
rs 1.772
c 0
b 0
f 0
wmc 321

How to fix   Complexity   

Complex Class

Complex classes like TestCase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TestCase, and based on these observations, apply Extract Interface, too.

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 DeepCopy\DeepCopy;
13
use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint;
14
use PHPUnit\Framework\Constraint\ExceptionCode;
15
use PHPUnit\Framework\Constraint\ExceptionMessage;
16
use PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression;
17
use PHPUnit\Framework\Error\Deprecated;
18
use PHPUnit\Framework\Error\Error;
19
use PHPUnit\Framework\Error\Notice;
20
use PHPUnit\Framework\Error\Warning as WarningError;
21
use PHPUnit\Framework\MockObject\Generator as MockGenerator;
22
use PHPUnit\Framework\MockObject\MockBuilder;
23
use PHPUnit\Framework\MockObject\MockObject;
24
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher;
25
use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher;
26
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher;
27
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher;
28
use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher;
29
use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher;
30
use PHPUnit\Framework\MockObject\Stub;
31
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub;
32
use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub;
33
use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub;
34
use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub;
35
use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub;
36
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
37
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub;
38
use PHPUnit\Runner\BaseTestRunner;
39
use PHPUnit\Runner\PhptTestCase;
40
use PHPUnit\Util\Exception as UtilException;
41
use PHPUnit\Util\GlobalState;
42
use PHPUnit\Util\PHP\AbstractPhpProcess;
43
use PHPUnit\Util\Test as TestUtil;
44
use PHPUnit\Util\Type;
45
use Prophecy\Exception\Prediction\PredictionException;
46
use Prophecy\Prophecy\MethodProphecy;
47
use Prophecy\Prophecy\ObjectProphecy;
48
use Prophecy\Prophet;
49
use SebastianBergmann\Comparator\Comparator;
50
use SebastianBergmann\Comparator\Factory as ComparatorFactory;
51
use SebastianBergmann\Diff\Differ;
52
use SebastianBergmann\Exporter\Exporter;
53
use SebastianBergmann\GlobalState\Blacklist;
54
use SebastianBergmann\GlobalState\Restorer;
55
use SebastianBergmann\GlobalState\Snapshot;
56
use SebastianBergmann\ObjectEnumerator\Enumerator;
57
use SebastianBergmann\Template\Template;
58
59
abstract class TestCase extends Assert implements SelfDescribing, Test
60
{
61
    private const LOCALE_CATEGORIES = [\LC_ALL, \LC_COLLATE, \LC_CTYPE, \LC_MONETARY, \LC_NUMERIC, \LC_TIME];
62
63
    /**
64
     * @var ?bool
65
     */
66
    protected $backupGlobals;
67
68
    /**
69
     * @var string[]
70
     */
71
    protected $backupGlobalsBlacklist = [];
72
73
    /**
74
     * @var bool
75
     */
76
    protected $backupStaticAttributes;
77
78
    /**
79
     * @var array<string,array<int,string>>
80
     */
81
    protected $backupStaticAttributesBlacklist = [];
82
83
    /**
84
     * @var bool
85
     */
86
    protected $runTestInSeparateProcess;
87
88
    /**
89
     * @var bool
90
     */
91
    protected $preserveGlobalState = true;
92
93
    /**
94
     * @var bool
95
     */
96
    private $runClassInSeparateProcess;
97
98
    /**
99
     * @var bool
100
     */
101
    private $inIsolation = false;
102
103
    /**
104
     * @var array
105
     */
106
    private $data;
107
108
    /**
109
     * @var int|string
110
     */
111
    private $dataName;
112
113
    /**
114
     * @var null|string
115
     */
116
    private $expectedException;
117
118
    /**
119
     * @var null|string
120
     */
121
    private $expectedExceptionMessage;
122
123
    /**
124
     * @var null|string
125
     */
126
    private $expectedExceptionMessageRegExp;
127
128
    /**
129
     * @var null|int|string
130
     */
131
    private $expectedExceptionCode;
132
133
    /**
134
     * @var string
135
     */
136
    private $name = '';
137
138
    /**
139
     * @var string[]
140
     */
141
    private $dependencies = [];
142
143
    /**
144
     * @var array
145
     */
146
    private $dependencyInput = [];
147
148
    /**
149
     * @var array<string,string>
150
     */
151
    private $iniSettings = [];
152
153
    /**
154
     * @var array
155
     */
156
    private $locale = [];
157
158
    /**
159
     * @var MockObject[]
160
     */
161
    private $mockObjects = [];
162
163
    /**
164
     * @var MockGenerator
165
     */
166
    private $mockObjectGenerator;
167
168
    /**
169
     * @var int
170
     */
171
    private $status = BaseTestRunner::STATUS_UNKNOWN;
172
173
    /**
174
     * @var string
175
     */
176
    private $statusMessage = '';
177
178
    /**
179
     * @var int
180
     */
181
    private $numAssertions = 0;
182
183
    /**
184
     * @var TestResult
185
     */
186
    private $result;
187
188
    /**
189
     * @var mixed
190
     */
191
    private $testResult;
192
193
    /**
194
     * @var string
195
     */
196
    private $output = '';
197
198
    /**
199
     * @var string
200
     */
201
    private $outputExpectedRegex;
202
203
    /**
204
     * @var string
205
     */
206
    private $outputExpectedString;
207
208
    /**
209
     * @var mixed
210
     */
211
    private $outputCallback = false;
212
213
    /**
214
     * @var bool
215
     */
216
    private $outputBufferingActive = false;
217
218
    /**
219
     * @var int
220
     */
221
    private $outputBufferingLevel;
222
223
    /**
224
     * @var bool
225
     */
226
    private $outputRetrievedForAssertion = false;
227
228
    /**
229
     * @var Snapshot
230
     */
231
    private $snapshot;
232
233
    /**
234
     * @var \Prophecy\Prophet
235
     */
236
    private $prophet;
237
238
    /**
239
     * @var bool
240
     */
241
    private $beStrictAboutChangesToGlobalState = false;
242
243
    /**
244
     * @var bool
245
     */
246
    private $registerMockObjectsFromTestArgumentsRecursively = false;
247
248
    /**
249
     * @var string[]
250
     */
251
    private $warnings = [];
252
253
    /**
254
     * @var string[]
255
     */
256
    private $groups = [];
257
258
    /**
259
     * @var bool
260
     */
261
    private $doesNotPerformAssertions = false;
262
263
    /**
264
     * @var Comparator[]
265
     */
266
    private $customComparators = [];
267
268
    /**
269
     * @var string[]
270
     */
271
    private $doubledTypes = [];
272
273
    /**
274
     * Returns a matcher that matches when the method is executed
275
     * zero or more times.
276
     */
277
    public static function any(): AnyInvokedCountMatcher
278
    {
279
        return new AnyInvokedCountMatcher;
280
    }
281
282
    /**
283
     * Returns a matcher that matches when the method is never executed.
284
     */
285
    public static function never(): InvokedCountMatcher
286
    {
287
        return new InvokedCountMatcher(0);
288
    }
289
290
    /**
291
     * Returns a matcher that matches when the method is executed
292
     * at least N times.
293
     */
294
    public static function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher
295
    {
296
        return new InvokedAtLeastCountMatcher(
297
            $requiredInvocations
298
        );
299
    }
300
301
    /**
302
     * Returns a matcher that matches when the method is executed at least once.
303
     */
304
    public static function atLeastOnce(): InvokedAtLeastOnceMatcher
305
    {
306
        return new InvokedAtLeastOnceMatcher;
307
    }
308
309
    /**
310
     * Returns a matcher that matches when the method is executed exactly once.
311
     */
312
    public static function once(): InvokedCountMatcher
313
    {
314
        return new InvokedCountMatcher(1);
315
    }
316
317
    /**
318
     * Returns a matcher that matches when the method is executed
319
     * exactly $count times.
320
     */
321
    public static function exactly(int $count): InvokedCountMatcher
322
    {
323
        return new InvokedCountMatcher($count);
324
    }
325
326
    /**
327
     * Returns a matcher that matches when the method is executed
328
     * at most N times.
329
     */
330
    public static function atMost(int $allowedInvocations): InvokedAtMostCountMatcher
331
    {
332
        return new InvokedAtMostCountMatcher($allowedInvocations);
333
    }
334
335
    /**
336
     * Returns a matcher that matches when the method is executed
337
     * at the given index.
338
     */
339
    public static function at(int $index): InvokedAtIndexMatcher
340
    {
341
        return new InvokedAtIndexMatcher($index);
342
    }
343
344
    public static function returnValue($value): ReturnStub
345
    {
346
        return new ReturnStub($value);
347
    }
348
349
    public static function returnValueMap(array $valueMap): ReturnValueMapStub
350
    {
351
        return new ReturnValueMapStub($valueMap);
352
    }
353
354
    public static function returnArgument(int $argumentIndex): ReturnArgumentStub
355
    {
356
        return new ReturnArgumentStub($argumentIndex);
357
    }
358
359
    public static function returnCallback($callback): ReturnCallbackStub
360
    {
361
        return new ReturnCallbackStub($callback);
362
    }
363
364
    /**
365
     * Returns the current object.
366
     *
367
     * This method is useful when mocking a fluent interface.
368
     */
369
    public static function returnSelf(): ReturnSelfStub
370
    {
371
        return new ReturnSelfStub;
372
    }
373
374
    public static function throwException(\Throwable $exception): ExceptionStub
375
    {
376
        return new ExceptionStub($exception);
377
    }
378
379
    public static function onConsecutiveCalls(...$args): ConsecutiveCallsStub
380
    {
381
        return new ConsecutiveCallsStub($args);
382
    }
383
384
    /**
385
     * @param int|string $dataName
386
     *
387
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
388
     */
389
    public function __construct(?string $name = null, array $data = [], $dataName = '')
390
    {
391
        if ($name !== null) {
392
            $this->setName($name);
393
        }
394
395
        $this->data     = $data;
396
        $this->dataName = $dataName;
397
    }
398
399
    /**
400
     * This method is called before the first test of this test class is run.
401
     */
402
    public static function setUpBeforeClass(): void
403
    {
404
    }
405
406
    /**
407
     * This method is called after the last test of this test class is run.
408
     */
409
    public static function tearDownAfterClass(): void
410
    {
411
    }
412
413
    /**
414
     * This method is called before each test.
415
     */
416
    protected function setUp(): void
417
    {
418
    }
419
420
    /**
421
     * This method is called after each test.
422
     */
423
    protected function tearDown(): void
424
    {
425
    }
426
427
    /**
428
     * Returns a string representation of the test case.
429
     *
430
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
431
     * @throws Exception
432
     */
433
    public function toString(): string
434
    {
435
        try {
436
            $class = new \ReflectionClass($this);
437
            // @codeCoverageIgnoreStart
438
        } catch (\ReflectionException $e) {
439
            throw new Exception(
440
                $e->getMessage(),
441
                (int) $e->getCode(),
442
                $e
443
            );
444
        }
445
        // @codeCoverageIgnoreEnd
446
447
        $buffer = \sprintf(
448
            '%s::%s',
449
            $class->name,
450
            $this->getName(false)
451
        );
452
453
        return $buffer . $this->getDataSetAsString();
454
    }
455
456
    public function count(): int
457
    {
458
        return 1;
459
    }
460
461
    public function getActualOutputForAssertion(): string
462
    {
463
        $this->outputRetrievedForAssertion = true;
464
465
        return $this->getActualOutput();
466
    }
467
468
    public function expectOutputRegex(string $expectedRegex): void
469
    {
470
        $this->outputExpectedRegex = $expectedRegex;
471
    }
472
473
    public function expectOutputString(string $expectedString): void
474
    {
475
        $this->outputExpectedString = $expectedString;
476
    }
477
478
    /**
479
     * @psalm-param class-string<\Throwable> $exception
480
     */
481
    public function expectException(string $exception): void
482
    {
483
        // @codeCoverageIgnoreStart
484
        switch ($exception) {
485
            case Deprecated::class:
486
                $this->addWarning('Support for using expectException() with PHPUnit\Framework\Error\Deprecated is deprecated and will be removed in PHPUnit 10. Use expectDeprecation() instead.');
487
488
            break;
489
490
            case Error::class:
491
                $this->addWarning('Support for using expectException() with PHPUnit\Framework\Error\Error is deprecated and will be removed in PHPUnit 10. Use expectError() instead.');
492
493
            break;
494
495
            case Notice::class:
496
                $this->addWarning('Support for using expectException() with PHPUnit\Framework\Error\Notice is deprecated and will be removed in PHPUnit 10. Use expectNotice() instead.');
497
498
            break;
499
500
            case WarningError::class:
501
                $this->addWarning('Support for using expectException() with PHPUnit\Framework\Error\Warning is deprecated and will be removed in PHPUnit 10. Use expectWarning() instead.');
502
503
            break;
504
        }
505
        // @codeCoverageIgnoreEnd
506
507
        $this->expectedException = $exception;
508
    }
509
510
    /**
511
     * @param int|string $code
512
     */
513
    public function expectExceptionCode($code): void
514
    {
515
        $this->expectedExceptionCode = $code;
516
    }
517
518
    public function expectExceptionMessage(string $message): void
519
    {
520
        $this->expectedExceptionMessage = $message;
521
    }
522
523
    public function expectExceptionMessageMatches(string $regularExpression): void
524
    {
525
        $this->expectedExceptionMessageRegExp = $regularExpression;
526
    }
527
528
    /**
529
     * Sets up an expectation for an exception to be raised by the code under test.
530
     * Information for expected exception class, expected exception message, and
531
     * expected exception code are retrieved from a given Exception object.
532
     */
533
    public function expectExceptionObject(\Exception $exception): void
534
    {
535
        $this->expectException(\get_class($exception));
536
        $this->expectExceptionMessage($exception->getMessage());
537
        $this->expectExceptionCode($exception->getCode());
538
    }
539
540
    public function expectNotToPerformAssertions(): void
541
    {
542
        $this->doesNotPerformAssertions = true;
543
    }
544
545
    public function expectDeprecation(): void
546
    {
547
        $this->expectedException = Deprecated::class;
548
    }
549
550
    public function expectDeprecationMessage(string $message): void
551
    {
552
        $this->expectExceptionMessage($message);
553
    }
554
555
    public function expectDeprecationMessageMatches(string $regularExpression): void
556
    {
557
        $this->expectExceptionMessageMatches($regularExpression);
558
    }
559
560
    public function expectNotice(): void
561
    {
562
        $this->expectedException = Notice::class;
563
    }
564
565
    public function expectNoticeMessage(string $message): void
566
    {
567
        $this->expectExceptionMessage($message);
568
    }
569
570
    public function expectNoticeMessageMatches(string $regularExpression): void
571
    {
572
        $this->expectExceptionMessageMatches($regularExpression);
573
    }
574
575
    public function expectWarning(): void
576
    {
577
        $this->expectedException = WarningError::class;
578
    }
579
580
    public function expectWarningMessage(string $message): void
581
    {
582
        $this->expectExceptionMessage($message);
583
    }
584
585
    public function expectWarningMessageMatches(string $regularExpression): void
586
    {
587
        $this->expectExceptionMessageMatches($regularExpression);
588
    }
589
590
    public function expectError(): void
591
    {
592
        $this->expectedException = Error::class;
593
    }
594
595
    public function expectErrorMessage(string $message): void
596
    {
597
        $this->expectExceptionMessage($message);
598
    }
599
600
    public function expectErrorMessageMatches(string $regularExpression): void
601
    {
602
        $this->expectExceptionMessageMatches($regularExpression);
603
    }
604
605
    public function getStatus(): int
606
    {
607
        return $this->status;
608
    }
609
610
    public function markAsRisky(): void
611
    {
612
        $this->status = BaseTestRunner::STATUS_RISKY;
613
    }
614
615
    public function getStatusMessage(): string
616
    {
617
        return $this->statusMessage;
618
    }
619
620
    public function hasFailed(): bool
621
    {
622
        $status = $this->getStatus();
623
624
        return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR;
625
    }
626
627
    /**
628
     * Runs the test case and collects the results in a TestResult object.
629
     * If no TestResult object is passed a new one will be created.
630
     *
631
     * @throws CodeCoverageException
632
     * @throws UtilException
633
     * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException
634
     * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
635
     * @throws \SebastianBergmann\CodeCoverage\RuntimeException
636
     * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException
637
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
638
     */
639
    public function run(TestResult $result = null): TestResult
640
    {
641
        if ($result === null) {
642
            $result = $this->createResult();
643
        }
644
645
        if (!$this instanceof WarningTestCase) {
646
            $this->setTestResultObject($result);
647
        }
648
649
        if (!$this instanceof WarningTestCase &&
650
            !$this instanceof SkippedTestCase &&
651
            !$this->handleDependencies()) {
652
            return $result;
653
        }
654
655
        if ($this->runInSeparateProcess()) {
656
            $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess;
657
658
            try {
659
                $class = new \ReflectionClass($this);
660
                // @codeCoverageIgnoreStart
661
            } catch (\ReflectionException $e) {
662
                throw new Exception(
663
                    $e->getMessage(),
664
                    (int) $e->getCode(),
665
                    $e
666
                );
667
            }
668
            // @codeCoverageIgnoreEnd
669
670
            if ($runEntireClass) {
671
                $template = new Template(
672
                    __DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'
673
                );
674
            } else {
675
                $template = new Template(
676
                    __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'
677
                );
678
            }
679
680
            if ($this->preserveGlobalState) {
681
                $constants     = GlobalState::getConstantsAsString();
682
                $globals       = GlobalState::getGlobalsAsString();
683
                $includedFiles = GlobalState::getIncludedFilesAsString();
684
                $iniSettings   = GlobalState::getIniSettingsAsString();
685
            } else {
686
                $constants = '';
687
688
                if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
689
                    $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n";
690
                } else {
691
                    $globals = '';
692
                }
693
694
                $includedFiles = '';
695
                $iniSettings   = '';
696
            }
697
698
            $coverage                                   = $result->getCollectCodeCoverageInformation() ? 'true' : 'false';
699
            $isStrictAboutTestsThatDoNotTestAnything    = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false';
700
            $isStrictAboutOutputDuringTests             = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false';
701
            $enforcesTimeLimit                          = $result->enforcesTimeLimit() ? 'true' : 'false';
702
            $isStrictAboutTodoAnnotatedTests            = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false';
703
            $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false';
704
705
            if (\defined('PHPUNIT_COMPOSER_INSTALL')) {
706
                $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true);
707
            } else {
708
                $composerAutoload = '\'\'';
709
            }
710
711
            if (\defined('__PHPUNIT_PHAR__')) {
712
                $phar = \var_export(__PHPUNIT_PHAR__, true);
713
            } else {
714
                $phar = '\'\'';
715
            }
716
717
            if ($result->getCodeCoverage()) {
718
                $codeCoverageFilter = $result->getCodeCoverage()->filter();
719
            } else {
720
                $codeCoverageFilter = null;
721
            }
722
723
            $data               = \var_export(\serialize($this->data), true);
724
            $dataName           = \var_export($this->dataName, true);
725
            $dependencyInput    = \var_export(\serialize($this->dependencyInput), true);
726
            $includePath        = \var_export(\get_include_path(), true);
727
            $codeCoverageFilter = \var_export(\serialize($codeCoverageFilter), true);
728
            // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC
729
            // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences
730
            $data               = "'." . $data . ".'";
731
            $dataName           = "'.(" . $dataName . ").'";
732
            $dependencyInput    = "'." . $dependencyInput . ".'";
733
            $includePath        = "'." . $includePath . ".'";
734
            $codeCoverageFilter = "'." . $codeCoverageFilter . ".'";
735
736
            $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? '';
737
738
            $var = [
739
                'composerAutoload'                           => $composerAutoload,
740
                'phar'                                       => $phar,
741
                'filename'                                   => $class->getFileName(),
742
                'className'                                  => $class->getName(),
743
                'collectCodeCoverageInformation'             => $coverage,
744
                'data'                                       => $data,
745
                'dataName'                                   => $dataName,
746
                'dependencyInput'                            => $dependencyInput,
747
                'constants'                                  => $constants,
748
                'globals'                                    => $globals,
749
                'include_path'                               => $includePath,
750
                'included_files'                             => $includedFiles,
751
                'iniSettings'                                => $iniSettings,
752
                'isStrictAboutTestsThatDoNotTestAnything'    => $isStrictAboutTestsThatDoNotTestAnything,
753
                'isStrictAboutOutputDuringTests'             => $isStrictAboutOutputDuringTests,
754
                'enforcesTimeLimit'                          => $enforcesTimeLimit,
755
                'isStrictAboutTodoAnnotatedTests'            => $isStrictAboutTodoAnnotatedTests,
756
                'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests,
757
                'codeCoverageFilter'                         => $codeCoverageFilter,
758
                'configurationFilePath'                      => $configurationFilePath,
759
                'name'                                       => $this->getName(false),
760
            ];
761
762
            if (!$runEntireClass) {
763
                $var['methodName'] = $this->name;
764
            }
765
766
            $template->setVar($var);
767
768
            $php = AbstractPhpProcess::factory();
769
            $php->runTestJob($template->render(), $this, $result);
770
        } else {
771
            $result->run($this);
772
        }
773
774
        $this->result = null;
775
776
        return $result;
777
    }
778
779
    /**
780
     * Returns a builder object to create mock objects using a fluent interface.
781
     *
782
     * @psalm-template RealInstanceType of object
783
     * @psalm-param class-string<RealInstanceType> $className
784
     * @psalm-return MockBuilder<RealInstanceType>
785
     */
786
    public function getMockBuilder(string $className): MockBuilder
787
    {
788
        $this->recordDoubledType($className);
789
790
        return new MockBuilder($this, $className);
791
    }
792
793
    public function registerComparator(Comparator $comparator): void
794
    {
795
        ComparatorFactory::getInstance()->register($comparator);
796
797
        $this->customComparators[] = $comparator;
798
    }
799
800
    /**
801
     * @return string[]
802
     *
803
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
804
     */
805
    public function doubledTypes(): array
806
    {
807
        return \array_unique($this->doubledTypes);
808
    }
809
810
    /**
811
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
812
     */
813
    public function getGroups(): array
814
    {
815
        return $this->groups;
816
    }
817
818
    /**
819
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
820
     */
821
    public function setGroups(array $groups): void
822
    {
823
        $this->groups = $groups;
824
    }
825
826
    /**
827
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
828
     */
829
    public function getAnnotations(): array
830
    {
831
        return TestUtil::parseTestMethodAnnotations(
832
            \get_class($this),
833
            $this->name
834
        );
835
    }
836
837
    /**
838
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
839
     *
840
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
841
     */
842
    public function getName(bool $withDataSet = true): string
843
    {
844
        if ($withDataSet) {
845
            return $this->name . $this->getDataSetAsString(false);
846
        }
847
848
        return $this->name;
849
    }
850
851
    /**
852
     * Returns the size of the test.
853
     *
854
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
855
     *
856
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
857
     */
858
    public function getSize(): int
859
    {
860
        return TestUtil::getSize(
861
            \get_class($this),
862
            $this->getName(false)
863
        );
864
    }
865
866
    /**
867
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
868
     *
869
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
870
     */
871
    public function hasSize(): bool
872
    {
873
        return $this->getSize() !== TestUtil::UNKNOWN;
874
    }
875
876
    /**
877
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
878
     *
879
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
880
     */
881
    public function isSmall(): bool
882
    {
883
        return $this->getSize() === TestUtil::SMALL;
884
    }
885
886
    /**
887
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
888
     *
889
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
890
     */
891
    public function isMedium(): bool
892
    {
893
        return $this->getSize() === TestUtil::MEDIUM;
894
    }
895
896
    /**
897
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
898
     *
899
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
900
     */
901
    public function isLarge(): bool
902
    {
903
        return $this->getSize() === TestUtil::LARGE;
904
    }
905
906
    /**
907
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
908
     */
909
    public function getActualOutput(): string
910
    {
911
        if (!$this->outputBufferingActive) {
912
            return $this->output;
913
        }
914
915
        return (string) \ob_get_contents();
916
    }
917
918
    /**
919
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
920
     */
921
    public function hasOutput(): bool
922
    {
923
        if ($this->output === '') {
924
            return false;
925
        }
926
927
        if ($this->hasExpectationOnOutput()) {
928
            return false;
929
        }
930
931
        return true;
932
    }
933
934
    /**
935
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
936
     */
937
    public function doesNotPerformAssertions(): bool
938
    {
939
        return $this->doesNotPerformAssertions;
940
    }
941
942
    /**
943
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
944
     */
945
    public function hasExpectationOnOutput(): bool
946
    {
947
        return \is_string($this->outputExpectedString) || \is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion;
948
    }
949
950
    /**
951
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
952
     */
953
    public function getExpectedException(): ?string
954
    {
955
        return $this->expectedException;
956
    }
957
958
    /**
959
     * @return null|int|string
960
     *
961
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
962
     */
963
    public function getExpectedExceptionCode()
964
    {
965
        return $this->expectedExceptionCode;
966
    }
967
968
    /**
969
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
970
     */
971
    public function getExpectedExceptionMessage(): ?string
972
    {
973
        return $this->expectedExceptionMessage;
974
    }
975
976
    /**
977
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
978
     */
979
    public function getExpectedExceptionMessageRegExp(): ?string
980
    {
981
        return $this->expectedExceptionMessageRegExp;
982
    }
983
984
    /**
985
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
986
     */
987
    public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
988
    {
989
        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
990
    }
991
992
    /**
993
     * @throws \Throwable
994
     *
995
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
996
     */
997
    public function runBare(): void
998
    {
999
        $this->numAssertions = 0;
1000
1001
        $this->snapshotGlobalState();
1002
        $this->startOutputBuffering();
1003
        \clearstatcache();
1004
        $currentWorkingDirectory = \getcwd();
1005
1006
        $hookMethods = TestUtil::getHookMethods(\get_class($this));
1007
1008
        $hasMetRequirements = false;
1009
1010
        try {
1011
            $this->checkRequirements();
1012
            $hasMetRequirements = true;
1013
1014
            if ($this->inIsolation) {
1015
                foreach ($hookMethods['beforeClass'] as $method) {
1016
                    $this->{$method}();
1017
                }
1018
            }
1019
1020
            $this->setDoesNotPerformAssertionsFromAnnotation();
1021
1022
            foreach ($hookMethods['before'] as $method) {
1023
                $this->{$method}();
1024
            }
1025
1026
            foreach ($hookMethods['preCondition'] as $method) {
1027
                $this->{$method}();
1028
            }
1029
1030
            $this->testResult = $this->runTest();
1031
            $this->verifyMockObjects();
1032
1033
            foreach ($hookMethods['postCondition'] as $method) {
1034
                $this->{$method}();
1035
            }
1036
1037
            if (!empty($this->warnings)) {
1038
                throw new Warning(
1039
                    \implode(
1040
                        "\n",
1041
                        \array_unique($this->warnings)
1042
                    )
1043
                );
1044
            }
1045
1046
            $this->status = BaseTestRunner::STATUS_PASSED;
1047
        } catch (IncompleteTest $e) {
1048
            $this->status        = BaseTestRunner::STATUS_INCOMPLETE;
1049
            $this->statusMessage = $e->getMessage();
1050
        } catch (SkippedTest $e) {
1051
            $this->status        = BaseTestRunner::STATUS_SKIPPED;
1052
            $this->statusMessage = $e->getMessage();
1053
        } catch (Warning $e) {
1054
            $this->status        = BaseTestRunner::STATUS_WARNING;
1055
            $this->statusMessage = $e->getMessage();
1056
        } catch (AssertionFailedError $e) {
1057
            $this->status        = BaseTestRunner::STATUS_FAILURE;
1058
            $this->statusMessage = $e->getMessage();
1059
        } catch (PredictionException $e) {
1060
            $this->status        = BaseTestRunner::STATUS_FAILURE;
1061
            $this->statusMessage = $e->getMessage();
1062
        } catch (\Throwable $_e) {
1063
            $e                   = $_e;
1064
            $this->status        = BaseTestRunner::STATUS_ERROR;
1065
            $this->statusMessage = $_e->getMessage();
1066
        }
1067
1068
        $this->mockObjects = [];
1069
        $this->prophet     = null;
1070
1071
        // Tear down the fixture. An exception raised in tearDown() will be
1072
        // caught and passed on when no exception was raised before.
1073
        try {
1074
            if ($hasMetRequirements) {
1075
                foreach ($hookMethods['after'] as $method) {
1076
                    $this->{$method}();
1077
                }
1078
1079
                if ($this->inIsolation) {
1080
                    foreach ($hookMethods['afterClass'] as $method) {
1081
                        $this->{$method}();
1082
                    }
1083
                }
1084
            }
1085
        } catch (\Throwable $_e) {
1086
            $e = $e ?? $_e;
1087
        }
1088
1089
        try {
1090
            $this->stopOutputBuffering();
1091
        } catch (RiskyTestError $_e) {
1092
            $e = $e ?? $_e;
1093
        }
1094
1095
        if (isset($_e)) {
1096
            $this->status        = BaseTestRunner::STATUS_ERROR;
1097
            $this->statusMessage = $_e->getMessage();
1098
        }
1099
1100
        \clearstatcache();
1101
1102
        if ($currentWorkingDirectory !== \getcwd()) {
1103
            \chdir($currentWorkingDirectory);
1104
        }
1105
1106
        $this->restoreGlobalState();
1107
        $this->unregisterCustomComparators();
1108
        $this->cleanupIniSettings();
1109
        $this->cleanupLocaleSettings();
1110
        \libxml_clear_errors();
1111
1112
        // Perform assertion on output.
1113
        if (!isset($e)) {
1114
            try {
1115
                if ($this->outputExpectedRegex !== null) {
1116
                    $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output);
1117
                } elseif ($this->outputExpectedString !== null) {
1118
                    $this->assertEquals($this->outputExpectedString, $this->output);
1119
                }
1120
            } catch (\Throwable $_e) {
1121
                $e = $_e;
1122
            }
1123
        }
1124
1125
        // Workaround for missing "finally".
1126
        if (isset($e)) {
1127
            if ($e instanceof PredictionException) {
1128
                $e = new AssertionFailedError($e->getMessage());
1129
            }
1130
1131
            $this->onNotSuccessfulTest($e);
1132
        }
1133
    }
1134
1135
    /**
1136
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1137
     */
1138
    public function setName(string $name): void
1139
    {
1140
        $this->name = $name;
1141
    }
1142
1143
    /**
1144
     * @param string[] $dependencies
1145
     *
1146
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1147
     */
1148
    public function setDependencies(array $dependencies): void
1149
    {
1150
        $this->dependencies = $dependencies;
1151
    }
1152
1153
    /**
1154
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1155
     */
1156
    public function getDependencies(): array
1157
    {
1158
        return $this->dependencies;
1159
    }
1160
1161
    /**
1162
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1163
     */
1164
    public function hasDependencies(): bool
1165
    {
1166
        return \count($this->dependencies) > 0;
1167
    }
1168
1169
    /**
1170
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1171
     */
1172
    public function setDependencyInput(array $dependencyInput): void
1173
    {
1174
        $this->dependencyInput = $dependencyInput;
1175
    }
1176
1177
    /**
1178
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1179
     */
1180
    public function getDependencyInput(): array
1181
    {
1182
        return $this->dependencyInput;
1183
    }
1184
1185
    /**
1186
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1187
     */
1188
    public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState): void
1189
    {
1190
        $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
1191
    }
1192
1193
    /**
1194
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1195
     */
1196
    public function setBackupGlobals(?bool $backupGlobals): void
1197
    {
1198
        if ($this->backupGlobals === null && $backupGlobals !== null) {
1199
            $this->backupGlobals = $backupGlobals;
1200
        }
1201
    }
1202
1203
    /**
1204
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1205
     */
1206
    public function setBackupStaticAttributes(?bool $backupStaticAttributes): void
1207
    {
1208
        if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) {
1209
            $this->backupStaticAttributes = $backupStaticAttributes;
1210
        }
1211
    }
1212
1213
    /**
1214
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1215
     */
1216
    public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void
1217
    {
1218
        if ($this->runTestInSeparateProcess === null) {
1219
            $this->runTestInSeparateProcess = $runTestInSeparateProcess;
1220
        }
1221
    }
1222
1223
    /**
1224
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1225
     */
1226
    public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void
1227
    {
1228
        if ($this->runClassInSeparateProcess === null) {
1229
            $this->runClassInSeparateProcess = $runClassInSeparateProcess;
1230
        }
1231
    }
1232
1233
    /**
1234
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1235
     */
1236
    public function setPreserveGlobalState(bool $preserveGlobalState): void
1237
    {
1238
        $this->preserveGlobalState = $preserveGlobalState;
1239
    }
1240
1241
    /**
1242
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1243
     */
1244
    public function setInIsolation(bool $inIsolation): void
1245
    {
1246
        $this->inIsolation = $inIsolation;
1247
    }
1248
1249
    /**
1250
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1251
     */
1252
    public function isInIsolation(): bool
1253
    {
1254
        return $this->inIsolation;
1255
    }
1256
1257
    /**
1258
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1259
     */
1260
    public function getResult()
1261
    {
1262
        return $this->testResult;
1263
    }
1264
1265
    /**
1266
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1267
     */
1268
    public function setResult($result): void
1269
    {
1270
        $this->testResult = $result;
1271
    }
1272
1273
    /**
1274
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1275
     */
1276
    public function setOutputCallback(callable $callback): void
1277
    {
1278
        $this->outputCallback = $callback;
1279
    }
1280
1281
    /**
1282
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1283
     */
1284
    public function getTestResultObject(): ?TestResult
1285
    {
1286
        return $this->result;
1287
    }
1288
1289
    /**
1290
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1291
     */
1292
    public function setTestResultObject(TestResult $result): void
1293
    {
1294
        $this->result = $result;
1295
    }
1296
1297
    /**
1298
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1299
     */
1300
    public function registerMockObject(MockObject $mockObject): void
1301
    {
1302
        $this->mockObjects[] = $mockObject;
1303
    }
1304
1305
    /**
1306
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1307
     */
1308
    public function addToAssertionCount(int $count): void
1309
    {
1310
        $this->numAssertions += $count;
1311
    }
1312
1313
    /**
1314
     * Returns the number of assertions performed by this test.
1315
     *
1316
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1317
     */
1318
    public function getNumAssertions(): int
1319
    {
1320
        return $this->numAssertions;
1321
    }
1322
1323
    /**
1324
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1325
     */
1326
    public function usesDataProvider(): bool
1327
    {
1328
        return !empty($this->data);
1329
    }
1330
1331
    /**
1332
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1333
     */
1334
    public function dataDescription(): string
1335
    {
1336
        return \is_string($this->dataName) ? $this->dataName : '';
1337
    }
1338
1339
    /**
1340
     * @return int|string
1341
     *
1342
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1343
     */
1344
    public function dataName()
1345
    {
1346
        return $this->dataName;
1347
    }
1348
1349
    /**
1350
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1351
     */
1352
    public function getDataSetAsString(bool $includeData = true): string
1353
    {
1354
        $buffer = '';
1355
1356
        if (!empty($this->data)) {
1357
            if (\is_int($this->dataName)) {
1358
                $buffer .= \sprintf(' with data set #%d', $this->dataName);
1359
            } else {
1360
                $buffer .= \sprintf(' with data set "%s"', $this->dataName);
1361
            }
1362
1363
            $exporter = new Exporter;
1364
1365
            if ($includeData) {
1366
                $buffer .= \sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data));
1367
            }
1368
        }
1369
1370
        return $buffer;
1371
    }
1372
1373
    /**
1374
     * Gets the data set of a TestCase.
1375
     *
1376
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1377
     */
1378
    public function getProvidedData(): array
1379
    {
1380
        return $this->data;
1381
    }
1382
1383
    /**
1384
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1385
     */
1386
    public function addWarning(string $warning): void
1387
    {
1388
        $this->warnings[] = $warning;
1389
    }
1390
1391
    /**
1392
     * Override to run the test and assert its state.
1393
     *
1394
     * @throws AssertionFailedError
1395
     * @throws Exception
1396
     * @throws ExpectationFailedException
1397
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
1398
     * @throws \Throwable
1399
     */
1400
    protected function runTest()
1401
    {
1402
        if (\trim($this->name) === '') {
1403
            throw new Exception(
1404
                'PHPUnit\Framework\TestCase::$name must be a non-blank string.'
1405
            );
1406
        }
1407
1408
        $testArguments = \array_merge($this->data, $this->dependencyInput);
1409
1410
        $this->registerMockObjectsFromTestArguments($testArguments);
1411
1412
        try {
1413
            $testResult = $this->{$this->name}(...\array_values($testArguments));
1414
        } catch (\Throwable $exception) {
1415
            if (!$this->checkExceptionExpectations($exception)) {
1416
                throw $exception;
1417
            }
1418
1419
            if ($this->expectedException !== null) {
1420
                $this->assertThat(
1421
                    $exception,
1422
                    new ExceptionConstraint(
1423
                        $this->expectedException
1424
                    )
1425
                );
1426
            }
1427
1428
            if ($this->expectedExceptionMessage !== null) {
1429
                $this->assertThat(
1430
                    $exception,
1431
                    new ExceptionMessage(
1432
                        $this->expectedExceptionMessage
1433
                    )
1434
                );
1435
            }
1436
1437
            if ($this->expectedExceptionMessageRegExp !== null) {
1438
                $this->assertThat(
1439
                    $exception,
1440
                    new ExceptionMessageRegularExpression(
1441
                        $this->expectedExceptionMessageRegExp
1442
                    )
1443
                );
1444
            }
1445
1446
            if ($this->expectedExceptionCode !== null) {
1447
                $this->assertThat(
1448
                    $exception,
1449
                    new ExceptionCode(
1450
                        $this->expectedExceptionCode
1451
                    )
1452
                );
1453
            }
1454
1455
            return;
1456
        }
1457
1458
        if ($this->expectedException !== null) {
1459
            $this->assertThat(
1460
                null,
1461
                new ExceptionConstraint(
1462
                    $this->expectedException
1463
                )
1464
            );
1465
        } elseif ($this->expectedExceptionMessage !== null) {
1466
            $this->numAssertions++;
1467
1468
            throw new AssertionFailedError(
1469
                \sprintf(
1470
                    'Failed asserting that exception with message "%s" is thrown',
1471
                    $this->expectedExceptionMessage
1472
                )
1473
            );
1474
        } elseif ($this->expectedExceptionMessageRegExp !== null) {
1475
            $this->numAssertions++;
1476
1477
            throw new AssertionFailedError(
1478
                \sprintf(
1479
                    'Failed asserting that exception with message matching "%s" is thrown',
1480
                    $this->expectedExceptionMessageRegExp
1481
                )
1482
            );
1483
        } elseif ($this->expectedExceptionCode !== null) {
1484
            $this->numAssertions++;
1485
1486
            throw new AssertionFailedError(
1487
                \sprintf(
1488
                    'Failed asserting that exception with code "%s" is thrown',
1489
                    $this->expectedExceptionCode
1490
                )
1491
            );
1492
        }
1493
1494
        return $testResult;
1495
    }
1496
1497
    /**
1498
     * This method is a wrapper for the ini_set() function that automatically
1499
     * resets the modified php.ini setting to its original value after the
1500
     * test is run.
1501
     *
1502
     * @throws Exception
1503
     */
1504
    protected function iniSet(string $varName, string $newValue): void
1505
    {
1506
        $currentValue = \ini_set($varName, $newValue);
1507
1508
        if ($currentValue !== false) {
1509
            $this->iniSettings[$varName] = $currentValue;
1510
        } else {
1511
            throw new Exception(
1512
                \sprintf(
1513
                    'INI setting "%s" could not be set to "%s".',
1514
                    $varName,
1515
                    $newValue
1516
                )
1517
            );
1518
        }
1519
    }
1520
1521
    /**
1522
     * This method is a wrapper for the setlocale() function that automatically
1523
     * resets the locale to its original value after the test is run.
1524
     *
1525
     * @throws Exception
1526
     */
1527
    protected function setLocale(...$args): void
1528
    {
1529
        if (\count($args) < 2) {
1530
            throw new Exception;
1531
        }
1532
1533
        [$category, $locale] = $args;
1534
1535
        if (\defined('LC_MESSAGES')) {
1536
            $categories[] = \LC_MESSAGES;
1537
        }
1538
1539
        if (!\in_array($category, self::LOCALE_CATEGORIES, true)) {
1540
            throw new Exception;
1541
        }
1542
1543
        if (!\is_array($locale) && !\is_string($locale)) {
1544
            throw new Exception;
1545
        }
1546
1547
        $this->locale[$category] = \setlocale($category, 0);
1548
1549
        $result = \setlocale(...$args);
1550
1551
        if ($result === false) {
1552
            throw new Exception(
1553
                'The locale functionality is not implemented on your platform, ' .
1554
                'the specified locale does not exist or the category name is ' .
1555
                'invalid.'
1556
            );
1557
        }
1558
    }
1559
1560
    /**
1561
     * Makes configurable stub for the specified class.
1562
     *
1563
     * @psalm-template RealInstanceType of object
1564
     * @psalm-param    class-string<RealInstanceType> $originalClassName
1565
     * @psalm-return   Stub&RealInstanceType
1566
     */
1567
    protected function createStub(string $originalClassName): Stub
1568
    {
1569
        return $this->createMock($originalClassName);
1570
    }
1571
1572
    /**
1573
     * Returns a mock object for the specified class.
1574
     *
1575
     * @psalm-template RealInstanceType of object
1576
     * @psalm-param class-string<RealInstanceType> $originalClassName
1577
     * @psalm-return MockObject&RealInstanceType
1578
     */
1579
    protected function createMock(string $originalClassName): MockObject
1580
    {
1581
        return $this->getMockBuilder($originalClassName)
1582
                    ->disableOriginalConstructor()
1583
                    ->disableOriginalClone()
1584
                    ->disableArgumentCloning()
1585
                    ->disallowMockingUnknownTypes()
1586
                    ->getMock();
1587
    }
1588
1589
    /**
1590
     * Returns a configured mock object for the specified class.
1591
     *
1592
     * @psalm-template RealInstanceType of object
1593
     * @psalm-param class-string<RealInstanceType> $originalClassName
1594
     * @psalm-return MockObject&RealInstanceType
1595
     */
1596
    protected function createConfiguredMock(string $originalClassName, array $configuration): MockObject
1597
    {
1598
        $o = $this->createMock($originalClassName);
1599
1600
        foreach ($configuration as $method => $return) {
1601
            $o->method($method)->willReturn($return);
1602
        }
1603
1604
        return $o;
1605
    }
1606
1607
    /**
1608
     * Returns a partial mock object for the specified class.
1609
     *
1610
     * @param string[] $methods
1611
     *
1612
     * @psalm-template RealInstanceType of object
1613
     * @psalm-param class-string<RealInstanceType> $originalClassName
1614
     * @psalm-return MockObject&RealInstanceType
1615
     */
1616
    protected function createPartialMock(string $originalClassName, array $methods): MockObject
1617
    {
1618
        try {
1619
            $reflector = new \ReflectionClass($originalClassName);
1620
            // @codeCoverageIgnoreStart
1621
        } catch (\ReflectionException $e) {
1622
            throw new Exception(
1623
                $e->getMessage(),
1624
                (int) $e->getCode(),
1625
                $e
1626
            );
1627
        }
1628
        // @codeCoverageIgnoreEnd
1629
1630
        $mockedMethodsThatDontExist = \array_filter(
1631
            $methods,
1632
            static function (string $method) use ($reflector) {
1633
                return !$reflector->hasMethod($method);
1634
            }
1635
        );
1636
1637
        if ($mockedMethodsThatDontExist) {
1638
            $this->addWarning(
1639
                \sprintf(
1640
                    'createPartialMock() called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.',
1641
                    \implode(', ', $mockedMethodsThatDontExist),
1642
                    $originalClassName
1643
                )
1644
            );
1645
        }
1646
1647
        return $this->getMockBuilder($originalClassName)
1648
                    ->disableOriginalConstructor()
1649
                    ->disableOriginalClone()
1650
                    ->disableArgumentCloning()
1651
                    ->disallowMockingUnknownTypes()
1652
                    ->setMethods(empty($methods) ? null : $methods)
1653
                    ->getMock();
1654
    }
1655
1656
    /**
1657
     * Returns a test proxy for the specified class.
1658
     *
1659
     * @psalm-template RealInstanceType of object
1660
     * @psalm-param class-string<RealInstanceType> $originalClassName
1661
     * @psalm-return MockObject&RealInstanceType
1662
     */
1663
    protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject
1664
    {
1665
        return $this->getMockBuilder($originalClassName)
1666
                    ->setConstructorArgs($constructorArguments)
1667
                    ->enableProxyingToOriginalMethods()
1668
                    ->getMock();
1669
    }
1670
1671
    /**
1672
     * Mocks the specified class and returns the name of the mocked class.
1673
     *
1674
     * @param null|array $methods $methods
1675
     *
1676
     * @psalm-template RealInstanceType of object
1677
     * @psalm-param class-string<RealInstanceType>|string $originalClassName
1678
     * @psalm-return class-string<MockObject&RealInstanceType>
1679
     */
1680
    protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = false, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = false): string
1681
    {
1682
        $this->recordDoubledType($originalClassName);
1683
1684
        $mock = $this->getMockObjectGenerator()->getMock(
1685
            $originalClassName,
1686
            $methods,
1687
            $arguments,
1688
            $mockClassName,
1689
            $callOriginalConstructor,
1690
            $callOriginalClone,
1691
            $callAutoload,
1692
            $cloneArguments
1693
        );
1694
1695
        return \get_class($mock);
1696
    }
1697
1698
    /**
1699
     * Returns a mock object for the specified abstract class with all abstract
1700
     * methods of the class mocked. Concrete methods are not mocked by default.
1701
     * To mock concrete methods, use the 7th parameter ($mockedMethods).
1702
     *
1703
     * @psalm-template RealInstanceType of object
1704
     * @psalm-param class-string<RealInstanceType> $originalClassName
1705
     * @psalm-return MockObject&RealInstanceType
1706
     */
1707
    protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject
1708
    {
1709
        $this->recordDoubledType($originalClassName);
1710
1711
        $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass(
1712
            $originalClassName,
1713
            $arguments,
1714
            $mockClassName,
1715
            $callOriginalConstructor,
1716
            $callOriginalClone,
1717
            $callAutoload,
1718
            $mockedMethods,
1719
            $cloneArguments
1720
        );
1721
1722
        $this->registerMockObject($mockObject);
1723
1724
        return $mockObject;
1725
    }
1726
1727
    /**
1728
     * Returns a mock object based on the given WSDL file.
1729
     *
1730
     * @psalm-template RealInstanceType of object
1731
     * @psalm-param class-string<RealInstanceType>|string $originalClassName
1732
     * @psalm-return MockObject&RealInstanceType
1733
     */
1734
    protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = true, array $options = []): MockObject
1735
    {
1736
        $this->recordDoubledType(\SoapClient::class);
1737
1738
        if ($originalClassName === '') {
1739
            $fileName          = \pathinfo(\basename(\parse_url($wsdlFile, \PHP_URL_PATH)), \PATHINFO_FILENAME);
1740
            $originalClassName = \preg_replace('/\W/', '', $fileName);
1741
        }
1742
1743
        if (!\class_exists($originalClassName)) {
1744
            eval(
1745
                $this->getMockObjectGenerator()->generateClassFromWsdl(
1746
                    $wsdlFile,
1747
                    $originalClassName,
1748
                    $methods,
1749
                    $options
1750
                )
1751
            );
1752
        }
1753
1754
        $mockObject = $this->getMockObjectGenerator()->getMock(
1755
            $originalClassName,
1756
            $methods,
1757
            ['', $options],
1758
            $mockClassName,
1759
            $callOriginalConstructor,
1760
            false,
1761
            false
1762
        );
1763
1764
        $this->registerMockObject($mockObject);
1765
1766
        return $mockObject;
1767
    }
1768
1769
    /**
1770
     * Returns a mock object for the specified trait with all abstract methods
1771
     * of the trait mocked. Concrete methods to mock can be specified with the
1772
     * `$mockedMethods` parameter.
1773
     */
1774
    protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject
1775
    {
1776
        $this->recordDoubledType($traitName);
1777
1778
        $mockObject = $this->getMockObjectGenerator()->getMockForTrait(
1779
            $traitName,
1780
            $arguments,
1781
            $mockClassName,
1782
            $callOriginalConstructor,
1783
            $callOriginalClone,
1784
            $callAutoload,
1785
            $mockedMethods,
1786
            $cloneArguments
1787
        );
1788
1789
        $this->registerMockObject($mockObject);
1790
1791
        return $mockObject;
1792
    }
1793
1794
    /**
1795
     * Returns an object for the specified trait.
1796
     */
1797
    protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true): object
1798
    {
1799
        $this->recordDoubledType($traitName);
1800
1801
        return $this->getMockObjectGenerator()->getObjectForTrait(
1802
            $traitName,
1803
            $traitClassName,
1804
            $callAutoload,
1805
            $callOriginalConstructor,
1806
            $arguments
1807
        );
1808
    }
1809
1810
    /**
1811
     * @throws \Prophecy\Exception\Doubler\ClassNotFoundException
1812
     * @throws \Prophecy\Exception\Doubler\DoubleException
1813
     * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException
1814
     *
1815
     * @psalm-param class-string|null $classOrInterface
1816
     */
1817
    protected function prophesize(?string $classOrInterface = null): ObjectProphecy
1818
    {
1819
        $this->addWarning('PHPUnit\Framework\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.');
1820
1821
        if (\is_string($classOrInterface)) {
1822
            $this->recordDoubledType($classOrInterface);
1823
        }
1824
1825
        return $this->getProphet()->prophesize($classOrInterface);
1826
    }
1827
1828
    /**
1829
     * Creates a default TestResult object.
1830
     *
1831
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1832
     */
1833
    protected function createResult(): TestResult
1834
    {
1835
        return new TestResult;
1836
    }
1837
1838
    /**
1839
     * Performs assertions shared by all tests of a test case.
1840
     *
1841
     * This method is called between setUp() and test.
1842
     */
1843
    protected function assertPreConditions(): void
1844
    {
1845
    }
1846
1847
    /**
1848
     * Performs assertions shared by all tests of a test case.
1849
     *
1850
     * This method is called between test and tearDown().
1851
     */
1852
    protected function assertPostConditions(): void
1853
    {
1854
    }
1855
1856
    /**
1857
     * This method is called when a test method did not execute successfully.
1858
     *
1859
     * @throws \Throwable
1860
     */
1861
    protected function onNotSuccessfulTest(\Throwable $t): void
1862
    {
1863
        throw $t;
1864
    }
1865
1866
    protected function recordDoubledType(string $originalClassName): void
1867
    {
1868
        $this->doubledTypes[] = $originalClassName;
1869
    }
1870
1871
    /**
1872
     * @throws \Throwable
1873
     */
1874
    private function verifyMockObjects(): void
1875
    {
1876
        foreach ($this->mockObjects as $mockObject) {
1877
            if ($mockObject->__phpunit_hasMatchers()) {
1878
                $this->numAssertions++;
1879
            }
1880
1881
            $mockObject->__phpunit_verify(
1882
                $this->shouldInvocationMockerBeReset($mockObject)
1883
            );
1884
        }
1885
1886
        if ($this->prophet !== null) {
1887
            try {
1888
                $this->prophet->checkPredictions();
1889
            } finally {
1890
                foreach ($this->prophet->getProphecies() as $objectProphecy) {
1891
                    foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) {
1892
                        foreach ($methodProphecies as $methodProphecy) {
1893
                            \assert($methodProphecy instanceof MethodProphecy);
1894
1895
                            $this->numAssertions += \count($methodProphecy->getCheckedPredictions());
1896
                        }
1897
                    }
1898
                }
1899
            }
1900
        }
1901
    }
1902
1903
    /**
1904
     * @throws Warning
1905
     * @throws SkippedTestError
1906
     * @throws SyntheticSkippedError
1907
     */
1908
    private function checkRequirements(): void
1909
    {
1910
        if (!$this->name || !\method_exists($this, $this->name)) {
1911
            return;
1912
        }
1913
1914
        $missingRequirements = TestUtil::getMissingRequirements(
1915
            \get_class($this),
1916
            $this->name
1917
        );
1918
1919
        if (!empty($missingRequirements)) {
1920
            $this->markTestSkipped(\implode(\PHP_EOL, $missingRequirements));
1921
        }
1922
    }
1923
1924
    private function handleDependencies(): bool
1925
    {
1926
        if (!empty($this->dependencies) && !$this->inIsolation) {
1927
            $className  = \get_class($this);
1928
            $passed     = $this->result->passed();
1929
            $passedKeys = \array_keys($passed);
1930
1931
            foreach ($passedKeys as $key => $value) {
1932
                $pos = \strpos($value, ' with data set');
1933
1934
                if ($pos !== false) {
1935
                    $passedKeys[$key] = \substr($value, 0, $pos);
1936
                }
1937
            }
1938
1939
            $passedKeys = \array_flip(\array_unique($passedKeys));
1940
1941
            foreach ($this->dependencies as $dependency) {
1942
                $deepClone    = false;
1943
                $shallowClone = false;
1944
1945
                if (empty($dependency)) {
1946
                    $this->markSkippedForNotSpecifyingDependency();
1947
1948
                    return false;
1949
                }
1950
1951
                if (\strpos($dependency, 'clone ') === 0) {
1952
                    $deepClone  = true;
1953
                    $dependency = \substr($dependency, \strlen('clone '));
1954
                } elseif (\strpos($dependency, '!clone ') === 0) {
1955
                    $deepClone  = false;
1956
                    $dependency = \substr($dependency, \strlen('!clone '));
1957
                }
1958
1959
                if (\strpos($dependency, 'shallowClone ') === 0) {
1960
                    $shallowClone = true;
1961
                    $dependency   = \substr($dependency, \strlen('shallowClone '));
1962
                } elseif (\strpos($dependency, '!shallowClone ') === 0) {
1963
                    $shallowClone = false;
1964
                    $dependency   = \substr($dependency, \strlen('!shallowClone '));
1965
                }
1966
1967
                if (\strpos($dependency, '::') === false) {
1968
                    $dependency = $className . '::' . $dependency;
1969
                }
1970
1971
                if (!isset($passedKeys[$dependency])) {
1972
                    if (!$this->isCallableTestMethod($dependency)) {
1973
                        $this->warnAboutDependencyThatDoesNotExist($dependency);
1974
                    } else {
1975
                        $this->markSkippedForMissingDependency($dependency);
1976
                    }
1977
1978
                    return false;
1979
                }
1980
1981
                if (isset($passed[$dependency])) {
1982
                    if ($passed[$dependency]['size'] !== TestUtil::UNKNOWN &&
1983
                        $this->getSize() !== TestUtil::UNKNOWN &&
1984
                        $passed[$dependency]['size'] > $this->getSize()) {
1985
                        $this->result->addError(
1986
                            $this,
1987
                            new SkippedTestError(
1988
                                'This test depends on a test that is larger than itself.'
1989
                            ),
1990
                            0
1991
                        );
1992
1993
                        return false;
1994
                    }
1995
1996
                    if ($deepClone) {
1997
                        $deepCopy = new DeepCopy;
1998
                        $deepCopy->skipUncloneable(false);
1999
2000
                        $this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']);
2001
                    } elseif ($shallowClone) {
2002
                        $this->dependencyInput[$dependency] = clone $passed[$dependency]['result'];
2003
                    } else {
2004
                        $this->dependencyInput[$dependency] = $passed[$dependency]['result'];
2005
                    }
2006
                } else {
2007
                    $this->dependencyInput[$dependency] = null;
2008
                }
2009
            }
2010
        }
2011
2012
        return true;
2013
    }
2014
2015
    private function markSkippedForNotSpecifyingDependency(): void
2016
    {
2017
        $this->status = BaseTestRunner::STATUS_SKIPPED;
2018
2019
        $this->result->startTest($this);
2020
2021
        $this->result->addError(
2022
            $this,
2023
            new SkippedTestError(
2024
                \sprintf('This method has an invalid @depends annotation.')
2025
            ),
2026
            0
2027
        );
2028
2029
        $this->result->endTest($this, 0);
2030
    }
2031
2032
    private function markSkippedForMissingDependency(string $dependency): void
2033
    {
2034
        $this->status = BaseTestRunner::STATUS_SKIPPED;
2035
2036
        $this->result->startTest($this);
2037
2038
        $this->result->addError(
2039
            $this,
2040
            new SkippedTestError(
2041
                \sprintf(
2042
                    'This test depends on "%s" to pass.',
2043
                    $dependency
2044
                )
2045
            ),
2046
            0
2047
        );
2048
2049
        $this->result->endTest($this, 0);
2050
    }
2051
2052
    private function warnAboutDependencyThatDoesNotExist(string $dependency): void
2053
    {
2054
        $this->status = BaseTestRunner::STATUS_WARNING;
2055
2056
        $this->result->startTest($this);
2057
2058
        $this->result->addWarning(
2059
            $this,
2060
            new Warning(
2061
                \sprintf(
2062
                    'This test depends on "%s" which does not exist.',
2063
                    $dependency
2064
                )
2065
            ),
2066
            0
2067
        );
2068
2069
        $this->result->endTest($this, 0);
2070
    }
2071
2072
    /**
2073
     * Get the mock object generator, creating it if it doesn't exist.
2074
     */
2075
    private function getMockObjectGenerator(): MockGenerator
2076
    {
2077
        if ($this->mockObjectGenerator === null) {
2078
            $this->mockObjectGenerator = new MockGenerator;
2079
        }
2080
2081
        return $this->mockObjectGenerator;
2082
    }
2083
2084
    private function startOutputBuffering(): void
2085
    {
2086
        \ob_start();
2087
2088
        $this->outputBufferingActive = true;
2089
        $this->outputBufferingLevel  = \ob_get_level();
2090
    }
2091
2092
    /**
2093
     * @throws RiskyTestError
2094
     */
2095
    private function stopOutputBuffering(): void
2096
    {
2097
        if (\ob_get_level() !== $this->outputBufferingLevel) {
2098
            while (\ob_get_level() >= $this->outputBufferingLevel) {
2099
                \ob_end_clean();
2100
            }
2101
2102
            throw new RiskyTestError(
2103
                'Test code or tested code did not (only) close its own output buffers'
2104
            );
2105
        }
2106
2107
        $this->output = \ob_get_contents();
2108
2109
        if ($this->outputCallback !== false) {
2110
            $this->output = (string) \call_user_func($this->outputCallback, $this->output);
2111
        }
2112
2113
        \ob_end_clean();
2114
2115
        $this->outputBufferingActive = false;
2116
        $this->outputBufferingLevel  = \ob_get_level();
2117
    }
2118
2119
    private function snapshotGlobalState(): void
2120
    {
2121
        if ($this->runTestInSeparateProcess || $this->inIsolation ||
2122
            (!$this->backupGlobals && !$this->backupStaticAttributes)) {
2123
            return;
2124
        }
2125
2126
        $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === true);
2127
    }
2128
2129
    /**
2130
     * @throws RiskyTestError
2131
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2132
     */
2133
    private function restoreGlobalState(): void
2134
    {
2135
        if (!$this->snapshot instanceof Snapshot) {
2136
            return;
2137
        }
2138
2139
        if ($this->beStrictAboutChangesToGlobalState) {
2140
            try {
2141
                $this->compareGlobalStateSnapshots(
2142
                    $this->snapshot,
2143
                    $this->createGlobalStateSnapshot($this->backupGlobals === true)
2144
                );
2145
            } catch (RiskyTestError $rte) {
2146
                // Intentionally left empty
2147
            }
2148
        }
2149
2150
        $restorer = new Restorer;
2151
2152
        if ($this->backupGlobals) {
2153
            $restorer->restoreGlobalVariables($this->snapshot);
2154
        }
2155
2156
        if ($this->backupStaticAttributes) {
2157
            $restorer->restoreStaticAttributes($this->snapshot);
2158
        }
2159
2160
        $this->snapshot = null;
2161
2162
        if (isset($rte)) {
2163
            throw $rte;
2164
        }
2165
    }
2166
2167
    private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot
2168
    {
2169
        $blacklist = new Blacklist;
2170
2171
        foreach ($this->backupGlobalsBlacklist as $globalVariable) {
2172
            $blacklist->addGlobalVariable($globalVariable);
2173
        }
2174
2175
        if (!\defined('PHPUNIT_TESTSUITE')) {
2176
            $blacklist->addClassNamePrefix('PHPUnit');
2177
            $blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage');
2178
            $blacklist->addClassNamePrefix('SebastianBergmann\FileIterator');
2179
            $blacklist->addClassNamePrefix('SebastianBergmann\Invoker');
2180
            $blacklist->addClassNamePrefix('SebastianBergmann\Template');
2181
            $blacklist->addClassNamePrefix('SebastianBergmann\Timer');
2182
            $blacklist->addClassNamePrefix('PHP_Token');
2183
            $blacklist->addClassNamePrefix('Symfony');
2184
            $blacklist->addClassNamePrefix('Doctrine\Instantiator');
2185
            $blacklist->addClassNamePrefix('Prophecy');
2186
2187
            foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) {
2188
                foreach ($attributes as $attribute) {
2189
                    $blacklist->addStaticAttribute($class, $attribute);
2190
                }
2191
            }
2192
        }
2193
2194
        return new Snapshot(
2195
            $blacklist,
2196
            $backupGlobals,
2197
            (bool) $this->backupStaticAttributes,
2198
            false,
2199
            false,
2200
            false,
2201
            false,
2202
            false,
2203
            false,
2204
            false
2205
        );
2206
    }
2207
2208
    /**
2209
     * @throws RiskyTestError
2210
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2211
     */
2212
    private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void
2213
    {
2214
        $backupGlobals = $this->backupGlobals === null || $this->backupGlobals;
2215
2216
        if ($backupGlobals) {
2217
            $this->compareGlobalStateSnapshotPart(
2218
                $before->globalVariables(),
2219
                $after->globalVariables(),
2220
                "--- Global variables before the test\n+++ Global variables after the test\n"
2221
            );
2222
2223
            $this->compareGlobalStateSnapshotPart(
2224
                $before->superGlobalVariables(),
2225
                $after->superGlobalVariables(),
2226
                "--- Super-global variables before the test\n+++ Super-global variables after the test\n"
2227
            );
2228
        }
2229
2230
        if ($this->backupStaticAttributes) {
2231
            $this->compareGlobalStateSnapshotPart(
2232
                $before->staticAttributes(),
2233
                $after->staticAttributes(),
2234
                "--- Static attributes before the test\n+++ Static attributes after the test\n"
2235
            );
2236
        }
2237
    }
2238
2239
    /**
2240
     * @throws RiskyTestError
2241
     */
2242
    private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void
2243
    {
2244
        if ($before != $after) {
2245
            $differ   = new Differ($header);
2246
            $exporter = new Exporter;
2247
2248
            $diff = $differ->diff(
2249
                $exporter->export($before),
2250
                $exporter->export($after)
2251
            );
2252
2253
            throw new RiskyTestError(
2254
                $diff
2255
            );
2256
        }
2257
    }
2258
2259
    private function getProphet(): Prophet
2260
    {
2261
        if ($this->prophet === null) {
2262
            $this->prophet = new Prophet;
2263
        }
2264
2265
        return $this->prophet;
2266
    }
2267
2268
    /**
2269
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
2270
     */
2271
    private function shouldInvocationMockerBeReset(MockObject $mock): bool
2272
    {
2273
        $enumerator = new Enumerator;
2274
2275
        foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
2276
            if ($mock === $object) {
2277
                return false;
2278
            }
2279
        }
2280
2281
        if (!\is_array($this->testResult) && !\is_object($this->testResult)) {
2282
            return true;
2283
        }
2284
2285
        return !\in_array($mock, $enumerator->enumerate($this->testResult), true);
2286
    }
2287
2288
    /**
2289
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
2290
     * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException
2291
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2292
     */
2293
    private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void
2294
    {
2295
        if ($this->registerMockObjectsFromTestArgumentsRecursively) {
2296
            foreach ((new Enumerator)->enumerate($testArguments) as $object) {
2297
                if ($object instanceof MockObject) {
2298
                    $this->registerMockObject($object);
2299
                }
2300
            }
2301
        } else {
2302
            foreach ($testArguments as $testArgument) {
2303
                if ($testArgument instanceof MockObject) {
2304
                    if (Type::isCloneable($testArgument)) {
2305
                        $testArgument = clone $testArgument;
2306
                    }
2307
2308
                    $this->registerMockObject($testArgument);
2309
                } elseif (\is_array($testArgument) && !\in_array($testArgument, $visited, true)) {
2310
                    $visited[] = $testArgument;
2311
2312
                    $this->registerMockObjectsFromTestArguments(
2313
                        $testArgument,
2314
                        $visited
2315
                    );
2316
                }
2317
            }
2318
        }
2319
    }
2320
2321
    private function setDoesNotPerformAssertionsFromAnnotation(): void
2322
    {
2323
        $annotations = $this->getAnnotations();
2324
2325
        if (isset($annotations['method']['doesNotPerformAssertions'])) {
2326
            $this->doesNotPerformAssertions = true;
2327
        }
2328
    }
2329
2330
    private function unregisterCustomComparators(): void
2331
    {
2332
        $factory = ComparatorFactory::getInstance();
2333
2334
        foreach ($this->customComparators as $comparator) {
2335
            $factory->unregister($comparator);
2336
        }
2337
2338
        $this->customComparators = [];
2339
    }
2340
2341
    private function cleanupIniSettings(): void
2342
    {
2343
        foreach ($this->iniSettings as $varName => $oldValue) {
2344
            \ini_set($varName, $oldValue);
2345
        }
2346
2347
        $this->iniSettings = [];
2348
    }
2349
2350
    private function cleanupLocaleSettings(): void
2351
    {
2352
        foreach ($this->locale as $category => $locale) {
2353
            \setlocale($category, $locale);
2354
        }
2355
2356
        $this->locale = [];
2357
    }
2358
2359
    /**
2360
     * @throws Exception
2361
     */
2362
    private function checkExceptionExpectations(\Throwable $throwable): bool
2363
    {
2364
        $result = false;
2365
2366
        if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) {
2367
            $result = true;
2368
        }
2369
2370
        if ($throwable instanceof Exception) {
2371
            $result = false;
2372
        }
2373
2374
        if (\is_string($this->expectedException)) {
2375
            try {
2376
                $reflector = new \ReflectionClass($this->expectedException);
2377
                // @codeCoverageIgnoreStart
2378
            } catch (\ReflectionException $e) {
2379
                throw new Exception(
2380
                    $e->getMessage(),
2381
                    (int) $e->getCode(),
2382
                    $e
2383
                );
2384
            }
2385
            // @codeCoverageIgnoreEnd
2386
2387
            if ($this->expectedException === 'PHPUnit\Framework\Exception' ||
2388
                $this->expectedException === '\PHPUnit\Framework\Exception' ||
2389
                $reflector->isSubclassOf(Exception::class)) {
2390
                $result = true;
2391
            }
2392
        }
2393
2394
        return $result;
2395
    }
2396
2397
    private function runInSeparateProcess(): bool
2398
    {
2399
        return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) &&
2400
            !$this->inIsolation && !$this instanceof PhptTestCase;
2401
    }
2402
2403
    private function isCallableTestMethod(string $dependency): bool
2404
    {
2405
        [$className, $methodName] = \explode('::', $dependency);
2406
2407
        if (!\class_exists($className)) {
2408
            return false;
2409
        }
2410
2411
        try {
2412
            $class = new \ReflectionClass($className);
2413
        } catch (\ReflectionException $e) {
2414
            return false;
2415
        }
2416
2417
        if (!$class->isSubclassOf(__CLASS__)) {
2418
            return false;
2419
        }
2420
2421
        if (!$class->hasMethod($methodName)) {
2422
            return false;
2423
        }
2424
2425
        try {
2426
            $method = $class->getMethod($methodName);
2427
        } catch (\ReflectionException $e) {
2428
            return false;
2429
        }
2430
2431
        return TestUtil::isTestMethod($method);
2432
    }
2433
}
2434