r82867 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r82866‎ | r82867 | r82868 >
Date:21:49, 26 February 2011
Author:platonides
Status:ok
Tags:
Comment:
Finally commit the testing stuff I have been with this week. Could be considered a continuation of r79411.
Mixture of the NewParserTests.php with old code present in MediaWikiParserTest.php, reverting r79184 (revert of a revert...) and even eval().
Using the iterator as dataProvider, but defering the insertion of articles, as the dataprovider is processed on load, before switching dbs.
Each parser test appear now as a phpunit test. This means that by adding 683 tests (partly from extensions) we now surpass two thousand tests.
The downside of that is that they become painfully slow.
Initialise $wgHooks to $wgHooks, not to array() (r82499) as otherwise we lose ParserFirstCallInit registrations.
There's no attempt to support extensions registering to $wgParser instead of using ParserFirstCallInit properly (Cite falls in this category).
Modified paths:
  • /trunk/phase3/tests/phpunit/MediaWikiTestCase.php (modified) (history)
  • /trunk/phase3/tests/phpunit/includes/parser/MediaWikiParserTest.php (modified) (history)
  • /trunk/phase3/tests/phpunit/includes/parser/NewParserHelpers.php (modified) (history)
  • /trunk/phase3/tests/phpunit/includes/parser/NewParserTest.php (modified) (history)

Diff [purge]

Index: trunk/phase3/tests/phpunit/MediaWikiTestCase.php
@@ -30,7 +30,13 @@
3131 }
3232
3333 function run( PHPUnit_Framework_TestResult $result = NULL ) {
34 -
 34+ global $wgCaches;
 35+ /* Some functions require some kind of caching, and will end up using the db,
 36+ * which we can't allow, as that would open a new connection for mysql.
 37+ * Replace with a HashBag. They would not be going to persist anyway.
 38+ */
 39+ $wgCaches[CACHE_DB] = new HashBagOStuff;
 40+
3541 if( $this->needsDB() ) {
3642
3743 global $wgDBprefix;
Index: trunk/phase3/tests/phpunit/includes/parser/NewParserHelpers.php
@@ -169,11 +169,11 @@
170170 }
171171
172172 $this->test = array(
173 - 'test' => $this->parserTest->removeEndingNewline( $data['test'] ),
174 - 'input' => $this->parserTest->removeEndingNewline( $data['input'] ),
175 - 'result' => $this->parserTest->removeEndingNewline( $data['result'] ),
176 - 'options' => $this->parserTest->removeEndingNewline( $data['options'] ),
177 - 'config' => $this->parserTest->removeEndingNewline( $data['config'] ) );
 173+ $this->parserTest->removeEndingNewline( $data['test'] ),
 174+ $this->parserTest->removeEndingNewline( $data['input'] ),
 175+ $this->parserTest->removeEndingNewline( $data['result'] ),
 176+ $this->parserTest->removeEndingNewline( $data['options'] ),
 177+ $this->parserTest->removeEndingNewline( $data['config'] ) );
178178
179179 return true;
180180 }
Index: trunk/phase3/tests/phpunit/includes/parser/MediaWikiParserTest.php
@@ -1,66 +1,35 @@
22 <?php
33
44 require_once( dirname( __FILE__ ) . '/ParserHelpers.php' );
 5+require_once( dirname( __FILE__ ) . '/NewParserTest.php' );
56 require_once( dirname(dirname(dirname( __FILE__ ))) . '/bootstrap.php' );
67
78 /**
 9+ * The UnitTest must be either a class that inherits from PHPUnit_Framework_TestCase
 10+ * or a class that provides a public static suite() method which returns
 11+ * an PHPUnit_Framework_Test object
 12+ *
813 * @group Parser
9 - * @group Destructive
1014 * @group Database
11 - * @group Broken
12 - * It's not really broken, but superseded
1315 */
14 -class MediaWikiParserTest extends MediaWikiTestCase {
15 - public $count; // Number of tests in the suite.
16 - public $articles = array(); // Array of test articles defined by the tests
17 - protected $pt;
18 -
19 - function setUp() {
20 - global $wgContLang;
21 - $wgContLang = Language::factory( 'en' );
22 -
23 - $this->pt = new PHPUnitParserTest;
24 - $this->pt->setupDatabase();
25 -
26 - }
27 -
28 - function tearDown() {
29 - if( is_object( $this->pt ) && $this->pt instanceof PHPUnitParserTest ) {
30 - $this->pt->teardownDatabase();
31 - $this->pt = null;
32 - }
33 - }
 16+class MediaWikiParserTest {
3417
35 -
36 - public function testParserTests() {
37 - //global $IP;
38 - //$wgParserTestFiles = array( "$IP/tests/parser/testparserTests.txt" );
39 -
40 - global $wgParserTestFiles;
41 -
42 - foreach( $wgParserTestFiles as $file ) {
 18+ public static function suite() {
 19+ global $IP, $wgParserTestFiles;
 20+
 21+ $suite = new PHPUnit_Framework_TestSuite;
 22+
 23+ foreach ( $wgParserTestFiles as $filename ) {
 24+ $testsName = basename( $filename, '.txt' );
 25+ $className = /*ucfirst( basename( dirname( $filename ) ) ) .*/ ucfirst( basename( $filename, '.txt' ) );
4326
44 - $iter = new TestFileIterator( $file, $this->pt );
45 -
46 - try {
47 - foreach( $iter as $test ) {
48 - $r = $this->pt->runTest( $test['test'], $test['input'],
49 - $test['result'], $test['options'], $test['config']
50 - );
51 -
52 - $this->assertTrue( $r, 'Parser test ' . $test['test'] );
53 -
54 - }
55 - }
56 - catch( DBQueryError $e ) {
57 - $this->assertTrue( false, 'Parser test ' . $test['test'] . ' (error: "' . $e->getMessage() . '")' );
58 - //This is annoying... it always stops on error and doesn't go to the next one.
59 - continue;
60 - }
61 -
 27+ eval( "/** @group Database\n@group Parser\n*/ class $className extends NewParserTest { protected \$file = \"" . addslashes( $filename ) . "\"; } " );
 28+
 29+ $parserTester = new $className( $testsName );
 30+ $suite->addTestSuite( new ReflectionClass ( $parserTester ) );
6231 }
6332
64 - }
6533
 34+ return $suite;
 35+ }
6636 }
67 -
Index: trunk/phase3/tests/phpunit/includes/parser/NewParserTest.php
@@ -2,9 +2,16 @@
33
44 /**
55 * @group Database
 6+ * @group Parser
 7+ * @group Stub (can also work independently)
68 */
79 class NewParserTest extends MediaWikiTestCase {
810
 11+ static protected $articles = array(); // Array of test articles defined by the tests
 12+ /* The dataProvider is run on a different instance than the test, so it must be static
 13+ * When running tests from several files, all tests will see all articles.
 14+ */
 15+
916 public $uploadDir;
1017 public $keepUploads = false;
1118 public $runDisabled = false;
@@ -21,10 +28,15 @@
2229 public $fuzzSeed = 0;
2330 public $memoryLimit = 50;
2431
25 - //PHPUnit + MediaWikiTestCase functions
26 -
 32+ protected $file = false;
 33+
 34+ /*function __construct($a = null,$b = array(),$c = null ) {
 35+ parent::__construct($a,$b,$c);
 36+ }*/
 37+
2738 function setUp() {
28 - global $wgContLang, $wgNamespaceProtection, $wgNamespaceAliases, $IP;
 39+ global $wgContLang, $wgNamespaceProtection, $wgNamespaceAliases;
 40+ global $wgHooks, $IP;
2941 $wgContLang = Language::factory( 'en' );
3042
3143 //Setup CLI arguments
@@ -56,7 +68,7 @@
5769 );
5870
5971 $tmpGlobals['wgEnableParserCache'] = false;
60 - $tmpGlobals['wgHooks'] = array();
 72+ $tmpGlobals['wgHooks'] = $wgHooks;
6173 $tmpGlobals['wgDeferredUpdateList'] = array();
6274 $tmpGlobals['wgMemc'] = &wfGetMainCache();
6375 $tmpGlobals['messageMemc'] = &wfGetMessageCacheStorage();
@@ -152,8 +164,9 @@
153165 MessageCache::singleton()->clear();
154166
155167 $this->uploadDir = $this->setupUploadDir();
156 -
 168+
157169 $user = User::newFromId( 0 );
 170+ LinkCache::singleton()->clear(); # Avoids the odd failure at creating the nullRevision
158171
159172 $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
160173 $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array(
@@ -258,7 +271,8 @@
259272 'wgHtml5' => true,
260273 'wgWellFormedXml' => true,
261274 'wgAllowMicrodataAttributes' => true,
262 - 'wgAdaptiveMessageCache' => true
 275+ 'wgAdaptiveMessageCache' => true,
 276+ 'wgUseDatabaseMessages' => true,
263277 );
264278
265279 if ( $config ) {
@@ -295,6 +309,13 @@
296310
297311 MagicWord::clearCache();
298312
 313+ # Publish the articles after we have the final language set
 314+ $this->publishTestArticles();
 315+
 316+ # The entries saved into RepoGroup cache with previous globals will be wrong.
 317+ RepoGroup::destroySingleton();
 318+ MessageCache::singleton()->destroyInstance();
 319+
299320 global $wgUser;
300321 $wgUser = new User();
301322 }
@@ -345,115 +366,93 @@
346367 return $dir;
347368 }
348369
 370+ public function parserTestProvider() {
 371+ if ( $this->file === false ) {
 372+ global $wgParserTestFiles;
 373+ $this->file = $wgParserTestFiles[0];
 374+ }
 375+ return new ParserTestFileIterator( $this->file, $this );
 376+ }
349377
 378+ /**
 379+ * Set the file from whose tests will be run by this instance
 380+ */
 381+ public function setParserTestFile( $filename ) {
 382+ $this->file = $filename;
 383+ }
350384
351 -
352 -
353 - //Actual test suites
 385+ /** @dataProvider parserTestProvider */
 386+ public function testParserTest( $desc, $input, $result, $opts, $config ) {
 387+ if ( !preg_match( '/' . $this->regex . '/', $desc ) ) return; //$this->markTestSkipped( 'Filtered out by the user' );
 388+ $opts = $this->parseOptions( $opts );
 389+ $this->setupGlobals( $opts, $config );
354390
355 - public function testParserTests() {
356 -
357 - global $wgParserTestFiles;
358 -
359 - $files = $wgParserTestFiles;
360 -
361 - if( $this->getCliArg( 'file=' ) ) {
362 - $files = array( $this->getCliArg( 'file=' ) );
 391+ $user = new User();
 392+ $options = ParserOptions::newFromUser( $user );
 393+
 394+ if ( isset( $opts['title'] ) ) {
 395+ $titleText = $opts['title'];
363396 }
 397+ else {
 398+ $titleText = 'Parser test';
 399+ }
 400+
 401+ $local = isset( $opts['local'] );
 402+ $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
 403+ $parser = $this->getParser( $preprocessor );
364404
365 - foreach( $files as $file ) {
366 -
367 - $iter = new ParserTestFileIterator( $file, $this );
368 -
369 - foreach ( $iter as $t ) {
370 -
371 - try {
372 -
373 - $desc = $t['test'];
374 - $input = $t['input'];
375 - $result = $t['result'];
376 - $opts = $t['options'];
377 - $config = $t['config'];
378 -
379 -
380 - $opts = $this->parseOptions( $opts );
381 - $this->setupGlobals( $opts, $config );
382 -
383 - $user = new User();
384 - $options = ParserOptions::newFromUser( $user );
385 -
386 - if ( isset( $opts['title'] ) ) {
387 - $titleText = $opts['title'];
388 - }
389 - else {
390 - $titleText = 'Parser test';
391 - }
392 -
393 - $local = isset( $opts['local'] );
394 - $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
395 - $parser = $this->getParser( $preprocessor );
396 - $title = Title::newFromText( $titleText );
397 -
398 - if ( isset( $opts['pst'] ) ) {
399 - $out = $parser->preSaveTransform( $input, $title, $user, $options );
400 - } elseif ( isset( $opts['msg'] ) ) {
401 - $out = $parser->transformMsg( $input, $options );
402 - } elseif ( isset( $opts['section'] ) ) {
403 - $section = $opts['section'];
404 - $out = $parser->getSection( $input, $section );
405 - } elseif ( isset( $opts['replace'] ) ) {
406 - $section = $opts['replace'][0];
407 - $replace = $opts['replace'][1];
408 - $out = $parser->replaceSection( $input, $section, $replace );
409 - } elseif ( isset( $opts['comment'] ) ) {
410 - $linker = $user->getSkin();
411 - $out = $linker->formatComment( $input, $title, $local );
412 - } elseif ( isset( $opts['preload'] ) ) {
413 - $out = $parser->getpreloadText( $input, $title, $options );
414 - } else {
415 - $output = $parser->parse( $input, $title, $options, true, true, 1337 );
416 - $out = $output->getText();
417 -
418 - if ( isset( $opts['showtitle'] ) ) {
419 - if ( $output->getTitleText() ) {
420 - $title = $output->getTitleText();
421 - }
422 -
423 - $out = "$title\n$out";
424 - }
425 -
426 - if ( isset( $opts['ill'] ) ) {
427 - $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) );
428 - } elseif ( isset( $opts['cat'] ) ) {
429 - global $wgOut;
430 -
431 - $wgOut->addCategoryLinks( $output->getCategories() );
432 - $cats = $wgOut->getCategoryLinks();
433 -
434 - if ( isset( $cats['normal'] ) ) {
435 - $out = $this->tidy( implode( ' ', $cats['normal'] ) );
436 - } else {
437 - $out = '';
438 - }
439 - }
440 - $parser->mPreprocessor = null;
441 -
442 - $result = $this->tidy( $result );
443 - }
444 -
445 - $this->teardownGlobals();
446 -
447 - $this->assertEquals( $result, $out, $desc );
448 -
 405+ $title = Title::newFromText( $titleText );
 406+
 407+ if ( isset( $opts['pst'] ) ) {
 408+ $out = $parser->preSaveTransform( $input, $title, $user, $options );
 409+ } elseif ( isset( $opts['msg'] ) ) {
 410+ $out = $parser->transformMsg( $input, $options );
 411+ } elseif ( isset( $opts['section'] ) ) {
 412+ $section = $opts['section'];
 413+ $out = $parser->getSection( $input, $section );
 414+ } elseif ( isset( $opts['replace'] ) ) {
 415+ $section = $opts['replace'][0];
 416+ $replace = $opts['replace'][1];
 417+ $out = $parser->replaceSection( $input, $section, $replace );
 418+ } elseif ( isset( $opts['comment'] ) ) {
 419+ $linker = $user->getSkin();
 420+ $out = $linker->formatComment( $input, $title, $local );
 421+ } elseif ( isset( $opts['preload'] ) ) {
 422+ $out = $parser->getpreloadText( $input, $title, $options );
 423+ } else {
 424+ $output = $parser->parse( $input, $title, $options, true, true, 1337 );
 425+ $out = $output->getText();
 426+
 427+ if ( isset( $opts['showtitle'] ) ) {
 428+ if ( $output->getTitleText() ) {
 429+ $title = $output->getTitleText();
449430 }
450 - catch( Exception $e ) {
451 - $this->assertTrue( false, $t['test'] . ' (failed: ' . $e->getMessage() . ')' );
 431+
 432+ $out = "$title\n$out";
 433+ }
 434+
 435+ if ( isset( $opts['ill'] ) ) {
 436+ $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) );
 437+ } elseif ( isset( $opts['cat'] ) ) {
 438+ global $wgOut;
 439+
 440+ $wgOut->addCategoryLinks( $output->getCategories() );
 441+ $cats = $wgOut->getCategoryLinks();
 442+
 443+ if ( isset( $cats['normal'] ) ) {
 444+ $out = $this->tidy( implode( ' ', $cats['normal'] ) );
 445+ } else {
 446+ $out = '';
452447 }
453 -
454448 }
455 -
 449+ $parser->mPreprocessor = null;
 450+
 451+ $result = $this->tidy( $result );
456452 }
457 -
 453+
 454+ $this->teardownGlobals();
 455+
 456+ $this->assertEquals( $result, $out, $desc );
458457 }
459458
460459 /**
@@ -593,59 +592,34 @@
594593 * Get a Parser object
595594 */
596595 function getParser( $preprocessor = null ) {
597 - global $wgParserConf;
 596+ global $wgParserConf, $wgHooks;
598597
599598 $class = $wgParserConf['class'];
600599 $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf );
601600
602 - foreach ( $this->hooks as $tag => $callback ) {
603 - $parser->setHook( $tag, $callback );
604 - }
605 -
606 - foreach ( $this->functionHooks as $tag => $bits ) {
607 - list( $callback, $flags ) = $bits;
608 - $parser->setFunctionHook( $tag, $callback, $flags );
609 - }
610 -
611601 wfRunHooks( 'ParserTestParser', array( &$parser ) );
612602
613603 return $parser;
614604 }
615605
616606 //Various action functions
617 -
618 - /**
619 - * Insert a temporary test article
620 - * @param $name String: the title, including any prefix
621 - * @param $text String: the article text
622 - * @param $line Integer: the input line number, for reporting errors
623 - */
624 - public function addArticle( $name, $text, $line = 'unknown' ) {
625 - global $wgCapitalLinks;
626607
627 - $text = $this->removeEndingNewline($text);
628 -
629 - $oldCapitalLinks = $wgCapitalLinks;
630 - $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
631 -
632 - $name = $this->removeEndingNewline( $name );
633 - $title = Title::newFromText( $name );
634 -
635 - if ( is_null( $title ) ) {
636 - throw new MWException( "invalid title ('$name' => '$title') at line $line\n" );
 608+ public function addArticle( $name, $text, $line ) {
 609+ self::$articles[$name] = $text;
 610+ }
 611+
 612+ public function publishTestArticles() {
 613+ if ( empty( self::$articles ) ) {
 614+ return;
637615 }
638616
639 - $aid = $title->getArticleID( Title::GAID_FOR_UPDATE );
 617+ foreach ( self::$articles as $name => $text ) {
 618+ $title = Title::newFromText( $name );
640619
641 - if ( $aid != 0 ) {
642 - debug_print_backtrace();
643 - throw new MWException( "duplicate article '$name' at line $line\n" );
 620+ if ( $title->getArticleID( Title::GAID_FOR_UPDATE ) == 0 ) {
 621+ ParserTest::addArticle( $name, $text );
 622+ }
644623 }
645 -
646 - $art = new Article( $title );
647 - $art->doEdit( $text, '', EDIT_NEW );
648 -
649 - $wgCapitalLinks = $oldCapitalLinks;
650624 }
651625
652626 /**
@@ -658,19 +632,15 @@
659633 */
660634 public function requireHook( $name ) {
661635 global $wgParser;
662 -
663636 $wgParser->firstCallInit( ); // make sure hooks are loaded.
664 -
665 - if ( isset( $wgParser->mTagHooks[$name] ) ) {
666 - $this->hooks[$name] = $wgParser->mTagHooks[$name];
667 - } else {
668 - echo " This test suite requires the '$name' hook extension, skipping.\n";
669 - return false;
670 - }
671 -
672 - return true;
 637+ return isset( $wgParser->mTagHooks[$name] );
673638 }
674639
 640+ public function requireFunctionHook( $name ) {
 641+ global $wgParser;
 642+ $wgParser->firstCallInit( ); // make sure hooks are loaded.
 643+ return isset( $wgParser->mFunctionHooks[$name] );
 644+ }
675645 //Various "cleanup" functions
676646
677647 /*

Follow-up revisions

RevisionCommit summaryAuthorDate
r82870Make Cite properly register to any parser instead of just $wgParser....platonides22:39, 26 February 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r79184Self-reverting my reversion of r79154 in r79158. The old version is flawed an...soxred9316:40, 29 December 2010
r79411Start work on porting ParserTests completely into PHPunit....soxred9303:39, 1 January 2011
r82499Save and restore $wgHooks in NewParserTest....platonides00:13, 20 February 2011

Status & tagging log