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 ) {
18 $this->fh = fopen( $this->file, "rt" );
21 throw new MWException( "Couldn't open file '$file'\n" );
24 $this->parserTest = $parserTest;
25 $this->parserTest->showRunFile( wfRelativePath( $this->file, $IP ) );
27 $this->lineNum = $this->index = 0;
31 if ( fseek( $this->fh, 0 ) ) {
32 throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
52 if ( $this->readNextTest() ) {
61 return $this->eof != true;
64 function readNextTest() {
65 $this->clearSection();
67 # Create a fake parser tests which never run anything unless
68 # asked to do so. This will avoid running hooks for a disabled test
69 $delayedParserTest = new DelayedParserTest();
71 while ( false !== ( $line = fgets( $this->fh ) ) ) {
75 if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) {
76 $this->section = strtolower( $matches[1] );
78 if ( $this->section == 'endarticle' ) {
79 $this->checkSection( 'text' );
80 $this->checkSection( 'article' );
82 $this->parserTest->addArticle( self::chomp( $this->sectionData['article'] ), $this->sectionData['text'], $this->lineNum );
84 $this->clearSection();
89 if ( $this->section == 'endhooks' ) {
90 $this->checkSection( 'hooks' );
92 foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
93 $line = trim( $line );
96 $delayedParserTest->requireHook( $line );
100 $this->clearSection();
105 if ( $this->section == 'endfunctionhooks' ) {
106 $this->checkSection( 'functionhooks' );
108 foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
109 $line = trim( $line );
112 $delayedParserTest->requireFunctionHook( $line );
116 $this->clearSection();
121 if ( $this->section == 'end' ) {
122 $this->checkSection( 'test' );
123 $this->checkSection( 'input' );
124 $this->checkSection( 'result' );
126 if ( !isset( $this->sectionData['options'] ) ) {
127 $this->sectionData['options'] = '';
130 if ( !isset( $this->sectionData['config'] ) ) {
131 $this->sectionData['config'] = '';
134 if ( ( ( preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) && !$this->parserTest->runDisabled )
135 || !preg_match( "/" . $this->parserTest->regex . "/i", $this->sectionData['test'] ) ) ) {
137 $this->clearSection();
139 # Forget any pending hooks call since test is disabled
140 $delayedParserTest->reset();
145 # We are really going to run the test, run pending hooks and hooks function
146 wfDebug( __METHOD__ . " unleashing delayed test for: {$this->sectionData['test']}" );
147 $hooksResult = $delayedParserTest->unleash( $this->parserTest );
148 if( !$hooksResult ) {
149 # Some hook reported an issue. Abort.
154 'test' => self::chomp( $this->sectionData['test'] ),
155 'input' => self::chomp( $this->sectionData['input'] ),
156 'result' => self::chomp( $this->sectionData['result'] ),
157 'options' => self::chomp( $this->sectionData['options'] ),
158 'config' => self::chomp( $this->sectionData['config'] ),
164 if ( isset ( $this->sectionData[$this->section] ) ) {
165 throw new MWException( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" );
168 $this->sectionData[$this->section] = '';
173 if ( $this->section ) {
174 $this->sectionData[$this->section] .= $line;
183 * Clear section name and its data
185 private function clearSection() {
186 $this->sectionData = array();
187 $this->section = null;
192 * Remove last character if it is a newline
195 public static function chomp( $s ) {
196 if ( substr( $s, -1 ) === "\n" ) {
197 return substr( $s, 0, -1 );
205 * Verify the current section data has some value for the given token
206 * name (first parameter).
207 * Throw an exception if it is not set, referencing current section
208 * and adding the current file name and line number
210 * @param $token String: expected token that should have been mentionned before closing this section
212 private function checkSection( $token ) {
213 if( is_null( $this->section ) ) {
214 throw new MWException( __METHOD__ . " can not verify a null section!\n" );
217 if( !isset($this->sectionData[$token]) ) {
218 throw new MWException( sprintf(
219 "'%s' without '%s' at line %s of %s\n",
231 * A class to delay execution of a parser test hooks.
233 class DelayedParserTest {
235 /** Initialized on construction */
239 public function __construct() {
244 * Init/reset or forgot about the current delayed test.
245 * Call to this will erase any hooks function that were pending.
247 public function reset() {
248 $this->hooks = array();
249 $this->fnHooks = array();
253 * Called whenever we actually want to run the hook.
254 * Should be the case if we found the parserTest is not disabled
256 public function unleash( &$parserTest ) {
257 if( !($parserTest instanceof ParserTest || $parserTest instanceof NewParserTest
259 throw new MWException( __METHOD__ . " must be passed an instance of ParserTest or NewParserTest classes\n" );
262 # Trigger delayed hooks. Any failure will make us abort
263 foreach( $this->hooks as $hook ) {
264 $ret = $parserTest->requireHook( $hook );
270 # Trigger delayed function hooks. Any failure will make us abort
271 foreach( $this->fnHooks as $fnHook ) {
272 $ret = $parserTest->requireFunctionHook( $fnHook );
278 # Delayed execution was successful.
283 * Similar to ParserTest object but does not run anything
284 * Use unleash() to really execute the hook
286 public function requireHook( $hook ) {
287 $this->hooks[] = $hook;
290 * Similar to ParserTest object but does not run anything
291 * Use unleash() to really execute the hook function
293 public function requireFunctionHook( $fnHook ) {
294 $this->fnHooks[] = $fnHook;