99 */
1010namespace SebastianBergmann \CodeCoverage \Report ;
1111
12+ use const PHP_EOL ;
13+ use function libxml_clear_errors ;
14+ use function libxml_get_errors ;
15+ use function libxml_use_internal_errors ;
16+ use function sprintf ;
17+ use function str_replace ;
18+ use function trim ;
19+ use DOMDocument ;
1220use PHPUnit \Framework \Attributes \CoversClass ;
1321use SebastianBergmann \CodeCoverage \TestCase ;
1422use SebastianBergmann \CodeCoverage \Util \Xml ;
@@ -21,7 +29,7 @@ public function testLineCoverageForBankAccountTest(): void
2129 {
2230 $ cobertura = new Cobertura ;
2331
24- $ this ->assertStringMatchesFormatFile (
32+ $ this ->assertAndValidate (
2533 TEST_FILES_PATH . 'Report/Cobertura/BankAccount-line.xml ' ,
2634 $ cobertura ->process ($ this ->getLineCoverageForBankAccount ()->getReport (), null ),
2735 );
@@ -31,7 +39,7 @@ public function testPathCoverageForBankAccountTest(): void
3139 {
3240 $ cobertura = new Cobertura ;
3341
34- $ this ->assertStringMatchesFormatFile (
42+ $ this ->assertAndValidate (
3543 TEST_FILES_PATH . 'Report/Cobertura/BankAccount-path.xml ' ,
3644 $ cobertura ->process ($ this ->getPathCoverageForBankAccount ()->getReport (), null ),
3745 );
@@ -41,7 +49,7 @@ public function testCoberturaForFileWithIgnoredLines(): void
4149 {
4250 $ cobertura = new Cobertura ;
4351
44- $ this ->assertStringMatchesFormatFile (
52+ $ this ->assertAndValidate (
4553 TEST_FILES_PATH . 'Report/Cobertura/ignored-lines.xml ' ,
4654 $ cobertura ->process ($ this ->getCoverageForFileWithIgnoredLines ()->getReport ()),
4755 );
@@ -51,7 +59,7 @@ public function testCoberturaForClassWithAnonymousFunction(): void
5159 {
5260 $ cobertura = new Cobertura ;
5361
54- $ this ->assertStringMatchesFormatFile (
62+ $ this ->assertAndValidate (
5563 TEST_FILES_PATH . 'Report/Cobertura/class-with-anonymous-function.xml ' ,
5664 $ cobertura ->process ($ this ->getCoverageForClassWithAnonymousFunction ()->getReport ()),
5765 );
@@ -61,9 +69,49 @@ public function testCoberturaForClassAndOutsideFunction(): void
6169 {
6270 $ cobertura = new Cobertura ;
6371
64- $ this ->assertStringMatchesFormatFile (
72+ $ this ->assertAndValidate (
6573 TEST_FILES_PATH . 'Report/Cobertura/class-with-outside-function.xml ' ,
6674 $ cobertura ->process ($ this ->getCoverageForClassWithOutsideFunction ()->getReport ()),
6775 );
6876 }
77+
78+ /**
79+ * @param non-empty-string $expectationFile
80+ * @param non-empty-string $coberturaXml
81+ */
82+ private function assertAndValidate (string $ expectationFile , string $ coberturaXml ): void
83+ {
84+ $ this ->assertStringMatchesFormatFile ($ expectationFile , $ coberturaXml );
85+
86+ $ coberturaXml = str_replace (
87+ '<?xml version="1.0" encoding="UTF-8"?> ' ,
88+ '<?xml version="1.0" encoding="UTF-8"?> ' . "\n" . '<!DOCTYPE coverage SYSTEM " ' . __DIR__ . '/../../_files/Report/Cobertura/coverage-04.dtd"> ' ,
89+ $ coberturaXml ,
90+ );
91+
92+ libxml_use_internal_errors (true );
93+
94+ $ document = new DOMDocument ;
95+ $ document ->loadXML ($ coberturaXml );
96+
97+ if (!$ document ->validate ()) {
98+ $ buffer = 'Generated XML document does not validate against Cobertura DTD: ' . PHP_EOL . PHP_EOL ;
99+
100+ foreach (libxml_get_errors () as $ error ) {
101+ $ buffer .= sprintf (
102+ '- Line %d: %s ' . PHP_EOL ,
103+ $ error ->line ,
104+ trim ($ error ->message ),
105+ );
106+ }
107+
108+ $ buffer .= PHP_EOL ;
109+ }
110+
111+ libxml_clear_errors ();
112+
113+ if (isset ($ buffer )) {
114+ $ this ->fail ($ buffer );
115+ }
116+ }
69117}
0 commit comments