Passed
Push — master ( c0bb05...28a3ce )
by Siad
05:34
created

PHPUnitTask::setErrorproperty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
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
     * @var \PHPUnit\TextUI\XmlConfiguration\CodeCoverage\CodeCoverage
71
     */
72
    private $codeCoverageConfig = null;
73
74
    /**
75
     * Initialize Task.
76
     * This method includes any necessary PHPUnit libraries and triggers
77
     * appropriate error if they cannot be found.  This is not done in header
78
     * because we may want this class to be loaded w/o triggering an error.
79
     */
80 4
    public function init()
81
    {
82 4
    }
83
84 4
    private function loadPHPUnit()
85
    {
86 4
        if (!empty($this->pharLocation)) {
87
            $GLOBALS['_SERVER']['SCRIPT_NAME'] = '-';
88
            ob_start();
89
            @include $this->pharLocation;
90
            ob_end_clean();
91
        }
92
93 4
        if (!class_exists('PHPUnit\Runner\Version')) {
94
            throw new BuildException("PHPUnitTask requires PHPUnit to be installed", $this->getLocation());
95
        }
96 4
    }
97
98
    /**
99
     * Sets the name of a bootstrap file that is run before
100
     * executing the tests
101
     *
102
     * @param string $bootstrap the name of the bootstrap file
103
     */
104 1
    public function setBootstrap($bootstrap)
105
    {
106 1
        $this->bootstrap = $bootstrap;
107 1
    }
108
109
    /**
110
     * @param $value
111
     */
112
    public function setErrorproperty($value)
113
    {
114
        $this->errorproperty = $value;
115
    }
116
117
    /**
118
     * @param $value
119
     */
120
    public function setFailureproperty($value)
121
    {
122
        $this->failureproperty = $value;
123
    }
124
125
    /**
126
     * @param $value
127
     */
128
    public function setIncompleteproperty($value)
129
    {
130
        $this->incompleteproperty = $value;
131
    }
132
133
    /**
134
     * @param $value
135
     */
136
    public function setSkippedproperty($value)
137
    {
138
        $this->skippedproperty = $value;
139
    }
140
141
    /**
142
     * @param $value
143
     */
144
    public function setRiskyproperty($value)
145
    {
146
        $this->riskyproperty = $value;
147
    }
148
149
    /**
150
     * @param $value
151
     */
152
    public function setWarningproperty($value)
153
    {
154
        $this->riskyproperty = $value;
155
    }
156
157
    /**
158
     * @return bool
159
     */
160
    public function getHaltondefect(): bool
161
    {
162
        return $this->haltondefect;
163
    }
164
165
    /**
166
     * @param bool $haltondefect
167
     */
168
    public function setHaltondefect(bool $haltondefect): void
169
    {
170
        $this->haltondefect = $haltondefect;
171
    }
172
173
    /**
174
     * @return bool
175
     */
176
    public function getHaltonwarning(): bool
177
    {
178
        return $this->haltonwarning;
179
    }
180
181
    /**
182
     * @param bool $haltonwarning
183
     */
184
    public function setHaltonwarning(bool $haltonwarning): void
185
    {
186
        $this->haltonwarning = $haltonwarning;
187
    }
188
189
    /**
190
     * @return bool
191
     */
192
    public function getHaltonrisky(): bool
193
    {
194
        return $this->haltonrisky;
195
    }
196
197
    /**
198
     * @param bool $haltonrisky
199
     */
200
    public function setHaltonrisky(bool $haltonrisky): void
201
    {
202
        $this->haltonrisky = $haltonrisky;
203
    }
204
205
    /**
206
     * @param $value
207
     */
208 3
    public function setHaltonerror($value)
209
    {
210 3
        $this->haltonerror = $value;
211 3
    }
212
213
    /**
214
     * @param $value
215
     */
216 3
    public function setHaltonfailure($value)
217
    {
218 3
        $this->haltonfailure = $value;
219 3
    }
220
221
    /**
222
     * @return bool
223
     */
224
    public function getHaltonfailure()
225
    {
226
        return $this->haltonfailure;
227
    }
228
229
    /**
230
     * @param $value
231
     */
232
    public function setHaltonincomplete($value)
233
    {
234
        $this->haltonincomplete = $value;
235
    }
236
237
    /**
238
     * @return bool
239
     */
240 1
    public function getHaltonincomplete()
241
    {
242 1
        return $this->haltonincomplete;
243
    }
244
245
    /**
246
     * @param $value
247
     */
248
    public function setHaltonskipped($value)
249
    {
250
        $this->haltonskipped = $value;
251
    }
252
253
    /**
254
     * @return bool
255
     */
256 1
    public function getHaltonskipped()
257
    {
258 1
        return $this->haltonskipped;
259
    }
260
261
    /**
262
     * @param $printsummary
263
     */
264 1
    public function setPrintsummary($printsummary)
265
    {
266 1
        $this->printsummary = $printsummary;
267 1
    }
268
269
    /**
270
     * @param $codecoverage
271
     */
272 1
    public function setCodecoverage($codecoverage)
273
    {
274 1
        $this->codecoverage = $codecoverage;
275 1
    }
276
277
    /**
278
     * @param $processIsolation
279
     */
280
    public function setProcessIsolation($processIsolation)
281
    {
282
        $this->processIsolation = $processIsolation;
283
    }
284
285
    /**
286
     * @param $usecustomerrorhandler
287
     */
288
    public function setUseCustomErrorHandler($usecustomerrorhandler)
289
    {
290
        $this->usecustomerrorhandler = $usecustomerrorhandler;
291
    }
292
293
    /**
294
     * @param $groups
295
     */
296
    public function setGroups($groups)
297
    {
298
        $token = ' ,;';
299
        $this->groups = [];
300
        $tok = strtok($groups, $token);
301
        while ($tok !== false) {
302
            $this->groups[] = $tok;
303
            $tok = strtok($token);
304
        }
305
    }
306
307
    /**
308
     * @param $excludeGroups
309
     */
310
    public function setExcludeGroups($excludeGroups)
311
    {
312
        $token = ' ,;';
313
        $this->excludeGroups = [];
314
        $tok = strtok($excludeGroups, $token);
315
        while ($tok !== false) {
316
            $this->excludeGroups[] = $tok;
317
            $tok = strtok($token);
318
        }
319
    }
320
321
    /**
322
     * Add a new formatter to all tests of this task.
323
     *
324
     * @param FormatterElement $fe formatter element
325
     */
326 2
    public function addFormatter(FormatterElement $fe)
327
    {
328 2
        $fe->setParent($this);
329 2
        $this->formatters[] = $fe;
330 2
    }
331
332
    /**
333
     * Add a new listener to all tests of this taks
334
     *
335
     * @param $listener
336
     */
337
    private function addListener($listener)
338
    {
339
        $this->listeners[] = $listener;
340
    }
341
342
    /**
343
     * @param PhingFile $configuration
344
     */
345
    public function setConfiguration(PhingFile $configuration)
346
    {
347
        $this->configuration = $configuration;
348
    }
349
350
    /**
351
     * @param string $pharLocation
352
     */
353
    public function setPharLocation($pharLocation)
354
    {
355
        $this->pharLocation = $pharLocation;
356
    }
357
358
    /**
359
     * Load and processes the PHPUnit configuration
360
     *
361
     * @param  $configuration
362
     * @return mixed
363
     * @throws ReflectionException
364
     * @throws BuildException
365
     */
366
    protected function handlePHPUnitConfiguration(PhingFile $configuration)
367
    {
368
        if (!$configuration->exists()) {
369
            throw new BuildException("Unable to find PHPUnit configuration file '" . (string) $configuration . "'");
370
        }
371
372
        if (class_exists('\PHPUnit\TextUI\Configuration\Registry')) {
373
            $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...
374
        } elseif (class_exists('\PHPUnit\TextUI\XmlConfiguration\Loader')) {
375
            $config = (new \PHPUnit\TextUI\XmlConfiguration\Loader())->load($configuration->getAbsolutePath());
376
        } else {
377
            throw new BuildException("Can't parse PHPUnit configuration file '" . (string) $configuration . "'");
378
        }
379
380
        if (empty($config)) {
381
            return [];
382
        }
383
384
        $phpunit = $config->phpunit();
385
386
        if (empty($phpunit)) {
387
            return [];
388
        }
389
390
        if ($phpunit->hasBootstrap()) {
391
            $this->setBootstrap($phpunit->bootstrap());
392
        }
393
        $this->setHaltonfailure($phpunit->stopOnFailure());
394
        $this->setHaltonerror($phpunit->stopOnError());
395
        $this->setHaltonskipped($phpunit->stopOnSkipped());
396
        $this->setHaltonincomplete($phpunit->stopOnIncomplete());
397
        $this->setHaltondefect($phpunit->stopOnDefect());
398
        $this->setHaltonwarning($phpunit->stopOnWarning());
399
        $this->setHaltonrisky($phpunit->stopOnRisky());
400
        $this->setProcessIsolation($phpunit->processIsolation());
401
402
        foreach ($config->listeners() as $listener) {
403
            if (
404
                !class_exists($listener->className(), false)
405
                && $listener->hasSourceFile()
406
            ) {
407
                include_once $listener->sourceFile();
408
            }
409
410
            if (class_exists($listener->className())) {
411
                if ($listener->hasArguments()) {
412
                    $listener = (new $listener->className())();
413
                } else {
414
                    $listenerClass = new ReflectionClass(
415
                        $listener->className()
416
                    );
417
                    $listener = $listenerClass->newInstanceArgs(
418
                        $listener->arguments()
419
                    );
420
                }
421
422
                if ($listener instanceof \PHPUnit\Framework\TestListener) {
423
                    $this->addListener($listener);
424
                }
425
            }
426
        }
427
428
        $this->codeCoverageConfig = $config->codeCoverage();
429
        return $phpunit;
430
    }
431
432
    /**
433
     * The main entry point
434
     *
435
     * @throws BuildException
436
     */
437 4
    public function main()
438
    {
439 4
        if ($this->codecoverage && !extension_loaded('xdebug')) {
440
            throw new BuildException("PHPUnitTask depends on Xdebug being installed to gather code coverage information.");
441
        }
442
443 4
        $this->loadPHPUnit();
444 4
        $suite = new \PHPUnit\Framework\TestSuite('AllTests');
445 4
        $autoloadSave = spl_autoload_functions();
446
447 4
        if ($this->bootstrap) {
448 1
            include $this->bootstrap;
449
        }
450
451 4
        if ($this->configuration) {
452
            $phpunit = $this->handlePHPUnitConfiguration($this->configuration);
453
454
            if ($phpunit->backupGlobals() === false) {
455
                $suite->setBackupGlobals(false);
456
            }
457
458
            if ($phpunit->backupStaticAttributes() === true) {
459
                $suite->setBackupStaticAttributes(true);
460
            }
461
        }
462
463 4
        if ($this->printsummary) {
464 1
            $fe = new FormatterElement();
465 1
            $fe->setParent($this);
466 1
            $fe->setType("summary");
467 1
            $fe->setUseFile(false);
468 1
            $this->formatters[] = $fe;
469
        }
470
471 4
        foreach ($this->batchtests as $batchTest) {
472 4
            $this->appendBatchTestToTestSuite($batchTest, $suite);
473
        }
474
475 4
        $this->execute($suite);
476
477 4
        if ($this->testfailed) {
478 1
            throw new BuildException($this->testfailuremessage);
479
        }
480
481 3
        $autoloadNew = spl_autoload_functions();
482 3
        if (is_array($autoloadNew)) {
0 ignored issues
show
introduced by
The condition is_array($autoloadNew) is always true.
Loading history...
483 3
            foreach ($autoloadNew as $autoload) {
484 3
                spl_autoload_unregister($autoload);
485
            }
486
        }
487
488 3
        if (is_array($autoloadSave)) {
489 3
            foreach ($autoloadSave as $autoload) {
490 3
                spl_autoload_register($autoload);
491
            }
492
        }
493 3
    }
494
495
    /**
496
     * @param $suite
497
     * @throws BuildException
498
     * @throws ReflectionException
499
     */
500 4
    protected function execute($suite)
501
    {
502
        if (
503 4
            class_exists('\PHPUnit\Runner\Version', false) &&
504 4
            version_compare(\PHPUnit\Runner\Version::id(), '9.0.0', '<')
505
        ) {
506
            $runner = new PHPUnitTestRunner8(
507
                $this->project,
508
                $this->groups,
509
                $this->excludeGroups,
510
                $this->processIsolation
511
            );
512
        } else {
513 4
            $runner = new PHPUnitTestRunner9(
514 4
                $this->project,
515 4
                $this->groups,
516 4
                $this->excludeGroups,
517 4
                $this->processIsolation
518
            );
519
        }
520
521 4
        if ($this->codecoverage) {
522
            /**
523
             * Add some defaults to the PHPUnit filter
524
             */
525
            $pwd = __DIR__;
526
            $path = realpath($pwd . '/../../../');
527
528
            if (class_exists('\SebastianBergmann\CodeCoverage\Filter')) {
529
                $filter = new \SebastianBergmann\CodeCoverage\Filter();
530
                if (method_exists($filter, 'addDirectoryToBlacklist')) {
531
                    $filter->addDirectoryToBlacklist($path);
532
                }
533
                if (class_exists('\SebastianBergmann\CodeCoverage\CodeCoverage')) {
534
                    if (null !== $this->codeCoverageConfig) {
535
                        // Update filters
536
                        foreach ($this->codeCoverageConfig->files()->asArray() as $file) {
537
                            $filter->includeFile($file->path());
538
                        }
539
                        foreach ($this->codeCoverageConfig->directories()->asArray() as $dir) {
540
                            $filter->includeDirectory($dir->path(), $dir->suffix(), $dir->prefix());
541
                        }
542
                        foreach ($this->codeCoverageConfig->excludeFiles()->asArray() as $file) {
543
                            $filter->excludeFile($file->path());
544
                        }
545
                        foreach ($this->codeCoverageConfig->excludeDirectories()->asArray() as $dir) {
546
                            $filter->excludeDirectory($dir->path(), $dir->suffix(), $dir->prefix());
547
                        }
548
                    }
549
550
                    if (null !== $this->codeCoverageConfig && $this->codeCoverageConfig->pathCoverage()) {
551
                        $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineAndPathCoverage($filter);
0 ignored issues
show
Unused Code introduced by
The assignment to $driver is dead and can be removed.
Loading history...
552
                    } else {
553
                        $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($filter);
554
                    }
555
556
                    $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector())->forLineCoverage($filter);
557
                    $codeCoverage = new \SebastianBergmann\CodeCoverage\CodeCoverage($driver, $filter);
558
559
                    if (null !== $this->codeCoverageConfig) {
560
                        // Set code coverage configuration
561
                        if ($this->codeCoverageConfig->hasCacheDirectory()) {
562
                            $codeCoverage->cacheStaticAnalysis($this->codeCoverageConfig->cacheDirectory()->path());
563
                        }
564
                        if ($this->codeCoverageConfig->ignoreDeprecatedCodeUnits()) {
565
                            $codeCoverage->ignoreDeprecatedCode();
566
                        } else {
567
                            $codeCoverage->doNotIgnoreDeprecatedCode();
568
                        }
569
                        if ($this->codeCoverageConfig->includeUncoveredFiles()) {
570
                            $codeCoverage->includeUncoveredFiles();
571
                        } else {
572
                            $codeCoverage->doNotProcessUncoveredFiles();
573
                        }
574
                    }
575
576
                    $runner->setCodecoverage($codeCoverage);
577
                }
578
            }
579
        }
580
581 4
        $runner->setUseCustomErrorHandler($this->usecustomerrorhandler);
582
583 4
        foreach ($this->listeners as $listener) {
584
            $runner->addListener($listener);
585
        }
586
587 4
        foreach ($this->formatters as $fe) {
588 2
            $formatter = $fe->getFormatter();
589
590 2
            if ($fe->getUseFile()) {
591
                try {
592
                    $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile());
593
                } catch (Exception $e) {
594
                    throw new BuildException('Unable to create destination.', $e);
595
                }
596
597
                $writer = new FileWriter($destFile->getAbsolutePath());
598
599
                $formatter->setOutput($writer);
600
            } else {
601 2
                $formatter->setOutput($this->getDefaultOutput());
602
            }
603
604 2
            $runner->addFormatter($formatter);
605
606 2
            $formatter->startTestRun();
607
        }
608
609 4
        $runner->run($suite);
610
611 4
        foreach ($this->formatters as $fe) {
612 2
            $formatter = $fe->getFormatter();
613 2
            $formatter->endTestRun();
614
        }
615
616 4
        if ($runner->hasErrors()) {
617 2
            if ($this->errorproperty) {
618
                $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

618
                $this->project->setNewProperty($this->errorproperty, /** @scrutinizer ignore-type */ true);
Loading history...
619
            }
620 2
            if ($this->haltonerror || $this->haltondefect) {
621
                $this->testfailed = true;
622
                $this->testfailuremessage = $runner->getLastErrorMessage();
623
            }
624
        }
625
626 4
        if ($runner->hasFailures()) {
627 3
            if ($this->failureproperty) {
628
                $this->project->setNewProperty($this->failureproperty, true);
629
            }
630
631 3
            if ($this->haltonfailure || $this->haltondefect) {
632 1
                $this->testfailed = true;
633 1
                $this->testfailuremessage = $runner->getLastFailureMessage();
634
            }
635
        }
636
637 4
        if ($runner->hasIncomplete()) {
638
            if ($this->incompleteproperty) {
639
                $this->project->setNewProperty($this->incompleteproperty, true);
640
            }
641
642
            if ($this->haltonincomplete) {
643
                $this->testfailed = true;
644
                $this->testfailuremessage = $runner->getLastIncompleteMessage();
645
            }
646
        }
647
648 4
        if ($runner->hasSkipped()) {
649
            if ($this->skippedproperty) {
650
                $this->project->setNewProperty($this->skippedproperty, true);
651
            }
652
653
            if ($this->haltonskipped) {
654
                $this->testfailed = true;
655
                $this->testfailuremessage = $runner->getLastSkippedMessage();
656
            }
657
        }
658
659 4
        if ($runner->hasWarnings()) {
660
            if ($this->warningproperty) {
661
                $this->project->setNewProperty($this->warningproperty, true);
662
            }
663
664
            if ($this->haltonwarning || $this->haltondefect) {
665
                $this->testfailed = true;
666
                $this->testfailuremessage = $runner->getLastWarningMessage();
667
            }
668
        }
669
670 4
        if ($runner->hasRisky()) {
671
            if ($this->riskyproperty) {
672
                $this->project->setNewProperty($this->riskyproperty, true);
673
            }
674
675
            if ($this->haltonrisky) {
676
                $this->testfailed = true;
677
                $this->testfailuremessage = $runner->getLastRiskyMessage();
678
            }
679
        }
680 4
    }
681
682
    /**
683
     * Add the tests in this batchtest to a test suite
684
     *
685
     * @param BatchTest $batchTest
686
     * @param PHPUnit\Framework\TestSuite $suite
687
     * @throws BuildException
688
     * @throws ReflectionException
689
     */
690 4
    protected function appendBatchTestToTestSuite(BatchTest $batchTest, $suite)
691
    {
692 4
        foreach ($batchTest->elements() as $element) {
693 4
            $testClass = new $element();
694 4
            if (!($testClass instanceof PHPUnit\Framework\TestSuite)) {
695 4
                $testClass = new ReflectionClass($element);
696
            }
697
            try {
698 4
                $suite->addTestSuite($testClass);
699
            } catch (\PHPUnit\Framework\Exception $e) {
700
                throw new BuildException('Unable to add TestSuite ' . get_class($testClass), $e);
701
            }
702
        }
703 4
    }
704
705
    /**
706
     * @return LogWriter
707
     */
708 2
    protected function getDefaultOutput()
709
    {
710 2
        return new LogWriter($this);
711
    }
712
713
    /**
714
     * Adds a set of tests based on pattern matching.
715
     *
716
     * @return BatchTest a new instance of a batch test.
717
     */
718 4
    public function createBatchTest()
719
    {
720 4
        $batchtest = new BatchTest($this->getProject());
721
722 4
        $this->batchtests[] = $batchtest;
723
724 4
        return $batchtest;
725
    }
726
}
727