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 <mrook@php.net>
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 $errorproperty;
41
    private $failureproperty;
42
    private $incompleteproperty;
43
    private $skippedproperty;
44
    private $printsummary = false;
45
    private $testfailed = false;
46
    private $testfailuremessage = "";
47
    private $codecoverage = null;
48
    private $groups = [];
49
    private $excludeGroups = [];
50
    private $processIsolation = false;
51
    private $usecustomerrorhandler = true;
52
    private $listeners = [];
53

54
    /**
55
     * @var string
56
     */
57
    private $pharLocation = "";
58

59
    /**
60
     * @var PhingFile
61
     */
62
    private $configuration = null;
63

64
    /**
65
     * Initialize Task.
66
     * This method includes any necessary PHPUnit libraries and triggers
67
     * appropriate error if they cannot be found.  This is not done in header
68
     * because we may want this class to be loaded w/o triggering an error.
69
     */
70 1
    public function init()
71
    {
72
    }
73

74 1
    private function loadPHPUnit()
75
    {
76
        /**
77
         * Determine PHPUnit version number, try
78
         * PEAR old-style, then composer, then PHAR
79
         */
80 1
        @include_once 'PHPUnit/Runner/Version.php';
81 1
        if (!class_exists('PHPUnit_Runner_Version')) {
82 1
            @include_once 'phpunit/Runner/Version.php';
83
        }
84 1
        if (!empty($this->pharLocation)) {
85 0
            $GLOBALS['_SERVER']['SCRIPT_NAME'] = '-';
86 0
            ob_start();
87 0
            @include $this->pharLocation;
88 0
            ob_end_clean();
89
        }
90

91 1
        @include_once 'PHPUnit/Autoload.php';
92 1
        if (!class_exists('PHPUnit\Runner\Version')) {
93 0
            throw new BuildException("PHPUnitTask requires PHPUnit to be installed", $this->getLocation());
94
        }
95
    }
96

97
    /**
98
     * Sets the name of a bootstrap file that is run before
99
     * executing the tests
100
     *
101
     * @param string $bootstrap the name of the bootstrap file
102
     */
103 1
    public function setBootstrap($bootstrap)
104
    {
105 1
        $this->bootstrap = $bootstrap;
106
    }
107

108
    /**
109
     * @param $value
110
     */
111 0
    public function setErrorproperty($value)
112
    {
113 0
        $this->errorproperty = $value;
114
    }
115

116
    /**
117
     * @param $value
118
     */
119 0
    public function setFailureproperty($value)
120
    {
121 0
        $this->failureproperty = $value;
122
    }
123

124
    /**
125
     * @param $value
126
     */
127 0
    public function setIncompleteproperty($value)
128
    {
129 0
        $this->incompleteproperty = $value;
130
    }
131

132
    /**
133
     * @param $value
134
     */
135 0
    public function setSkippedproperty($value)
136
    {
137 0
        $this->skippedproperty = $value;
138
    }
139

140
    /**
141
     * @param $value
142
     */
143 1
    public function setHaltonerror($value)
144
    {
145 1
        $this->haltonerror = $value;
146
    }
147

148
    /**
149
     * @param $value
150
     */
151 1
    public function setHaltonfailure($value)
152
    {
153 1
        $this->haltonfailure = $value;
154
    }
155

156
    /**
157
     * @return bool
158
     */
159 0
    public function getHaltonfailure()
160
    {
161 0
        return $this->haltonfailure;
162
    }
163

164
    /**
165
     * @param $value
166
     */
167 0
    public function setHaltonincomplete($value)
168
    {
169 0
        $this->haltonincomplete = $value;
170
    }
171

172
    /**
173
     * @return bool
174
     */
175 1
    public function getHaltonincomplete()
176
    {
177 1
        return $this->haltonincomplete;
178
    }
179

180
    /**
181
     * @param $value
182
     */
183 0
    public function setHaltonskipped($value)
184
    {
185 0
        $this->haltonskipped = $value;
186
    }
187

188
    /**
189
     * @return bool
190
     */
191 1
    public function getHaltonskipped()
192
    {
193 1
        return $this->haltonskipped;
194
    }
195

196
    /**
197
     * @param $printsummary
198
     */
199 1
    public function setPrintsummary($printsummary)
200
    {
201 1
        $this->printsummary = $printsummary;
202
    }
203

204
    /**
205
     * @param $codecoverage
206
     */
207 1
    public function setCodecoverage($codecoverage)
208
    {
209 1
        $this->codecoverage = $codecoverage;
210
    }
211

212
    /**
213
     * @param $processIsolation
214
     */
215 0
    public function setProcessIsolation($processIsolation)
216
    {
217 0
        $this->processIsolation = $processIsolation;
218
    }
219

220
    /**
221
     * @param $usecustomerrorhandler
222
     */
223 0
    public function setUseCustomErrorHandler($usecustomerrorhandler)
224
    {
225 0
        $this->usecustomerrorhandler = $usecustomerrorhandler;
226
    }
227

228
    /**
229
     * @param $groups
230
     */
231 0
    public function setGroups($groups)
232
    {
233 0
        $token = ' ,;';
234 0
        $this->groups = [];
235 0
        $tok = strtok($groups, $token);
236 0
        while ($tok !== false) {
237 0
            $this->groups[] = $tok;
238 0
            $tok = strtok($token);
239
        }
240
    }
241

242
    /**
243
     * @param $excludeGroups
244
     */
245 0
    public function setExcludeGroups($excludeGroups)
246
    {
247 0
        $token = ' ,;';
248 0
        $this->excludeGroups = [];
249 0
        $tok = strtok($excludeGroups, $token);
250 0
        while ($tok !== false) {
251 0
            $this->excludeGroups[] = $tok;
252 0
            $tok = strtok($token);
253
        }
254
    }
255

256
    /**
257
     * Add a new formatter to all tests of this task.
258
     *
259
     * @param FormatterElement $fe formatter element
260
     */
261 1
    public function addFormatter(FormatterElement $fe)
262
    {
263 1
        $fe->setParent($this);
264 1
        $this->formatters[] = $fe;
265
    }
266

267
    /**
268
     * Add a new listener to all tests of this taks
269
     *
270
     * @param $listener
271
     */
272 0
    private function addListener($listener)
273
    {
274 0
        $this->listeners[] = $listener;
275
    }
276

277
    /**
278
     * @param PhingFile $configuration
279
     */
280 0
    public function setConfiguration(PhingFile $configuration)
281
    {
282 0
        $this->configuration = $configuration;
283
    }
284

285
    /**
286
     * @param string $pharLocation
287
     */
288 0
    public function setPharLocation($pharLocation)
289
    {
290 0
        $this->pharLocation = $pharLocation;
291
    }
292

293
    /**
294
     * Load and processes the PHPUnit configuration
295
     *
296
     * @param  $configuration
297
     * @return array
298
     * @throws ReflectionException
299
     * @throws BuildException
300
     */
301 0
    protected function handlePHPUnitConfiguration(PhingFile $configuration)
302
    {
303 0
        if (!$configuration->exists()) {
304 0
            throw new BuildException("Unable to find PHPUnit configuration file '" . (string) $configuration . "'");
305
        }
306

307 0
        $config = \PHPUnit\TextUI\Configuration\Registry::getInstance()->get($configuration->getAbsolutePath());
308

309 0
        if (empty($config)) {
310 0
            return [];
311
        }
312

313 0
        $phpunit = $config->phpunit();
314

315 0
        if (empty($phpunit)) {
316 0
            return [];
317
        }
318

319 0
        if ($phpunit->hasBootstrap()) {
320 0
            $this->setBootstrap($phpunit->bootstrap());
321
        }
322 0
        $this->setHaltonfailure($phpunit->stopOnFailure());
323 0
        $this->setHaltonerror($phpunit->stopOnError());
324 0
        $this->setHaltonskipped($phpunit->stopOnSkipped());
325 0
        $this->setHaltonincomplete($phpunit->stopOnIncomplete());
326 0
        $this->setProcessIsolation($phpunit->processIsolation());
327

328 0
        foreach ($config->listeners() as $listener) {
329
            if (
330 0
                !class_exists($listener->className(), false)
331 0
                && $listener->hasSourceFile()
332
            ) {
333 0
                include_once $listener->sourceFile();
334
            }
335

336 0
            if (class_exists($listener->className())) {
337 0
                if ($listener->hasArguments()) {
338 0
                    $listener = (new $listener->className())();
339
                } else {
340 0
                    $listenerClass = new ReflectionClass(
341 0
                        $listener->className()
342
                    );
343 0
                    $listener = $listenerClass->newInstanceArgs(
344 0
                        $listener->arguments()
345
                    );
346
                }
347

348 0
                if ($listener instanceof \PHPUnit\Framework\TestListener) {
349 0
                    $this->addListener($listener);
350
                }
351
            }
352
        }
353

354
/*        if (method_exists($config, 'getSeleniumBrowserConfiguration')) {
355
            $browsers = $config->getSeleniumBrowserConfiguration();
356

357
            if (
358
                !empty($browsers)
359
                && class_exists('PHPUnit_Extensions_SeleniumTestCase')
360
            ) {
361
                PHPUnit_Extensions_SeleniumTestCase::$browsers = $browsers;
362
            }
363
        } */
364

365 0
        return $phpunit;
366
    }
367

368
    /**
369
     * The main entry point
370
     *
371
     * @throws BuildException
372
     */
373 1
    public function main()
374
    {
375 1
        if ($this->codecoverage && !extension_loaded('xdebug')) {
376 0
            throw new BuildException("PHPUnitTask depends on Xdebug being installed to gather code coverage information.");
377
        }
378

379 1
        $this->loadPHPUnit();
380 1
        $suite = new \PHPUnit\Framework\TestSuite('AllTests');
381 1
        $autoloadSave = spl_autoload_functions();
382

383 1
        if ($this->bootstrap) {
384 1
            include $this->bootstrap;
385
        }
386

387 1
        if ($this->configuration) {
388 0
            $phpunit = $this->handlePHPUnitConfiguration($this->configuration);
389

390 0
            if ($phpunit->backupGlobals() === false) {
391 0
                $suite->setBackupGlobals(false);
392
            }
393

394 0
            if ($phpunit->backupStaticAttributes() === true) {
395 0
                $suite->setBackupStaticAttributes(true);
396
            }
397
        }
398

399 1
        if ($this->printsummary) {
400 1
            $fe = new FormatterElement();
401 1
            $fe->setParent($this);
402 1
            $fe->setType("summary");
403 1
            $fe->setUseFile(false);
404 1
            $this->formatters[] = $fe;
405
        }
406

407 1
        foreach ($this->batchtests as $batchTest) {
408 1
            $this->appendBatchTestToTestSuite($batchTest, $suite);
409
        }
410

411 1
        $this->execute($suite);
412

413 1
        if ($this->testfailed) {
414 1
            throw new BuildException($this->testfailuremessage);
415
        }
416

417 1
        $autoloadNew = spl_autoload_functions();
418 1
        if (is_array($autoloadNew)) {
419 1
            foreach ($autoloadNew as $autoload) {
420 1
                spl_autoload_unregister($autoload);
421
            }
422
        }
423

424 1
        if (is_array($autoloadSave)) {
425 1
            foreach ($autoloadSave as $autoload) {
426 1
                spl_autoload_register($autoload);
427
            }
428
        }
429
    }
430

431
    /**
432
     * @param $suite
433
     * @throws BuildException
434
     * @throws ReflectionException
435
     */
436 1
    protected function execute($suite)
437
    {
438
        if (
439 1
            class_exists('\PHPUnit\Runner\Version', false) &&
440 1
            version_compare(\PHPUnit\Runner\Version::id(), '8.0.0', '<')
441
        ) {
442 0
            $runner = new PHPUnitTestRunner7(
443 0
                $this->project,
444 0
                $this->groups,
445 0
                $this->excludeGroups,
446 0
                $this->processIsolation
447
            );
448
        } else {
449 1
            $runner = new PHPUnitTestRunner8(
450 1
                $this->project,
451 1
                $this->groups,
452 1
                $this->excludeGroups,
453 1
                $this->processIsolation
454
            );
455
        }
456

457 1
        if ($this->codecoverage) {
458
            /**
459
             * Add some defaults to the PHPUnit filter
460
             */
461 0
            $pwd = __DIR__;
462 0
            $path = realpath($pwd . '/../../../');
463

464 0
            if (class_exists('\SebastianBergmann\CodeCoverage\Filter')) {
465 0
                $filter = new \SebastianBergmann\CodeCoverage\Filter();
466 0
                if (method_exists($filter, 'addDirectoryToBlacklist')) {
467 0
                    $filter->addDirectoryToBlacklist($path);
468
                }
469 0
                if (class_exists('\SebastianBergmann\CodeCoverage\CodeCoverage')) {
470 0
                    $codeCoverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(null, $filter);
471 0
                    $runner->setCodecoverage($codeCoverage);
472
                }
473
            }
474
        }
475

476 1
        $runner->setUseCustomErrorHandler($this->usecustomerrorhandler);
477

478 1
        foreach ($this->listeners as $listener) {
479 0
            $runner->addListener($listener);
480
        }
481

482 1
        foreach ($this->formatters as $fe) {
483 1
            $formatter = $fe->getFormatter();
484

485 1
            if ($fe->getUseFile()) {
486
                try {
487 0
                    $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile());
488 0
                } catch (Exception $e) {
489 0
                    throw new BuildException('Unable to create destination.', $e);
490
                }
491

492 0
                $writer = new FileWriter($destFile->getAbsolutePath());
493

494 0
                $formatter->setOutput($writer);
495
            } else {
496 1
                $formatter->setOutput($this->getDefaultOutput());
497
            }
498

499 1
            $runner->addFormatter($formatter);
500

501 1
            $formatter->startTestRun();
502
        }
503

504 1
        $runner->run($suite);
505

506 1
        foreach ($this->formatters as $fe) {
507 1
            $formatter = $fe->getFormatter();
508 1
            $formatter->endTestRun();
509
        }
510

511 1
        if ($runner->hasErrors()) {
512 1
            if ($this->errorproperty) {
513 0
                $this->project->setNewProperty($this->errorproperty, true);
514
            }
515 1
            if ($this->haltonerror) {
516 0
                $this->testfailed = true;
517 0
                $this->testfailuremessage = $runner->getLastErrorMessage();
518
            }
519
        }
520

521 1
        if ($runner->hasFailures()) {
522 1
            if ($this->failureproperty) {
523 0
                $this->project->setNewProperty($this->failureproperty, true);
524
            }
525

526 1
            if ($this->haltonfailure) {
527 1
                $this->testfailed = true;
528 1
                $this->testfailuremessage = $runner->getLastFailureMessage();
529
            }
530
        }
531

532 1
        if ($runner->hasIncomplete()) {
533 0
            if ($this->incompleteproperty) {
534 0
                $this->project->setNewProperty($this->incompleteproperty, true);
535
            }
536

537 0
            if ($this->haltonincomplete) {
538 0
                $this->testfailed = true;
539 0
                $this->testfailuremessage = $runner->getLastIncompleteMessage();
540
            }
541
        }
542

543 1
        if ($runner->hasSkipped()) {
544 0
            if ($this->skippedproperty) {
545 0
                $this->project->setNewProperty($this->skippedproperty, true);
546
            }
547

548 0
            if ($this->haltonskipped) {
549 0
                $this->testfailed = true;
550 0
                $this->testfailuremessage = $runner->getLastSkippedMessage();
551
            }
552
        }
553
    }
554

555
    /**
556
     * Add the tests in this batchtest to a test suite
557
     *
558
     * @param BatchTest $batchTest
559
     * @param PHPUnit\Framework\TestSuite $suite
560
     * @throws BuildException
561
     * @throws ReflectionException
562
     */
563 1
    protected function appendBatchTestToTestSuite(BatchTest $batchTest, $suite)
564
    {
565 1
        foreach ($batchTest->elements() as $element) {
566 1
            $testClass = new $element();
567 1
            if (!($testClass instanceof PHPUnit\Framework\TestSuite)) {
568 1
                $testClass = new ReflectionClass($element);
569
            }
570
            try {
571 1
                $suite->addTestSuite($testClass);
572 0
            } catch (\PHPUnit\Framework\Exception $e) {
573 0
                throw new BuildException('Unable to add TestSuite ' . get_class($testClass), $e);
574
            }
575
        }
576
    }
577

578
    /**
579
     * @return LogWriter
580
     */
581 1
    protected function getDefaultOutput()
582
    {
583 1
        return new LogWriter($this);
584
    }
585

586
    /**
587
     * Adds a set of tests based on pattern matching.
588
     *
589
     * @return BatchTest a new instance of a batch test.
590
     */
591 1
    public function createBatchTest()
592
    {
593 1
        $batchtest = new BatchTest($this->getProject());
594

595 1
        $this->batchtests[] = $batchtest;
596

597 1
        return $batchtest;
598
    }
599
}

Read our documentation on viewing source code .

Loading