LogPrinter   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 304
Duplicated Lines 40.13 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 94.74%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 5
dl 122
loc 304
ccs 108
cts 114
cp 0.9474
rs 9.92
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getLogDirectory() 14 14 3
A __construct() 0 9 2
A addError() 0 12 1
A addWarning() 0 12 1
A addFailure() 0 12 1
A addIncompleteTest() 12 12 1
A addRiskyTest() 12 12 1
A addSkippedTest() 12 12 1
A startTestSuite() 11 11 1
A endTestSuite() 0 5 1
A startTest() 11 11 2
A endTest() 0 6 2
A writeCase() 18 18 2
A writeArray() 10 10 2
A writeToLog() 8 8 3
A getLogFilename() 14 14 4
A convertToUtf8() 0 8 2
A getStackTrace() 0 4 1

How to fix   Duplicated Code   

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:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Paraunit\Parser\JSON;
6
7
use Paraunit\Configuration\EnvVariables;
8
use PHPUnit\Framework\AssertionFailedError;
9
use PHPUnit\Framework\SelfDescribing;
10
use PHPUnit\Framework\Test;
11
use PHPUnit\Framework\TestCase;
12
use PHPUnit\Framework\TestFailure;
13
use PHPUnit\Framework\TestListener;
14
use PHPUnit\Framework\TestSuite;
15
use PHPUnit\Framework\Warning;
16
use PHPUnit\Runner\Version;
17
use PHPUnit\Util;
18
19 1
if (version_compare(Version::id(), '7.0.0', '<')) {
20
    class_alias('Paraunit\Parser\JSON\LogPrinterV6', 'Paraunit\Parser\JSON\LogPrinter');
21
// Using an early return instead of a else does not work when using the PHPUnit phar due to some weird PHP behavior
22
// (the class gets defined without executing the code before it and so the definition is not properly conditional)
23
} else {
24
    /**
25
     * This class comes from Util\Log_JSON.
26
     * It's copied and refactored here because it's deprecated in PHPUnit 5.7 and it will be dropped in PHPUnit 6
27
     *
28
     * Class LogPrinter
29
     * @package Paraunit\Parser\JSON
30
     */
31
    class LogPrinter extends Util\Printer implements TestListener
32
    {
33
        const STATUS_ERROR = 'error';
34
        const STATUS_WARNING = 'warning';
35
        const STATUS_FAIL = 'fail';
36
        const STATUS_PASS = 'pass';
37
38
        const MESSAGE_INCOMPLETE_TEST = 'Incomplete Test: ';
39
        const MESSAGE_RISKY_TEST = 'Risky Test: ';
40
        const MESSAGE_SKIPPED_TEST = 'Skipped Test: ';
41
42
        /** @var resource */
43
        private $logFile;
44
45
        /** @var string */
46
        private $currentTestSuiteName;
47
48
        /** @var string */
49
        private $currentTestName;
50
51
        /** @var bool */
52
        private $currentTestPass;
53
54 9
        public function __construct()
55
        {
56 9
            $file = fopen($this->getLogFilename(), 'wt');
57 9
            if (! \is_resource($file)) {
58
                throw new \RuntimeException('Unable to create log file');
59
            }
60
            $this->logFile = $file;
61
            $this->autoFlush = true;
62
        }
63
64
        /**
65
         * An error occurred.
66
         *
67 1
         * @param Test $test
68
         * @param \Throwable $exception
69 1
         * @param float $time
70 1
         */
71 1
        public function addError(Test $test, \Throwable $exception, float $time): void
72 1
        {
73 1
            $this->writeCase(
74 1
                self::STATUS_ERROR,
75
                $time,
76
                $this->getStackTrace($exception),
77 1
                TestFailure::exceptionToString($exception),
78
                $test
79
            );
80
81
            $this->currentTestPass = false;
82
        }
83
84
        /**
85
         * A warning occurred.
86
         *
87 1
         * @param Test $test
88
         * @param Warning $warning
89 1
         * @param float $time
90 1
         */
91 1
        public function addWarning(Test $test, Warning $warning, float $time): void
92 1
        {
93 1
            $this->writeCase(
94 1
                self::STATUS_WARNING,
95
                $time,
96
                $this->getStackTrace($warning),
97 1
                TestFailure::exceptionToString($warning),
98
                $test
99
            );
100
101
            $this->currentTestPass = false;
102
        }
103
104
        /**
105
         * A failure occurred.
106
         *
107 1
         * @param Test $test
108
         * @param AssertionFailedError $error
109 1
         * @param float $time
110 1
         */
111 1
        public function addFailure(Test $test, AssertionFailedError $error, float $time): void
112 1
        {
113 1
            $this->writeCase(
114 1
                self::STATUS_FAIL,
115
                $time,
116
                $this->getStackTrace($error),
117 1
                TestFailure::exceptionToString($error),
118
                $test
119
            );
120
121
            $this->currentTestPass = false;
122
        }
123
124
        /**
125
         * Incomplete test.
126
         *
127 1
         * @param Test $test
128
         * @param \Throwable $error
129 1
         * @param float $time
130 1
         */
131 1 View Code Duplication
        public function addIncompleteTest(Test $test, \Throwable $error, float $time): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132 1
        {
133 1
            $this->writeCase(
134 1
                self::STATUS_ERROR,
135
                $time,
136
                $this->getStackTrace($error),
137 1
                self::MESSAGE_INCOMPLETE_TEST . $error->getMessage(),
138
                $test
139
            );
140
141
            $this->currentTestPass = false;
142
        }
143
144
        /**
145
         * Risky test.
146
         *
147 1
         * @param Test $test
148
         * @param \Throwable $exception
149 1
         * @param float $time
150 1
         */
151 1 View Code Duplication
        public function addRiskyTest(Test $test, \Throwable $exception, float $time): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152 1
        {
153 1
            $this->writeCase(
154 1
                self::STATUS_ERROR,
155
                $time,
156
                $this->getStackTrace($exception),
157 1
                self::MESSAGE_RISKY_TEST . $exception->getMessage(),
158
                $test
159
            );
160
161
            $this->currentTestPass = false;
162
        }
163
164
        /**
165
         * Skipped test.
166
         *
167 1
         * @param Test $test
168
         * @param \Throwable $exception
169 1
         * @param float $time
170 1
         */
171 1 View Code Duplication
        public function addSkippedTest(Test $test, \Throwable $exception, float $time): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172 1
        {
173 1
            $this->writeCase(
174 1
                self::STATUS_ERROR,
175
                $time,
176
                $this->getStackTrace($exception),
177 1
                self::MESSAGE_SKIPPED_TEST . $exception->getMessage(),
178
                $test
179
            );
180
181
            $this->currentTestPass = false;
182
        }
183
184
        /**
185
         * A testsuite started.
186 9
         *
187
         * @param TestSuite $suite
188 9
         * @throws \RuntimeException
189 9
         */
190 View Code Duplication
        public function startTestSuite(TestSuite $suite): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191 9
        {
192 9
            $this->currentTestSuiteName = $suite->getName();
193 9
            $this->currentTestName = '';
194 9
195
            $this->writeArray([
196
                'event' => 'suiteStart',
197
                'suite' => $this->currentTestSuiteName,
198 1
                'tests' => count($suite),
199
            ]);
200 1
        }
201 1
202
        public function endTestSuite(TestSuite $suite): void
203
        {
204 7
            $this->currentTestSuiteName = '';
205
            $this->currentTestName = '';
206 7
        }
207 7
208 View Code Duplication
        public function startTest(Test $test): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209 7
        {
210 7
            $this->currentTestName = $test instanceof SelfDescribing ? $test->toString() : \get_class($test);
211 7
            $this->currentTestPass = true;
212 7
213
            $this->writeArray([
214
                'event' => 'testStart',
215
                'suite' => $this->currentTestSuiteName,
216
                'test' => $this->currentTestName,
217
            ]);
218
        }
219
220
        /**
221
         * A test ended.
222 1
         *
223
         * @param Test $test
224 1
         * @param float $time
225 1
         */
226
        public function endTest(Test $test, float $time): void
227
        {
228
            if ($this->currentTestPass) {
229
                $this->writeCase(self::STATUS_PASS, $time, '', '', $test);
230
            }
231
        }
232
233
        /**
234
         * @param string $status
235
         * @param float $time
236 7
         * @param string $trace
237
         * @param string $message
238 7
         * @param Test|TestCase|null $test
239 7
         */
240 View Code Duplication
        private function writeCase(string $status, float $time, string $trace, $message = '', $test = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
        {
242
            $output = '';
243 7
            if ($test instanceof TestCase) {
244 7
                $output = $test->getActualOutput();
245 7
            }
246 7
247 7
            $this->writeArray([
248 7
                'event' => 'test',
249 7
                'suite' => $this->currentTestSuiteName,
250 7
                'test' => $this->currentTestName,
251 7
                'status' => $status,
252
                'time' => $time,
253
                'trace' => $trace,
254
                'message' => $this->convertToUtf8($message),
255
                'output' => $output,
256
            ]);
257
        }
258 9
259
        /**
260
         * @param array $buffer
261 9
         */
262 9 View Code Duplication
        private function writeArray($buffer)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
        {
264 9
            array_walk_recursive($buffer, function (&$input) {
265
                if (is_string($input)) {
266 9
                    $input = $this->convertToUtf8($input);
267
                }
268
            });
269 9
270
            $this->writeToLog(json_encode($buffer, JSON_PRETTY_PRINT));
271
        }
272 9
273 9 View Code Duplication
        private function writeToLog($buffer)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
274 9
        {
275
            // ignore everything that is not a JSON object
276
            if ($buffer != '' && $buffer[0] === '{') {
277
                \fwrite($this->logFile, $buffer);
278
                \fflush($this->logFile);
279
            }
280
        }
281
282
        /**
283 9
         * @return string
284
         * @throws \RuntimeException
285 9
         * @throws \InvalidArgumentException
286 9
         */
287 View Code Duplication
        private function getLogFilename(): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
        {
289
            $logDir = $this->getLogDirectory();
290 9
            if (! @mkdir($logDir, 0777, true) && ! is_dir($logDir)) {
291 9
                throw new \RuntimeException('Cannot create folder for JSON logs');
292
            }
293
294
            $logFilename = getenv(EnvVariables::PROCESS_UNIQUE_ID);
295 9
            if ($logFilename === false) {
296
                throw new \InvalidArgumentException('Log filename not received: environment variable not set');
297
            }
298
299
            return $logDir . $logFilename . '.json.log';
300
        }
301
302 9
        /**
303
         * @return string
304 9
         * @throws \InvalidArgumentException
305
         */
306 9 View Code Duplication
        private function getLogDirectory(): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307
        {
308
            $logDirectory = getenv(EnvVariables::LOG_DIR);
309
310 9
            if ($logDirectory === false) {
311
                throw new \InvalidArgumentException('Log directory not received: environment variable not set');
312
            }
313
314 9
            if (substr($logDirectory, -1) !== DIRECTORY_SEPARATOR) {
315
                $logDirectory .= DIRECTORY_SEPARATOR;
316
            }
317 9
318
            return $logDirectory;
319 9
        }
320
321
        private function convertToUtf8($string): string
322
        {
323 9
            if (! \mb_detect_encoding($string, 'UTF-8', true)) {
324
                return \mb_convert_encoding($string, 'UTF-8');
325
            }
326 6
327
            return $string;
328 6
        }
329
330
        private function getStackTrace($error): string
331
        {
332
            return Util\Filter::getFilteredStacktrace($error);
333
        }
334
    }
335
}
336