Skip to content

Commit db52364

Browse files
authored
JUnit format improvements (#164)
1 parent 213c157 commit db52364

File tree

3 files changed

+96
-108
lines changed

3 files changed

+96
-108
lines changed

src/Result/JunitFormatter.php

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,26 @@
22

33
namespace ShipMonk\ComposerDependencyAnalyser\Result;
44

5+
use DOMDocument;
56
use ShipMonk\ComposerDependencyAnalyser\CliOptions;
67
use ShipMonk\ComposerDependencyAnalyser\Config\Configuration;
78
use ShipMonk\ComposerDependencyAnalyser\Config\Ignore\UnusedErrorIgnore;
89
use ShipMonk\ComposerDependencyAnalyser\Config\Ignore\UnusedSymbolIgnore;
910
use ShipMonk\ComposerDependencyAnalyser\Printer;
1011
use ShipMonk\ComposerDependencyAnalyser\SymbolKind;
1112
use function array_fill_keys;
12-
use function array_reduce;
1313
use function count;
14+
use function extension_loaded;
1415
use function htmlspecialchars;
1516
use function implode;
1617
use function sprintf;
1718
use function strlen;
1819
use function strpos;
1920
use function substr;
21+
use function trim;
2022
use const ENT_COMPAT;
2123
use const ENT_XML1;
24+
use const LIBXML_NOEMPTYTAG;
2225
use const PHP_INT_MAX;
2326

2427
class JunitFormatter implements ResultFormatter
@@ -120,9 +123,13 @@ public function format(
120123
$xml .= $this->createUnusedIgnoresTestSuite($unusedIgnores);
121124
}
122125

126+
if ($hasError) {
127+
$xml .= sprintf('<!-- %s -->', $this->getUsagesComment($maxShownUsages));
128+
}
129+
123130
$xml .= '</testsuites>';
124131

125-
$this->printer->print($xml);
132+
$this->printer->print($this->prettyPrintXml($xml));
126133

127134
if ($hasError) {
128135
return 255;
@@ -154,30 +161,18 @@ private function createSymbolBasedTestSuite(string $title, array $errors, int $m
154161
foreach ($errors as $symbol => $usages) {
155162
$xml .= sprintf('<testcase name="%s">', $this->escape($symbol));
156163

157-
if ($maxShownUsages > 1) {
158-
$failureUsage = [];
164+
$failureUsage = [];
159165

160-
foreach ($usages as $index => $usage) {
161-
$failureUsage[] = $this->relativizeUsage($usage);
166+
foreach ($usages as $index => $usage) {
167+
$failureUsage[] = $this->relativizeUsage($usage);
162168

163-
if ($index === $maxShownUsages - 1) {
164-
$restUsagesCount = count($usages) - $index - 1;
165-
166-
if ($restUsagesCount > 0) {
167-
$failureUsage[] = "+ {$restUsagesCount} more";
168-
break;
169-
}
170-
}
169+
if ($index === $maxShownUsages) {
170+
break;
171171
}
172-
173-
$xml .= sprintf('<failure>%s</failure>', $this->escape(implode('\n', $failureUsage)));
174-
} else {
175-
$firstUsage = $usages[0];
176-
$restUsagesCount = count($usages) - 1;
177-
$rest = $restUsagesCount > 0 ? " (+ {$restUsagesCount} more)" : '';
178-
$xml .= sprintf('<failure>in %s%s</failure>', $this->escape($this->relativizeUsage($firstUsage)), $rest);
179172
}
180173

174+
$xml .= sprintf('<failure>%s</failure>', $this->escape(implode("\n", $failureUsage)));
175+
181176
$xml .= '</testcase>';
182177
}
183178

@@ -195,7 +190,24 @@ private function createPackageBasedTestSuite(string $title, array $errors, int $
195190

196191
foreach ($errors as $packageName => $usagesPerClassname) {
197192
$xml .= sprintf('<testcase name="%s">', $this->escape($packageName));
198-
$xml .= sprintf('<failure>%s</failure>', $this->escape(implode('\n', $this->createUsages($usagesPerClassname, $maxShownUsages))));
193+
194+
$printedSymbols = 0;
195+
196+
foreach ($usagesPerClassname as $symbol => $usages) {
197+
$printedSymbols++;
198+
$xml .= sprintf(
199+
'<failure message="%s">%s</failure>',
200+
$symbol,
201+
$this->escape(
202+
implode("\n", $this->createUsages($usages, $maxShownUsages))
203+
)
204+
);
205+
206+
if ($printedSymbols === $maxShownUsages) {
207+
break;
208+
}
209+
}
210+
199211
$xml .= '</testcase>';
200212
}
201213

@@ -205,59 +217,19 @@ private function createPackageBasedTestSuite(string $title, array $errors, int $
205217
}
206218

207219
/**
208-
* @param array<string, list<SymbolUsage>> $usagesPerSymbol
220+
* @param list<SymbolUsage> $usages
209221
* @return list<string>
210222
*/
211-
private function createUsages(array $usagesPerSymbol, int $maxShownUsages): array
223+
private function createUsages(array $usages, int $maxShownUsages): array
212224
{
213225
$usageMessages = [];
214226

215-
if ($maxShownUsages === 1) {
216-
$countOfAllUsages = array_reduce(
217-
$usagesPerSymbol,
218-
static function (int $carry, array $usages): int {
219-
return $carry + count($usages);
220-
},
221-
0
222-
);
227+
foreach ($usages as $index => $usage) {
228+
$usageMessages[] = $this->relativizeUsage($usage);
223229

224-
foreach ($usagesPerSymbol as $symbol => $usages) {
225-
$firstUsage = $usages[0];
226-
$restUsagesCount = $countOfAllUsages - 1;
227-
$rest = $countOfAllUsages > 1 ? " (+ {$restUsagesCount} more)" : '';
228-
$usageMessages[] = "e.g. {$symbol} in {$this->relativizeUsage($firstUsage)}$rest";
230+
if ($index === $maxShownUsages - 1) {
229231
break;
230232
}
231-
} else {
232-
$classnamesPrinted = 0;
233-
234-
foreach ($usagesPerSymbol as $symbol => $usages) {
235-
$classnamesPrinted++;
236-
237-
$usageMessages[] = $symbol;
238-
239-
foreach ($usages as $index => $usage) {
240-
$usageMessages[] = " {$this->relativizeUsage($usage)}";
241-
242-
if ($index === $maxShownUsages - 1) {
243-
$restUsagesCount = count($usages) - $index - 1;
244-
245-
if ($restUsagesCount > 0) {
246-
$usageMessages[] = " + {$restUsagesCount} more";
247-
break;
248-
}
249-
}
250-
}
251-
252-
if ($classnamesPrinted === $maxShownUsages) {
253-
$restSymbolsCount = count($usagesPerSymbol) - $classnamesPrinted;
254-
255-
if ($restSymbolsCount > 0) {
256-
$usageMessages[] = " + {$restSymbolsCount} more symbol" . ($restSymbolsCount > 1 ? 's' : '');
257-
break;
258-
}
259-
}
260-
}
261233
}
262234

263235
return $usageMessages;
@@ -323,4 +295,37 @@ private function escape(string $string): string
323295
return htmlspecialchars($string, ENT_XML1 | ENT_COMPAT, 'UTF-8');
324296
}
325297

298+
private function prettyPrintXml(string $inputXml): string
299+
{
300+
if (!extension_loaded('dom') || !extension_loaded('libxml')) {
301+
return $inputXml;
302+
}
303+
304+
$dom = new DOMDocument();
305+
$dom->preserveWhiteSpace = false;
306+
$dom->formatOutput = true;
307+
$dom->loadXML($inputXml);
308+
309+
$outputXml = $dom->saveXML(null, LIBXML_NOEMPTYTAG);
310+
311+
if ($outputXml === false) {
312+
return $inputXml;
313+
}
314+
315+
return trim($outputXml);
316+
}
317+
318+
private function getUsagesComment(int $maxShownUsages): string
319+
{
320+
if ($maxShownUsages === PHP_INT_MAX) {
321+
return 'showing all failure usages';
322+
}
323+
324+
if ($maxShownUsages === 1) {
325+
return 'showing only first example failure usage';
326+
}
327+
328+
return sprintf('showing only first %d example failure usages', $maxShownUsages);
329+
}
330+
326331
}

tests/BinTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function test(): void
2727

2828
$usingConfig = 'Using config';
2929

30-
$junitOutput = '<?xml version="1.0" encoding="UTF-8"?><testsuites></testsuites>';
30+
$junitOutput = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites></testsuites>";
3131

3232
$this->runCommand('php bin/composer-dependency-analyser', $rootDir, 0, $okOutput, $usingConfig);
3333
$this->runCommand('php bin/composer-dependency-analyser --verbose', $rootDir, 0, $okOutput, $usingConfig);

tests/JunitFormatterTest.php

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22

33
namespace ShipMonk\ComposerDependencyAnalyser;
44

5-
use DOMDocument;
65
use ShipMonk\ComposerDependencyAnalyser\Config\Configuration;
76
use ShipMonk\ComposerDependencyAnalyser\Config\ErrorType;
87
use ShipMonk\ComposerDependencyAnalyser\Config\Ignore\UnusedErrorIgnore;
98
use ShipMonk\ComposerDependencyAnalyser\Result\AnalysisResult;
109
use ShipMonk\ComposerDependencyAnalyser\Result\JunitFormatter;
1110
use ShipMonk\ComposerDependencyAnalyser\Result\ResultFormatter;
1211
use ShipMonk\ComposerDependencyAnalyser\Result\SymbolUsage;
13-
use function trim;
14-
use const LIBXML_NOEMPTYTAG;
1512

1613
class JunitFormatterTest extends FormatterTest
1714
{
@@ -39,11 +36,12 @@ public function testPrintResult(): void
3936
<failure>'shadow-dependency' was globally ignored, but it was never applied.</failure>
4037
</testcase>
4138
</testsuite>
39+
<!-- showing only first example failure usage -->
4240
</testsuites>
4341
OUT;
4442

45-
self::assertSame($this->normalizeEol($expectedNoIssuesOutput), $this->prettyPrintXml($noIssuesOutput));
46-
self::assertSame($this->normalizeEol($expectedNoIssuesButWarningsOutput), $this->prettyPrintXml($noIssuesButUnusedIgnores));
43+
self::assertSame($this->normalizeEol($expectedNoIssuesOutput), $noIssuesOutput);
44+
self::assertSame($this->normalizeEol($expectedNoIssuesButWarningsOutput), $noIssuesButUnusedIgnores);
4745

4846
$analysisResult = new AnalysisResult(
4947
10,
@@ -87,37 +85,34 @@ public function testPrintResult(): void
8785
<testsuites>
8886
<testsuite name="unknown classes" failures="1">
8987
<testcase name="Unknown\Thing">
90-
<failure>in app/init.php:1093</failure>
88+
<failure>app/init.php:1093</failure>
9189
</testcase>
9290
</testsuite>
9391
<testsuite name="unknown functions" failures="1">
9492
<testcase name="Unknown\function">
95-
<failure>in app/foo.php:51</failure>
93+
<failure>app/foo.php:51</failure>
9694
</testcase>
9795
</testsuite>
9896
<testsuite name="shadow dependencies" failures="2">
9997
<testcase name="shadow/another">
100-
<failure>e.g. Another\Controller in src/bootstrap.php:173</failure>
98+
<failure message="Another\Controller">src/bootstrap.php:173</failure>
10199
</testcase>
102100
<testcase name="shadow/package">
103-
<failure>e.g. Forth\Provider in src/bootstrap.php:873 (+ 6 more)</failure>
101+
<failure message="Forth\Provider">src/bootstrap.php:873</failure>
104102
</testcase>
105103
</testsuite>
106104
<testsuite name="dev dependencies in production code" failures="1">
107105
<testcase name="some/package">
108-
<failure>e.g. Another\Command in src/ProductGenerator.php:28</failure>
106+
<failure message="Another\Command">src/ProductGenerator.php:28</failure>
109107
</testcase>
110108
</testsuite>
111109
<testsuite name="prod dependencies used only in dev paths" failures="1">
112-
<testcase name="misplaced/package">
113-
<failure></failure>
114-
</testcase>
110+
<testcase name="misplaced/package"></testcase>
115111
</testsuite>
116112
<testsuite name="unused dependencies" failures="1">
117-
<testcase name="dead/package">
118-
<failure></failure>
119-
</testcase>
113+
<testcase name="dead/package"></testcase>
120114
</testsuite>
115+
<!-- showing only first example failure usage -->
121116
</testsuites>
122117
OUT;
123118
$expectedVerboseOutput = <<<'OUT'
@@ -135,48 +130,36 @@ public function testPrintResult(): void
135130
</testsuite>
136131
<testsuite name="shadow dependencies" failures="2">
137132
<testcase name="shadow/another">
138-
<failure>Another\Controller\n src/bootstrap.php:173</failure>
133+
<failure message="Another\Controller">src/bootstrap.php:173</failure>
139134
</testcase>
140135
<testcase name="shadow/package">
141-
<failure>Forth\Provider\n src/bootstrap.php:873\nShadow\Comparator\n src/Printer.php:25\nShadow\Utils\n src/Utils.php:19\n src/Utils.php:22\n src/Application.php:128\n + 1 more\n + 1 more symbol</failure>
136+
<failure message="Forth\Provider">src/bootstrap.php:873</failure>
137+
<failure message="Shadow\Comparator">src/Printer.php:25</failure>
138+
<failure message="Shadow\Utils">src/Utils.php:19
139+
src/Utils.php:22
140+
src/Application.php:128</failure>
142141
</testcase>
143142
</testsuite>
144143
<testsuite name="dev dependencies in production code" failures="1">
145144
<testcase name="some/package">
146-
<failure>Another\Command\n src/ProductGenerator.php:28</failure>
145+
<failure message="Another\Command">src/ProductGenerator.php:28</failure>
147146
</testcase>
148147
</testsuite>
149148
<testsuite name="prod dependencies used only in dev paths" failures="1">
150-
<testcase name="misplaced/package">
151-
<failure></failure>
152-
</testcase>
149+
<testcase name="misplaced/package"></testcase>
153150
</testsuite>
154151
<testsuite name="unused dependencies" failures="1">
155-
<testcase name="dead/package">
156-
<failure></failure>
157-
</testcase>
152+
<testcase name="dead/package"></testcase>
158153
</testsuite>
154+
<!-- showing only first 3 example failure usages -->
159155
</testsuites>
160156
OUT;
161157

162-
self::assertSame($this->normalizeEol($expectedRegularOutput), $this->prettyPrintXml($regularOutput));
163-
self::assertSame($this->normalizeEol($expectedVerboseOutput), $this->prettyPrintXml($verboseOutput));
158+
self::assertSame($this->normalizeEol($expectedRegularOutput), $regularOutput);
159+
self::assertSame($this->normalizeEol($expectedVerboseOutput), $verboseOutput);
164160
// editorconfig-checker-enable
165161
}
166162

167-
private function prettyPrintXml(string $inputXml): string
168-
{
169-
$dom = new DOMDocument();
170-
$dom->preserveWhiteSpace = false;
171-
$dom->formatOutput = true;
172-
$dom->loadXML($inputXml);
173-
174-
$outputXml = $dom->saveXML(null, LIBXML_NOEMPTYTAG);
175-
self::assertNotFalse($outputXml);
176-
177-
return trim($outputXml);
178-
}
179-
180163
protected function createFormatter(Printer $printer): ResultFormatter
181164
{
182165
return new JunitFormatter('/app', $printer);

0 commit comments

Comments
 (0)