Completed
Push — master ( 96d573...f9f049 )
by Ehsan
07:54
created

TestSuite::setBackupGlobals()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of PHPUnit.
4
 *
5
 * (c) Sebastian Bergmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace PHPUnit\Framework;
11
12
use Iterator;
13
use IteratorAggregate;
14
use PHPUnit\Runner\BaseTestRunner;
15
use PHPUnit\Runner\Filter\Factory;
16
use PHPUnit\Runner\PhptTestCase;
17
use PHPUnit\Util\Fileloader;
18
use PHPUnit\Util\InvalidArgumentHelper;
19
use RecursiveIteratorIterator;
20
use ReflectionClass;
21
use ReflectionMethod;
22
use Throwable;
23
24
/**
25
 * A TestSuite is a composite of Tests. It runs a collection of test cases.
26
 */
27
class TestSuite implements Test, SelfDescribing, IteratorAggregate
28
{
29
    /**
30
     * Last count of tests in this suite.
31
     *
32
     * @var int|null
33
     */
34
    private $cachedNumTests;
35
36
    /**
37
     * Enable or disable the backup and restoration of the $GLOBALS array.
38
     *
39
     * @var bool
40
     */
41
    protected $backupGlobals;
42
43
    /**
44
     * Enable or disable the backup and restoration of static attributes.
45
     *
46
     * @var bool
47
     */
48
    protected $backupStaticAttributes;
49
50
    /**
51
     * @var bool
52
     */
53
    private $beStrictAboutChangesToGlobalState;
54
55
    /**
56
     * @var bool
57
     */
58
    protected $runTestInSeparateProcess = false;
59
60
    /**
61
     * The name of the test suite.
62
     *
63
     * @var string
64
     */
65
    protected $name = '';
66
67
    /**
68
     * The test groups of the test suite.
69
     *
70
     * @var array
71
     */
72
    protected $groups = [];
73
74
    /**
75
     * The tests in the test suite.
76
     *
77
     * @var array
78
     */
79
    protected $tests = [];
80
81
    /**
82
     * The number of tests in the test suite.
83
     *
84
     * @var int
85
     */
86
    protected $numTests = -1;
87
88
    /**
89
     * @var bool
90
     */
91
    protected $testCase = false;
92
93
    /**
94
     * @var array
95
     */
96
    protected $foundClasses = [];
97
98
    /**
99
     * @var Factory
100
     */
101
    private $iteratorFilter;
102
103
    /**
104
     * Constructs a new TestSuite:
105
     *
106
     *   - PHPUnit_Framework_TestSuite() constructs an empty TestSuite.
107
     *
108
     *   - PHPUnit_Framework_TestSuite(ReflectionClass) constructs a
109
     *     TestSuite from the given class.
110
     *
111
     *   - PHPUnit_Framework_TestSuite(ReflectionClass, String)
112
     *     constructs a TestSuite from the given class with the given
113
     *     name.
114
     *
115
     *   - PHPUnit_Framework_TestSuite(String) either constructs a
116
     *     TestSuite from the given class (if the passed string is the
117
     *     name of an existing class) or constructs an empty TestSuite
118
     *     with the given name.
119
     *
120
     * @param mixed  $theClass
121
     * @param string $name
122
     *
123
     * @throws Exception
124
     */
125
    public function __construct($theClass = '', $name = '')
126
    {
127
        $argumentsValid = false;
128
129
        if (\is_object($theClass) &&
130
            $theClass instanceof ReflectionClass) {
131
            $argumentsValid = true;
132
        } elseif (\is_string($theClass) &&
133
            $theClass !== '' &&
134
            \class_exists($theClass, false)) {
135
            $argumentsValid = true;
136
137
            if ($name == '') {
138
                $name = $theClass;
139
            }
140
141
            $theClass = new ReflectionClass($theClass);
142
        } elseif (\is_string($theClass)) {
143
            $this->setName($theClass);
144
145
            return;
146
        }
147
148
        if (!$argumentsValid) {
149
            throw new Exception;
150
        }
151
152
        if (!$theClass->isSubclassOf(TestCase::class)) {
153
            throw new Exception(
154
                'Class "' . $theClass->name . '" does not extend PHPUnit\Framework\TestCase.'
155
            );
156
        }
157
158
        if ($name != '') {
159
            $this->setName($name);
160
        } else {
161
            $this->setName($theClass->getName());
162
        }
163
164
        $constructor = $theClass->getConstructor();
165
166
        if ($constructor !== null &&
167
            !$constructor->isPublic()) {
168
            $this->addTest(
169
                self::warning(
170
                    \sprintf(
171
                        'Class "%s" has no public constructor.',
172
                        $theClass->getName()
173
                    )
174
                )
175
            );
176
177
            return;
178
        }
179
180
        foreach ($theClass->getMethods() as $method) {
181
            $this->addTestMethod($theClass, $method);
182
        }
183
184
        if (empty($this->tests)) {
185
            $this->addTest(
186
                self::warning(
187
                    \sprintf(
188
                        'No tests found in class "%s".',
189
                        $theClass->getName()
190
                    )
191
                )
192
            );
193
        }
194
195
        $this->testCase = true;
196
    }
197
198
    /**
199
     * Returns a string representation of the test suite.
200
     *
201
     * @return string
202
     */
203
    public function toString()
204
    {
205
        return $this->getName();
206
    }
207
208
    /**
209
     * Adds a test to the suite.
210
     *
211
     * @param Test  $test
212
     * @param array $groups
213
     */
214
    public function addTest(Test $test, $groups = [])
215
    {
216
        $class = new ReflectionClass($test);
217
218
        if (!$class->isAbstract()) {
219
            $this->tests[]  = $test;
220
            $this->numTests = -1;
221
222
            if ($test instanceof self && empty($groups)) {
223
                $groups = $test->getGroups();
224
            }
225
226
            if (empty($groups)) {
227
                $groups = ['default'];
228
            }
229
230
            foreach ($groups as $group) {
231
                if (!isset($this->groups[$group])) {
232
                    $this->groups[$group] = [$test];
233
                } else {
234
                    $this->groups[$group][] = $test;
235
                }
236
            }
237
238
            if ($test instanceof TestCase) {
239
                $test->setGroups($groups);
240
            }
241
        }
242
    }
243
244
    /**
245
     * Adds the tests from the given class to the suite.
246
     *
247
     * @param mixed $testClass
248
     *
249
     * @throws Exception
250
     */
251
    public function addTestSuite($testClass)
252
    {
253
        if (\is_string($testClass) && \class_exists($testClass)) {
254
            $testClass = new ReflectionClass($testClass);
255
        }
256
257
        if (!\is_object($testClass)) {
258
            throw InvalidArgumentHelper::factory(
259
                1,
260
                'class name or object'
261
            );
262
        }
263
264
        if ($testClass instanceof self) {
265
            $this->addTest($testClass);
266
        } elseif ($testClass instanceof ReflectionClass) {
267
            $suiteMethod = false;
268
269
            if (!$testClass->isAbstract()) {
270
                if ($testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) {
271
                    $method = $testClass->getMethod(
272
                        BaseTestRunner::SUITE_METHODNAME
273
                    );
274
275
                    if ($method->isStatic()) {
276
                        $this->addTest(
277
                            $method->invoke(null, $testClass->getName())
0 ignored issues
show
Bug introduced by
Consider using $testClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
278
                        );
279
280
                        $suiteMethod = true;
281
                    }
282
                }
283
            }
284
285
            if (!$suiteMethod && !$testClass->isAbstract()) {
286
                $this->addTest(new self($testClass));
287
            }
288
        } else {
289
            throw new Exception;
290
        }
291
    }
292
293
    /**
294
     * Wraps both <code>addTest()</code> and <code>addTestSuite</code>
295
     * as well as the separate import statements for the user's convenience.
296
     *
297
     * If the named file cannot be read or there are no new tests that can be
298
     * added, a <code>PHPUnit_Framework_WarningTestCase</code> will be created instead,
299
     * leaving the current test run untouched.
300
     *
301
     * @param string $filename
302
     *
303
     * @throws Exception
304
     */
305
    public function addTestFile($filename)
306
    {
307
        if (!\is_string($filename)) {
308
            throw InvalidArgumentHelper::factory(1, 'string');
309
        }
310
311
        if (\file_exists($filename) && \substr($filename, -5) == '.phpt') {
312
            $this->addTest(
313
                new PhptTestCase($filename)
314
            );
315
316
            return;
317
        }
318
319
        // The given file may contain further stub classes in addition to the
320
        // test class itself. Figure out the actual test class.
321
        $classes    = \get_declared_classes();
322
        $filename   = Fileloader::checkAndLoad($filename);
323
        $newClasses = \array_diff(\get_declared_classes(), $classes);
324
325
        // The diff is empty in case a parent class (with test methods) is added
326
        // AFTER a child class that inherited from it. To account for that case,
327
        // cumulate all discovered classes, so the parent class may be found in
328
        // a later invocation.
329
        if (!empty($newClasses)) {
330
            // On the assumption that test classes are defined first in files,
331
            // process discovered classes in approximate LIFO order, so as to
332
            // avoid unnecessary reflection.
333
            $this->foundClasses = \array_merge($newClasses, $this->foundClasses);
334
        }
335
336
        // The test class's name must match the filename, either in full, or as
337
        // a PEAR/PSR-0 prefixed shortname ('NameSpace_ShortName'), or as a
338
        // PSR-1 local shortname ('NameSpace\ShortName'). The comparison must be
339
        // anchored to prevent false-positive matches (e.g., 'OtherShortName').
340
        $shortname      = \basename($filename, '.php');
341
        $shortnameRegEx = '/(?:^|_|\\\\)' . \preg_quote($shortname, '/') . '$/';
342
343
        foreach ($this->foundClasses as $i => $className) {
344
            if (\preg_match($shortnameRegEx, $className)) {
345
                $class = new ReflectionClass($className);
346
347
                if ($class->getFileName() == $filename) {
348
                    $newClasses = [$className];
349
                    unset($this->foundClasses[$i]);
350
                    break;
351
                }
352
            }
353
        }
354
355
        foreach ($newClasses as $className) {
356
            $class = new ReflectionClass($className);
357
358
            if (!$class->isAbstract()) {
359
                if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) {
360
                    $method = $class->getMethod(
361
                        BaseTestRunner::SUITE_METHODNAME
362
                    );
363
364
                    if ($method->isStatic()) {
365
                        $this->addTest($method->invoke(null, $className));
366
                    }
367
                } elseif ($class->implementsInterface(Test::class)) {
368
                    $this->addTestSuite($class);
369
                }
370
            }
371
        }
372
373
        $this->numTests = -1;
374
    }
375
376
    /**
377
     * Wrapper for addTestFile() that adds multiple test files.
378
     *
379
     * @param array|Iterator $filenames
380
     *
381
     * @throws Exception
382
     */
383
    public function addTestFiles($filenames)
384
    {
385
        if (!(\is_array($filenames) ||
386
            (\is_object($filenames) && $filenames instanceof Iterator))) {
387
            throw InvalidArgumentHelper::factory(
388
                1,
389
                'array or iterator'
390
            );
391
        }
392
393
        foreach ($filenames as $filename) {
394
            $this->addTestFile((string) $filename);
395
        }
396
    }
397
398
    /**
399
     * Counts the number of test cases that will be run by this test.
400
     *
401
     * @param bool $preferCache Indicates if cache is preferred.
402
     *
403
     * @return int
404
     */
405
    public function count($preferCache = false)
406
    {
407
        if ($preferCache && $this->cachedNumTests !== null) {
408
            return $this->cachedNumTests;
409
        }
410
411
        $numTests = 0;
412
413
        foreach ($this as $test) {
414
            $numTests += \count($test);
415
        }
416
417
        $this->cachedNumTests = $numTests;
418
419
        return $numTests;
420
    }
421
422
    /**
423
     * @param ReflectionClass $theClass
424
     * @param string          $name
425
     *
426
     * @return Test
427
     *
428
     * @throws Exception
429
     */
430
    public static function createTest(ReflectionClass $theClass, $name)
431
    {
432
        $className = $theClass->getName();
0 ignored issues
show
Bug introduced by
Consider using $theClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
433
434
        if (!$theClass->isInstantiable()) {
435
            return self::warning(
436
                \sprintf('Cannot instantiate class "%s".', $className)
437
            );
438
        }
439
440
        $backupSettings = \PHPUnit\Util\Test::getBackupSettings(
441
            $className,
442
            $name
443
        );
444
445
        $preserveGlobalState = \PHPUnit\Util\Test::getPreserveGlobalStateSettings(
446
            $className,
447
            $name
448
        );
449
450
        $runTestInSeparateProcess = \PHPUnit\Util\Test::getProcessIsolationSettings(
451
            $className,
452
            $name
453
        );
454
455
        $constructor = $theClass->getConstructor();
456
457
        if ($constructor !== null) {
458
            $parameters = $constructor->getParameters();
459
460
            // TestCase() or TestCase($name)
461
            if (\count($parameters) < 2) {
462
                $test = new $className;
463
            } // TestCase($name, $data)
464
            else {
465
                try {
466
                    $data = \PHPUnit\Util\Test::getProvidedData(
467
                        $className,
468
                        $name
469
                    );
470
                } catch (IncompleteTestError $e) {
471
                    $message = \sprintf(
472
                        'Test for %s::%s marked incomplete by data provider',
473
                        $className,
474
                        $name
475
                    );
476
477
                    $_message = $e->getMessage();
478
479
                    if (!empty($_message)) {
480
                        $message .= "\n" . $_message;
481
                    }
482
483
                    $data = self::incompleteTest($className, $name, $message);
484
                } catch (SkippedTestError $e) {
485
                    $message = \sprintf(
486
                        'Test for %s::%s skipped by data provider',
487
                        $className,
488
                        $name
489
                    );
490
491
                    $_message = $e->getMessage();
492
493
                    if (!empty($_message)) {
494
                        $message .= "\n" . $_message;
495
                    }
496
497
                    $data = self::skipTest($className, $name, $message);
498
                } catch (Throwable $_t) {
499
                    $t = $_t;
500
                } catch (Exception $_t) {
501
                    $t = $_t;
502
                }
503
504
                if (isset($t)) {
505
                    $message = \sprintf(
506
                        'The data provider specified for %s::%s is invalid.',
507
                        $className,
508
                        $name
509
                    );
510
511
                    $_message = $t->getMessage();
512
513
                    if (!empty($_message)) {
514
                        $message .= "\n" . $_message;
515
                    }
516
517
                    $data = self::warning($message);
518
                }
519
520
                // Test method with @dataProvider.
521
                if (isset($data)) {
522
                    $test = new DataProviderTestSuite(
523
                        $className . '::' . $name
524
                    );
525
526
                    if (empty($data)) {
527
                        $data = self::warning(
528
                            \sprintf(
529
                                'No tests found in suite "%s".',
530
                                $test->getName()
531
                            )
532
                        );
533
                    }
534
535
                    $groups = \PHPUnit\Util\Test::getGroups($className, $name);
536
537
                    if ($data instanceof WarningTestCase ||
538
                        $data instanceof SkippedTestCase ||
539
                        $data instanceof IncompleteTestCase) {
540
                        $test->addTest($data, $groups);
541
                    } else {
542
                        foreach ($data as $_dataName => $_data) {
543
                            $_test = new $className($name, $_data, $_dataName);
544
545
                            if ($runTestInSeparateProcess) {
546
                                $_test->setRunTestInSeparateProcess(true);
547
548
                                if ($preserveGlobalState !== null) {
549
                                    $_test->setPreserveGlobalState($preserveGlobalState);
550
                                }
551
                            }
552
553
                            if ($backupSettings['backupGlobals'] !== null) {
554
                                $_test->setBackupGlobals(
555
                                    $backupSettings['backupGlobals']
556
                                );
557
                            }
558
559
                            if ($backupSettings['backupStaticAttributes'] !== null) {
560
                                $_test->setBackupStaticAttributes(
561
                                    $backupSettings['backupStaticAttributes']
562
                                );
563
                            }
564
565
                            $test->addTest($_test, $groups);
566
                        }
567
                    }
568
                } else {
569
                    $test = new $className;
570
                }
571
            }
572
        }
573
574
        if (!isset($test)) {
575
            throw new Exception('No valid test provided.');
576
        }
577
578
        if ($test instanceof TestCase) {
579
            $test->setName($name);
580
581
            if ($runTestInSeparateProcess) {
582
                $test->setRunTestInSeparateProcess(true);
583
584
                if ($preserveGlobalState !== null) {
585
                    $test->setPreserveGlobalState($preserveGlobalState);
586
                }
587
            }
588
589
            if ($backupSettings['backupGlobals'] !== null) {
590
                $test->setBackupGlobals($backupSettings['backupGlobals']);
591
            }
592
593
            if ($backupSettings['backupStaticAttributes'] !== null) {
594
                $test->setBackupStaticAttributes(
595
                    $backupSettings['backupStaticAttributes']
596
                );
597
            }
598
        }
599
600
        return $test;
601
    }
602
603
    /**
604
     * Creates a default TestResult object.
605
     *
606
     * @return TestResult
607
     */
608
    protected function createResult()
609
    {
610
        return new TestResult;
611
    }
612
613
    /**
614
     * Returns the name of the suite.
615
     *
616
     * @return string
617
     */
618
    public function getName()
619
    {
620
        return $this->name;
621
    }
622
623
    /**
624
     * Returns the test groups of the suite.
625
     *
626
     * @return array
627
     */
628
    public function getGroups()
629
    {
630
        return \array_keys($this->groups);
631
    }
632
633
    public function getGroupDetails()
634
    {
635
        return $this->groups;
636
    }
637
638
    /**
639
     * Set tests groups of the test case
640
     *
641
     * @param array $groups
642
     */
643
    public function setGroupDetails(array $groups)
644
    {
645
        $this->groups = $groups;
646
    }
647
648
    /**
649
     * Runs the tests and collects their result in a TestResult.
650
     *
651
     * @param TestResult $result
652
     *
653
     * @return TestResult
654
     */
655
    public function run(TestResult $result = null)
656
    {
657
        if ($result === null) {
658
            $result = $this->createResult();
659
        }
660
661
        if (\count($this) == 0) {
662
            return $result;
663
        }
664
665
        $hookMethods = \PHPUnit\Util\Test::getHookMethods($this->name);
666
667
        $result->startTestSuite($this);
668
669
        try {
670
            $this->setUp();
671
672
            foreach ($hookMethods['beforeClass'] as $beforeClassMethod) {
673
                if ($this->testCase === true &&
674
                    \class_exists($this->name, false) &&
675
                    \method_exists($this->name, $beforeClassMethod)) {
676
                    if ($missingRequirements = \PHPUnit\Util\Test::getMissingRequirements($this->name, $beforeClassMethod)) {
677
                        $this->markTestSuiteSkipped(\implode(PHP_EOL, $missingRequirements));
678
                    }
679
680
                    \call_user_func([$this->name, $beforeClassMethod]);
681
                }
682
            }
683
        } catch (SkippedTestSuiteError $e) {
684
            $numTests = \count($this);
685
686
            for ($i = 0; $i < $numTests; $i++) {
687
                $result->startTest($this);
688
                $result->addFailure($this, $e, 0);
689
                $result->endTest($this, 0);
690
            }
691
692
            $this->tearDown();
693
            $result->endTestSuite($this);
694
695
            return $result;
696
        } catch (Throwable $_t) {
697
            $t = $_t;
698
        } catch (Exception $_t) {
699
            $t = $_t;
700
        }
701
702
        if (isset($t)) {
703
            $numTests = \count($this);
704
705
            for ($i = 0; $i < $numTests; $i++) {
706
                if ($result->shouldStop()) {
707
                    break;
708
                }
709
710
                $result->startTest($this);
711
                $result->addError($this, $t, 0);
712
                $result->endTest($this, 0);
713
            }
714
715
            $this->tearDown();
716
            $result->endTestSuite($this);
717
718
            return $result;
719
        }
720
721
        foreach ($this as $test) {
722
            if ($result->shouldStop()) {
723
                break;
724
            }
725
726
            if ($test instanceof TestCase || $test instanceof self) {
727
                $test->setbeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState);
728
                $test->setBackupGlobals($this->backupGlobals);
729
                $test->setBackupStaticAttributes($this->backupStaticAttributes);
730
                $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess);
731
            }
732
733
            $test->run($result);
734
        }
735
736
        foreach ($hookMethods['afterClass'] as $afterClassMethod) {
737
            if ($this->testCase === true && \class_exists($this->name, false) && \method_exists($this->name, $afterClassMethod)) {
738
                \call_user_func([$this->name, $afterClassMethod]);
739
            }
740
        }
741
742
        $this->tearDown();
743
744
        $result->endTestSuite($this);
745
746
        return $result;
747
    }
748
749
    /**
750
     * @param bool $runTestInSeparateProcess
751
     *
752
     * @throws Exception
753
     */
754
    public function setRunTestInSeparateProcess($runTestInSeparateProcess)
755
    {
756
        if (\is_bool($runTestInSeparateProcess)) {
757
            $this->runTestInSeparateProcess = $runTestInSeparateProcess;
758
        } else {
759
            throw InvalidArgumentHelper::factory(1, 'boolean');
760
        }
761
    }
762
763
    /**
764
     * Runs a test.
765
     *
766
     * @deprecated
767
     *
768
     * @param Test       $test
769
     * @param TestResult $result
770
     */
771
    public function runTest(Test $test, TestResult $result)
772
    {
773
        $test->run($result);
774
    }
775
776
    /**
777
     * Sets the name of the suite.
778
     *
779
     * @param  string
780
     */
781
    public function setName($name)
782
    {
783
        $this->name = $name;
784
    }
785
786
    /**
787
     * Returns the test at the given index.
788
     *
789
     * @param  int|false
790
     *
791
     * @return Test
792
     */
793
    public function testAt($index)
794
    {
795
        if (isset($this->tests[$index])) {
796
            return $this->tests[$index];
797
        }
798
799
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PHPUnit\Framework\TestSuite::testAt of type PHPUnit\Framework\Test.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
800
    }
801
802
    /**
803
     * Returns the tests as an enumeration.
804
     *
805
     * @return array
806
     */
807
    public function tests()
808
    {
809
        return $this->tests;
810
    }
811
812
    /**
813
     * Set tests of the test suite
814
     *
815
     * @param array $tests
816
     */
817
    public function setTests(array $tests)
818
    {
819
        $this->tests = $tests;
820
    }
821
822
    /**
823
     * Mark the test suite as skipped.
824
     *
825
     * @param string $message
826
     *
827
     * @throws SkippedTestSuiteError
828
     */
829
    public function markTestSuiteSkipped($message = '')
830
    {
831
        throw new SkippedTestSuiteError($message);
832
    }
833
834
    /**
835
     * @param ReflectionClass  $class
836
     * @param ReflectionMethod $method
837
     */
838
    protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method)
839
    {
840
        if (!$this->isTestMethod($method)) {
841
            return;
842
        }
843
844
        $name = $method->getName();
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
845
846
        if (!$method->isPublic()) {
847
            $this->addTest(
848
                self::warning(
849
                    \sprintf(
850
                        'Test method "%s" in test class "%s" is not public.',
851
                        $name,
852
                        $class->getName()
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
853
                    )
854
                )
855
            );
856
857
            return;
858
        }
859
860
        $test = self::createTest($class, $name);
861
862
        if ($test instanceof TestCase || $test instanceof DataProviderTestSuite) {
863
            $test->setDependencies(
864
                \PHPUnit\Util\Test::getDependencies($class->getName(), $name)
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
865
            );
866
        }
867
868
        $this->addTest(
869
            $test,
870
            \PHPUnit\Util\Test::getGroups($class->getName(), $name)
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
871
        );
872
    }
873
874
    /**
875
     * @param ReflectionMethod $method
876
     *
877
     * @return bool
878
     */
879
    public static function isTestMethod(ReflectionMethod $method)
880
    {
881
        if (\strpos($method->name, 'test') === 0) {
882
            return true;
883
        }
884
885
        // @scenario on TestCase::testMethod()
886
        // @test     on TestCase::testMethod()
887
        $docComment = $method->getDocComment();
888
889
        return \strpos($docComment, '@test') !== false ||
890
            \strpos($docComment, '@scenario') !== false;
891
    }
892
893
    /**
894
     * @param string $message
895
     *
896
     * @return WarningTestCase
897
     */
898
    protected static function warning($message)
899
    {
900
        return new WarningTestCase($message);
901
    }
902
903
    /**
904
     * @param string $class
905
     * @param string $methodName
906
     * @param string $message
907
     *
908
     * @return SkippedTestCase
909
     */
910
    protected static function skipTest($class, $methodName, $message)
911
    {
912
        return new SkippedTestCase($class, $methodName, $message);
913
    }
914
915
    /**
916
     * @param string $class
917
     * @param string $methodName
918
     * @param string $message
919
     *
920
     * @return IncompleteTestCase
921
     */
922
    protected static function incompleteTest($class, $methodName, $message)
923
    {
924
        return new IncompleteTestCase($class, $methodName, $message);
925
    }
926
927
    /**
928
     * @param bool $beStrictAboutChangesToGlobalState
929
     */
930
    public function setbeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState)
931
    {
932
        if (\is_null($this->beStrictAboutChangesToGlobalState) && \is_bool($beStrictAboutChangesToGlobalState)) {
933
            $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
934
        }
935
    }
936
937
    /**
938
     * @param bool $backupGlobals
939
     */
940
    public function setBackupGlobals($backupGlobals)
941
    {
942
        if (\is_null($this->backupGlobals) && \is_bool($backupGlobals)) {
943
            $this->backupGlobals = $backupGlobals;
944
        }
945
    }
946
947
    /**
948
     * @param bool $backupStaticAttributes
949
     */
950
    public function setBackupStaticAttributes($backupStaticAttributes)
951
    {
952
        if (\is_null($this->backupStaticAttributes) && \is_bool($backupStaticAttributes)) {
953
            $this->backupStaticAttributes = $backupStaticAttributes;
954
        }
955
    }
956
957
    /**
958
     * Returns an iterator for this test suite.
959
     *
960
     * @return RecursiveIteratorIterator
961
     */
962
    public function getIterator()
963
    {
964
        $iterator = new TestSuiteIterator($this);
965
966
        if ($this->iteratorFilter !== null) {
967
            $iterator = $this->iteratorFilter->factory($iterator, $this);
968
        }
969
970
        return $iterator;
971
    }
972
973
    public function injectFilter(Factory $filter)
974
    {
975
        $this->iteratorFilter = $filter;
976
        foreach ($this as $test) {
977
            if ($test instanceof self) {
978
                $test->injectFilter($filter);
979
            }
980
        }
981
    }
982
983
    /**
984
     * Template Method that is called before the tests
985
     * of this test suite are run.
986
     */
987
    protected function setUp()
988
    {
989
    }
990
991
    /**
992
     * Template Method that is called after the tests
993
     * of this test suite have finished running.
994
     */
995
    protected function tearDown()
996
    {
997
    }
998
}
999