Note on SpecialContributions.php fix for Postgres timestamps.
[lhc/web/wiklou.git] / maintenance / fuzz-tester.php
1 <?php
2 /**
3 * @addtogroup Maintenance
4 * @author Nick Jenkins ( http://nickj.org/ ).
5 * @copyright 2006 Nick Jenkins
6 * @licence GNU General Public Licence 2.0
7
8 Started: 18 May 2006.
9
10 Description:
11 Performs fuzz-style testing of MediaWiki's parser and forms.
12
13 How:
14 - Generate lots of nasty wiki text.
15 - Ask the Parser to render that wiki text to HTML, or ask MediaWiki's forms
16 to deal with that wiki text.
17 - Check MediaWiki's output for problems.
18 - Repeat.
19
20 Why:
21 - To help find bugs.
22 - To help find security issues, or potential security issues.
23
24 What type of problems are being checked for:
25 - Unclosed tags.
26 - Errors or interesting warnings from Tidy.
27 - PHP errors / warnings / notices.
28 - MediaWiki internal errors.
29 - Very slow responses.
30 - No response from apache.
31 - Optionally checking for malformed HTML using the W3C validator.
32
33 Background:
34 Many of the wikiFuzz class methods are a modified PHP port,
35 of a "shameless" Python port, of LCAMTUF'S MANGELME:
36 - http://www.securiteam.com/tools/6Z00N1PBFK.html
37 - http://www.securityfocus.com/archive/1/378632/2004-10-15/2004-10-21/0
38
39 Video:
40 There's an XviD video discussing this fuzz tester. You can get it from:
41 http://files.nickj.org/MediaWiki/Fuzz-Testing-MediaWiki-xvid.avi
42
43 Requirements:
44 To run this, you will need:
45 - Command-line PHP5, with PHP-curl enabled (not all installations have this
46 enabled - try "apt-get install php5-curl" if you're on Debian to install).
47 - the Tidy standalone executable. ("apt-get install tidy").
48
49 Optional:
50 - If you want to run the curl scripts, you'll need standalone curl installed
51 ("apt-get install curl")
52 - For viewing the W3C validator output on a command line, the "html2text"
53 program may be useful ("apt-get install html2text")
54
55 Saving tests and test results:
56 Any of the fuzz tests which find problems are saved for later review.
57 In order to help track down problems, tests are saved in a number of
58 different formats. The default filename extensions and their meanings are:
59 - ".test.php" : PHP script that reproduces just that one problem using PHP-Curl.
60 - ".curl.sh" : Shell script that reproduces that problem using standalone curl.
61 - ".data.bin" : The serialized PHP data so that this script can re-run the test.
62 - ".info.txt" : A human-readable text file with details of the field contents.
63
64 Wiki configuration for testing:
65 You should make some additions to LocalSettings.php in order to catch the most
66 errors. Note this configuration is for **TESTING PURPOSES ONLY**, and is IN NO
67 WAY, SHAPE, OR FORM suitable for deployment on a hostile network. That said,
68 personally I find these additions to be the most helpful for testing purposes:
69
70 // --------- Start ---------
71 // Everyone can do everything. Very useful for testing, yet useless for deployment.
72 $wgGroupPermissions['*']['autoconfirmed'] = true;
73 $wgGroupPermissions['*']['block'] = true;
74 $wgGroupPermissions['*']['bot'] = true;
75 $wgGroupPermissions['*']['delete'] = true;
76 $wgGroupPermissions['*']['deletedhistory'] = true;
77 $wgGroupPermissions['*']['deleterevision'] = true;
78 $wgGroupPermissions['*']['editinterface'] = true;
79 $wgGroupPermissions['*']['hiderevision'] = true;
80 $wgGroupPermissions['*']['import'] = true;
81 $wgGroupPermissions['*']['importupload'] = true;
82 $wgGroupPermissions['*']['minoredit'] = true;
83 $wgGroupPermissions['*']['move'] = true;
84 $wgGroupPermissions['*']['patrol'] = true;
85 $wgGroupPermissions['*']['protect'] = true;
86 $wgGroupPermissions['*']['proxyunbannable'] = true;
87 $wgGroupPermissions['*']['renameuser'] = true;
88 $wgGroupPermissions['*']['reupload'] = true;
89 $wgGroupPermissions['*']['reupload-shared'] = true;
90 $wgGroupPermissions['*']['rollback'] = true;
91 $wgGroupPermissions['*']['siteadmin'] = true;
92 $wgGroupPermissions['*']['trackback'] = true;
93 $wgGroupPermissions['*']['unwatchedpages'] = true;
94 $wgGroupPermissions['*']['upload'] = true;
95 $wgGroupPermissions['*']['userrights'] = true;
96 $wgGroupPermissions['*']['renameuser'] = true;
97 $wgGroupPermissions['*']['makebot'] = true;
98 $wgGroupPermissions['*']['makesysop'] = true;
99
100 // Enable weird and wonderful options:
101 // Increase default error reporting level.
102 error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT
103 $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
104 $wgEnableUploads = true; // enable uploads.
105 //$wgUseTrackbacks = true; // enable trackbacks; However this breaks the viewPageTest, so currently disabled.
106 $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path.
107 $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden).
108 $wgShowExceptionDetails = true; // want backtraces.
109 $wgEnableAPI = true; // enable API.
110 $wgEnableWriteAPI = true; // enable API.
111
112 // Install & enable Parser Hook extensions to increase code coverage. E.g.:
113 require_once("extensions/ParserFunctions/ParserFunctions.php");
114 require_once("extensions/Cite/Cite.php");
115 require_once("extensions/inputbox/inputbox.php");
116 require_once("extensions/Sort/Sort.php");
117 require_once("extensions/wikihiero/wikihiero.php");
118 require_once("extensions/CharInsert/CharInsert.php");
119 require_once("extensions/FixedImage/FixedImage.php");
120
121 // Install & enable Special Page extensions to increase code coverage. E.g.:
122 require_once("extensions/Cite/SpecialCite.php");
123 require_once("extensions/Filepath/SpecialFilepath.php");
124 require_once("extensions/Makebot/Makebot.php");
125 require_once("extensions/Makesysop/SpecialMakesysop.php");
126 require_once("extensions/Renameuser/SpecialRenameuser.php");
127 require_once("extensions/LinkSearch/LinkSearch.php");
128 // --------- End ---------
129
130 If you want to try E_STRICT error logging, add this to the above:
131 // --------- Start ---------
132 error_reporting (E_ALL | E_STRICT);
133 set_error_handler( 'error_handler' );
134 function error_handler ($type, $message, $file=__FILE__, $line=__LINE__) {
135 if ($message == "var: Deprecated. Please use the public/private/protected modifiers") return;
136 print "<br />\n<b>Strict Standards:</b> Type: <b>$type</b>: $message in <b>$file</b> on line <b>$line</b><br />\n";
137 }
138 // --------- End ---------
139
140 Also add/change this in AdminSettings.php:
141 // --------- Start ---------
142 $wgEnableProfileInfo = true;
143 $wgDBserver = "localhost"; // replace with DB server hostname
144 // --------- End ---------
145
146 Usage:
147 Run with "php fuzz-tester.php".
148 To see the various command-line options, run "php fuzz-tester.php --help".
149 To stop the script, press Ctrl-C.
150
151 Console output:
152 - If requested, first any previously failed tests will be rerun.
153 - Then new tests will be generated and run. Any tests that fail will be saved,
154 and a brief message about why they failed will be printed on the console.
155 - The console will show the number of tests run, time run, number of tests
156 failed, number of tests being done per minute, and the name of the current test.
157
158 TODO:
159 Some known things that could improve this script:
160 - Logging in with cookie jar storage needed for some tests (as there are some
161 pages that cannot be tested without being logged in, and which are currently
162 untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist).
163 - Testing of Timeline extension (I cannot test as ploticus has/had issues on
164 my architecture).
165
166 */
167
168 /////////////////////////// COMMAND LINE HELP ////////////////////////////////////
169
170 // This is a command line script, load MediaWiki env (gives command line options);
171 include('commandLine.inc');
172
173 // if the user asked for an explanation of command line options.
174 if ( isset( $options["help"] ) ) {
175 print <<<ENDS
176 MediaWiki $wgVersion fuzz tester
177 Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
178 [--directory=<failed-test-path>] [--include-binary]
179 [--w3c-validate] [--delete-passed-retests] [--help]
180 [--user=<username>] [--password=<password>]
181 [--rerun-failed-tests] [--max-errors=<int>]
182 [--max-runtime=<num-minutes>]
183 [--specific-test=<test-name>]
184
185 Options:
186 --quiet : Hides passed tests, shows only failed tests.
187 --base-url : URL to a wiki on which to run the tests.
188 The "http://" is optional and can be omitted.
189 --directory : Full path to directory for storing failed tests.
190 Will be created if it does not exist.
191 --include-binary : Includes non-alphanumeric characters in the tests.
192 --w3c-validate : Validates pages using the W3C's web validator.
193 Slow. Currently many pages fail validation.
194 --user : Login name of a valid user on your test wiki.
195 --password : Password for the valid user on your test wiki.
196 --delete-passed-retests : Will delete retests that now pass.
197 Requires --rerun-failed-tests to be meaningful.
198 --rerun-failed-tests : Whether to rerun any previously failed tests.
199 --max-errors : Maximum number of errors to report before exiting.
200 Does not include errors from --rerun-failed-tests
201 --max-runtime : Maximum runtime, in minutes, to run before exiting.
202 Only applies to new tests, not --rerun-failed-tests
203 --specific-test : Runs only the specified fuzz test.
204 Only applies to new tests, not --rerun-failed-tests
205 --keep-passed-tests : Saves all test files, even those that pass.
206 --help : Show this help message.
207
208 Example:
209 If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour,
210 and only wanted to be informed of errors, and did not want to redo previously
211 failed tests, and wanted a maximum of 100 errors, then you could do:
212 php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60
213
214
215 ENDS;
216
217 exit( 0 );
218 }
219
220
221 // if we got command line options, check they look valid.
222 $validOptions = array ("quiet", "base-url", "directory", "include-binary",
223 "w3c-validate", "user", "password", "delete-passed-retests",
224 "rerun-failed-tests", "max-errors",
225 "max-runtime", "specific-test", "keep-passed-tests", "help" );
226 if (!empty($options)) {
227 $unknownArgs = array_diff (array_keys($options), $validOptions);
228 foreach ($unknownArgs as $invalidArg) {
229 print "Ignoring invalid command-line option: --$invalidArg\n";
230 }
231 }
232
233
234 ///////////////////////////// CONFIGURATION ////////////////////////////////////
235
236 // URL to some wiki on which we can run our tests.
237 if (!empty($options["base-url"])) {
238 define("WIKI_BASE_URL", $options["base-url"]);
239 } else {
240 define("WIKI_BASE_URL", $wgServer . $wgScriptPath . '/');
241 }
242
243 // The directory name where we store the output.
244 // Example for Windows: "c:\\temp\\wiki-fuzz"
245 if (!empty($options["directory"])) {
246 define("DIRECTORY", $options["directory"] );
247 } else {
248 define("DIRECTORY", "{$wgUploadDirectory}/fuzz-tests");
249 }
250
251 // Should our test fuzz data include binary strings?
252 define("INCLUDE_BINARY", isset($options["include-binary"]) );
253
254 // Whether we want to validate HTML output on the web.
255 // At the moment very few generated pages will validate, so not recommended.
256 define("VALIDATE_ON_WEB", isset($options["w3c-validate"]) );
257 // URL to use to validate our output:
258 define("VALIDATOR_URL", "http://validator.w3.org/check");
259
260 // Location of Tidy standalone executable.
261 define("PATH_TO_TIDY", "/usr/bin/tidy");
262
263 // The name of a user who has edited on your wiki. Used
264 // when testing the Special:Contributions and Special:Userlogin page.
265 if (!empty($options["user"])) {
266 define("USER_ON_WIKI", $options["user"] );
267 } else {
268 define("USER_ON_WIKI", "nickj");
269 }
270
271 // The password of the above user. Used when testing the login page,
272 // and to do this we sometimes need to login successfully.
273 if (!empty($options["password"])) {
274 define("USER_PASSWORD", $options["password"] );
275 } else {
276 // And no, this is not a valid password on any public wiki.
277 define("USER_PASSWORD", "nickj");
278 }
279
280 // If we have a test that failed, and then we run it again, and it passes,
281 // do you want to delete it or keep it?
282 define("DELETE_PASSED_RETESTS", isset($options["delete-passed-retests"]) );
283
284 // Do we want to rerun old saved tests at script startup?
285 // Set to true to help catch regressions, or false if you only want new stuff.
286 define("RERUN_OLD_TESTS", isset($options["rerun-failed-tests"]) );
287
288 // File where the database errors are logged. Should be defined in LocalSettings.php.
289 define("DB_ERROR_LOG_FILE", $wgDBerrorLog );
290
291 // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)?
292 define("QUIET", isset($options["quiet"]) );
293
294 // Keep all test files, even those that pass. Potentially useful to tracking input that causes something
295 // unusual to happen, if you don't know what "unusual" is until later.
296 define("KEEP_PASSED_TESTS", isset($options["keep-passed-tests"]) );
297
298 // The maximum runtime, if specified.
299 if (!empty($options["max-runtime"]) && intval($options["max-runtime"])>0) {
300 define("MAX_RUNTIME", intval($options["max-runtime"]) );
301 }
302
303 // The maximum number of problems to find, if specified. Excludes retest errors.
304 if (!empty($options["max-errors"]) && intval($options["max-errors"])>0) {
305 define("MAX_ERRORS", intval($options["max-errors"]) );
306 }
307
308 // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid.
309 if (!empty($options["specific-test"])) {
310 if (class_exists($options["specific-test"]) && get_parent_class($options["specific-test"])=="pageTest") {
311 define("SPECIFIC_TEST", $options["specific-test"] );
312 }
313 else {
314 print "Ignoring invalid --specific-test\n";
315 }
316 }
317
318 // Define the file extensions we'll use:
319 define("PHP_TEST" , ".test.php");
320 define("CURL_TEST", ".curl.sh" );
321 define("DATA_FILE", ".data.bin");
322 define("INFO_FILE", ".info.txt");
323 define("HTML_FILE", ".wiki_preview.html");
324
325 // If it goes wrong, we want to know about it.
326 error_reporting(E_ALL | E_STRICT);
327
328 //////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS //////////////////////
329
330 class wikiFuzz {
331
332 // Only some HTML tags are understood with params by MediaWiki, the rest are ignored.
333 // List the tags that accept params below, as well as what those params are.
334 public static $data = array(
335 "B" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
336 "CAPTION" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"),
337 "CENTER" => array("CLASS", "STYLE", "ID", "lang", "dir", "title"),
338 "DIV" => array("CLASS", "STYLE", "ID", "align", "lang", "dir", "title"),
339 "FONT" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color"),
340 "H1" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"),
341 "H2" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"),
342 "HR" => array("STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade"),
343 "LI" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value"),
344 "TABLE" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING",
345 "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules"),
346 "TD" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
347 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
348 "dir", "title", "char", "charoff"),
349 "TH" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
350 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
351 "dir", "title", "char", "charoff"),
352 "TR" => array("CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff"),
353 "UL" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "type"),
354 "P" => array("style", "class", "id", "align", "lang", "dir", "title"),
355 "blockquote" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "cite"),
356 "span" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"),
357 "code" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
358 "tt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
359 "small" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
360 "big" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
361 "s" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
362 "u" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
363 "del" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"),
364 "ins" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"),
365 "sub" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
366 "sup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
367 "ol" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start"),
368 "br" => array("CLASS", "ID", "STYLE", "title", "clear"),
369 "cite" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
370 "var" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
371 "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
372 "ruby" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
373 "rt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
374 "rp" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
375 "dt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
376 "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
377 "em" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
378 "strong" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
379 "i" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
380 "thead" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
381 "tfoot" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
382 "tbody" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
383 "colgroup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'),
384 "col" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'),
385 "pre" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "width"),
386
387 // extension tags that accept parameters:
388 "sort" => array("order", "class"),
389 "ref" => array("name"),
390 "categorytree" => array("hideroot", "mode", "style"),
391 "chemform" => array("link", "wikilink", "query"),
392 "section" => array("begin", "new"),
393
394 // older MW transclusion.
395 "transclude" => array("page"),
396 );
397
398 // The types of the HTML that we will be testing were defined above
399 // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data);
400 // as such, it also needs to also be publicly modifiable.
401 public static $types;
402
403
404 // Some attribute values.
405 static private $other = array("&","=",":","?","\"","\n","%n%n%n%n%n%n%n%n%n%n%n%n","\\");
406 static private $ints = array(
407 // various numbers
408 "0","-1","127","-7897","89000","808080","90928345",
409 "0xfffffff","ffff",
410
411 // Different ways of saying: '
412 "&#0000039;", // Long UTF-8 Unicode encoding
413 "&#39;", // dec version.
414 "&#x27;", // hex version.
415 "&#xA7;", // malformed hex variant, MSB not zero.
416
417 // Different ways of saying: "
418 "&#0000034;", // Long UTF-8 Unicode encoding
419 "&#34;",
420 "&#x22;", // hex version.
421 "&#xA2;", // malformed hex variant, MSB not zero.
422
423 // Different ways of saying: <
424 "<",
425 "&#0000060", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
426 "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
427 "&#60;",
428 "&#x3C;", // hex version.
429 "&#xBC;", // malformed hex variant, MSB not zero.
430 "&#x0003C;", // mid-length hex version
431 "&#X00003C;", // slightly longer hex version, with capital "X"
432
433 // Different ways of saying: >
434 ">",
435 "&#0000062;", // Long UTF-8 Unicode encoding
436 "&#62;",
437 "&#x3E;", // hex version.
438 "&#xBE;", // malformed variant, MSB not zero.
439
440 // Different ways of saying: [
441 "&#0000091;", // Long UTF-8 Unicode encoding
442 "&#91;",
443 "&#x5B;", // hex version.
444
445 // Different ways of saying: {{
446 "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
447 "&#123;&#123;",
448 "&#x7B;&#x7B;", // hex version.
449
450 // Different ways of saying: |
451 "&#0000124;", // Long UTF-8 Unicode encoding
452 "&#124;",
453 "&#x7C;", // hex version.
454 "&#xFC;", // malformed hex variant, MSB not zero.
455
456 // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
457 "&zwnj;"
458 );
459
460 // Defines various wiki-related bits of syntax, that can potentially cause
461 // MediaWiki to do something other than just print that literal text.
462 static private $ext = array(
463 // links, templates, parameters.
464 "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
465
466 // wiki tables.
467 "\n{|", "\n|}",
468 "!",
469 "\n!",
470 "!!",
471 "||",
472 "\n|-", "| ", "\n|",
473
474 // section headings.
475 "=", "==", "===", "====", "=====", "======",
476
477 // lists (ordered and unordered) and indentation.
478 "\n*", "*", "\n:", ":",
479 "\n#", "#",
480
481 // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
482 "\n;", ";", "\n ",
483
484 // Whitespace: newline, tab, space.
485 "\n", "\t", " ",
486
487 // Some XSS attack vectors from http://ha.ckers.org/xss.html
488 "&#x09;", // tab
489 "&#x0A;", // newline
490 "&#x0D;", // carriage return
491 "\0", // null character
492 " &#14; ", // spaces and meta characters
493 "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
494
495 // various NULL fields
496 "%00",
497 "&#00;",
498 "\0",
499
500 // horizontal rule.
501 "-----", "\n-----",
502
503 // signature, redirect, bold, italics.
504 "~~~~", "#REDIRECT [[", "'''", "''",
505
506 // comments.
507 "<!--", "-->",
508
509 // quotes.
510 "\"", "'",
511
512 // tag start and tag end.
513 "<", ">",
514
515 // implicit link creation on URIs.
516 "http://",
517 "https://",
518 "ftp://",
519 "irc://",
520 "news:",
521 'gopher://',
522 'telnet://',
523 'nntp://',
524 'worldwind://',
525 'mailto:',
526
527 // images.
528 "[[image:",
529 ".gif",
530 ".png",
531 ".jpg",
532 ".jpeg",
533 'thumbnail=',
534 'thumbnail',
535 'thumb=',
536 'thumb',
537 'right',
538 'none',
539 'left',
540 'framed',
541 'frame',
542 'enframed',
543 'centre',
544 'center',
545 "Image:",
546 "[[:Image",
547 'px',
548
549 // misc stuff to throw at the Parser.
550 '%08X',
551 '/',
552 ":x{|",
553 "\n|+",
554 "<noinclude>",
555 "</noinclude>",
556 " \302\273",
557 " :",
558 " !",
559 " ;",
560 "\302\253",
561 "[[category:",
562 "?=",
563 "(",
564 ")",
565 "]]]",
566 "../",
567 "{{{{",
568 "}}}}",
569 "[[Special:",
570 "<includeonly>",
571 "</includeonly>",
572 "<!--MWTEMPLATESECTION=",
573 '<!--MWTOC-->',
574
575 // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
576 "ISBN 2",
577 "RFC 000",
578 "PMID 000",
579 "ISBN ",
580 "RFC ",
581 "PMID ",
582
583 // magic words:
584 '__NOTOC__',
585 '__FORCETOC__',
586 '__NOEDITSECTION__',
587 '__START__',
588 '__NOTITLECONVERT__',
589 '__NOCONTENTCONVERT__',
590 '__END__',
591 '__TOC__',
592 '__NOTC__',
593 '__NOCC__',
594 "__FORCETOC__",
595 "__NEWSECTIONLINK__",
596 "__NOGALLERY__",
597
598 // more magic words / internal templates.
599 '{{PAGENAME}}',
600 '{{PAGENAMEE}}',
601 '{{NAMESPACE}}',
602 "{{MSG:",
603 "}}",
604 "{{MSGNW:",
605 "}}",
606 "{{INT:",
607 "}}",
608 '{{SITENAME}}',
609 "{{NS:",
610 "}}",
611 "{{LOCALURL:",
612 "}}",
613 "{{LOCALURLE:",
614 "}}",
615 "{{SCRIPTPATH}}",
616 "{{GRAMMAR:gentiv|",
617 "}}",
618 "{{REVISIONID}}",
619 "{{SUBPAGENAME}}",
620 "{{SUBPAGENAMEE}}",
621 "{{ns:0}}",
622 "{{fullurle:",
623 "}}",
624 "{{subst:",
625 "}}",
626 "{{UCFIRST:",
627 "}}",
628 "{{UC:",
629 '{{SERVERNAME}}',
630 '{{SERVER}}',
631 "{{RAW:",
632 "}}",
633 "{{PLURAL:",
634 "}}",
635 "{{LCFIRST:",
636 "}}",
637 "{{LC:",
638 "}}",
639 '{{CURRENTWEEK}}',
640 '{{CURRENTDOW}}',
641 "{{INT:{{LC:contribs-showhideminor}}|",
642 "}}",
643 "{{INT:googlesearch|",
644 "}}",
645 "{{BASEPAGENAME}}",
646 "{{CONTENTLANGUAGE}}",
647 "{{PAGESINNAMESPACE:}}",
648 "{{#language:",
649 "}}",
650 "{{#special:",
651 "}}",
652 "{{#special:emailuser",
653 "}}",
654
655 // Some raw link for magic words.
656 "{{NUMBEROFPAGES:R",
657 "}}",
658 "{{NUMBEROFUSERS:R",
659 "}}",
660 "{{NUMBEROFARTICLES:R",
661 "}}",
662 "{{NUMBEROFFILES:R",
663 "}}",
664 "{{NUMBEROFADMINS:R",
665 "}}",
666 "{{padleft:",
667 "}}",
668 "{{padright:",
669 "}}",
670 "{{DEFAULTSORT:",
671 "}}",
672
673 // internal Math "extension":
674 "<math>",
675 "</math>",
676
677 // Parser extension functions:
678 "{{#expr:",
679 "{{#if:",
680 "{{#ifeq:",
681 "{{#ifexist:",
682 "{{#ifexpr:",
683 "{{#switch:",
684 "{{#time:",
685 "}}",
686
687 // references table for the Cite extension.
688 "<references/>",
689
690 // Internal Parser tokens - try inserting some of these.
691 "UNIQ25f46b0524f13e67NOPARSE",
692 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
693 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
694
695 // Inputbox extension:
696 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
697 "</inputbox>",
698
699 // charInsert extension:
700 "<charInsert>",
701 "</charInsert>",
702
703 // wikiHiero extension:
704 "<hiero>",
705 "</hiero>",
706
707 // Image gallery:
708 "<gallery>",
709 "</gallery>",
710
711 // FixedImage extension.
712 "<fundraising/>",
713
714 // Timeline extension: currently untested.
715
716 // Nowiki:
717 "<nOwIkI>",
718 "</nowiki>",
719
720 // an external image to test the external image displaying code
721 "http://debian.org/Pics/debian.png",
722
723 // LabeledSectionTransclusion extension.
724 "{{#lstx:",
725 "}}",
726 "{{#lst:",
727 "}}",
728 "{{#lst:Main Page|",
729 "}}"
730 );
731
732 /**
733 ** Randomly returns one element of the input array.
734 */
735 static public function chooseInput(array $input) {
736 $randindex = wikiFuzz::randnum(count($input) - 1);
737 return $input[$randindex];
738 }
739
740 // Max number of parameters for HTML attributes.
741 static private $maxparams = 10;
742
743 /**
744 ** Returns random number between finish and start.
745 */
746 static public function randnum($finish,$start=0) {
747 return mt_rand($start,$finish);
748 }
749
750 /**
751 ** Returns a mix of random text and random wiki syntax.
752 */
753 static private function randstring() {
754 $thestring = "";
755
756 for ($i=0; $i<40; $i++) {
757 $what = wikiFuzz::randnum(1);
758
759 if ($what == 0) { // include some random wiki syntax
760 $which = wikiFuzz::randnum(count(wikiFuzz::$ext) - 1);
761 $thestring .= wikiFuzz::$ext[$which];
762 }
763 else { // include some random text
764 $char = INCLUDE_BINARY
765 // Decimal version:
766 // "&#" . wikiFuzz::randnum(255) . ";"
767 // Hex version:
768 ? "&#x" . str_pad(dechex(wikiFuzz::randnum(255)), wikiFuzz::randnum(2, 7), "0", STR_PAD_LEFT) . ";"
769 // A truly binary version:
770 // ? chr(wikiFuzz::randnum(0,255))
771 : chr(wikiFuzz::randnum(126,32));
772
773 $length = wikiFuzz::randnum(8);
774 $thestring .= str_repeat ($char, $length);
775 }
776 }
777 return $thestring;
778 }
779
780 /**
781 ** Returns either random text, or random wiki syntax, or random data from "ints",
782 ** or random data from "other".
783 */
784 static private function makestring() {
785 $what = wikiFuzz::randnum(2);
786 if ($what == 0) {
787 return wikiFuzz::randstring();
788 }
789 elseif ($what == 1) {
790 return wikiFuzz::$ints[wikiFuzz::randnum(count(wikiFuzz::$ints) - 1)];
791 }
792 else {
793 return wikiFuzz::$other[wikiFuzz::randnum(count(wikiFuzz::$other) - 1)];
794 }
795 }
796
797
798 /**
799 ** Strips out the stuff that Mediawiki balks at in a page's title.
800 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
801 */
802 static public function makeTitleSafe($str) {
803 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
804 return preg_replace_callback(
805 "/([^$legalTitleChars])/",
806 create_function(
807 // single quotes are essential here,
808 // or alternative escape all $ as \$
809 '$matches',
810 'return sprintf( "\\x%02x", ord( $matches[1] ) );'
811 ),
812 $str );
813 }
814
815 /**
816 ** Returns a string of fuzz text.
817 */
818 static private function loop() {
819 switch ( wikiFuzz::randnum(3) ) {
820 case 1: // an opening tag, with parameters.
821 $string = "";
822 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
823 $t = wikiFuzz::$types[$i];
824 $arr = wikiFuzz::$data[$t];
825 $string .= "<" . $t . " ";
826 $num_params = min(wikiFuzz::$maxparams, count($arr));
827 for ($z=0; $z<$num_params; $z++) {
828 $badparam = $arr[wikiFuzz::randnum(count($arr) - 1)];
829 $badstring = wikiFuzz::makestring();
830 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
831 }
832 $string .= ">\n";
833 return $string;
834 case 2: // a closing tag.
835 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
836 return "</". wikiFuzz::$types[$i] . ">";
837 case 3: // a random string, between tags.
838 return wikiFuzz::makeString();
839 }
840 return ""; // catch-all, should never be called.
841 }
842
843 /**
844 ** Returns one of the three styles of random quote: ', ", and nothing.
845 */
846 static private function getRandQuote() {
847 switch ( wikiFuzz::randnum(3) ) {
848 case 1 : return "'";
849 case 2 : return "\"";
850 default: return "";
851 }
852 }
853
854 /**
855 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
856 */
857 static public function makeFuzz($maxtypes = 2) {
858 $page = "";
859 for ($k=0; $k<$maxtypes; $k++) {
860 $page .= wikiFuzz::loop();
861 }
862 return $page;
863 }
864 }
865
866
867 //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
868
869 /**
870 ** A page test has just these things:
871 ** 1) Form parameters.
872 ** 2) the URL we are going to test those parameters on.
873 ** 3) Any cookies required for the test.
874 ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off.
875 ** Declared abstract because it should be extended by a class
876 ** that supplies these parameters.
877 */
878 abstract class pageTest {
879 protected $params;
880 protected $pagePath;
881 protected $cookie = "";
882 protected $tidyValidate = true;
883
884 public function getParams() {
885 return $this->params;
886 }
887
888 public function getPagePath() {
889 return $this->pagePath;
890 }
891
892 public function getCookie() {
893 return $this->cookie;
894 }
895
896 public function tidyValidate() {
897 return $this->tidyValidate;
898 }
899 }
900
901
902 /**
903 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
904 */
905 class editPageTest extends pageTest {
906 function __construct() {
907 $this->pagePath = "index.php?title=WIKIFUZZ";
908
909 $this->params = array (
910 "action" => "submit",
911 "wpMinoredit" => wikiFuzz::makeFuzz(2),
912 "wpPreview" => wikiFuzz::makeFuzz(2),
913 "wpSection" => wikiFuzz::makeFuzz(2),
914 "wpEdittime" => wikiFuzz::makeFuzz(2),
915 "wpSummary" => wikiFuzz::makeFuzz(2),
916 "wpScrolltop" => wikiFuzz::makeFuzz(2),
917 "wpStarttime" => wikiFuzz::makeFuzz(2),
918 "wpAutoSummary" => wikiFuzz::makeFuzz(2),
919 "wpTextbox1" => wikiFuzz::makeFuzz(40) // the main wiki text, need lots of this.
920 );
921
922 // sometimes we don't want to specify certain parameters.
923 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSection"]);
924 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEdittime"]);
925 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSummary"]);
926 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpScrolltop"]);
927 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpStarttime"]);
928 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpAutoSummary"]);
929 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpTextbox1"]);
930 }
931 }
932
933
934 /**
935 ** a page test for "Special:Listusers".
936 */
937 class listusersTest extends pageTest {
938 function __construct() {
939 $this->pagePath = "index.php?title=Special:Listusers";
940
941 $this->params = array (
942 "title" => wikiFuzz::makeFuzz(2),
943 "group" => wikiFuzz::makeFuzz(2),
944 "username" => wikiFuzz::makeFuzz(2),
945 "Go" => wikiFuzz::makeFuzz(2),
946 "limit" => wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
947 "offset" => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) )
948 );
949 }
950 }
951
952
953 /**
954 ** a page test for "Special:Search".
955 */
956 class searchTest extends pageTest {
957 function __construct() {
958 $this->pagePath = "index.php?title=Special:Search";
959
960 $this->params = array (
961 "action" => "index.php?title=Special:Search",
962 "ns0" => wikiFuzz::makeFuzz(2),
963 "ns1" => wikiFuzz::makeFuzz(2),
964 "ns2" => wikiFuzz::makeFuzz(2),
965 "ns3" => wikiFuzz::makeFuzz(2),
966 "ns4" => wikiFuzz::makeFuzz(2),
967 "ns5" => wikiFuzz::makeFuzz(2),
968 "ns6" => wikiFuzz::makeFuzz(2),
969 "ns7" => wikiFuzz::makeFuzz(2),
970 "ns8" => wikiFuzz::makeFuzz(2),
971 "ns9" => wikiFuzz::makeFuzz(2),
972 "ns10" => wikiFuzz::makeFuzz(2),
973 "ns11" => wikiFuzz::makeFuzz(2),
974 "ns12" => wikiFuzz::makeFuzz(2),
975 "ns13" => wikiFuzz::makeFuzz(2),
976 "ns14" => wikiFuzz::makeFuzz(2),
977 "ns15" => wikiFuzz::makeFuzz(2),
978 "redirs" => wikiFuzz::makeFuzz(2),
979 "search" => wikiFuzz::makeFuzz(2),
980 "offset" => wikiFuzz::chooseInput( array("", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) ),
981 "fulltext" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) ),
982 "searchx" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) )
983 );
984 }
985 }
986
987
988 /**
989 ** a page test for "Special:Recentchanges".
990 */
991 class recentchangesTest extends pageTest {
992 function __construct() {
993 $this->pagePath = "index.php?title=Special:Recentchanges";
994
995 $this->params = array (
996 "action" => wikiFuzz::makeFuzz(2),
997 "title" => wikiFuzz::makeFuzz(2),
998 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
999 "Go" => wikiFuzz::makeFuzz(2),
1000 "invert" => wikiFuzz::chooseInput( array("-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1001 "hideanons" => wikiFuzz::chooseInput( array("-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1002 'limit' => wikiFuzz::chooseInput( array("0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz(2)) ),
1003 "days" => wikiFuzz::chooseInput( array("-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1004 "hideminor" => wikiFuzz::chooseInput( array("-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1005 "hidebots" => wikiFuzz::chooseInput( array("-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1006 "hideliu" => wikiFuzz::chooseInput( array("-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1007 "hidepatrolled" => wikiFuzz::chooseInput( array("-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1008 "hidemyself" => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1009 'categories_any'=> wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1010 'categories' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1011 'feed' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) )
1012 );
1013 }
1014 }
1015
1016
1017 /**
1018 ** a page test for "Special:Prefixindex".
1019 */
1020 class prefixindexTest extends pageTest {
1021 function __construct() {
1022 $this->pagePath = "index.php?title=Special:Prefixindex";
1023
1024 $this->params = array (
1025 "title" => "Special:Prefixindex",
1026 "namespace" => wikiFuzz::randnum(-10,101),
1027 "Go" => wikiFuzz::makeFuzz(2)
1028 );
1029
1030 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
1031 if (wikiFuzz::randnum(3) == 0) {
1032 $this->params["prefix"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
1033 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1034 }
1035 if (wikiFuzz::randnum(3) == 0) {
1036 $this->params["from"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
1037 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1038 }
1039 }
1040 }
1041
1042
1043 /**
1044 ** a page test for "Special:MIMEsearch".
1045 */
1046 class mimeSearchTest extends pageTest {
1047 function __construct() {
1048 $this->pagePath = "index.php?title=Special:MIMEsearch";
1049
1050 $this->params = array (
1051 "action" => "index.php?title=Special:MIMEsearch",
1052 "mime" => wikiFuzz::makeFuzz(3),
1053 'limit' => wikiFuzz::chooseInput( array("0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz(2)) ),
1054 'offset' => wikiFuzz::chooseInput( array("0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz(2)) )
1055 );
1056 }
1057 }
1058
1059
1060 /**
1061 ** a page test for "Special:Log".
1062 */
1063 class specialLogTest extends pageTest {
1064 function __construct() {
1065 $this->pagePath = "index.php?title=Special:Log";
1066
1067 $this->params = array (
1068 "type" => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1069 "par" => wikiFuzz::makeFuzz(2),
1070 "user" => wikiFuzz::makeFuzz(2),
1071 "page" => wikiFuzz::makeFuzz(2),
1072 "from" => wikiFuzz::makeFuzz(2),
1073 "until" => wikiFuzz::makeFuzz(2),
1074 "title" => wikiFuzz::makeFuzz(2)
1075 );
1076 }
1077 }
1078
1079
1080 /**
1081 ** a page test for "Special:Userlogin", with a successful login.
1082 */
1083 class successfulUserLoginTest extends pageTest {
1084 function __construct() {
1085 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz(2);
1086
1087 $this->params = array (
1088 "wpName" => USER_ON_WIKI,
1089 // sometimes real password, sometimes not:
1090 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2), USER_PASSWORD ) ),
1091 'wpRemember' => wikiFuzz::makeFuzz(2)
1092 );
1093
1094 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1095 }
1096 }
1097
1098
1099 /**
1100 ** a page test for "Special:Userlogin".
1101 */
1102 class userLoginTest extends pageTest {
1103 function __construct() {
1104
1105 $this->pagePath = "index.php?title=Special:Userlogin";
1106
1107 $this->params = array (
1108 'wpRetype' => wikiFuzz::makeFuzz(2),
1109 'wpRemember' => wikiFuzz::makeFuzz(2),
1110 'wpRealName' => wikiFuzz::makeFuzz(2),
1111 'wpPassword' => wikiFuzz::makeFuzz(2),
1112 'wpName' => wikiFuzz::makeFuzz(2),
1113 'wpMailmypassword'=> wikiFuzz::makeFuzz(2),
1114 'wpLoginattempt' => wikiFuzz::makeFuzz(2),
1115 'wpEmail' => wikiFuzz::makeFuzz(2),
1116 'wpDomain' => wikiFuzz::chooseInput( array("", "local", wikiFuzz::makeFuzz(2)) ),
1117 'wpCreateaccountMail' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1118 'wpCreateaccount' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1119 'wpCookieCheck' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1120 'type' => wikiFuzz::chooseInput( array("signup", "login", "", wikiFuzz::makeFuzz(2)) ),
1121 'returnto' => wikiFuzz::makeFuzz(2),
1122 'action' => wikiFuzz::chooseInput( array("", "submitlogin", wikiFuzz::makeFuzz(2)) )
1123 );
1124
1125 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1126 }
1127 }
1128
1129
1130 /**
1131 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1132 */
1133 class ipblocklistTest extends pageTest {
1134 function __construct() {
1135 $this->pagePath = "index.php?title=Special:Ipblocklist";
1136
1137 $this->params = array (
1138 'wpUnblockAddress'=> wikiFuzz::makeFuzz(2),
1139 'ip' => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1140 // something like an IP address, sometimes invalid:
1141 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1142 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1143 'id' => wikiFuzz::makeFuzz(2),
1144 'wpUnblockReason' => wikiFuzz::makeFuzz(2),
1145 'action' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "success", "submit", "unblock") ),
1146 'wpEditToken' => wikiFuzz::makeFuzz(2),
1147 'wpBlock' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "") ),
1148 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1",
1149 "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1150 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1",
1151 "09700980982341535324234234", wikiFuzz::makeFuzz(2)) )
1152 );
1153
1154 // sometimes we don't want to specify certain parameters.
1155 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1156 if (wikiFuzz::randnum(3) == 0) unset($this->params["ip"]);
1157 if (wikiFuzz::randnum(2) == 0) unset($this->params["id"]);
1158 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpUnblockAddress"]);
1159 }
1160 }
1161
1162
1163 /**
1164 ** a page test for "Special:Newimages".
1165 */
1166 class newImagesTest extends pageTest {
1167 function __construct() {
1168 $this->pagePath = "index.php?title=Special:Newimages";
1169
1170 $this->params = array (
1171 'hidebots' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "1", "", "-1") ),
1172 'wpIlMatch' => wikiFuzz::makeFuzz(2),
1173 'until' => wikiFuzz::makeFuzz(2),
1174 'from' => wikiFuzz::makeFuzz(2)
1175 );
1176
1177 // sometimes we don't want to specify certain parameters.
1178 if (wikiFuzz::randnum(6) == 0) unset($this->params["until"]);
1179 if (wikiFuzz::randnum(6) == 0) unset($this->params["from"]);
1180 }
1181 }
1182
1183
1184 /**
1185 ** a page test for the "Special:Imagelist" page.
1186 */
1187 class imagelistTest extends pageTest {
1188 function __construct() {
1189 $this->pagePath = "index.php?title=Special:Imagelist";
1190
1191 $this->params = array (
1192 'sort' => wikiFuzz::chooseInput( array("bysize", "byname" , "bydate", wikiFuzz::makeFuzz(2)) ),
1193 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1194 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1195 'wpIlMatch' => wikiFuzz::makeFuzz(2)
1196 );
1197 }
1198 }
1199
1200
1201 /**
1202 ** a page test for "Special:Export".
1203 */
1204 class specialExportTest extends pageTest {
1205 function __construct() {
1206 $this->pagePath = "index.php?title=Special:Export";
1207
1208 $this->params = array (
1209 'action' => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1210 'pages' => wikiFuzz::makeFuzz(2),
1211 'curonly' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1212 'listauthors' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1213 'history' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1214
1215 );
1216
1217 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1218 if ($this->params['action'] == 'submit') $this->params['action'] = '';
1219
1220 // Sometimes remove the history field.
1221 if (wikiFuzz::randnum(2) == 0) unset($this->params["history"]);
1222
1223 // page does not produce HTML.
1224 $this->tidyValidate = false;
1225 }
1226 }
1227
1228
1229 /**
1230 ** a page test for "Special:Booksources".
1231 */
1232 class specialBooksourcesTest extends pageTest {
1233 function __construct() {
1234 $this->pagePath = "index.php?title=Special:Booksources";
1235
1236 $this->params = array (
1237 'go' => wikiFuzz::makeFuzz(2),
1238 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1239 'isbn' => "0X0" . wikiFuzz::makeFuzz(2)
1240 );
1241 }
1242 }
1243
1244
1245 /**
1246 ** a page test for "Special:Allpages".
1247 */
1248 class specialAllpagesTest extends pageTest {
1249 function __construct() {
1250 $this->pagePath = "index.php?title=Special%3AAllpages";
1251
1252 $this->params = array (
1253 'from' => wikiFuzz::makeFuzz(2),
1254 'namespace' => wikiFuzz::chooseInput( range(-1, 15) ),
1255 'go' => wikiFuzz::makeFuzz(2)
1256 );
1257 }
1258 }
1259
1260
1261 /**
1262 ** a page test for the page History.
1263 */
1264 class pageHistoryTest extends pageTest {
1265 function __construct() {
1266 $this->pagePath = "index.php?title=Main_Page&action=history";
1267
1268 $this->params = array (
1269 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1270 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1271 "go" => wikiFuzz::chooseInput( array("first", "last", wikiFuzz::makeFuzz(2)) ),
1272 "dir" => wikiFuzz::chooseInput( array("prev", "next", wikiFuzz::makeFuzz(2)) ),
1273 "diff" => wikiFuzz::chooseInput( array("-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1274 "oldid" => wikiFuzz::chooseInput( array("prev", "-1", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1275 "feed" => wikiFuzz::makeFuzz(2)
1276 );
1277 }
1278 }
1279
1280
1281 /**
1282 ** a page test for the Special:Contributions".
1283 */
1284 class contributionsTest extends pageTest {
1285 function __construct() {
1286 $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
1287
1288 $this->params = array (
1289 'target' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "newbies", USER_ON_WIKI) ),
1290 'namespace' => wikiFuzz::chooseInput( array(-1, 15, 1, wikiFuzz::makeFuzz(2)) ),
1291 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz(2)) ),
1292 'bot' => wikiFuzz::chooseInput( array("", "-1", "0", "1", wikiFuzz::makeFuzz(2)) ),
1293 'go' => wikiFuzz::chooseInput( array("-1", 'prev', 'next', wikiFuzz::makeFuzz(2)) )
1294 );
1295 }
1296 }
1297
1298
1299 /**
1300 ** a page test for viewing a normal page, whilst posting various params.
1301 */
1302 class viewPageTest extends pageTest {
1303 function __construct() {
1304 $this->pagePath = "index.php?title=Main_Page";
1305
1306 $this->params = array (
1307 "useskin" => wikiFuzz::chooseInput( array("chick", "cologneblue", "myskin",
1308 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz(2)) ),
1309 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2),
1310 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1311 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1312 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1313 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1314 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1315 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1316 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1317 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1318 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1319 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el", "sr-jc", "sr-jl",
1320 "su", "sv", "ta", "te", "th", "tlh", "tr", "tt", "ty", "tyv", "udm",
1321 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1322 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw") ),
1323 "returnto" => wikiFuzz::makeFuzz(2),
1324 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1325 "rcid" => wikiFuzz::makeFuzz(2),
1326 "action" => wikiFuzz::chooseInput( array("view", "raw", "render", wikiFuzz::makeFuzz(2), "markpatrolled") ),
1327 "printable" => wikiFuzz::makeFuzz(2),
1328 "oldid" => wikiFuzz::makeFuzz(2),
1329 "redirect" => wikiFuzz::makeFuzz(2),
1330 "diff" => wikiFuzz::makeFuzz(2),
1331 "search" => wikiFuzz::makeFuzz(2),
1332 "rdfrom" => wikiFuzz::makeFuzz(2), // things from Article.php from here on:
1333 "token" => wikiFuzz::makeFuzz(2),
1334 "tbid" => wikiFuzz::makeFuzz(2),
1335 "action" => wikiFuzz::chooseInput( array("purge", wikiFuzz::makeFuzz(2)) ),
1336 "wpReason" => wikiFuzz::makeFuzz(2),
1337 "wpEditToken" => wikiFuzz::makeFuzz(2),
1338 "from" => wikiFuzz::makeFuzz(2),
1339 "bot" => wikiFuzz::makeFuzz(2),
1340 "summary" => wikiFuzz::makeFuzz(2),
1341 "direction" => wikiFuzz::chooseInput( array("next", "prev", wikiFuzz::makeFuzz(2)) ),
1342 "section" => wikiFuzz::makeFuzz(2),
1343 "preload" => wikiFuzz::makeFuzz(2),
1344
1345 );
1346
1347 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1348 if ($this->params["feed"] == "atom") { unset($this->params["feed"]); }
1349 else if ($this->params["feed"] == "rss") { unset($this->params["feed"]); }
1350
1351 // Raw pages cannot really be validated
1352 if ($this->params["action"] == "raw") unset($this->params["action"]);
1353
1354 // sometimes we don't want to specify certain parameters.
1355 if (wikiFuzz::randnum(6) == 0) unset($this->params["rcid"]);
1356 if (wikiFuzz::randnum(6) == 0) unset($this->params["diff"]);
1357 if (wikiFuzz::randnum(6) == 0) unset($this->params["rdfrom"]);
1358 if (wikiFuzz::randnum(3) == 0) unset($this->params["oldid"]);
1359
1360 // usually don't want action == purge.
1361 if (wikiFuzz::randnum(6) > 1) unset($this->params["action"]);
1362 }
1363 }
1364
1365
1366 /**
1367 ** a page test for "Special:Allmessages".
1368 */
1369 class specialAllmessagesTest extends pageTest {
1370 function __construct() {
1371 $this->pagePath = "index.php?title=Special:Allmessages";
1372
1373 // only really has one parameter
1374 $this->params = array (
1375 "ot" => wikiFuzz::chooseInput( array("php", "html", wikiFuzz::makeFuzz(2)) )
1376 );
1377 }
1378 }
1379
1380 /**
1381 ** a page test for "Special:Newpages".
1382 */
1383 class specialNewpages extends pageTest {
1384 function __construct() {
1385 $this->pagePath = "index.php?title=Special:Newpages";
1386
1387 $this->params = array (
1388 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
1389 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1390 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1391 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) )
1392 );
1393
1394 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1395 if ($this->params["feed"] == "atom") { unset($this->params["feed"]); }
1396 else if ($this->params["feed"] == "rss") { unset($this->params["feed"]); }
1397 }
1398 }
1399
1400 /**
1401 ** a page test for "redirect.php"
1402 */
1403 class redirectTest extends pageTest {
1404 function __construct() {
1405 $this->pagePath = "redirect.php";
1406
1407 $this->params = array (
1408 "wpDropdown" => wikiFuzz::makeFuzz(2)
1409 );
1410
1411 // sometimes we don't want to specify certain parameters.
1412 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpDropdown"]);
1413 }
1414 }
1415
1416
1417 /**
1418 ** a page test for "Special:Confirmemail"
1419 */
1420 class confirmEmail extends pageTest {
1421 function __construct() {
1422 // sometimes we send a bogus confirmation code, and sometimes we don't.
1423 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array("", "/" . wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(1)) ) );
1424
1425 $this->params = array (
1426 "token" => wikiFuzz::makeFuzz(2)
1427 );
1428 }
1429 }
1430
1431
1432 /**
1433 ** a page test for "Special:Watchlist"
1434 ** Note: this test would be better if we were logged in.
1435 */
1436 class watchlistTest extends pageTest {
1437 function __construct() {
1438 $this->pagePath = "index.php?title=Special:Watchlist";
1439
1440 $this->params = array (
1441 "remove" => wikiFuzz::chooseInput( array("Remove checked items from watchlist", wikiFuzz::makeFuzz(2))),
1442 'days' => wikiFuzz::chooseInput( array(0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz(2)) ),
1443 'hideOwn' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1444 'hideBots' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1445 'namespace'=> wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1446 'action' => wikiFuzz::chooseInput( array("submit", "clear", wikiFuzz::makeFuzz(2)) ),
1447 'id[]' => wikiFuzz::makeFuzz(2),
1448 'edit' => wikiFuzz::makeFuzz(2),
1449 'token' => wikiFuzz::chooseInput( array("", "1243213", wikiFuzz::makeFuzz(2)) )
1450 );
1451
1452 // sometimes we specifiy "reset", and sometimes we don't.
1453 if (wikiFuzz::randnum(3) == 0) $this->params["reset"] = wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) );
1454 }
1455 }
1456
1457
1458 /**
1459 ** a page test for "Special:Blockme"
1460 */
1461 class specialBlockmeTest extends pageTest {
1462 function __construct() {
1463 $this->pagePath = "index.php?title=Special:Blockme";
1464
1465 $this->params = array ( );
1466
1467 // sometimes we specify "ip", and sometimes we don't.
1468 if (wikiFuzz::randnum(1) == 0) {
1469 $this->params["ip"] = wikiFuzz::chooseInput( array("10.12.41.213", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1470 }
1471 }
1472 }
1473
1474
1475 /**
1476 ** a page test for "Special:Movepage"
1477 */
1478 class specialMovePage extends pageTest {
1479 function __construct() {
1480 $this->pagePath = "index.php?title=Special:Movepage";
1481
1482 $this->params = array (
1483 "action" => wikiFuzz::chooseInput( array("success", "submit", "", wikiFuzz::makeFuzz(2)) ),
1484 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1485 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1486 'wpOldTitle' => wikiFuzz::chooseInput( array("z", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1487 'wpNewTitle' => wikiFuzz::chooseInput( array("y", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1488 'wpReason' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2)) ),
1489 'wpMovetalk' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1490 'wpDeleteAndMove' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1491 'wpConfirm' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1492 'talkmoved' => wikiFuzz::chooseInput( array("1", wikiFuzz::makeFuzz(2), "articleexists", 'notalkpage') ),
1493 'oldtitle' => wikiFuzz::makeFuzz(2),
1494 'newtitle' => wikiFuzz::makeFuzz(2),
1495 'wpMovetalk' => wikiFuzz::chooseInput( array("1", "0", wikiFuzz::makeFuzz(2)) )
1496 );
1497
1498 // sometimes we don't want to specify certain parameters.
1499 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1500 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1501 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpNewTitle"]);
1502 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpReason"]);
1503 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpOldTitle"]);
1504 }
1505 }
1506
1507
1508 /**
1509 ** a page test for "Special:Undelete"
1510 */
1511 class specialUndelete extends pageTest {
1512 function __construct() {
1513 $this->pagePath = "index.php?title=Special:Undelete";
1514
1515 $this->params = array (
1516 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1517 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1518 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1519 'timestamp' => wikiFuzz::chooseInput( array("125223", wikiFuzz::makeFuzz(2) ) ),
1520 'file' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1521 'restore' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1522 'preview' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1523 'wpComment' => wikiFuzz::makeFuzz(2)
1524 );
1525
1526 // sometimes we don't want to specify certain parameters.
1527 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1528 if (wikiFuzz::randnum(4) == 0) unset($this->params["target"]);
1529 if (wikiFuzz::randnum(1) == 0) unset($this->params["restore"]);
1530 if (wikiFuzz::randnum(1) == 0) unset($this->params["preview"]);
1531 }
1532 }
1533
1534
1535 /**
1536 ** a page test for "Special:Unlockdb"
1537 */
1538 class specialUnlockdb extends pageTest {
1539 function __construct() {
1540 $this->pagePath = "index.php?title=Special:Unlockdb";
1541
1542 $this->params = array (
1543 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1544 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1545 'wpLockConfirm' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) )
1546 );
1547
1548 // sometimes we don't want to specify certain parameters.
1549 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1550 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1551 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1552 }
1553 }
1554
1555
1556 /**
1557 ** a page test for "Special:Lockdb"
1558 */
1559 class specialLockdb extends pageTest {
1560 function __construct() {
1561 $this->pagePath = "index.php?title=Special:Lockdb";
1562
1563 $this->params = array (
1564 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1565 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1566 'wpLockReason' => wikiFuzz::makeFuzz(2),
1567 'wpLockConfirm'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1568 );
1569
1570 // sometimes we don't want to specify certain parameters.
1571 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1572 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1573 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1574 }
1575 }
1576
1577
1578 /**
1579 ** a page test for "Special:Userrights"
1580 */
1581 class specialUserrights extends pageTest {
1582 function __construct() {
1583 $this->pagePath = "index.php?title=Special:Userrights";
1584
1585 $this->params = array (
1586 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1587 'user-editname' => wikiFuzz::chooseInput( array("Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz(2)) ),
1588 'ssearchuser' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1589 'saveusergroups'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)), "Save User Groups"),
1590 'member[]' => wikiFuzz::chooseInput( array("0", "bot", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1591 "available[]" => wikiFuzz::chooseInput( array("0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1592 );
1593
1594 // sometimes we don't want to specify certain parameters.
1595 if (wikiFuzz::randnum(3) == 0) unset($this->params['ssearchuser']);
1596 if (wikiFuzz::randnum(3) == 0) unset($this->params['saveusergroups']);
1597 }
1598 }
1599
1600
1601 /**
1602 ** a test for page protection and unprotection.
1603 */
1604 class pageProtectionForm extends pageTest {
1605 function __construct() {
1606 $this->pagePath = "index.php?title=Main_Page";
1607
1608 $this->params = array (
1609 "action" => "protect",
1610 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1611 "mwProtect-level-edit" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1612 "mwProtect-level-move" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1613 "mwProtectUnchained" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1614 'mwProtect-reason' => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) )
1615 );
1616
1617
1618 // sometimes we don't want to specify certain parameters.
1619 if (wikiFuzz::randnum(3) == 0) unset($this->params["mwProtectUnchained"]);
1620 if (wikiFuzz::randnum(3) == 0) unset($this->params['mwProtect-reason']);
1621 }
1622 }
1623
1624
1625 /**
1626 ** a page test for "Special:Blockip".
1627 */
1628 class specialBlockip extends pageTest {
1629 function __construct() {
1630 $this->pagePath = "index.php?title=Special:Blockip";
1631
1632 $this->params = array (
1633 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1634 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1635 "wpBlockAddress" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1636 // something like an IP address, sometimes invalid:
1637 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1638 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1639 "ip" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1640 // something like an IP address, sometimes invalid:
1641 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1642 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1643 "wpBlockOther" => wikiFuzz::chooseInput( array('', 'Nickj2', wikifuzz::makeFuzz(2)) ),
1644 "wpBlockExpiry" => wikiFuzz::chooseInput( array("other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1645 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz(2)) ),
1646 "wpBlockReason" => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) ),
1647 "wpAnonOnly" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1648 "wpCreateAccount" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1649 "wpBlock" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1650 );
1651
1652 // sometimes we don't want to specify certain parameters.
1653 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockOther"]);
1654 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockExpiry"]);
1655 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockReason"]);
1656 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpAnonOnly"]);
1657 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpCreateAccount"]);
1658 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockAddress"]);
1659 if (wikiFuzz::randnum(4) == 0) unset($this->params["ip"]);
1660 }
1661 }
1662
1663
1664 /**
1665 ** a test for the imagepage.
1666 */
1667 class imagepageTest extends pageTest {
1668 function __construct() {
1669 $this->pagePath = "index.php?title=Image:Small-email.png";
1670
1671 $this->params = array (
1672 "image" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1673 "wpReason" => wikifuzz::makeFuzz(2),
1674 "oldimage" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1675 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1676 );
1677
1678 // sometimes we don't want to specify certain parameters.
1679 if (wikiFuzz::randnum(6) == 0) unset($this->params["image"]);
1680 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1681 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldimage"]);
1682 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEditToken"]);
1683 }
1684 }
1685
1686
1687 /**
1688 ** a test for page deletion form.
1689 */
1690 class pageDeletion extends pageTest {
1691 function __construct() {
1692 $this->pagePath = "index.php?title=Main_Page&action=delete";
1693
1694 $this->params = array (
1695 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1696 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1697 "wpConfirm" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1698 );
1699
1700 // sometimes we don't want to specify certain parameters.
1701 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpReason"]);
1702 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpEditToken"]);
1703 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpConfirm"]);
1704 }
1705 }
1706
1707
1708
1709 /**
1710 ** a test for Revision Deletion.
1711 */
1712 class specialRevisionDelete extends pageTest {
1713 function __construct() {
1714 $this->pagePath = "index.php?title=Special:Revisiondelete";
1715
1716 $this->params = array (
1717 "target" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1718 "oldid" => wikifuzz::makeFuzz(2),
1719 "oldid[]" => wikifuzz::makeFuzz(2),
1720 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1721 "revdelete-hide-text" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1722 "revdelete-hide-comment" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1723 "revdelete-hide-user" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1724 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1725 );
1726
1727 // sometimes we don't want to specify certain parameters.
1728 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1729 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid"]);
1730 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid[]"]);
1731 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1732 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-text"]);
1733 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-comment"]);
1734 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-user"]);
1735 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-restricted"]);
1736 }
1737 }
1738
1739
1740 /**
1741 ** a test for Special:Import.
1742 */
1743 class specialImport extends pageTest {
1744 function __construct() {
1745 $this->pagePath = "index.php?title=Special:Import";
1746
1747 $this->params = array (
1748 "action" => "submit",
1749 "source" => wikiFuzz::chooseInput( array("upload", "interwiki", wikifuzz::makeFuzz(2)) ),
1750 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1751 "xmlimport" => wikiFuzz::chooseInput( array("/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1752 "namespace" => wikiFuzz::chooseInput( array(wikiFuzz::randnum(30,-6), wikiFuzz::makeFuzz(2)) ),
1753 "interwiki" => wikiFuzz::makeFuzz(2),
1754 "interwikiHistory" => wikiFuzz::makeFuzz(2),
1755 "frompage" => wikiFuzz::makeFuzz(2),
1756 );
1757
1758 // sometimes we don't want to specify certain parameters.
1759 if (wikiFuzz::randnum(6) == 0) unset($this->params["action"]);
1760 if (wikiFuzz::randnum(6) == 0) unset($this->params["source"]);
1761 if (wikiFuzz::randnum(6) == 0) unset($this->params["MAX_FILE_SIZE"]);
1762 if (wikiFuzz::randnum(6) == 0) unset($this->params["xmlimport"]);
1763 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwiki"]);
1764 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwikiHistory"]);
1765 if (wikiFuzz::randnum(6) == 0) unset($this->params["frompage"]);
1766
1767 // Note: Need to do a file upload to fully test this Special page.
1768 }
1769 }
1770
1771
1772 /**
1773 ** a test for thumb.php
1774 */
1775 class thumbTest extends pageTest {
1776 function __construct() {
1777 $this->pagePath = "thumb.php";
1778
1779 $this->params = array (
1780 "f" => wikiFuzz::chooseInput( array("..", "\\", "small-email.png", wikifuzz::makeFuzz(2)) ),
1781 "w" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1782 "r" => wikiFuzz::chooseInput( array("0", wikifuzz::makeFuzz(2)) ),
1783 );
1784
1785 // sometimes we don't want to specify certain parameters.
1786 if (wikiFuzz::randnum(6) == 0) unset($this->params["f"]);
1787 if (wikiFuzz::randnum(6) == 0) unset($this->params["w"]);
1788 if (wikiFuzz::randnum(6) == 0) unset($this->params["r"]);
1789 }
1790 }
1791
1792
1793 /**
1794 ** a test for trackback.php
1795 */
1796 class trackbackTest extends pageTest {
1797 function __construct() {
1798 $this->pagePath = "trackback.php";
1799
1800 $this->params = array (
1801 "url" => wikifuzz::makeFuzz(2),
1802 "blog_name" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1803 "article" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1804 "title" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1805 "excerpt" => wikifuzz::makeFuzz(2),
1806 );
1807
1808 // sometimes we don't want to specify certain parameters.
1809 if (wikiFuzz::randnum(3) == 0) unset($this->params["title"]);
1810 if (wikiFuzz::randnum(3) == 0) unset($this->params["excerpt"]);
1811
1812 // page does not produce HTML.
1813 $this->tidyValidate = false;
1814 }
1815 }
1816
1817
1818 /**
1819 ** a test for profileinfo.php
1820 */
1821 class profileInfo extends pageTest {
1822 function __construct() {
1823 $this->pagePath = "profileinfo.php";
1824
1825 $this->params = array (
1826 "expand" => wikifuzz::makeFuzz(2),
1827 "sort" => wikiFuzz::chooseInput( array("time", "count", "name", wikifuzz::makeFuzz(2)) ),
1828 "filter" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1829 );
1830
1831 // sometimes we don't want to specify certain parameters.
1832 if (wikiFuzz::randnum(3) == 0) unset($this->params["sort"]);
1833 if (wikiFuzz::randnum(3) == 0) unset($this->params["filter"]);
1834 }
1835 }
1836
1837
1838 /**
1839 ** a test for Special:Cite (extension Special page).
1840 */
1841 class specialCite extends pageTest {
1842 function __construct() {
1843 $this->pagePath = "index.php?title=Special:Cite";
1844
1845 $this->params = array (
1846 "page" => wikiFuzz::chooseInput( array("\" onmouseover=\"alert(1);\"", "Main Page", wikifuzz::makeFuzz(2)) ),
1847 "id" => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1848 );
1849
1850 // sometimes we don't want to specify certain parameters.
1851 if (wikiFuzz::randnum(6) == 0) unset($this->params["page"]);
1852 if (wikiFuzz::randnum(6) == 0) unset($this->params["id"]);
1853 }
1854 }
1855
1856
1857 /**
1858 ** a test for Special:Filepath (extension Special page).
1859 */
1860 class specialFilepath extends pageTest {
1861 function __construct() {
1862 $this->pagePath = "index.php?title=Special:Filepath";
1863
1864 $this->params = array (
1865 "file" => wikiFuzz::chooseInput( array("Small-email.png", "Small-email.png" . wikifuzz::makeFuzz(1), wikiFuzz::makeFuzz(2)) ),
1866 );
1867 }
1868 }
1869
1870
1871 /**
1872 ** a test for Special:Makebot (extension Special page).
1873 */
1874 class specialMakebot extends pageTest {
1875 function __construct() {
1876 $this->pagePath = "index.php?title=Special:Makebot";
1877
1878 $this->params = array (
1879 "username" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1880 "dosearch" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1881 "grant" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1882 "comment" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1883 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1884 );
1885
1886 // sometimes we don't want to specify certain parameters.
1887 if (wikiFuzz::randnum(2) == 0) unset($this->params["dosearch"]);
1888 if (wikiFuzz::randnum(2) == 0) unset($this->params["grant"]);
1889 if (wikiFuzz::randnum(5) == 0) unset($this->params["token"]);
1890 }
1891 }
1892
1893
1894 /**
1895 ** a test for Special:Makesysop (extension Special page).
1896 */
1897 class specialMakesysop extends pageTest {
1898 function __construct() {
1899 $this->pagePath = "index.php?title=Special:Makesysop";
1900
1901 $this->params = array (
1902 "wpMakesysopUser" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1903 "action" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1904 "wpMakesysopSubmit" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1905 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1906 "wpSetBureaucrat" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1907 );
1908
1909 // sometimes we don't want to specify certain parameters.
1910 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpMakesysopSubmit"]);
1911 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpEditToken"]);
1912 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpSetBureaucrat"]);
1913 }
1914 }
1915
1916
1917 /**
1918 ** a test for Special:Renameuser (extension Special page).
1919 */
1920 class specialRenameuser extends pageTest {
1921 function __construct() {
1922 $this->pagePath = "index.php?title=Special:Renameuser";
1923
1924 $this->params = array (
1925 "oldusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1926 "newusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1927 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1928 );
1929 }
1930 }
1931
1932
1933 /**
1934 ** a test for Special:Linksearch (extension Special page).
1935 */
1936 class specialLinksearch extends pageTest {
1937 function __construct() {
1938 $this->pagePath = "index.php?title=Special%3ALinksearch";
1939
1940 $this->params = array (
1941 "target" => wikifuzz::makeFuzz(2),
1942 );
1943
1944 // sometimes we don't want to specify certain parameters.
1945 if (wikiFuzz::randnum(10) == 0) unset($this->params["target"]);
1946 }
1947 }
1948
1949
1950 /**
1951 ** a test for Special:CategoryTree (extension Special page).
1952 */
1953 class specialCategoryTree extends pageTest {
1954 function __construct() {
1955 $this->pagePath = "index.php?title=Special:CategoryTree";
1956
1957 $this->params = array (
1958 "target" => wikifuzz::makeFuzz(2),
1959 "from" => wikifuzz::makeFuzz(2),
1960 "until" => wikifuzz::makeFuzz(2),
1961 "showas" => wikifuzz::makeFuzz(2),
1962 "mode" => wikiFuzz::chooseInput( array("pages", "categories", "all", wikifuzz::makeFuzz(2)) ),
1963 );
1964
1965 // sometimes we do want to specify certain parameters.
1966 if (wikiFuzz::randnum(5) == 0) $this->params["notree"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
1967 }
1968 }
1969
1970
1971 /**
1972 ** a test for "Special:Chemicalsources" (extension Special page).
1973 */
1974 class specialChemicalsourcesTest extends pageTest {
1975 function __construct() {
1976 $this->pagePath = "index.php?title=Special:Chemicalsources";
1977
1978 // choose an input format to use.
1979 $format = wikiFuzz::chooseInput(
1980 array( 'go',
1981 'CAS',
1982 'EINECS',
1983 'CHEBI',
1984 'PubChem',
1985 'SMILES',
1986 'InChI',
1987 'ATCCode',
1988 'KEGG',
1989 'RTECS',
1990 'ECNumber',
1991 'DrugBank',
1992 'Formula',
1993 'Name'
1994 )
1995 );
1996
1997 // values for different formats usually start with either letters or numbers.
1998 switch ($format) {
1999 case 'Name' : $value = "A"; break;
2000 case 'InChI' :
2001 case 'SMILES' :
2002 case 'Formula': $value = "C"; break;
2003 default : $value = "0"; break;
2004 }
2005
2006 // and then we append the fuzz input.
2007 $this->params = array ($format => $value . wikifuzz::makeFuzz(2) );
2008 }
2009 }
2010
2011
2012 /**
2013 ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats).
2014 ** Quite involved to test because there are lots of options/parameters, and because
2015 ** for a lot of the functionality if all the parameters don't make sense then it just
2016 ** returns the help screen - so currently a lot of the tests aren't actually doing much
2017 ** because something wasn't right in the query.
2018 **
2019 ** @todo: Incomplete / unfinished; Runs too fast (suggests not much testing going on).
2020 */
2021 class api extends pageTest {
2022
2023 // API login mode.
2024 private static function loginMode() {
2025 $arr = array ( "lgname" => wikifuzz::makeFuzz(2),
2026 "lgpassword" => wikifuzz::makeFuzz(2),
2027 );
2028 // sometimes we want to specify the extra "lgdomain" parameter.
2029 if (wikiFuzz::randnum(3) == 0) {
2030 $arr["lgdomain"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2031 }
2032
2033 return $arr;
2034 }
2035
2036 // API OpenSearch mode.
2037 private static function opensearchMode() {
2038 return array ("search" => wikifuzz::makeFuzz(2));
2039 }
2040
2041 // API watchlist feed mode.
2042 private static function feedwatchlistMode() {
2043 // FIXME: add "wikifuzz::makeFuzz(2)" as possible value below?
2044 return array ("feedformat" => wikiFuzz::chooseInput( array("rss", "atom") ) );
2045 }
2046
2047 // API query mode.
2048 private static function queryMode() {
2049 // FIXME: add "wikifuzz::makeFuzz(2)" as possible params for the elements below?
2050 // Suspect this will stuff up the tests more, but need to check.
2051 $params = array (
2052 // FIXME: More titles.
2053 "titles" => wikiFuzz::chooseInput( array("Main Page")),
2054 // FIXME: More pageids.
2055 "pageids" => 1,
2056 "prop" => wikiFuzz::chooseInput( array("info", "revisions", "watchlist")),
2057 "list" => wikiFuzz::chooseInput( array("allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks") ),
2058 "meta" => wikiFuzz::chooseInput( array("siteinfo")),
2059 "generator" => wikiFuzz::chooseInput( array("allpages", "logevents", "watchlist", "info", "revisions") ),
2060 "siprop" => wikiFuzz::chooseInput( array("general", "namespaces", "general|namespaces") ),
2061 );
2062
2063 // Add extra parameters based on what list choice we got.
2064 switch ($params["list"]) {
2065 case "usercontribs" : self::addListParams ($params, "uc", array("limit", "start", "end", "user", "dir") ); break;
2066 case "allpages" : self::addListParams ($params, "ap", array("from", "prefix", "namespace", "filterredir", "limit") ); break;
2067 case "watchlist" : self::addListParams ($params, "wl", array("allrev", "start", "end", "namespace", "dir", "limit", "prop") ); break;
2068 case "logevents" : self::addListParams ($params, "le", array("limit", "type", "start", "end", "user", "dir") ); break;
2069 case "recentchanges": self::addListParams ($params, "rc", array("limit", "prop", "show", "namespace", "start", "end", "dir") ); break;
2070 case "backlinks" : self::addListParams ($params, "bl", array("continue", "namespace", "redirect", "limit") ); break;
2071 case "embeddedin" : self::addListParams ($params, "ei", array("continue", "namespace", "redirect", "limit") ); break;
2072 case "imagelinks" : self::addListParams ($params, "il", array("continue", "namespace", "redirect", "limit") ); break;
2073 }
2074
2075 if ($params["prop"] == "revisions") {
2076 self::addListParams ($params, "rv", array("prop", "limit", "startid", "endid", "end", "dir") );
2077 }
2078
2079 // Sometimes we want redirects, sometimes we don't.
2080 if (wikiFuzz::randnum(3) == 0) {
2081 $params["redirects"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2082 }
2083
2084 return $params;
2085 }
2086
2087 // Adds all the elements to the array, using the specified prefix.
2088 private static function addListParams(&$array, $prefix, $elements) {
2089 foreach ($elements as $element) {
2090 $array[$prefix . $element] = self::getParamDetails($element);
2091 }
2092 }
2093
2094 // For a given element name, returns the data for that element.
2095 private static function getParamDetails($element) {
2096 switch ($element) {
2097 case 'startid' :
2098 case 'endid' :
2099 case 'start' :
2100 case 'end' :
2101 case 'limit' : return wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum(9000, -100), wikiFuzz::makeFuzz(2)) );
2102 case 'dir' : return wikiFuzz::chooseInput( array("newer", "older", wikifuzz::makeFuzz(2) ) );
2103 case 'user' : return wikiFuzz::chooseInput( array(USER_ON_WIKI, wikifuzz::makeFuzz(2) ) );
2104 case 'namespace' : return wikiFuzz::chooseInput( array(-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 200000, wikifuzz::makeFuzz(2)) );
2105 case 'filterredir': return wikiFuzz::chooseInput( array("all", "redirects", "nonredirectsallpages", wikifuzz::makeFuzz(2)) );
2106 case 'allrev' : return wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2107 case 'prop' : return wikiFuzz::chooseInput( array("user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikifuzz::makeFuzz(2) ) );
2108 case 'type' : return wikiFuzz::chooseInput( array("block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikifuzz::makeFuzz(2) ) );
2109 case 'hide' : return wikiFuzz::chooseInput( array("minor", "bots", "anons", "liu", "liu|bots|", wikifuzz::makeFuzz(2) ) );
2110 case 'show' : return wikiFuzz::chooseInput( array('minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikifuzz::makeFuzz(2) ) );
2111 default : return wikifuzz::makeFuzz(2);
2112 }
2113 }
2114
2115 // Entry point.
2116 function __construct() {
2117 $this->pagePath = "api.php";
2118
2119 $modes = array ("help",
2120 "login",
2121 "opensearch",
2122 "feedwatchlist",
2123 "query");
2124 $action = wikiFuzz::chooseInput( array_merge ($modes, array(wikifuzz::makeFuzz(2))) );
2125
2126 switch ($action) {
2127 case "login" : $this->params = self::loginMode();
2128 break;
2129 case "opensearch" : $this->params = self::opensearchMode();
2130 break;
2131 case "feedwatchlist" : $this->params = self::feedwatchlistMode();
2132 break;
2133 case "query" : $this->params = self::queryMode();
2134 break;
2135 case "help" :
2136 default : // Do something random - "Crazy Ivan" mode.
2137 $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
2138 // There is no "helpMode".
2139 if ($random_mode == "helpMode") $random_mode = "queryMode";
2140 $this->params = self::$random_mode();
2141 break;
2142 }
2143
2144 // Save the selected action.
2145 $this->params["action"] = $action;
2146
2147 // Set the cookie:
2148 // FIXME: need to get this cookie dynamically set, rather than hard-coded.
2149 $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
2150
2151 // Output format
2152 $this->params["format"] = wikiFuzz::chooseInput( array("json", "jsonfm", "php", "phpfm",
2153 "wddx", "wddxfm", "xml", "xmlfm",
2154 "yaml", "yamlfm", "raw", "rawfm",
2155 wikifuzz::makeFuzz(2) ) );
2156
2157 // Page does not produce HTML (sometimes).
2158 $this->tidyValidate = false;
2159 }
2160 }
2161
2162
2163 /**
2164 ** selects a page test to run.
2165 */
2166 function selectPageTest($count) {
2167
2168 // if the user only wants a specific test, then only ever give them that.
2169 if (defined("SPECIFIC_TEST")) {
2170 $testType = SPECIFIC_TEST;
2171 return new $testType ();
2172 }
2173
2174 // Some of the time we test Special pages, the remaining
2175 // time we test using the standard edit page.
2176 switch ($count % 100) {
2177 case 0 : return new successfulUserLoginTest();
2178 case 1 : return new listusersTest();
2179 case 2 : return new searchTest();
2180 case 3 : return new recentchangesTest();
2181 case 4 : return new prefixindexTest();
2182 case 5 : return new mimeSearchTest();
2183 case 6 : return new specialLogTest();
2184 case 7 : return new userLoginTest();
2185 case 8 : return new ipblocklistTest();
2186 case 9 : return new newImagesTest();
2187 case 10: return new imagelistTest();
2188 case 11: return new specialExportTest();
2189 case 12: return new specialBooksourcesTest();
2190 case 13: return new specialAllpagesTest();
2191 case 14: return new pageHistoryTest();
2192 case 15: return new contributionsTest();
2193 case 16: return new viewPageTest();
2194 case 17: return new specialAllmessagesTest();
2195 case 18: return new specialNewpages();
2196 case 19: return new searchTest();
2197 case 20: return new redirectTest();
2198 case 21: return new confirmEmail();
2199 case 22: return new watchlistTest();
2200 case 23: return new specialBlockmeTest();
2201 case 24: return new specialUndelete();
2202 case 25: return new specialMovePage();
2203 case 26: return new specialUnlockdb();
2204 case 27: return new specialLockdb();
2205 case 28: return new specialUserrights();
2206 case 29: return new pageProtectionForm();
2207 case 30: return new specialBlockip();
2208 case 31: return new imagepageTest();
2209 case 32: return new pageDeletion();
2210 case 33: return new specialRevisionDelete();
2211 case 34: return new specialImport();
2212 case 35: return new thumbTest();
2213 case 36: return new trackbackTest();
2214 case 37: return new profileInfo();
2215 case 38: return new specialCite();
2216 case 39: return new specialFilepath();
2217 case 40: return new specialMakebot();
2218 case 41: return new specialMakesysop();
2219 case 42: return new specialRenameuser();
2220 case 43: return new specialLinksearch();
2221 case 44: return new specialCategoryTree();
2222 case 45: return new api();
2223 case 45: return new specialChemicalsourcesTest();
2224 default: return new editPageTest();
2225 }
2226 }
2227
2228
2229 /////////////////////// SAVING OUTPUT /////////////////////////
2230
2231 /**
2232 ** Utility function for saving a file. Currently has no error checking.
2233 */
2234 function saveFile($data, $name) {
2235 file_put_contents($name, $data);
2236 }
2237
2238
2239 /**
2240 ** Returns a test as an experimental GET-to-POST URL.
2241 ** This doesn't seem to always work though, and sometimes the output is too long
2242 ** to be a valid GET URL, so we also save in other formats.
2243 */
2244 function getAsURL(pageTest $test) {
2245 $used_question_mark = (strpos($test->getPagePath(), "?") !== false);
2246 $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
2247 foreach ($test->getParams() as $param => $value) {
2248 if (!$used_question_mark) {
2249 $retval .= "?";
2250 $used_question_mark = true;
2251 }
2252 else {
2253 $retval .= "&";
2254 }
2255 $retval .= $param . "=" . urlencode($value);
2256 }
2257 return $retval;
2258 }
2259
2260
2261 /**
2262 ** Saves a plain-text human-readable version of a test.
2263 */
2264 function saveTestAsText(pageTest $test, $filename) {
2265 $str = "Test: " . $test->getPagePath();
2266 foreach ($test->getParams() as $param => $value) {
2267 $str .= "\n$param: $value";
2268 }
2269 $str .= "\nGet-to-post URL: " . getAsURL($test) . "\n";
2270 saveFile($str, $filename);
2271 }
2272
2273
2274 /**
2275 ** Saves a test as a standalone basic PHP script that shows this one problem.
2276 ** Resulting script requires PHP-Curl be installed in order to work.
2277 */
2278 function saveTestAsPHP(pageTest $test, $filename) {
2279 $str = "<?php\n"
2280 . "\$params = " . var_export(escapeForCurl($test->getParams()), true) . ";\n"
2281 . "\$ch = curl_init();\n"
2282 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2283 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2284 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export(WIKI_BASE_URL . $test->getPagePath(), true) . ");\n"
2285 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2286 . ($test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export($test->getCookie(), true) . ");\n" : "")
2287 . "\$result=curl_exec(\$ch);\n"
2288 . "curl_close (\$ch);\n"
2289 . "print \$result;\n"
2290 . "?>\n";
2291 saveFile($str, $filename);
2292 }
2293
2294
2295 /**
2296 ** Escapes a value so that it can be used on the command line by Curl.
2297 ** Specifically, "<" and "@" need to be escaped if they are the first character,
2298 ** otherwise curl interprets these as meaning that we want to insert a file.
2299 */
2300 function escapeForCurl(array $input_params) {
2301 $output_params = array();
2302 foreach ($input_params as $param => $value) {
2303 if (strlen($value) > 0 && ( $value[0] == "@" || $value[0] == "<")) {
2304 $value = "\\" . $value;
2305 }
2306 $output_params[$param] = $value;
2307 }
2308 return $output_params;
2309 }
2310
2311
2312 /**
2313 ** Saves a test as a standalone CURL shell script that shows this one problem.
2314 ** Resulting script requires standalone Curl be installed in order to work.
2315 */
2316 function saveTestAsCurl(pageTest $test, $filename) {
2317 $str = "#!/bin/bash\n"
2318 . "curl --silent --include --globoff \\\n"
2319 . ($test->getCookie() ? " --cookie " . escapeshellarg($test->getCookie()) . " \\\n" : "");
2320 foreach (escapeForCurl($test->getParams()) as $param => $value) {
2321 $str .= " -F " . escapeshellarg($param) . "=" . escapeshellarg($value) . " \\\n";
2322 }
2323 $str .= " " . escapeshellarg(WIKI_BASE_URL . $test->getPagePath()); // beginning space matters.
2324 $str .= "\n";
2325 saveFile($str, $filename);
2326 chmod($filename, 0755); // make executable
2327 }
2328
2329
2330 /**
2331 ** Saves the internal data structure to file.
2332 */
2333 function saveTestData (pageTest $test, $filename) {
2334 saveFile(serialize($test), $filename);
2335 }
2336
2337
2338 /**
2339 ** saves a test in the various formats.
2340 */
2341 function saveTest(pageTest $test, $testname) {
2342 $base_name = DIRECTORY . "/" . $testname;
2343 saveTestAsText($test, $base_name . INFO_FILE);
2344 saveTestAsPHP ($test, $base_name . PHP_TEST );
2345 saveTestAsCurl($test, $base_name . CURL_TEST);
2346 saveTestData ($test, $base_name . DATA_FILE);
2347 }
2348
2349
2350 //////////////////// MEDIAWIKI OUTPUT /////////////////////////
2351
2352 /**
2353 ** Asks MediaWiki for the HTML output of a test.
2354 */
2355 function wikiTestOutput(pageTest $test) {
2356
2357 $ch = curl_init();
2358
2359 // specify the cookie, if required.
2360 if ($test->getCookie()) curl_setopt($ch, CURLOPT_COOKIE, $test->getCookie());
2361 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2362
2363 $params = escapeForCurl($test->getParams());
2364 curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2365
2366 curl_setopt($ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2367 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2368
2369 $result=curl_exec ($ch);
2370
2371 // if we encountered an error, then say so, and return an empty string.
2372 if (curl_error($ch)) {
2373 print "\nCurl error #: " . curl_errno($ch) . " - " . curl_error ($ch);
2374 $result = "";
2375 }
2376
2377 curl_close ($ch);
2378
2379 return $result;
2380 }
2381
2382
2383 //////////////////// HTML VALIDATION /////////////////////////
2384
2385 /*
2386 ** Asks the validator whether this is valid HTML, or not.
2387 */
2388 function validateHTML($text) {
2389
2390 $params = array ("fragment" => $text);
2391
2392 $ch = curl_init();
2393
2394 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2395 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); // load the POST variables
2396 curl_setopt($ch, CURLOPT_URL, VALIDATOR_URL); // set url to post to
2397 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2398
2399 $result=curl_exec ($ch);
2400
2401 // if we encountered an error, then log it, and exit.
2402 if (curl_error($ch)) {
2403 trigger_error("Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) );
2404 print "Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) . " - exiting.\n";
2405 exit();
2406 }
2407
2408 curl_close ($ch);
2409
2410 $valid = (strpos($result, "Failed validation") === false ? true : false);
2411
2412 return array($valid, $result);
2413 }
2414
2415
2416 /**
2417 ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2418 */
2419 function tidyCheckFile($name) {
2420 $file = DIRECTORY . "/" . $name;
2421 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2422 $x = `$command`;
2423
2424 // Look for the most interesting Tidy errors and warnings.
2425 if ( strpos($x,"end of file while parsing attributes") !== false
2426 || strpos($x,"attribute with missing trailing quote mark") !== false
2427 || strpos($x,"missing '>' for end of tag") !== false
2428 || strpos($x,"Error:") !== false) {
2429 print "\nTidy found something - view details with: $command";
2430 return false;
2431 } else {
2432 return true;
2433 }
2434 }
2435
2436
2437 /**
2438 ** Returns whether or not an database error log file has changed in size since
2439 ** the last time this was run. This is used to tell if a test caused a DB error.
2440 */
2441 function dbErrorLogged() {
2442 static $filesize;
2443
2444 // first time running this function
2445 if (!isset($filesize)) {
2446 // create log if it does not exist
2447 if (!file_exists(DB_ERROR_LOG_FILE)) {
2448 saveFile("", DB_ERROR_LOG_FILE);
2449 }
2450 $filesize = filesize(DB_ERROR_LOG_FILE);
2451 return false;
2452 }
2453
2454 $newsize = filesize(DB_ERROR_LOG_FILE);
2455 // if the log has grown, then assume the current test caused it.
2456 if ($newsize != $filesize) {
2457 $filesize = $newsize;
2458 return true;
2459 }
2460
2461 return false;
2462 }
2463
2464 ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2465
2466 /**
2467 ** takes a page test, and runs it and tests it for problems in the output.
2468 ** Returns: False on finding a problem, or True on no problems being found.
2469 */
2470 function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) {
2471
2472 // by default don't overwrite a previous test of the same name.
2473 while ( ! $can_overwrite && file_exists(DIRECTORY . "/" . $testname . DATA_FILE)) {
2474 $testname .= "-" . mt_rand(0,9);
2475 }
2476
2477 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2478
2479 // Store the time before and after, to find slow pages.
2480 $before = microtime(true);
2481
2482 // Get MediaWiki to give us the output of this test.
2483 $wiki_preview = wikiTestOutput($test);
2484
2485 $after = microtime(true);
2486
2487 // if we received no response, then that's interesting.
2488 if ($wiki_preview == "") {
2489 print "\nNo response received for: $filename";
2490 return false;
2491 }
2492
2493 // save output HTML to file.
2494 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2495 saveFile($wiki_preview, $html_file);
2496
2497 // if there were PHP errors in the output, then that's interesting too.
2498 if ( strpos($wiki_preview, "<b>Warning</b>: " ) !== false
2499 || strpos($wiki_preview, "<b>Fatal error</b>: " ) !== false
2500 || strpos($wiki_preview, "<b>Notice</b>: " ) !== false
2501 || strpos($wiki_preview, "<b>Error</b>: " ) !== false
2502 || strpos($wiki_preview, "<b>Strict Standards:</b>") !== false
2503 ) {
2504 $error = substr($wiki_preview, strpos($wiki_preview, "</b>:") + 7, 50);
2505 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2506 if ($error != "Unknown: The session id contains illegal character") {
2507 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2508 return false;
2509 }
2510 }
2511
2512 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2513 if( strpos($wiki_preview, "Backtrace:") !== false ) {
2514 print "\nInternal MediaWiki error in HTML output: $html_file";
2515 return false;
2516 }
2517
2518 // if there was a Parser error comment in the output, then that's potentially interesting.
2519 if( strpos($wiki_preview, "!-- ERR") !== false ) {
2520 print "\nParser Error comment in HTML output: $html_file";
2521 return false;
2522 }
2523
2524 // if a database error was logged, then that's definitely interesting.
2525 if( dbErrorLogged() ) {
2526 print "\nDatabase Error logged for: $filename";
2527 return false;
2528 }
2529
2530 // validate result
2531 $valid = true;
2532 if( VALIDATE_ON_WEB ) {
2533 list ($valid, $validator_output) = validateHTML($wiki_preview);
2534 if (!$valid) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2535 }
2536
2537 // Get tidy to check the page, unless we already know it produces non-XHTML output.
2538 if( $test->tidyValidate() ) {
2539 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2540 }
2541
2542 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2543 if (($after - $before) >= 2) {
2544 print "\nParticularly slow to render (" . round($after - $before, 2) . " seconds): $filename";
2545 return false;
2546 }
2547
2548 if( $valid ) {
2549 // Remove temp HTML file if test was valid:
2550 unlink( $html_file );
2551 } elseif( VALIDATE_ON_WEB ) {
2552 saveFile($validator_output, DIRECTORY . "/" . $testname . ".validator_output.html");
2553 }
2554
2555 return $valid;
2556 }
2557
2558
2559 /////////////////// RERUNNING OLD TESTS ///////////////////
2560
2561 /**
2562 ** We keep our failed tests so that they can be rerun.
2563 ** This function does that retesting.
2564 */
2565 function rerunPreviousTests() {
2566 print "Retesting previously found problems.\n";
2567
2568 $dir_contents = scandir (DIRECTORY);
2569
2570 // sort file into the order a normal person would use.
2571 natsort ($dir_contents);
2572
2573 foreach ($dir_contents as $file) {
2574
2575 // if file is not a test, then skip it.
2576 // Note we need to escape any periods or will be treated as "any character".
2577 $matches = array();
2578 if (!ereg("(.*)" . str_replace(".", "\.", DATA_FILE) . "$", $file, $matches)) continue;
2579
2580 // reload the test.
2581 $full_path = DIRECTORY . "/" . $file;
2582 $test = unserialize(file_get_contents($full_path));
2583
2584 // if this is not a valid test, then skip it.
2585 if (! $test instanceof pageTest) {
2586 print "\nSkipping invalid test - $full_path";
2587 continue;
2588 }
2589
2590 // The date format is in Apache log format, which makes it easier to locate
2591 // which retest caused which error in the Apache logs (only happens usually if
2592 // apache segfaults).
2593 if (!QUIET) print "[" . date ("D M d H:i:s Y") . "] Retesting $file (" . get_class($test) . ")";
2594
2595 // run test
2596 $testname = $matches[1];
2597 $valid = runWikiTest($test, $testname, true);
2598
2599 if (!$valid) {
2600 saveTest($test, $testname);
2601 if (QUIET) {
2602 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2603 } else {
2604 print "\n";
2605 }
2606 }
2607 else {
2608 if (!QUIET) print "\r";
2609 if (DELETE_PASSED_RETESTS) {
2610 $prefix = DIRECTORY . "/" . $testname;
2611 if (is_file($prefix . DATA_FILE)) unlink($prefix . DATA_FILE);
2612 if (is_file($prefix . PHP_TEST )) unlink($prefix . PHP_TEST );
2613 if (is_file($prefix . CURL_TEST)) unlink($prefix . CURL_TEST);
2614 if (is_file($prefix . INFO_FILE)) unlink($prefix . INFO_FILE);
2615 }
2616 }
2617 }
2618
2619 print "\nDone retesting.\n";
2620 }
2621
2622
2623 ////////////////////// MAIN LOOP ////////////////////////
2624
2625
2626 // first check whether CURL is installed, because sometimes it's not.
2627 if( ! function_exists('curl_init') ) {
2628 die("Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n");
2629 }
2630
2631 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2632 // access it staticly and not have any globals.
2633 wikiFuzz::$types = array_keys(wikiFuzz::$data);
2634
2635 // Make directory if doesn't exist
2636 if (!is_dir(DIRECTORY)) {
2637 mkdir (DIRECTORY, 0700 );
2638 }
2639 // otherwise, we first retest the things that we have found in previous runs
2640 else if (RERUN_OLD_TESTS) {
2641 rerunPreviousTests();
2642 }
2643
2644 // main loop.
2645 $start_time = date("U");
2646 $num_errors = 0;
2647 if (!QUIET) {
2648 print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2649 print "Press CTRL+C to stop testing.\n";
2650 }
2651
2652 for ($count=0; true; $count++) {
2653 if (!QUIET) {
2654 // spinning progress indicator.
2655 switch( $count % 4 ) {
2656 case '0': print "\r/"; break;
2657 case '1': print "\r-"; break;
2658 case '2': print "\r\\"; break;
2659 case '3': print "\r|"; break;
2660 }
2661 print " $count";
2662 }
2663
2664 // generate a page test to run.
2665 $test = selectPageTest($count);
2666
2667 $mins = ( date("U") - $start_time ) / 60;
2668 if (!QUIET && $mins > 0) {
2669 print ". $num_errors poss errors. "
2670 . floor($mins) . " mins. "
2671 . round ($count / $mins, 0) . " tests/min. "
2672 . get_class($test); // includes the current test name.
2673 }
2674
2675 // run this test against MediaWiki, and see if the output was valid.
2676 $testname = $count;
2677 $valid = runWikiTest($test, $testname, false);
2678
2679 // save the failed test
2680 if ( ! $valid ) {
2681 if (QUIET) {
2682 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2683 } else {
2684 print "\n";
2685 }
2686 saveTest($test, $testname);
2687 $num_errors += 1;
2688 } else if ( KEEP_PASSED_TESTS ) {
2689 // print current time, with microseconds (matches "strace" format), and the test name.
2690 print " " . date("H:i:s.") . substr(current(explode(" ", microtime())), 2) . " " . $testname;
2691 saveTest($test, $testname);
2692 }
2693
2694 // stop if we have reached max number of errors.
2695 if (defined("MAX_ERRORS") && $num_errors>=MAX_ERRORS) {
2696 break;
2697 }
2698
2699 // stop if we have reached max number of mins runtime.
2700 if (defined("MAX_RUNTIME") && $mins>=MAX_RUNTIME) {
2701 break;
2702 }
2703 }
2704
2705 ?>