Completed
Push — master ( 02f97d...6ad174 )
by Alessandro
05:33
created

LogPrinter::isUtf8()   D

Complexity

Conditions 9
Paths 14

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 23.4162

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 7
cts 16
cp 0.4375
rs 4.909
c 0
b 0
f 0
cc 9
eloc 17
nc 14
nop 1
crap 23.4162
1
<?php
2
declare(strict_types=1);
3
4
namespace Paraunit\Parser\JSON;
5
6
use Paraunit\Configuration\EnvVariables;
7
use PHPUnit\Framework\AssertionFailedError;
8
use PHPUnit\Framework\Test;
9
use PHPUnit\Framework\TestCase;
10
use PHPUnit\Framework\TestFailure;
11
use PHPUnit\Framework\TestListener;
12
use PHPUnit\Framework\TestSuite;
13
use PHPUnit\Framework\Warning;
14
use PHPUnit\Util;
15
16
/**
17
 * This class comes from Util\Log_JSON.
18
 * It's copied and refactored here because it's deprecated in PHPUnit 5.7 and it will be dropped in PHPUnit 6
19
 *
20
 * Class LogPrinter
21
 * @package Paraunit\Parser\JSON
22
 */
23
class LogPrinter extends Util\Printer implements TestListener
24
{
25
    /** @var resource */
26
    private $logFile;
27
28
    /** @var string */
29
    private $currentTestSuiteName;
30
31
    /** @var string */
32
    private $currentTestName;
33
34
    /** @var bool */
35
    private $currentTestPass;
36
37 9
    public function __construct()
38
    {
39 9
        $this->logFile = fopen($this->getLogFilename(), 'wt');
40 9
        $this->autoFlush = true;
41
    }
42
43
    /**
44
     * An error occurred.
45
     *
46
     * @param Test $test
47
     * @param \Exception $e
48
     * @param float $time
49
     */
50 1
    public function addError(Test $test, \Exception $e, $time)
51
    {
52 1
        $this->writeCase(
53 1
            'error',
54 1
            $time,
55 1
            Util\Filter::getFilteredStacktrace($e, false),
0 ignored issues
show
Compatibility introduced by
$e of type object<Exception> is not a sub-type of object<PHPUnit\Framework\Exception>. It seems like you assume a child class of the class Exception to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like \PHPUnit\Util\Filter::ge...edStacktrace($e, false) targeting PHPUnit\Util\Filter::getFilteredStacktrace() can also be of type string; however, Paraunit\Parser\JSON\LogPrinter::writeCase() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
56 1
            TestFailure::exceptionToString($e),
57 1
            $test
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
58
        );
59
60 1
        $this->currentTestPass = false;
61
    }
62
63
    /**
64
     * A warning occurred.
65
     *
66
     * @param Test $test
67
     * @param Warning $e
68
     * @param float $time
69
     */
70 1 View Code Duplication
    public function addWarning(Test $test, Warning $e, $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...
71
    {
72 1
        $this->writeCase(
73 1
            'warning',
74 1
            $time,
75 1
            Util\Filter::getFilteredStacktrace($e, false),
0 ignored issues
show
Bug introduced by
It seems like \PHPUnit\Util\Filter::ge...edStacktrace($e, false) targeting PHPUnit\Util\Filter::getFilteredStacktrace() can also be of type string; however, Paraunit\Parser\JSON\LogPrinter::writeCase() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
76 1
            TestFailure::exceptionToString($e),
77 1
            $test
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
78
        );
79
80 1
        $this->currentTestPass = false;
81
    }
82
83
    /**
84
     * A failure occurred.
85
     *
86
     * @param Test $test
87
     * @param AssertionFailedError $e
88
     * @param float $time
89
     */
90 1 View Code Duplication
    public function addFailure(Test $test, AssertionFailedError $e, $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...
91
    {
92 1
        $this->writeCase(
93 1
            'fail',
94 1
            $time,
95 1
            Util\Filter::getFilteredStacktrace($e, false),
0 ignored issues
show
Bug introduced by
It seems like \PHPUnit\Util\Filter::ge...edStacktrace($e, false) targeting PHPUnit\Util\Filter::getFilteredStacktrace() can also be of type string; however, Paraunit\Parser\JSON\LogPrinter::writeCase() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
96 1
            TestFailure::exceptionToString($e),
97 1
            $test
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
98
        );
99
100 1
        $this->currentTestPass = false;
101
    }
102
103
    /**
104
     * Incomplete test.
105
     *
106
     * @param Test $test
107
     * @param \Exception $e
108
     * @param float $time
109
     */
110 1 View Code Duplication
    public function addIncompleteTest(Test $test, \Exception $e, $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...
111
    {
112 1
        $this->writeCase(
113 1
            'error',
114 1
            $time,
115 1
            Util\Filter::getFilteredStacktrace($e, false),
0 ignored issues
show
Compatibility introduced by
$e of type object<Exception> is not a sub-type of object<PHPUnit\Framework\Exception>. It seems like you assume a child class of the class Exception to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like \PHPUnit\Util\Filter::ge...edStacktrace($e, false) targeting PHPUnit\Util\Filter::getFilteredStacktrace() can also be of type string; however, Paraunit\Parser\JSON\LogPrinter::writeCase() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
116 1
            'Incomplete Test: ' . $e->getMessage(),
117 1
            $test
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
118
        );
119
120 1
        $this->currentTestPass = false;
121
    }
122
123
    /**
124
     * Risky test.
125
     *
126
     * @param Test $test
127
     * @param \Exception $e
128
     * @param float $time
129
     */
130 1 View Code Duplication
    public function addRiskyTest(Test $test, \Exception $e, $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...
131
    {
132 1
        $this->writeCase(
133 1
            'error',
134 1
            $time,
135 1
            Util\Filter::getFilteredStacktrace($e, false),
0 ignored issues
show
Compatibility introduced by
$e of type object<Exception> is not a sub-type of object<PHPUnit\Framework\Exception>. It seems like you assume a child class of the class Exception to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like \PHPUnit\Util\Filter::ge...edStacktrace($e, false) targeting PHPUnit\Util\Filter::getFilteredStacktrace() can also be of type string; however, Paraunit\Parser\JSON\LogPrinter::writeCase() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
136 1
            'Risky Test: ' . $e->getMessage(),
137 1
            $test
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
138
        );
139
140 1
        $this->currentTestPass = false;
141
    }
142
143
    /**
144
     * Skipped test.
145
     *
146
     * @param Test $test
147
     * @param \Exception $e
148
     * @param float $time
149
     */
150 1 View Code Duplication
    public function addSkippedTest(Test $test, \Exception $e, $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...
151
    {
152 1
        $this->writeCase(
153 1
            'error',
154 1
            $time,
155 1
            Util\Filter::getFilteredStacktrace($e, false),
0 ignored issues
show
Compatibility introduced by
$e of type object<Exception> is not a sub-type of object<PHPUnit\Framework\Exception>. It seems like you assume a child class of the class Exception to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like \PHPUnit\Util\Filter::ge...edStacktrace($e, false) targeting PHPUnit\Util\Filter::getFilteredStacktrace() can also be of type string; however, Paraunit\Parser\JSON\LogPrinter::writeCase() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
156 1
            'Skipped Test: ' . $e->getMessage(),
157 1
            $test
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
158
        );
159
160 1
        $this->currentTestPass = false;
161
    }
162
163
    /**
164
     * A testsuite started.
165
     *
166
     * @param TestSuite $suite
167
     * @throws \RuntimeException
168
     */
169 9
    public function startTestSuite(TestSuite $suite)
170
    {
171 9
        $this->currentTestSuiteName = $suite->getName();
172 9
        $this->currentTestName = '';
173
174 9
        $this->writeArray([
175 9
            'event' => 'suiteStart',
176 9
            'suite' => $this->currentTestSuiteName,
177 9
            'tests' => count($suite)
178
        ]);
179
    }
180
181 1
    public function endTestSuite(TestSuite $suite)
182
    {
183 1
        $this->currentTestSuiteName = '';
184 1
        $this->currentTestName = '';
185
    }
186
187 7
    public function startTest(Test $test)
188
    {
189 7
        $this->currentTestName = Util\Test::describe($test);
190 7
        $this->currentTestPass = true;
191
192 7
        $this->writeArray([
193 7
            'event' => 'testStart',
194 7
            'suite' => $this->currentTestSuiteName,
195 7
            'test' => $this->currentTestName
196
        ]);
197
    }
198
199
    /**
200
     * A test ended.
201
     *
202
     * @param Test $test
203
     * @param float $time
204
     */
205 1
    public function endTest(Test $test, $time)
206
    {
207 1
        if ($this->currentTestPass) {
208 1
            $this->writeCase('pass', $time, [], '', $test);
0 ignored issues
show
Documentation introduced by
$test is of type object<PHPUnit\Framework\Test>, but the function expects a object<PHPUnit\Framework\TestCase>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
209
        }
210
    }
211
212
    /**
213
     * @param string $status
214
     * @param float $time
215
     * @param array $trace
216
     * @param string $message
217
     * @param TestCase|null $test
218
     */
219 7
    private function writeCase($status, $time, array $trace = [], $message = '', $test = null)
220
    {
221 7
        $output = '';
222
        // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput
223 7
        if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) {
224
            $output = $test->getActualOutput();
225
        }
226 7
        $this->writeArray([
227 7
            'event' => 'test',
228 7
            'suite' => $this->currentTestSuiteName,
229 7
            'test' => $this->currentTestName,
230 7
            'status' => $status,
231 7
            'time' => $time,
232 7
            'trace' => $trace,
233 7
            'message' => $this->convertToUtf8($message),
234 7
            'output' => $output,
235
        ]);
236
    }
237
238
    /**
239
     * @param array $buffer
240
     */
241
    private function writeArray($buffer)
242
    {
243 9
        array_walk_recursive($buffer, function (&$input) {
244 9
            if (is_string($input)) {
245 9
                $input = $this->convertToUtf8($input);
246
            }
247 9
        });
248
249 9
        $this->writeToLog(json_encode($buffer, JSON_PRETTY_PRINT));
250
    }
251
252 9
    private function writeToLog($buffer)
253
    {
254
        // ignore everything that is not a JSON object
255 9
        if ($buffer != '' && $buffer[0] === '{') {
256 9
            \fwrite($this->logFile, $buffer);
257 9
            \fflush($this->logFile);
258
        }
259
    }
260
261
    /**
262
     * @return string
263
     * @throws \RuntimeException
264
     * @throws \InvalidArgumentException
265
     */
266 9
    private function getLogFilename(): string
267
    {
268 9
        $logDir = $this->getLogDirectory();
269 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...
270
            throw new \RuntimeException('Cannot create folder for JSON logs');
271
        }
272
273 9
        $logFilename = getenv(EnvVariables::PROCESS_UNIQUE_ID) . '.json.log';
274 9
        if ($logFilename === false) {
275
            throw new \InvalidArgumentException('Log filename not received: environment variable not set');
276
        }
277
278 9
        return $logDir . $logFilename;
279
    }
280
281
    /**
282
     * @return string
283
     * @throws \InvalidArgumentException
284
     */
285 9
    private function getLogDirectory(): string
286
    {
287 9
        $logDirectory = getenv(EnvVariables::LOG_DIR);
288
289 9
        if ($logDirectory === false) {
290
            throw new \InvalidArgumentException('Log directory not received: environment variable not set');
291
        }
292
293 9
        if (substr($logDirectory, -1) !== DIRECTORY_SEPARATOR) {
294
            $logDirectory .= DIRECTORY_SEPARATOR;
295
        }
296
297 9
        return $logDirectory;
298
    }
299
300 9
    private function convertToUtf8($string): string
301
    {
302 9
        if (! \mb_detect_encoding($string, 'UTF-8', true)) {
303
            return \mb_convert_encoding($string, 'UTF-8');
304
        }
305
306 9
        return $string;
307
    }
308
}
309