Passed
Push — master ( fedd27...f10ad5 )
by Michiel
11:13
created

PHPUnitTestRunner8   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 478
Duplicated Lines 0 %

Test Coverage

Coverage 42.45%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 104
dl 0
loc 478
ccs 59
cts 139
cp 0.4245
rs 4.5599
c 1
b 0
f 0
wmc 58

36 Methods

Rating   Name   Duplication   Size   Complexity  
A setCodecoverage() 0 3 1
A addError() 0 3 1
A __construct() 0 10 1
A addFormatter() 0 4 1
A testStarted() 0 2 1
A getLastFailureMessage() 0 3 1
A getLastErrorMessage() 0 3 1
A startTest() 0 2 1
A addRiskyTest() 0 3 1
A addListener() 0 3 1
A hasSkipped() 0 3 1
A hasIncomplete() 0 3 1
A hasRisky() 0 3 1
A composeMessage() 0 11 4
B checkResult() 0 24 7
A testEnded() 0 2 1
A testFailed() 0 2 1
A addFailure() 0 6 1
A injectFilters() 0 23 5
A setUseCustomErrorHandler() 0 3 1
A getLastSkippedMessage() 0 3 1
A addWarning() 0 3 1
A getLastRiskyMessage() 0 3 1
A getLastIncompleteMessage() 0 3 1
A hasWarnings() 0 3 1
A getLastWarningMessage() 0 3 1
A addIncompleteTest() 0 3 1
A runFailed() 0 3 1
A hasErrors() 0 3 1
B run() 0 44 8
A endTestSuite() 0 2 1
A addSkippedTest() 0 3 1
A handleError() 0 4 1
A startTestSuite() 0 2 1
A hasFailures() 0 3 1
A endTest() 0 4 3

How to fix   Complexity   

Complex Class

Complex classes like PHPUnitTestRunner8 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PHPUnitTestRunner8, and based on these observations, apply Extract Interface, too.

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 PHPUnitTestRunner8 implements \PHPUnit\Runner\TestHook, \PHPUnit\Framework\TestListener
0 ignored issues
show
Deprecated Code introduced by
The interface PHPUnit\Framework\TestListener has been deprecated: Use the `TestHook` interfaces instead ( Ignorable by Annotation )

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

26
class PHPUnitTestRunner8 implements \PHPUnit\Runner\TestHook, /** @scrutinizer ignore-deprecated */ \PHPUnit\Framework\TestListener

This interface has been deprecated. The supplier of the interface has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.

Loading history...
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
42
    /**
43
     * @var \PHPUnit\Runner\TestHook[]
44
     */
45
    private $listeners = [];
46
47
    private $codecoverage;
48
49
    /**
50
     * @var Project $project
51
     */
52
    private $project;
53
54
    private $groups = [];
55
    private $excludeGroups = [];
56
57
    private $processIsolation = false;
58
59
    private $useCustomErrorHandler = true;
60
61
    /**
62
     * @param Project $project
63
     * @param array $groups
64
     * @param array $excludeGroups
65
     * @param bool $processIsolation
66
     */
67 4
    public function __construct(
68
        Project $project,
69
        $groups = [],
70
        $excludeGroups = [],
71
        $processIsolation = false
72
    ) {
73 4
        $this->project = $project;
74 4
        $this->groups = $groups;
75 4
        $this->excludeGroups = $excludeGroups;
76 4
        $this->processIsolation = $processIsolation;
77 4
    }
78
79
    /**
80
     * @param $codecoverage
81
     */
82
    public function setCodecoverage($codecoverage): void
83
    {
84
        $this->codecoverage = $codecoverage;
85
    }
86
87
    /**
88
     * @param $useCustomErrorHandler
89
     */
90 4
    public function setUseCustomErrorHandler($useCustomErrorHandler): void
91
    {
92 4
        $this->useCustomErrorHandler = $useCustomErrorHandler;
93 4
    }
94
95
    /**
96
     * @param $formatter
97
     */
98 2
    public function addFormatter($formatter): void
99
    {
100 2
        $this->addListener($formatter);
101 2
        $this->formatters[] = $formatter;
102 2
    }
103
104 2
    public function addListener($listener): void
105
    {
106 2
        $this->listeners[] = $listener;
107 2
    }
108
109
    /**
110
     * @param $level
111
     * @param $message
112
     * @param $file
113
     * @param $line
114
     * @return bool
115
     */
116 99
    public function handleError($level, $message, $file, $line): bool
117
    {
118 99
        $invoke = new PHPUnit\Util\ErrorHandler(true, true, true, true);
119 99
        return $invoke($level, $message, $file, $line);
120
    }
121
122
    /**
123
     * Run a test
124
     *
125
     * @param  PHPUnit\Framework\TestSuite $suite
126
     * @throws \BuildException
127
     * @throws ReflectionException
128
     */
129 4
    public function run(PHPUnit\Framework\TestSuite $suite)
130
    {
131 4
        $res = new PHPUnit\Framework\TestResult();
132
133 4
        if ($this->codecoverage) {
134
            $whitelist = \Phing\Tasks\Ext\Coverage\CoverageMerger::getWhiteList($this->project);
135
136
            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);
137
138
            $res->setCodeCoverage($this->codecoverage);
139
        }
140
141
        // $res->addListener($this);
142
143 4
        foreach ($this->formatters as $formatter) {
144 2
            $res->addListener($formatter);
0 ignored issues
show
Deprecated Code introduced by
The function PHPUnit\Framework\TestResult::addListener() has been deprecated: Use the `TestHook` interfaces instead ( Ignorable by Annotation )

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

144
            /** @scrutinizer ignore-deprecated */ $res->addListener($formatter);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
145
        }
146
147
        /* Set PHPUnit error handler */
148 4
        if ($this->useCustomErrorHandler) {
149 4
            set_error_handler([$this, 'handleError'], E_ALL | E_STRICT);
150
        }
151
152 4
        $this->injectFilters($suite);
153 4
        $suite->run($res);
154
155 4
        foreach ($this->formatters as $formatter) {
156 2
            $formatter->processResult($res);
157
        }
158
159
        /* Restore Phing error handler */
160 4
        if ($this->useCustomErrorHandler) {
161 4
            restore_error_handler();
162
        }
163
164 4
        if ($this->codecoverage) {
165
            try {
166
                \Phing\Tasks\Ext\Coverage\CoverageMerger::merge($this->project, $this->codecoverage->getData());
167
            } catch (IOException $e) {
168
                throw new BuildException('Merging code coverage failed.', $e);
169
            }
170
        }
171
172 4
        $this->checkResult($res);
173 4
    }
174
175
    /**
176
     * @param PHPUnit\Framework\TestSuite $suite
177
     * @throws ReflectionException
178
     */
179 4
    private function injectFilters(PHPUnit\Framework\TestSuite $suite): void
180
    {
181 4
        $filterFactory = new PHPUnit\Runner\Filter\Factory();
182
183 4
        if (empty($this->excludeGroups) && empty($this->groups)) {
184 4
            return;
185
        }
186
187
        if (!empty($this->excludeGroups)) {
188
            $filterFactory->addFilter(
189
                new ReflectionClass(\PHPUnit\Runner\Filter\ExcludeGroupFilterIterator::class),
190
                $this->excludeGroups
191
            );
192
        }
193
194
        if (!empty($this->groups)) {
195
            $filterFactory->addFilter(
196
                new ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class),
197
                $this->groups
198
            );
199
        }
200
201
        $suite->injectFilter($filterFactory);
202
    }
203
204
    /**
205
     * @param \PHPUnit\Framework\TestResult $res
206
     */
207 4
    private function checkResult(\PHPUnit\Framework\TestResult $res): void
208
    {
209 4
        if ($res->skippedCount() > 0) {
210
            $this->hasSkipped = true;
211
        }
212
213 4
        if ($res->notImplementedCount() > 0) {
214
            $this->hasIncomplete = true;
215
        }
216
217 4
        if ($res->warningCount() > 0) {
218
            $this->hasWarnings = true;
219
        }
220
221 4
        if ($res->failureCount() > 0) {
222 3
            $this->hasFailures = true;
223
        }
224
225 4
        if ($res->errorCount() > 0) {
226 2
            $this->hasErrors = true;
227
        }
228
229 4
        if ($res->riskyCount() > 0) {
230
            $this->hasRisky = true;
231
        }
232 4
    }
233
234
    /**
235
     * @return boolean
236
     */
237 4
    public function hasErrors(): bool
238
    {
239 4
        return $this->hasErrors;
240
    }
241
242
    /**
243
     * @return boolean
244
     */
245 4
    public function hasFailures(): bool
246
    {
247 4
        return $this->hasFailures;
248
    }
249
250
    /**
251
     * @return boolean
252
     */
253
    public function hasWarnings(): bool
254
    {
255
        return $this->hasWarnings;
256
    }
257
258
    /**
259
     * @return boolean
260
     */
261 4
    public function hasIncomplete(): bool
262
    {
263 4
        return $this->hasIncomplete;
264
    }
265
266
    /**
267
     * @return boolean
268
     */
269 4
    public function hasSkipped(): bool
270
    {
271 4
        return $this->hasSkipped;
272
    }
273
274
    /**
275
     * @return boolean
276
     */
277
    public function hasRisky(): bool
278
    {
279
        return $this->hasRisky;
280
    }
281
282
    /**
283
     * @return string
284
     */
285
    public function getLastErrorMessage(): string
286
    {
287
        return $this->lastErrorMessage;
288
    }
289
290
    /**
291
     * @return string
292
     */
293 1
    public function getLastFailureMessage(): string
294
    {
295 1
        return $this->lastFailureMessage;
296
    }
297
298
    /**
299
     * @return string
300
     */
301
    public function getLastIncompleteMessage(): string
302
    {
303
        return $this->lastIncompleteMessage;
304
    }
305
306
    /**
307
     * @return string
308
     */
309
    public function getLastSkippedMessage(): string
310
    {
311
        return $this->lastSkippedMessage;
312
    }
313
314
    /**
315
     * @return string
316
     */
317
    public function getLastWarningMessage(): string
318
    {
319
        return $this->lastWarningMessage;
320
    }
321
322
    /**
323
     * @return string
324
     */
325
    public function getLastRiskyMessage(): string
326
    {
327
        return $this->lastRiskyMessage;
328
    }
329
330
    /**
331
     * An error occurred.
332
     *
333
     * @param PHPUnit\Framework\Test $test
334
     * @param Throwable $e
335
     * @param float $time
336
     */
337
    public function addError(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
338
    {
339
        $this->lastErrorMessage = $this->composeMessage('ERROR', $test, $e);
340
    }
341
342
    /**
343
     * @param string $message
344
     * @param PHPUnit\Framework\Test $test
345
     * @param Throwable $e
346
     * @return string
347
     */
348
    protected function composeMessage($message, PHPUnit\Framework\Test $test, Throwable $e)
349
    {
350
        $name = ($test instanceof \PHPUnit\Framework\TestCase ? $test->getName() : '');
351
        $message = "Test {$message} ({$name} in class " . get_class($test) . ' ' . $e->getFile()
352
            . ' on line ' . $e->getLine() . '): ' . $e->getMessage();
353
354
        if ($e instanceof PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) {
355
            $message .= "\n" . $e->getComparisonFailure()->getDiff();
356
        }
357
358
        return $message;
359
    }
360
361
    /**
362
     * A failure occurred.
363
     *
364
     * @param PHPUnit\Framework\Test $test
365
     * @param PHPUnit\Framework\AssertionFailedError $e
366
     * @param float $time
367
     */
368
    public function addFailure(
369
        PHPUnit\Framework\Test $test,
370
        PHPUnit\Framework\AssertionFailedError $e,
371
        float $time
372
    ): void {
373
        $this->lastFailureMessage = $this->composeMessage('FAILURE', $test, $e);
374
    }
375
376
    /**
377
     * A failure occurred.
378
     *
379
     * @param PHPUnit\Framework\Test $test
380
     * @param PHPUnit\Framework\AssertionFailedError $e
381
     * @param float $time
382
     */
383
    public function addWarning(PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time): void
384
    {
385
        $this->lastWarningMessage = $this->composeMessage("WARNING", $test, $e);
386
    }
387
388
    /**
389
     * Incomplete test.
390
     *
391
     * @param PHPUnit\Framework\Test $test
392
     * @param Exception $e
393
     * @param float $time
394
     */
395
    public function addIncompleteTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
396
    {
397
        $this->lastIncompleteMessage = $this->composeMessage("INCOMPLETE", $test, $e);
398
    }
399
400
    /**
401
     * Skipped test.
402
     *
403
     * @param PHPUnit\Framework\Test $test
404
     * @param Exception $e
405
     * @param float $time
406
     * @since Method available since Release 3.0.0
407
     */
408
    public function addSkippedTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
409
    {
410
        $this->lastSkippedMessage = $this->composeMessage('SKIPPED', $test, $e);
411
    }
412
413
    /**
414
     * Risky test
415
     *
416
     * @param PHPUnit\Framework\Test $test
417
     * @param Throwable $e
418
     * @param float $time
419
     */
420
    public function addRiskyTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
421
    {
422
        $this->lastRiskyMessage = $this->composeMessage('RISKY', $test, $e);
423
    }
424
425
    /**
426
     * A test started.
427
     *
428
     * @param string $testName
429
     */
430
    public function testStarted($testName): void
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

430
    public function testStarted(/** @scrutinizer ignore-unused */ $testName): void

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...
431
    {
432
    }
433
434
    /**
435
     * A test ended.
436
     *
437
     * @param string $testName
438
     */
439
    public function testEnded($testName): void
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

439
    public function testEnded(/** @scrutinizer ignore-unused */ $testName): void

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...
440
    {
441
    }
442
443
    /**
444
     * A test failed.
445
     *
446
     * @param integer $status
447
     * @param PHPUnit\Framework\Test $test
448
     * @param PHPUnit\Framework\AssertionFailedError $e
449
     */
450
    public function testFailed($status, PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e): void
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

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

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

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

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

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

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...
451
    {
452
    }
453
454
    /**
455
     * A test suite started.
456
     *
457
     * @param PHPUnit\Framework\TestSuite $suite
458
     */
459
    public function startTestSuite(PHPUnit\Framework\TestSuite $suite): void
460
    {
461
    }
462
463
    /**
464
     * A test suite ended.
465
     *
466
     * @param PHPUnit\Framework\TestSuite $suite
467
     */
468
    public function endTestSuite(PHPUnit\Framework\TestSuite $suite): void
469
    {
470
    }
471
472
    /**
473
     * A test started.
474
     *
475
     * @param PHPUnit\Framework\Test $test
476
     */
477
    public function startTest(PHPUnit\Framework\Test $test): void
478
    {
479
    }
480
481
    /**
482
     * A test ended.
483
     *
484
     * @param PHPUnit\Framework\Test $test
485
     * @param float $time
486
     */
487
    public function endTest(PHPUnit\Framework\Test $test, float $time): void
488
    {
489
        if (($test instanceof PHPUnit\Framework\TestCase) && !$test->hasExpectationOnOutput()) {
490
            echo $test->getActualOutput();
491
        }
492
    }
493
494
    /**
495
     * Override to define how to handle a failed loading of
496
     * a test suite.
497
     *
498
     * @param  string $message
499
     * @throws BuildException
500
     */
501
    protected function runFailed($message): void
502
    {
503
        throw new BuildException($message);
504
    }
505
}
506