Completed
Pull Request — master (#128)
by Alessandro
04:58 queued 02:19
created

LogPrinter::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

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