Localisation updates from http://translatewiki.net.
[mediawiki.git] / maintenance / fuzz-tester.php
blob4c0398079e6f9219a8c332d4067e9b12f91466ae
1 <?php
2 /**
3 * Performs fuzz-style testing of MediaWiki's parser and forms.
5 * Copyright © 2006 Nick Jenkins
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/gpl.html
22 * @file
23 * @ingroup Maintenance
24 * @author Nick Jenkins ( http://nickj.org/ ).
27 Started: 18 May 2006.
29 Description:
30 Performs fuzz-style testing of MediaWiki's parser and forms.
32 How:
33 - Generate lots of nasty wiki text.
34 - Ask the Parser to render that wiki text to HTML, or ask MediaWiki's forms
35 to deal with that wiki text.
36 - Check MediaWiki's output for problems.
37 - Repeat.
39 Why:
40 - To help find bugs.
41 - To help find security issues, or potential security issues.
43 What type of problems are being checked for:
44 - Unclosed tags.
45 - Errors or interesting warnings from Tidy.
46 - PHP errors / warnings / notices.
47 - MediaWiki internal errors.
48 - Very slow responses.
49 - No response from apache.
50 - Optionally checking for malformed HTML using the W3C validator.
52 Background:
53 Many of the wikiFuzz class methods are a modified PHP port,
54 of a "shameless" Python port, of LCAMTUF'S MANGELME:
55 - http://www.securiteam.com/tools/6Z00N1PBFK.html
56 - http://www.securityfocus.com/archive/1/378632/2004-10-15/2004-10-21/0
58 Video:
59 There's an XviD video discussing this fuzz tester. You can get it from:
60 http://files.nickj.org/MediaWiki/Fuzz-Testing-MediaWiki-xvid.avi
62 Requirements:
63 To run this, you will need:
64 - Command-line PHP5, with PHP-curl enabled (not all installations have this
65 enabled - try "apt-get install php5-curl" if you're on Debian to install).
66 - the Tidy standalone executable. ("apt-get install tidy").
68 Optional:
69 - If you want to run the curl scripts, you'll need standalone curl installed
70 ("apt-get install curl")
71 - For viewing the W3C validator output on a command line, the "html2text"
72 program may be useful ("apt-get install html2text")
74 Saving tests and test results:
75 Any of the fuzz tests which find problems are saved for later review.
76 In order to help track down problems, tests are saved in a number of
77 different formats. The default filename extensions and their meanings are:
78 - ".test.php" : PHP script that reproduces just that one problem using PHP-Curl.
79 - ".curl.sh" : Shell script that reproduces that problem using standalone curl.
80 - ".data.bin" : The serialized PHP data so that this script can re-run the test.
81 - ".info.txt" : A human-readable text file with details of the field contents.
83 Wiki configuration for testing:
84 You should make some additions to LocalSettings.php in order to catch the most
85 errors. Note this configuration is for **TESTING PURPOSES ONLY**, and is IN NO
86 WAY, SHAPE, OR FORM suitable for deployment on a hostile network. That said,
87 personally I find these additions to be the most helpful for testing purposes:
89 // --------- Start ---------
90 // Everyone can do everything. Very useful for testing, yet useless for deployment.
91 $wgGroupPermissions['*']['autoconfirmed'] = true;
92 $wgGroupPermissions['*']['block'] = true;
93 $wgGroupPermissions['*']['bot'] = true;
94 $wgGroupPermissions['*']['delete'] = true;
95 $wgGroupPermissions['*']['deletedhistory'] = true;
96 $wgGroupPermissions['*']['deleterevision'] = true;
97 $wgGroupPermissions['*']['editinterface'] = true;
98 $wgGroupPermissions['*']['hiderevision'] = true;
99 $wgGroupPermissions['*']['import'] = true;
100 $wgGroupPermissions['*']['importupload'] = true;
101 $wgGroupPermissions['*']['minoredit'] = true;
102 $wgGroupPermissions['*']['move'] = true;
103 $wgGroupPermissions['*']['patrol'] = true;
104 $wgGroupPermissions['*']['protect'] = true;
105 $wgGroupPermissions['*']['proxyunbannable'] = true;
106 $wgGroupPermissions['*']['renameuser'] = true;
107 $wgGroupPermissions['*']['reupload'] = true;
108 $wgGroupPermissions['*']['reupload-shared'] = true;
109 $wgGroupPermissions['*']['rollback'] = true;
110 $wgGroupPermissions['*']['siteadmin'] = true;
111 $wgGroupPermissions['*']['unwatchedpages'] = true;
112 $wgGroupPermissions['*']['upload'] = true;
113 $wgGroupPermissions['*']['userrights'] = true;
114 $wgGroupPermissions['*']['renameuser'] = true;
115 $wgGroupPermissions['*']['makebot'] = true;
116 $wgGroupPermissions['*']['makesysop'] = true;
118 // Enable weird and wonderful options:
119 // Increase default error reporting level.
120 error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT
121 $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
122 $wgEnableUploads = true; // enable uploads.
123 $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path.
124 $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden).
125 $wgShowExceptionDetails = true; // want backtraces.
126 $wgEnableAPI = true; // enable API.
127 $wgEnableWriteAPI = true; // enable API.
129 // Install & enable Parser Hook extensions to increase code coverage. E.g.:
130 require_once("extensions/ParserFunctions/ParserFunctions.php");
131 require_once("extensions/Cite/Cite.php");
132 require_once("extensions/inputbox/inputbox.php");
133 require_once("extensions/Sort/Sort.php");
134 require_once("extensions/wikihiero/wikihiero.php");
135 require_once("extensions/CharInsert/CharInsert.php");
136 require_once("extensions/FixedImage/FixedImage.php");
138 // Install & enable Special Page extensions to increase code coverage. E.g.:
139 require_once("extensions/Cite/SpecialCite.php");
140 require_once("extensions/Renameuser/SpecialRenameuser.php");
141 // --------- End ---------
143 If you want to try E_STRICT error logging, add this to the above:
144 // --------- Start ---------
145 error_reporting (E_ALL | E_STRICT);
146 set_error_handler( 'error_handler' );
147 function error_handler ($type, $message, $file=__FILE__, $line=__LINE__) {
148 if ($message == "var: Deprecated. Please use the public/private/protected modifiers") return;
149 print "<br />\n<b>Strict Standards:</b> Type: <b>$type</b>: $message in <b>$file</b> on line <b>$line</b><br />\n";
151 // --------- End ---------
153 Also add/change this in LocalSettings.php:
154 // --------- Start ---------
155 $wgEnableProfileInfo = true;
156 $wgDBserver = "localhost"; // replace with DB server hostname
157 // --------- End ---------
159 Usage:
160 Run with "php fuzz-tester.php".
161 To see the various command-line options, run "php fuzz-tester.php --help".
162 To stop the script, press Ctrl-C.
164 Console output:
165 - If requested, first any previously failed tests will be rerun.
166 - Then new tests will be generated and run. Any tests that fail will be saved,
167 and a brief message about why they failed will be printed on the console.
168 - The console will show the number of tests run, time run, number of tests
169 failed, number of tests being done per minute, and the name of the current test.
171 TODO:
172 Some known things that could improve this script:
173 - Logging in with cookie jar storage needed for some tests (as there are some
174 pages that cannot be tested without being logged in, and which are currently
175 untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist).
176 - Testing of Timeline extension (I cannot test as ploticus has/had issues on
177 my architecture).
181 // ///////////////////////// COMMAND LINE HELP ////////////////////////////////////
183 // This is a command line script, load MediaWiki env (gives command line options);
184 require_once( __DIR__ . '/commandLine.inc' );
186 // if the user asked for an explanation of command line options.
187 if ( isset( $options["help"] ) ) {
188 print <<<ENDS
189 MediaWiki $wgVersion fuzz tester
190 Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
191 [--directory=<failed-test-path>] [--include-binary]
192 [--w3c-validate] [--delete-passed-retests] [--help]
193 [--user=<username>] [--password=<password>]
194 [--rerun-failed-tests] [--max-errors=<int>]
195 [--max-runtime=<num-minutes>]
196 [--specific-test=<test-name>]
198 Options:
199 --quiet : Hides passed tests, shows only failed tests.
200 --base-url : URL to a wiki on which to run the tests.
201 The "http://" is optional and can be omitted.
202 --directory : Full path to directory for storing failed tests.
203 Will be created if it does not exist.
204 --include-binary : Includes non-alphanumeric characters in the tests.
205 --w3c-validate : Validates pages using the W3C's web validator.
206 Slow. Currently many pages fail validation.
207 --user : Login name of a valid user on your test wiki.
208 --password : Password for the valid user on your test wiki.
209 --delete-passed-retests : Will delete retests that now pass.
210 Requires --rerun-failed-tests to be meaningful.
211 --rerun-failed-tests : Whether to rerun any previously failed tests.
212 --max-errors : Maximum number of errors to report before exiting.
213 Does not include errors from --rerun-failed-tests
214 --max-runtime : Maximum runtime, in minutes, to run before exiting.
215 Only applies to new tests, not --rerun-failed-tests
216 --specific-test : Runs only the specified fuzz test.
217 Only applies to new tests, not --rerun-failed-tests
218 --keep-passed-tests : Saves all test files, even those that pass.
219 --help : Show this help message.
221 Example:
222 If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour,
223 and only wanted to be informed of errors, and did not want to redo previously
224 failed tests, and wanted a maximum of 100 errors, then you could do:
225 php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60
228 ENDS;
230 exit( 0 );
234 // if we got command line options, check they look valid.
235 $validOptions = array ( "quiet", "base-url", "directory", "include-binary",
236 "w3c-validate", "user", "password", "delete-passed-retests",
237 "rerun-failed-tests", "max-errors",
238 "max-runtime", "specific-test", "keep-passed-tests", "help" );
239 if ( !empty( $options ) ) {
240 $unknownArgs = array_diff ( array_keys( $options ), $validOptions );
241 foreach ( $unknownArgs as $invalidArg ) {
242 print "Ignoring invalid command-line option: --$invalidArg\n";
247 // /////////////////////////// CONFIGURATION ////////////////////////////////////
249 // URL to some wiki on which we can run our tests.
250 if ( !empty( $options["base-url"] ) ) {
251 define( "WIKI_BASE_URL", $options["base-url"] );
252 } else {
253 define( "WIKI_BASE_URL", $wgServer . $wgScriptPath . '/' );
256 // The directory name where we store the output.
257 // Example for Windows: "c:\\temp\\wiki-fuzz"
258 if ( !empty( $options["directory"] ) ) {
259 define( "DIRECTORY", $options["directory"] );
260 } else {
261 define( "DIRECTORY", "{$wgUploadDirectory}/fuzz-tests" );
264 // Should our test fuzz data include binary strings?
265 define( "INCLUDE_BINARY", isset( $options["include-binary"] ) );
267 // Whether we want to validate HTML output on the web.
268 // At the moment very few generated pages will validate, so not recommended.
269 define( "VALIDATE_ON_WEB", isset( $options["w3c-validate"] ) );
270 // URL to use to validate our output:
271 define( "VALIDATOR_URL", "http://validator.w3.org/check" );
273 // Location of Tidy standalone executable.
274 define( "PATH_TO_TIDY", "/usr/bin/tidy" );
276 // The name of a user who has edited on your wiki. Used
277 // when testing the Special:Contributions and Special:Userlogin page.
278 if ( !empty( $options["user"] ) ) {
279 define( "USER_ON_WIKI", $options["user"] );
280 } else {
281 define( "USER_ON_WIKI", "nickj" );
284 // The password of the above user. Used when testing the login page,
285 // and to do this we sometimes need to login successfully.
286 if ( !empty( $options["password"] ) ) {
287 define( "USER_PASSWORD", $options["password"] );
288 } else {
289 // And no, this is not a valid password on any public wiki.
290 define( "USER_PASSWORD", "nickj" );
293 // If we have a test that failed, and then we run it again, and it passes,
294 // do you want to delete it or keep it?
295 define( "DELETE_PASSED_RETESTS", isset( $options["delete-passed-retests"] ) );
297 // Do we want to rerun old saved tests at script startup?
298 // Set to true to help catch regressions, or false if you only want new stuff.
299 define( "RERUN_OLD_TESTS", isset( $options["rerun-failed-tests"] ) );
301 // File where the database errors are logged. Should be defined in LocalSettings.php.
302 define( "DB_ERROR_LOG_FILE", $wgDBerrorLog );
304 // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)?
305 define( "QUIET", isset( $options["quiet"] ) );
307 // Keep all test files, even those that pass. Potentially useful to tracking input that causes something
308 // unusual to happen, if you don't know what "unusual" is until later.
309 define( "KEEP_PASSED_TESTS", isset( $options["keep-passed-tests"] ) );
311 // The maximum runtime, if specified.
312 if ( !empty( $options["max-runtime"] ) && intval( $options["max-runtime"] ) > 0 ) {
313 define( "MAX_RUNTIME", intval( $options["max-runtime"] ) );
316 // The maximum number of problems to find, if specified. Excludes retest errors.
317 if ( !empty( $options["max-errors"] ) && intval( $options["max-errors"] ) > 0 ) {
318 define( "MAX_ERRORS", intval( $options["max-errors"] ) );
321 // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid.
322 if ( !empty( $options["specific-test"] ) ) {
323 if ( class_exists( $options["specific-test"] ) && get_parent_class( $options["specific-test"] ) == "pageTest" ) {
324 define( "SPECIFIC_TEST", $options["specific-test"] );
326 else {
327 print "Ignoring invalid --specific-test\n";
331 // Define the file extensions we'll use:
332 define( "PHP_TEST" , ".test.php" );
333 define( "CURL_TEST", ".curl.sh" );
334 define( "DATA_FILE", ".data.bin" );
335 define( "INFO_FILE", ".info.txt" );
336 define( "HTML_FILE", ".wiki_preview.html" );
338 // If it goes wrong, we want to know about it.
339 error_reporting( E_ALL | E_STRICT );
341 // ////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS //////////////////////
343 class wikiFuzz {
345 // Only some HTML tags are understood with params by MediaWiki, the rest are ignored.
346 // List the tags that accept params below, as well as what those params are.
347 public static $data = array(
348 "B" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
349 "CAPTION" => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ),
350 "CENTER" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title" ),
351 "DIV" => array( "CLASS", "STYLE", "ID", "align", "lang", "dir", "title" ),
352 "FONT" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color" ),
353 "H1" => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ),
354 "H2" => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ),
355 "HR" => array( "STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade" ),
356 "LI" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value" ),
357 "TABLE" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING",
358 "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules" ),
359 "TD" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
360 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
361 "dir", "title", "char", "charoff" ),
362 "TH" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
363 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
364 "dir", "title", "char", "charoff" ),
365 "TR" => array( "CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff" ),
366 "UL" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "type" ),
367 "P" => array( "style", "class", "id", "align", "lang", "dir", "title" ),
368 "blockquote" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "cite" ),
369 "span" => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ),
370 "code" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
371 "tt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
372 "small" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
373 "big" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
374 "s" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
375 "u" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
376 "del" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ),
377 "ins" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ),
378 "sub" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
379 "sup" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
380 "ol" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start" ),
381 "br" => array( "CLASS", "ID", "STYLE", "title", "clear" ),
382 "cite" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
383 "var" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
384 "ruby" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
385 "rt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
386 "rp" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
387 "dt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
388 "dl" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
389 "em" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
390 "strong" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
391 "i" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
392 "thead" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ),
393 "tfoot" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ),
394 "tbody" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ),
395 "colgroup" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width' ),
396 "col" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width' ),
397 "pre" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "width" ),
399 // extension tags that accept parameters:
400 "sort" => array( "order", "class" ),
401 "ref" => array( "name" ),
402 "categorytree" => array( "hideroot", "mode", "style" ),
403 "chemform" => array( "link", "wikilink", "query" ),
404 "section" => array( "begin", "new" ),
406 // older MW transclusion.
407 "transclude" => array( "page" ),
410 // The types of the HTML that we will be testing were defined above
411 // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data);
412 // as such, it also needs to also be publicly modifiable.
413 public static $types;
416 // Some attribute values.
417 static private $other = array( "&", "=", ":", "?", "\"", "\n", "%n%n%n%n%n%n%n%n%n%n%n%n", "\\" );
418 static private $ints = array(
419 // various numbers
420 "0", "-1", "127", "-7897", "89000", "808080", "90928345",
421 "0xfffffff", "ffff",
423 // Different ways of saying: '
424 "&#0000039;", // Long UTF-8 Unicode encoding
425 "&#39;", // dec version.
426 "&#x27;", // hex version.
427 "&#xA7;", // malformed hex variant, MSB not zero.
429 // Different ways of saying: "
430 "&#0000034;", // Long UTF-8 Unicode encoding
431 "&#34;",
432 "&#x22;", // hex version.
433 "&#xA2;", // malformed hex variant, MSB not zero.
435 // Different ways of saying: <
436 "<",
437 "&#0000060", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
438 "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
439 "&#60;",
440 "&#x3C;", // hex version.
441 "&#xBC;", // malformed hex variant, MSB not zero.
442 "&#x0003C;", // mid-length hex version
443 "&#X00003C;", // slightly longer hex version, with capital "X"
445 // Different ways of saying: >
446 ">",
447 "&#0000062;", // Long UTF-8 Unicode encoding
448 "&#62;",
449 "&#x3E;", // hex version.
450 "&#xBE;", // malformed variant, MSB not zero.
452 // Different ways of saying: [
453 "&#0000091;", // Long UTF-8 Unicode encoding
454 "&#91;",
455 "&#x5B;", // hex version.
457 // Different ways of saying: {{
458 "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
459 "&#123;&#123;",
460 "&#x7B;&#x7B;", // hex version.
462 // Different ways of saying: |
463 "&#0000124;", // Long UTF-8 Unicode encoding
464 "&#124;",
465 "&#x7C;", // hex version.
466 "&#xFC;", // malformed hex variant, MSB not zero.
468 // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
469 // &#8204; == &zwnj;
470 "&#8204;"
473 // Defines various wiki-related bits of syntax, that can potentially cause
474 // MediaWiki to do something other than just print that literal text.
475 static private $ext = array(
476 // links, templates, parameters.
477 "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
479 // wiki tables.
480 "\n{|", "\n|}",
481 "!",
482 "\n!",
483 "!!",
484 "||",
485 "\n|-", "| ", "\n|",
487 // section headings.
488 "=", "==", "===", "====", "=====", "======",
490 // lists (ordered and unordered) and indentation.
491 "\n*", "*", "\n:", ":",
492 "\n#", "#",
494 // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
495 "\n;", ";", "\n ",
497 // Whitespace: newline, tab, space.
498 "\n", "\t", " ",
500 // Some XSS attack vectors from http://ha.ckers.org/xss.html
501 "&#x09;", // tab
502 "&#x0A;", // newline
503 "&#x0D;", // carriage return
504 "\0", // null character
505 " &#14; ", // spaces and meta characters
506 "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
508 // various NULL fields
509 "%00",
510 "&#00;",
511 "\0",
513 // horizontal rule.
514 "-----", "\n-----",
516 // signature, redirect, bold, italics.
517 "~~~~", "#REDIRECT [[", "'''", "''",
519 // comments.
520 "<!--", "-->",
522 // quotes.
523 "\"", "'",
525 // tag start and tag end.
526 "<", ">",
528 // implicit link creation on URIs.
529 "http://",
530 "https://",
531 "ftp://",
532 "irc://",
533 "news:",
534 'gopher://',
535 'telnet://',
536 'nntp://',
537 'worldwind://',
538 'mailto:',
540 // images.
541 "[[image:",
542 ".gif",
543 ".png",
544 ".jpg",
545 ".jpeg",
546 'thumbnail=',
547 'thumbnail',
548 'thumb=',
549 'thumb',
550 'right',
551 'none',
552 'left',
553 'framed',
554 'frame',
555 'enframed',
556 'centre',
557 'center',
558 "Image:",
559 "[[:Image",
560 'px',
561 'upright=',
562 'border',
564 // misc stuff to throw at the Parser.
565 '%08X',
566 '/',
567 ":x{|",
568 "\n|+",
569 "<noinclude>",
570 "</noinclude>",
571 " \302\273",
572 " :",
573 " !",
574 " ;",
575 "\302\253",
576 "[[category:",
577 "?=",
578 "(",
579 ")",
580 "]]]",
581 "../",
582 "{{{{",
583 "}}}}",
584 "[[Special:",
585 "<includeonly>",
586 "</includeonly>",
587 "<!--MWTEMPLATESECTION=",
588 '<!--MWTOC-->',
590 // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
591 "ISBN 2",
592 "RFC 000",
593 "PMID 000",
594 "ISBN ",
595 "RFC ",
596 "PMID ",
598 // magic words:
599 '__NOTOC__',
600 '__FORCETOC__',
601 '__NOEDITSECTION__',
602 '__START__',
603 '__NOTITLECONVERT__',
604 '__NOCONTENTCONVERT__',
605 '__END__',
606 '__TOC__',
607 '__NOTC__',
608 '__NOCC__',
609 "__FORCETOC__",
610 "__NEWSECTIONLINK__",
611 "__NOGALLERY__",
613 // more magic words / internal templates.
614 '{{PAGENAME}}',
615 '{{PAGENAMEE}}',
616 '{{NAMESPACE}}',
617 "{{MSG:",
618 "}}",
619 "{{MSGNW:",
620 "}}",
621 "{{INT:",
622 "}}",
623 '{{SITENAME}}',
624 "{{NS:",
625 "}}",
626 "{{LOCALURL:",
627 "}}",
628 "{{LOCALURLE:",
629 "}}",
630 "{{SCRIPTPATH}}",
631 "{{GRAMMAR:gentiv|",
632 "}}",
633 "{{REVISIONID}}",
634 "{{SUBPAGENAME}}",
635 "{{SUBPAGENAMEE}}",
636 "{{ns:0}}",
637 "{{fullurle:",
638 "}}",
639 "{{subst::",
640 "}}",
641 "{{UCFIRST:",
642 "}}",
643 "{{UC:",
644 '{{SERVERNAME}}',
645 '{{SERVER}}',
646 "{{RAW:",
647 "}}",
648 "{{PLURAL:",
649 "}}",
650 "{{LCFIRST:",
651 "}}",
652 "{{LC:",
653 "}}",
654 '{{CURRENTWEEK}}',
655 '{{CURRENTDOW}}',
656 "{{INT:{{LC:contribs-showhideminor}}|",
657 "}}",
658 "{{INT:googlesearch|",
659 "}}",
660 "{{BASEPAGENAME}}",
661 "{{CONTENTLANGUAGE}}",
662 "{{PAGESINNAMESPACE:}}",
663 "{{#language:",
664 "}}",
665 "{{#special:",
666 "}}",
667 "{{#special:emailuser",
668 "}}",
670 // Some raw link for magic words.
671 "{{NUMBEROFPAGES:R",
672 "}}",
673 "{{NUMBEROFUSERS:R",
674 "}}",
675 "{{NUMBEROFARTICLES:R",
676 "}}",
677 "{{NUMBEROFFILES:R",
678 "}}",
679 "{{NUMBEROFADMINS:R",
680 "}}",
681 "{{padleft:",
682 "}}",
683 "{{padright:",
684 "}}",
685 "{{DEFAULTSORT:",
686 "}}",
688 // internal Math "extension":
689 "<math>",
690 "</math>",
692 // Parser extension functions:
693 "{{#expr:",
694 "{{#if:",
695 "{{#ifeq:",
696 "{{#ifexist:",
697 "{{#ifexpr:",
698 "{{#switch:",
699 "{{#time:",
700 "}}",
702 // references table for the Cite extension.
703 "<references/>",
705 // Internal Parser tokens - try inserting some of these.
706 "UNIQ25f46b0524f13e67NOPARSE",
707 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
708 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
710 // Inputbox extension:
711 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
712 "</inputbox>",
714 // charInsert extension:
715 "<charInsert>",
716 "</charInsert>",
718 // wikiHiero extension:
719 "<hiero>",
720 "</hiero>",
722 // Image gallery:
723 "<gallery>",
724 "</gallery>",
726 // FixedImage extension.
727 "<fundraising/>",
729 // Timeline extension: currently untested.
731 // Nowiki:
732 "<nOwIkI>",
733 "</nowiki>",
735 // an external image to test the external image displaying code
736 "http://debian.org/Pics/debian.png",
738 // LabeledSectionTransclusion extension.
739 "{{#lstx:",
740 "}}",
741 "{{#lst:",
742 "}}",
743 "{{#lst:Main Page|",
744 "}}"
748 ** Randomly returns one element of the input array.
750 public static function chooseInput( array $input ) {
751 $randindex = wikiFuzz::randnum( count( $input ) - 1 );
752 return $input[$randindex];
755 // Max number of parameters for HTML attributes.
756 static private $maxparams = 10;
759 * Returns random number between finish and start.
760 * @param $finish
761 * @param $start int
762 * @return int
764 public static function randnum( $finish, $start = 0 ) {
765 return mt_rand( $start, $finish );
769 * Returns a mix of random text and random wiki syntax.
770 * @return string
772 private static function randstring() {
773 $thestring = "";
775 for ( $i = 0; $i < 40; $i++ ) {
776 $what = wikiFuzz::randnum( 1 );
778 if ( $what == 0 ) { // include some random wiki syntax
779 $which = wikiFuzz::randnum( count( wikiFuzz::$ext ) - 1 );
780 $thestring .= wikiFuzz::$ext[$which];
782 else { // include some random text
783 $char = INCLUDE_BINARY
784 // Decimal version:
785 // "&#" . wikiFuzz::randnum(255) . ";"
786 // Hex version:
787 ? "&#x" . str_pad( dechex( wikiFuzz::randnum( 255 ) ), wikiFuzz::randnum( 2, 7 ), "0", STR_PAD_LEFT ) . ";"
788 // A truly binary version:
789 // ? chr(wikiFuzz::randnum(0,255))
790 : chr( wikiFuzz::randnum( 126, 32 ) );
792 $length = wikiFuzz::randnum( 8 );
793 $thestring .= str_repeat ( $char, $length );
796 return $thestring;
800 * Returns either random text, or random wiki syntax, or random data from "ints",
801 * or random data from "other".
802 * @return string
804 private static function makestring() {
805 $what = wikiFuzz::randnum( 2 );
806 if ( $what == 0 ) {
807 return wikiFuzz::randstring();
808 } elseif ( $what == 1 ) {
809 return wikiFuzz::$ints[wikiFuzz::randnum( count( wikiFuzz::$ints ) - 1 )];
810 } else {
811 return wikiFuzz::$other[wikiFuzz::randnum( count( wikiFuzz::$other ) - 1 )];
816 * Returns the matched character slash-escaped as in a C string
817 * Helper for makeTitleSafe callback
818 * @param $matches
819 * @return string
821 private static function stringEscape( $matches ) {
822 return sprintf( "\\x%02x", ord( $matches[1] ) );
826 ** Strips out the stuff that Mediawiki balks at in a page's title.
827 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
828 * @param $str string
829 * @return string
831 public static function makeTitleSafe( $str ) {
832 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
833 return preg_replace_callback(
834 "/([^$legalTitleChars])/", 'wikiFuzz::stringEscape',
835 $str );
839 ** Returns a string of fuzz text.
840 * @return string
842 private static function loop() {
843 switch ( wikiFuzz::randnum( 3 ) ) {
844 case 1: // an opening tag, with parameters.
845 $string = "";
846 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
847 $t = wikiFuzz::$types[$i];
848 $arr = wikiFuzz::$data[$t];
849 $string .= "<" . $t . " ";
850 $num_params = min( wikiFuzz::$maxparams, count( $arr ) );
851 for ( $z = 0; $z < $num_params; $z++ ) {
852 $badparam = $arr[wikiFuzz::randnum( count( $arr ) - 1 )];
853 $badstring = wikiFuzz::makestring();
854 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
856 $string .= ">\n";
857 return $string;
858 case 2: // a closing tag.
859 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
860 return "</" . wikiFuzz::$types[$i] . ">";
861 case 3: // a random string, between tags.
862 return wikiFuzz::makeString();
864 return ""; // catch-all, should never be called.
868 * Returns one of the three styles of random quote: ', ", and nothing.
869 * @return string
871 private static function getRandQuote() {
872 switch ( wikiFuzz::randnum( 3 ) ) {
873 case 1 : return "'";
874 case 2 : return "\"";
875 default: return "";
880 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
881 * @param $maxtypes int
882 * @return string
884 public static function makeFuzz( $maxtypes = 2 ) {
885 $page = "";
886 for ( $k = 0; $k < $maxtypes; $k++ ) {
887 $page .= wikiFuzz::loop();
889 return $page;
894 // ////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
897 ** A page test has just these things:
898 ** 1) Form parameters.
899 ** 2) the URL we are going to test those parameters on.
900 ** 3) Any cookies required for the test.
901 ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off.
902 ** Declared abstract because it should be extended by a class
903 ** that supplies these parameters.
905 abstract class pageTest {
906 protected $params;
907 protected $pagePath;
908 protected $cookie = "";
909 protected $tidyValidate = true;
911 public function getParams() {
912 return $this->params;
915 public function getPagePath() {
916 return $this->pagePath;
919 public function getCookie() {
920 return $this->cookie;
923 public function tidyValidate() {
924 return $this->tidyValidate;
930 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
932 class editPageTest extends pageTest {
933 function __construct() {
934 $this->pagePath = "index.php?title=WIKIFUZZ";
936 $this->params = array (
937 "action" => "submit",
938 "wpMinoredit" => wikiFuzz::makeFuzz( 2 ),
939 "wpPreview" => wikiFuzz::makeFuzz( 2 ),
940 "wpSection" => wikiFuzz::makeFuzz( 2 ),
941 "wpEdittime" => wikiFuzz::makeFuzz( 2 ),
942 "wpSummary" => wikiFuzz::makeFuzz( 2 ),
943 "wpScrolltop" => wikiFuzz::makeFuzz( 2 ),
944 "wpStarttime" => wikiFuzz::makeFuzz( 2 ),
945 "wpAutoSummary" => wikiFuzz::makeFuzz( 2 ),
946 "wpTextbox1" => wikiFuzz::makeFuzz( 40 ) // the main wiki text, need lots of this.
949 // sometimes we don't want to specify certain parameters.
950 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSection"] );
951 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEdittime"] );
952 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSummary"] );
953 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpScrolltop"] );
954 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpStarttime"] );
955 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpAutoSummary"] );
956 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpTextbox1"] );
962 ** a page test for "Special:Listusers".
964 class listusersTest extends pageTest {
965 function __construct() {
966 $this->pagePath = "index.php?title=Special:Listusers";
968 $this->params = array (
969 "title" => wikiFuzz::makeFuzz( 2 ),
970 "group" => wikiFuzz::makeFuzz( 2 ),
971 "username" => wikiFuzz::makeFuzz( 2 ),
972 "Go" => wikiFuzz::makeFuzz( 2 ),
973 "limit" => wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
974 "offset" => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) )
981 ** a page test for "Special:Search".
983 class searchTest extends pageTest {
984 function __construct() {
985 $this->pagePath = "index.php?title=Special:Search";
987 $this->params = array (
988 "action" => "index.php?title=Special:Search",
989 "ns0" => wikiFuzz::makeFuzz( 2 ),
990 "ns1" => wikiFuzz::makeFuzz( 2 ),
991 "ns2" => wikiFuzz::makeFuzz( 2 ),
992 "ns3" => wikiFuzz::makeFuzz( 2 ),
993 "ns4" => wikiFuzz::makeFuzz( 2 ),
994 "ns5" => wikiFuzz::makeFuzz( 2 ),
995 "ns6" => wikiFuzz::makeFuzz( 2 ),
996 "ns7" => wikiFuzz::makeFuzz( 2 ),
997 "ns8" => wikiFuzz::makeFuzz( 2 ),
998 "ns9" => wikiFuzz::makeFuzz( 2 ),
999 "ns10" => wikiFuzz::makeFuzz( 2 ),
1000 "ns11" => wikiFuzz::makeFuzz( 2 ),
1001 "ns12" => wikiFuzz::makeFuzz( 2 ),
1002 "ns13" => wikiFuzz::makeFuzz( 2 ),
1003 "ns14" => wikiFuzz::makeFuzz( 2 ),
1004 "ns15" => wikiFuzz::makeFuzz( 2 ),
1005 "redirs" => wikiFuzz::makeFuzz( 2 ),
1006 "search" => wikiFuzz::makeFuzz( 2 ),
1007 "offset" => wikiFuzz::chooseInput( array( "", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) ),
1008 "fulltext" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) ),
1009 "searchx" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) )
1016 ** a page test for "Special:Recentchanges".
1018 class recentchangesTest extends pageTest {
1019 function __construct() {
1020 $this->pagePath = "index.php?title=Special:Recentchanges";
1022 $this->params = array (
1023 "action" => wikiFuzz::makeFuzz( 2 ),
1024 "title" => wikiFuzz::makeFuzz( 2 ),
1025 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
1026 "Go" => wikiFuzz::makeFuzz( 2 ),
1027 "invert" => wikiFuzz::chooseInput( array( "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1028 "hideanons" => wikiFuzz::chooseInput( array( "-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1029 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz( 2 ) ) ),
1030 "days" => wikiFuzz::chooseInput( array( "-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1031 "hideminor" => wikiFuzz::chooseInput( array( "-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1032 "hidebots" => wikiFuzz::chooseInput( array( "-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1033 "hideliu" => wikiFuzz::chooseInput( array( "-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1034 "hidepatrolled" => wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1035 "hidemyself" => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1036 'categories_any' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1037 'categories' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1038 'feed' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) )
1045 ** a page test for "Special:Prefixindex".
1047 class prefixindexTest extends pageTest {
1048 function __construct() {
1049 $this->pagePath = "index.php?title=Special:Prefixindex";
1051 $this->params = array (
1052 "title" => "Special:Prefixindex",
1053 "namespace" => wikiFuzz::randnum( 101, -10 ),
1054 "Go" => wikiFuzz::makeFuzz( 2 )
1057 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
1058 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1059 $this->params["prefix"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
1060 wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
1062 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1063 $this->params["from"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
1064 wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
1071 ** a page test for "Special:MIMEsearch".
1073 class mimeSearchTest extends pageTest {
1074 function __construct() {
1075 $this->pagePath = "index.php?title=Special:MIMEsearch";
1077 $this->params = array (
1078 "action" => "index.php?title=Special:MIMEsearch",
1079 "mime" => wikiFuzz::makeFuzz( 3 ),
1080 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz( 2 ) ) ),
1081 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz( 2 ) ) )
1088 ** a page test for "Special:Log".
1090 class specialLogTest extends pageTest {
1091 function __construct() {
1092 $this->pagePath = "index.php?title=Special:Log";
1094 $this->params = array (
1095 "type" => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1096 "par" => wikiFuzz::makeFuzz( 2 ),
1097 "user" => wikiFuzz::makeFuzz( 2 ),
1098 "page" => wikiFuzz::makeFuzz( 2 ),
1099 "from" => wikiFuzz::makeFuzz( 2 ),
1100 "until" => wikiFuzz::makeFuzz( 2 ),
1101 "title" => wikiFuzz::makeFuzz( 2 )
1108 ** a page test for "Special:Userlogin", with a successful login.
1110 class successfulUserLoginTest extends pageTest {
1111 function __construct() {
1112 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz( 2 );
1114 $this->params = array (
1115 "wpName" => USER_ON_WIKI,
1116 // sometimes real password, sometimes not:
1117 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), USER_PASSWORD ) ),
1118 'wpRemember' => wikiFuzz::makeFuzz( 2 )
1121 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
1127 ** a page test for "Special:Userlogin".
1129 class userLoginTest extends pageTest {
1130 function __construct() {
1132 $this->pagePath = "index.php?title=Special:Userlogin";
1134 $this->params = array (
1135 'wpRetype' => wikiFuzz::makeFuzz( 2 ),
1136 'wpRemember' => wikiFuzz::makeFuzz( 2 ),
1137 'wpRealName' => wikiFuzz::makeFuzz( 2 ),
1138 'wpPassword' => wikiFuzz::makeFuzz( 2 ),
1139 'wpName' => wikiFuzz::makeFuzz( 2 ),
1140 'wpMailmypassword' => wikiFuzz::makeFuzz( 2 ),
1141 'wpLoginattempt' => wikiFuzz::makeFuzz( 2 ),
1142 'wpEmail' => wikiFuzz::makeFuzz( 2 ),
1143 'wpDomain' => wikiFuzz::chooseInput( array( "", "local", wikiFuzz::makeFuzz( 2 ) ) ),
1144 'wpCreateaccountMail' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1145 'wpCreateaccount' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1146 'wpCookieCheck' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1147 'type' => wikiFuzz::chooseInput( array( "signup", "login", "", wikiFuzz::makeFuzz( 2 ) ) ),
1148 'returnto' => wikiFuzz::makeFuzz( 2 ),
1149 'action' => wikiFuzz::chooseInput( array( "", "submitlogin", wikiFuzz::makeFuzz( 2 ) ) )
1152 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
1158 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1160 class ipblocklistTest extends pageTest {
1161 function __construct() {
1162 $this->pagePath = "index.php?title=Special:Ipblocklist";
1164 $this->params = array (
1165 'wpUnblockAddress' => wikiFuzz::makeFuzz( 2 ),
1166 'ip' => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1167 // something like an IP address, sometimes invalid:
1168 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1169 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1170 'id' => wikiFuzz::makeFuzz( 2 ),
1171 'wpUnblockReason' => wikiFuzz::makeFuzz( 2 ),
1172 'action' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "success", "submit", "unblock" ) ),
1173 'wpEditToken' => wikiFuzz::makeFuzz( 2 ),
1174 'wpBlock' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "" ) ),
1175 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1",
1176 "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ),
1177 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1",
1178 "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) )
1181 // sometimes we don't want to specify certain parameters.
1182 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1183 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["ip"] );
1184 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["id"] );
1185 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpUnblockAddress"] );
1191 ** a page test for "Special:Newimages".
1193 class newImagesTest extends pageTest {
1194 function __construct() {
1195 $this->pagePath = "index.php?title=Special:Newimages";
1197 $this->params = array (
1198 'hidebots' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "1", "", "-1" ) ),
1199 'wpIlMatch' => wikiFuzz::makeFuzz( 2 ),
1200 'until' => wikiFuzz::makeFuzz( 2 ),
1201 'from' => wikiFuzz::makeFuzz( 2 )
1204 // sometimes we don't want to specify certain parameters.
1205 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["until"] );
1206 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["from"] );
1212 ** a page test for the "Special:Imagelist" page.
1214 class imagelistTest extends pageTest {
1215 function __construct() {
1216 $this->pagePath = "index.php?title=Special:Imagelist";
1218 $this->params = array (
1219 'sort' => wikiFuzz::chooseInput( array( "bysize", "byname" , "bydate", wikiFuzz::makeFuzz( 2 ) ) ),
1220 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ),
1221 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
1222 'wpIlMatch' => wikiFuzz::makeFuzz( 2 )
1229 ** a page test for "Special:Export".
1231 class specialExportTest extends pageTest {
1232 function __construct() {
1233 $this->pagePath = "index.php?title=Special:Export";
1235 $this->params = array (
1236 'action' => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1237 'pages' => wikiFuzz::makeFuzz( 2 ),
1238 'curonly' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
1239 'listauthors' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
1240 'history' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
1244 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1245 if ( $this->params['action'] == 'submit' ) $this->params['action'] = '';
1247 // Sometimes remove the history field.
1248 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["history"] );
1250 // page does not produce HTML.
1251 $this->tidyValidate = false;
1257 ** a page test for "Special:Booksources".
1259 class specialBooksourcesTest extends pageTest {
1260 function __construct() {
1261 $this->pagePath = "index.php?title=Special:Booksources";
1263 $this->params = array (
1264 'go' => wikiFuzz::makeFuzz( 2 ),
1265 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1266 'isbn' => "0X0" . wikiFuzz::makeFuzz( 2 )
1273 ** a page test for "Special:Allpages".
1275 class specialAllpagesTest extends pageTest {
1276 function __construct() {
1277 $this->pagePath = "index.php?title=Special%3AAllpages";
1279 $this->params = array (
1280 'from' => wikiFuzz::makeFuzz( 2 ),
1281 'namespace' => wikiFuzz::chooseInput( range( -1, 15 ) ),
1282 'go' => wikiFuzz::makeFuzz( 2 )
1289 ** a page test for the page History.
1291 class pageHistoryTest extends pageTest {
1292 function __construct() {
1293 $this->pagePath = "index.php?title=Main_Page&action=history";
1295 $this->params = array (
1296 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1297 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
1298 "go" => wikiFuzz::chooseInput( array( "first", "last", wikiFuzz::makeFuzz( 2 ) ) ),
1299 "dir" => wikiFuzz::chooseInput( array( "prev", "next", wikiFuzz::makeFuzz( 2 ) ) ),
1300 "diff" => wikiFuzz::chooseInput( array( "-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1301 "oldid" => wikiFuzz::chooseInput( array( "prev", "-1", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1302 "feed" => wikiFuzz::makeFuzz( 2 )
1309 ** a page test for the Special:Contributions".
1311 class contributionsTest extends pageTest {
1312 function __construct() {
1313 $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
1315 $this->params = array (
1316 'target' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "newbies", USER_ON_WIKI ) ),
1317 'namespace' => wikiFuzz::chooseInput( array( -1, 15, 1, wikiFuzz::makeFuzz( 2 ) ) ),
1318 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz( 2 ) ) ),
1319 'bot' => wikiFuzz::chooseInput( array( "", "-1", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1320 'go' => wikiFuzz::chooseInput( array( "-1", 'prev', 'next', wikiFuzz::makeFuzz( 2 ) ) )
1327 ** a page test for viewing a normal page, whilst posting various params.
1329 class viewPageTest extends pageTest {
1330 function __construct() {
1331 $this->pagePath = "index.php?title=Main_Page";
1333 $this->params = array (
1334 "useskin" => wikiFuzz::chooseInput( array( "chick", "cologneblue", "myskin",
1335 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz( 2 ) ) ),
1336 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ),
1337 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1338 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1339 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1340 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1341 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1342 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1343 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1344 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1345 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1346 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el",
1347 "su", "sv", "ta", "te", "th", "tr", "tt", "ty", "tyv", "udm",
1348 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1349 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw" ) ),
1350 "returnto" => wikiFuzz::makeFuzz( 2 ),
1351 "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
1352 "rcid" => wikiFuzz::makeFuzz( 2 ),
1353 "action" => wikiFuzz::chooseInput( array( "view", "raw", "render", wikiFuzz::makeFuzz( 2 ), "markpatrolled" ) ),
1354 "printable" => wikiFuzz::makeFuzz( 2 ),
1355 "oldid" => wikiFuzz::makeFuzz( 2 ),
1356 "redirect" => wikiFuzz::makeFuzz( 2 ),
1357 "diff" => wikiFuzz::makeFuzz( 2 ),
1358 "search" => wikiFuzz::makeFuzz( 2 ),
1359 "rdfrom" => wikiFuzz::makeFuzz( 2 ), // things from Article.php from here on:
1360 "token" => wikiFuzz::makeFuzz( 2 ),
1361 "tbid" => wikiFuzz::makeFuzz( 2 ),
1362 // @todo FIXME: Duplicate array key.
1363 "action" => wikiFuzz::chooseInput( array( "purge", wikiFuzz::makeFuzz( 2 ) ) ),
1364 "wpReason" => wikiFuzz::makeFuzz( 2 ),
1365 "wpEditToken" => wikiFuzz::makeFuzz( 2 ),
1366 "from" => wikiFuzz::makeFuzz( 2 ),
1367 "bot" => wikiFuzz::makeFuzz( 2 ),
1368 "summary" => wikiFuzz::makeFuzz( 2 ),
1369 "direction" => wikiFuzz::chooseInput( array( "next", "prev", wikiFuzz::makeFuzz( 2 ) ) ),
1370 "section" => wikiFuzz::makeFuzz( 2 ),
1371 "preload" => wikiFuzz::makeFuzz( 2 ),
1375 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1376 if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); }
1377 elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
1379 // Raw pages cannot really be validated
1380 if ( $this->params["action"] == "raw" ) unset( $this->params["action"] );
1382 // sometimes we don't want to specify certain parameters.
1383 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rcid"] );
1384 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["diff"] );
1385 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rdfrom"] );
1386 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["oldid"] );
1388 // usually don't want action == purge.
1389 if ( wikiFuzz::randnum( 6 ) > 1 ) unset( $this->params["action"] );
1395 ** a page test for "Special:Allmessages".
1397 class specialAllmessagesTest extends pageTest {
1398 function __construct() {
1399 $this->pagePath = "index.php?title=Special:Allmessages";
1401 // only really has one parameter
1402 $this->params = array (
1403 "ot" => wikiFuzz::chooseInput( array( "php", "html", wikiFuzz::makeFuzz( 2 ) ) )
1409 ** a page test for "Special:Newpages".
1411 class specialNewpagesPageTest extends pageTest {
1412 function __construct() {
1413 $this->pagePath = "index.php?title=Special:Newpages";
1415 $this->params = array (
1416 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
1417 "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
1418 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1419 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) )
1422 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1423 if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); }
1424 elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
1429 ** a page test for "redirect.php"
1431 class redirectTest extends pageTest {
1432 function __construct() {
1433 $this->pagePath = "redirect.php";
1435 $this->params = array (
1436 "wpDropdown" => wikiFuzz::makeFuzz( 2 )
1439 // sometimes we don't want to specify certain parameters.
1440 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpDropdown"] );
1446 ** a page test for "Special:Confirmemail"
1448 class confirmEmail extends pageTest {
1449 function __construct() {
1450 // sometimes we send a bogus confirmation code, and sometimes we don't.
1451 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array( "", "/" . wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 1 ) ) ) );
1453 $this->params = array (
1454 "token" => wikiFuzz::makeFuzz( 2 )
1461 ** a page test for "Special:Watchlist"
1462 ** Note: this test would be better if we were logged in.
1464 class watchlistTest extends pageTest {
1465 function __construct() {
1466 $this->pagePath = "index.php?title=Special:Watchlist";
1468 $this->params = array (
1469 "remove" => wikiFuzz::chooseInput( array( "Remove checked items from watchlist", wikiFuzz::makeFuzz( 2 ) ) ),
1470 'days' => wikiFuzz::chooseInput( array( 0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz( 2 ) ) ),
1471 'hideOwn' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1472 'hideBots' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1473 'namespace' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1474 'action' => wikiFuzz::chooseInput( array( "submit", "clear", wikiFuzz::makeFuzz( 2 ) ) ),
1475 'id[]' => wikiFuzz::makeFuzz( 2 ),
1476 'edit' => wikiFuzz::makeFuzz( 2 ),
1477 'token' => wikiFuzz::chooseInput( array( "", "1243213", wikiFuzz::makeFuzz( 2 ) ) )
1480 // sometimes we specifiy "reset", and sometimes we don't.
1481 if ( wikiFuzz::randnum( 3 ) == 0 ) $this->params["reset"] = wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) );
1487 ** a page test for "Special:Blockme"
1489 class specialBlockmeTest extends pageTest {
1490 function __construct() {
1491 $this->pagePath = "index.php?title=Special:Blockme";
1493 $this->params = array ();
1495 // sometimes we specify "ip", and sometimes we don't.
1496 if ( wikiFuzz::randnum( 1 ) == 0 ) {
1497 $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
1504 ** a page test for "Special:Movepage"
1506 class specialMovePage extends pageTest {
1507 function __construct() {
1508 $this->pagePath = "index.php?title=Special:Movepage";
1510 $this->params = array (
1511 "action" => wikiFuzz::chooseInput( array( "success", "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1512 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
1513 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
1514 'wpOldTitle' => wikiFuzz::chooseInput( array( "z", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
1515 'wpNewTitle' => wikiFuzz::chooseInput( array( "y", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
1516 'wpReason' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ) ) ),
1517 'wpMovetalk' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1518 'wpDeleteAndMove' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1519 'wpConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1520 'talkmoved' => wikiFuzz::chooseInput( array( "1", wikiFuzz::makeFuzz( 2 ), "articleexists", 'notalkpage' ) ),
1521 'oldtitle' => wikiFuzz::makeFuzz( 2 ),
1522 'newtitle' => wikiFuzz::makeFuzz( 2 ),
1523 'wpMovetalk' => wikiFuzz::chooseInput( array( "1", "0", wikiFuzz::makeFuzz( 2 ) ) )
1526 // sometimes we don't want to specify certain parameters.
1527 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
1528 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
1529 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpNewTitle"] );
1530 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpReason"] );
1531 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpOldTitle"] );
1537 ** a page test for "Special:Undelete"
1539 class specialUndeletePageTest extends pageTest {
1540 function __construct() {
1541 $this->pagePath = "index.php?title=Special:Undelete";
1543 $this->params = array (
1544 "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1545 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
1546 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
1547 'timestamp' => wikiFuzz::chooseInput( array( "125223", wikiFuzz::makeFuzz( 2 ) ) ),
1548 'file' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1549 'restore' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1550 'preview' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1551 'wpComment' => wikiFuzz::makeFuzz( 2 )
1554 // sometimes we don't want to specify certain parameters.
1555 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
1556 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["target"] );
1557 if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["restore"] );
1558 if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["preview"] );
1564 ** a page test for "Special:Unlockdb"
1566 class specialUnlockdbPageTest extends pageTest {
1567 function __construct() {
1568 $this->pagePath = "index.php?title=Special:Unlockdb";
1570 $this->params = array (
1571 "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
1572 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1573 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) )
1576 // sometimes we don't want to specify certain parameters.
1577 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
1578 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1579 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
1585 ** a page test for "Special:Lockdb"
1587 class specialLockdbPageTest extends pageTest {
1588 function __construct() {
1589 $this->pagePath = "index.php?title=Special:Lockdb";
1591 $this->params = array (
1592 "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
1593 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1594 'wpLockReason' => wikiFuzz::makeFuzz( 2 ),
1595 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1598 // sometimes we don't want to specify certain parameters.
1599 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
1600 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1601 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
1607 ** a page test for "Special:Userrights"
1609 class specialUserrights extends pageTest {
1610 function __construct() {
1611 $this->pagePath = "index.php?title=Special:Userrights";
1613 $this->params = array (
1614 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1615 'user-editname' => wikiFuzz::chooseInput( array( "Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz( 2 ) ) ),
1616 'ssearchuser' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1617 'saveusergroups' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ), "Save User Groups" ),
1618 'member[]' => wikiFuzz::chooseInput( array( "0", "bot", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1619 "available[]" => wikiFuzz::chooseInput( array( "0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1622 // sometimes we don't want to specify certain parameters.
1623 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['ssearchuser'] );
1624 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['saveusergroups'] );
1630 ** a test for page protection and unprotection.
1632 class pageProtectionForm extends pageTest {
1633 function __construct() {
1634 $this->pagePath = "index.php?title=Main_Page";
1636 $this->params = array (
1637 "action" => "protect",
1638 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1639 "mwProtect-level-edit" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
1640 "mwProtect-level-move" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
1641 "mwProtectUnchained" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1642 'mwProtect-reason' => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) )
1646 // sometimes we don't want to specify certain parameters.
1647 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["mwProtectUnchained"] );
1648 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['mwProtect-reason'] );
1654 ** a page test for "Special:Blockip".
1656 class specialBlockip extends pageTest {
1657 function __construct() {
1658 $this->pagePath = "index.php?title=Special:Blockip";
1660 $this->params = array (
1661 "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1662 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1663 "wpBlockAddress" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1664 // something like an IP address, sometimes invalid:
1665 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1666 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1667 "ip" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1668 // something like an IP address, sometimes invalid:
1669 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1670 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1671 "wpBlockOther" => wikiFuzz::chooseInput( array( '', 'Nickj2', wikiFuzz::makeFuzz( 2 ) ) ),
1672 "wpBlockExpiry" => wikiFuzz::chooseInput( array( "other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1673 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz( 2 ) ) ),
1674 "wpBlockReason" => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) ),
1675 "wpAnonOnly" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1676 "wpCreateAccount" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1677 "wpBlock" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1680 // sometimes we don't want to specify certain parameters.
1681 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockOther"] );
1682 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockExpiry"] );
1683 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockReason"] );
1684 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpAnonOnly"] );
1685 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpCreateAccount"] );
1686 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockAddress"] );
1687 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["ip"] );
1693 ** a test for the imagepage.
1695 class imagepageTest extends pageTest {
1696 function __construct() {
1697 $this->pagePath = "index.php?title=Image:Small-email.png";
1699 $this->params = array (
1700 "image" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1701 "wpReason" => wikiFuzz::makeFuzz( 2 ),
1702 "oldimage" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1703 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1706 // sometimes we don't want to specify certain parameters.
1707 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["image"] );
1708 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
1709 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldimage"] );
1710 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEditToken"] );
1716 ** a test for page deletion form.
1718 class pageDeletion extends pageTest {
1719 function __construct() {
1720 $this->pagePath = "index.php?title=Main_Page&action=delete";
1722 $this->params = array (
1723 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1724 "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1725 "wpConfirm" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1728 // sometimes we don't want to specify certain parameters.
1729 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpReason"] );
1730 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpEditToken"] );
1731 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpConfirm"] );
1738 ** a test for Revision Deletion.
1740 class specialRevisionDeletePageTest extends pageTest {
1741 function __construct() {
1742 $this->pagePath = "index.php?title=Special:Revisiondelete";
1744 $this->params = array (
1745 "target" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1746 "oldid" => wikiFuzz::makeFuzz( 2 ),
1747 "oldid[]" => wikiFuzz::makeFuzz( 2 ),
1748 "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1749 "revdelete-hide-text" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1750 "revdelete-hide-comment" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1751 "revdelete-hide-user" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1752 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1755 // sometimes we don't want to specify certain parameters.
1756 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
1757 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid"] );
1758 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid[]"] );
1759 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
1760 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-text"] );
1761 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-comment"] );
1762 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-user"] );
1763 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-restricted"] );
1769 ** a test for Special:Import.
1771 class specialImportPageTest extends pageTest {
1772 function __construct() {
1773 $this->pagePath = "index.php?title=Special:Import";
1775 $this->params = array (
1776 "action" => "submit",
1777 "source" => wikiFuzz::chooseInput( array( "upload", "interwiki", wikiFuzz::makeFuzz( 2 ) ) ),
1778 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1779 "xmlimport" => wikiFuzz::chooseInput( array( "/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1780 "namespace" => wikiFuzz::chooseInput( array( wikiFuzz::randnum( 30, -6 ), wikiFuzz::makeFuzz( 2 ) ) ),
1781 "interwiki" => wikiFuzz::makeFuzz( 2 ),
1782 "interwikiHistory" => wikiFuzz::makeFuzz( 2 ),
1783 "frompage" => wikiFuzz::makeFuzz( 2 ),
1786 // sometimes we don't want to specify certain parameters.
1787 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["action"] );
1788 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["source"] );
1789 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["MAX_FILE_SIZE"] );
1790 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["xmlimport"] );
1791 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwiki"] );
1792 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwikiHistory"] );
1793 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["frompage"] );
1795 // Note: Need to do a file upload to fully test this Special page.
1801 ** a test for thumb.php
1803 class thumbTest extends pageTest {
1804 function __construct() {
1805 $this->pagePath = "thumb.php";
1807 $this->params = array (
1808 "f" => wikiFuzz::chooseInput( array( "..", "\\", "small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1809 "w" => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ),
1810 "r" => wikiFuzz::chooseInput( array( "0", wikiFuzz::makeFuzz( 2 ) ) ),
1813 // sometimes we don't want to specify certain parameters.
1814 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["f"] );
1815 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["w"] );
1816 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["r"] );
1821 ** a test for profileinfo.php
1823 class profileInfo extends pageTest {
1824 function __construct() {
1825 $this->pagePath = "profileinfo.php";
1827 $this->params = array (
1828 "expand" => wikiFuzz::makeFuzz( 2 ),
1829 "sort" => wikiFuzz::chooseInput( array( "time", "count", "name", wikiFuzz::makeFuzz( 2 ) ) ),
1830 "filter" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1833 // sometimes we don't want to specify certain parameters.
1834 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["sort"] );
1835 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["filter"] );
1841 ** a test for Special:Cite (extension Special page).
1843 class specialCitePageTest extends pageTest {
1844 function __construct() {
1845 $this->pagePath = "index.php?title=Special:Cite";
1847 $this->params = array (
1848 "page" => wikiFuzz::chooseInput( array( "\" onmouseover=\"alert(1);\"", "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1849 "id" => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
1852 // sometimes we don't want to specify certain parameters.
1853 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["page"] );
1854 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["id"] );
1860 ** a test for Special:Filepath (extension Special page).
1862 class specialFilepathPageTest extends pageTest {
1863 function __construct() {
1864 $this->pagePath = "index.php?title=Special:Filepath";
1866 $this->params = array (
1867 "file" => wikiFuzz::chooseInput( array( "Small-email.png", "Small-email.png" . wikiFuzz::makeFuzz( 1 ), wikiFuzz::makeFuzz( 2 ) ) ),
1874 ** a test for Special:Renameuser (extension Special page).
1876 class specialRenameuserPageTest extends pageTest {
1877 function __construct() {
1878 $this->pagePath = "index.php?title=Special:Renameuser";
1880 $this->params = array (
1881 "oldusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1882 "newusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1883 "token" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1890 ** a test for Special:Linksearch (extension Special page).
1892 class specialLinksearch extends pageTest {
1893 function __construct() {
1894 $this->pagePath = "index.php?title=Special%3ALinksearch";
1896 $this->params = array (
1897 "target" => wikiFuzz::makeFuzz( 2 ),
1900 // sometimes we don't want to specify certain parameters.
1901 if ( wikiFuzz::randnum( 10 ) == 0 ) unset( $this->params["target"] );
1907 ** a test for Special:CategoryTree (extension Special page).
1909 class specialCategoryTree extends pageTest {
1910 function __construct() {
1911 $this->pagePath = "index.php?title=Special:CategoryTree";
1913 $this->params = array (
1914 "target" => wikiFuzz::makeFuzz( 2 ),
1915 "from" => wikiFuzz::makeFuzz( 2 ),
1916 "until" => wikiFuzz::makeFuzz( 2 ),
1917 "showas" => wikiFuzz::makeFuzz( 2 ),
1918 "mode" => wikiFuzz::chooseInput( array( "pages", "categories", "all", wikiFuzz::makeFuzz( 2 ) ) ),
1921 // sometimes we do want to specify certain parameters.
1922 if ( wikiFuzz::randnum( 5 ) == 0 ) $this->params["notree"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
1928 ** a test for "Special:Chemicalsources" (extension Special page).
1930 class specialChemicalsourcesTest extends pageTest {
1931 function __construct() {
1932 $this->pagePath = "index.php?title=Special:Chemicalsources";
1934 // choose an input format to use.
1935 $format = wikiFuzz::chooseInput(
1936 array( 'go',
1937 'CAS',
1938 'EINECS',
1939 'CHEBI',
1940 'PubChem',
1941 'SMILES',
1942 'InChI',
1943 'ATCCode',
1944 'KEGG',
1945 'RTECS',
1946 'ECNumber',
1947 'DrugBank',
1948 'Formula',
1949 'Name'
1953 // values for different formats usually start with either letters or numbers.
1954 switch ( $format ) {
1955 case 'Name' : $value = "A"; break;
1956 case 'InChI' :
1957 case 'SMILES' :
1958 case 'Formula': $value = "C"; break;
1959 default : $value = "0"; break;
1962 // and then we append the fuzz input.
1963 $this->params = array ( $format => $value . wikiFuzz::makeFuzz( 2 ) );
1969 ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats).
1970 ** Quite involved to test because there are lots of options/parameters, and because
1971 ** for a lot of the functionality if all the parameters don't make sense then it just
1972 ** returns the help screen - so currently a lot of the tests aren't actually doing much
1973 ** because something wasn't right in the query.
1975 ** @todo: Incomplete / unfinished; Runs too fast (suggests not much testing going on).
1977 class api extends pageTest {
1979 // API login mode.
1980 private static function loginMode() {
1981 $arr = array ( "lgname" => wikiFuzz::makeFuzz( 2 ),
1982 "lgpassword" => wikiFuzz::makeFuzz( 2 ),
1984 // sometimes we want to specify the extra "lgdomain" parameter.
1985 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1986 $arr["lgdomain"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
1989 return $arr;
1992 // API OpenSearch mode.
1993 private static function opensearchMode() {
1994 return array ( "search" => wikiFuzz::makeFuzz( 2 ) );
1997 // API watchlist feed mode.
1998 private static function feedwatchlistMode() {
1999 // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible value below?
2000 return array ( "feedformat" => wikiFuzz::chooseInput( array( "rss", "atom" ) ) );
2003 // API query mode.
2004 private static function queryMode() {
2005 // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible params for the elements below?
2006 // Suspect this will stuff up the tests more, but need to check.
2007 $params = array (
2008 // @todo FIXME: More titles.
2009 "titles" => wikiFuzz::chooseInput( array( "Main Page" ) ),
2010 // @todo FIXME: More pageids.
2011 "pageids" => 1,
2012 "prop" => wikiFuzz::chooseInput( array( "info", "revisions", "watchlist" ) ),
2013 "list" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks" ) ),
2014 "meta" => wikiFuzz::chooseInput( array( "siteinfo" ) ),
2015 "generator" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "info", "revisions" ) ),
2016 "siprop" => wikiFuzz::chooseInput( array( "general", "namespaces", "general|namespaces" ) ),
2019 // Add extra parameters based on what list choice we got.
2020 switch ( $params["list"] ) {
2021 case "usercontribs" : self::addListParams ( $params, "uc", array( "limit", "start", "end", "user", "dir" ) ); break;
2022 case "allpages" : self::addListParams ( $params, "ap", array( "from", "prefix", "namespace", "filterredir", "limit" ) ); break;
2023 case "watchlist" : self::addListParams ( $params, "wl", array( "allrev", "start", "end", "namespace", "dir", "limit", "prop" ) ); break;
2024 case "logevents" : self::addListParams ( $params, "le", array( "limit", "type", "start", "end", "user", "dir" ) ); break;
2025 case "recentchanges": self::addListParams ( $params, "rc", array( "limit", "prop", "show", "namespace", "start", "end", "dir" ) ); break;
2026 case "backlinks" : self::addListParams ( $params, "bl", array( "continue", "namespace", "redirect", "limit" ) ); break;
2027 case "embeddedin" : self::addListParams ( $params, "ei", array( "continue", "namespace", "redirect", "limit" ) ); break;
2028 case "imagelinks" : self::addListParams ( $params, "il", array( "continue", "namespace", "redirect", "limit" ) ); break;
2031 if ( $params["prop"] == "revisions" ) {
2032 self::addListParams ( $params, "rv", array( "prop", "limit", "startid", "endid", "end", "dir" ) );
2035 // Sometimes we want redirects, sometimes we don't.
2036 if ( wikiFuzz::randnum( 3 ) == 0 ) {
2037 $params["redirects"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2040 return $params;
2043 // Adds all the elements to the array, using the specified prefix.
2044 private static function addListParams( &$array, $prefix, $elements ) {
2045 foreach ( $elements as $element ) {
2046 $array[$prefix . $element] = self::getParamDetails( $element );
2050 // For a given element name, returns the data for that element.
2051 private static function getParamDetails( $element ) {
2052 switch ( $element ) {
2053 case 'startid' :
2054 case 'endid' :
2055 case 'start' :
2056 case 'end' :
2057 case 'limit' : return wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum( 9000, -100 ), wikiFuzz::makeFuzz( 2 ) ) );
2058 case 'dir' : return wikiFuzz::chooseInput( array( "newer", "older", wikiFuzz::makeFuzz( 2 ) ) );
2059 case 'user' : return wikiFuzz::chooseInput( array( USER_ON_WIKI, wikiFuzz::makeFuzz( 2 ) ) );
2060 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 ) ) );
2061 case 'filterredir': return wikiFuzz::chooseInput( array( "all", "redirects", "nonredirectsallpages", wikiFuzz::makeFuzz( 2 ) ) );
2062 case 'allrev' : return wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2063 case 'prop' : return wikiFuzz::chooseInput( array( "user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikiFuzz::makeFuzz( 2 ) ) );
2064 case 'type' : return wikiFuzz::chooseInput( array( "block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikiFuzz::makeFuzz( 2 ) ) );
2065 case 'hide' : return wikiFuzz::chooseInput( array( "minor", "bots", "anons", "liu", "liu|bots|", wikiFuzz::makeFuzz( 2 ) ) );
2066 case 'show' : return wikiFuzz::chooseInput( array( 'minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikiFuzz::makeFuzz( 2 ) ) );
2067 default : return wikiFuzz::makeFuzz( 2 );
2071 // Entry point.
2072 function __construct() {
2073 $this->pagePath = "api.php";
2075 $modes = array ( "help",
2076 "login",
2077 "opensearch",
2078 "feedwatchlist",
2079 "query" );
2080 $action = wikiFuzz::chooseInput( array_merge ( $modes, array( wikiFuzz::makeFuzz( 2 ) ) ) );
2082 switch ( $action ) {
2083 case "login" : $this->params = self::loginMode();
2084 break;
2085 case "opensearch" : $this->params = self::opensearchMode();
2086 break;
2087 case "feedwatchlist" : $this->params = self::feedwatchlistMode();
2088 break;
2089 case "query" : $this->params = self::queryMode();
2090 break;
2091 case "help" :
2092 default : // Do something random - "Crazy Ivan" mode.
2093 $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
2094 // There is no "helpMode".
2095 if ( $random_mode == "helpMode" ) $random_mode = "queryMode";
2096 $this->params = self::$random_mode();
2097 break;
2100 // Save the selected action.
2101 $this->params["action"] = $action;
2103 // Set the cookie:
2104 // @todo FIXME: Need to get this cookie dynamically set, rather than hard-coded.
2105 $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
2107 // Output format
2108 $this->params["format"] = wikiFuzz::chooseInput( array( "json", "jsonfm", "php", "phpfm",
2109 "wddx", "wddxfm", "xml", "xmlfm",
2110 "yaml", "yamlfm", "raw", "rawfm",
2111 wikiFuzz::makeFuzz( 2 ) ) );
2113 // Page does not produce HTML (sometimes).
2114 $this->tidyValidate = false;
2120 ** a page test for the GeSHi extension.
2122 class GeSHi_Test extends pageTest {
2124 private function getGeSHiContent() {
2125 return "<source lang=\"" . $this->getLang() . "\" "
2126 . ( wikiFuzz::randnum( 2 ) == 0 ? "line " : "" )
2127 . ( wikiFuzz::randnum( 2 ) == 0 ? "strict " : "" )
2128 . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( 6000, -6000 ), wikiFuzz::makeFuzz( 2 ) ) )
2129 . ">"
2130 . wikiFuzz::makeFuzz( 2 )
2131 . "</source>";
2134 private function getLang() {
2135 return wikiFuzz::chooseInput( array( "actionscript", "ada", "apache", "applescript", "asm", "asp", "autoit", "bash", "blitzbasic", "bnf", "c", "c_mac", "caddcl", "cadlisp",
2136 "cfdg", "cfm", "cpp", "cpp-qt", "csharp", "css", "d", "delphi", "diff", "div", "dos", "eiffel", "fortran", "freebasic", "gml", "groovy", "html4strict", "idl",
2137 "ini", "inno", "io", "java", "java5", "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas",
2138 "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", "smalltalk", "smarty",
2139 "sql", "tcl", "text", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80", wikiFuzz::makeFuzz( 1 ) ) );
2142 function __construct() {
2143 $this->pagePath = "index.php?title=WIKIFUZZ";
2145 $this->params = array (
2146 "action" => "submit",
2147 "wpMinoredit" => "test",
2148 "wpPreview" => "test",
2149 "wpSection" => "test",
2150 "wpEdittime" => "test",
2151 "wpSummary" => "test",
2152 "wpScrolltop" => "test",
2153 "wpStarttime" => "test",
2154 "wpAutoSummary" => "test",
2155 "wpTextbox1" => $this->getGeSHiContent() // the main wiki text, contains fake GeSHi content.
2161 ** selects a page test to run.
2162 * @param $count
2163 * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBlockmeTest|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest
2165 function selectPageTest( $count ) {
2167 // if the user only wants a specific test, then only ever give them that.
2168 if ( defined( "SPECIFIC_TEST" ) ) {
2169 $testType = SPECIFIC_TEST;
2170 return new $testType ();
2173 // Some of the time we test Special pages, the remaining
2174 // time we test using the standard edit page.
2175 switch ( $count % 100 ) {
2176 case 0 : return new successfulUserLoginTest();
2177 case 1 : return new listusersTest();
2178 case 2 : return new searchTest();
2179 case 3 : return new recentchangesTest();
2180 case 4 : return new prefixindexTest();
2181 case 5 : return new mimeSearchTest();
2182 case 6 : return new specialLogTest();
2183 case 7 : return new userLoginTest();
2184 case 8 : return new ipblocklistTest();
2185 case 9 : return new newImagesTest();
2186 case 10: return new imagelistTest();
2187 case 11: return new specialExportTest();
2188 case 12: return new specialBooksourcesTest();
2189 case 13: return new specialAllpagesTest();
2190 case 14: return new pageHistoryTest();
2191 case 15: return new contributionsTest();
2192 case 16: return new viewPageTest();
2193 case 17: return new specialAllmessagesTest();
2194 case 18: return new specialNewpagesPageTest();
2195 case 19: return new searchTest();
2196 case 20: return new redirectTest();
2197 case 21: return new confirmEmail();
2198 case 22: return new watchlistTest();
2199 case 23: return new specialBlockmeTest();
2200 case 24: return new specialUndeletePageTest();
2201 case 25: return new specialMovePage();
2202 case 26: return new specialUnlockdbPageTest();
2203 case 27: return new specialLockdbPageTest();
2204 case 28: return new specialUserrights();
2205 case 29: return new pageProtectionForm();
2206 case 30: return new specialBlockip();
2207 case 31: return new imagepageTest();
2208 case 32: return new pageDeletion();
2209 case 33: return new specialRevisionDeletePageTest();
2210 case 34: return new specialImportPageTest();
2211 case 35: return new thumbTest();
2212 case 37: return new profileInfo();
2213 case 38: return new specialCitePageTest();
2214 case 39: return new specialFilepathPageTest();
2215 case 40: return new specialRenameuserPageTest();
2216 case 41: return new specialLinksearch();
2217 case 42: return new specialCategoryTree();
2218 case 43: return new api();
2219 case 44: return new specialChemicalsourcesTest();
2220 default: return new editPageTest();
2225 // ///////////////////// SAVING OUTPUT /////////////////////////
2228 ** Utility function for saving a file. Currently has no error checking.
2230 function saveFile( $data, $name ) {
2231 file_put_contents( $name, $data );
2235 ** Returns a test as an experimental GET-to-POST URL.
2236 ** This doesn't seem to always work though, and sometimes the output is too long
2237 ** to be a valid GET URL, so we also save in other formats.
2238 * @param $test pageTest
2239 * @return string
2241 function getAsURL( pageTest $test ) {
2242 $used_question_mark = ( strpos( $test->getPagePath(), "?" ) !== false );
2243 $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
2244 foreach ( $test->getParams() as $param => $value ) {
2245 if ( !$used_question_mark ) {
2246 $retval .= "?";
2247 $used_question_mark = true;
2249 else {
2250 $retval .= "&";
2252 $retval .= $param . "=" . urlencode( $value );
2254 return $retval;
2259 ** Saves a plain-text human-readable version of a test.
2261 function saveTestAsText( pageTest $test, $filename ) {
2262 $str = "Test: " . $test->getPagePath();
2263 foreach ( $test->getParams() as $param => $value ) {
2264 $str .= "\n$param: $value";
2266 $str .= "\nGet-to-post URL: " . getAsURL( $test ) . "\n";
2267 saveFile( $str, $filename );
2272 ** Saves a test as a standalone basic PHP script that shows this one problem.
2273 ** Resulting script requires PHP-Curl be installed in order to work.
2275 function saveTestAsPHP( pageTest $test, $filename ) {
2276 $str = "<?php\n"
2277 . "\$params = " . var_export( escapeForCurl( $test->getParams() ), true ) . ";\n"
2278 . "\$ch = curl_init();\n"
2279 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2280 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2281 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export( WIKI_BASE_URL . $test->getPagePath(), true ) . ");\n"
2282 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2283 . ( $test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export( $test->getCookie(), true ) . ");\n" : "" )
2284 . "\$result=curl_exec(\$ch);\n"
2285 . "curl_close (\$ch);\n"
2286 . "print \$result;\n"
2287 . "\n";
2288 saveFile( $str, $filename );
2292 * Escapes a value so that it can be used on the command line by Curl.
2293 * Specifically, "<" and "@" need to be escaped if they are the first character,
2294 * otherwise curl interprets these as meaning that we want to insert a file.
2295 * @param $input_params array
2296 * @return array
2298 function escapeForCurl( array $input_params ) {
2299 $output_params = array();
2300 foreach ( $input_params as $param => $value ) {
2301 if ( strlen( $value ) > 0 && ( $value[0] == "@" || $value[0] == "<" ) ) {
2302 $value = "\\" . $value;
2304 $output_params[$param] = $value;
2306 return $output_params;
2311 ** Saves a test as a standalone CURL shell script that shows this one problem.
2312 ** Resulting script requires standalone Curl be installed in order to work.
2314 function saveTestAsCurl( pageTest $test, $filename ) {
2315 $str = "#!/bin/bash\n"
2316 . "curl --silent --include --globoff \\\n"
2317 . ( $test->getCookie() ? " --cookie " . escapeshellarg( $test->getCookie() ) . " \\\n" : "" );
2318 foreach ( escapeForCurl( $test->getParams() ) as $param => $value ) {
2319 $str .= " -F " . escapeshellarg( $param ) . "=" . escapeshellarg( $value ) . " \\\n";
2321 $str .= " " . escapeshellarg( WIKI_BASE_URL . $test->getPagePath() ); // beginning space matters.
2322 $str .= "\n";
2323 saveFile( $str, $filename );
2324 chmod( $filename, 0755 ); // make executable
2329 ** Saves the internal data structure to file.
2331 function saveTestData ( pageTest $test, $filename ) {
2332 saveFile( serialize( $test ), $filename );
2337 ** saves a test in the various formats.
2339 function saveTest( pageTest $test, $testname ) {
2340 $base_name = DIRECTORY . "/" . $testname;
2341 saveTestAsText( $test, $base_name . INFO_FILE );
2342 saveTestAsPHP ( $test, $base_name . PHP_TEST );
2343 saveTestAsCurl( $test, $base_name . CURL_TEST );
2344 saveTestData ( $test, $base_name . DATA_FILE );
2347 // ////////////////// MEDIAWIKI OUTPUT /////////////////////////
2350 * Asks MediaWiki for the HTML output of a test.
2351 * @param $test pageTest
2352 * @return string
2354 function wikiTestOutput( pageTest $test ) {
2356 $ch = curl_init();
2358 // specify the cookie, if required.
2359 if ( $test->getCookie() ) {
2360 curl_setopt( $ch, CURLOPT_COOKIE, $test->getCookie() );
2362 curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST
2364 $params = escapeForCurl( $test->getParams() );
2365 curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2367 curl_setopt( $ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2368 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
2370 $result = curl_exec ( $ch );
2372 // if we encountered an error, then say so, and return an empty string.
2373 if ( curl_error( $ch ) ) {
2374 print "\nCurl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch );
2375 $result = "";
2378 curl_close ( $ch );
2380 return $result;
2384 // ////////////////// HTML VALIDATION /////////////////////////
2387 * Asks the validator whether this is valid HTML, or not.
2388 * @param $text string
2389 * @return array
2391 function validateHTML( $text ) {
2393 $params = array ( "fragment" => $text );
2395 $ch = curl_init();
2397 curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST
2398 curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2399 curl_setopt( $ch, CURLOPT_URL, VALIDATOR_URL ); // set url to post to
2400 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
2402 $result = curl_exec ( $ch );
2404 // if we encountered an error, then log it, and exit.
2405 if ( curl_error( $ch ) ) {
2406 trigger_error( "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) );
2407 print "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) . " - exiting.\n";
2408 exit( 1 );
2411 curl_close ( $ch );
2413 $valid = ( strpos( $result, "Failed validation" ) === false );
2415 return array( $valid, $result );
2419 * Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2420 * @param $name
2421 * @return bool
2423 function tidyCheckFile( $name ) {
2424 $file = DIRECTORY . "/" . $name;
2425 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2426 $x = `$command`;
2428 // Look for the most interesting Tidy errors and warnings.
2429 if ( strpos( $x, "end of file while parsing attributes" ) !== false
2430 || strpos( $x, "attribute with missing trailing quote mark" ) !== false
2431 || strpos( $x, "missing '>' for end of tag" ) !== false
2432 || strpos( $x, "Error:" ) !== false ) {
2433 print "\nTidy found something - view details with: $command";
2434 return false;
2435 } else {
2436 return true;
2441 ** Returns whether or not an database error log file has changed in size since
2442 ** the last time this was run. This is used to tell if a test caused a DB error.
2443 * @return bool
2445 function dbErrorLogged() {
2446 static $filesize;
2448 // first time running this function
2449 if ( !isset( $filesize ) ) {
2450 // create log if it does not exist
2451 if ( DB_ERROR_LOG_FILE && !file_exists( DB_ERROR_LOG_FILE ) ) {
2452 saveFile( '', DB_ERROR_LOG_FILE );
2454 $filesize = filesize( DB_ERROR_LOG_FILE );
2455 return false;
2458 $newsize = filesize( DB_ERROR_LOG_FILE );
2459 // if the log has grown, then assume the current test caused it.
2460 if ( $newsize != $filesize ) {
2461 $filesize = $newsize;
2462 return true;
2465 return false;
2468 // //////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2471 * takes a page test, and runs it and tests it for problems in the output.
2472 * Returns: False on finding a problem, or True on no problems being found.
2473 * @param $test pageTest
2474 * @param $testname
2475 * @param $can_overwrite bool
2476 * @return bool
2478 function runWikiTest( pageTest $test, &$testname, $can_overwrite = false ) {
2480 // by default don't overwrite a previous test of the same name.
2481 while ( ! $can_overwrite && file_exists( DIRECTORY . "/" . $testname . DATA_FILE ) ) {
2482 $testname .= "-" . mt_rand( 0, 9 );
2485 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2487 // Store the time before and after, to find slow pages.
2488 $before = microtime( true );
2490 // Get MediaWiki to give us the output of this test.
2491 $wiki_preview = wikiTestOutput( $test );
2493 $after = microtime( true );
2495 // if we received no response, then that's interesting.
2496 if ( $wiki_preview == "" ) {
2497 print "\nNo response received for: $filename";
2498 return false;
2501 // save output HTML to file.
2502 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2503 saveFile( $wiki_preview, $html_file );
2505 // if there were PHP errors in the output, then that's interesting too.
2506 if ( strpos( $wiki_preview, "<b>Warning</b>: " ) !== false
2507 || strpos( $wiki_preview, "<b>Fatal error</b>: " ) !== false
2508 || strpos( $wiki_preview, "<b>Notice</b>: " ) !== false
2509 || strpos( $wiki_preview, "<b>Error</b>: " ) !== false
2510 || strpos( $wiki_preview, "<b>Strict Standards:</b>" ) !== false
2512 $error = substr( $wiki_preview, strpos( $wiki_preview, "</b>:" ) + 7, 50 );
2513 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2514 if ( $error != "Unknown: The session id contains illegal character" ) {
2515 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2516 return false;
2520 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2521 if ( strpos( $wiki_preview, "Backtrace:" ) !== false ) {
2522 print "\nInternal MediaWiki error in HTML output: $html_file";
2523 return false;
2526 // if there was a Parser error comment in the output, then that's potentially interesting.
2527 if ( strpos( $wiki_preview, "!-- ERR" ) !== false ) {
2528 print "\nParser Error comment in HTML output: $html_file";
2529 return false;
2532 // if a database error was logged, then that's definitely interesting.
2533 if ( dbErrorLogged() ) {
2534 print "\nDatabase Error logged for: $filename";
2535 return false;
2538 // validate result
2539 $valid = true;
2540 if ( VALIDATE_ON_WEB ) {
2541 list ( $valid, $validator_output ) = validateHTML( $wiki_preview );
2542 if ( !$valid ) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2545 // Get tidy to check the page, unless we already know it produces non-XHTML output.
2546 if ( $test->tidyValidate() ) {
2547 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2550 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2551 if ( ( $after - $before ) >= 2 ) {
2552 print "\nParticularly slow to render (" . round( $after - $before, 2 ) . " seconds): $filename";
2553 return false;
2556 if ( $valid ) {
2557 // Remove temp HTML file if test was valid:
2558 unlink( $html_file );
2559 } elseif ( VALIDATE_ON_WEB ) {
2560 saveFile( $validator_output, DIRECTORY . "/" . $testname . ".validator_output.html" );
2563 return $valid;
2567 // ///////////////// RERUNNING OLD TESTS ///////////////////
2570 ** We keep our failed tests so that they can be rerun.
2571 ** This function does that retesting.
2573 function rerunPreviousTests() {
2574 print "Retesting previously found problems.\n";
2576 $dir_contents = scandir ( DIRECTORY );
2578 // sort file into the order a normal person would use.
2579 natsort ( $dir_contents );
2581 foreach ( $dir_contents as $file ) {
2583 // if file is not a test, then skip it.
2584 // Note we need to escape any periods or will be treated as "any character".
2585 $matches = array();
2586 if ( !preg_match( "/(.*)" . str_replace( ".", "\.", DATA_FILE ) . "$/", $file, $matches ) ) continue;
2588 // reload the test.
2589 $full_path = DIRECTORY . "/" . $file;
2590 $test = unserialize( file_get_contents( $full_path ) );
2592 // if this is not a valid test, then skip it.
2593 if ( ! $test instanceof pageTest ) {
2594 print "\nSkipping invalid test - $full_path";
2595 continue;
2598 // The date format is in Apache log format, which makes it easier to locate
2599 // which retest caused which error in the Apache logs (only happens usually if
2600 // apache segfaults).
2601 if ( !QUIET ) print "[" . date ( "D M d H:i:s Y" ) . "] Retesting $file (" . get_class( $test ) . ")";
2603 // run test
2604 $testname = $matches[1];
2605 $valid = runWikiTest( $test, $testname, true );
2607 if ( !$valid ) {
2608 saveTest( $test, $testname );
2609 if ( QUIET ) {
2610 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
2611 } else {
2612 print "\n";
2615 else {
2616 if ( !QUIET ) print "\r";
2617 if ( DELETE_PASSED_RETESTS ) {
2618 $prefix = DIRECTORY . "/" . $testname;
2619 if ( is_file( $prefix . DATA_FILE ) ) unlink( $prefix . DATA_FILE );
2620 if ( is_file( $prefix . PHP_TEST ) ) unlink( $prefix . PHP_TEST );
2621 if ( is_file( $prefix . CURL_TEST ) ) unlink( $prefix . CURL_TEST );
2622 if ( is_file( $prefix . INFO_FILE ) ) unlink( $prefix . INFO_FILE );
2627 print "\nDone retesting.\n";
2631 // //////////////////// MAIN LOOP ////////////////////////
2634 // first check whether CURL is installed, because sometimes it's not.
2635 if ( ! function_exists( 'curl_init' ) ) {
2636 die( "Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n" );
2639 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2640 // access it staticly and not have any globals.
2641 wikiFuzz::$types = array_keys( wikiFuzz::$data );
2643 // Make directory if doesn't exist
2644 if ( !is_dir( DIRECTORY ) ) {
2645 mkdir ( DIRECTORY, 0700 );
2647 // otherwise, we first retest the things that we have found in previous runs
2648 elseif ( RERUN_OLD_TESTS ) {
2649 rerunPreviousTests();
2652 // main loop.
2653 $start_time = date( "U" );
2654 $num_errors = 0;
2655 if ( !QUIET ) {
2656 print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2657 print "Press CTRL+C to stop testing.\n";
2660 for ( $count = 0; true; $count++ ) {
2661 if ( !QUIET ) {
2662 // spinning progress indicator.
2663 switch( $count % 4 ) {
2664 case '0': print "\r/"; break;
2665 case '1': print "\r-"; break;
2666 case '2': print "\r\\"; break;
2667 case '3': print "\r|"; break;
2669 print " $count";
2672 // generate a page test to run.
2673 $test = selectPageTest( $count );
2675 $mins = ( date( "U" ) - $start_time ) / 60;
2676 if ( !QUIET && $mins > 0 ) {
2677 print ". $num_errors poss errors. "
2678 . floor( $mins ) . " mins. "
2679 . round ( $count / $mins, 0 ) . " tests/min. "
2680 . get_class( $test ); // includes the current test name.
2683 // run this test against MediaWiki, and see if the output was valid.
2684 $testname = $count;
2685 $valid = runWikiTest( $test, $testname, false );
2687 // save the failed test
2688 if ( ! $valid ) {
2689 if ( QUIET ) {
2690 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
2691 } else {
2692 print "\n";
2694 saveTest( $test, $testname );
2695 $num_errors += 1;
2696 } elseif ( KEEP_PASSED_TESTS ) {
2697 // print current time, with microseconds (matches "strace" format), and the test name.
2698 print " " . date( "H:i:s." ) . substr( current( explode( " ", microtime() ) ), 2 ) . " " . $testname;
2699 saveTest( $test, $testname );
2702 // stop if we have reached max number of errors.
2703 if ( defined( "MAX_ERRORS" ) && $num_errors >= MAX_ERRORS ) {
2704 break;
2707 // stop if we have reached max number of mins runtime.
2708 if ( defined( "MAX_RUNTIME" ) && $mins >= MAX_RUNTIME ) {
2709 break;