Passed
Push — master ( b4a548...043b6c )
by Michiel
06:00
created

PHPUnitTestRunner8   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 476
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 102
c 1
b 0
f 0
dl 0
loc 476
ccs 0
cts 215
cp 0
rs 4.5599
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 getLastFailureMessage() 0 3 1
A getLastErrorMessage() 0 3 1
A addListener() 0 3 1
A hasSkipped() 0 3 1
A hasIncomplete() 0 3 1
A hasRisky() 0 3 1
B checkResult() 0 24 7
A injectFilters() 0 23 5
A setUseCustomErrorHandler() 0 3 1
A getLastSkippedMessage() 0 3 1
A getLastRiskyMessage() 0 3 1
A getLastIncompleteMessage() 0 3 1
A hasWarnings() 0 3 1
A getLastWarningMessage() 0 3 1
A hasErrors() 0 3 1
B run() 0 44 8
A handleError() 0 3 1
A hasFailures() 0 3 1
A testStarted() 0 2 1
A startTest() 0 2 1
A addRiskyTest() 0 3 1
A composeMessage() 0 10 4
A testEnded() 0 2 1
A testFailed() 0 2 1
A addFailure() 0 6 1
A addWarning() 0 3 1
A addIncompleteTest() 0 3 1
A runFailed() 0 3 1
A endTestSuite() 0 2 1
A addSkippedTest() 0 3 1
A startTestSuite() 0 2 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
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
    public function __construct(
68
        Project $project,
69
        $groups = [],
70
        $excludeGroups = [],
71
        $processIsolation = false
72
    ) {
73
        $this->project = $project;
74
        $this->groups = $groups;
75
        $this->excludeGroups = $excludeGroups;
76
        $this->processIsolation = $processIsolation;
77
    }
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
    public function setUseCustomErrorHandler($useCustomErrorHandler): void
91
    {
92
        $this->useCustomErrorHandler = $useCustomErrorHandler;
93
    }
94
95
    /**
96
     * @param $formatter
97
     */
98
    public function addFormatter($formatter): void
99
    {
100
        $this->addListener($formatter);
101
        $this->formatters[] = $formatter;
102
    }
103
104
    public function addListener($listener): void
105
    {
106
        $this->listeners[] = $listener;
107
    }
108
109
    /**
110
     * @param $level
111
     * @param $message
112
     * @param $file
113
     * @param $line
114
     * @return bool
115
     */
116
    public function handleError($level, $message, $file, $line): bool
117
    {
118
        return PHPUnit\Util\ErrorHandler::handleError($level, $message, $file, $line);
119
    }
120
121
    /**
122
     * Run a test
123
     *
124
     * @param  PHPUnit\Framework\TestSuite $suite
125
     * @throws \BuildException
126
     * @throws ReflectionException
127
     */
128
    public function run(PHPUnit\Framework\TestSuite $suite)
129
    {
130
        $res = new PHPUnit\Framework\TestResult();
131
132
        if ($this->codecoverage) {
133
            $whitelist = CoverageMerger::getWhiteList($this->project);
134
135
            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);
136
137
            $res->setCodeCoverage($this->codecoverage);
138
        }
139
140
        // $res->addListener($this);
141
142
        foreach ($this->formatters as $formatter) {
143
            $res->addListener($formatter);
144
        }
145
146
        /* Set PHPUnit error handler */
147
        if ($this->useCustomErrorHandler) {
148
            set_error_handler([$this, 'handleError'], E_ALL | E_STRICT);
149
        }
150
151
        $this->injectFilters($suite);
152
        $suite->run($res);
153
154
        foreach ($this->formatters as $formatter) {
155
            $formatter->processResult($res);
156
        }
157
158
        /* Restore Phing error handler */
159
        if ($this->useCustomErrorHandler) {
160
            restore_error_handler();
161
        }
162
163
        if ($this->codecoverage) {
164
            try {
165
                CoverageMerger::merge($this->project, $this->codecoverage->getData());
166
            } catch (IOException $e) {
167
                throw new BuildException('Merging code coverage failed.', $e);
168
            }
169
        }
170
171
        $this->checkResult($res);
172
    }
173
174
    /**
175
     * @param PHPUnit\Framework\TestSuite $suite
176
     * @throws ReflectionException
177
     */
178
    private function injectFilters(PHPUnit\Framework\TestSuite $suite): void
179
    {
180
        $filterFactory = new PHPUnit\Runner\Filter\Factory();
181
182
        if (empty($this->excludeGroups) && empty($this->groups)) {
183
            return;
184
        }
185
186
        if (!empty($this->excludeGroups)) {
187
            $filterFactory->addFilter(
188
                new ReflectionClass(\PHPUnit\Runner\Filter\ExcludeGroupFilterIterator::class),
189
                $this->excludeGroups
190
            );
191
        }
192
193
        if (!empty($this->groups)) {
194
            $filterFactory->addFilter(
195
                new ReflectionClass(\PHPUnit\Runner\Filter\IncludeGroupFilterIterator::class),
196
                $this->groups
197
            );
198
        }
199
200
        $suite->injectFilter($filterFactory);
201
    }
202
203
    /**
204
     * @param \PHPUnit\Framework\TestResult $res
205
     */
206
    private function checkResult(\PHPUnit\Framework\TestResult $res): void
207
    {
208
        if ($res->skippedCount() > 0) {
209
            $this->hasSkipped = true;
210
        }
211
212
        if ($res->notImplementedCount() > 0) {
213
            $this->hasIncomplete = true;
214
        }
215
216
        if ($res->warningCount() > 0) {
217
            $this->hasWarnings = true;
218
        }
219
220
        if ($res->failureCount() > 0) {
221
            $this->hasFailures = true;
222
        }
223
224
        if ($res->errorCount() > 0) {
225
            $this->hasErrors = true;
226
        }
227
228
        if ($res->riskyCount() > 0) {
229
            $this->hasRisky = true;
230
        }
231
    }
232
233
    /**
234
     * @return boolean
235
     */
236
    public function hasErrors(): bool
237
    {
238
        return $this->hasErrors;
239
    }
240
241
    /**
242
     * @return boolean
243
     */
244
    public function hasFailures(): bool
245
    {
246
        return $this->hasFailures;
247
    }
248
249
    /**
250
     * @return boolean
251
     */
252
    public function hasWarnings(): bool
253
    {
254
        return $this->hasWarnings;
255
    }
256
257
    /**
258
     * @return boolean
259
     */
260
    public function hasIncomplete(): bool
261
    {
262
        return $this->hasIncomplete;
263
    }
264
265
    /**
266
     * @return boolean
267
     */
268
    public function hasSkipped(): bool
269
    {
270
        return $this->hasSkipped;
271
    }
272
273
    /**
274
     * @return boolean
275
     */
276
    public function hasRisky(): bool
277
    {
278
        return $this->hasRisky;
279
    }
280
281
    /**
282
     * @return string
283
     */
284
    public function getLastErrorMessage(): string
285
    {
286
        return $this->lastErrorMessage;
287
    }
288
289
    /**
290
     * @return string
291
     */
292
    public function getLastFailureMessage(): string
293
    {
294
        return $this->lastFailureMessage;
295
    }
296
297
    /**
298
     * @return string
299
     */
300
    public function getLastIncompleteMessage(): string
301
    {
302
        return $this->lastIncompleteMessage;
303
    }
304
305
    /**
306
     * @return string
307
     */
308
    public function getLastSkippedMessage(): string
309
    {
310
        return $this->lastSkippedMessage;
311
    }
312
313
    /**
314
     * @return string
315
     */
316
    public function getLastWarningMessage(): string
317
    {
318
        return $this->lastWarningMessage;
319
    }
320
321
    /**
322
     * @return string
323
     */
324
    public function getLastRiskyMessage(): string
325
    {
326
        return $this->lastRiskyMessage;
327
    }
328
329
    /**
330
     * An error occurred.
331
     *
332
     * @param PHPUnit\Framework\Test $test
333
     * @param Throwable $e
334
     * @param float $time
335
     */
336
    public function addError(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
337
    {
338
        $this->lastErrorMessage = $this->composeMessage('ERROR', $test, $e);
339
    }
340
341
    /**
342
     * @param string $message
343
     * @param PHPUnit\Framework\Test $test
344
     * @param Throwable $e
345
     * @return string
346
     */
347
    protected function composeMessage($message, PHPUnit\Framework\Test $test, Throwable $e)
348
    {
349
        $name = ($test instanceof \PHPUnit\Framework\TestCase ? $test->getName() : '');
350
        $message = "Test $message (" . $name . ' in class ' . get_class($test) . '): ' . $e->getMessage();
351
352
        if ($e instanceof PHPUnit\Framework\ExpectationFailedException && $e->getComparisonFailure()) {
353
            $message .= "\n" . $e->getComparisonFailure()->getDiff();
354
        }
355
356
        return $message;
357
    }
358
359
    /**
360
     * A failure occurred.
361
     *
362
     * @param PHPUnit\Framework\Test $test
363
     * @param PHPUnit\Framework\AssertionFailedError $e
364
     * @param float $time
365
     */
366
    public function addFailure(
367
        PHPUnit\Framework\Test $test,
368
        PHPUnit\Framework\AssertionFailedError $e,
369
        float $time
370
    ): void {
371
        $this->lastFailureMessage = $this->composeMessage('FAILURE', $test, $e);
372
    }
373
374
    /**
375
     * A failure occurred.
376
     *
377
     * @param PHPUnit\Framework\Test $test
378
     * @param PHPUnit\Framework\AssertionFailedError $e
379
     * @param float $time
380
     */
381
    public function addWarning(PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time): void
382
    {
383
        $this->lastWarningMessage = $this->composeMessage("WARNING", $test, $e);
384
    }
385
386
    /**
387
     * Incomplete test.
388
     *
389
     * @param PHPUnit\Framework\Test $test
390
     * @param Exception $e
391
     * @param float $time
392
     */
393
    public function addIncompleteTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
394
    {
395
        $this->lastIncompleteMessage = $this->composeMessage("INCOMPLETE", $test, $e);
396
    }
397
398
    /**
399
     * Skipped test.
400
     *
401
     * @param PHPUnit\Framework\Test $test
402
     * @param Exception $e
403
     * @param float $time
404
     * @since Method available since Release 3.0.0
405
     */
406
    public function addSkippedTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
407
    {
408
        $this->lastSkippedMessage = $this->composeMessage('SKIPPED', $test, $e);
409
    }
410
411
    /**
412
     * Risky test
413
     *
414
     * @param PHPUnit\Framework\Test $test
415
     * @param Throwable $e
416
     * @param float $time
417
     */
418
    public function addRiskyTest(PHPUnit\Framework\Test $test, Throwable $e, float $time): void
419
    {
420
        $this->lastRiskyMessage = $this->composeMessage('RISKY', $test, $e);
421
    }
422
423
    /**
424
     * A test started.
425
     *
426
     * @param string $testName
427
     */
428
    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

428
    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...
429
    {
430
    }
431
432
    /**
433
     * A test ended.
434
     *
435
     * @param string $testName
436
     */
437
    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

437
    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...
438
    {
439
    }
440
441
    /**
442
     * A test failed.
443
     *
444
     * @param integer $status
445
     * @param PHPUnit\Framework\Test $test
446
     * @param PHPUnit\Framework\AssertionFailedError $e
447
     */
448
    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

448
    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

448
    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

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