mOptions = ParserOptions::newFromUserAndLang( new User, MediaWikiServices::getInstance()->getContentLanguage() ); $this->mPreprocessors = []; foreach ( self::$classNames as $className ) { $this->mPreprocessors[$className] = new $className( $this ); } } function getStripList() { return [ 'gallery', 'display map' /* Used by Maps, see r80025 CR */, '/foo' ]; } protected static function addClassArg( $testCases ) { $newTestCases = []; foreach ( self::$classNames as $className ) { foreach ( $testCases as $testCase ) { array_unshift( $testCase, $className ); $newTestCases[] = $testCase; } } return $newTestCases; } public static function provideCases() { // phpcs:disable Generic.Files.LineLength return self::addClassArg( [ [ "Foo", "Foo" ], [ "", "<!-- Foo -->" ], [ "", "<!-- Foo --><!-- Bar -->" ], [ " ", "<!-- Foo --> <!-- Bar -->" ], [ " \n ", "<!-- Foo --> \n <!-- Bar -->" ], [ " \n \n", "<!-- Foo --> \n <!-- Bar -->\n" ], [ " \n", "<!-- Foo --> <!-- Bar -->\n" ], [ "Bar", "<!-->Bar" ], [ "\n== Baz ==\n", "== Foo ==\n <!-- Bar -->\n== Baz ==\n" ], [ "", "gallery" ], [ "Foo Bar", "Foo gallery Bar" ], [ "", "gallery</gallery>" ], [ " ", "<foo> gallery</gallery>" ], [ " ", "<foo> gallery<gallery></gallery>" ], [ " Foo bar ", "<noinclude> Foo bar </noinclude>" ], [ "\n{{Foo}}\n", "<noinclude>\n\n</noinclude>" ], [ "\n{{Foo}}\n\n", "<noinclude>\n\n</noinclude>\n" ], [ "foo bar", "<gallery>foo bar" ], [ "<{{foo}}>", "<>" ], [ "<{{{foo}}}>", "<foo>" ], [ "", "gallery</gallery</gallery>" ], [ "=== Foo === ", "=== Foo === " ], [ "=== Foo === ", "==<!-- -->= Foo === " ], [ "=== Foo === ", "=== Foo ==<!-- -->= " ], [ "=== Foo ===\n", "=== Foo ===<!-- -->\n" ], [ "=== Foo === \n", "=== Foo ===<!-- --> <!-- -->\n" ], [ "== Foo ==\n== Bar == \n", "== Foo ==\n== Bar == \n" ], [ "===========", "===========" ], [ "Foo\n=\n==\n=\n", "Foo\n=\n==\n=\n" ], [ "{{Foo}}", "" ], [ "\n{{Foo}}", "\n" ], [ "{{Foo|bar}}", "" ], [ "{{Foo|bar}}a", "a" ], [ "{{Foo|bar|baz}}", "" ], [ "{{Foo|1=bar}}", "" ], [ "{{Foo|=bar}}", "" ], [ "{{Foo|bar=baz}}", "" ], [ "{{Foo|{{bar}}=baz}}", "" ], [ "{{Foo|1=bar|baz}}", "" ], [ "{{Foo|1=bar|2=baz}}", "" ], [ "{{Foo|bar|foo=baz}}", "" ], [ "{{{1}}}", "1" ], [ "{{{1|}}}", "1" ], [ "{{{Foo}}}", "Foo" ], [ "{{{Foo|}}}", "Foo" ], [ "{{{Foo|bar|baz}}}", "Foobarbaz" ], [ "{{Foo}}", "{<!-- -->{Foo}}" ], [ "{{{{Foobar}}}}", "{Foobar}" ], [ "{{{ {{Foo}} }}}", " <template><title>Foo " ], [ "{{ {{{Foo}}} }}", "" ], [ "{{{{{Foo}}}}}", "" ], [ "{{{{{Foo}} }}}", "<template><title>Foo " ], [ "{{{{{{Foo}}}}}}", "<tplarg><title>Foo" ], [ "{{{{{{Foo}}}}}", "{" ], [ "[[[Foo]]", "[[[Foo]]" ], [ "{{Foo|[[[[bar]]|baz]]}}", "" ], // This test is important, since it means the difference between having the [[ rule stacked or not [ "{{Foo|[[[[bar]|baz]]}}", "{{Foo|[[[[bar]|baz]]}}" ], [ "{{Foo|Foo [[[[bar]|baz]]}}", "{{Foo|Foo [[[[bar]|baz]]}}" ], [ "Foo BarBaz", "Foo display mapBar</display map >Baz" ], [ "Foo BarBaz", "Foo display map fooBar</display map >Baz" ], [ "Foo ", "Foo gallery bar="baz" " ], [ "Foo ", "Foo gallery bar="1" baz=2 " ], [ "Foo", "/fooFoo<//foo>" ], # Worth blacklisting IMHO [ "{{#ifexpr: ({{{1|1}}} = 2) | Foo | Bar }}", "" ], [ "{{#if: {{{1|}}} | Foo | {{Bar}} }}", "" ], [ "{{#if: {{{1|}}} | Foo | [[Bar]] }}", "" ], [ "{{#if: {{{1|}}} | [[Foo]] | Bar }}", "" ], [ "{{#if: {{{1|}}} | 1 | {{#if: {{{1|}}} | 2 | 3 }} }}", "" ], [ "{{ {{Foo}}", "{{ " ], [ "{{Foobar {{Foo}} {{Bar}} {{Baz}} ", "{{Foobar " ], [ "[[Foo]] |", "[[Foo]] |" ], [ "{{Foo|Bar|", "{{Foo|Bar|" ], [ "[[Foo]", "[[Foo]" ], [ "[[Foo|Bar]", "[[Foo|Bar]" ], [ "{{Foo| [[Bar] }}", "{{Foo| [[Bar] }}" ], [ "{{Foo| [[Bar|Baz] }}", "{{Foo| [[Bar|Baz] }}" ], [ "{{Foo|bar=[[baz]}}", "{{Foo|bar=[[baz]}}" ], [ "{{foo|", "{{foo|" ], [ "{{foo|}", "{{foo|}" ], [ "{{foo|} }}", "" ], [ "{{foo|bar=|}", "{{foo|bar=|}" ], [ "{{Foo|} Bar=", "{{Foo|} Bar=" ], [ "{{Foo|} Bar=}}", "" ], /* [ file_get_contents( __DIR__ . '/QuoteQuran.txt' ], file_get_contents( __DIR__ . '/QuoteQuranExpanded.txt' ) ], */ ] ); // phpcs:enable } /** * Get XML preprocessor tree from the preprocessor (which may not be the * native XML-based one). * * @param string $className * @param string $wikiText * @return string */ protected function preprocessToXml( $className, $wikiText ) { $preprocessor = $this->mPreprocessors[$className]; if ( method_exists( $preprocessor, 'preprocessToXml' ) ) { return $this->normalizeXml( $preprocessor->preprocessToXml( $wikiText ) ); } $dom = $preprocessor->preprocessToObj( $wikiText ); if ( is_callable( [ $dom, 'saveXML' ] ) ) { return $dom->saveXML(); } else { return $this->normalizeXml( $dom->__toString() ); } } /** * Normalize XML string to the form that a DOMDocument saves out. * * @param string $xml * @return string */ protected function normalizeXml( $xml ) { // Normalize self-closing tags $xml = preg_replace( '!<([a-z]+)/>!', '<$1>', str_replace( ' />', '/>', $xml ) ); // Remove tags, which only occur in Preprocessor_Hash and // have no semantic value $xml = preg_replace( '!!', '', $xml ); return $xml; } /** * @dataProvider provideCases */ public function testPreprocessorOutput( $className, $wikiText, $expectedXml ) { $this->assertEquals( $this->normalizeXml( $expectedXml ), $this->preprocessToXml( $className, $wikiText ) ); } /** * These are more complex test cases taken out of wiki articles. */ public static function provideFiles() { // phpcs:disable Generic.Files.LineLength return self::addClassArg( [ [ "QuoteQuran" ], # https://en.wikipedia.org/w/index.php?title=Template:QuoteQuran/sandbox&oldid=237348988 GFDL + CC BY-SA by Striver [ "Factorial" ], # https://en.wikipedia.org/w/index.php?title=Template:Factorial&oldid=98548758 GFDL + CC BY-SA by Polonium [ "All_system_messages" ], # https://tl.wiktionary.org/w/index.php?title=Suleras:All_system_messages&oldid=2765 GPL text generated by MediaWiki [ "Fundraising" ], # https://tl.wiktionary.org/w/index.php?title=MediaWiki:Sitenotice&oldid=5716 GFDL + CC BY-SA, copied there by Sky Harbor. [ "NestedTemplates" ], # T29936 ] ); // phpcs:enable } /** * @dataProvider provideFiles */ public function testPreprocessorOutputFiles( $className, $filename ) { $folder = __DIR__ . "/../../../parser/preprocess"; $wikiText = file_get_contents( "$folder/$filename.txt" ); $output = $this->preprocessToXml( $className, $wikiText ); $expectedFilename = "$folder/$filename.expected"; if ( file_exists( $expectedFilename ) ) { $expectedXml = $this->normalizeXml( file_get_contents( $expectedFilename ) ); $this->assertEquals( $expectedXml, $output ); } else { $tempFilename = tempnam( $folder, "$filename." ); file_put_contents( $tempFilename, $output ); $this->markTestIncomplete( "File $expectedFilename missing. Output stored as $tempFilename" ); } } /** * Tests from T30642 ยท https://phabricator.wikimedia.org/T30642 */ public static function provideHeadings() { // phpcs:disable Generic.Files.LineLength return self::addClassArg( [ /* These should become headings: */ [ "== h ==", "== h ==<!--c1-->" ], [ "== h == ", "== h == <!--c1-->" ], [ "== h == ", "== h ==<!--c1--> " ], [ "== h == ", "== h == <!--c1--> " ], [ "== h ==", "== h ==<!--c1--><!--c2-->" ], [ "== h == ", "== h == <!--c1--><!--c2-->" ], [ "== h == ", "== h ==<!--c1--><!--c2--> " ], [ "== h == ", "== h == <!--c1--><!--c2--> " ], [ "== h == ", "== h == <!--c1--> <!--c2-->" ], [ "== h == ", "== h ==<!--c1--> <!--c2--> " ], [ "== h == ", "== h == <!--c1--> <!--c2--> " ], [ "== h ==", "== h ==<!--c1--><!--c2--><!--c3-->" ], [ "== h == ", "== h ==<!--c1--> <!--c2--><!--c3-->" ], [ "== h == ", "== h ==<!--c1--><!--c2--> <!--c3-->" ], [ "== h == ", "== h ==<!--c1--> <!--c2--> <!--c3-->" ], [ "== h == ", "== h == <!--c1--><!--c2--><!--c3-->" ], [ "== h == ", "== h == <!--c1--> <!--c2--><!--c3-->" ], [ "== h == ", "== h == <!--c1--><!--c2--> <!--c3-->" ], [ "== h == ", "== h == <!--c1--> <!--c2--> <!--c3-->" ], [ "== h == ", "== h ==<!--c1--><!--c2--><!--c3--> " ], [ "== h == ", "== h ==<!--c1--> <!--c2--><!--c3--> " ], [ "== h == ", "== h ==<!--c1--><!--c2--> <!--c3--> " ], [ "== h == ", "== h ==<!--c1--> <!--c2--> <!--c3--> " ], [ "== h == ", "== h == <!--c1--><!--c2--><!--c3--> " ], [ "== h == ", "== h == <!--c1--> <!--c2--><!--c3--> " ], [ "== h == ", "== h == <!--c1--><!--c2--> <!--c3--> " ], [ "== h == ", "== h == <!--c1--> <!--c2--> <!--c3--> " ], [ "== h == ", "== h ==<!--c1--> <!--c2-->" ], [ "== h == ", "== h == <!--c1--> <!--c2-->" ], [ "== h == ", "== h ==<!--c1--> <!--c2--> " ], /* These are not working: */ [ "== h == x ", "== h == x <!--c1--><!--c2--><!--c3--> " ], [ "== h == x ", "== h ==<!--c1--> x <!--c2--><!--c3--> " ], [ "== h == x ", "== h ==<!--c1--><!--c2--><!--c3--> x " ], ] ); // phpcs:enable } /** * @dataProvider provideHeadings */ public function testHeadings( $className, $wikiText, $expectedXml ) { $this->assertEquals( $this->normalizeXml( $expectedXml ), $this->preprocessToXml( $className, $wikiText ) ); } }