Skip to content

Commit 93defe3

Browse files
authored
Merge pull request #49 from PauliusB/add-psalm-support
Add psalm report support
2 parents 81b6f00 + ded1d3d commit 93defe3

File tree

6 files changed

+250
-0
lines changed

6 files changed

+250
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Below is a list of all tools and a brief description
7979
--phpstan Parses the text output of phpstan
8080
--phpunit Parses text output in clover (xml) format generated with coverage-clover=file.xml
8181
--pylint Parses PyLint output
82+
--psalm Parses Psalm output
8283
```
8384

8485

src/PsalmLoader.php

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
namespace exussum12\CoverageChecker;
4+
5+
use XMLReader;
6+
7+
/**
8+
* Class PsalmLoader
9+
* Used for parsing psalm output
10+
*
11+
* @package exussum12\CoverageChecker
12+
*/
13+
class PsalmLoader implements FileChecker
14+
{
15+
/**
16+
* @var string
17+
*/
18+
protected $file;
19+
20+
/**
21+
* @var array
22+
*/
23+
protected $errors = [];
24+
25+
/**
26+
* @var array
27+
*/
28+
protected $errorRanges = [];
29+
30+
/**
31+
* PsalmLoader constructor.
32+
*
33+
* @param string $file the path to the psalm xml file
34+
*/
35+
public function __construct($file)
36+
{
37+
$this->file = $file;
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public function parseLines()
44+
{
45+
$this->errors = [];
46+
$this->errorRanges = [];
47+
$reader = new XMLReader;
48+
$reader->open($this->file);
49+
50+
while ($reader->read()) {
51+
if ($this->isElementBeginning($reader, 'item')) {
52+
$this->parseItem($reader);
53+
}
54+
}
55+
56+
return array_keys($this->errors);
57+
}
58+
59+
/**
60+
* {@inheritdoc}
61+
*/
62+
public function getErrorsOnLine($file, $lineNumber)
63+
{
64+
$errors = [];
65+
foreach ($this->errorRanges[$file] as $number => $error) {
66+
if ((
67+
$error['start'] <= $lineNumber
68+
&& $error['end'] >= $lineNumber
69+
)
70+
) {
71+
$errors[] = $error['error'];
72+
unset($this->errorRanges[$file][$number]);
73+
}
74+
}
75+
76+
return $errors;
77+
}
78+
79+
/**
80+
* @param XMLReader $reader
81+
* @param string $currentFile
82+
*/
83+
protected function parseItem(XMLReader $reader)
84+
{
85+
$attributes = [];
86+
87+
while ($reader->read()) {
88+
if ($this->isElementEnd($reader, 'item')) {
89+
break;
90+
}
91+
92+
if ($reader->nodeType == XMLReader::ELEMENT) {
93+
$attributes[$reader->name] = $reader->readString();
94+
}
95+
}
96+
97+
$error = $attributes['message'];
98+
$start = $attributes['line_from'];
99+
$end = $attributes['line_to'];
100+
$fileName = $attributes['file_name'];
101+
102+
$this->errorRanges[$fileName][] = [
103+
'start' => $start,
104+
'end' => $end,
105+
'error' => $error,
106+
];
107+
108+
$this->addForAllLines($fileName, $start, $end, $error);
109+
}
110+
111+
/**
112+
* {@inheritdoc}
113+
*/
114+
public function handleNotFoundFile()
115+
{
116+
return true;
117+
}
118+
119+
/**
120+
* {@inheritdoc}
121+
*/
122+
public static function getDescription()
123+
{
124+
return 'Parses the xml report format of psalm';
125+
}
126+
127+
protected function addForAllLines($currentFile, $start, $end, $error)
128+
{
129+
for ($i = $start; $i <= $end; $i++) {
130+
if ((
131+
!isset($this->errors[$currentFile][$i])
132+
|| !in_array($error, $this->errors[$currentFile][$i])
133+
)
134+
) {
135+
$this->errors[$currentFile][$i][] = $error;
136+
}
137+
}
138+
}
139+
140+
/**
141+
* @param XMLReader $reader
142+
* @param string $name
143+
*
144+
* @return bool
145+
*/
146+
protected function isElementBeginning($reader, $name)
147+
{
148+
return $reader->name === $name && $reader->nodeType == XMLReader::ELEMENT;
149+
}
150+
151+
/**
152+
* @param XMLReader $reader
153+
* @param string $name
154+
*
155+
* @return bool
156+
*/
157+
protected function isElementEnd($reader, $name)
158+
{
159+
return $reader->name === $name && $reader->nodeType == XMLReader::END_ELEMENT;
160+
}
161+
}

src/Runners/generic.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
'phpstan' => 'PhpStanLoader',
4949
'phpunit' => 'PhpUnitLoader',
5050
'pylint' => 'PylintLoader',
51+
'psalm' => 'PsalmLoader',
5152
];
5253

5354
$fileCheck = CoverageChecker\getFileChecker(

tests/PsalmDiffFilterTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
namespace exussum12\CoverageChecker\tests;
3+
4+
use PHPUnit\Framework\TestCase;
5+
use Exception;
6+
7+
/**
8+
* Ignored due to acceptance test needing to write values
9+
* @SuppressWarnings(PHPMD.Superglobals)
10+
*/
11+
class PsalmDiffFilterTest extends TestCase
12+
{
13+
14+
public function testValid()
15+
{
16+
$GLOBALS['argv'] = [
17+
'diffFilter',
18+
'--psalm',
19+
__DIR__ . '/fixtures/change.txt',
20+
__DIR__ . '/fixtures/psalm.xml'
21+
];
22+
ob_start();
23+
require(__DIR__ . "/../src/Runners/generic.php");
24+
$output = ob_get_clean();
25+
$this->assertContains('100.00% Covered', $output);
26+
}
27+
28+
public function testNoValidLines()
29+
{
30+
$GLOBALS['argv'] = [
31+
'diffFilter',
32+
'--psalm',
33+
__DIR__ . '/fixtures/change.txt',
34+
__DIR__ . '/fixtures/psalm-change.xml',
35+
];
36+
try {
37+
ob_start();
38+
require(__DIR__ . "/../src/Runners/generic.php");
39+
} catch (Exception $exception) {
40+
$output = ob_get_clean();
41+
$this->assertEquals(2, $exception->getCode());
42+
$this->assertContains('0.00%', $output);
43+
return;
44+
}
45+
$this->fail("no exception thrown");
46+
}
47+
}

tests/fixtures/psalm-change.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<report>
3+
<item>
4+
<severity>info</severity>
5+
<line_from>3</line_from>
6+
<line_to>4</line_to>
7+
<type>MissingPropertyType</type>
8+
<message>Violation message</message>
9+
<file_name>/full/path/to/file/src/changedFile.php</file_name>
10+
<file_path>/full/path/to/file/src/changedFile.php</file_path>
11+
<snippet> protected $foo;</snippet>
12+
<selected_text>protected $foo;</selected_text>
13+
<from>192</from>
14+
<to>222</to>
15+
<snippet_from>188</snippet_from>
16+
<snippet_to>222</snippet_to>
17+
<column_from>5</column_from>
18+
<column_to>35</column_to>
19+
</item>
20+
</report>

tests/fixtures/psalm.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<report>
3+
<item>
4+
<severity>info</severity>
5+
<line_from>11</line_from>
6+
<line_to>11</line_to>
7+
<type>MissingPropertyType</type>
8+
<message>Violation message</message>
9+
<file_name>full/path/to/file/src/CoverageCheck.php</file_name>
10+
<file_path>/full/path/to/file/src/CoverageCheck.php</file_path>
11+
<snippet> protected $foo;</snippet>
12+
<selected_text>protected $foo;</selected_text>
13+
<from>192</from>
14+
<to>222</to>
15+
<snippet_from>188</snippet_from>
16+
<snippet_to>222</snippet_to>
17+
<column_from>5</column_from>
18+
<column_to>35</column_to>
19+
</item>
20+
</report>

0 commit comments

Comments
 (0)