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
 * Simple Testrunner for PHPUnit that runs all tests of a testsuite.
22
 *
23
 * @author  Michiel Rook <mrook@php.net>
24
 * @package phing.tasks.ext.phpunit
25
 */
26
class PHPUnitTestRunner7 implements \PHPUnit\Framework\TestListener
27
{
28
    private $hasErrors = false;
29
    private $hasFailures = false;
30
    private $hasWarnings = false;
31
    private $hasIncomplete = false;
32
    private $hasSkipped = false;
33
    private $hasRisky = false;
34
    private $lastErrorMessage = '';
35
    private $lastFailureMessage = '';
36
    private $lastWarningMessage = '';
37
    private $lastIncompleteMessage = '';
38
    private $lastSkippedMessage = '';
39
    private $lastRiskyMessage = '';
40
    private $formatters = [];
41
    private $listeners = [];
42

43
    private $codecoverage = null;
44

45
    private $project = null;
46

47
    private $groups = [];
48
    private $excludeGroups = [];
49

50
    private $processIsolation = false;
51

52
    private $useCustomErrorHandler = true;
53

54
    /**
55
     * @param Project $project
56
     * @param array $groups
57
     * @param array $excludeGroups
58
     * @param bool $processIsolation
59
     */
60 0
    public function __construct(
61 0
        Project $project,
62 0
        $groups = [],
63 0
        $excludeGroups = [],
64 0
        $processIsolation = false
65 0
    ) {
66 0
        $this->project = $project;
67 0
        $this->groups = $groups;
68 0
        $this->excludeGroups = $excludeGroups;
69 0
        $this->processIsolation = $processIsolation;
70
    }
71

72
    /**
73
     * @param $codecoverage
74
     */
75 0
    public function setCodecoverage($codecoverage)
76
    {
77 0
        $this->codecoverage = $codecoverage;
78
    }
79

80
    /**
81
     * @param $useCustomErrorHandler
82
     */
83 0
    public function setUseCustomErrorHandler($useCustomErrorHandler)
84
    {
85 0
        $this->useCustomErrorHandler = $useCustomErrorHandler;
86
    }
87

88
    /**
89
     * @param $formatter
90
     */
91 0
    public function addFormatter($formatter)
92
    {
93 0
        $this->addListener($formatter);
94 0
        $this->formatters[] = $formatter;
95
    }
96

97
    /**
98
     * @param $level
99
     * @param $message
100
     * @param $file
101
     * @param $line
102
     */
103 0
    public function handleError($level, $message, $file, $line)
104
    {
105 0
        return PHPUnit\Util\ErrorHandler::handleError($level, $message, $file, $line);
106
    }
107

108 0
    public function addListener($listener)
109
    {
110 0
        $this->listeners[] = $listener;
111
    }
112

113
    /**
114
     * Run a test
115
     *
116
     * @param  PHPUnit\Framework\TestSuite $suite
117
     * @throws \BuildException
118
     */
119 0
    public function run(PHPUnit\Framework\TestSuite $suite)
120
    {
121 0
        $res = new PHPUnit\Framework\TestResult();
122

123 0
        if ($this->codecoverage) {
124 0
            $whitelist = \Phing\Tasks\Ext\Coverage\CoverageMerger::getWhiteList($this->project);
125

126 0
            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);
127

128 0
            $res->setCodeCoverage($this->codecoverage);
129
        }
130

131 0
        $res->addListener($this);
132

133 0
        foreach ($this->formatters as $formatter) {
134 0
            $res->addListener($formatter);
135
        }
136

137
        /* Set PHPUnit error handler */
138 0
        if ($this->useCustomErrorHandler) {
139 0
            $oldErrorHandler = set_error_handler([$this, 'handleError'], E_ALL | E_STRICT);
140
        }
141

142 0
        $this->injectFilters($suite);
143 0
        $suite->run($res);
144

145 0
        foreach ($this->formatters as $formatter) {
146 0
            $formatter->processResult($res);
147
        }
148

149
        /* Restore Phing error handler */
150 0
        if ($this->useCustomErrorHandler) {
151 0
            restore_error_handler();
152
        }
153

154 0
        if ($this->codecoverage) {
155 0
            try {
156 0
                \Phing\Tasks\Ext\Coverage\CoverageMerger::merge($this->project, $this->codecoverage->getData());
157 0
            } catch (IOException $e) {
158 0
                throw new BuildException('Merging code coverage failed.', $e);
159
            }
160
        }
161

162 0
        $this->checkResult($res);
163
    }
164

165
    /**
166
     * @param \PHPUnit\Framework\TestResult $res
167
     */
168 0
    private function checkResult(\PHPUnit\Framework\TestResult $res): void
169
    {
170 0
        if ($res->skippedCount() > 0) {
171 0
            $this->hasSkipped = true;
172
        }
173

174 0
        if ($res->notImplementedCount() > 0) {
175 0
            $this->hasIncomplete = true;
176
        }
177

178 0
        if ($res->warningCount() > 0) {
179 0
            $this->hasWarnings = true;
180
        }
181

182 0
        if ($res->failureCount() > 0) {
183 0
            $this->hasFailures = true;
184
        }
185

186 0
        if ($res->errorCount() > 0) {
187 0
            $this->hasErrors = true;
188
        }
189

190 0
        if ($res->riskyCount() > 0) {
191 0
            $this->hasRisky = true;
192
        }
193
    }
194

195
    /**
196
     * @return boolean
197
     */
198 0
    public function hasErrors()
199
    {
200 0
        return $this->hasErrors;
201
    }
202

203
    /**
204
     * @return boolean
205
     */
206 0
    public function hasFailures()
207
    {
208 0
        return $this->hasFailures;
209
    }
210

211
    /**
212
     * @return boolean
213
     */
214 0
    public function hasWarnings()
215
    {
216 0
        return $this->hasWarnings;
217
    }
218

219
    /**
220
     * @return boolean
221
     */
222 0
    public function hasIncomplete()
223
    {
224 0
        return $this->hasIncomplete;
225
    }
226

227
    /**
228
     * @return boolean
229
     */
230 0
    public function hasSkipped()
231
    {
232 0
        return $this->hasSkipped;
233
    }
234

235
    /**
236
     * @return boolean
237
     */
238 0
    public function hasRisky(): bool
239
    {
240 0
        return $this->hasRisky;
241
    }
242

243
    /**
244
     * @return string
245
     */
246 0
    public function getLastErrorMessage()
247
    {
248 0
        return $this->lastErrorMessage;
249
    }
250

251
    /**
252
     * @return string
253
     */
254 0
    public function getLastFailureMessage()
255
    {
256 0
        return $this->lastFailureMessage;
257
    }
258

259
    /**
260
     * @return string
261
     */
262 0
    public function getLastIncompleteMessage()
263
    {
264 0
        return $this->lastIncompleteMessage;
265
    }
266

267
    /**
268
     * @return string
269
     */
270 0
    public function getLastSkippedMessage()
271
    {
272 0
        return $this->lastSkippedMessage;
273
    }
274

275
    /**
276
     * @return string
277
     */
278 0
    public function getLastWarningMessage()
279
    {
280 0
        return $this->lastWarningMessage;
281
    }
282

283
    /**
284
     * @return string
285
     */
286 0
    public function getLastRiskyMessage()
287
    {
288 0
        return $this->lastRiskyMessage;
289
    }
290

291
    /**
292
     * @param string $message
293
     * @param PHPUnit\Framework\Test $test
294
     * @param \Throwable $e
295
     * @return string
296
     */
297 0
    protected function composeMessage($message, PHPUnit\Framework\Test $test, \Throwable $e)
298
    {
299 0
        $name = ($test instanceof \PHPUnit\Framework\TestCase ? $test->getName() : '');
300 0
        $message = "Test {$message} ({$name} in class " . get_class($test) . ' ' . $e->getFile()
301 0
            . ' on line ' . $e->getLine() . '): ' . $e->getMessage();
302

303 0
        if ($e instanceof PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) {
304 0
            $message .= "\n" . $e->getComparisonFailure()->getDiff();
305
        }
306

307 0
        return $message;
308
    }
309

310
    /**
311
     * An error occurred.
312
     *
313
     * @param PHPUnit\Framework\Test $test
314
     * @param \Throwable $e
315
     * @param float $time
316
     */
317 0
    public function addError(PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
318
    {
319 0
        $this->lastErrorMessage = $this->composeMessage("ERROR", $test, $e);
320
    }
321

322
    /**
323
     * A failure occurred.
324
     *
325
     * @param PHPUnit\Framework\Test $test
326
     * @param PHPUnit\Framework\AssertionFailedError $e
327
     * @param float $time
328
     */
329 0
    public function addFailure(
330 0
        PHPUnit\Framework\Test $test,
331 0
        PHPUnit\Framework\AssertionFailedError $e,
332 0
        float $time
333 0
    ): void {
334 0
        $this->lastFailureMessage = $this->composeMessage("FAILURE", $test, $e);
335
    }
336

337
    /**
338
     * A failure occurred.
339
     *
340
     * @param PHPUnit\Framework\Test $test
341
     * @param PHPUnit\Framework\AssertionFailedError $e
342
     * @param float $time
343
     */
344 0
    public function addWarning(PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time): void
345
    {
346 0
        $this->lastWarningMessage = $this->composeMessage("WARNING", $test, $e);
347
    }
348

349
    /**
350
     * Incomplete test.
351
     *
352
     * @param PHPUnit\Framework\Test $test
353
     * @param Exception $e
354
     * @param float $time
355
     */
356 0
    public function addIncompleteTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
357
    {
358 0
        $this->lastIncompleteMessage = $this->composeMessage("INCOMPLETE", $test, $e);
359
    }
360

361
    /**
362
     * Skipped test.
363
     *
364
     * @param PHPUnit\Framework\Test $test
365
     * @param Exception $e
366
     * @param float $time
367
     * @since Method available since Release 3.0.0
368
     */
369 0
    public function addSkippedTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
370
    {
371 0
        $this->lastSkippedMessage = $this->composeMessage("SKIPPED", $test, $e);
372
    }
373

374
    /**
375
     * Risky test
376
     *
377
     * @param PHPUnit\Framework\Test $test
378
     * @param Exception $e
379
     * @param float $time
380
     */
381 0
    public function addRiskyTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
382
    {
383 0
        $this->lastRiskyMessage = $this->composeMessage('RISKY', $test, $e);
384
    }
385

386
    /**
387
     * A test started.
388
     *
389
     * @param string $testName
390
     */
391 0
    public function testStarted($testName)
392
    {
393
    }
394

395
    /**
396
     * A test ended.
397
     *
398
     * @param string $testName
399
     */
400 0
    public function testEnded($testName)
401
    {
402
    }
403

404
    /**
405
     * A test failed.
406
     *
407
     * @param integer $status
408
     * @param PHPUnit\Framework\Test $test
409
     * @param PHPUnit\Framework\AssertionFailedError $e
410
     */
411 0
    public function testFailed($status, PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e)
412
    {
413
    }
414

415
    /**
416
     * Override to define how to handle a failed loading of
417
     * a test suite.
418
     *
419
     * @param  string $message
420
     * @throws BuildException
421
     */
422 0
    protected function runFailed($message)
423
    {
424 0
        throw new BuildException($message);
425
    }
426

427
    /**
428
     * A test suite started.
429
     *
430
     * @param PHPUnit\Framework\TestSuite $suite
431
     * @since Method available since Release 2.2.0
432
     */
433 0
    public function startTestSuite(PHPUnit\Framework\TestSuite $suite): void
434
    {
435
    }
436

437
    /**
438
     * A test suite ended.
439
     *
440
     * @param PHPUnit\Framework\TestSuite $suite
441
     * @since Method available since Release 2.2.0
442
     */
443 0
    public function endTestSuite(PHPUnit\Framework\TestSuite $suite): void
444
    {
445
    }
446

447
    /**
448
     * A test started.
449
     *
450
     * @param PHPUnit\Framework\Test $test
451
     */
452 0
    public function startTest(PHPUnit\Framework\Test $test): void
453
    {
454
    }
455

456
    /**
457
     * A test ended.
458
     *
459
     * @param PHPUnit\Framework\Test $test
460
     * @param float $time
461
     */
462 0
    public function endTest(PHPUnit\Framework\Test $test, float $time): void
463
    {
464 0
        if ($test instanceof PHPUnit\Framework\TestCase) {
465 0
            if (!$test->hasExpectationOnOutput()) {
466 0
                echo $test->getActualOutput();
467
            }
468
        }
469
    }
470

471
    /**
472
     * @param PHPUnit\Framework\TestSuite $suite
473
     */
474 0
    private function injectFilters(PHPUnit\Framework\TestSuite $suite)
475
    {
476 0
        $filterFactory = new PHPUnit\Runner\Filter\Factory();
477

478 0
        if (empty($this->excludeGroups) && empty($this->groups)) {
479 0
            return;
480
        }
481

482 0
        if (!empty($this->excludeGroups)) {
483 0
            $filterFactory->addFilter(
484 0
                new ReflectionClass(\PHPUnit\Runner\Filter\ExcludeGroupFilterIterator::class),
485 0
                $this->excludeGroups
486
            );
487
        }
488

489 0
        if (!empty($this->groups)) {
490 0
            $filterFactory->addFilter(
491 0
                new ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class),
492 0
                $this->groups
493
            );
494
        }
495

496 0
        $suite->injectFilter($filterFactory);
497
    }
498
}

Read our documentation on viewing source code .

Loading