Completed
Push — master ( 20056a...86f14a )
by Johannes Skov
02:44
created

Parser::explodeTestName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
/**
3
 * VisualPHPUnit
4
 *
5
 * VisualPHPUnit is a visual front-end for PHPUnit.
6
 *
7
 * PHP Version 5.6<
8
 *
9
 * @author    Johannes Skov Frandsen <[email protected]>
10
 * @copyright 2011-2015 VisualPHPUnit
11
 * @license   http://opensource.org/licenses/BSD-3-Clause The BSD License
12
 * @link      https://github.com/VisualPHPUnit/VisualPHPUnit VisualPHPUnit
13
 */
14
namespace Visualphpunit\Core;
15
16
use \PHPUnit_Framework_TestSuite;
17
use \PHPUnit_Framework_TestResult;
18
use \PHPUnit_Framework_ExpectationFailedException;
19
use \Exception;
20
21
/**
22
 * Visualphpunit parser
23
 *
24
 * @author Johannes Skov Frandsen <[email protected]>
25
 */
26
class Parser
27
{
28
29
    /**
30
     * Run the list of test files
31
     *
32
     * @param string[] $tests
33
     *
34
     * @return array<string,double|integer|array>
35
     */
36
    public function run($tests)
37
    {
38
        $suite = new PHPUnit_Framework_TestSuite();
39
        $this->addBootstrap($tests);
40
        $suite->addTestFiles($tests);
41
        return $this->parseTestSuite($suite->run(new PHPUnit_Framework_TestResult()));
42
    }
43
44
    /**
45
     * Require bootstrap if vpu can find it
46
     *
47
     * @param array $tests
48
     *
49
     * @return void
50
     */
51
    private function addBootstrap($tests)
52
    {
53
        foreach ($tests as $filename) {
54
            if (file_exists($filename)) {
55
                $case1 = strpos($filename, 'tests');
56
                $case2 = strpos($filename, 'Tests');
57
                
58
                if (is_numeric($case1)) {
59
                    $path = substr($filename, 0, $case1 + 6) . 'bootstrap.php';
60
                    if (file_exists($path)) {
61
                        require_once $path;
62
                    }
63
                }
64
                if (is_numeric($case2)) {
65
                    $path = substr($filename, 0, $case2 + 6) . 'bootstrap.php';
66
                    if (file_exists($path)) {
67
                        require_once $path;
68
                    }
69
                }
70
            }
71
        }
72
    }
73
74
    /**
75
     * Parse the test suite result
76
     *
77
     * @param \PHPUnit_Framework_TestResult $result
78
     * @return array<string,double|integer|array>
79
     */
80
    private function parseTestSuite($result)
81
    {
82
        $passed = 0;
83
        $error = 0;
84
        $failed = 0;
85
        $notImplemented = 0;
86
        $skipped = 0;
87
        
88
        $tests = [];
89
        foreach ($result->passed() as $key => $value) {
90
            $tests[] = $this->parseTest('passed', $key);
91
            $passed ++;
92
        }
93
        foreach ($result->failures() as $obj) {
94
            $tests[] = $this->parseTest('failed', $obj);
95
            $failed ++;
96
        }
97
        foreach ($result->skipped() as $obj) {
98
            $tests[] = $this->parseTest('skipped', $obj);
99
            $skipped ++;
100
        }
101
        foreach ($result->notImplemented() as $obj) {
102
            $tests[] = $this->parseTest('notImplemented', $obj);
103
            $notImplemented ++;
104
        }
105
        foreach ($result->errors() as $obj) {
106
            $tests[] = $this->parseTest('error', $obj);
107
            $error ++;
108
        }
109
        
110
        usort($tests, function ($a, $b) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
111
            return strnatcmp($a['class'], $b['class']);
112
        });
113
        
114
        $data = [
115
            'time' => $result->time(),
116
            'total' => count($tests),
117
            'passed' => $passed,
118
            'error' => $error,
119
            'failed' => $failed,
120
            'notImplemented' => $notImplemented,
121
            'skipped' => $skipped,
122
            'tests' => $tests
123
        ];
124
        return $data;
125
    }
126
127
    /**
128
     * Filter the trace to exclude vendor and VPU classes
129
     *
130
     * @param array $trace
131
     * @return mixed[]
132
     */
133
    private function filterTrace($trace)
134
    {
135
        $vpuPath = realpath(__DIR__ . '/../');
136
        $vendorPath = realpath(__DIR__ . '/../../vendor');
137
        $backendPath = realpath(__DIR__ . '/../../backend');
138
        
139
        $newTrace = [];
140
        if (! empty($trace)) {
141
            foreach ($trace as $entity) {
142
                if (isset($entity['file'])
143
                    && ! strstr($entity['file'], $vendorPath)
144
                    && ! strstr($entity['file'], $vpuPath)
145
                    && ! strstr($entity['file'], $backendPath)) {
146
                    $newTrace[] = $entity;
147
                }
148
            }
149
        }
150
        return $newTrace;
151
    }
152
153
    /**
154
     * Parse individual test
155
     *
156
     * @param string $status
157
     * @param string|object $test
158
     *
159
     * @return mixed[]
160
     */
161
    private function parseTest($status, $test)
162
    {
163
        if (is_object($test)) {
164
            return [
165
                'class' => $this->explodeTestName($test->getTestName())['class'],
166
                'name' => $this->explodeTestName($test->getTestName())['method'],
167
                'friendly-name' => $this->friendlyName($this->explodeTestName($test->getTestName())['method']),
0 ignored issues
show
Documentation introduced by
$this->explodeTestName($...etTestName())['method'] is of type string, but the function expects a object<Visualphpunit\Core\sreing>.

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...
168
                'status' => $status,
169
                'message' => $test->thrownException()->getMessage(),
170
                'expected' => $this->getComparison($test->thrownException())['expected'],
171
                'actual' => $this->getComparison($test->thrownException())['actual'],
172
                'trace' => $this->filterTrace($test->thrownException()
173
                    ->getTrace())
174
            ];
175
        } else {
176
            return [
177
                'class' => $this->explodeTestName($test)['class'],
178
                'name' => $this->explodeTestName($test)['method'],
179
                'friendly-name' => $this->friendlyName($this->explodeTestName($test)['method']),
0 ignored issues
show
Documentation introduced by
$this->explodeTestName($test)['method'] is of type string, but the function expects a object<Visualphpunit\Core\sreing>.

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...
180
                'status' => $status,
181
                'message' => '',
182
                'expected' => '',
183
                'actual' => '',
184
                'trace' => ''
185
            ];
186
        }
187
    }
188
189
    /**
190
     * Convert camelCase to friendly name
191
     *
192
     * @param sreing $camelCaseString
193
     *
194
     * @return string
195
     */
196
    private function friendlyName($camelCaseString)
197
    {
198
        $re = '/(?<=[a-z])(?=[A-Z])/x';
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $re. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
199
        $a = preg_split($re, $camelCaseString);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
200
        $a[0] = ucfirst($a[0]);
201
        return join($a, " ");
202
    }
203
204
    /**
205
     * Explode a testname into class and method components
206
     *
207
     * @param string $testName
208
     * @return mixed[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,string>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
209
     */
210
    private function explodeTestName($testName)
211
    {
212
        $matches = [];
213
        preg_match('/([a-zA-Z0-9]+)::([a-zA-Z0-9_]+)$/', $testName, $matches);
214
        return [
215
            'class' => $matches[1],
216
            'method' => $matches[2]
217
        ];
218
    }
219
220
    /**
221
     * Get expected and actual if available
222
     *
223
     * @param Exception $e
224
     *
225
     * @return mixed[]
226
     */
227
    private function getComparison(Exception $e)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $e. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
228
    {
229
        if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) {
230
            return [
231
                'expected' => $e->getComparisonFailure()->getExpected(),
232
                'actual' => $e->getComparisonFailure()->getActual()
233
            ];
234
        }
235
        return [
236
            'expected' => '',
237
            'actual' => ''
238
        ];
239
    }
240
}
241