Remove no-op codepath
[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 $this->file = $file;
16 $this->fh = fopen( $this->file, "rt" );
17
18 if ( !$this->fh ) {
19 throw new MWException( "Couldn't open file '$file'\n" );
20 }
21
22 $this->parserTest = $parserTest;
23
24 $this->lineNum = $this->index = 0;
25 }
26
27 function rewind() {
28 if ( fseek( $this->fh, 0 ) ) {
29 throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
30 }
31
32 $this->index = -1;
33 $this->lineNum = 0;
34 $this->eof = false;
35 $this->next();
36
37 return true;
38 }
39
40 function current() {
41 return $this->test;
42 }
43
44 function key() {
45 return $this->index;
46 }
47
48 function next() {
49 if ( $this->readNextTest() ) {
50 $this->index++;
51 return true;
52 } else {
53 $this->eof = true;
54 }
55 }
56
57 function valid() {
58 return $this->eof != true;
59 }
60
61 function readNextTest() {
62 $this->clearSection();
63
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();
67
68 while ( false !== ( $line = fgets( $this->fh ) ) ) {
69 $this->lineNum++;
70 $matches = array();
71
72 if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) {
73 $this->section = strtolower( $matches[1] );
74
75 if ( $this->section == 'endarticle' ) {
76 $this->checkSection( 'text' );
77 $this->checkSection( 'article' );
78
79 $this->parserTest->addArticle( self::chomp( $this->sectionData['article'] ), $this->sectionData['text'], $this->lineNum );
80
81 $this->clearSection();
82
83 continue;
84 }
85
86 if ( $this->section == 'endhooks' ) {
87 $this->checkSection( 'hooks' );
88
89 foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
90 $line = trim( $line );
91
92 if ( $line ) {
93 $delayedParserTest->requireHook( $line );
94 }
95 }
96
97 $this->clearSection();
98
99 continue;
100 }
101
102 if ( $this->section == 'endfunctionhooks' ) {
103 $this->checkSection( 'functionhooks' );
104
105 foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
106 $line = trim( $line );
107
108 if ( $line ) {
109 $delayedParserTest->requireFunctionHook( $line );
110 }
111 }
112
113 $this->clearSection();
114
115 continue;
116 }
117
118 if ( $this->section == 'end' ) {
119 $this->checkSection( 'test' );
120 $this->checkSection( 'input' );
121 $this->checkSection( 'result' );
122
123 if ( !isset( $this->sectionData['options'] ) ) {
124 $this->sectionData['options'] = '';
125 }
126
127 if ( !isset( $this->sectionData['config'] ) ) {
128 $this->sectionData['config'] = '';
129 }
130
131 if ( ( ( preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) && !$this->parserTest->runDisabled )
132 || !preg_match( "/" . $this->parserTest->regex . "/i", $this->sectionData['test'] ) ) ) {
133 # disabled test
134 $this->clearSection();
135
136 # Forget any pending hooks call since test is disabled
137 $delayedParserTest->reset();
138
139 continue;
140 }
141
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.
147 return false;
148 }
149
150 $this->test = array(
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'] ),
156 );
157
158 return true;
159 }
160
161 if ( isset ( $this->sectionData[$this->section] ) ) {
162 throw new MWException( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" );
163 }
164
165 $this->sectionData[$this->section] = '';
166
167 continue;
168 }
169
170 if ( $this->section ) {
171 $this->sectionData[$this->section] .= $line;
172 }
173 }
174
175 return false;
176 }
177
178
179 /**
180 * Clear section name and its data
181 */
182 private function clearSection() {
183 $this->sectionData = array();
184 $this->section = null;
185
186 }
187
188 /**
189 * Remove last character if it is a newline
190 * @group utility
191 */
192 public static function chomp( $s ) {
193 if ( substr( $s, -1 ) === "\n" ) {
194 return substr( $s, 0, -1 );
195 }
196 else {
197 return $s;
198 }
199 }
200
201 /**
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
206 *
207 * @param $token String: expected token that should have been mentionned before closing this section
208 */
209 private function checkSection( $token ) {
210 if( is_null( $this->section ) ) {
211 throw new MWException( __METHOD__ . " can not verify a null section!\n" );
212 }
213
214 if( !isset($this->sectionData[$token]) ) {
215 throw new MWException( sprintf(
216 "'%s' without '%s' at line %s of %s\n",
217 $this->section,
218 $token,
219 $this->lineNum,
220 $this->file
221 ));
222 }
223 return true;
224 }
225 }
226
227 /**
228 * A class to delay execution of a parser test hooks.
229 */
230 class DelayedParserTest {
231
232 /** Initialized on construction */
233 private $hooks;
234 private $fnHooks;
235
236 public function __construct() {
237 $this->reset();
238 }
239
240 /**
241 * Init/reset or forgot about the current delayed test.
242 * Call to this will erase any hooks function that were pending.
243 */
244 public function reset() {
245 $this->hooks = array();
246 $this->fnHooks = array();
247 }
248
249 /**
250 * Called whenever we actually want to run the hook.
251 * Should be the case if we found the parserTest is not disabled
252 */
253 public function unleash( &$parserTest ) {
254 if( !($parserTest instanceof ParserTest || $parserTest instanceof NewParserTest
255 ) ) {
256 throw new MWException( __METHOD__ . " must be passed an instance of ParserTest or NewParserTest classes\n" );
257 }
258
259 # Trigger delayed hooks. Any failure will make us abort
260 foreach( $this->hooks as $hook ) {
261 $ret = $parserTest->requireHook( $hook );
262 if( !$ret ) {
263 return false;
264 }
265 }
266
267 # Trigger delayed function hooks. Any failure will make us abort
268 foreach( $this->fnHooks as $fnHook ) {
269 $ret = $parserTest->requireFunctionHook( $fnHook );
270 if( !$ret ) {
271 return false;
272 }
273 }
274
275 # Delayed execution was successful.
276 return true;
277 }
278
279 /**
280 * Similar to ParserTest object but does not run anything
281 * Use unleash() to really execute the hook
282 */
283 public function requireHook( $hook ) {
284 $this->hooks[] = $hook;
285 }
286 /**
287 * Similar to ParserTest object but does not run anything
288 * Use unleash() to really execute the hook function
289 */
290 public function requireFunctionHook( $fnHook ) {
291 $this->fnHooks[] = $fnHook;
292 }
293
294 }