Passed
Push — master ( b50327...7563db )
by Siad
20:52
created

PHPUnitTask::setExcludeGroups()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 7
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * Runs PHPUnit tests.
22
 *
23
 * @author  Michiel Rook <[email protected]>
24
 * @package phing.tasks.ext.phpunit
25
 * @see     BatchTest
26
 * @since   2.1.0
27
 */
28
class PHPUnitTask extends Task
29
{
30
    private $batchtests = [];
31
    /**
32
     * @var FormatterElement[] $formatters
33
     */
34
    private $formatters = [];
35
    private $bootstrap = "";
36
    private $haltonerror = false;
37
    private $haltonfailure = false;
38
    private $haltonincomplete = false;
39
    private $haltonskipped = false;
40
    private $haltondefect = false;
41
    private $haltonwarning = false;
42
    private $haltonrisky = false;
43
    private $errorproperty;
44
    private $failureproperty;
45
    private $incompleteproperty;
46
    private $skippedproperty;
47
    private $warningproperty;
48
    private $riskyproperty;
49
    private $printsummary = false;
50
    private $testfailed = false;
51
    private $testfailuremessage = "";
52
    private $codecoverage = null;
53
    private $groups = [];
54
    private $excludeGroups = [];
55
    private $processIsolation = false;
56
    private $usecustomerrorhandler = true;
57
    private $listeners = [];
58
59
    /**
60
     * @var string
61
     */
62
    private $pharLocation = "";
63
64
    /**
65
     * @var PhingFile
66
     */
67
    private $configuration = null;
68
69
    /**
70
     * Initialize Task.
71
     * This method includes any necessary PHPUnit libraries and triggers
72
     * appropriate error if they cannot be found.  This is not done in header
73
     * because we may want this class to be loaded w/o triggering an error.
74
     */
75 4
    public function init()
76
    {
77 4
    }
78
79 4
    private function loadPHPUnit()
80
    {
81 4
        if (!empty($this->pharLocation)) {
82
            $GLOBALS['_SERVER']['SCRIPT_NAME'] = '-';
83
            ob_start();
84
            @include $this->pharLocation;
85
            ob_end_clean();
86
        }
87
88 4
        if (!class_exists('PHPUnit\Runner\Version')) {
89
            throw new BuildException("PHPUnitTask requires PHPUnit to be installed", $this->getLocation());
90
        }
91 4
    }
92
93
    /**
94
     * Sets the name of a bootstrap file that is run before
95
     * executing the tests
96
     *
97
     * @param string $bootstrap the name of the bootstrap file
98
     */
99 1
    public function setBootstrap($bootstrap)
100
    {
101 1
        $this->bootstrap = $bootstrap;
102 1
    }
103
104
    /**
105
     * @param $value
106
     */
107
    public function setErrorproperty($value)
108
    {
109
        $this->errorproperty = $value;
110
    }
111
112
    /**
113
     * @param $value
114
     */
115
    public function setFailureproperty($value)
116
    {
117
        $this->failureproperty = $value;
118
    }
119
120
    /**
121
     * @param $value
122
     */
123
    public function setIncompleteproperty($value)
124
    {
125
        $this->incompleteproperty = $value;
126
    }
127
128
    /**
129
     * @param $value
130
     */
131
    public function setSkippedproperty($value)
132
    {
133
        $this->skippedproperty = $value;
134
    }
135
136
    /**
137
     * @param $value
138
     */
139
    public function setRiskyproperty($value)
140
    {
141
        $this->riskyproperty = $value;
142
    }
143
144
    /**
145
     * @param $value
146
     */
147
    public function setWarningproperty($value)
148
    {
149
        $this->riskyproperty = $value;
150
    }
151
152
    /**
153
     * @return bool
154
     */
155
    public function getHaltondefect(): bool
156
    {
157
        return $this->haltondefect;
158
    }
159
160
    /**
161
     * @param bool $haltondefect
162
     */
163
    public function setHaltondefect(bool $haltondefect): void
164
    {
165
        $this->haltondefect = $haltondefect;
166
    }
167
168
    /**
169
     * @return bool
170
     */
171
    public function getHaltonwarning(): bool
172
    {
173
        return $this->haltonwarning;
174
    }
175
176
    /**
177
     * @param bool $haltonwarning
178
     */
179
    public function setHaltonwarning(bool $haltonwarning): void
180
    {
181
        $this->haltonwarning = $haltonwarning;
182
    }
183
184
    /**
185
     * @return bool
186
     */
187
    public function getHaltonrisky(): bool
188
    {
189
        return $this->haltonrisky;
190
    }
191
192
    /**
193
     * @param bool $haltonrisky
194
     */
195
    public function setHaltonrisky(bool $haltonrisky): void
196
    {
197
        $this->haltonrisky = $haltonrisky;
198
    }
199
200
    /**
201
     * @param $value
202
     */
203 3
    public function setHaltonerror($value)
204
    {
205 3
        $this->haltonerror = $value;
206 3
    }
207
208
    /**
209
     * @param $value
210
     */
211 3
    public function setHaltonfailure($value)
212
    {
213 3
        $this->haltonfailure = $value;
214 3
    }
215
216
    /**
217
     * @return bool
218
     */
219
    public function getHaltonfailure()
220
    {
221
        return $this->haltonfailure;
222
    }
223
224
    /**
225
     * @param $value
226
     */
227
    public function setHaltonincomplete($value)
228
    {
229
        $this->haltonincomplete = $value;
230
    }
231
232
    /**
233
     * @return bool
234
     */
235 1
    public function getHaltonincomplete()
236
    {
237 1
        return $this->haltonincomplete;
238
    }
239
240
    /**
241
     * @param $value
242
     */
243
    public function setHaltonskipped($value)
244
    {
245
        $this->haltonskipped = $value;
246
    }
247
248
    /**
249
     * @return bool
250
     */
251 1
    public function getHaltonskipped()
252
    {
253 1
        return $this->haltonskipped;
254
    }
255
256
    /**
257
     * @param $printsummary
258
     */
259 1
    public function setPrintsummary($printsummary)
260
    {
261 1
        $this->printsummary = $printsummary;
262 1
    }
263
264
    /**
265
     * @param $codecoverage
266
     */
267 1
    public function setCodecoverage($codecoverage)
268
    {
269 1
        $this->codecoverage = $codecoverage;
270 1
    }
271
272
    /**
273
     * @param $processIsolation
274
     */
275
    public function setProcessIsolation($processIsolation)
276
    {
277
        $this->processIsolation = $processIsolation;
278
    }
279
280
    /**
281
     * @param $usecustomerrorhandler
282
     */
283
    public function setUseCustomErrorHandler($usecustomerrorhandler)
284
    {
285
        $this->usecustomerrorhandler = $usecustomerrorhandler;
286
    }
287
288
    /**
289
     * @param $groups
290
     */
291
    public function setGroups($groups)
292
    {
293
        $token = ' ,;';
294
        $this->groups = [];
295
        $tok = strtok($groups, $token);
296
        while ($tok !== false) {
297
            $this->groups[] = $tok;
298
            $tok = strtok($token);
299
        }
300
    }
301
302
    /**
303
     * @param $excludeGroups
304
     */
305
    public function setExcludeGroups($excludeGroups)
306
    {
307
        $token = ' ,;';
308
        $this->excludeGroups = [];
309
        $tok = strtok($excludeGroups, $token);
310
        while ($tok !== false) {
311
            $this->excludeGroups[] = $tok;
312
            $tok = strtok($token);
313
        }
314
    }
315
316
    /**
317
     * Add a new formatter to all tests of this task.
318
     *
319
     * @param FormatterElement $fe formatter element
320
     */
321 2
    public function addFormatter(FormatterElement $fe)
322
    {
323 2
        $fe->setParent($this);
324 2
        $this->formatters[] = $fe;
325 2
    }
326
327
    /**
328
     * Add a new listener to all tests of this taks
329
     *
330
     * @param $listener
331
     */
332
    private function addListener($listener)
333
    {
334
        $this->listeners[] = $listener;
335
    }
336
337
    /**
338
     * @param PhingFile $configuration
339
     */
340
    public function setConfiguration(PhingFile $configuration)
341
    {
342
        $this->configuration = $configuration;
343
    }
344
345
    /**
346
     * @param string $pharLocation
347
     */
348
    public function setPharLocation($pharLocation)
349
    {
350
        $this->pharLocation = $pharLocation;
351
    }
352
353
    /**
354
     * Load and processes the PHPUnit configuration
355
     *
356
     * @param  $configuration
357
     * @return mixed
358
     * @throws ReflectionException
359
     * @throws BuildException
360
     */
361
    protected function handlePHPUnitConfiguration(PhingFile $configuration)
362
    {
363
        if (!$configuration->exists()) {
364
            throw new BuildException("Unable to find PHPUnit configuration file '" . (string) $configuration . "'");
365
        }
366
367
        if (class_exists('\PHPUnit\TextUI\Configuration\Registry')) {
368
            $config = \PHPUnit\TextUI\Configuration\Registry::getInstance()->get($configuration->getAbsolutePath());
0 ignored issues
show
Bug introduced by
The type PHPUnit\TextUI\Configuration\Registry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
369
        } elseif (class_exists('\PHPUnit\TextUI\XmlConfiguration\Loader')) {
370
            $config = (new \PHPUnit\TextUI\XmlConfiguration\Loader())->load($configuration->getAbsolutePath());
371
        } else {
372
            throw new BuildException("Can't parse PHPUnit configuration file '" . (string) $configuration . "'");
373
        }
374
375
        if (empty($config)) {
376
            return [];
377
        }
378
379
        $phpunit = $config->phpunit();
380
381
        if (empty($phpunit)) {
382
            return [];
383
        }
384
385
        if ($phpunit->hasBootstrap()) {
386
            $this->setBootstrap($phpunit->bootstrap());
387
        }
388
        $this->setHaltonfailure($phpunit->stopOnFailure());
389
        $this->setHaltonerror($phpunit->stopOnError());
390
        $this->setHaltonskipped($phpunit->stopOnSkipped());
391
        $this->setHaltonincomplete($phpunit->stopOnIncomplete());
392
        $this->setHaltondefect($phpunit->stopOnDefect());
393
        $this->setHaltonwarning($phpunit->stopOnWarning());
394
        $this->setHaltonrisky($phpunit->stopOnRisky());
395
        $this->setProcessIsolation($phpunit->processIsolation());
396
397
        foreach ($config->listeners() as $listener) {
398
            if (
399
                !class_exists($listener->className(), false)
400
                && $listener->hasSourceFile()
401
            ) {
402
                include_once $listener->sourceFile();
403
            }
404
405
            if (class_exists($listener->className())) {
406
                if ($listener->hasArguments()) {
407
                    $listener = (new $listener->className())();
408
                } else {
409
                    $listenerClass = new ReflectionClass(
410
                        $listener->className()
411
                    );
412
                    $listener = $listenerClass->newInstanceArgs(
413
                        $listener->arguments()
414
                    );
415
                }
416
417
                if ($listener instanceof \PHPUnit\Framework\TestListener) {
418
                    $this->addListener($listener);
419
                }
420
            }
421
        }
422
423
        return $phpunit;
424
    }
425
426
    /**
427
     * The main entry point
428
     *
429
     * @throws BuildException
430
     */
431 4
    public function main()
432
    {
433 4
        if ($this->codecoverage && !extension_loaded('xdebug')) {
434
            throw new BuildException("PHPUnitTask depends on Xdebug being installed to gather code coverage information.");
435
        }
436
437 4
        $this->loadPHPUnit();
438 4
        $suite = new \PHPUnit\Framework\TestSuite('AllTests');
439 4
        $autoloadSave = spl_autoload_functions();
440
441 4
        if ($this->bootstrap) {
442 1
            include $this->bootstrap;
443
        }
444
445 4
        if ($this->configuration) {
446
            $phpunit = $this->handlePHPUnitConfiguration($this->configuration);
447
448
            if ($phpunit->backupGlobals() === false) {
449
                $suite->setBackupGlobals(false);
450
            }
451
452
            if ($phpunit->backupStaticAttributes() === true) {
453
                $suite->setBackupStaticAttributes(true);
454
            }
455
        }
456
457 4
        if ($this->printsummary) {
458 1
            $fe = new FormatterElement();
459 1
            $fe->setParent($this);
460 1
            $fe->setType("summary");
461 1
            $fe->setUseFile(false);
462 1
            $this->formatters[] = $fe;
463
        }
464
465 4
        foreach ($this->batchtests as $batchTest) {
466 4
            $this->appendBatchTestToTestSuite($batchTest, $suite);
467
        }
468
469 4
        $this->execute($suite);
470
471 4
        if ($this->testfailed) {
472 1
            throw new BuildException($this->testfailuremessage);
473
        }
474
475 3
        $autoloadNew = spl_autoload_functions();
476 3
        if (is_array($autoloadNew)) {
0 ignored issues
show
introduced by
The condition is_array($autoloadNew) is always true.
Loading history...
477 3
            foreach ($autoloadNew as $autoload) {
478 3
                spl_autoload_unregister($autoload);
479
            }
480
        }
481
482 3
        if (is_array($autoloadSave)) {
483 3
            foreach ($autoloadSave as $autoload) {
484 3
                spl_autoload_register($autoload);
485
            }
486
        }
487 3
    }
488
489
    /**
490
     * @param $suite
491
     * @throws BuildException
492
     * @throws ReflectionException
493
     */
494 4
    protected function execute($suite)
495
    {
496
        if (
497 4
            class_exists('\PHPUnit\Runner\Version', false) &&
498 4
            version_compare(\PHPUnit\Runner\Version::id(), '8.0.0', '<')
499
        ) {
500
            $runner = new PHPUnitTestRunner7(
501
                $this->project,
502
                $this->groups,
503
                $this->excludeGroups,
504
                $this->processIsolation
505
            );
506
        } else {
507 4
            $runner = new PHPUnitTestRunner8(
508 4
                $this->project,
509 4
                $this->groups,
510 4
                $this->excludeGroups,
511 4
                $this->processIsolation
512
            );
513
        }
514
515 4
        if ($this->codecoverage) {
516
            /**
517
             * Add some defaults to the PHPUnit filter
518
             */
519
            $pwd = __DIR__;
520
            $path = realpath($pwd . '/../../../');
521
522
            if (class_exists('\SebastianBergmann\CodeCoverage\Filter')) {
523
                $filter = new \SebastianBergmann\CodeCoverage\Filter();
524
                if (method_exists($filter, 'addDirectoryToBlacklist')) {
525
                    $filter->addDirectoryToBlacklist($path);
526
                }
527
                if (class_exists('\SebastianBergmann\CodeCoverage\CodeCoverage')) {
528
                    $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($filter);
529
                    $codeCoverage = new \SebastianBergmann\CodeCoverage\CodeCoverage($driver, $filter);
530
                    $runner->setCodecoverage($codeCoverage);
531
                }
532
            }
533
        }
534
535 4
        $runner->setUseCustomErrorHandler($this->usecustomerrorhandler);
536
537 4
        foreach ($this->listeners as $listener) {
538
            $runner->addListener($listener);
539
        }
540
541 4
        foreach ($this->formatters as $fe) {
542 2
            $formatter = $fe->getFormatter();
543
544 2
            if ($fe->getUseFile()) {
545
                try {
546
                    $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile());
547
                } catch (Exception $e) {
548
                    throw new BuildException('Unable to create destination.', $e);
549
                }
550
551
                $writer = new FileWriter($destFile->getAbsolutePath());
552
553
                $formatter->setOutput($writer);
554
            } else {
555 2
                $formatter->setOutput($this->getDefaultOutput());
556
            }
557
558 2
            $runner->addFormatter($formatter);
559
560 2
            $formatter->startTestRun();
561
        }
562
563 4
        $runner->run($suite);
564
565 4
        foreach ($this->formatters as $fe) {
566 2
            $formatter = $fe->getFormatter();
567 2
            $formatter->endTestRun();
568
        }
569
570 4
        if ($runner->hasErrors()) {
571 2
            if ($this->errorproperty) {
572
                $this->project->setNewProperty($this->errorproperty, true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of Project::setNewProperty(). ( Ignorable by Annotation )

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

572
                $this->project->setNewProperty($this->errorproperty, /** @scrutinizer ignore-type */ true);
Loading history...
573
            }
574 2
            if ($this->haltonerror) {
575
                $this->testfailed = true;
576
                $this->testfailuremessage = $runner->getLastErrorMessage();
577
            }
578
        }
579
580 4
        if ($runner->hasFailures()) {
581 3
            if ($this->failureproperty) {
582
                $this->project->setNewProperty($this->failureproperty, true);
583
            }
584
585 3
            if ($this->haltonfailure) {
586 1
                $this->testfailed = true;
587 1
                $this->testfailuremessage = $runner->getLastFailureMessage();
588
            }
589
        }
590
591 4
        if ($runner->hasIncomplete()) {
592
            if ($this->incompleteproperty) {
593
                $this->project->setNewProperty($this->incompleteproperty, true);
594
            }
595
596
            if ($this->haltonincomplete) {
597
                $this->testfailed = true;
598
                $this->testfailuremessage = $runner->getLastIncompleteMessage();
599
            }
600
        }
601
602 4
        if ($runner->hasSkipped()) {
603
            if ($this->skippedproperty) {
604
                $this->project->setNewProperty($this->skippedproperty, true);
605
            }
606
607
            if ($this->haltonskipped) {
608
                $this->testfailed = true;
609
                $this->testfailuremessage = $runner->getLastSkippedMessage();
610
            }
611
        }
612
613 4
        if ($runner->hasWarnings()) {
614
            if ($this->warningproperty) {
615
                $this->project->setNewProperty($this->warningproperty, true);
616
            }
617
618
            if ($this->haltonwarning) {
619
                $this->testfailed = true;
620
                $this->testfailuremessage = $runner->getLastWarningMessage();
621
            }
622
        }
623
624 4
        if ($runner->hasRisky()) {
625
            if ($this->riskyproperty) {
626
                $this->project->setNewProperty($this->riskyproperty, true);
627
            }
628
629
            if ($this->haltonrisky) {
630
                $this->testfailed = true;
631
                $this->testfailuremessage = $runner->getLastRiskyMessage();
632
            }
633
        }
634 4
    }
635
636
    /**
637
     * Add the tests in this batchtest to a test suite
638
     *
639
     * @param BatchTest $batchTest
640
     * @param PHPUnit\Framework\TestSuite $suite
641
     * @throws BuildException
642
     * @throws ReflectionException
643
     */
644 4
    protected function appendBatchTestToTestSuite(BatchTest $batchTest, $suite)
645
    {
646 4
        foreach ($batchTest->elements() as $element) {
647 4
            $testClass = new $element();
648 4
            if (!($testClass instanceof PHPUnit\Framework\TestSuite)) {
649 4
                $testClass = new ReflectionClass($element);
650
            }
651
            try {
652 4
                $suite->addTestSuite($testClass);
653
            } catch (\PHPUnit\Framework\Exception $e) {
654
                throw new BuildException('Unable to add TestSuite ' . get_class($testClass), $e);
655
            }
656
        }
657 4
    }
658
659
    /**
660
     * @return LogWriter
661
     */
662 2
    protected function getDefaultOutput()
663
    {
664 2
        return new LogWriter($this);
665
    }
666
667
    /**
668
     * Adds a set of tests based on pattern matching.
669
     *
670
     * @return BatchTest a new instance of a batch test.
671
     */
672 4
    public function createBatchTest()
673
    {
674 4
        $batchtest = new BatchTest($this->getProject());
675
676 4
        $this->batchtests[] = $batchtest;
677
678 4
        return $batchtest;
679
    }
680
}
681