Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

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