From aa7662f781ddf0d86a001d785bc994e2ed0e9ce2 Mon Sep 17 00:00:00 2001 From: "Mark A. Hershberger" Date: Fri, 5 Feb 2010 04:01:15 +0000 Subject: [PATCH] =?utf8?q?PHPUnit=20now=20sees=20individual=20parser=20tes?= =?utf8?q?ts=20as=20separate=20tests.=20=20This=20involved=20changing=20pa?= =?utf8?q?rserTests.inc=20slightly=20to=20create=20an=20iterator=20that=20?= =?utf8?q?reads=20parserTests.txt=20=E2=80=94=20separating=20the=20reading?= =?utf8?q?=20of=20the=20file=20from=20the=20execution=20of=20the=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit PHPUnit still doesn't see individual assertions, but that is less important right now. Tested maintenance/parserTests.php against maintenance/parserTests.txt and extensions/Cite/citeParserTests.txt to verify that the parser tests still worked as expected. --- maintenance/parserTests.inc | 306 +++++++++++++--------- maintenance/tests/MediaWikiParserTest.php | 242 ++++++++++------- 2 files changed, 336 insertions(+), 212 deletions(-) diff --git a/maintenance/parserTests.inc b/maintenance/parserTests.inc index 26eee2478e..ae12c20998 100644 --- a/maintenance/parserTests.inc +++ b/maintenance/parserTests.inc @@ -1,5 +1,5 @@ +# Copyright (C) 2004, 2010 Brion Vibber # http://www.mediawiki.org/ # # This program is free software; you can redistribute it and/or modify @@ -120,6 +120,8 @@ class ParserTest { $this->recorder = new DbTestPreviewer( $this ); } elseif( isset( $options['upload'] ) ) { $this->recorder = new RemoteTestRecorder( $this ); + } elseif( class_exists( 'PHPUnitTestRecorder' ) ) { + $this->recorder = new PHPUnitTestRecorder( $this ); } else { $this->recorder = new TestRecorder( $this ); } @@ -138,7 +140,7 @@ class ParserTest { /** * Remove last character if it is a newline */ - private function chomp($s) { + public function chomp($s) { if (substr($s, -1) === "\n") { return substr($s, 0, -1); } @@ -276,7 +278,8 @@ class ParserTest { $this->setupDatabase(); $ok = true; foreach( $filenames as $filename ) { - $ok = $this->runFile( $filename ) && $ok; + $tests = new TestFileIterator( $filename, $this ); + $ok = $this->runTests( $tests ) && $ok; } $this->teardownDatabase(); $this->recorder->report(); @@ -284,124 +287,17 @@ class ParserTest { return $ok; } - private function runFile( $filename ) { - $infile = fopen( $filename, 'rt' ); - if( !$infile ) { - wfDie( "Couldn't open file '$filename'\n" ); - } else { - global $IP; - $relative = wfRelativePath( $filename, $IP ); - $this->showRunFile( $relative ); - } - - $data = array(); - $section = null; - $n = 0; + function runTests($tests) { $ok = true; - while( false !== ($line = fgets( $infile ) ) ) { - $n++; - $matches = array(); - if( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) { - $section = strtolower( $matches[1] ); - if( $section == 'endarticle') { - if( !isset( $data['text'] ) ) { - wfDie( "'endarticle' without 'text' at line $n of $filename\n" ); - } - if( !isset( $data['article'] ) ) { - wfDie( "'endarticle' without 'article' at line $n of $filename\n" ); - } - $this->addArticle($this->chomp($data['article']), $this->chomp($data['text']), $n); - $data = array(); - $section = null; - continue; - } - if( $section == 'endhooks' ) { - if( !isset( $data['hooks'] ) ) { - wfDie( "'endhooks' without 'hooks' at line $n of $filename\n" ); - } - foreach( explode( "\n", $data['hooks'] ) as $line ) { - $line = trim( $line ); - if( $line ) { - $this->requireHook( $line ); - } - } - $data = array(); - $section = null; - continue; - } - if( $section == 'endfunctionhooks' ) { - if( !isset( $data['functionhooks'] ) ) { - wfDie( "'endfunctionhooks' without 'functionhooks' at line $n of $filename\n" ); - } - foreach( explode( "\n", $data['functionhooks'] ) as $line ) { - $line = trim( $line ); - if( $line ) { - $this->requireFunctionHook( $line ); - } - } - $data = array(); - $section = null; - continue; - } - if( $section == 'end' ) { - if( !isset( $data['test'] ) ) { - wfDie( "'end' without 'test' at line $n of $filename\n" ); - } - if( !isset( $data['input'] ) ) { - wfDie( "'end' without 'input' at line $n of $filename\n" ); - } - if( !isset( $data['result'] ) ) { - wfDie( "'end' without 'result' at line $n of $filename\n" ); - } - if( !isset( $data['options'] ) ) { - $data['options'] = ''; - } - else { - $data['options'] = $this->chomp( $data['options'] ); - } - if (!isset( $data['config'] ) ) - $data['config'] = ''; - - if ( (preg_match('/\\bdisabled\\b/i', $data['options']) - || !preg_match("/{$this->regex}/i", $data['test'])) && !$this->runDisabled ) { - # disabled test - $data = array(); - $section = null; - continue; - } - if ( preg_match('/\\bmath\\b/i', $data['options']) && !$this->savedGlobals['wgUseTeX'] ) { - # don't run math tests if $wgUseTeX is set to false in LocalSettings - $data = array(); - $section = null; - continue; - } - $result = $this->runTest( - $this->chomp( $data['test'] ), - $this->chomp( $data['input'] ), - $this->chomp( $data['result'] ), - $this->chomp( $data['options'] ), - $this->chomp( $data['config'] ) - ); - $ok = $ok && $result; - $this->recorder->record( $this->chomp( $data['test'] ), $result ); - $data = array(); - $section = null; - continue; - } - if ( isset ($data[$section] ) ) { - wfDie( "duplicate section '$section' at line $n of $filename\n" ); - } - $data[$section] = ''; - continue; - } - if( $section ) { - $data[$section] .= $line; - } + foreach($tests as $i => $t) { + $result = + $this->runTest($t['test'], $t['input'], $t['result'], $t['options'], $t['config']); + $ok = $ok && $result; + $this->recorder->record( $t['test'], $result ); } if ( $this->showProgress ) { print "\n"; } - return $ok; } /** @@ -431,7 +327,7 @@ class ParserTest { * @param string $result Result to output * @return bool */ - private function runTest( $desc, $input, $result, $opts, $config ) { + public function runTest( $desc, $input, $result, $opts, $config ) { if( $this->showProgress ) { $this->showTesting( $desc ); } @@ -722,7 +618,7 @@ class ParserTest { * Currently this will only be done once per run, and any changes to * the db will be visible to later tests in the run. */ - private function setupDatabase() { + function setupDatabase() { global $wgDBprefix, $wgDBtype; if ( $this->databaseSetupDone ) { return; @@ -1111,7 +1007,7 @@ class ParserTest { * * @param String $path */ - protected function showRunFile( $path ){ + public function showRunFile( $path ){ print $this->term->color( 1 ) . "Reading tests from \"$path\"..." . $this->term->reset() . @@ -1124,7 +1020,7 @@ class ParserTest { * @param string $text the article text * @param int $line the input line number, for reporting errors */ - private function addArticle($name, $text, $line) { + public function addArticle($name, $text, $line) { $this->setupGlobals(); $title = Title::newFromText( $name ); if ( is_null($title) ) { @@ -1148,7 +1044,7 @@ class ParserTest { * die a painful dead to warn the others. * @param string $name */ - private function requireHook( $name ) { + public function requireHook( $name ) { global $wgParser; $wgParser->firstCallInit( ); //make sure hooks are loaded. if( isset( $wgParser->mTagHooks[$name] ) ) { @@ -1652,3 +1548,171 @@ class RemoteTestRecorder extends TestRecorder { return Http::post( $url, array( 'postData' => $data) ); } } + +class TestFileIterator implements Iterator { + private $file; + private $fh; + private $parser; + private $index = 0; + private $test; + private $lineNum; + private $eof; + + function __construct( $file, $parser = null ) { + global $IP; + + $this->file = $file; + $this->fh = fopen($this->file, "r"); + if( !$this->fh ) { + wfDie( "Couldn't open file '$file'\n" ); + } + + $this->parser = $parser; + + if( $this->parser ) $this->parser->showRunFile( wfRelativePath( $this->file, $IP ) ); + $this->lineNum = $this->index = 0; + } + + function setParser( ParserTest $parser ) { + $this->parser = $parser; + } + + function rewind() { + if(fseek($this->fh, 0)) { + wfDie( "Couldn't fseek to the start of '$filename'\n" ); + } + $this->index = 0; + $this->lineNum = 0; + $this->eof = false; + $this->readNextTest(); + + return true; + } + + function current() { + return $this->test; + } + + function key() { + return $this->index; + } + + function next() { + if($this->readNextTest()) { + $this->index++; + return true; + } else { + $this->eof = true; + } + } + + function valid() { + return $this->eof != true; + } + + function readNextTest() { + $data = array(); + $section = null; + + while( false !== ($line = fgets( $this->fh ) ) ) { + $this->lineNum++; + $matches = array(); + if( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) { + $section = strtolower( $matches[1] ); + if( $section == 'endarticle') { + if( !isset( $data['text'] ) ) { + wfDie( "'endarticle' without 'text' at line {$this->lineNum} of $filename\n" ); + } + if( !isset( $data['article'] ) ) { + wfDie( "'endarticle' without 'article' at line {$this->lineNum} of $filename\n" ); + } + if( $this->parser ) $this->parser->addArticle($this->parser->chomp($data['article']), $this->parser->chomp($data['text']), + $this->lineNum); + $data = array(); + $section = null; + continue; + } + if( $section == 'endhooks' ) { + if( !isset( $data['hooks'] ) ) { + wfDie( "'endhooks' without 'hooks' at line {$this->lineNum} of $filename\n" ); + } + foreach( explode( "\n", $data['hooks'] ) as $line ) { + $line = trim( $line ); + if( $line ) { + if( $this->parser ) $this->parser->requireHook( $line ); + } + } + $data = array(); + $section = null; + continue; + } + if( $section == 'endfunctionhooks' ) { + if( !isset( $data['functionhooks'] ) ) { + wfDie( "'endfunctionhooks' without 'functionhooks' at line {$this->lineNum} of $filename\n" ); + } + foreach( explode( "\n", $data['functionhooks'] ) as $line ) { + $line = trim( $line ); + if( $line ) { + if( $this->parser ) $this->parser->requireFunctionHook( $line ); + } + } + $data = array(); + $section = null; + continue; + } + if( $section == 'end' ) { + if( !isset( $data['test'] ) ) { + wfDie( "'end' without 'test' at line {$this->lineNum} of $filename\n" ); + } + if( !isset( $data['input'] ) ) { + wfDie( "'end' without 'input' at line {$this->lineNum} of $filename\n" ); + } + if( !isset( $data['result'] ) ) { + wfDie( "'end' without 'result' at line {$this->lineNum} of $filename\n" ); + } + if( !isset( $data['options'] ) ) { + $data['options'] = ''; + } + if (!isset( $data['config'] ) ) + $data['config'] = ''; + + if ( $this->parser && (preg_match('/\\bdisabled\\b/i', $data['options']) + || !preg_match("/{$this->parser->regex}/i", $data['test'])) && !$this->parser->runDisabled ) { + # disabled test + $data = array(); + $section = null; + continue; + } + if ( $this->parser && + preg_match('/\\bmath\\b/i', $data['options']) && !$this->parser->savedGlobals['wgUseTeX'] ) { + # don't run math tests if $wgUseTeX is set to false in LocalSettings + $data = array(); + $section = null; + continue; + } + + if( $this->parser ) { + $this->test = array( + 'test' => $this->parser->chomp( $data['test'] ), + 'input' => $this->parser->chomp( $data['input'] ), + 'result' => $this->parser->chomp( $data['result'] ), + 'options' => $this->parser->chomp( $data['options'] ), + 'config' => $this->parser->chomp( $data['config'] ) ); + } else { + $this->test['test'] = $data['test']; + } + return true; + } + if ( isset ($data[$section] ) ) { + wfDie( "duplicate section '$section' at line {$this->lineNum} of $filename\n" ); + } + $data[$section] = ''; + continue; + } + if( $section ) { + $data[$section] .= $line; + } + } + return false; + } +} \ No newline at end of file diff --git a/maintenance/tests/MediaWikiParserTest.php b/maintenance/tests/MediaWikiParserTest.php index ad2ed61e0e..aebe019428 100644 --- a/maintenance/tests/MediaWikiParserTest.php +++ b/maintenance/tests/MediaWikiParserTest.php @@ -2,45 +2,109 @@ global $IP; define( "NO_COMMAND_LINE", 1 ); +define( "PARSER_TESTS", "$IP/maintenance/parserTests.txt" ); + require_once( "$IP/maintenance/parserTests.inc" ); -require_once( "ImageFunctions.php" ); -require_once( "ProxyTools.php" ); -require_once( "ObjectCache.php" ); -class PTShell extends ParserTest { +class PHPUnitTestRecorder extends TestRecorder { - private $cb; + function record( $test, $result ) { + $this->total++; + $this->success += $result; - function setCallback( $cb ) { - $this->cb = $cb; } - function showTesting( $desc ) { - } + function reportPercentage( $success, $total ) {} +} - function showRunFile( $path ) { +class MediaWikiParserTestSuite extends PHPUnit_Framework_TestSuite { +#implements PHPUnit_Framework_SelfDescribing { + static private $count; + static public $parser; + static public $iter; + + public static function suite() { + $suite = new PHPUnit_Framework_TestSuite(); + + self::$iter = new TestFileIterator( PARSER_TESTS ); + + foreach(self::$iter as $i => $test) { + $suite->addTest(new ParserUnitTest($i, $test['test'])); + self::$count++; + } + unset($tests); + + self::$parser = new PTShell; + self::$iter->setParser(self::$parser); + self::$parser->recorder->start(); + self::$parser->setupDatabase(); + self::$iter->rewind(); + /* } */ + /* function setUp() { */ + global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc, $wgDeferredUpdateList, + $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache, + $wgMessageCache, $wgUseDatabaseMessages, $wgMsgCacheExpiry, $parserMemc, + $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo, + $wgNamespacesWithSubpages, $wgThumbnailScriptPath, $wgScriptPath, + $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath; + + $wgScript = '/index.php'; + $wgScriptPath = '/'; + $wgArticlePath = '/wiki/$1'; + $wgStyleSheetPath = '/skins'; + $wgStylePath = '/skins'; + $wgThumbnailScriptPath = false; + $wgLocalFileRepo = array( + 'class' => 'LocalRepo', + 'name' => 'local', + 'directory' => '', + 'url' => 'http://example.com/images', + 'hashLevels' => 2, + 'transformVia404' => false, + ); + $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface'; + $wgNamespaceAliases['Image'] = NS_FILE; + $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK; + + + $wgEnableParserCache = false; + $wgDeferredUpdateList = array(); + $wgMemc =& wfGetMainCache(); + $messageMemc =& wfGetMessageCacheStorage(); + $parserMemc =& wfGetParserCacheStorage(); + + $wgContLang = new StubContLang; + $wgUser = new StubUser; + $wgLang = new StubUserLang; + $wgOut = new StubObject( 'wgOut', 'OutputPage' ); + $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) ); + $wgRequest = new WebRequest; + + $wgMessageCache = new StubObject( 'wgMessageCache', 'MessageCache', + array( $messageMemc, $wgUseDatabaseMessages, + $wgMsgCacheExpiry, wfWikiID() ) ); + if( $wgStyleDirectory === false) $wgStyleDirectory = "$IP/skins"; + + return $suite; } - function showSuccess( $desc ) { - $this->cb->assertTrue( true, $desc ); - echo "PASSED: $desc\n"; - return true; + public function tearDown() { + $this->teardownDatabase(); + $this->recorder->report(); + $this->recorder->end(); + $this->teardownUploadDir($this->uploadDir); } - function showFailure( $desc, $expected, $got ) { - /* $this->cb->assertEquals( $expected, $got, $desc ); */ - echo "FAILED: $desc\n"; - echo "got: $got\n"; - echo "expected: $expected\n"; + public function count() {return self::$count;} + + public function toString() { + return "MediaWiki Parser Tests"; } -} -class MediaWikiParserTest extends PHPUnit_Framework_TestCase { - private $parserTester; + private $db; private $uploadDir; private $keepUploads; - /** * Remove the dummy uploads directory */ @@ -132,88 +196,84 @@ class MediaWikiParserTest extends PHPUnit_Framework_TestCase { copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" ); return $dir; } +} + +class ParserUnitTest extends PHPUnit_Framework_TestCase { + private $number = 0; + private $test = ""; + + public function __construct($number, $test) { + $this->number = $number; + $this->test = $test; + } - function setUp() { - global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc, $wgDeferredUpdateList, - $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache, $wgMessageCache, - $wgUseDatabaseMessages, $wgMsgCacheExpiry, $parserMemc, $wgNamespaceAliases, $wgNamespaceProtection, - $wgLocalFileRepo, $wgNamespacesWithSubpages, $wgThumbnailScriptPath, $wgScriptPath, - $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath; + function count() {return 1;} - $wgScript = '/index.php'; - $wgScriptPath = '/'; - $wgArticlePath = '/wiki/$1'; - $wgStyleSheetPath = '/skins'; - $wgStylePath = '/skins'; - $wgThumbnailScriptPath = false; - $this->uploadDir = $this->setupUploadDir(); - $wgLocalFileRepo = array( - 'class' => 'LocalRepo', - 'name' => 'local', - 'directory' => $this->uploadDir, - 'url' => 'http://example.com/images', - 'hashLevels' => 2, - 'transformVia404' => false, - ); - //$wgNamespacesWithSubpages = array( 0 => isset( $opts['subpage'] ) ); - $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface'; - $wgNamespaceAliases['Image'] = NS_FILE; - $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK; + public function run(PHPUnit_Framework_TestResult $result = NULL) { + PHPUnit_Framework_Assert::resetCount(); + if ($result === NULL) { + $result = new PHPUnit_Framework_TestResult; + } + $t = MediaWikiParserTestSuite::$iter->current(); + $k = MediaWikiParserTestSuite::$iter->key(); - $wgEnableParserCache = false; - $wgDeferredUpdateList = array(); - $wgMemc =& wfGetMainCache(); - $messageMemc =& wfGetMessageCacheStorage(); - $parserMemc =& wfGetParserCacheStorage(); + if(!MediaWikiParserTestSuite::$iter->valid()) { + return; + } - $wgContLang = new StubContLang; - $wgUser = new StubUser; - $wgLang = new StubUserLang; - $wgOut = new StubObject( 'wgOut', 'OutputPage' ); - $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) ); - $wgRequest = new WebRequest; + // The only way this should happen is if the parserTest.txt + // file were modified while the script is running. + if($k != $this->number) { + $i = $this->number; + wfDie("I got confused!\n"); + } - $wgMessageCache = new StubObject( 'wgMessageCache', 'MessageCache', - array( $messageMemc, $wgUseDatabaseMessages, $wgMsgCacheExpiry, wfWikiID() ) ); - if( $wgStyleDirectory === false) $wgStyleDirectory = "$IP/skins"; + $result->startTest($this); + PHPUnit_Util_Timer::start(); + + $r = false; + try { + $r = MediaWikiParserTestSuite::$parser->runTest( + $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] + ); + PHPUnit_Framework_Assert::assertTrue(true, $t['test']); + } + catch (PHPUnit_Framework_AssertionFailedError $e) { + $result->addFailure($this, $e, PHPUnit_Util_Timer::stop()); + } + catch (Exception $e) { + $result->addError($this, $e, PHPUnit_Util_Timer::stop()); + } + PHPUnit_Framework_Assert::assertTrue(true, $t['test']); + + $result->endTest($this, PHPUnit_Util_Timer::stop()); + + MediaWikiParserTestSuite::$parser->recorder->record($t['test'], $r); + MediaWikiParserTestSuite::$iter->next(); + $this->addToAssertionCount(PHPUnit_Framework_Assert::getCount()); - $this->parserTester = new PTShell(); - $this->parserTester->setCallback( $this ); - - /* global $wgDBtype, $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBport, $wgDBmwschema, $wgDBts2chema; */ - /* $this->db['type'] = $wgDBtype; */ - /* $this->db['server'] = $wgDBserver; */ - /* $this->db['name'] = $wgDBname; */ - /* $this->db['user'] = $wgDBuser; */ - /* $this->db['password'] = $wgDBpassword; */ - /* $this->db['port'] = $wgDBport; */ - /* $this->db['mwschema'] = $wgDBmwschema; */ - /* $this->db['ts2schema'] = $wgDBts2chema; */ + return $result; } - function tearDown() { - $this->teardownUploadDir($this->uploadDir); - /* $db = wfGetDB( DB_MASTER ); */ - /* $db->close(); */ - /* global $wgDBtype, $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBport, $wgDBmwschema, $wgDBts2chema; */ - - /* $wgDBtype = $this->db['type']; */ - /* $wgDBserver = $this->db['server']; */ - /* $wgDBname = $this->db['name']; */ - /* $wgDBuser = $this->db['user']; */ - /* $wgDBpassword = $this->db['password']; */ - /* $wgDBport = $this->db['port']; */ - /* $wgDBmwschema = $this->db['mwschema']; */ - /* $wgDBts2chema = $this->db['ts2schema']; */ +} +class PTShell extends ParserTest { + function showTesting( $desc ) { } + function showRunFile( $path ) { + } - function testParser() { - global $IP; + function showSuccess( $desc ) { + PHPUnit_Framework_Assert::assertTrue(true, $desc); + return true; + } - $this->parserTester->runTestsFromFiles( array( "$IP/maintenance/parserTests.txt" ) ); + function showFailure( $desc, $expected, $got ) { + PHPUnit_Framework_Assert::assertEquals($expected, $got, $desc); } + } + -- 2.20.1