Merge "Selenium: replace UserLoginPage with BlankPage where possible"
[lhc/web/wiklou.git] / tests / phpunit / includes / parser / PreprocessorTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4
5 /**
6 * @covers Preprocessor
7 *
8 * @covers Preprocessor_DOM
9 * @covers PPDStack
10 * @covers PPDStackElement
11 * @covers PPDPart
12 * @covers PPFrame_DOM
13 * @covers PPTemplateFrame_DOM
14 * @covers PPCustomFrame_DOM
15 * @covers PPNode_DOM
16 *
17 * @covers Preprocessor_Hash
18 * @covers PPDStack_Hash
19 * @covers PPDStackElement_Hash
20 * @covers PPDPart_Hash
21 * @covers PPFrame_Hash
22 * @covers PPTemplateFrame_Hash
23 * @covers PPCustomFrame_Hash
24 * @covers PPNode_Hash_Tree
25 * @covers PPNode_Hash_Text
26 * @covers PPNode_Hash_Array
27 * @covers PPNode_Hash_Attr
28 */
29 class PreprocessorTest extends MediaWikiTestCase {
30 protected $mTitle = 'Page title';
31 protected $mPPNodeCount = 0;
32 /**
33 * @var ParserOptions
34 */
35 protected $mOptions;
36 /**
37 * @var array
38 */
39 protected $mPreprocessors;
40
41 protected static $classNames = [
42 Preprocessor_DOM::class,
43 Preprocessor_Hash::class
44 ];
45
46 protected function setUp() {
47 parent::setUp();
48 $this->mOptions = ParserOptions::newFromUserAndLang( new User,
49 MediaWikiServices::getInstance()->getContentLanguage() );
50
51 # Suppress deprecation warning for Preprocessor_DOM while testing
52 $this->hideDeprecated( 'Preprocessor_DOM::__construct' );
53
54 $this->mPreprocessors = [];
55 foreach ( self::$classNames as $className ) {
56 $this->mPreprocessors[$className] = new $className( $this );
57 }
58 }
59
60 function getStripList() {
61 return [ 'gallery', 'display map' /* Used by Maps, see r80025 CR */, '/foo' ];
62 }
63
64 protected static function addClassArg( $testCases ) {
65 $newTestCases = [];
66 foreach ( self::$classNames as $className ) {
67 foreach ( $testCases as $testCase ) {
68 array_unshift( $testCase, $className );
69 $newTestCases[] = $testCase;
70 }
71 }
72 return $newTestCases;
73 }
74
75 public static function provideCases() {
76 // phpcs:disable Generic.Files.LineLength
77 return self::addClassArg( [
78 [ "Foo", "<root>Foo</root>" ],
79 [ "<!-- Foo -->", "<root><comment>&lt;!-- Foo --&gt;</comment></root>" ],
80 [ "<!-- Foo --><!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment><comment>&lt;!-- Bar --&gt;</comment></root>" ],
81 [ "<!-- Foo --> <!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment> <comment>&lt;!-- Bar --&gt;</comment></root>" ],
82 [ "<!-- Foo --> \n <!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment> \n <comment>&lt;!-- Bar --&gt;</comment></root>" ],
83 [ "<!-- Foo --> \n <!-- Bar -->\n", "<root><comment>&lt;!-- Foo --&gt;</comment> \n<comment> &lt;!-- Bar --&gt;\n</comment></root>" ],
84 [ "<!-- Foo --> <!-- Bar -->\n", "<root><comment>&lt;!-- Foo --&gt;</comment> <comment>&lt;!-- Bar --&gt;</comment>\n</root>" ],
85 [ "<!-->Bar", "<root><comment>&lt;!--&gt;Bar</comment></root>" ],
86 [ "<!-- Comment -- comment", "<root><comment>&lt;!-- Comment -- comment</comment></root>" ],
87 [ "== Foo ==\n <!-- Bar -->\n== Baz ==\n", "<root><h level=\"2\" i=\"1\">== Foo ==</h>\n<comment> &lt;!-- Bar --&gt;\n</comment><h level=\"2\" i=\"2\">== Baz ==</h>\n</root>" ],
88 [ "<gallery/>", "<root><ext><name>gallery</name><attr></attr></ext></root>" ],
89 [ "Foo <gallery/> Bar", "<root>Foo <ext><name>gallery</name><attr></attr></ext> Bar</root>" ],
90 [ "<gallery></gallery>", "<root><ext><name>gallery</name><attr></attr><inner></inner><close>&lt;/gallery&gt;</close></ext></root>" ],
91 [ "<foo> <gallery></gallery>", "<root>&lt;foo&gt; <ext><name>gallery</name><attr></attr><inner></inner><close>&lt;/gallery&gt;</close></ext></root>" ],
92 [ "<foo> <gallery><gallery></gallery>", "<root>&lt;foo&gt; <ext><name>gallery</name><attr></attr><inner>&lt;gallery&gt;</inner><close>&lt;/gallery&gt;</close></ext></root>" ],
93 [ "<noinclude> Foo bar </noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore> Foo bar <ignore>&lt;/noinclude&gt;</ignore></root>" ],
94 [ "<noinclude>\n{{Foo}}\n</noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore></root>" ],
95 [ "<noinclude>\n{{Foo}}\n</noinclude>\n", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore>\n</root>" ],
96 [ "<gallery>foo bar", "<root>&lt;gallery&gt;foo bar</root>" ],
97 [ "<{{foo}}>", "<root>&lt;<template><title>foo</title></template>&gt;</root>" ],
98 [ "<{{{foo}}}>", "<root>&lt;<tplarg><title>foo</title></tplarg>&gt;</root>" ],
99 [ "<gallery></gallery</gallery>", "<root><ext><name>gallery</name><attr></attr><inner>&lt;/gallery</inner><close>&lt;/gallery&gt;</close></ext></root>" ],
100 [ "=== Foo === ", "<root><h level=\"3\" i=\"1\">=== Foo === </h></root>" ],
101 [ "==<!-- -->= Foo === ", "<root><h level=\"2\" i=\"1\">==<comment>&lt;!-- --&gt;</comment>= Foo === </h></root>" ],
102 [ "=== Foo ==<!-- -->= ", "<root><h level=\"1\" i=\"1\">=== Foo ==<comment>&lt;!-- --&gt;</comment>= </h></root>" ],
103 [ "=== Foo ===<!-- -->\n", "<root><h level=\"3\" i=\"1\">=== Foo ===<comment>&lt;!-- --&gt;</comment></h>\n</root>" ],
104 [ "=== Foo ===<!-- --> <!-- -->\n", "<root><h level=\"3\" i=\"1\">=== Foo ===<comment>&lt;!-- --&gt;</comment> <comment>&lt;!-- --&gt;</comment></h>\n</root>" ],
105 [ "== Foo ==\n== Bar == \n", "<root><h level=\"2\" i=\"1\">== Foo ==</h>\n<h level=\"2\" i=\"2\">== Bar == </h>\n</root>" ],
106 [ "===========", "<root><h level=\"5\" i=\"1\">===========</h></root>" ],
107 [ "Foo\n=\n==\n=\n", "<root>Foo\n=\n==\n=\n</root>" ],
108 [ "{{Foo}}", "<root><template><title>Foo</title></template></root>" ],
109 [ "\n{{Foo}}", "<root>\n<template lineStart=\"1\"><title>Foo</title></template></root>" ],
110 [ "{{Foo|bar}}", "<root><template><title>Foo</title><part><name index=\"1\" /><value>bar</value></part></template></root>" ],
111 [ "{{Foo|bar}}a", "<root><template><title>Foo</title><part><name index=\"1\" /><value>bar</value></part></template>a</root>" ],
112 [ "{{Foo|bar|baz}}", "<root><template><title>Foo</title><part><name index=\"1\" /><value>bar</value></part><part><name index=\"2\" /><value>baz</value></part></template></root>" ],
113 [ "{{Foo|1=bar}}", "<root><template><title>Foo</title><part><name>1</name>=<value>bar</value></part></template></root>" ],
114 [ "{{Foo|=bar}}", "<root><template><title>Foo</title><part><name></name>=<value>bar</value></part></template></root>" ],
115 [ "{{Foo|bar=baz}}", "<root><template><title>Foo</title><part><name>bar</name>=<value>baz</value></part></template></root>" ],
116 [ "{{Foo|{{bar}}=baz}}", "<root><template><title>Foo</title><part><name><template><title>bar</title></template></name>=<value>baz</value></part></template></root>" ],
117 [ "{{Foo|1=bar|baz}}", "<root><template><title>Foo</title><part><name>1</name>=<value>bar</value></part><part><name index=\"1\" /><value>baz</value></part></template></root>" ],
118 [ "{{Foo|1=bar|2=baz}}", "<root><template><title>Foo</title><part><name>1</name>=<value>bar</value></part><part><name>2</name>=<value>baz</value></part></template></root>" ],
119 [ "{{Foo|bar|foo=baz}}", "<root><template><title>Foo</title><part><name index=\"1\" /><value>bar</value></part><part><name>foo</name>=<value>baz</value></part></template></root>" ],
120 [ "{{{1}}}", "<root><tplarg><title>1</title></tplarg></root>" ],
121 [ "{{{1|}}}", "<root><tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg></root>" ],
122 [ "{{{Foo}}}", "<root><tplarg><title>Foo</title></tplarg></root>" ],
123 [ "{{{Foo|}}}", "<root><tplarg><title>Foo</title><part><name index=\"1\" /><value></value></part></tplarg></root>" ],
124 [ "{{{Foo|bar|baz}}}", "<root><tplarg><title>Foo</title><part><name index=\"1\" /><value>bar</value></part><part><name index=\"2\" /><value>baz</value></part></tplarg></root>" ],
125 [ "{<!-- -->{Foo}}", "<root>{<comment>&lt;!-- --&gt;</comment>{Foo}}</root>" ],
126 [ "{{{{Foobar}}}}", "<root>{<tplarg><title>Foobar</title></tplarg>}</root>" ],
127 [ "{{{ {{Foo}} }}}", "<root><tplarg><title> <template><title>Foo</title></template> </title></tplarg></root>" ],
128 [ "{{ {{{Foo}}} }}", "<root><template><title> <tplarg><title>Foo</title></tplarg> </title></template></root>" ],
129 [ "{{{{{Foo}}}}}", "<root><template><title><tplarg><title>Foo</title></tplarg></title></template></root>" ],
130 [ "{{{{{Foo}} }}}", "<root><tplarg><title><template><title>Foo</title></template> </title></tplarg></root>" ],
131 [ "{{{{{{Foo}}}}}}", "<root><tplarg><title><tplarg><title>Foo</title></tplarg></title></tplarg></root>" ],
132 [ "{{{{{{Foo}}}}}", "<root>{<template><title><tplarg><title>Foo</title></tplarg></title></template></root>" ],
133 [ "[[[Foo]]", "<root>[[[Foo]]</root>" ],
134 [ "{{Foo|[[[[bar]]|baz]]}}", "<root><template><title>Foo</title><part><name index=\"1\" /><value>[[[[bar]]|baz]]</value></part></template></root>" ], // This test is important, since it means the difference between having the [[ rule stacked or not
135 [ "{{Foo|[[[[bar]|baz]]}}", "<root>{{Foo|[[[[bar]|baz]]}}</root>" ],
136 [ "{{Foo|Foo [[[[bar]|baz]]}}", "<root>{{Foo|Foo [[[[bar]|baz]]}}</root>" ],
137 [ "Foo <display map>Bar</display map >Baz", "<root>Foo <ext><name>display map</name><attr></attr><inner>Bar</inner><close>&lt;/display map &gt;</close></ext>Baz</root>" ],
138 [ "Foo <display map foo>Bar</display map >Baz", "<root>Foo <ext><name>display map</name><attr> foo</attr><inner>Bar</inner><close>&lt;/display map &gt;</close></ext>Baz</root>" ],
139 [ "Foo <gallery bar=\"baz\" />", "<root>Foo <ext><name>gallery</name><attr> bar=&quot;baz&quot; </attr></ext></root>" ],
140 [ "Foo <gallery bar=\"1\" baz=2 />", "<root>Foo <ext><name>gallery</name><attr> bar=&quot;1&quot; baz=2 </attr></ext></root>" ],
141 [ "</foo>Foo<//foo>", "<root><ext><name>/foo</name><attr></attr><inner>Foo</inner><close>&lt;//foo&gt;</close></ext></root>" ], # Worth blacklisting IMHO
142 [ "{{#ifexpr: ({{{1|1}}} = 2) | Foo | Bar }}", "<root><template><title>#ifexpr: (<tplarg><title>1</title><part><name index=\"1\" /><value>1</value></part></tplarg> = 2) </title><part><name index=\"1\" /><value> Foo </value></part><part><name index=\"2\" /><value> Bar </value></part></template></root>" ],
143 [ "{{#if: {{{1|}}} | Foo | {{Bar}} }}", "<root><template><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> Foo </value></part><part><name index=\"2\" /><value> <template><title>Bar</title></template> </value></part></template></root>" ],
144 [ "{{#if: {{{1|}}} | Foo | [[Bar]] }}", "<root><template><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> Foo </value></part><part><name index=\"2\" /><value> [[Bar]] </value></part></template></root>" ],
145 [ "{{#if: {{{1|}}} | [[Foo]] | Bar }}", "<root><template><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> [[Foo]] </value></part><part><name index=\"2\" /><value> Bar </value></part></template></root>" ],
146 [ "{{#if: {{{1|}}} | 1 | {{#if: {{{1|}}} | 2 | 3 }} }}", "<root><template><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> 1 </value></part><part><name index=\"2\" /><value> <template><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> 2 </value></part><part><name index=\"2\" /><value> 3 </value></part></template> </value></part></template></root>" ],
147 [ "{{ {{Foo}}", "<root>{{ <template><title>Foo</title></template></root>" ],
148 [ "{{Foobar {{Foo}} {{Bar}} {{Baz}} ", "<root>{{Foobar <template><title>Foo</title></template> <template><title>Bar</title></template> <template><title>Baz</title></template> </root>" ],
149 [ "[[Foo]] |", "<root>[[Foo]] |</root>" ],
150 [ "{{Foo|Bar|", "<root>{{Foo|Bar|</root>" ],
151 [ "[[Foo]", "<root>[[Foo]</root>" ],
152 [ "[[Foo|Bar]", "<root>[[Foo|Bar]</root>" ],
153 [ "{{Foo| [[Bar] }}", "<root>{{Foo| [[Bar] }}</root>" ],
154 [ "{{Foo| [[Bar|Baz] }}", "<root>{{Foo| [[Bar|Baz] }}</root>" ],
155 [ "{{Foo|bar=[[baz]}}", "<root>{{Foo|bar=[[baz]}}</root>" ],
156 [ "{{foo|", "<root>{{foo|</root>" ],
157 [ "{{foo|}", "<root>{{foo|}</root>" ],
158 [ "{{foo|} }}", "<root><template><title>foo</title><part><name index=\"1\" /><value>} </value></part></template></root>" ],
159 [ "{{foo|bar=|}", "<root>{{foo|bar=|}</root>" ],
160 [ "{{Foo|} Bar=", "<root>{{Foo|} Bar=</root>" ],
161 [ "{{Foo|} Bar=}}", "<root><template><title>Foo</title><part><name>} Bar</name>=<value></value></part></template></root>" ],
162 /* [ file_get_contents( __DIR__ . '/QuoteQuran.txt' ], file_get_contents( __DIR__ . '/QuoteQuranExpanded.txt' ) ], */
163 ] );
164 // phpcs:enable
165 }
166
167 /**
168 * Get XML preprocessor tree from the preprocessor (which may not be the
169 * native XML-based one).
170 *
171 * @param string $className
172 * @param string $wikiText
173 * @return string
174 */
175 protected function preprocessToXml( $className, $wikiText ) {
176 $preprocessor = $this->mPreprocessors[$className];
177 if ( method_exists( $preprocessor, 'preprocessToXml' ) ) {
178 return $this->normalizeXml( $preprocessor->preprocessToXml( $wikiText ) );
179 }
180
181 $dom = $preprocessor->preprocessToObj( $wikiText );
182 if ( is_callable( [ $dom, 'saveXML' ] ) ) {
183 return $dom->saveXML();
184 } else {
185 return $this->normalizeXml( $dom->__toString() );
186 }
187 }
188
189 /**
190 * Normalize XML string to the form that a DOMDocument saves out.
191 *
192 * @param string $xml
193 * @return string
194 */
195 protected function normalizeXml( $xml ) {
196 // Normalize self-closing tags
197 $xml = preg_replace( '!<([a-z]+)/>!', '<$1></$1>', str_replace( ' />', '/>', $xml ) );
198 // Remove <equals> tags, which only occur in Preprocessor_Hash and
199 // have no semantic value
200 $xml = preg_replace( '!</?equals>!', '', $xml );
201 return $xml;
202 }
203
204 /**
205 * @dataProvider provideCases
206 */
207 public function testPreprocessorOutput( $className, $wikiText, $expectedXml ) {
208 $this->assertEquals( $this->normalizeXml( $expectedXml ),
209 $this->preprocessToXml( $className, $wikiText ) );
210 }
211
212 /**
213 * These are more complex test cases taken out of wiki articles.
214 */
215 public static function provideFiles() {
216 // phpcs:disable Generic.Files.LineLength
217 return self::addClassArg( [
218 [ "QuoteQuran" ], # https://en.wikipedia.org/w/index.php?title=Template:QuoteQuran/sandbox&oldid=237348988 GFDL + CC BY-SA by Striver
219 [ "Factorial" ], # https://en.wikipedia.org/w/index.php?title=Template:Factorial&oldid=98548758 GFDL + CC BY-SA by Polonium
220 [ "All_system_messages" ], # https://tl.wiktionary.org/w/index.php?title=Suleras:All_system_messages&oldid=2765 GPL text generated by MediaWiki
221 [ "Fundraising" ], # https://tl.wiktionary.org/w/index.php?title=MediaWiki:Sitenotice&oldid=5716 GFDL + CC BY-SA, copied there by Sky Harbor.
222 [ "NestedTemplates" ], # T29936
223 ] );
224 // phpcs:enable
225 }
226
227 /**
228 * @dataProvider provideFiles
229 */
230 public function testPreprocessorOutputFiles( $className, $filename ) {
231 $folder = __DIR__ . "/../../../parser/preprocess";
232 $wikiText = file_get_contents( "$folder/$filename.txt" );
233 $output = $this->preprocessToXml( $className, $wikiText );
234
235 $expectedFilename = "$folder/$filename.expected";
236 if ( file_exists( $expectedFilename ) ) {
237 $expectedXml = $this->normalizeXml( file_get_contents( $expectedFilename ) );
238 $this->assertEquals( $expectedXml, $output );
239 } else {
240 $tempFilename = tempnam( $folder, "$filename." );
241 file_put_contents( $tempFilename, $output );
242 $this->markTestIncomplete( "File $expectedFilename missing. Output stored as $tempFilename" );
243 }
244 }
245
246 /**
247 * Tests from T30642 ยท https://phabricator.wikimedia.org/T30642
248 */
249 public static function provideHeadings() {
250 // phpcs:disable Generic.Files.LineLength
251 return self::addClassArg( [
252 /* These should become headings: */
253 [ "== h ==<!--c1-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment></h></root>" ],
254 [ "== h == <!--c1-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment></h></root>" ],
255 [ "== h ==<!--c1--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> </h></root>" ],
256 [ "== h == <!--c1--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> </h></root>" ],
257 [ "== h ==<!--c1--><!--c2-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment></h></root>" ],
258 [ "== h == <!--c1--><!--c2-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment></h></root>" ],
259 [ "== h ==<!--c1--><!--c2--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> </h></root>" ],
260 [ "== h == <!--c1--><!--c2--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> </h></root>" ],
261 [ "== h == <!--c1--> <!--c2-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment></h></root>" ],
262 [ "== h ==<!--c1--> <!--c2--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> </h></root>" ],
263 [ "== h == <!--c1--> <!--c2--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> </h></root>" ],
264 [ "== h ==<!--c1--><!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ],
265 [ "== h ==<!--c1--> <!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ],
266 [ "== h ==<!--c1--><!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ],
267 [ "== h ==<!--c1--> <!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ],
268 [ "== h == <!--c1--><!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ],
269 [ "== h == <!--c1--> <!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ],
270 [ "== h == <!--c1--><!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ],
271 [ "== h == <!--c1--> <!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ],
272 [ "== h ==<!--c1--><!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ],
273 [ "== h ==<!--c1--> <!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ],
274 [ "== h ==<!--c1--><!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ],
275 [ "== h ==<!--c1--> <!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ],
276 [ "== h == <!--c1--><!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ],
277 [ "== h == <!--c1--> <!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ],
278 [ "== h == <!--c1--><!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ],
279 [ "== h == <!--c1--> <!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ],
280 [ "== h ==<!--c1--> <!--c2-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment></h></root>" ],
281 [ "== h == <!--c1--> <!--c2-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment></h></root>" ],
282 [ "== h ==<!--c1--> <!--c2--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> </h></root>" ],
283
284 /* These are not working: */
285 [ "== h == x <!--c1--><!--c2--><!--c3--> ", "<root>== h == x <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </root>" ],
286 [ "== h ==<!--c1--> x <!--c2--><!--c3--> ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment> x <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </root>" ],
287 [ "== h ==<!--c1--><!--c2--><!--c3--> x ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> x </root>" ],
288 ] );
289 // phpcs:enable
290 }
291
292 /**
293 * @dataProvider provideHeadings
294 */
295 public function testHeadings( $className, $wikiText, $expectedXml ) {
296 $this->assertEquals( $this->normalizeXml( $expectedXml ),
297 $this->preprocessToXml( $className, $wikiText ) );
298 }
299 }