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

PHPUnitTestRunner7   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 471
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 105
dl 0
loc 471
ccs 0
cts 219
cp 0
rs 4.5599
c 1
b 0
f 0
wmc 58

36 Methods

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

How to fix   Complexity   

Complex Class

Complex classes like PHPUnitTestRunner7 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 PHPUnitTestRunner7, 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 PHPUnitTestRunner7 implements \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 PHPUnitTestRunner7 implements /** @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
    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
    public function __construct(
61
        Project $project,
62
        $groups = [],
63
        $excludeGroups = [],
64
        $processIsolation = false
65
    ) {
66
        $this->project = $project;
67
        $this->groups = $groups;
68
        $this->excludeGroups = $excludeGroups;
69
        $this->processIsolation = $processIsolation;
70
    }
71
72
    /**
73
     * @param $codecoverage
74
     */
75
    public function setCodecoverage($codecoverage)
76
    {
77
        $this->codecoverage = $codecoverage;
78
    }
79
80
    /**
81
     * @param $useCustomErrorHandler
82
     */
83
    public function setUseCustomErrorHandler($useCustomErrorHandler)
84
    {
85
        $this->useCustomErrorHandler = $useCustomErrorHandler;
86
    }
87
88
    /**
89
     * @param $formatter
90
     */
91
    public function addFormatter($formatter)
92
    {
93
        $this->addListener($formatter);
94
        $this->formatters[] = $formatter;
95
    }
96
97
    /**
98
     * @param $level
99
     * @param $message
100
     * @param $file
101
     * @param $line
102
     */
103
    public function handleError($level, $message, $file, $line)
104
    {
105
        return PHPUnit\Util\ErrorHandler::handleError($level, $message, $file, $line);
0 ignored issues
show
Bug introduced by
The method handleError() does not exist on PHPUnit\Util\ErrorHandler. ( Ignorable by Annotation )

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

105
        return PHPUnit\Util\ErrorHandler::/** @scrutinizer ignore-call */ handleError($level, $message, $file, $line);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
106
    }
107
108
    public function addListener($listener)
109
    {
110
        $this->listeners[] = $listener;
111
    }
112
113
    /**
114
     * Run a test
115
     *
116
     * @param  PHPUnit\Framework\TestSuite $suite
117
     * @throws \BuildException
118
     */
119
    public function run(PHPUnit\Framework\TestSuite $suite)
120
    {
121
        $res = new PHPUnit\Framework\TestResult();
122
123
        if ($this->codecoverage) {
124
            $whitelist = \Phing\Tasks\Ext\Coverage\CoverageMerger::getWhiteList($this->project);
125
126
            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);
127
128
            $res->setCodeCoverage($this->codecoverage);
129
        }
130
131
        $res->addListener($this);
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

131
        /** @scrutinizer ignore-deprecated */ $res->addListener($this);

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...
132
133
        foreach ($this->formatters as $formatter) {
134
            $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

134
            /** @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...
135
        }
136
137
        /* Set PHPUnit error handler */
138
        if ($this->useCustomErrorHandler) {
139
            $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
        $this->injectFilters($suite);
143
        $suite->run($res);
144
145
        foreach ($this->formatters as $formatter) {
146
            $formatter->processResult($res);
147
        }
148
149
        /* Restore Phing error handler */
150
        if ($this->useCustomErrorHandler) {
151
            restore_error_handler();
152
        }
153
154
        if ($this->codecoverage) {
155
            try {
156
                \Phing\Tasks\Ext\Coverage\CoverageMerger::merge($this->project, $this->codecoverage->getData());
157
            } catch (IOException $e) {
158
                throw new BuildException('Merging code coverage failed.', $e);
159
            }
160
        }
161
162
        $this->checkResult($res);
163
    }
164
165
    /**
166
     * @param \PHPUnit\Framework\TestResult $res
167
     */
168
    private function checkResult(\PHPUnit\Framework\TestResult $res): void
169
    {
170
        if ($res->skippedCount() > 0) {
171
            $this->hasSkipped = true;
172
        }
173
174
        if ($res->notImplementedCount() > 0) {
175
            $this->hasIncomplete = true;
176
        }
177
178
        if ($res->warningCount() > 0) {
179
            $this->hasWarnings = true;
180
        }
181
182
        if ($res->failureCount() > 0) {
183
            $this->hasFailures = true;
184
        }
185
186
        if ($res->errorCount() > 0) {
187
            $this->hasErrors = true;
188
        }
189
190
        if ($res->riskyCount() > 0) {
191
            $this->hasRisky = true;
192
        }
193
    }
194
195
    /**
196
     * @return boolean
197
     */
198
    public function hasErrors()
199
    {
200
        return $this->hasErrors;
201
    }
202
203
    /**
204
     * @return boolean
205
     */
206
    public function hasFailures()
207
    {
208
        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
    public function hasIncomplete()
223
    {
224
        return $this->hasIncomplete;
225
    }
226
227
    /**
228
     * @return boolean
229
     */
230
    public function hasSkipped()
231
    {
232
        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
    public function getLastFailureMessage()
255
    {
256
        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
    protected function composeMessage($message, PHPUnit\Framework\Test $test, \Throwable $e)
298
    {
299
        $name = ($test instanceof \PHPUnit\Framework\TestCase ? $test->getName() : '');
300
        $message = "Test {$message} ({$name} in class " . get_class($test) . ' ' . $e->getFile()
301
            . ' on line ' . $e->getLine() . '): ' . $e->getMessage();
302
303
        if ($e instanceof PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) {
304
            $message .= "\n" . $e->getComparisonFailure()->getDiff();
305
        }
306
307
        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
    public function addError(PHPUnit\Framework\Test $test, \Throwable $e, float $time): void
318
    {
319
        $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
    public function addFailure(
330
        PHPUnit\Framework\Test $test,
331
        PHPUnit\Framework\AssertionFailedError $e,
332
        float $time
333
    ): void {
334
        $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
    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
    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
    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
    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
    public function endTest(PHPUnit\Framework\Test $test, float $time): void
463
    {
464
        if ($test instanceof PHPUnit\Framework\TestCase) {
465
            if (!$test->hasExpectationOnOutput()) {
466
                echo $test->getActualOutput();
467
            }
468
        }
469
    }
470
471
    /**
472
     * @param PHPUnit\Framework\TestSuite $suite
473
     */
474
    private function injectFilters(PHPUnit\Framework\TestSuite $suite)
475
    {
476
        $filterFactory = new PHPUnit\Runner\Filter\Factory();
477
478
        if (empty($this->excludeGroups) && empty($this->groups)) {
479
            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