Completed
Push — master ( 1a3a48...6e0e69 )
by Alessandro
09:39 queued 07:07
created

LogPrinter::startTest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
crap 1
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\Test;
10
use PHPUnit\Framework\TestCase;
11
use PHPUnit\Framework\TestFailure;
12
use PHPUnit\Framework\TestListener;
13
use PHPUnit\Framework\TestSuite;
14
use PHPUnit\Framework\Warning;
15
use PHPUnit\Util;
16
17
/**
18
 * This class comes from Util\Log_JSON.
19
 * It's copied and refactored here because it's deprecated in PHPUnit 5.7 and it will be dropped in PHPUnit 6
20
 *
21
 * Class LogPrinter
22
 * @package Paraunit\Parser\JSON
23
 */
24
class LogPrinter extends Util\Printer implements TestListener
25
{
26
    const STATUS_ERROR = 'error';
27
    const STATUS_WARNING = 'warning';
28
    const STATUS_FAIL = 'fail';
29
    const STATUS_PASS = 'pass';
30
31
    const MESSAGE_INCOMPLETE_TEST = 'Incomplete Test: ';
32
    const MESSAGE_RISKY_TEST = 'Risky Test: ';
33
    const MESSAGE_SKIPPED_TEST = 'Skipped Test: ';
34
35
    /** @var resource */
36
    private $logFile;
37
38
    /** @var string */
39
    private $currentTestSuiteName;
40
41
    /** @var string */
42
    private $currentTestName;
43
44
    /** @var bool */
45
    private $currentTestPass;
46
47 9
    public function __construct()
48
    {
49 9
        $this->logFile = fopen($this->getLogFilename(), 'wt');
50 9
        $this->autoFlush = true;
51
    }
52
53
    /**
54
     * An error occurred.
55
     *
56
     * @param Test $test
57
     * @param \Exception $exception
58
     * @param float $time
59
     */
60 1 View Code Duplication
    public function addError(Test $test, \Exception $exception, $time)
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...
61
    {
62 1
        $this->writeCase(
63 1
            self::STATUS_ERROR,
64 1
            $time,
65 1
            $this->getStackTrace($exception),
66 1
            TestFailure::exceptionToString($exception),
67 1
            $test
68
        );
69
70 1
        $this->currentTestPass = false;
71
    }
72
73
    /**
74
     * A warning occurred.
75
     *
76
     * @param Test $test
77
     * @param Warning $warning
78
     * @param float $time
79
     */
80 1 View Code Duplication
    public function addWarning(Test $test, Warning $warning, $time)
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...
81
    {
82 1
        $this->writeCase(
83 1
            self::STATUS_WARNING,
84 1
            $time,
85 1
            $this->getStackTrace($warning),
86 1
            TestFailure::exceptionToString($warning),
87 1
            $test
88
        );
89
90 1
        $this->currentTestPass = false;
91
    }
92
93
    /**
94
     * A failure occurred.
95
     *
96
     * @param Test $test
97
     * @param AssertionFailedError $error
98
     * @param float $time
99
     */
100 1 View Code Duplication
    public function addFailure(Test $test, AssertionFailedError $error, $time)
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...
101
    {
102 1
        $this->writeCase(
103 1
            self::STATUS_FAIL,
104 1
            $time,
105 1
            $this->getStackTrace($error),
106 1
            TestFailure::exceptionToString($error),
107 1
            $test
108
        );
109
110 1
        $this->currentTestPass = false;
111
    }
112
113
    /**
114
     * Incomplete test.
115
     *
116
     * @param Test $test
117
     * @param \Exception $error
118
     * @param float $time
119
     */
120 1 View Code Duplication
    public function addIncompleteTest(Test $test, \Exception $error, $time)
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...
121
    {
122 1
        $this->writeCase(
123 1
            self::STATUS_ERROR,
124 1
            $time,
125 1
            $this->getStackTrace($error),
126 1
            self::MESSAGE_INCOMPLETE_TEST . $error->getMessage(),
127 1
            $test
128
        );
129
130 1
        $this->currentTestPass = false;
131
    }
132
133
    /**
134
     * Risky test.
135
     *
136
     * @param Test $test
137
     * @param \Exception $exception
138
     * @param float $time
139
     */
140 1 View Code Duplication
    public function addRiskyTest(Test $test, \Exception $exception, $time)
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...
141
    {
142 1
        $this->writeCase(
143 1
            self::STATUS_ERROR,
144 1
            $time,
145 1
            $this->getStackTrace($exception),
146 1
            self::MESSAGE_RISKY_TEST . $exception->getMessage(),
147 1
            $test
148
        );
149
150 1
        $this->currentTestPass = false;
151
    }
152
153
    /**
154
     * Skipped test.
155
     *
156
     * @param Test $test
157
     * @param \Exception $exception
158
     * @param float $time
159
     */
160 1 View Code Duplication
    public function addSkippedTest(Test $test, \Exception $exception, $time)
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...
161
    {
162 1
        $this->writeCase(
163 1
            self::STATUS_ERROR,
164 1
            $time,
165 1
            $this->getStackTrace($exception),
166 1
            self::MESSAGE_SKIPPED_TEST . $exception->getMessage(),
167 1
            $test
168
        );
169
170 1
        $this->currentTestPass = false;
171
    }
172
173
    /**
174
     * A testsuite started.
175
     *
176
     * @param TestSuite $suite
177
     * @throws \RuntimeException
178
     */
179 9
    public function startTestSuite(TestSuite $suite)
180
    {
181 9
        $this->currentTestSuiteName = $suite->getName();
182 9
        $this->currentTestName = '';
183
184 9
        $this->writeArray([
185 9
            'event' => 'suiteStart',
186 9
            'suite' => $this->currentTestSuiteName,
187 9
            'tests' => count($suite),
188
        ]);
189
    }
190
191 1
    public function endTestSuite(TestSuite $suite)
192
    {
193 1
        $this->currentTestSuiteName = '';
194 1
        $this->currentTestName = '';
195
    }
196
197 7
    public function startTest(Test $test)
198
    {
199 7
        $this->currentTestName = Util\Test::describe($test);
200 7
        $this->currentTestPass = true;
201
202 7
        $this->writeArray([
203 7
            'event' => 'testStart',
204 7
            'suite' => $this->currentTestSuiteName,
205 7
            'test' => $this->currentTestName,
206
        ]);
207
    }
208
209
    /**
210
     * A test ended.
211
     *
212
     * @param Test $test
213
     * @param float $time
214
     */
215 1
    public function endTest(Test $test, $time)
216
    {
217 1
        if ($this->currentTestPass) {
218 1
            $this->writeCase(self::STATUS_PASS, $time, [], '', $test);
219
        }
220
    }
221
222
    /**
223
     * @param string $status
224
     * @param float $time
225
     * @param array $trace
226
     * @param string $message
227
     * @param Test|TestCase|null $test
228
     */
229 7
    private function writeCase($status, $time, array $trace = [], $message = '', $test = null)
230
    {
231 7
        $output = '';
232 7
        if ($test instanceof TestCase) {
233
            $output = $test->getActualOutput();
234
        }
235
236 7
        $this->writeArray([
237 7
            'event' => 'test',
238 7
            'suite' => $this->currentTestSuiteName,
239 7
            'test' => $this->currentTestName,
240 7
            'status' => $status,
241 7
            'time' => $time,
242 7
            'trace' => $trace,
243 7
            'message' => $this->convertToUtf8($message),
244 7
            'output' => $output,
245
        ]);
246
    }
247
248
    /**
249
     * @param array $buffer
250
     */
251
    private function writeArray($buffer)
252
    {
253 9
        array_walk_recursive($buffer, function (&$input) {
254 9
            if (is_string($input)) {
255 9
                $input = $this->convertToUtf8($input);
256
            }
257 9
        });
258
259 9
        $this->writeToLog(json_encode($buffer, JSON_PRETTY_PRINT));
260
    }
261
262 9
    private function writeToLog($buffer)
263
    {
264
        // ignore everything that is not a JSON object
265 9
        if ($buffer != '' && $buffer[0] === '{') {
266 9
            \fwrite($this->logFile, $buffer);
267 9
            \fflush($this->logFile);
268
        }
269
    }
270
271
    /**
272
     * @return string
273
     * @throws \RuntimeException
274
     * @throws \InvalidArgumentException
275
     */
276 9
    private function getLogFilename(): string
277
    {
278 9
        $logDir = $this->getLogDirectory();
279 9 View Code Duplication
        if (! @mkdir($logDir, 0777, true) && ! is_dir($logDir)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
280
            throw new \RuntimeException('Cannot create folder for JSON logs');
281
        }
282
283 9
        $logFilename = getenv(EnvVariables::PROCESS_UNIQUE_ID);
284 9
        if ($logFilename === false) {
285
            throw new \InvalidArgumentException('Log filename not received: environment variable not set');
286
        }
287
288 9
        return $logDir . $logFilename . '.json.log';
289
    }
290
291
    /**
292
     * @return string
293
     * @throws \InvalidArgumentException
294
     */
295 9
    private function getLogDirectory(): string
296
    {
297 9
        $logDirectory = getenv(EnvVariables::LOG_DIR);
298
299 9
        if ($logDirectory === false) {
300
            throw new \InvalidArgumentException('Log directory not received: environment variable not set');
301
        }
302
303 9
        if (substr($logDirectory, -1) !== DIRECTORY_SEPARATOR) {
304
            $logDirectory .= DIRECTORY_SEPARATOR;
305
        }
306
307 9
        return $logDirectory;
308
    }
309
310 9
    private function convertToUtf8($string): string
311
    {
312 9
        if (! \mb_detect_encoding($string, 'UTF-8', true)) {
313
            return \mb_convert_encoding($string, 'UTF-8');
314
        }
315
316 9
        return $string;
317
    }
318
319 6
    private function getStackTrace($error): array
320
    {
321
        /** @var string[] $trace */
322 6
        $trace = Util\Filter::getFilteredStacktrace($error, false);
323
324 6
        return $trace;
325
    }
326
}
327