Completed
Pull Request — master (#116)
by Alessandro
08:37
created

LogPrinter::convertToUtf8()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2.0625
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 20.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
            $this->logFile = fopen($this->getLogFilename(), 'wt');
57 9
            $this->autoFlush = true;
58
        }
59
60
        /**
61
         * An error occurred.
62
         *
63
         * @param Test $test
64
         * @param \Throwable $exception
65
         * @param float $time
66
         */
67 1 View Code Duplication
        public function addError(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...
68
        {
69 1
            $this->writeCase(
70 1
                self::STATUS_ERROR,
71 1
                $time,
72 1
                $this->getStackTrace($exception),
73 1
                TestFailure::exceptionToString($exception),
74 1
                $test
75
            );
76
77 1
            $this->currentTestPass = false;
78
        }
79
80
        /**
81
         * A warning occurred.
82
         *
83
         * @param Test $test
84
         * @param Warning $warning
85
         * @param float $time
86
         */
87 1 View Code Duplication
        public function addWarning(Test $test, Warning $warning, 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...
88
        {
89 1
            $this->writeCase(
90 1
                self::STATUS_WARNING,
91 1
                $time,
92 1
                $this->getStackTrace($warning),
93 1
                TestFailure::exceptionToString($warning),
94 1
                $test
95
            );
96
97 1
            $this->currentTestPass = false;
98
        }
99
100
        /**
101
         * A failure occurred.
102
         *
103
         * @param Test $test
104
         * @param AssertionFailedError $error
105
         * @param float $time
106
         */
107 1 View Code Duplication
        public function addFailure(Test $test, AssertionFailedError $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...
108
        {
109 1
            $this->writeCase(
110 1
                self::STATUS_FAIL,
111 1
                $time,
112 1
                $this->getStackTrace($error),
113 1
                TestFailure::exceptionToString($error),
114 1
                $test
115
            );
116
117 1
            $this->currentTestPass = false;
118
        }
119
120
        /**
121
         * Incomplete test.
122
         *
123
         * @param Test $test
124
         * @param \Throwable $error
125
         * @param float $time
126
         */
127 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...
128
        {
129 1
            $this->writeCase(
130 1
                self::STATUS_ERROR,
131 1
                $time,
132 1
                $this->getStackTrace($error),
133 1
                self::MESSAGE_INCOMPLETE_TEST . $error->getMessage(),
134 1
                $test
135
            );
136
137 1
            $this->currentTestPass = false;
138
        }
139
140
        /**
141
         * Risky test.
142
         *
143
         * @param Test $test
144
         * @param \Throwable $exception
145
         * @param float $time
146
         */
147 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...
148
        {
149 1
            $this->writeCase(
150 1
                self::STATUS_ERROR,
151 1
                $time,
152 1
                $this->getStackTrace($exception),
153 1
                self::MESSAGE_RISKY_TEST . $exception->getMessage(),
154 1
                $test
155
            );
156
157 1
            $this->currentTestPass = false;
158
        }
159
160
        /**
161
         * Skipped test.
162
         *
163
         * @param Test $test
164
         * @param \Throwable $exception
165
         * @param float $time
166
         */
167 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...
168
        {
169 1
            $this->writeCase(
170 1
                self::STATUS_ERROR,
171 1
                $time,
172 1
                $this->getStackTrace($exception),
173 1
                self::MESSAGE_SKIPPED_TEST . $exception->getMessage(),
174 1
                $test
175
            );
176
177 1
            $this->currentTestPass = false;
178
        }
179
180
        /**
181
         * A testsuite started.
182
         *
183
         * @param TestSuite $suite
184
         * @throws \RuntimeException
185
         */
186 9 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...
187
        {
188 9
            $this->currentTestSuiteName = $suite->getName();
189 9
            $this->currentTestName = '';
190
191 9
            $this->writeArray([
192 9
                'event' => 'suiteStart',
193 9
                'suite' => $this->currentTestSuiteName,
194 9
                'tests' => count($suite),
195
            ]);
196
        }
197
198 1
        public function endTestSuite(TestSuite $suite): void
199
        {
200 1
            $this->currentTestSuiteName = '';
201 1
            $this->currentTestName = '';
202
        }
203
204 7 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...
205
        {
206 7
            $this->currentTestName = $test instanceof SelfDescribing ? $test->toString() : \get_class($test);
207 7
            $this->currentTestPass = true;
208
209 7
            $this->writeArray([
210 7
                'event' => 'testStart',
211 7
                'suite' => $this->currentTestSuiteName,
212 7
                'test' => $this->currentTestName,
213
            ]);
214
        }
215
216
        /**
217
         * A test ended.
218
         *
219
         * @param Test $test
220
         * @param float $time
221
         */
222 1
        public function endTest(Test $test, float $time): void
223
        {
224 1
            if ($this->currentTestPass) {
225 1
                $this->writeCase(self::STATUS_PASS, $time, '', '', $test);
226
            }
227
        }
228
229
        /**
230
         * @param string $status
231
         * @param float $time
232
         * @param string $trace
233
         * @param string $message
234
         * @param Test|TestCase|null $test
235
         */
236 7 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...
237
        {
238 7
            $output = '';
239 7
            if ($test instanceof TestCase) {
240
                $output = $test->getActualOutput();
241
            }
242
243 7
            $this->writeArray([
244 7
                'event' => 'test',
245 7
                'suite' => $this->currentTestSuiteName,
246 7
                'test' => $this->currentTestName,
247 7
                'status' => $status,
248 7
                'time' => $time,
249 7
                'trace' => $trace,
250 7
                'message' => $this->convertToUtf8($message),
251 7
                'output' => $output,
252
            ]);
253
        }
254
255
        /**
256
         * @param array $buffer
257
         */
258 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...
259
        {
260 9
            array_walk_recursive($buffer, function (&$input) {
261 9
                if (is_string($input)) {
262 9
                    $input = $this->convertToUtf8($input);
263
                }
264 9
            });
265
266 9
            $this->writeToLog(json_encode($buffer, JSON_PRETTY_PRINT));
267
        }
268
269 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...
270
        {
271
            // ignore everything that is not a JSON object
272 9
            if ($buffer != '' && $buffer[0] === '{') {
273 9
                \fwrite($this->logFile, $buffer);
274 9
                \fflush($this->logFile);
275
            }
276
        }
277
278
        /**
279
         * @return string
280
         * @throws \RuntimeException
281
         * @throws \InvalidArgumentException
282
         */
283 9 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...
284
        {
285 9
            $logDir = $this->getLogDirectory();
286 9
            if (! @mkdir($logDir, 0777, true) && ! is_dir($logDir)) {
287
                throw new \RuntimeException('Cannot create folder for JSON logs');
288
            }
289
290 9
            $logFilename = getenv(EnvVariables::PROCESS_UNIQUE_ID);
291 9
            if ($logFilename === false) {
292
                throw new \InvalidArgumentException('Log filename not received: environment variable not set');
293
            }
294
295 9
            return $logDir . $logFilename . '.json.log';
296
        }
297
298
        /**
299
         * @return string
300
         * @throws \InvalidArgumentException
301
         */
302 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...
303
        {
304 9
            $logDirectory = getenv(EnvVariables::LOG_DIR);
305
306 9
            if ($logDirectory === false) {
307
                throw new \InvalidArgumentException('Log directory not received: environment variable not set');
308
            }
309
310 9
            if (substr($logDirectory, -1) !== DIRECTORY_SEPARATOR) {
311
                $logDirectory .= DIRECTORY_SEPARATOR;
312
            }
313
314 9
            return $logDirectory;
315
        }
316
317 9
        private function convertToUtf8($string): string
318
        {
319 9
            if (! \mb_detect_encoding($string, 'UTF-8', true)) {
320
                return \mb_convert_encoding($string, 'UTF-8');
321
            }
322
323 9
            return $string;
324
        }
325
326 6
        private function getStackTrace($error): string
327
        {
328 6
            return Util\Filter::getFilteredStacktrace($error);
329
        }
330
    }
331
}
332