TestCase::getMockForAbstractClass()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 8
dl 0
loc 16
rs 9.9
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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

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

1973
            $differ   = new Differ(/** @scrutinizer ignore-type */ $header);
Loading history...
1974
            $exporter = new Exporter;
1975
1976
            $diff = $differ->diff(
1977
                $exporter->export($before),
1978
                $exporter->export($after)
1979
            );
1980
1981
            throw new RiskyTestError(
1982
                $diff
1983
            );
1984
        }
1985
    }
1986
1987
    private function getProphet(): Prophet
1988
    {
1989
        if ($this->prophet === null) {
1990
            $this->prophet = new Prophet;
1991
        }
1992
1993
        return $this->prophet;
1994
    }
1995
1996
    /**
1997
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
1998
     */
1999
    private function shouldInvocationMockerBeReset(MockObject $mock): bool
2000
    {
2001
        $enumerator = new Enumerator;
2002
2003
        foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
2004
            if ($mock === $object) {
2005
                return false;
2006
            }
2007
        }
2008
2009
        if (!\is_array($this->testResult) && !\is_object($this->testResult)) {
2010
            return true;
2011
        }
2012
2013
        return !\in_array($mock, $enumerator->enumerate($this->testResult), true);
2014
    }
2015
2016
    /**
2017
     * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
2018
     * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException
2019
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
2020
     */
2021
    private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void
2022
    {
2023
        if ($this->registerMockObjectsFromTestArgumentsRecursively) {
2024
            $enumerator = new Enumerator;
2025
2026
            foreach ($enumerator->enumerate($testArguments) as $object) {
2027
                if ($object instanceof MockObject) {
2028
                    $this->registerMockObject($object);
2029
                }
2030
            }
2031
        } else {
2032
            foreach ($testArguments as $testArgument) {
2033
                if ($testArgument instanceof MockObject) {
2034
                    if ($this->isCloneable($testArgument)) {
2035
                        $testArgument = clone $testArgument;
2036
                    }
2037
2038
                    $this->registerMockObject($testArgument);
2039
                } elseif (\is_array($testArgument) && !\in_array($testArgument, $visited, true)) {
2040
                    $visited[] = $testArgument;
2041
2042
                    $this->registerMockObjectsFromTestArguments(
2043
                        $testArgument,
2044
                        $visited
2045
                    );
2046
                }
2047
            }
2048
        }
2049
    }
2050
2051
    private function setDoesNotPerformAssertionsFromAnnotation(): void
2052
    {
2053
        $annotations = $this->getAnnotations();
2054
2055
        if (isset($annotations['method']['doesNotPerformAssertions'])) {
2056
            $this->doesNotPerformAssertions = true;
2057
        }
2058
    }
2059
2060
    private function isCloneable(MockObject $testArgument): bool
2061
    {
2062
        $reflector = new ReflectionObject($testArgument);
2063
2064
        if (!$reflector->isCloneable()) {
2065
            return false;
2066
        }
2067
2068
        if ($reflector->hasMethod('__clone') &&
2069
            $reflector->getMethod('__clone')->isPublic()) {
2070
            return true;
2071
        }
2072
2073
        return false;
2074
    }
2075
2076
    private function unregisterCustomComparators(): void
2077
    {
2078
        $factory = ComparatorFactory::getInstance();
2079
2080
        foreach ($this->customComparators as $comparator) {
2081
            $factory->unregister($comparator);
2082
        }
2083
2084
        $this->customComparators = [];
2085
    }
2086
2087
    private function cleanupIniSettings(): void
2088
    {
2089
        foreach ($this->iniSettings as $varName => $oldValue) {
2090
            \ini_set($varName, $oldValue);
2091
        }
2092
2093
        $this->iniSettings = [];
2094
    }
2095
2096
    private function cleanupLocaleSettings(): void
2097
    {
2098
        foreach ($this->locale as $category => $locale) {
2099
            \setlocale($category, $locale);
2100
        }
2101
2102
        $this->locale = [];
2103
    }
2104
2105
    /**
2106
     * @throws ReflectionException
2107
     */
2108
    private function checkExceptionExpectations(Throwable $throwable): bool
2109
    {
2110
        $result = false;
2111
2112
        if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) {
2113
            $result = true;
2114
        }
2115
2116
        if ($throwable instanceof Exception) {
2117
            $result = false;
2118
        }
2119
2120
        if (\is_string($this->expectedException)) {
2121
            $reflector = new ReflectionClass($this->expectedException);
2122
2123
            if ($this->expectedException === 'PHPUnit\Framework\Exception' ||
2124
                $this->expectedException === '\PHPUnit\Framework\Exception' ||
2125
                $reflector->isSubclassOf(Exception::class)) {
2126
                $result = true;
2127
            }
2128
        }
2129
2130
        return $result;
2131
    }
2132
2133
    private function runInSeparateProcess(): bool
2134
    {
2135
        return ($this->runTestInSeparateProcess === true || $this->runClassInSeparateProcess === true) &&
2136
               $this->inIsolation !== true && !$this instanceof PhptTestCase;
2137
    }
2138
}
2139