Passed
Push — master ( dd99a9...c8547b )
by Michiel
10:09
created

PHPUnitTestRunner8::getLastErrorMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
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 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
    /**
48
     * @var \SebastianBergmann\CodeCoverage\CodeCoverage
49
     */
50
    private $codecoverage;
51
52
    /**
53
     * @var Project $project
54
     */
55
    private $project;
56
57
    private $groups = [];
58
    private $excludeGroups = [];
59
60
    private $processIsolation = false;
61
62
    private $useCustomErrorHandler = true;
63
64
    /**
65
     * @param Project $project
66
     * @param array $groups
67
     * @param array $excludeGroups
68
     * @param bool $processIsolation
69
     */
70 4
    public function __construct(
71
        Project $project,
72
        $groups = [],
73
        $excludeGroups = [],
74
        $processIsolation = false
75
    ) {
76 4
        $this->project = $project;
77 4
        $this->groups = $groups;
78 4
        $this->excludeGroups = $excludeGroups;
79 4
        $this->processIsolation = $processIsolation;
80 4
    }
81
82
    /**
83
     * @param $codecoverage
84
     */
85
    public function setCodecoverage(\SebastianBergmann\CodeCoverage\CodeCoverage $codecoverage): void
86
    {
87
        $this->codecoverage = $codecoverage;
88
    }
89
90
    /**
91
     * @param $useCustomErrorHandler
92
     */
93 4
    public function setUseCustomErrorHandler($useCustomErrorHandler): void
94
    {
95 4
        $this->useCustomErrorHandler = $useCustomErrorHandler;
96 4
    }
97
98
    /**
99
     * @param $formatter
100
     */
101 2
    public function addFormatter($formatter): void
102
    {
103 2
        $this->addListener($formatter);
104 2
        $this->formatters[] = $formatter;
105 2
    }
106
107 2
    public function addListener($listener): void
108
    {
109 2
        $this->listeners[] = $listener;
110 2
    }
111
112
    /**
113
     * @param $level
114
     * @param $message
115
     * @param $file
116
     * @param $line
117
     * @return bool
118
     */
119 98
    public function handleError($level, $message, $file, $line): bool
120
    {
121 98
        $invoke = new PHPUnit\Util\ErrorHandler(true, true, true, true);
122 98
        return $invoke($level, $message, $file, $line);
123
    }
124
125
    /**
126
     * Run a test
127
     *
128
     * @param  PHPUnit\Framework\TestSuite $suite
129
     * @throws \BuildException
130
     * @throws ReflectionException
131
     */
132 4
    public function run(PHPUnit\Framework\TestSuite $suite)
133
    {
134 4
        $res = new PHPUnit\Framework\TestResult();
135
136 4
        if ($this->codecoverage) {
137
            $whitelist = \Phing\Tasks\Ext\Coverage\CoverageMerger::getWhiteList($this->project);
138
            $filter = $this->codecoverage->filter();
139
140
            if (method_exists($filter, 'includeFiles')) {
141
                $filter->includeFiles($whitelist);
142
            } else if (method_exists($filter, 'addFilesToWhiteList')) {
143
                $filter->addFilesToWhiteList($whitelist);
144
            }
145
146
            $res->setCodeCoverage($this->codecoverage);
147
        }
148
149
        // $res->addListener($this);
150
151 4
        foreach ($this->formatters as $formatter) {
152 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

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

438
    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...
439
    {
440
    }
441
442
    /**
443
     * A test ended.
444
     *
445
     * @param string $testName
446
     */
447
    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

447
    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...
448
    {
449
    }
450
451
    /**
452
     * A test failed.
453
     *
454
     * @param integer $status
455
     * @param PHPUnit\Framework\Test $test
456
     * @param PHPUnit\Framework\AssertionFailedError $e
457
     */
458
    public function testFailed($status, PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e): void
0 ignored issues
show
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

458
    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...
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

458
    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 $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

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