* Drop old parserTests.php way of running parser tests. You can run parser tests...
[lhc/web/wiklou.git] / tests / testHelpers.inc
1 <?php
2
3 class TestFileIterator implements Iterator {
4 private $file;
5 private $fh;
6 private $parserTest; /* An instance of ParserTest (parserTests.php) or MediaWikiParserTest (phpunit) */
7 private $index = 0;
8 private $test;
9 private $section = null; /** String|null: current test section being analyzed */
10 private $sectionData = array();
11 private $lineNum;
12 private $eof;
13
14 function __construct( $file, $parserTest ) {
15 global $IP;
16
17 $this->file = $file;
18 $this->fh = fopen( $this->file, "rt" );
19
20 if ( !$this->fh ) {
21 throw new MWException( "Couldn't open file '$file'\n" );
22 }
23
24 $this->parserTest = $parserTest;
25 $this->parserTest->showRunFile( wfRelativePath( $this->file, $IP ) );
26
27 $this->lineNum = $this->index = 0;
28 }
29
30 function rewind() {
31 if ( fseek( $this->fh, 0 ) ) {
32 throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
33 }
34
35 $this->index = -1;
36 $this->lineNum = 0;
37 $this->eof = false;
38 $this->next();
39
40 return true;
41 }
42
43 function current() {
44 return $this->test;
45 }
46
47 function key() {
48 return $this->index;
49 }
50
51 function next() {
52 if ( $this->readNextTest() ) {
53 $this->index++;
54 return true;
55 } else {
56 $this->eof = true;
57 }
58 }
59
60 function valid() {
61 return $this->eof != true;
62 }
63
64 function readNextTest() {
65 $this->clearSection();
66
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();
70
71 while ( false !== ( $line = fgets( $this->fh ) ) ) {
72 $this->lineNum++;
73 $matches = array();
74
75 if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) {
76 $this->section = strtolower( $matches[1] );
77
78 if ( $this->section == 'endarticle' ) {
79 $this->checkSection( 'text' );
80 $this->checkSection( 'article' );
81
82 $this->parserTest->addArticle( self::chomp( $this->sectionData['article'] ), $this->sectionData['text'], $this->lineNum );
83
84 $this->clearSection();
85
86 continue;
87 }
88
89 if ( $this->section == 'endhooks' ) {
90 $this->checkSection( 'hooks' );
91
92 foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
93 $line = trim( $line );
94
95 if ( $line ) {
96 $delayedParserTest->requireHook( $line );
97 }
98 }
99
100 $this->clearSection();
101
102 continue;
103 }
104
105 if ( $this->section == 'endfunctionhooks' ) {
106 $this->checkSection( 'functionhooks' );
107
108 foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
109 $line = trim( $line );
110
111 if ( $line ) {
112 $delayedParserTest->requireFunctionHook( $line );
113 }
114 }
115
116 $this->clearSection();
117
118 continue;
119 }
120
121 if ( $this->section == 'end' ) {
122 $this->checkSection( 'test' );
123 $this->checkSection( 'input' );
124 $this->checkSection( 'result' );
125
126 if ( !isset( $this->sectionData['options'] ) ) {
127 $this->sectionData['options'] = '';
128 }
129
130 if ( !isset( $this->sectionData['config'] ) ) {
131 $this->sectionData['config'] = '';
132 }
133
134 if ( ( ( preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) && !$this->parserTest->runDisabled )
135 || !preg_match( "/" . $this->parserTest->regex . "/i", $this->sectionData['test'] ) ) ) {
136 # disabled test
137 $this->clearSection();
138
139 # Forget any pending hooks call since test is disabled
140 $delayedParserTest->reset();
141
142 continue;
143 }
144
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.
150 return false;
151 }
152
153 $this->test = array(
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'] ),
159 );
160
161 return true;
162 }
163
164 if ( isset ( $this->sectionData[$this->section] ) ) {
165 throw new MWException( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" );
166 }
167
168 $this->sectionData[$this->section] = '';
169
170 continue;
171 }
172
173 if ( $this->section ) {
174 $this->sectionData[$this->section] .= $line;
175 }
176 }
177
178 return false;
179 }
180
181
182 /**
183 * Clear section name and its data
184 */
185 private function clearSection() {
186 $this->sectionData = array();
187 $this->section = null;
188
189 }
190
191 /**
192 * Remove last character if it is a newline
193 * @group utility
194 */
195 public static function chomp( $s ) {
196 if ( substr( $s, -1 ) === "\n" ) {
197 return substr( $s, 0, -1 );
198 }
199 else {
200 return $s;
201 }
202 }
203
204 /**
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
209 *
210 * @param $token String: expected token that should have been mentionned before closing this section
211 */
212 private function checkSection( $token ) {
213 if( is_null( $this->section ) ) {
214 throw new MWException( __METHOD__ . " can not verify a null section!\n" );
215 }
216
217 if( !isset($this->sectionData[$token]) ) {
218 throw new MWException( sprintf(
219 "'%s' without '%s' at line %s of %s\n",
220 $this->section,
221 $token,
222 $this->lineNum,
223 $this->file
224 ));
225 }
226 return true;
227 }
228 }