Index: trunk/phase3/tests/phpunit/MediaWikiTestCase.php |
— | — | @@ -30,7 +30,13 @@ |
31 | 31 | } |
32 | 32 | |
33 | 33 | 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 | + |
35 | 41 | if( $this->needsDB() ) { |
36 | 42 | |
37 | 43 | global $wgDBprefix; |
Index: trunk/phase3/tests/phpunit/includes/parser/NewParserHelpers.php |
— | — | @@ -169,11 +169,11 @@ |
170 | 170 | } |
171 | 171 | |
172 | 172 | $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'] ) ); |
178 | 178 | |
179 | 179 | return true; |
180 | 180 | } |
Index: trunk/phase3/tests/phpunit/includes/parser/MediaWikiParserTest.php |
— | — | @@ -1,66 +1,35 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | require_once( dirname( __FILE__ ) . '/ParserHelpers.php' ); |
| 5 | +require_once( dirname( __FILE__ ) . '/NewParserTest.php' ); |
5 | 6 | require_once( dirname(dirname(dirname( __FILE__ ))) . '/bootstrap.php' ); |
6 | 7 | |
7 | 8 | /** |
| 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 | + * |
8 | 13 | * @group Parser |
9 | | - * @group Destructive |
10 | 14 | * @group Database |
11 | | - * @group Broken |
12 | | - * It's not really broken, but superseded |
13 | 15 | */ |
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 { |
34 | 17 | |
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' ) ); |
43 | 26 | |
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 ) ); |
62 | 31 | } |
63 | 32 | |
64 | | - } |
65 | 33 | |
| 34 | + return $suite; |
| 35 | + } |
66 | 36 | } |
67 | | - |
Index: trunk/phase3/tests/phpunit/includes/parser/NewParserTest.php |
— | — | @@ -2,9 +2,16 @@ |
3 | 3 | |
4 | 4 | /** |
5 | 5 | * @group Database |
| 6 | + * @group Parser |
| 7 | + * @group Stub (can also work independently) |
6 | 8 | */ |
7 | 9 | class NewParserTest extends MediaWikiTestCase { |
8 | 10 | |
| 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 | + |
9 | 16 | public $uploadDir; |
10 | 17 | public $keepUploads = false; |
11 | 18 | public $runDisabled = false; |
— | — | @@ -21,10 +28,15 @@ |
22 | 29 | public $fuzzSeed = 0; |
23 | 30 | public $memoryLimit = 50; |
24 | 31 | |
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 | + |
27 | 38 | function setUp() { |
28 | | - global $wgContLang, $wgNamespaceProtection, $wgNamespaceAliases, $IP; |
| 39 | + global $wgContLang, $wgNamespaceProtection, $wgNamespaceAliases; |
| 40 | + global $wgHooks, $IP; |
29 | 41 | $wgContLang = Language::factory( 'en' ); |
30 | 42 | |
31 | 43 | //Setup CLI arguments |
— | — | @@ -56,7 +68,7 @@ |
57 | 69 | ); |
58 | 70 | |
59 | 71 | $tmpGlobals['wgEnableParserCache'] = false; |
60 | | - $tmpGlobals['wgHooks'] = array(); |
| 72 | + $tmpGlobals['wgHooks'] = $wgHooks; |
61 | 73 | $tmpGlobals['wgDeferredUpdateList'] = array(); |
62 | 74 | $tmpGlobals['wgMemc'] = &wfGetMainCache(); |
63 | 75 | $tmpGlobals['messageMemc'] = &wfGetMessageCacheStorage(); |
— | — | @@ -152,8 +164,9 @@ |
153 | 165 | MessageCache::singleton()->clear(); |
154 | 166 | |
155 | 167 | $this->uploadDir = $this->setupUploadDir(); |
156 | | - |
| 168 | + |
157 | 169 | $user = User::newFromId( 0 ); |
| 170 | + LinkCache::singleton()->clear(); # Avoids the odd failure at creating the nullRevision |
158 | 171 | |
159 | 172 | $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) ); |
160 | 173 | $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array( |
— | — | @@ -258,7 +271,8 @@ |
259 | 272 | 'wgHtml5' => true, |
260 | 273 | 'wgWellFormedXml' => true, |
261 | 274 | 'wgAllowMicrodataAttributes' => true, |
262 | | - 'wgAdaptiveMessageCache' => true |
| 275 | + 'wgAdaptiveMessageCache' => true, |
| 276 | + 'wgUseDatabaseMessages' => true, |
263 | 277 | ); |
264 | 278 | |
265 | 279 | if ( $config ) { |
— | — | @@ -295,6 +309,13 @@ |
296 | 310 | |
297 | 311 | MagicWord::clearCache(); |
298 | 312 | |
| 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 | + |
299 | 320 | global $wgUser; |
300 | 321 | $wgUser = new User(); |
301 | 322 | } |
— | — | @@ -345,115 +366,93 @@ |
346 | 367 | return $dir; |
347 | 368 | } |
348 | 369 | |
| 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 | + } |
349 | 377 | |
| 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 | + } |
350 | 384 | |
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 ); |
354 | 390 | |
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']; |
363 | 396 | } |
| 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 ); |
364 | 404 | |
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(); |
449 | 430 | } |
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 = ''; |
452 | 447 | } |
453 | | - |
454 | 448 | } |
455 | | - |
| 449 | + $parser->mPreprocessor = null; |
| 450 | + |
| 451 | + $result = $this->tidy( $result ); |
456 | 452 | } |
457 | | - |
| 453 | + |
| 454 | + $this->teardownGlobals(); |
| 455 | + |
| 456 | + $this->assertEquals( $result, $out, $desc ); |
458 | 457 | } |
459 | 458 | |
460 | 459 | /** |
— | — | @@ -593,59 +592,34 @@ |
594 | 593 | * Get a Parser object |
595 | 594 | */ |
596 | 595 | function getParser( $preprocessor = null ) { |
597 | | - global $wgParserConf; |
| 596 | + global $wgParserConf, $wgHooks; |
598 | 597 | |
599 | 598 | $class = $wgParserConf['class']; |
600 | 599 | $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf ); |
601 | 600 | |
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 | | - |
611 | 601 | wfRunHooks( 'ParserTestParser', array( &$parser ) ); |
612 | 602 | |
613 | 603 | return $parser; |
614 | 604 | } |
615 | 605 | |
616 | 606 | //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; |
626 | 607 | |
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; |
637 | 615 | } |
638 | 616 | |
639 | | - $aid = $title->getArticleID( Title::GAID_FOR_UPDATE ); |
| 617 | + foreach ( self::$articles as $name => $text ) { |
| 618 | + $title = Title::newFromText( $name ); |
640 | 619 | |
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 | + } |
644 | 623 | } |
645 | | - |
646 | | - $art = new Article( $title ); |
647 | | - $art->doEdit( $text, '', EDIT_NEW ); |
648 | | - |
649 | | - $wgCapitalLinks = $oldCapitalLinks; |
650 | 624 | } |
651 | 625 | |
652 | 626 | /** |
— | — | @@ -658,19 +632,15 @@ |
659 | 633 | */ |
660 | 634 | public function requireHook( $name ) { |
661 | 635 | global $wgParser; |
662 | | - |
663 | 636 | $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] ); |
673 | 638 | } |
674 | 639 | |
| 640 | + public function requireFunctionHook( $name ) { |
| 641 | + global $wgParser; |
| 642 | + $wgParser->firstCallInit( ); // make sure hooks are loaded. |
| 643 | + return isset( $wgParser->mFunctionHooks[$name] ); |
| 644 | + } |
675 | 645 | //Various "cleanup" functions |
676 | 646 | |
677 | 647 | /* |