Completed
Push — master ( d87474...50d944 )
by Siad
14:47
created

PHPUnitTestRunner7::setUseCustomErrorHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
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
 * Simple Testrunner for PHPUnit that runs all tests of a testsuite.
22
 *
23
 * @author  Michiel Rook <[email protected]>
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 4
    public function __construct(
61
        Project $project,
62
        $groups = [],
63
        $excludeGroups = [],
64
        $processIsolation = false
65
    ) {
66 4
        $this->project = $project;
67 4
        $this->groups = $groups;
68 4
        $this->excludeGroups = $excludeGroups;
69 4
        $this->processIsolation = $processIsolation;
70 4
    }
71
72
    /**
73
     * @param $codecoverage
74
     */
75
    public function setCodecoverage($codecoverage)
76
    {
77
        $this->codecoverage = $codecoverage;
78
    }
79
80
    /**
81
     * @param $useCustomErrorHandler
82
     */
83 4
    public function setUseCustomErrorHandler($useCustomErrorHandler)
84
    {
85 4
        $this->useCustomErrorHandler = $useCustomErrorHandler;
86 4
    }
87
88
    /**
89
     * @param $formatter
90
     */
91 2
    public function addFormatter($formatter)
92
    {
93 2
        $this->addListener($formatter);
94 2
        $this->formatters[] = $formatter;
95 2
    }
96
97
    /**
98
     * @param $level
99
     * @param $message
100
     * @param $file
101
     * @param $line
102
     */
103 99
    public function handleError($level, $message, $file, $line)
104
    {
105 99
        return PHPUnit\Util\ErrorHandler::handleError($level, $message, $file, $line);
106
    }
107
108 2
    public function addListener($listener)
109
    {
110 2
        $this->listeners[] = $listener;
111 2
    }
112
113
    /**
114
     * Run a test
115
     *
116
     * @param  PHPUnit\Framework\TestSuite $suite
117
     * @throws \BuildException
118
     */
119 4
    public function run(PHPUnit\Framework\TestSuite $suite)
120
    {
121 4
        $res = new PHPUnit\Framework\TestResult();
122
123 4
        if ($this->codecoverage) {
124
            $whitelist = CoverageMerger::getWhiteList($this->project);
125
126
            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);
127
128
            $res->setCodeCoverage($this->codecoverage);
129
        }
130
131 4
        $res->addListener($this);
132
133 4
        foreach ($this->formatters as $formatter) {
134 2
            $res->addListener($formatter);
135
        }
136
137
        /* Set PHPUnit error handler */
138 4
        if ($this->useCustomErrorHandler) {
139 4
            $oldErrorHandler = set_error_handler([$this, 'handleError'], E_ALL | E_STRICT);
0 ignored issues
show
Unused Code introduced by
The assignment to $oldErrorHandler is dead and can be removed.
Loading history...
140
        }
141
142 4
        $this->injectFilters($suite);
143 4
        $suite->run($res);
144
145 4
        foreach ($this->formatters as $formatter) {
146 2
            $formatter->processResult($res);
147
        }
148
149
        /* Restore Phing error handler */
150 4
        if ($this->useCustomErrorHandler) {
151 4
            restore_error_handler();
152
        }
153
154 4
        if ($this->codecoverage) {
155
            try {
156
                CoverageMerger::merge($this->project, $this->codecoverage->getData());
157
            } catch (IOException $e) {
158
                throw new BuildException('Merging code coverage failed.', $e);
159
            }
160
        }
161
162 4
        $this->checkResult($res);
163 4
    }
164
165
    /**
166
     * @param \PHPUnit\Framework\TestResult $res
167
     */
168 4
    private function checkResult(\PHPUnit\Framework\TestResult $res): void
169
    {
170 4
        if ($res->skippedCount() > 0) {
171
            $this->hasSkipped = true;
172
        }
173
174 4
        if ($res->notImplementedCount() > 0) {
175
            $this->hasIncomplete = true;
176
        }
177
178 4
        if ($res->warningCount() > 0) {
179
            $this->hasWarnings = true;
180
        }
181
182 4
        if ($res->failureCount() > 0) {
183 3
            $this->hasFailures = true;
184
        }
185
186 4
        if ($res->errorCount() > 0) {
187 2
            $this->hasErrors = true;
188
        }
189
190 4
        if ($res->riskyCount() > 0) {
191
            $this->hasRisky = true;
192
        }
193 4
    }
194
195
    /**
196
     * @return boolean
197
     */
198 4
    public function hasErrors()
199
    {
200 4
        return $this->hasErrors;
201
    }
202
203
    /**
204
     * @return boolean
205
     */
206 4
    public function hasFailures()
207
    {
208 4
        return $this->hasFailures;
209
    }
210
211
    /**
212
     * @return boolean
213
     */
214
    public function hasWarnings()
215
    {
216
        return $this->hasWarnings;
217
    }
218
219
    /**
220
     * @return boolean
221
     */
222 4
    public function hasIncomplete()
223
    {
224 4
        return $this->hasIncomplete;
225
    }
226
227
    /**
228
     * @return boolean
229
     */
230 4
    public function hasSkipped()
231
    {
232 4
        return $this->hasSkipped;
233
    }
234
235
    /**
236
     * @return boolean
237
     */
238
    public function hasRisky(): bool
239
    {
240
        return $this->hasRisky;
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getLastErrorMessage()
247
    {
248
        return $this->lastErrorMessage;
249
    }
250
251
    /**
252
     * @return string
253
     */
254 1
    public function getLastFailureMessage()
255
    {
256 1
        return $this->lastFailureMessage;
257
    }
258
259
    /**
260
     * @return string
261
     */
262
    public function getLastIncompleteMessage()
263
    {
264
        return $this->lastIncompleteMessage;
265
    }
266
267
    /**
268
     * @return string
269
     */
270
    public function getLastSkippedMessage()
271
    {
272
        return $this->lastSkippedMessage;
273
    }
274
275
    /**
276
     * @return string
277
     */
278
    public function getLastWarningMessage()
279
    {
280
        return $this->lastWarningMessage;
281
    }
282
283
    /**
284
     * @return string
285
     */
286
    public function getLastRiskyMessage()
287
    {
288
        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 3
    protected function composeMessage($message, PHPUnit\Framework\Test $test, \Throwable $e)
298
    {
299 3
        $name = ($test instanceof \PHPUnit\Framework\TestCase ? $test->getName() : '');
300 3
        $message = "Test {$message} ({$name} in class " . get_class($test) . ' ' . $e->getFile()
301 3
            . ' on line ' . $e->getLine() . '): ' . $e->getMessage();
302
303 3
        if ($e instanceof PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) {
304
            $message .= "\n" . $e->getComparisonFailure()->getDiff();
305
        }
306
307 3
        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 2
    public function addError(PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
318
    {
319 2
        $this->lastErrorMessage = $this->composeMessage("ERROR", $test, $e);
320 2
    }
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 3
    public function addFailure(
330
        PHPUnit\Framework\Test $test,
331
        PHPUnit\Framework\AssertionFailedError $e,
332
        float $time
333
    ): void {
334 3
        $this->lastFailureMessage = $this->composeMessage("FAILURE", $test, $e);
335 3
    }
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
    public function addWarning(PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time): void
345
    {
346
        $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
    public function addIncompleteTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
357
    {
358
        $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
    public function addSkippedTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
370
    {
371
        $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
    public function addRiskyTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
382
    {
383
        $this->lastRiskyMessage = $this->composeMessage('RISKY', $test, $e);
384
    }
385
386
    /**
387
     * A test started.
388
     *
389
     * @param string $testName
390
     */
391
    public function testStarted($testName)
0 ignored issues
show
Unused Code introduced by
The parameter $testName is not used and could be removed. ( Ignorable by Annotation )

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

391
    public function testStarted(/** @scrutinizer ignore-unused */ $testName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
392
    {
393
    }
394
395
    /**
396
     * A test ended.
397
     *
398
     * @param string $testName
399
     */
400
    public function testEnded($testName)
0 ignored issues
show
Unused Code introduced by
The parameter $testName is not used and could be removed. ( Ignorable by Annotation )

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

400
    public function testEnded(/** @scrutinizer ignore-unused */ $testName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
    public function testFailed($status, PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e)
0 ignored issues
show
Unused Code introduced by
The parameter $test is not used and could be removed. ( Ignorable by Annotation )

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

411
    public function testFailed($status, /** @scrutinizer ignore-unused */ PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $e is not used and could be removed. ( Ignorable by Annotation )

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

411
    public function testFailed($status, PHPUnit\Framework\Test $test, /** @scrutinizer ignore-unused */ PHPUnit\Framework\AssertionFailedError $e)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $status is not used and could be removed. ( Ignorable by Annotation )

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

411
    public function testFailed(/** @scrutinizer ignore-unused */ $status, PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
    protected function runFailed($message)
423
    {
424
        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 4
    public function startTestSuite(PHPUnit\Framework\TestSuite $suite): void
434
    {
435 4
    }
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 4
    public function endTestSuite(PHPUnit\Framework\TestSuite $suite): void
444
    {
445 4
    }
446
447
    /**
448
     * A test started.
449
     *
450
     * @param PHPUnit\Framework\Test $test
451
     */
452 4
    public function startTest(PHPUnit\Framework\Test $test): void
453
    {
454 4
    }
455
456
    /**
457
     * A test ended.
458
     *
459
     * @param PHPUnit\Framework\Test $test
460
     * @param float $time
461
     */
462 4
    public function endTest(PHPUnit\Framework\Test $test, float $time): void
463
    {
464 4
        if ($test instanceof PHPUnit\Framework\TestCase) {
465 4
            if (!$test->hasExpectationOnOutput()) {
466 4
                echo $test->getActualOutput();
467
            }
468
        }
469 4
    }
470
471
    /**
472
     * @param PHPUnit\Framework\TestSuite $suite
473
     */
474 4
    private function injectFilters(PHPUnit\Framework\TestSuite $suite)
475
    {
476 4
        $filterFactory = new PHPUnit\Runner\Filter\Factory();
477
478 4
        if (empty($this->excludeGroups) && empty($this->groups)) {
479 4
            return;
480
        }
481
482
        if (!empty($this->excludeGroups)) {
483
            $filterFactory->addFilter(
484
                new ReflectionClass(\PHPUnit\Runner\Filter\ExcludeGroupFilterIterator::class),
485
                $this->excludeGroups
486
            );
487
        }
488
489
        if (!empty($this->groups)) {
490
            $filterFactory->addFilter(
491
                new ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class),
492
                $this->groups
493
            );
494
        }
495
496
        $suite->injectFilter($filterFactory);
497
    }
498
}
499