Skip to content
Open
58 changes: 50 additions & 8 deletions src/Command/ErrorFormatter/JunitErrorFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\File\RelativePathHelper;
use SplFileObject;
use function array_filter;
use function file_exists;
use function htmlspecialchars;
use function implode;
use function is_null;
use function sprintf;
use const ENT_COMPAT;
use const ENT_XML1;
use const PHP_EOL;

#[AutowiredService(name: 'errorFormatter.junit')]
final class JunitErrorFormatter implements ErrorFormatter
Expand All @@ -33,17 +39,19 @@ public function formatErrors(

$result = '<?xml version="1.0" encoding="UTF-8"?>';
$result .= sprintf(
'<testsuite failures="%d" name="phpstan" tests="%d" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">',
'<testsuite failures="%d" name="phpstan" tests="%d" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">',
$totalFailuresCount,
$totalTestsCount,
);

foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
$fileName = $this->relativePathHelper->getRelativePath($fileSpecificError->getFile());
$result .= $this->createTestCase(
sprintf('%s:%s', $fileName, (string) $fileSpecificError->getLine()),
$fileName,
'ERROR',
$fileSpecificError->getMessage(),
implode(PHP_EOL, array_filter([ $fileSpecificError->getMessage(), $fileSpecificError->getTip() ], static fn (?string $part): bool => $part !== null && $part !== '')),
$fileSpecificError->getLine(),
$fileSpecificError->getIdentifier(),
);
}

Expand Down Expand Up @@ -71,17 +79,51 @@ public function formatErrors(
*
*
*/
private function createTestCase(string $reference, string $type, ?string $message = null): string
private function createTestCase(string $reference, string $type, ?string $message = null, ?int $line = null, ?string $identifier = null): string
{
$result = sprintf('<testcase name="%s">', $this->escape($reference));
$result = sprintf(
'<testcase name="%s" classname="%s">', // class="%s" file="%s" line="%d",
$this->escape(is_null($identifier) ? '' : $identifier),
//$this->escape($reference),
//$this->escape($reference),
$this->escape(sprintf('%s:%d', $reference, $line ?? 0)),
//$line,
);

if ($message !== null) {
$result .= sprintf('<failure type="%s" message="%s" />', $this->escape($type), $this->escape($message));
if ($line === null) {
$result .= sprintf(
'<failure type="%s" message="%s" />',
$this->escape($type),
$this->escape($message),
);
} else {
$result .= sprintf(
'<failure type="%s" message="%s"><![CDATA[%s]]></failure>',
$this->escape($type),
$this->escape($message),
$this->fileGetLine($reference, $line),
);
}
}

$result .= '</testcase>';
return $result . '</testcase>';
}

private function fileGetLine(string $fileName, int $lineNumber): ?string
{
if (!file_exists($fileName)) {
return null;
}

$file = new SplFileObject($fileName);
if ($file->eof()) {
return null;
}

return $result;
$file->seek($lineNumber);
// @phpstan-ignore-next-line
return $file->current(); // $contents would hold the data from line x
}

/**
Expand Down
46 changes: 23 additions & 23 deletions tests/PHPStan/Command/ErrorFormatter/JunitErrorFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public static function dataFormatterOutputProvider(): Generator
0,
0,
'<?xml version="1.0" encoding="UTF-8"?>
<testsuite failures="0" name="phpstan" tests="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="phpstan"/>
<testsuite failures="0" name="phpstan" tests="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase classname="phpstan:0" name=""/>
</testsuite>
',
];
Expand All @@ -43,8 +43,8 @@ public static function dataFormatterOutputProvider(): Generator
1,
0,
'<?xml version="1.0" encoding="UTF-8"?>
<testsuite failures="1" name="phpstan" tests="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:4">
<testsuite failures="1" name="phpstan" tests="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase classname="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:4" name="">
<failure type="ERROR" message="Foo" />
</testcase>
</testsuite>
Expand All @@ -56,8 +56,8 @@ public static function dataFormatterOutputProvider(): Generator
0,
1,
'<?xml version="1.0" encoding="UTF-8"?>
<testsuite failures="1" name="phpstan" tests="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="General error">
<testsuite failures="1" name="phpstan" tests="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase classname="General error:0" name="">
<failure type="ERROR" message="first generic error" />
</testcase>
</testsuite>
Expand All @@ -69,18 +69,18 @@ public static function dataFormatterOutputProvider(): Generator
4,
0,
'<?xml version="1.0" encoding="UTF-8"?>
<testsuite failures="4" name="phpstan" tests="4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:2">
<testsuite failures="4" name="phpstan" tests="4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase classname="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:2" name="">
<failure type="ERROR" message="Bar Bar2" />
</testcase>
<testcase name="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:4">
<testcase classname="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:4" name="">
<failure type="ERROR" message="Foo" />
</testcase>
<testcase name="foo.php:1">
<testcase classname="foo.php:1" name="">
<failure type="ERROR" message="Foo&lt;Bar&gt;"/>
</testcase>
<testcase name="foo.php:5">
<failure type="ERROR" message="Bar Bar2"/>
<testcase classname="foo.php:5" name="">
<failure type="ERROR" message="Bar Bar2 a tip"/>
</testcase>
</testsuite>
',
Expand All @@ -91,11 +91,11 @@ public static function dataFormatterOutputProvider(): Generator
0,
2,
'<?xml version="1.0" encoding="UTF-8"?>
<testsuite failures="2" name="phpstan" tests="2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="General error">
<testsuite failures="2" name="phpstan" tests="2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase classname="General error:0" name="">
<failure type="ERROR" message="first generic error" />
</testcase>
<testcase name="General error">
<testcase classname="General error:0" name="">
<failure type="ERROR" message="second generic&lt;error&gt;"/>
</testcase>
</testsuite>
Expand All @@ -107,23 +107,23 @@ public static function dataFormatterOutputProvider(): Generator
4,
2,
'<?xml version="1.0" encoding="UTF-8"?>
<testsuite failures="6" name="phpstan" tests="6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:2">
<testsuite failures="6" name="phpstan" tests="6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.9.3/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase classname="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:2" name="">
<failure type="ERROR" message="Bar Bar2" />
</testcase>
<testcase name="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:4">
<testcase classname="folder with unicode &#x1F603;/file name with &quot;spaces&quot; and unicode &#x1F603;.php:4" name="">
<failure type="ERROR" message="Foo" />
</testcase>
<testcase name="foo.php:1">
<testcase classname="foo.php:1" name="">
<failure type="ERROR" message="Foo&lt;Bar&gt;"/>
</testcase>
<testcase name="foo.php:5">
<failure type="ERROR" message="Bar Bar2"/>
<testcase classname="foo.php:5" name="">
<failure type="ERROR" message="Bar Bar2 a tip"/>
</testcase>
<testcase name="General error">
<testcase classname="General error:0" name="">
<failure type="ERROR" message="first generic error" />
</testcase>
<testcase name="General error">
<testcase classname="General error:0" name="">
<failure type="ERROR" message="second generic&lt;error&gt;"/>
</testcase>
</testsuite>
Expand Down
Loading