3 class TestFileIterator implements Iterator {
6 private $parserTest; /* An instance of ParserTest (parserTests.php) or MediaWikiParserTest (phpunit) */
9 private $section = null; /** String|null: current test section being analyzed */
10 private $sectionData = array();
14 function __construct( $file, $parserTest ) {
16 $this->fh = fopen( $this->file, "rt" );
19 throw new MWException( "Couldn't open file '$file'\n" );
22 $this->parserTest = $parserTest;
24 $this->lineNum = $this->index = 0;
28 if ( fseek( $this->fh, 0 ) ) {
29 throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
49 if ( $this->readNextTest() ) {
58 return $this->eof != true;
61 function readNextTest() {
62 $this->clearSection();
64 # Create a fake parser tests which never run anything unless
65 # asked to do so. This will avoid running hooks for a disabled test
66 $delayedParserTest = new DelayedParserTest();
68 while ( false !== ( $line = fgets( $this->fh ) ) ) {
72 if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) {
73 $this->section = strtolower( $matches[1] );
75 if ( $this->section == 'endarticle' ) {
76 $this->checkSection( 'text' );
77 $this->checkSection( 'article' );
79 $this->parserTest->addArticle( self::chomp( $this->sectionData['article'] ), $this->sectionData['text'], $this->lineNum );
81 $this->clearSection();
86 if ( $this->section == 'endhooks' ) {
87 $this->checkSection( 'hooks' );
89 foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
90 $line = trim( $line );
93 $delayedParserTest->requireHook( $line );
97 $this->clearSection();
102 if ( $this->section == 'endfunctionhooks' ) {
103 $this->checkSection( 'functionhooks' );
105 foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
106 $line = trim( $line );
109 $delayedParserTest->requireFunctionHook( $line );
113 $this->clearSection();
118 if ( $this->section == 'end' ) {
119 $this->checkSection( 'test' );
120 $this->checkSection( 'input' );
121 $this->checkSection( 'result' );
123 if ( !isset( $this->sectionData['options'] ) ) {
124 $this->sectionData['options'] = '';
127 if ( !isset( $this->sectionData['config'] ) ) {
128 $this->sectionData['config'] = '';
131 if ( ( ( preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) && !$this->parserTest->runDisabled )
132 || !preg_match( "/" . $this->parserTest->regex . "/i", $this->sectionData['test'] ) ) ) {
134 $this->clearSection();
136 # Forget any pending hooks call since test is disabled
137 $delayedParserTest->reset();
142 # We are really going to run the test, run pending hooks and hooks function
143 wfDebug( __METHOD__ . " unleashing delayed test for: {$this->sectionData['test']}" );
144 $hooksResult = $delayedParserTest->unleash( $this->parserTest );
145 if( !$hooksResult ) {
146 # Some hook reported an issue. Abort.
151 'test' => self::chomp( $this->sectionData['test'] ),
152 'input' => self::chomp( $this->sectionData['input'] ),
153 'result' => self::chomp( $this->sectionData['result'] ),
154 'options' => self::chomp( $this->sectionData['options'] ),
155 'config' => self::chomp( $this->sectionData['config'] ),
161 if ( isset ( $this->sectionData[$this->section] ) ) {
162 throw new MWException( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" );
165 $this->sectionData[$this->section] = '';
170 if ( $this->section ) {
171 $this->sectionData[$this->section] .= $line;
180 * Clear section name and its data
182 private function clearSection() {
183 $this->sectionData = array();
184 $this->section = null;
189 * Remove last character if it is a newline
192 public static function chomp( $s ) {
193 if ( substr( $s, -1 ) === "\n" ) {
194 return substr( $s, 0, -1 );
202 * Verify the current section data has some value for the given token
203 * name (first parameter).
204 * Throw an exception if it is not set, referencing current section
205 * and adding the current file name and line number
207 * @param $token String: expected token that should have been mentionned before closing this section
209 private function checkSection( $token ) {
210 if( is_null( $this->section ) ) {
211 throw new MWException( __METHOD__ . " can not verify a null section!\n" );
214 if( !isset($this->sectionData[$token]) ) {
215 throw new MWException( sprintf(
216 "'%s' without '%s' at line %s of %s\n",
228 * A class to delay execution of a parser test hooks.
230 class DelayedParserTest {
232 /** Initialized on construction */
236 public function __construct() {
241 * Init/reset or forgot about the current delayed test.
242 * Call to this will erase any hooks function that were pending.
244 public function reset() {
245 $this->hooks = array();
246 $this->fnHooks = array();
250 * Called whenever we actually want to run the hook.
251 * Should be the case if we found the parserTest is not disabled
253 public function unleash( &$parserTest ) {
254 if( !($parserTest instanceof ParserTest || $parserTest instanceof NewParserTest
256 throw new MWException( __METHOD__ . " must be passed an instance of ParserTest or NewParserTest classes\n" );
259 # Trigger delayed hooks. Any failure will make us abort
260 foreach( $this->hooks as $hook ) {
261 $ret = $parserTest->requireHook( $hook );
267 # Trigger delayed function hooks. Any failure will make us abort
268 foreach( $this->fnHooks as $fnHook ) {
269 $ret = $parserTest->requireFunctionHook( $fnHook );
275 # Delayed execution was successful.
280 * Similar to ParserTest object but does not run anything
281 * Use unleash() to really execute the hook
283 public function requireHook( $hook ) {
284 $this->hooks[] = $hook;
287 * Similar to ParserTest object but does not run anything
288 * Use unleash() to really execute the hook function
290 public function requireFunctionHook( $fnHook ) {
291 $this->fnHooks[] = $fnHook;