Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | 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:
621 lars 602
                $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');
148 lars 603
 
604
                break;
605
 
606
            case Error::class:
621 lars 607
                $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');
148 lars 608
 
609
                break;
610
 
611
            case Notice::class:
621 lars 612
                $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');
148 lars 613
 
614
                break;
615
 
616
            case WarningError::class:
621 lars 617
                $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');
148 lars 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
 
621 lars 661
    /**
662
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
663
     */
148 lars 664
    public function expectDeprecation(): void
665
    {
621 lars 666
        $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');
667
 
148 lars 668
        $this->expectedException = Deprecated::class;
669
    }
670
 
621 lars 671
    /**
672
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
673
     */
148 lars 674
    public function expectDeprecationMessage(string $message): void
675
    {
621 lars 676
        $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');
677
 
148 lars 678
        $this->expectExceptionMessage($message);
679
    }
680
 
621 lars 681
    /**
682
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
683
     */
148 lars 684
    public function expectDeprecationMessageMatches(string $regularExpression): void
685
    {
621 lars 686
        $this->addWarning('Expecting E_DEPRECATED and E_USER_DEPRECATED is deprecated and will no longer be possible in PHPUnit 10.');
687
 
148 lars 688
        $this->expectExceptionMessageMatches($regularExpression);
689
    }
690
 
621 lars 691
    /**
692
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
693
     */
148 lars 694
    public function expectNotice(): void
695
    {
621 lars 696
        $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');
697
 
148 lars 698
        $this->expectedException = Notice::class;
699
    }
700
 
621 lars 701
    /**
702
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
703
     */
148 lars 704
    public function expectNoticeMessage(string $message): void
705
    {
621 lars 706
        $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');
707
 
148 lars 708
        $this->expectExceptionMessage($message);
709
    }
710
 
621 lars 711
    /**
712
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
713
     */
148 lars 714
    public function expectNoticeMessageMatches(string $regularExpression): void
715
    {
621 lars 716
        $this->addWarning('Expecting E_STRICT, E_NOTICE, and E_USER_NOTICE is deprecated and will no longer be possible in PHPUnit 10.');
717
 
148 lars 718
        $this->expectExceptionMessageMatches($regularExpression);
719
    }
720
 
621 lars 721
    /**
722
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
723
     */
148 lars 724
    public function expectWarning(): void
725
    {
621 lars 726
        $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');
727
 
148 lars 728
        $this->expectedException = WarningError::class;
729
    }
730
 
621 lars 731
    /**
732
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
733
     */
148 lars 734
    public function expectWarningMessage(string $message): void
735
    {
621 lars 736
        $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');
737
 
148 lars 738
        $this->expectExceptionMessage($message);
739
    }
740
 
621 lars 741
    /**
742
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
743
     */
148 lars 744
    public function expectWarningMessageMatches(string $regularExpression): void
745
    {
621 lars 746
        $this->addWarning('Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.');
747
 
148 lars 748
        $this->expectExceptionMessageMatches($regularExpression);
749
    }
750
 
621 lars 751
    /**
752
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
753
     */
148 lars 754
    public function expectError(): void
755
    {
621 lars 756
        $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');
757
 
148 lars 758
        $this->expectedException = Error::class;
759
    }
760
 
621 lars 761
    /**
762
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
763
     */
148 lars 764
    public function expectErrorMessage(string $message): void
765
    {
621 lars 766
        $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');
767
 
148 lars 768
        $this->expectExceptionMessage($message);
769
    }
770
 
621 lars 771
    /**
772
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/5062
773
     */
148 lars 774
    public function expectErrorMessageMatches(string $regularExpression): void
775
    {
621 lars 776
        $this->addWarning('Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.');
777
 
148 lars 778
        $this->expectExceptionMessageMatches($regularExpression);
779
    }
780
 
781
    public function getStatus(): int
782
    {
783
        return $this->status;
784
    }
785
 
786
    public function markAsRisky(): void
787
    {
788
        $this->status = BaseTestRunner::STATUS_RISKY;
789
    }
790
 
791
    public function getStatusMessage(): string
792
    {
793
        return $this->statusMessage;
794
    }
795
 
796
    public function hasFailed(): bool
797
    {
798
        $status = $this->getStatus();
799
 
800
        return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR;
801
    }
802
 
803
    /**
804
     * Runs the test case and collects the results in a TestResult object.
805
     * If no TestResult object is passed a new one will be created.
806
     *
807
     * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
808
     * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException
809
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
810
     * @throws CodeCoverageException
811
     * @throws UtilException
812
     */
813
    public function run(TestResult $result = null): TestResult
814
    {
815
        if ($result === null) {
816
            $result = $this->createResult();
817
        }
818
 
819
        if (!$this instanceof ErrorTestCase && !$this instanceof WarningTestCase) {
820
            $this->setTestResultObject($result);
821
        }
822
 
823
        if (!$this instanceof ErrorTestCase &&
824
            !$this instanceof WarningTestCase &&
825
            !$this instanceof SkippedTestCase &&
826
            !$this->handleDependencies()) {
827
            return $result;
828
        }
829
 
830
        if ($this->runInSeparateProcess()) {
831
            $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess;
832
 
833
            try {
834
                $class = new ReflectionClass($this);
835
                // @codeCoverageIgnoreStart
836
            } catch (ReflectionException $e) {
837
                throw new Exception(
838
                    $e->getMessage(),
839
                    $e->getCode(),
840
                    $e
841
                );
842
            }
843
            // @codeCoverageIgnoreEnd
844
 
845
            if ($runEntireClass) {
846
                $template = new Template(
847
                    __DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'
848
                );
849
            } else {
850
                $template = new Template(
851
                    __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'
852
                );
853
            }
854
 
855
            if ($this->preserveGlobalState) {
856
                $constants     = GlobalState::getConstantsAsString();
857
                $globals       = GlobalState::getGlobalsAsString();
858
                $includedFiles = GlobalState::getIncludedFilesAsString();
859
                $iniSettings   = GlobalState::getIniSettingsAsString();
860
            } else {
861
                $constants = '';
862
 
863
                if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
864
                    $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n";
865
                } else {
866
                    $globals = '';
867
                }
868
 
869
                $includedFiles = '';
870
                $iniSettings   = '';
871
            }
872
 
873
            $coverage                                   = $result->getCollectCodeCoverageInformation() ? 'true' : 'false';
874
            $isStrictAboutTestsThatDoNotTestAnything    = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false';
875
            $isStrictAboutOutputDuringTests             = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false';
876
            $enforcesTimeLimit                          = $result->enforcesTimeLimit() ? 'true' : 'false';
877
            $isStrictAboutTodoAnnotatedTests            = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false';
878
            $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false';
879
 
880
            if (defined('PHPUNIT_COMPOSER_INSTALL')) {
881
                $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true);
882
            } else {
883
                $composerAutoload = '\'\'';
884
            }
885
 
886
            if (defined('__PHPUNIT_PHAR__')) {
887
                $phar = var_export(__PHPUNIT_PHAR__, true);
888
            } else {
889
                $phar = '\'\'';
890
            }
891
 
892
            $codeCoverage               = $result->getCodeCoverage();
893
            $codeCoverageFilter         = null;
894
            $cachesStaticAnalysis       = 'false';
895
            $codeCoverageCacheDirectory = null;
896
            $driverMethod               = 'forLineCoverage';
897
 
898
            if ($codeCoverage) {
899
                $codeCoverageFilter = $codeCoverage->filter();
900
 
901
                if ($codeCoverage->collectsBranchAndPathCoverage()) {
902
                    $driverMethod = 'forLineAndPathCoverage';
903
                }
904
 
905
                if ($codeCoverage->cachesStaticAnalysis()) {
906
                    $cachesStaticAnalysis       = 'true';
907
                    $codeCoverageCacheDirectory = $codeCoverage->cacheDirectory();
908
                }
909
            }
910
 
911
            $data                       = var_export(serialize($this->data), true);
912
            $dataName                   = var_export($this->dataName, true);
913
            $dependencyInput            = var_export(serialize($this->dependencyInput), true);
914
            $includePath                = var_export(get_include_path(), true);
915
            $codeCoverageFilter         = var_export(serialize($codeCoverageFilter), true);
916
            $codeCoverageCacheDirectory = var_export(serialize($codeCoverageCacheDirectory), true);
917
            // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC
918
            // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences
919
            $data                       = "'." . $data . ".'";
920
            $dataName                   = "'.(" . $dataName . ").'";
921
            $dependencyInput            = "'." . $dependencyInput . ".'";
922
            $includePath                = "'." . $includePath . ".'";
923
            $codeCoverageFilter         = "'." . $codeCoverageFilter . ".'";
924
            $codeCoverageCacheDirectory = "'." . $codeCoverageCacheDirectory . ".'";
925
 
926
            $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? '';
927
 
928
            $var = [
929
                'composerAutoload'                           => $composerAutoload,
930
                'phar'                                       => $phar,
931
                'filename'                                   => $class->getFileName(),
932
                'className'                                  => $class->getName(),
933
                'collectCodeCoverageInformation'             => $coverage,
934
                'cachesStaticAnalysis'                       => $cachesStaticAnalysis,
935
                'codeCoverageCacheDirectory'                 => $codeCoverageCacheDirectory,
936
                'driverMethod'                               => $driverMethod,
937
                'data'                                       => $data,
938
                'dataName'                                   => $dataName,
939
                'dependencyInput'                            => $dependencyInput,
940
                'constants'                                  => $constants,
941
                'globals'                                    => $globals,
942
                'include_path'                               => $includePath,
943
                'included_files'                             => $includedFiles,
944
                'iniSettings'                                => $iniSettings,
945
                'isStrictAboutTestsThatDoNotTestAnything'    => $isStrictAboutTestsThatDoNotTestAnything,
946
                'isStrictAboutOutputDuringTests'             => $isStrictAboutOutputDuringTests,
947
                'enforcesTimeLimit'                          => $enforcesTimeLimit,
948
                'isStrictAboutTodoAnnotatedTests'            => $isStrictAboutTodoAnnotatedTests,
949
                'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests,
950
                'codeCoverageFilter'                         => $codeCoverageFilter,
951
                'configurationFilePath'                      => $configurationFilePath,
952
                'name'                                       => $this->getName(false),
953
            ];
954
 
955
            if (!$runEntireClass) {
956
                $var['methodName'] = $this->name;
957
            }
958
 
959
            $template->setVar($var);
960
 
961
            $php = AbstractPhpProcess::factory();
962
            $php->runTestJob($template->render(), $this, $result);
963
        } else {
964
            $result->run($this);
965
        }
966
 
967
        $this->result = null;
968
 
969
        return $result;
970
    }
971
 
972
    /**
973
     * Returns a builder object to create mock objects using a fluent interface.
974
     *
975
     * @psalm-template RealInstanceType of object
976
     *
977
     * @psalm-param class-string<RealInstanceType> $className
978
     *
979
     * @psalm-return MockBuilder<RealInstanceType>
980
     */
981
    public function getMockBuilder(string $className): MockBuilder
982
    {
983
        $this->recordDoubledType($className);
984
 
985
        return new MockBuilder($this, $className);
986
    }
987
 
988
    public function registerComparator(Comparator $comparator): void
989
    {
990
        ComparatorFactory::getInstance()->register($comparator);
991
 
992
        $this->customComparators[] = $comparator;
993
    }
994
 
995
    /**
996
     * @return string[]
997
     *
998
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
999
     */
1000
    public function doubledTypes(): array
1001
    {
1002
        return array_unique($this->doubledTypes);
1003
    }
1004
 
1005
    /**
1006
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1007
     */
1008
    public function getGroups(): array
1009
    {
1010
        return $this->groups;
1011
    }
1012
 
1013
    /**
1014
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1015
     */
1016
    public function setGroups(array $groups): void
1017
    {
1018
        $this->groups = $groups;
1019
    }
1020
 
1021
    /**
1022
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1023
     *
1024
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1025
     */
1026
    public function getName(bool $withDataSet = true): string
1027
    {
1028
        if ($withDataSet) {
1029
            return $this->name . $this->getDataSetAsString(false);
1030
        }
1031
 
1032
        return $this->name;
1033
    }
1034
 
1035
    /**
1036
     * Returns the size of the test.
1037
     *
1038
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1039
     *
1040
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1041
     */
1042
    public function getSize(): int
1043
    {
1044
        return TestUtil::getSize(
1045
            static::class,
1046
            $this->getName(false)
1047
        );
1048
    }
1049
 
1050
    /**
1051
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1052
     *
1053
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1054
     */
1055
    public function hasSize(): bool
1056
    {
1057
        return $this->getSize() !== TestUtil::UNKNOWN;
1058
    }
1059
 
1060
    /**
1061
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1062
     *
1063
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1064
     */
1065
    public function isSmall(): bool
1066
    {
1067
        return $this->getSize() === TestUtil::SMALL;
1068
    }
1069
 
1070
    /**
1071
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1072
     *
1073
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1074
     */
1075
    public function isMedium(): bool
1076
    {
1077
        return $this->getSize() === TestUtil::MEDIUM;
1078
    }
1079
 
1080
    /**
1081
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
1082
     *
1083
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1084
     */
1085
    public function isLarge(): bool
1086
    {
1087
        return $this->getSize() === TestUtil::LARGE;
1088
    }
1089
 
1090
    /**
1091
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1092
     */
1093
    public function getActualOutput(): string
1094
    {
1095
        if (!$this->outputBufferingActive) {
1096
            return $this->output;
1097
        }
1098
 
1099
        return (string) ob_get_contents();
1100
    }
1101
 
1102
    /**
1103
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1104
     */
1105
    public function hasOutput(): bool
1106
    {
1107
        if ($this->output === '') {
1108
            return false;
1109
        }
1110
 
1111
        if ($this->hasExpectationOnOutput()) {
1112
            return false;
1113
        }
1114
 
1115
        return true;
1116
    }
1117
 
1118
    /**
1119
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1120
     */
1121
    public function doesNotPerformAssertions(): bool
1122
    {
1123
        return $this->doesNotPerformAssertions;
1124
    }
1125
 
1126
    /**
1127
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1128
     */
1129
    public function hasExpectationOnOutput(): bool
1130
    {
1131
        return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion;
1132
    }
1133
 
1134
    /**
1135
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1136
     */
1137
    public function getExpectedException(): ?string
1138
    {
1139
        return $this->expectedException;
1140
    }
1141
 
1142
    /**
1143
     * @return null|int|string
1144
     *
1145
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1146
     */
1147
    public function getExpectedExceptionCode()
1148
    {
1149
        return $this->expectedExceptionCode;
1150
    }
1151
 
1152
    /**
1153
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1154
     */
1155
    public function getExpectedExceptionMessage(): ?string
1156
    {
1157
        return $this->expectedExceptionMessage;
1158
    }
1159
 
1160
    /**
1161
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1162
     */
1163
    public function getExpectedExceptionMessageRegExp(): ?string
1164
    {
1165
        return $this->expectedExceptionMessageRegExp;
1166
    }
1167
 
1168
    /**
1169
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1170
     */
1171
    public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
1172
    {
1173
        $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
1174
    }
1175
 
1176
    /**
1177
     * @throws Throwable
1178
     *
1179
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1180
     */
1181
    public function runBare(): void
1182
    {
1183
        $this->numAssertions = 0;
1184
 
1185
        $this->snapshotGlobalState();
1186
        $this->startOutputBuffering();
1187
        clearstatcache();
1188
        $currentWorkingDirectory = getcwd();
1189
 
1190
        $hookMethods = TestUtil::getHookMethods(static::class);
1191
 
1192
        $hasMetRequirements = false;
1193
 
1194
        try {
1195
            $this->checkRequirements();
1196
            $hasMetRequirements = true;
1197
 
1198
            if ($this->inIsolation) {
1199
                foreach ($hookMethods['beforeClass'] as $method) {
1200
                    $this->{$method}();
1201
                }
1202
            }
1203
 
1204
            $this->setDoesNotPerformAssertionsFromAnnotation();
1205
 
1206
            foreach ($hookMethods['before'] as $method) {
1207
                $this->{$method}();
1208
            }
1209
 
1210
            foreach ($hookMethods['preCondition'] as $method) {
1211
                $this->{$method}();
1212
            }
1213
 
1214
            $this->testResult = $this->runTest();
1215
            $this->verifyMockObjects();
1216
 
1217
            foreach ($hookMethods['postCondition'] as $method) {
1218
                $this->{$method}();
1219
            }
1220
 
1221
            if (!empty($this->warnings)) {
1222
                throw new Warning(
1223
                    implode(
1224
                        "\n",
1225
                        array_unique($this->warnings)
1226
                    )
1227
                );
1228
            }
1229
 
1230
            $this->status = BaseTestRunner::STATUS_PASSED;
1231
        } catch (IncompleteTest $e) {
1232
            $this->status        = BaseTestRunner::STATUS_INCOMPLETE;
1233
            $this->statusMessage = $e->getMessage();
1234
        } catch (SkippedTest $e) {
1235
            $this->status        = BaseTestRunner::STATUS_SKIPPED;
1236
            $this->statusMessage = $e->getMessage();
1237
        } catch (Warning $e) {
1238
            $this->status        = BaseTestRunner::STATUS_WARNING;
1239
            $this->statusMessage = $e->getMessage();
1240
        } catch (AssertionFailedError $e) {
1241
            $this->status        = BaseTestRunner::STATUS_FAILURE;
1242
            $this->statusMessage = $e->getMessage();
1243
        } catch (PredictionException $e) {
1244
            $this->status        = BaseTestRunner::STATUS_FAILURE;
1245
            $this->statusMessage = $e->getMessage();
1246
        } catch (Throwable $_e) {
1247
            $e                   = $_e;
1248
            $this->status        = BaseTestRunner::STATUS_ERROR;
1249
            $this->statusMessage = $_e->getMessage();
1250
        }
1251
 
1252
        $this->mockObjects = [];
1253
        $this->prophet     = null;
1254
 
1255
        // Tear down the fixture. An exception raised in tearDown() will be
1256
        // caught and passed on when no exception was raised before.
1257
        try {
1258
            if ($hasMetRequirements) {
1259
                foreach ($hookMethods['after'] as $method) {
1260
                    $this->{$method}();
1261
                }
1262
 
1263
                if ($this->inIsolation) {
1264
                    foreach ($hookMethods['afterClass'] as $method) {
1265
                        $this->{$method}();
1266
                    }
1267
                }
1268
            }
1269
        } catch (Throwable $_e) {
1270
            $e = $e ?? $_e;
1271
        }
1272
 
1273
        try {
1274
            $this->stopOutputBuffering();
1275
        } catch (RiskyTestError $_e) {
1276
            $e = $e ?? $_e;
1277
        }
1278
 
1279
        if (isset($_e)) {
1280
            $this->status        = BaseTestRunner::STATUS_ERROR;
1281
            $this->statusMessage = $_e->getMessage();
1282
        }
1283
 
1284
        clearstatcache();
1285
 
1286
        if ($currentWorkingDirectory !== getcwd()) {
1287
            chdir($currentWorkingDirectory);
1288
        }
1289
 
1290
        $this->restoreGlobalState();
1291
        $this->unregisterCustomComparators();
1292
        $this->cleanupIniSettings();
1293
        $this->cleanupLocaleSettings();
1294
        libxml_clear_errors();
1295
 
1296
        // Perform assertion on output.
1297
        if (!isset($e)) {
1298
            try {
1299
                if ($this->outputExpectedRegex !== null) {
1300
                    $this->assertMatchesRegularExpression($this->outputExpectedRegex, $this->output);
1301
                } elseif ($this->outputExpectedString !== null) {
1302
                    $this->assertEquals($this->outputExpectedString, $this->output);
1303
                }
1304
            } catch (Throwable $_e) {
1305
                $e = $_e;
1306
            }
1307
        }
1308
 
1309
        // Workaround for missing "finally".
1310
        if (isset($e)) {
1311
            if ($e instanceof PredictionException) {
1312
                $e = new AssertionFailedError($e->getMessage());
1313
            }
1314
 
1315
            $this->onNotSuccessfulTest($e);
1316
        }
1317
    }
1318
 
1319
    /**
1320
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1321
     */
1322
    public function setName(string $name): void
1323
    {
1324
        $this->name = $name;
1325
 
1326
        if (is_callable($this->sortId(), true)) {
1327
            $this->providedTests = [new ExecutionOrderDependency($this->sortId())];
1328
        }
1329
    }
1330
 
1331
    /**
1332
     * @param list<ExecutionOrderDependency> $dependencies
1333
     *
1334
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1335
     */
1336
    public function setDependencies(array $dependencies): void
1337
    {
1338
        $this->dependencies = $dependencies;
1339
    }
1340
 
1341
    /**
1342
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1343
     */
1344
    public function setDependencyInput(array $dependencyInput): void
1345
    {
1346
        $this->dependencyInput = $dependencyInput;
1347
    }
1348
 
1349
    /**
1350
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1351
     */
1352
    public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState): void
1353
    {
1354
        $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
1355
    }
1356
 
1357
    /**
1358
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1359
     */
1360
    public function setBackupGlobals(?bool $backupGlobals): void
1361
    {
1362
        if ($this->backupGlobals === null && $backupGlobals !== null) {
1363
            $this->backupGlobals = $backupGlobals;
1364
        }
1365
    }
1366
 
1367
    /**
1368
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1369
     */
1370
    public function setBackupStaticAttributes(?bool $backupStaticAttributes): void
1371
    {
1372
        if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) {
1373
            $this->backupStaticAttributes = $backupStaticAttributes;
1374
        }
1375
    }
1376
 
1377
    /**
1378
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1379
     */
1380
    public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void
1381
    {
1382
        if ($this->runTestInSeparateProcess === null) {
1383
            $this->runTestInSeparateProcess = $runTestInSeparateProcess;
1384
        }
1385
    }
1386
 
1387
    /**
1388
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1389
     */
1390
    public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void
1391
    {
1392
        if ($this->runClassInSeparateProcess === null) {
1393
            $this->runClassInSeparateProcess = $runClassInSeparateProcess;
1394
        }
1395
    }
1396
 
1397
    /**
1398
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1399
     */
1400
    public function setPreserveGlobalState(bool $preserveGlobalState): void
1401
    {
1402
        $this->preserveGlobalState = $preserveGlobalState;
1403
    }
1404
 
1405
    /**
1406
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1407
     */
1408
    public function setInIsolation(bool $inIsolation): void
1409
    {
1410
        $this->inIsolation = $inIsolation;
1411
    }
1412
 
1413
    /**
1414
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1415
     */
1416
    public function isInIsolation(): bool
1417
    {
1418
        return $this->inIsolation;
1419
    }
1420
 
1421
    /**
1422
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1423
     */
1424
    public function getResult()
1425
    {
1426
        return $this->testResult;
1427
    }
1428
 
1429
    /**
1430
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1431
     */
1432
    public function setResult($result): void
1433
    {
1434
        $this->testResult = $result;
1435
    }
1436
 
1437
    /**
1438
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1439
     */
1440
    public function setOutputCallback(callable $callback): void
1441
    {
1442
        $this->outputCallback = $callback;
1443
    }
1444
 
1445
    /**
1446
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1447
     */
1448
    public function getTestResultObject(): ?TestResult
1449
    {
1450
        return $this->result;
1451
    }
1452
 
1453
    /**
1454
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1455
     */
1456
    public function setTestResultObject(TestResult $result): void
1457
    {
1458
        $this->result = $result;
1459
    }
1460
 
1461
    /**
1462
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1463
     */
1464
    public function registerMockObject(MockObject $mockObject): void
1465
    {
1466
        $this->mockObjects[] = $mockObject;
1467
    }
1468
 
1469
    /**
1470
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1471
     */
1472
    public function addToAssertionCount(int $count): void
1473
    {
1474
        $this->numAssertions += $count;
1475
    }
1476
 
1477
    /**
1478
     * Returns the number of assertions performed by this test.
1479
     *
1480
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1481
     */
1482
    public function getNumAssertions(): int
1483
    {
1484
        return $this->numAssertions;
1485
    }
1486
 
1487
    /**
1488
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1489
     */
1490
    public function usesDataProvider(): bool
1491
    {
1492
        return !empty($this->data);
1493
    }
1494
 
1495
    /**
1496
     * @return int|string
1497
     *
1498
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1499
     */
1500
    public function dataName()
1501
    {
1502
        return $this->dataName;
1503
    }
1504
 
1505
    /**
1506
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1507
     */
1508
    public function getDataSetAsString(bool $includeData = true): string
1509
    {
1510
        $buffer = '';
1511
 
1512
        if (!empty($this->data)) {
1513
            if (is_int($this->dataName)) {
1514
                $buffer .= sprintf(' with data set #%d', $this->dataName);
1515
            } else {
1516
                $buffer .= sprintf(' with data set "%s"', $this->dataName);
1517
            }
1518
 
1519
            if ($includeData) {
1520
                $exporter = new Exporter;
1521
 
1522
                $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data));
1523
            }
1524
        }
1525
 
1526
        return $buffer;
1527
    }
1528
 
1529
    /**
1530
     * Gets the data set of a TestCase.
1531
     *
1532
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1533
     */
1534
    public function getProvidedData(): array
1535
    {
1536
        return $this->data;
1537
    }
1538
 
1539
    /**
1540
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
1541
     */
1542
    public function addWarning(string $warning): void
1543
    {
1544
        $this->warnings[] = $warning;
1545
    }
1546
 
1547
    public function sortId(): string
1548
    {
1549
        $id = $this->name;
1550
 
1551
        if (strpos($id, '::') === false) {
1552
            $id = static::class . '::' . $id;
1553
        }
1554
 
1555
        if ($this->usesDataProvider()) {
1556
            $id .= $this->getDataSetAsString(false);
1557
        }
1558
 
1559
        return $id;
1560
    }
1561
 
1562
    /**
1563
     * Returns the normalized test name as class::method.
1564
     *
1565
     * @return list<ExecutionOrderDependency>
1566
     */
1567
    public function provides(): array
1568
    {
1569
        return $this->providedTests;
1570
    }
1571
 
1572
    /**
1573
     * Returns a list of normalized dependency names, class::method.
1574
     *
1575
     * This list can differ from the raw dependencies as the resolver has
1576
     * no need for the [!][shallow]clone prefix that is filtered out
1577
     * during normalization.
1578
     *
1579
     * @return list<ExecutionOrderDependency>
1580
     */
1581
    public function requires(): array
1582
    {
1583
        return $this->dependencies;
1584
    }
1585
 
1586
    /**
1587
     * Override to run the test and assert its state.
1588
     *
1589
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
1590
     * @throws AssertionFailedError
1591
     * @throws Exception
1592
     * @throws ExpectationFailedException
1593
     * @throws Throwable
1594
     */
1595
    protected function runTest()
1596
    {
1597
        if (trim($this->name) === '') {
1598
            throw new Exception(
1599
                'PHPUnit\Framework\TestCase::$name must be a non-blank string.'
1600
            );
1601
        }
1602
 
1603
        $testArguments = array_merge($this->data, $this->dependencyInput);
1604
 
1605
        $this->registerMockObjectsFromTestArguments($testArguments);
1606
 
1607
        try {
1608
            $testResult = $this->{$this->name}(...array_values($testArguments));
1609
        } catch (Throwable $exception) {
1610
            if (!$this->checkExceptionExpectations($exception)) {
1611
                throw $exception;
1612
            }
1613
 
1614
            if ($this->expectedException !== null) {
1615
                if ($this->expectedException === Error::class) {
1616
                    $this->assertThat(
1617
                        $exception,
1618
                        LogicalOr::fromConstraints(
1619
                            new ExceptionConstraint(Error::class),
1620
                            new ExceptionConstraint(\Error::class)
1621
                        )
1622
                    );
1623
                } else {
1624
                    $this->assertThat(
1625
                        $exception,
1626
                        new ExceptionConstraint(
1627
                            $this->expectedException
1628
                        )
1629
                    );
1630
                }
1631
            }
1632
 
1633
            if ($this->expectedExceptionMessage !== null) {
1634
                $this->assertThat(
1635
                    $exception,
1636
                    new ExceptionMessage(
1637
                        $this->expectedExceptionMessage
1638
                    )
1639
                );
1640
            }
1641
 
1642
            if ($this->expectedExceptionMessageRegExp !== null) {
1643
                $this->assertThat(
1644
                    $exception,
1645
                    new ExceptionMessageRegularExpression(
1646
                        $this->expectedExceptionMessageRegExp
1647
                    )
1648
                );
1649
            }
1650
 
1651
            if ($this->expectedExceptionCode !== null) {
1652
                $this->assertThat(
1653
                    $exception,
1654
                    new ExceptionCode(
1655
                        $this->expectedExceptionCode
1656
                    )
1657
                );
1658
            }
1659
 
1660
            return;
1661
        }
1662
 
1663
        if ($this->expectedException !== null) {
1664
            $this->assertThat(
1665
                null,
1666
                new ExceptionConstraint(
1667
                    $this->expectedException
1668
                )
1669
            );
1670
        } elseif ($this->expectedExceptionMessage !== null) {
1671
            $this->numAssertions++;
1672
 
1673
            throw new AssertionFailedError(
1674
                sprintf(
1675
                    'Failed asserting that exception with message "%s" is thrown',
1676
                    $this->expectedExceptionMessage
1677
                )
1678
            );
1679
        } elseif ($this->expectedExceptionMessageRegExp !== null) {
1680
            $this->numAssertions++;
1681
 
1682
            throw new AssertionFailedError(
1683
                sprintf(
1684
                    'Failed asserting that exception with message matching "%s" is thrown',
1685
                    $this->expectedExceptionMessageRegExp
1686
                )
1687
            );
1688
        } elseif ($this->expectedExceptionCode !== null) {
1689
            $this->numAssertions++;
1690
 
1691
            throw new AssertionFailedError(
1692
                sprintf(
1693
                    'Failed asserting that exception with code "%s" is thrown',
1694
                    $this->expectedExceptionCode
1695
                )
1696
            );
1697
        }
1698
 
1699
        return $testResult;
1700
    }
1701
 
1702
    /**
1703
     * This method is a wrapper for the ini_set() function that automatically
1704
     * resets the modified php.ini setting to its original value after the
1705
     * test is run.
1706
     *
1707
     * @throws Exception
1708
     */
1709
    protected function iniSet(string $varName, string $newValue): void
1710
    {
1711
        $currentValue = ini_set($varName, $newValue);
1712
 
1713
        if ($currentValue !== false) {
1714
            $this->iniSettings[$varName] = $currentValue;
1715
        } else {
1716
            throw new Exception(
1717
                sprintf(
1718
                    'INI setting "%s" could not be set to "%s".',
1719
                    $varName,
1720
                    $newValue
1721
                )
1722
            );
1723
        }
1724
    }
1725
 
1726
    /**
1727
     * This method is a wrapper for the setlocale() function that automatically
1728
     * resets the locale to its original value after the test is run.
1729
     *
1730
     * @throws Exception
1731
     */
1732
    protected function setLocale(...$args): void
1733
    {
1734
        if (count($args) < 2) {
1735
            throw new Exception;
1736
        }
1737
 
1738
        [$category, $locale] = $args;
1739
 
1740
        if (!in_array($category, self::LOCALE_CATEGORIES, true)) {
1741
            throw new Exception;
1742
        }
1743
 
1744
        if (!is_array($locale) && !is_string($locale)) {
1745
            throw new Exception;
1746
        }
1747
 
1748
        $this->locale[$category] = setlocale($category, 0);
1749
 
1750
        $result = setlocale(...$args);
1751
 
1752
        if ($result === false) {
1753
            throw new Exception(
1754
                'The locale functionality is not implemented on your platform, ' .
1755
                'the specified locale does not exist or the category name is ' .
1756
                'invalid.'
1757
            );
1758
        }
1759
    }
1760
 
1761
    /**
1762
     * Makes configurable stub for the specified class.
1763
     *
1764
     * @psalm-template RealInstanceType of object
1765
     *
1766
     * @psalm-param    class-string<RealInstanceType> $originalClassName
1767
     *
1768
     * @psalm-return   Stub&RealInstanceType
1769
     */
1770
    protected function createStub(string $originalClassName): Stub
1771
    {
1772
        return $this->createMockObject($originalClassName);
1773
    }
1774
 
1775
    /**
1776
     * Returns a mock object for the specified class.
1777
     *
1778
     * @psalm-template RealInstanceType of object
1779
     *
1780
     * @psalm-param class-string<RealInstanceType> $originalClassName
1781
     *
1782
     * @psalm-return MockObject&RealInstanceType
1783
     */
1784
    protected function createMock(string $originalClassName): MockObject
1785
    {
1786
        return $this->createMockObject($originalClassName);
1787
    }
1788
 
1789
    /**
1790
     * Returns a configured mock object for the specified class.
1791
     *
1792
     * @psalm-template RealInstanceType of object
1793
     *
1794
     * @psalm-param class-string<RealInstanceType> $originalClassName
1795
     *
1796
     * @psalm-return MockObject&RealInstanceType
1797
     */
1798
    protected function createConfiguredMock(string $originalClassName, array $configuration): MockObject
1799
    {
1800
        $o = $this->createMockObject($originalClassName);
1801
 
1802
        foreach ($configuration as $method => $return) {
1803
            $o->method($method)->willReturn($return);
1804
        }
1805
 
1806
        return $o;
1807
    }
1808
 
1809
    /**
1810
     * Returns a partial mock object for the specified class.
1811
     *
1812
     * @param string[] $methods
1813
     *
1814
     * @psalm-template RealInstanceType of object
1815
     *
1816
     * @psalm-param class-string<RealInstanceType> $originalClassName
1817
     *
1818
     * @psalm-return MockObject&RealInstanceType
1819
     */
1820
    protected function createPartialMock(string $originalClassName, array $methods): MockObject
1821
    {
1822
        try {
1823
            $reflector = new ReflectionClass($originalClassName);
1824
            // @codeCoverageIgnoreStart
1825
        } catch (ReflectionException $e) {
1826
            throw new Exception(
1827
                $e->getMessage(),
1828
                $e->getCode(),
1829
                $e
1830
            );
1831
        }
1832
        // @codeCoverageIgnoreEnd
1833
 
1834
        $mockedMethodsThatDontExist = array_filter(
1835
            $methods,
1836
            static function (string $method) use ($reflector)
1837
            {
1838
                return !$reflector->hasMethod($method);
1839
            }
1840
        );
1841
 
1842
        if ($mockedMethodsThatDontExist) {
1843
            $this->addWarning(
1844
                sprintf(
1845
                    'createPartialMock() called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.',
1846
                    implode(', ', $mockedMethodsThatDontExist),
1847
                    $originalClassName
1848
                )
1849
            );
1850
        }
1851
 
1852
        return $this->getMockBuilder($originalClassName)
1853
                    ->disableOriginalConstructor()
1854
                    ->disableOriginalClone()
1855
                    ->disableArgumentCloning()
1856
                    ->disallowMockingUnknownTypes()
1857
                    ->setMethods(empty($methods) ? null : $methods)
1858
                    ->getMock();
1859
    }
1860
 
1861
    /**
1862
     * Returns a test proxy for the specified class.
1863
     *
1864
     * @psalm-template RealInstanceType of object
1865
     *
1866
     * @psalm-param class-string<RealInstanceType> $originalClassName
1867
     *
1868
     * @psalm-return MockObject&RealInstanceType
1869
     */
1870
    protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject
1871
    {
1872
        return $this->getMockBuilder($originalClassName)
1873
                    ->setConstructorArgs($constructorArguments)
1874
                    ->enableProxyingToOriginalMethods()
1875
                    ->getMock();
1876
    }
1877
 
1878
    /**
1879
     * Mocks the specified class and returns the name of the mocked class.
1880
     *
1881
     * @param null|array $methods $methods
1882
     *
1883
     * @psalm-template RealInstanceType of object
1884
     *
1885
     * @psalm-param class-string<RealInstanceType>|string $originalClassName
1886
     *
1887
     * @psalm-return class-string<MockObject&RealInstanceType>
621 lars 1888
     *
1889
     * @deprecated
148 lars 1890
     */
1891
    protected function getMockClass(string $originalClassName, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = false, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = false): string
1892
    {
621 lars 1893
        $this->addWarning('PHPUnit\Framework\TestCase::getMockClass() is deprecated and will be removed in PHPUnit 10.');
1894
 
148 lars 1895
        $this->recordDoubledType($originalClassName);
1896
 
1897
        $mock = $this->getMockObjectGenerator()->getMock(
1898
            $originalClassName,
1899
            $methods,
1900
            $arguments,
1901
            $mockClassName,
1902
            $callOriginalConstructor,
1903
            $callOriginalClone,
1904
            $callAutoload,
1905
            $cloneArguments
1906
        );
1907
 
1908
        return get_class($mock);
1909
    }
1910
 
1911
    /**
1912
     * Returns a mock object for the specified abstract class with all abstract
1913
     * methods of the class mocked. Concrete methods are not mocked by default.
1914
     * To mock concrete methods, use the 7th parameter ($mockedMethods).
1915
     *
1916
     * @psalm-template RealInstanceType of object
1917
     *
1918
     * @psalm-param class-string<RealInstanceType> $originalClassName
1919
     *
1920
     * @psalm-return MockObject&RealInstanceType
1921
     */
1922
    protected function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject
1923
    {
1924
        $this->recordDoubledType($originalClassName);
1925
 
1926
        $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass(
1927
            $originalClassName,
1928
            $arguments,
1929
            $mockClassName,
1930
            $callOriginalConstructor,
1931
            $callOriginalClone,
1932
            $callAutoload,
1933
            $mockedMethods,
1934
            $cloneArguments
1935
        );
1936
 
1937
        $this->registerMockObject($mockObject);
1938
 
1939
        return $mockObject;
1940
    }
1941
 
1942
    /**
1943
     * Returns a mock object based on the given WSDL file.
1944
     *
1945
     * @psalm-template RealInstanceType of object
1946
     *
1947
     * @psalm-param class-string<RealInstanceType>|string $originalClassName
1948
     *
1949
     * @psalm-return MockObject&RealInstanceType
1950
     */
1951
    protected function getMockFromWsdl(string $wsdlFile, string $originalClassName = '', string $mockClassName = '', array $methods = [], bool $callOriginalConstructor = true, array $options = []): MockObject
1952
    {
1953
        $this->recordDoubledType(SoapClient::class);
1954
 
1955
        if ($originalClassName === '') {
1956
            $fileName          = pathinfo(basename(parse_url($wsdlFile, PHP_URL_PATH)), PATHINFO_FILENAME);
1957
            $originalClassName = preg_replace('/\W/', '', $fileName);
1958
        }
1959
 
1960
        if (!class_exists($originalClassName)) {
1961
            eval(
1962
                $this->getMockObjectGenerator()->generateClassFromWsdl(
1963
                    $wsdlFile,
1964
                    $originalClassName,
1965
                    $methods,
1966
                    $options
1967
                )
1968
            );
1969
        }
1970
 
1971
        $mockObject = $this->getMockObjectGenerator()->getMock(
1972
            $originalClassName,
1973
            $methods,
1974
            ['', $options],
1975
            $mockClassName,
1976
            $callOriginalConstructor,
1977
            false,
1978
            false
1979
        );
1980
 
1981
        $this->registerMockObject($mockObject);
1982
 
1983
        return $mockObject;
1984
    }
1985
 
1986
    /**
1987
     * Returns a mock object for the specified trait with all abstract methods
1988
     * of the trait mocked. Concrete methods to mock can be specified with the
1989
     * `$mockedMethods` parameter.
1990
     *
1991
     * @psalm-param trait-string $traitName
1992
     */
1993
    protected function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = false): MockObject
1994
    {
1995
        $this->recordDoubledType($traitName);
1996
 
1997
        $mockObject = $this->getMockObjectGenerator()->getMockForTrait(
1998
            $traitName,
1999
            $arguments,
2000
            $mockClassName,
2001
            $callOriginalConstructor,
2002
            $callOriginalClone,
2003
            $callAutoload,
2004
            $mockedMethods,
2005
            $cloneArguments
2006
        );
2007
 
2008
        $this->registerMockObject($mockObject);
2009
 
2010
        return $mockObject;
2011
    }
2012
 
2013
    /**
2014
     * Returns an object for the specified trait.
2015
     *
2016
     * @psalm-param trait-string $traitName
2017
     */
2018
    protected function getObjectForTrait(string $traitName, array $arguments = [], string $traitClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true): object
2019
    {
2020
        $this->recordDoubledType($traitName);
2021
 
2022
        return $this->getMockObjectGenerator()->getObjectForTrait(
2023
            $traitName,
2024
            $traitClassName,
2025
            $callAutoload,
2026
            $callOriginalConstructor,
2027
            $arguments
2028
        );
2029
    }
2030
 
2031
    /**
2032
     * @throws \Prophecy\Exception\Doubler\ClassNotFoundException
2033
     * @throws \Prophecy\Exception\Doubler\DoubleException
2034
     * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException
2035
     *
2036
     * @psalm-param class-string|null $classOrInterface
2037
     *
2038
     * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4141
2039
     */
2040
    protected function prophesize(?string $classOrInterface = null): ObjectProphecy
2041
    {
2042
        if (!class_exists(Prophet::class)) {
2043
            throw new Exception('This test uses TestCase::prophesize(), but phpspec/prophecy is not installed. Please run "composer require --dev phpspec/prophecy".');
2044
        }
2045
 
2046
        $this->addWarning('PHPUnit\Framework\TestCase::prophesize() is deprecated and will be removed in PHPUnit 10. Please use the trait provided by phpspec/prophecy-phpunit.');
2047
 
2048
        if (is_string($classOrInterface)) {
2049
            $this->recordDoubledType($classOrInterface);
2050
        }
2051
 
2052
        return $this->getProphet()->prophesize($classOrInterface);
2053
    }
2054
 
2055
    /**
2056
     * Creates a default TestResult object.
2057
     *
2058
     * @internal This method is not covered by the backward compatibility promise for PHPUnit
2059
     */
2060
    protected function createResult(): TestResult
2061
    {
2062
        return new TestResult;
2063
    }
2064
 
2065
    /**
2066
     * This method is called when a test method did not execute successfully.
2067
     *
2068
     * @throws Throwable
2069
     */
2070
    protected function onNotSuccessfulTest(Throwable $t): void
2071
    {
2072
        throw $t;
2073
    }
2074
 
2075
    protected function recordDoubledType(string $originalClassName): void
2076
    {
2077
        $this->doubledTypes[] = $originalClassName;
2078
    }
2079
 
2080
    /**
2081
     * @throws Throwable
2082
     */
2083
    private function verifyMockObjects(): void
2084
    {
2085
        foreach ($this->mockObjects as $mockObject) {
2086
            if ($mockObject->__phpunit_hasMatchers()) {
2087
                $this->numAssertions++;
2088
            }
2089
 
2090
            $mockObject->__phpunit_verify(
2091
                $this->shouldInvocationMockerBeReset($mockObject)
2092
            );
2093
        }
2094
 
2095
        if ($this->prophet !== null) {
2096
            try {
2097
                $this->prophet->checkPredictions();
2098
            } finally {
2099
                foreach ($this->prophet->getProphecies() as $objectProphecy) {
2100
                    foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) {
2101
                        foreach ($methodProphecies as $methodProphecy) {
2102
                            /* @var MethodProphecy $methodProphecy */
2103
                            $this->numAssertions += count($methodProphecy->getCheckedPredictions());
2104
                        }
2105
                    }
2106
                }
2107
            }
2108
        }
2109
    }
2110
 
2111
    /**
2112
     * @throws SkippedTestError
2113
     * @throws SyntheticSkippedError
2114
     * @throws Warning
2115
     */
2116
    private function checkRequirements(): void
2117
    {
2118
        if (!$this->name || !method_exists($this, $this->name)) {
2119
            return;
2120
        }
2121
 
2122
        $missingRequirements = TestUtil::getMissingRequirements(
2123
            static::class,
2124
            $this->name
2125
        );
2126
 
2127
        if (!empty($missingRequirements)) {
2128
            $this->markTestSkipped(implode(PHP_EOL, $missingRequirements));
2129
        }
2130
    }
2131
 
2132
    private function handleDependencies(): bool
2133
    {
2134
        if ([] === $this->dependencies || $this->inIsolation) {
2135
            return true;
2136
        }
2137
 
2138
        $passed     = $this->result->passed();
2139
        $passedKeys = array_keys($passed);
2140
        $numKeys    = count($passedKeys);
2141
 
2142
        for ($i = 0; $i < $numKeys; $i++) {
2143
            $pos = strpos($passedKeys[$i], ' with data set');
2144
 
2145
            if ($pos !== false) {
2146
                $passedKeys[$i] = substr($passedKeys[$i], 0, $pos);
2147
            }
2148
        }
2149
 
2150
        $passedKeys = array_flip(array_unique($passedKeys));
2151
 
2152
        foreach ($this->dependencies as $dependency) {
2153
            if (!$dependency->isValid()) {
2154
                $this->markSkippedForNotSpecifyingDependency();
2155
 
2156
                return false;
2157
            }
2158
 
2159
            if ($dependency->targetIsClass()) {
2160
                $dependencyClassName = $dependency->getTargetClassName();
2161
 
2162
                if (array_search($dependencyClassName, $this->result->passedClasses(), true) === false) {
2163
                    $this->markSkippedForMissingDependency($dependency);
2164
 
2165
                    return false;
2166
                }
2167
 
2168
                continue;
2169
            }
2170
 
2171
            $dependencyTarget = $dependency->getTarget();
2172
 
2173
            if (!isset($passedKeys[$dependencyTarget])) {
2174
                if (!$this->isCallableTestMethod($dependencyTarget)) {
2175
                    $this->markWarningForUncallableDependency($dependency);
2176
                } else {
2177
                    $this->markSkippedForMissingDependency($dependency);
2178
                }
2179
 
2180
                return false;
2181
            }
2182
 
2183
            if (isset($passed[$dependencyTarget])) {
2184
                if ($passed[$dependencyTarget]['size'] != \PHPUnit\Util\Test::UNKNOWN &&
2185
                    $this->getSize() != \PHPUnit\Util\Test::UNKNOWN &&
2186
                    $passed[$dependencyTarget]['size'] > $this->getSize()) {
2187
                    $this->result->addError(
2188
                        $this,
2189
                        new SkippedTestError(
2190
                            'This test depends on a test that is larger than itself.'
2191
                        ),
2192
 
2193
                    );
2194
 
2195
                    return false;
2196
                }
2197
 
2198
                if ($dependency->useDeepClone()) {
2199
                    $deepCopy = new DeepCopy;
2200
                    $deepCopy->skipUncloneable(false);
2201
 
2202
                    $this->dependencyInput[$dependencyTarget] = $deepCopy->copy($passed[$dependencyTarget]['result']);
2203
                } elseif ($dependency->useShallowClone()) {
2204
                    $this->dependencyInput[$dependencyTarget] = clone $passed[$dependencyTarget]['result'];
2205
                } else {
2206
                    $this->dependencyInput[$dependencyTarget] = $passed[$dependencyTarget]['result'];
2207
                }
2208
            } else {
2209
                $this->dependencyInput[$dependencyTarget] = null;
2210
            }
2211
        }
2212
 
2213
        return true;
2214
    }
2215
 
2216
    private function markSkippedForNotSpecifyingDependency(): void
2217
    {
2218
        $this->status = BaseTestRunner::STATUS_SKIPPED;
2219
 
2220
        $this->result->startTest($this);
2221
 
2222
        $this->result->addError(
2223
            $this,
2224
            new SkippedTestError(
2225
                'This method has an invalid @depends annotation.'
2226
            ),
2227
 
2228
        );
2229
 
2230
        $this->result->endTest($this, 0);
2231
    }
2232
 
2233
    private function markSkippedForMissingDependency(ExecutionOrderDependency $dependency): void
2234
    {
2235
        $this->status = BaseTestRunner::STATUS_SKIPPED;
2236
 
2237
        $this->result->startTest($this);
2238
 
2239
        $this->result->addError(
2240
            $this,
2241
            new SkippedTestError(
2242
                sprintf(
2243
                    'This test depends on "%s" to pass.',
2244
                    $dependency->getTarget()
2245
                )
2246
            ),
2247
 
2248
        );
2249
 
2250
        $this->result->endTest($this, 0);
2251
    }
2252
 
2253
    private function markWarningForUncallableDependency(ExecutionOrderDependency $dependency): void
2254
    {
2255
        $this->status = BaseTestRunner::STATUS_WARNING;
2256
 
2257
        $this->result->startTest($this);
2258
 
2259
        $this->result->addWarning(
2260
            $this,
2261
            new Warning(
2262
                sprintf(
2263
                    'This test depends on "%s" which does not exist.',
2264
                    $dependency->getTarget()
2265
                )
2266
            ),
2267
 
2268
        );
2269
 
2270
        $this->result->endTest($this, 0);
2271
    }
2272
 
2273
    /**
2274
     * Get the mock object generator, creating it if it doesn't exist.
2275
     */
2276
    private function getMockObjectGenerator(): MockGenerator
2277
    {
2278
        if ($this->mockObjectGenerator === null) {
2279
            $this->mockObjectGenerator = new MockGenerator;
2280
        }
2281
 
2282
        return $this->mockObjectGenerator;
2283
    }
2284
 
2285
    private function startOutputBuffering(): void
2286
    {
2287
        ob_start();
2288
 
2289
        $this->outputBufferingActive = true;
2290
        $this->outputBufferingLevel  = ob_get_level();
2291
    }
2292
 
2293
    /**
2294
     * @throws RiskyTestError
2295
     */
2296
    private function stopOutputBuffering(): void
2297
    {
2298
        if (ob_get_level() !== $this->outputBufferingLevel) {
2299
            while (ob_get_level() >= $this->outputBufferingLevel) {
2300
                ob_end_clean();
2301
            }
2302
 
2303
            throw new RiskyTestError(
2304
                'Test code or tested code did not (only) close its own output buffers'
2305
            );
2306
        }
2307
 
2308
        $this->output = ob_get_contents();
2309
 
2310
        if ($this->outputCallback !== false) {
2311
            $this->output = (string) call_user_func($this->outputCallback, $this->output);
2312
        }
2313
 
2314
        ob_end_clean();
2315
 
2316
        $this->outputBufferingActive = false;
2317
        $this->outputBufferingLevel  = ob_get_level();
2318
    }
2319
 
2320
    private function snapshotGlobalState(): void
2321
    {
2322
        if ($this->runTestInSeparateProcess || $this->inIsolation ||
2323
            (!$this->backupGlobals && !$this->backupStaticAttributes)) {
2324
            return;
2325
        }
2326
 
2327
        $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === true);
2328
    }
2329
 
2330
    /**
2331
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2332
     * @throws RiskyTestError
2333
     */
2334
    private function restoreGlobalState(): void
2335
    {
2336
        if (!$this->snapshot instanceof Snapshot) {
2337
            return;
2338
        }
2339
 
2340
        if ($this->beStrictAboutChangesToGlobalState) {
2341
            try {
2342
                $this->compareGlobalStateSnapshots(
2343
                    $this->snapshot,
2344
                    $this->createGlobalStateSnapshot($this->backupGlobals === true)
2345
                );
2346
            } catch (RiskyTestError $rte) {
2347
                // Intentionally left empty
2348
            }
2349
        }
2350
 
2351
        $restorer = new Restorer;
2352
 
2353
        if ($this->backupGlobals) {
2354
            $restorer->restoreGlobalVariables($this->snapshot);
2355
        }
2356
 
2357
        if ($this->backupStaticAttributes) {
2358
            $restorer->restoreStaticAttributes($this->snapshot);
2359
        }
2360
 
2361
        $this->snapshot = null;
2362
 
2363
        if (isset($rte)) {
2364
            throw $rte;
2365
        }
2366
    }
2367
 
2368
    private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot
2369
    {
2370
        $excludeList = new ExcludeList;
2371
 
2372
        foreach ($this->backupGlobalsExcludeList as $globalVariable) {
2373
            $excludeList->addGlobalVariable($globalVariable);
2374
        }
2375
 
2376
        if (!empty($this->backupGlobalsBlacklist)) {
2377
            $this->addWarning('PHPUnit\Framework\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\Framework\TestCase::$backupGlobalsExcludeList instead.');
2378
 
2379
            foreach ($this->backupGlobalsBlacklist as $globalVariable) {
2380
                $excludeList->addGlobalVariable($globalVariable);
2381
            }
2382
        }
2383
 
2384
        if (!defined('PHPUNIT_TESTSUITE')) {
2385
            $excludeList->addClassNamePrefix('PHPUnit');
2386
            $excludeList->addClassNamePrefix('SebastianBergmann\CodeCoverage');
2387
            $excludeList->addClassNamePrefix('SebastianBergmann\FileIterator');
2388
            $excludeList->addClassNamePrefix('SebastianBergmann\Invoker');
2389
            $excludeList->addClassNamePrefix('SebastianBergmann\Template');
2390
            $excludeList->addClassNamePrefix('SebastianBergmann\Timer');
2391
            $excludeList->addClassNamePrefix('Doctrine\Instantiator');
2392
            $excludeList->addClassNamePrefix('Prophecy');
2393
            $excludeList->addStaticAttribute(ComparatorFactory::class, 'instance');
2394
 
2395
            foreach ($this->backupStaticAttributesExcludeList as $class => $attributes) {
2396
                foreach ($attributes as $attribute) {
2397
                    $excludeList->addStaticAttribute($class, $attribute);
2398
                }
2399
            }
2400
 
2401
            if (!empty($this->backupStaticAttributesBlacklist)) {
2402
                $this->addWarning('PHPUnit\Framework\TestCase::$backupStaticAttributesBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\Framework\TestCase::$backupStaticAttributesExcludeList instead.');
2403
 
2404
                foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) {
2405
                    foreach ($attributes as $attribute) {
2406
                        $excludeList->addStaticAttribute($class, $attribute);
2407
                    }
2408
                }
2409
            }
2410
        }
2411
 
2412
        return new Snapshot(
2413
            $excludeList,
2414
            $backupGlobals,
2415
            (bool) $this->backupStaticAttributes,
2416
            false,
2417
            false,
2418
            false,
2419
            false,
2420
            false,
2421
            false,
2422
            false
2423
        );
2424
    }
2425
 
2426
    /**
2427
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2428
     * @throws RiskyTestError
2429
     */
2430
    private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void
2431
    {
2432
        $backupGlobals = $this->backupGlobals === null || $this->backupGlobals;
2433
 
2434
        if ($backupGlobals) {
2435
            $this->compareGlobalStateSnapshotPart(
2436
                $before->globalVariables(),
2437
                $after->globalVariables(),
2438
                "--- Global variables before the test\n+++ Global variables after the test\n"
2439
            );
2440
 
2441
            $this->compareGlobalStateSnapshotPart(
2442
                $before->superGlobalVariables(),
2443
                $after->superGlobalVariables(),
2444
                "--- Super-global variables before the test\n+++ Super-global variables after the test\n"
2445
            );
2446
        }
2447
 
2448
        if ($this->backupStaticAttributes) {
2449
            $this->compareGlobalStateSnapshotPart(
2450
                $before->staticAttributes(),
2451
                $after->staticAttributes(),
2452
                "--- Static attributes before the test\n+++ Static attributes after the test\n"
2453
            );
2454
        }
2455
    }
2456
 
2457
    /**
2458
     * @throws RiskyTestError
2459
     */
2460
    private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void
2461
    {
2462
        if ($before != $after) {
2463
            $differ   = new Differ($header);
2464
            $exporter = new Exporter;
2465
 
2466
            $diff = $differ->diff(
2467
                $exporter->export($before),
2468
                $exporter->export($after)
2469
            );
2470
 
2471
            throw new RiskyTestError(
2472
                $diff
2473
            );
2474
        }
2475
    }
2476
 
2477
    private function getProphet(): Prophet
2478
    {
2479
        if ($this->prophet === null) {
2480
            $this->prophet = new Prophet;
2481
        }
2482
 
2483
        return $this->prophet;
2484
    }
2485
 
2486
    /**
2487
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
2488
     */
2489
    private function shouldInvocationMockerBeReset(MockObject $mock): bool
2490
    {
2491
        $enumerator = new Enumerator;
2492
 
2493
        foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
2494
            if ($mock === $object) {
2495
                return false;
2496
            }
2497
        }
2498
 
2499
        if (!is_array($this->testResult) && !is_object($this->testResult)) {
2500
            return true;
2501
        }
2502
 
2503
        return !in_array($mock, $enumerator->enumerate($this->testResult), true);
2504
    }
2505
 
2506
    /**
2507
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
2508
     * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException
2509
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2510
     */
2511
    private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void
2512
    {
2513
        if ($this->registerMockObjectsFromTestArgumentsRecursively) {
2514
            foreach ((new Enumerator)->enumerate($testArguments) as $object) {
2515
                if ($object instanceof MockObject) {
2516
                    $this->registerMockObject($object);
2517
                }
2518
            }
2519
        } else {
2520
            foreach ($testArguments as $testArgument) {
2521
                if ($testArgument instanceof MockObject) {
2522
                    $testArgument = Cloner::clone($testArgument);
2523
 
2524
                    $this->registerMockObject($testArgument);
2525
                } elseif (is_array($testArgument) && !in_array($testArgument, $visited, true)) {
2526
                    $visited[] = $testArgument;
2527
 
2528
                    $this->registerMockObjectsFromTestArguments(
2529
                        $testArgument,
2530
                        $visited
2531
                    );
2532
                }
2533
            }
2534
        }
2535
    }
2536
 
2537
    private function setDoesNotPerformAssertionsFromAnnotation(): void
2538
    {
2539
        $annotations = TestUtil::parseTestMethodAnnotations(
2540
            static::class,
2541
            $this->name
2542
        );
2543
 
2544
        if (isset($annotations['method']['doesNotPerformAssertions'])) {
2545
            $this->doesNotPerformAssertions = true;
2546
        }
2547
    }
2548
 
2549
    private function unregisterCustomComparators(): void
2550
    {
2551
        $factory = ComparatorFactory::getInstance();
2552
 
2553
        foreach ($this->customComparators as $comparator) {
2554
            $factory->unregister($comparator);
2555
        }
2556
 
2557
        $this->customComparators = [];
2558
    }
2559
 
2560
    private function cleanupIniSettings(): void
2561
    {
2562
        foreach ($this->iniSettings as $varName => $oldValue) {
2563
            ini_set($varName, $oldValue);
2564
        }
2565
 
2566
        $this->iniSettings = [];
2567
    }
2568
 
2569
    private function cleanupLocaleSettings(): void
2570
    {
2571
        foreach ($this->locale as $category => $locale) {
2572
            setlocale($category, $locale);
2573
        }
2574
 
2575
        $this->locale = [];
2576
    }
2577
 
2578
    /**
2579
     * @throws Exception
2580
     */
2581
    private function checkExceptionExpectations(Throwable $throwable): bool
2582
    {
2583
        $result = false;
2584
 
2585
        if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) {
2586
            $result = true;
2587
        }
2588
 
2589
        if ($throwable instanceof Exception) {
2590
            $result = false;
2591
        }
2592
 
2593
        if (is_string($this->expectedException)) {
2594
            try {
2595
                $reflector = new ReflectionClass($this->expectedException);
2596
                // @codeCoverageIgnoreStart
2597
            } catch (ReflectionException $e) {
2598
                throw new Exception(
2599
                    $e->getMessage(),
2600
                    $e->getCode(),
2601
                    $e
2602
                );
2603
            }
2604
            // @codeCoverageIgnoreEnd
2605
 
2606
            if ($this->expectedException === 'PHPUnit\Framework\Exception' ||
2607
                $this->expectedException === '\PHPUnit\Framework\Exception' ||
2608
                $reflector->isSubclassOf(Exception::class)) {
2609
                $result = true;
2610
            }
2611
        }
2612
 
2613
        return $result;
2614
    }
2615
 
2616
    private function runInSeparateProcess(): bool
2617
    {
2618
        return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) &&
2619
            !$this->inIsolation && !$this instanceof PhptTestCase;
2620
    }
2621
 
2622
    private function isCallableTestMethod(string $dependency): bool
2623
    {
2624
        [$className, $methodName] = explode('::', $dependency);
2625
 
2626
        if (!class_exists($className)) {
2627
            return false;
2628
        }
2629
 
2630
        try {
2631
            $class = new ReflectionClass($className);
2632
        } catch (ReflectionException $e) {
2633
            return false;
2634
        }
2635
 
2636
        if (!$class->isSubclassOf(__CLASS__)) {
2637
            return false;
2638
        }
2639
 
2640
        if (!$class->hasMethod($methodName)) {
2641
            return false;
2642
        }
2643
 
2644
        try {
2645
            $method = $class->getMethod($methodName);
2646
        } catch (ReflectionException $e) {
2647
            return false;
2648
        }
2649
 
2650
        return TestUtil::isTestMethod($method);
2651
    }
2652
 
2653
    /**
2654
     * @psalm-template RealInstanceType of object
2655
     *
2656
     * @psalm-param class-string<RealInstanceType> $originalClassName
2657
     *
2658
     * @psalm-return MockObject&RealInstanceType
2659
     */
2660
    private function createMockObject(string $originalClassName): MockObject
2661
    {
2662
        return $this->getMockBuilder($originalClassName)
2663
                    ->disableOriginalConstructor()
2664
                    ->disableOriginalClone()
2665
                    ->disableArgumentCloning()
2666
                    ->disallowMockingUnknownTypes()
2667
                    ->getMock();
2668
    }
2669
}