GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

CoverFishOutput   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 379
Duplicated Lines 7.12 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 10
dl 27
loc 379
rs 8.3999
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A initOutputConfig() 0 18 3
A writeResultHeadlines() 0 20 3
C writeSingleTestResult() 0 40 8
B writeSingleMappingResult() 0 26 5
A writeResult() 0 17 3
A checkForEmptyUnitTestClass() 0 14 3
A writeFinalCheckResults() 0 10 2
A writeJsonFailureStream() 0 14 1
A writeJsonResult() 0 6 1
B getMacroLineInfo() 0 21 5
A getMacroFileInfo() 13 13 2
A getMacroCoverInfo() 14 14 2
A getMacroCoverErrorMessage() 0 17 3
B writeFailureStream() 0 34 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CoverFishOutput 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 CoverFishOutput, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace DF\PHPCoverFish\Common;
4
5
use DF\PHPCoverFish\CoverFishScanner;
6
use DF\PHPCoverFish\Common\Base\BaseCoverFishOutput;
7
use DF\PHPCoverFish\Common\CoverFishColor as Color;
8
use Symfony\Component\Console\Output\OutputInterface;
9
10
/**
11
 * Class CoverFishOutput
12
 *
13
 * @package    DF\PHPCoverFish
14
 * @author     Patrick Paechnatz <[email protected]>
15
 * @copyright  2015 Patrick Paechnatz <[email protected]>
16
 * @license    http://www.opensource.org/licenses/MIT
17
 * @link       http://github.com/dunkelfrosch/phpcoverfish/tree
18
 * @since      class available since Release 0.9.0
19
 * @version    1.0.0
20
 */
21
class CoverFishOutput extends BaseCoverFishOutput
22
{
23
    /**
24
     * @param array            $outputOptions
25
     * @param OutputInterface  $output
26
     * @param CoverFishScanner $scanner
27
     *
28
     * @codeCoverageIgnore
29
     */
30
    public function __construct(array $outputOptions, OutputInterface $output, CoverFishScanner $scanner)
31
    {
32
        $this->output = $output;
33
        $this->scanner = $scanner;
34
        $this->coverFishHelper = new CoverFishHelper();
35
36
        $this->initOutputConfig($outputOptions);
37
    }
38
39
    /**
40
     * @param array $outputOptions
41
     *
42
     * @codeCoverageIgnore
43
     */
44
    private function initOutputConfig(array $outputOptions)
45
    {
46
        $this->scanFailure = false;
47
        $this->outputFormat = $outputOptions['out_format'];
48
        $this->outputLevel = $outputOptions['out_level'];
49
        $this->preventAnsiColors = $outputOptions['out_no_ansi'];
50
        $this->preventEcho = $outputOptions['out_no_echo'];
51
52
        if ($this->outputFormat === 'json') {
53
            $this->outputFormatText = false;
54
            $this->outputFormatJson = true;
55
            $this->preventAnsiColors = true;
56
        }
57
58
        if ($this->outputLevel > 0) {
59
            $this->writeResultHeadlines();
60
        }
61
    }
62
63
    /**
64
     * @return bool
65
     */
66
    private function writeResultHeadlines()
67
    {
68
        if ($this->outputFormat === 'json') {
69
            return false;
70
        }
71
72
        $initLine = 'switch in raw scan mode, using commandline parameters:';
73
        if ($this->coverFishHelper->checkParamNotEmpty($this->scanner->getPhpUnitXMLFile())) {
74
            $initLine = sprintf('switch in phpunit-config scan mode, using phpunit-config file "%s"', $this->scanner->getPhpUnitXMLFile());
75
        }
76
77
        $this->output->writeln($initLine);
78
        $this->output->write(PHP_EOL);
79
        $this->output->writeln(sprintf('- autoload file: %s', $this->scanner->getTestAutoloadPath()));
80
        $this->output->writeln(sprintf('- test source path for scan: %s', $this->scanner->getTestSourcePath()));
81
        $this->output->writeln(sprintf('- exclude test source path: %s', $this->scanner->getTestExcludePath()));
82
        $this->output->write(PHP_EOL);
83
84
        return true;
85
    }
86
87
    /**
88
     * @param CoverFishPHPUnitFile $coverFishUnitFile
89
     * @param CoverFishResult      $coverFishResult
90
     */
91
    private function writeSingleTestResult(
92
        CoverFishPHPUnitFile $coverFishUnitFile,
93
        CoverFishResult $coverFishResult
94
    ) {
95
96
        $this->writeFileName($coverFishUnitFile);
97
        $this->writeJsonResult($coverFishUnitFile);
98
99
        $coverFishResult->addFileCount();
100
101
        /** @var CoverFishPHPUnitTest $coverFishTest */
102
        foreach ($coverFishUnitFile->getTests() as $coverFishTest) {
103
            // ignore output for className test result as function
104
            if ($coverFishUnitFile->getClassName() === $coverFishTest->getSignature()) {
105
                continue;
106
            }
107
108
            if (null === $coverFishTest->getVisibility()) {
109
                $coverFishTest->setVisibility('public');
110
            }
111
112
            // higher output level required? print out complete information!
113
            if ($this->outputLevel > 1) {
114
                $this->write(sprintf('%s-> %s %s : ',
115
                    PHP_EOL,
116
                    (false === $this->preventAnsiColors)
117
                        ? Color::tplDarkGrayColor($coverFishTest->getVisibility())
118
                        : $coverFishTest->getVisibility()
119
                    ,
120
                    $coverFishTest->getSignature()));
121
            }
122
123
            $this->writeSingleMappingResult($coverFishTest, $coverFishResult);
124
125
            // stop on failure defined? break ... do not check further unit tests!
126
            if ($this->scanFailure && $this->scanner->isStopOnFailure()) {
127
                break;
128
            }
129
        }
130
    }
131
132
    /**
133
     * output mapping/scanning result of each scanned file
134
     *
135
     * @param CoverFishPHPUnitTest $coverFishTest
136
     * @param CoverFishResult      $coverFishResult
137
     */
138
    private function writeSingleMappingResult(
139
        CoverFishPHPUnitTest $coverFishTest,
140
        CoverFishResult $coverFishResult
141
    ) {
142
        /** @var CoverFishMapping $coverMappings */
143
        foreach ($coverFishTest->getCoverMappings() as $coverMappings) {
144
            $coverFishResult->addTestCount();
145
            if (false === $coverMappings->getValidatorResult()->isPass()) {
146
                $this->scanFailure = true;
147
                $this->writeFailureStream($coverFishResult, $coverFishTest, $coverMappings);
148
                $this->writeProgress(self::MACRO_FAILURE);
149
                // stop on failure defined? break ... do not check further methods, exit!
150
                if ($this->scanner->isStopOnFailure()) {
151
                    break;
152
                }
153
154
            } else {
155
                $coverFishResult->addPassCount();
156
                $this->writeProgress(self::MACRO_PASS);
157
            }
158
        }
159
160
        if (count($coverFishTest->getCoverMappings()) === 0) {
161
            $this->writeProgress(self::MACRO_SKIPPED);
162
        }
163
    }
164
165
    /**
166
     * handle single file/unit test result (process/failureStream and final check result)
167
     *
168
     * @param CoverFishResult $coverFishResult
169
     *
170
     * @return null|string
171
     */
172
    public function writeResult(CoverFishResult $coverFishResult)
173
    {
174
        /** @var CoverFishPHPUnitFile $coverFishUnitFile */
175
        foreach ($coverFishResult->getUnits() as $coverFishUnitFile) {
176
            $this->scanFailure = false;
177
            $coverFishResult->setFailureStream(null);
178
179
            if (false === $this->checkForEmptyUnitTestClass($coverFishUnitFile)) {
180
                continue;
181
            }
182
183
            $this->writeSingleTestResult($coverFishUnitFile, $coverFishResult);
184
            $this->writeFinalCheckResults($coverFishResult);
185
        }
186
187
        return $this->outputResult($coverFishResult);
188
    }
189
190
    /**
191
     * @codeCoverageIgnore
192
     * 
193
     * @param CoverFishPHPUnitFile $coverFishUnitFile
194
     *
195
     * @return bool
196
     */
197
    public function checkForEmptyUnitTestClass(CoverFishPHPUnitFile $coverFishUnitFile)
198
    {
199
        $testCount = 0;
200
        foreach ($coverFishUnitFile->getTests() as $coverFishTest) {
201
            // ignore output for className test result as function
202
            if ($coverFishUnitFile->getClassName() === $coverFishTest->getSignature()) {
203
                continue;
204
            }
205
206
            $testCount++;
207
        }
208
209
        return $testCount > 0;
210
    }
211
212
    /**
213
     * write single file check result
214
     *
215
     * @param CoverFishResult $coverFishResult
216
     */
217
    private function writeFinalCheckResults(CoverFishResult $coverFishResult)
218
    {
219
        if (false === $this->scanFailure) {
220
            $this->writeFileResult(self::FILE_PASS, null);
221
        } else {
222
            $this->writeFileResult(self::FILE_FAILURE, $coverFishResult->getFailureStream());
223
        }
224
225
        $this->jsonResults[] = $this->jsonResult;
226
    }
227
228
    /**
229
     * write single json error line
230
     *
231
     * @param CoverFishResult       $coverFishResult
232
     * @param CoverFishPHPUnitTest  $unitTest
233
     * @param CoverFishMessageError $mappingError
234
     * @param $coverLine
235
     */
236
    private function writeJsonFailureStream(
237
        CoverFishResult $coverFishResult,
238
        CoverFishPHPUnitTest $unitTest,
239
        CoverFishMessageError $mappingError,
240
        $coverLine
241
    ) {
242
        $this->jsonResult['errorCount'] = $coverFishResult->getFailureCount();
243
        $this->jsonResult['errorMessage'] = $mappingError->getMessageTitle();
244
        $this->jsonResult['errorCode'] = $mappingError->getMessageCode();
245
        $this->jsonResult['cover'] = $coverLine;
246
        $this->jsonResult['method'] = $unitTest->getSignature();
247
        $this->jsonResult['line'] = $unitTest->getLine();
248
        $this->jsonResult['file'] = $unitTest->getFile();
249
    }
250
251
    /**
252
     * @param CoverFishPHPUnitFile $coverFishUnitFile
253
     */
254
    private function writeJsonResult(CoverFishPHPUnitFile $coverFishUnitFile)
255
    {
256
        $this->jsonResult['pass'] = false;
257
        $this->jsonResult['file'] = $this->coverFishHelper->getFileNameFromPath($coverFishUnitFile->getFile());
258
        $this->jsonResult['fileFQN'] = $coverFishUnitFile->getFile();
259
    }
260
261
    /**
262
     * message block macro, line 01, message title
263
     *
264
     * @param int                  $failureCount
265
     * @param CoverFishPHPUnitTest $unitTest
266
     *
267
     * @return string
268
     */
269
    protected function getMacroLineInfo($failureCount, CoverFishPHPUnitTest $unitTest)
270
    {
271
        $lineInfoMacro = '%sError #%s in method "%s" (L:~%s)';
272
        if ($this->outputLevel > 1) {
273
            $lineInfoMacro = '%sError #%s in method "%s", Line ~%s';
274
        }
275
276
        return sprintf($lineInfoMacro,
277
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
278
            (false === $this->preventAnsiColors)
279
                ? Color::tplWhiteColor($failureCount)
280
                : $failureCount,
281
            (false === $this->preventAnsiColors)
282
                ? Color::tplWhiteColor($unitTest->getSignature())
283
                : $unitTest->getSignature(),
284
            (false === $this->preventAnsiColors)
285
                ? Color::tplWhiteColor($unitTest->getLine())
286
                : $unitTest->getLine(),
287
            PHP_EOL
288
        );
289
    }
290
291
    /**
292
     * message block macro, line 02, cover/annotation line
293
     *
294
     * @param CoverFishPHPUnitTest $unitTest
295
     *
296
     * @return string
297
     */
298 View Code Duplication
    protected function getMacroFileInfo(CoverFishPHPUnitTest $unitTest)
299
    {
300
        $fileInfoMacro = '%s%s%s: %s';
301
        return sprintf($fileInfoMacro,
302
            PHP_EOL,
303
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
304
            (false === $this->preventAnsiColors)
305
                ? Color::tplDarkGrayColor('File')
306
                : 'File'
307
            ,
308
            $unitTest->getFileAndPath()
309
        );
310
    }
311
312
    /**
313
     * message block macro, line 03, cover/annotation line
314
     *
315
     * @param string $coverLine
316
     * @return string
317
     */
318 View Code Duplication
    protected function getMacroCoverInfo($coverLine)
319
    {
320
        $lineCoverMacro = '%s%s%s: %s%s';
321
        return sprintf($lineCoverMacro,
322
            PHP_EOL,
323
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
324
            (false === $this->preventAnsiColors)
325
                ? Color::tplDarkGrayColor('Annotation')
326
                : 'Annotation'
327
            ,
328
            $coverLine,
329
            PHP_EOL
330
        );
331
    }
332
333
    /**
334
     * message block macro, line 04, error message
335
     *
336
     * @param CoverFishMessageError $mappingError
337
     *
338
     * @return string
339
     */
340
    protected function getMacroCoverErrorMessage(CoverFishMessageError $mappingError)
341
    {
342
        $lineMessageMacro = '%s%s: %s ';
343
        if ($this->outputLevel > 1) {
344
            $lineMessageMacro = '%s%s: %s (ErrorCode: %s)';
345
        }
346
347
        return sprintf($lineMessageMacro,
348
            $this->setIndent(self::MACRO_CONFIG_DETAIL_LINE_INDENT),
349
            (false === $this->preventAnsiColors)
350
                ? Color::tplDarkGrayColor('Message')
351
                : 'Message',
352
353
            $mappingError->getMessageTitle(),
354
            $mappingError->getMessageCode()
355
        );
356
    }
357
358
    /**
359
     * @param CoverFishResult      $coverFishResult
360
     * @param CoverFishPHPUnitTest $unitTest
361
     * @param CoverFishMapping     $coverMapping
362
     *
363
     * @return null
364
     */
365
    private function writeFailureStream(
366
        CoverFishResult $coverFishResult,
367
        CoverFishPHPUnitTest $unitTest,
368
        CoverFishMapping $coverMapping
369
    )
370
    {
371
        /** @var CoverFishMessageError $mappingError */
372
        foreach ($coverMapping->getValidatorResult()->getErrors() as $mappingError) {
373
            $coverFishResult->addFailureCount();
374
            $coverLine = $mappingError->getErrorStreamTemplate($coverMapping, $this->preventAnsiColors);
375
            $this->writeJsonFailureStream($coverFishResult, $unitTest, $mappingError, $coverLine);
376
377
            if (0 === $this->outputLevel) {
378
                continue;
379
            }
380
381
            $coverFishResult->addFailureToStream(PHP_EOL);
382
383
            $lineInfo = $this->getMacroLineInfo($coverFishResult->getFailureCount(), $unitTest);
384
            $fileInfo = $this->getMacroFileInfo($unitTest);
385
            $lineCover = $this->getMacroCoverInfo($coverLine);
386
            $lineMessage = $this->getMacroCoverErrorMessage($mappingError);
387
388
            $coverFishResult->addFailureToStream($lineInfo);
389
390
            if ($this->outputLevel > 1) {
391
                $coverFishResult->addFailureToStream($fileInfo);
392
            }
393
394
            $coverFishResult->addFailureToStream($lineCover);
395
            $coverFishResult->addFailureToStream($lineMessage);
396
            $coverFishResult->addFailureToStream(PHP_EOL);
397
        }
398
    }
399
}