on the line).
- // So the source output looks like this:
- //
- //
- //
-
-
-
GeSHi examples
-
-
-
-
GeSHi Example Script
-
To use this script, make sure that geshi.php is in the parent directory or in your
-include_path, and that the language files are in a subdirectory of GeSHi's directory called geshi/.
-
Enter your source and a language to highlight the source in and submit, or just choose a language to
-have that language file highlighted in PHP.
-parse_code();
- echo '
';
-}
-?>
-
-
-
-
diff --git a/extensions/SyntaxHighlight_GeSHi/geshi/contrib/langcheck.php b/extensions/SyntaxHighlight_GeSHi/geshi/contrib/langcheck.php
deleted file mode 100644
index fa8288be..00000000
--- a/extensions/SyntaxHighlight_GeSHi/geshi/contrib/langcheck.php
+++ /dev/null
@@ -1,769 +0,0 @@
-';
- $colors = array(
- TYPE_NOTICE => '
',
- TYPE_WARNING => '',
- TYPE_ERROR => '',
- TYPE_OK => ''
- );
- } else {
- $end = chr(27).'[0m';
- $colors = array(
- TYPE_NOTICE => chr(27).'[1m',
- TYPE_WARNING => chr(27).'[1;33m',
- TYPE_ERROR => chr(27).'[1;31m',
- TYPE_OK => chr(27).'[1;32m'
- );
- }
- }
-
- if ( !isset($colors[$level]) ) {
- trigger_error("no colors for level $level", E_USER_ERROR);
- }
-
- return $colors[$level].$string.$end;
-}
-
-define ('TYPE_NOTICE', 0);
-define ('TYPE_WARNING', 1);
-define ('TYPE_ERROR', 2);
-define ('TYPE_OK', 3);
-
-$error_abort = false;
-$error_cache = array();
-function output_error_cache(){
- global $error_cache, $error_abort;
-
- if(count($error_cache)) {
- echo colorize(TYPE_ERROR, "Failed");
- if ( PHP_SAPI == 'cli' ) {
- echo "\n\n";
- } else {
- echo "
\n";
- }
- foreach($error_cache as $error_msg) {
- if ( PHP_SAPI == 'cli' ) {
- echo "\n";
- } else {
- echo "- ";
- }
- switch($error_msg['t']) {
- case TYPE_NOTICE:
- $msg = 'NOTICE';
- break;
- case TYPE_WARNING:
- $msg = 'WARNING';
- break;
- case TYPE_ERROR:
- $msg = 'ERROR';
- break;
- }
- echo colorize($error_msg['t'], $msg);
- if ( PHP_SAPI == 'cli' ) {
- echo "\t" . $error_msg['m'];
- } else {
- echo " " . $error_msg['m'] . "
";
- }
- }
- if ( PHP_SAPI == 'cli' ) {
- echo "\n";
- } else {
- echo "
\n";
- }
- } else {
- echo colorize(TYPE_OK, "OK");
- if ( PHP_SAPI == 'cli' ) {
- echo "\n";
- } else {
- echo "\n
";
- }
- }
- echo "\n";
-
- $error_cache = array();
-}
-
-function report_error($type, $message) {
- global $error_cache, $error_abort;
-
- $error_cache[] = array('t' => $type, 'm' => $message);
- if(TYPE_ERROR == $type) {
- $error_abort = true;
- }
-}
-
-function dupfind_strtolower(&$value){
- $value = strtolower($value);
-}
-
-if ( PHP_SAPI != 'cli' ) { ?>
-
-
-
- GeSHi Language File Validation Script
-
-
-
-GeSHi Language File Validation Script
-To use this script, make sure that geshi.php is in the
-parent directory or in your include_path, and that the language files are in a
-subdirectory of GeSHi's directory called geshi/.
-Everything else will be done by this script automatically. After the script
-finished you should see messages of what could cause trouble with GeSHi or where
-your language files can be improved. Please be patient, as this might take some time.
-
-
-- Checking where to find GeSHi installation ...
-
-
-
-To use this script, make sure that is in the
-parent directory or in your include_path, and that the language files are in a
-subdirectory of GeSHi's directory called .
-
-Everything else will be done by this script automatically. After the script
-finished you should see messages of what could cause trouble with GeSHi or where
-your language files can be improved. Please be patient, as this might take some time.
-
-
-Checking where to find GeSHi installation ...\n
- Listing available language files ... ";
- }
-
- if (!($dir = @opendir(GESHI_LANG_ROOT))) {
- report_error(TYPE_ERROR, 'Error requesting listing for available language files!');
- }
-
- $languages = array();
-
- if(!$error_abort) {
- while ($file = readdir($dir)) {
- if (!$file || $file[0] == '.' || strpos($file, '.php') === false) {
- continue;
- }
- $lang = substr($file, 0, strpos($file, '.'));
- if(4 != strlen($file) - strlen($lang)) {
- continue;
- }
- $languages[] = $lang;
- }
- closedir($dir);
- }
-
- $languages = array_unique($languages);
- sort($languages);
-
- if(!count($languages)) {
- report_error(TYPE_WARNING, 'Unable to locate any usable language files in "'.GESHI_LANG_ROOT.'"!');
- }
-
- output_error_cache();
-}
-
-if ( PHP_SAPI == 'cli' ) {
- if (isset($_SERVER['argv'][1]) && in_array($_SERVER['argv'][1], $languages)) {
- $languages = array($_SERVER['argv'][1]);
- }
-} else {
- if (isset($_REQUEST['show']) && in_array($_REQUEST['show'], $languages)) {
- $languages = array($_REQUEST['show']);
- }
-}
-
-if(!$error_abort) {
- foreach ($languages as $lang) {
-
- if ( PHP_SAPI == 'cli' ) {
- echo "Validating language file for '$lang' ...\t\t";
- } else {
- echo "
\n- Validating language file for '$lang' ... ";
- }
-
- $langfile = GESHI_LANG_ROOT . $lang . '.php';
-
- $language_data = array();
-
- if(!is_file($langfile)) {
- report_error(TYPE_ERROR, 'The path "' .$langfile. '" does not ressemble a regular file!');
- } elseif(!is_readable($langfile)) {
- report_error(TYPE_ERROR, 'Cannot read file "' .$langfile. '"!');
- } else {
- $langfile_content = file_get_contents($langfile);
- if(preg_match("/\?>(?:\r?\n|\r(?!\n)){2,}\Z/", $langfile_content)) {
- report_error(TYPE_ERROR, 'Language file contains trailing empty lines at EOF!');
- }
- if(!preg_match("/\?>(?:\r?\n|\r(?!\n))?\Z/", $langfile_content)) {
- report_error(TYPE_ERROR, 'Language file contains no PHP end marker at EOF!');
- }
- if(preg_match("/\t/", $langfile_content)) {
- report_error(TYPE_NOTICE, 'Language file contains unescaped tabulator chars (probably for indentation)!');
- }
- if(preg_match('/^(?: )*(?! )(?! \*) /m', $langfile_content)) {
- report_error(TYPE_NOTICE, 'Language file contains irregular indentation (other than 4 spaces per indentation level)!');
- }
-
- if(!preg_match("/\/\*\*((?!\*\/).)*?Author:((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not contain a specification of an author!');
- }
- if(!preg_match("/\/\*\*((?!\*\/).)*?Copyright:((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not contain a specification of the copyright!');
- }
- if(!preg_match("/\/\*\*((?!\*\/).)*?Release Version:((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not contain a specification of the release version!');
- }
- if(!preg_match("/\/\*\*((?!\*\/).)*?Date Started:((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not contain a specification of the date it was started!');
- }
- if(!preg_match("/\/\*\*((?!\*\/).)*?This file is part of GeSHi\.((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not state that it belongs to GeSHi!');
- }
- if(!preg_match("/\/\*\*((?!\*\/).)*?language file for GeSHi\.((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not state that it is a language file for GeSHi!');
- }
- if(!preg_match("/\/\*\*((?!\*\/).)*?GNU General Public License((?!\*\/).)*?\*\//s", $langfile_content)) {
- report_error(TYPE_WARNING, 'Language file does not state that it is provided under the terms of the GNU GPL!');
- }
-
- unset($langfile_content);
-
- include $langfile;
-
- if(!isset($language_data)) {
- report_error(TYPE_ERROR, 'Language file does not contain a $language_data structure to check!');
- } elseif (!is_array($language_data)) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data structure which is not an array!');
- }
- }
-
- if(!$error_abort) {
- if(!isset($language_data['LANG_NAME'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'LANG_NAME\'] specification!');
- } elseif (!is_string($language_data['LANG_NAME'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'LANG_NAME\'] specification which is not a string!');
- }
-
- if(!isset($language_data['COMMENT_SINGLE'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'COMMENT_SIGNLE\'] structure to check!');
- } elseif (!is_array($language_data['COMMENT_SINGLE'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_SINGLE\'] structure which is not an array!');
- }
-
- if(!isset($language_data['COMMENT_MULTI'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'COMMENT_MULTI\'] structure to check!');
- } elseif (!is_array($language_data['COMMENT_MULTI'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_MULTI\'] structure which is not an array!');
- }
-
- if(isset($language_data['COMMENT_REGEXP'])) {
- if (!is_array($language_data['COMMENT_REGEXP'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_REGEXP\'] structure which is not an array!');
- }
- }
-
- if(!isset($language_data['QUOTEMARKS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'QUOTEMARKS\'] structure to check!');
- } elseif (!is_array($language_data['QUOTEMARKS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'QUOTEMARKS\'] structure which is not an array!');
- }
-
- if(isset($language_data['HARDQUOTE'])) {
- if (!is_array($language_data['HARDQUOTE'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'HARDQUOTE\'] structure which is not an array!');
- }
- }
-
- if(!isset($language_data['ESCAPE_CHAR'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'ESCAPE_CHAR\'] specification to check!');
- } elseif (!is_string($language_data['ESCAPE_CHAR'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification which is not a string!');
- } elseif (1 < strlen($language_data['ESCAPE_CHAR'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification is not empty or exactly one char!');
- }
-
- if(!isset($language_data['CASE_KEYWORDS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'CASE_KEYWORDS\'] specification!');
- } elseif (!is_int($language_data['CASE_KEYWORDS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is not an integer!');
- } elseif (GESHI_CAPS_NO_CHANGE != $language_data['CASE_KEYWORDS'] &&
- GESHI_CAPS_LOWER != $language_data['CASE_KEYWORDS'] &&
- GESHI_CAPS_UPPER != $language_data['CASE_KEYWORDS']) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is neither of GESHI_CAPS_NO_CHANGE, GESHI_CAPS_LOWER nor GESHI_CAPS_UPPER!');
- }
-
- if(!isset($language_data['KEYWORDS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'KEYWORDS\'] structure to check!');
- } elseif (!is_array($language_data['KEYWORDS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'KEYWORDS\'] structure which is not an array!');
- } else {
- foreach($language_data['KEYWORDS'] as $kw_key => $kw_value) {
- if(!is_integer($kw_key)) {
- report_error(TYPE_WARNING, "Language file contains an key '$kw_key' in \$language_data['KEYWORDS'] that is not integer!");
- } elseif (!is_array($kw_value)) {
- report_error(TYPE_ERROR, "Language file contains a \$language_data['KEYWORDS']['$kw_value'] structure which is not an array!");
- }
- }
- }
-
- if(!isset($language_data['SYMBOLS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'SYMBOLS\'] structure to check!');
- } elseif (!is_array($language_data['SYMBOLS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'SYMBOLS\'] structure which is not an array!');
- }
-
- if(!isset($language_data['CASE_SENSITIVE'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'CASE_SENSITIVE\'] structure to check!');
- } elseif (!is_array($language_data['CASE_SENSITIVE'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_SENSITIVE\'] structure which is not an array!');
- } else {
- foreach($language_data['CASE_SENSITIVE'] as $cs_key => $cs_value) {
- if(!is_integer($cs_key)) {
- report_error(TYPE_WARNING, "Language file contains an key '$cs_key' in \$language_data['CASE_SENSITIVE'] that is not integer!");
- } elseif (!is_bool($cs_value)) {
- report_error(TYPE_ERROR, "Language file contains a Case Sensitivity specification for \$language_data['CASE_SENSITIVE']['$cs_value'] which is not a boolean!");
- }
- }
- }
-
- if(!isset($language_data['URLS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'URLS\'] structure to check!');
- } elseif (!is_array($language_data['URLS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'URLS\'] structure which is not an array!');
- } else {
- foreach($language_data['URLS'] as $url_key => $url_value) {
- if(!is_integer($url_key)) {
- report_error(TYPE_WARNING, "Language file contains an key '$url_key' in \$language_data['URLS'] that is not integer!");
- } elseif (!is_string($url_value)) {
- report_error(TYPE_ERROR, "Language file contains a Documentation URL specification for \$language_data['URLS']['$url_value'] which is not a string!");
- } elseif (preg_match('#&([^;]*(=|$))#U', $url_value)) {
- report_error(TYPE_ERROR, "Language file contains unescaped ampersands (&) in \$language_data['URLS']!");
- }
- }
- }
-
- if(!isset($language_data['OOLANG'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'OOLANG\'] specification!');
- } elseif (!is_int($language_data['OOLANG']) && !is_bool($language_data['OOLANG'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither boolean nor integer!');
- } elseif (false !== $language_data['OOLANG'] &&
- true !== $language_data['OOLANG'] &&
- 2 !== $language_data['OOLANG']) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither of false, true or 2!');
- }
-
- if(!isset($language_data['OBJECT_SPLITTERS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'OBJECT_SPLITTERS\'] structure to check!');
- } elseif (!is_array($language_data['OBJECT_SPLITTERS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OBJECT_SPLITTERS\'] structure which is not an array!');
- }
-
- if(!isset($language_data['REGEXPS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'REGEXPS\'] structure to check!');
- } elseif (!is_array($language_data['REGEXPS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'REGEXPS\'] structure which is not an array!');
- }
-
- if(!isset($language_data['STRICT_MODE_APPLIES'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'STRICT_MODE_APPLIES\'] specification!');
- } elseif (!is_int($language_data['STRICT_MODE_APPLIES'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is not an integer!');
- } elseif (GESHI_MAYBE != $language_data['STRICT_MODE_APPLIES'] &&
- GESHI_ALWAYS != $language_data['STRICT_MODE_APPLIES'] &&
- GESHI_NEVER != $language_data['STRICT_MODE_APPLIES']) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is neither of GESHI_MAYBE, GESHI_ALWAYS nor GESHI_NEVER!');
- }
-
- if(!isset($language_data['SCRIPT_DELIMITERS'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'SCRIPT_DELIMITERS\'] structure to check!');
- } elseif (!is_array($language_data['SCRIPT_DELIMITERS'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'SCRIPT_DELIMITERS\'] structure which is not an array!');
- }
-
- if(!isset($language_data['HIGHLIGHT_STRICT_BLOCK'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'HIGHLIGHT_STRICT_BLOCK\'] structure to check!');
- } elseif (!is_array($language_data['HIGHLIGHT_STRICT_BLOCK'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'HIGHLIGHT_STRICT_BLOCK\'] structure which is not an array!');
- }
-
- if(isset($language_data['TAB_WIDTH'])) {
- if (!is_int($language_data['TAB_WIDTH'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is not an integer!');
- } elseif (1 > $language_data['TAB_WIDTH']) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is less than 1!');
- }
- }
-
- if(isset($language_data['PARSER_CONTROL'])) {
- if (!is_array($language_data['PARSER_CONTROL'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'PARSER_CONTROL\'] structure which is not an array!');
- }
- }
-
- if(!isset($language_data['STYLES'])) {
- report_error(TYPE_ERROR, 'Language file contains no $language_data[\'STYLES\'] structure to check!');
- } elseif (!is_array($language_data['STYLES'])) {
- report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STYLES\'] structure which is not an array!');
- } else {
- $style_arrays = array('KEYWORDS', 'COMMENTS', 'ESCAPE_CHAR',
- 'BRACKETS', 'STRINGS', 'NUMBERS', 'METHODS', 'SYMBOLS',
- 'REGEXPS', 'SCRIPT');
- foreach($style_arrays as $style_kind) {
- if(!isset($language_data['STYLES'][$style_kind])) {
- report_error(TYPE_ERROR, "Language file contains no \$language_data['STYLES']['$style_kind'] structure to check!");
- } elseif (!is_array($language_data['STYLES'][$style_kind])) {
- report_error(TYPE_ERROR, "Language file contains a \$language_data['STYLES\']['$style_kind'] structure which is not an array!");
- } else {
- foreach($language_data['STYLES'][$style_kind] as $sk_key => $sk_value) {
- if(!is_int($sk_key) && ('COMMENTS' != $style_kind && 'MULTI' != $sk_key)
- && !(('STRINGS' == $style_kind || 'ESCAPE_CHAR' == $style_kind) && 'HARD' == $sk_key)) {
- report_error(TYPE_WARNING, "Language file contains an key '$sk_key' in \$language_data['STYLES']['$style_kind'] that is not integer!");
- } elseif (!is_string($sk_value)) {
- report_error(TYPE_WARNING, "Language file contains a CSS specification for \$language_data['STYLES']['$style_kind'][$key] which is not a string!");
- }
- }
- }
- }
-
- unset($style_arrays);
- }
- }
-
- if(!$error_abort) {
- //Initial sanity checks survived? --> Let's dig deeper!
- foreach($language_data['KEYWORDS'] as $key => $keywords) {
- if(!isset($language_data['CASE_SENSITIVE'][$key])) {
- report_error(TYPE_ERROR, "Language file contains no \$language_data['CASE_SENSITIVE'] specification for keyword group $key!");
- }
- if(!isset($language_data['URLS'][$key])) {
- report_error(TYPE_ERROR, "Language file contains no \$language_data['URLS'] specification for keyword group $key!");
- }
- if(empty($keywords)) {
- report_error(TYPE_WARNING, "Language file contains an empty keyword list in \$language_data['KEYWORDS'] for group $key!");
- }
- foreach($keywords as $id => $kw) {
- if(!is_string($kw)) {
- report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['KEYWORDS'][$key][$id]!");
- } elseif (!strlen($kw)) {
- report_error(TYPE_ERROR, "Language file contains an empty string entry at \$language_data['KEYWORDS'][$key][$id]!");
- } elseif (preg_match('/^([\(\)\{\}\[\]\^=.,:;\-+\*\/%\$\"\'\?]|&[\w#]\w*;)+$/i', $kw)) {
- report_error(TYPE_NOTICE, "Language file contains an keyword ('$kw') at \$language_data['KEYWORDS'][$key][$id] which seems to be better suited for the symbols section!");
- }
- }
- if(isset($language_data['CASE_SENSITIVE'][$key]) && !$language_data['CASE_SENSITIVE'][$key]) {
- array_walk($keywords, 'dupfind_strtolower');
- }
- if(count($keywords) != count(array_unique($keywords))) {
- $kw_diffs = array_count_values($keywords);
- foreach($kw_diffs as $kw => $kw_count) {
- if($kw_count > 1) {
- report_error(TYPE_WARNING, "Language file contains per-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key]!");
- }
- }
- }
- }
-
- $disallowed_before = "(?|^&";
- $disallowed_after = "(?![a-zA-Z0-9_\|%\\-&;";
-
- foreach($language_data['KEYWORDS'] as $key => $keywords) {
- foreach($language_data['KEYWORDS'] as $key2 => $keywords2) {
- if($key2 <= $key) {
- continue;
- }
- $kw_diffs = array_intersect($keywords, $keywords2);
- foreach($kw_diffs as $kw) {
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS'])) {
- //Check the precondition\post-cindition for the involved keyword groups
- $g1_pre = $disallowed_before;
- $g2_pre = $disallowed_before;
- $g1_post = $disallowed_after;
- $g2_post = $disallowed_after;
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'])) {
- $g1_pre = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
- $g2_pre = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
- }
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'])) {
- $g1_post = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
- $g2_post = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
- }
-
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'])) {
- $g1_pre = $language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'];
- }
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'])) {
- $g1_post = $language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'];
- }
-
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'])) {
- $g2_pre = $language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'];
- }
- if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'])) {
- $g2_post = $language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'];
- }
-
- if($g1_pre != $g2_pre || $g1_post != $g2_post) {
- continue;
- }
- }
- report_error(TYPE_WARNING, "Language file contains cross-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key] and \$language_data['KEYWORDS'][$key2]!");
- }
- }
- }
- foreach($language_data['CASE_SENSITIVE'] as $key => $keywords) {
- if(!isset($language_data['KEYWORDS'][$key]) && $key != GESHI_COMMENTS) {
- report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['CASE_SENSITIVE'] specification for non-existing keyword group $key!");
- }
- }
- foreach($language_data['URLS'] as $key => $keywords) {
- if(!isset($language_data['KEYWORDS'][$key])) {
- report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['URLS'] specification for non-existing keyword group $key!");
- }
- }
- foreach($language_data['STYLES']['KEYWORDS'] as $key => $keywords) {
- if(!isset($language_data['KEYWORDS'][$key])) {
- report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['STYLES']['KEYWORDS'] specification for non-existing keyword group $key!");
- }
- }
-
- foreach($language_data['COMMENT_SINGLE'] as $ck => $cv) {
- if(!is_int($ck)) {
- report_error(TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_SINGLE'] that is not integer!");
- }
- if(!is_string($cv)) {
- report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_SINGLE'][$ck]!");
- }
- if(!isset($language_data['STYLES']['COMMENTS'][$ck])) {
- report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
- }
- }
- if(isset($language_data['COMMENT_REGEXP'])) {
- foreach($language_data['COMMENT_REGEXP'] as $ck => $cv) {
- if(!is_int($ck)) {
- report_error(TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_REGEXP'] that is not integer!");
- }
- if(!is_string($cv)) {
- report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_REGEXP'][$ck]!");
- }
- if(!isset($language_data['STYLES']['COMMENTS'][$ck])) {
- report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
- }
- }
- }
- foreach($language_data['STYLES']['COMMENTS'] as $ck => $cv) {
- if($ck != 'MULTI' && !isset($language_data['COMMENT_SINGLE'][$ck]) &&
- !isset($language_data['COMMENT_REGEXP'][$ck])) {
- report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['COMMENTS'] specification for Single Line or Regular-Expression Comment key $ck!");
- }
- }
- if (isset($language_data['STYLES']['STRINGS']['HARD'])) {
- if (empty($language_data['HARDQUOTE'])) {
- report_error(TYPE_NOTICE, "Language file contains superfluous \$language_data['STYLES']['STRINGS'] specification for key 'HARD', but no 'HARDQUOTE's are defined!");
- }
- unset($language_data['STYLES']['STRINGS']['HARD']);
- }
- foreach($language_data['STYLES']['STRINGS'] as $sk => $sv) {
- if($sk && !isset($language_data['QUOTEMARKS'][$sk])) {
- report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['STRINGS'] specification for non-existing quotemark key $sk!");
- }
- }
-
- foreach($language_data['REGEXPS'] as $rk => $rv) {
- if(!is_int($rk)) {
- report_error(TYPE_WARNING, "Language file contains an key '$rk' in \$language_data['REGEXPS'] that is not integer!");
- }
- if(is_string($rv)) {
- //Check for unmasked / in regular expressions ...
- if(empty($rv)) {
- report_error(TYPE_WARNING, "Language file contains an empty regular expression at \$language_data['REGEXPS'][$rk]!");
- } else {
- if(preg_match("/(?)/s", $rv)) {
- report_error(TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '<PIPE>' instead at \$language_data['REGEXPS'][$rk]!");
- }
- }
- } elseif(is_array($rv)) {
- if(!isset($rv[GESHI_SEARCH])) {
- report_error(TYPE_ERROR, "Language file contains no GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
- } elseif(!is_string($rv[GESHI_SEARCH])) {
- report_error(TYPE_ERROR, "Language file contains a GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
- } else {
- if(preg_match("/(?)/s", $rv[GESHI_SEARCH])) {
- report_error(TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '<PIPE>' instead at \$language_data['REGEXPS'][$rk]!");
- }
- }
- if(!isset($rv[GESHI_REPLACE])) {
- report_error(TYPE_WARNING, "Language file contains no GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
- } elseif(!is_string($rv[GESHI_REPLACE])) {
- report_error(TYPE_ERROR, "Language file contains a GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
- }
- if(!isset($rv[GESHI_MODIFIERS])) {
- report_error(TYPE_WARNING, "Language file contains no GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
- } elseif(!is_string($rv[GESHI_MODIFIERS])) {
- report_error(TYPE_ERROR, "Language file contains a GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
- }
- if(!isset($rv[GESHI_BEFORE])) {
- report_error(TYPE_WARNING, "Language file contains no GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
- } elseif(!is_string($rv[GESHI_BEFORE])) {
- report_error(TYPE_ERROR, "Language file contains a GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
- }
- if(!isset($rv[GESHI_AFTER])) {
- report_error(TYPE_WARNING, "Language file contains no GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
- } elseif(!is_string($rv[GESHI_AFTER])) {
- report_error(TYPE_ERROR, "Language file contains a GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
- }
- } else {
- report_error(TYPE_WARNING, "Language file contains an non-string and non-array entry at \$language_data['REGEXPS'][$rk]!");
- }
- if(!isset($language_data['STYLES']['REGEXPS'][$rk])) {
- report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['REGEXPS'] specification for regexp group $rk!");
- }
- }
- foreach($language_data['STYLES']['REGEXPS'] as $rk => $rv) {
- if(!isset($language_data['REGEXPS'][$rk])) {
- report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['REGEXPS'] specification for regexp key $rk!");
- }
- }
-
-
- }
-
- output_error_cache();
-
- flush();
-
- if($error_abort) {
- break;
- }
- }
-}
-
-$time_end = explode(' ', microtime());
-$time_diff = $time_end[0] + $time_end[1] - $time_start[0] - $time_start[1];
-
-if ( PHP_SAPI != 'cli' ) {
-?>
-
-
-Validation process completed in printf("%.2f", $time_diff); ?> seconds.
-
-
-
-
-
-
-
-Validation process completed in printf("%.2f", $time_diff); ?> seconds.
-
-GeSHi © 2004-2007 Nigel McNie, 2007-2012 Benny Baumann, released under the GNU GPL
-
-
\ No newline at end of file
diff --git a/extensions/SyntaxHighlight_GeSHi/geshi/contrib/langwiz.php b/extensions/SyntaxHighlight_GeSHi/geshi/contrib/langwiz.php
deleted file mode 100644
index 32d025a9..00000000
--- a/extensions/SyntaxHighlight_GeSHi/geshi/contrib/langwiz.php
+++ /dev/null
@@ -1,1158 +0,0 @@
-Failed
";
- echo "\n";
- foreach($error_cache as $error_msg) {
- echo "- ";
- switch($error_msg['t']) {
- case TYPE_NOTICE:
- echo "NOTICE:";
- break;
- case TYPE_WARNING:
- echo "WARNING:";
- break;
- case TYPE_ERROR:
- echo "ERROR:";
- break;
- }
- echo " " . $error_msg['m'] . "
";
- }
- echo "
\n";
- } else {
- echo "OK
";
- }
- echo "\n";
-
- $error_cache = array();
-}
-
-function report_error($type, $message) {
- global $error_cache, $error_abort;
-
- $error_cache[] = array('t' => $type, 'm' => $message);
- if(TYPE_ERROR == $type) {
- $error_abort = true;
- }
-}
-
-?>
-
-
-
- GeSHi Language File Generator Script
-
-
-
-GeSHi Language File Generator Script
-To use this script, make sure that geshi.php is in the
-parent directory or in your include_path, and that the language files are in a
-subdirectory of GeSHi's directory called geshi/.
-If not already done, select a language file below that will be used as
-base for the language file to generate or create a blank one. Following this
-you can do whatever you like to edit your language file. But note that not all
-features are made available through this script.
-
-Checking GeSHi installation ... 'example',
- 'name' => 'Example'
- );
-
-$ai = array(
- 'name' => 'Benny Baumann',
- 'email' => 'BenBE@geshi.org',
- 'web' => 'http://qbnz.com/highlighter/'
- );
-
-$ld = array(
- 'cmt' => array(
- 'sl' => array(
- 1 => array(
- 'start' => '//',
- 'style' => 'font-style: italic; color: #666666;'
- ),
- 2 => array(
- 'start' => '#',
- 'style' => 'font-style: italic; color: #666666;'
- )
- ),
- 'ml' => array(
- 1 => array(
- 'start' => '/*',
- 'end' => '*/',
- 'style' => 'font-style: italic; color: #666666;'
- ),
- 2 => array(
- 'start' => '/**',
- 'end' => '*/',
- 'style' => 'font-style: italic; color: #006600;'
- )
- ),
- 'rxc' => array(
- 1 => array(
- 'rx' => '/Hello RegExp/',
- 'style' => 'font-style: italic; color: #666666;'
- )
- )
- ),
- 'str' => array(
- 'qm' => array(
- 1 => array(
- 'delim' => "'",
- 'style' => 'color: #0000FF;'
- ),
- 2 => array(
- 'delim' => """,
- 'style' => 'color: #0000FF;'
- )
- ),
- 'ec' => array(
- 'char' => '\\',
- 'style' => 'font-weight: bold; color: #000080;'
- ),
- 'erx' => array(
- 1 => array(
- 'rx' => '/\{\\\\$\w+\}/',
- 'style' => 'font-weight: bold; color: #008080;'
- ),
- 2 => array(
- 'rx'=> '/\{\\\\$\w+\}/',
- 'style' => 'font-weight: bold; color: #008080;'
- )
- )
- ),
- 'kw_case' => 'GESHI_CAPS_NO_CHANGE',
- 'kw' => array(
- 1 => array(
- 'list' => '',
- 'case' => '0',
- 'style' => 'color: #0000FF; font-weight: bold;',
- 'docs' => ''
- )
- ),
- 'sy' => array(
- 0 => array(
- 'list' => '',
- 'style' => 'color: #0000FF; font-weight: bold;'
- )
- )
- );
-
-$kw_case_sel = array(
- 'GESHI_CAPS_NO_CHANGE' => '',
- 'GESHI_CAPS_UPPER' => '',
- 'GESHI_CAPS_LOWER' => ''
- );
-
-$kw_cases_sel = array(
- 1 => array(
- 0 => '',
- 1 => ''
- )
- );
-// --- empty variables for values of $_POST - end ---
-
-echo "
";
-//var_dump($languages);
-
-foreach($post_var_names as $varName) { // export wanted variables of $_POST array...
- if(array_key_exists($varName, $_POST)) {
- $$varName = htmlspecialchars_deep($_POST[$varName]);
- }
-}
-
-// determine the selected kw_case...
-$kw_case_sel[$ld['kw_case']] = ' selected="selected"';
-
-// determine the selected kw_cases...
-for($i = 1; $i <= count($kw_cases_sel); $i += 1) {
- $kw_cases_sel[$i][(int) $ld['kw'][$i]['case']] = ' selected="selected"';
-}
-
-$lang = validate_lang();
-var_dump($lang);
-echo "
";
-
-?>
-
-
-
-Operation completed in
-$time_end = explode(' ', microtime());
-$time_diff = $time_end[0] + $time_end[1] - $time_start[0] - $time_start[1];
-
-echo sprintf("%.2f", $time_diff);
-?> seconds.
-
-
-
-
-
-
-function str_to_phpstring($str, $doublequote = false){
- if($doublequote) {
- return '"' . strtr($str,
- array(
- "\"" => "\\\"",
- "\\" => "\\\\",
- "\0" => "\\0",
- "\n" => "\\n",
- "\r" => "\\r",
- "\t" => "\\t",
- "\$" => "\\\$"
- )
- ) . '"';
- } else {
- return "'" . strtr($str,
- array(
- "'" => "\\'",
- "\\" => "\\\\"
- )
- ) . "'";
- }
-}
-
-function validate_lang(){
- $ai = array(
- 'name' => 'Benny Baumann',
- 'email' => 'BenBE@geshi.org',
- 'web' => 'http://qbnz.com/highlighter/'
- );
-
- $li = array(
- 'file' => 'example',
- 'desc' => 'Example'
- );
-
- if(isset($_POST['ld'])) {
- $ld = $_POST['ld'];
- } else {
- $ld = array(
- 'cmt' => array(
- 'sl' => array(
- 1 => array(
- 'start' => '//',
- 'style' => 'test'
- )
- ),
- 'ml' => array(
- 1 => array(
- 'start' => '/*',
- 'end' => '*/',
- 'style' => 'font-style: italic; color: #666666;'
- )
- ),
- 'rxc' => array(
- 1 => array(
- 'rx' => '/Hello/',
- 'style' => 'color: #00000'
- )
- )
- ),
- 'str' => array(
- 'qm' => array(),
- 'ec' => array(
- 'char' => ''
- ),
- 'erx' => array()
- ),
- 'kw' => array(),
- 'kw_case' => 'GESHI_CAPS_NO_CHANGE',
- 'sy' => array()
- );
- }
-
- return array('ai' => $ai, 'li' => $li, 'ld' => $ld);
-}
-
-function gen_langfile($lang){
- $langfile = $lang['li']['file'];
- $langdesc = $lang['li']['desc'];
-
- $langauthor_name = $lang['ai']['name'];
- $langauthor_email = $lang['ai']['email'];
- $langauthor_web = $lang['ai']['web'];
-
- $langversion = GESHI_VERSION;
-
- $langdate = date('Y/m/d');
- $langyear = date('Y');
-
- $i = ' ';
- $i = array('', $i, $i.$i, $i.$i.$i);
-
- $src = << ".str_to_phpstring($langdesc).",\n";
-
- //Comments
- $src .= $i[1] . "'COMMENT_SINGLE' => array(\n";
- foreach($lang['ld']['cmt']['sl'] as $idx_cmt_sl => $tmp_cmt_sl) {
- $src .= $i[2] . ((int)$idx_cmt_sl). " => ". str_to_phpstring($tmp_cmt_sl['start']) . ",\n";
- }
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'COMMENT_MULTI' => array(\n";
- foreach($lang['ld']['cmt']['ml'] as $tmp_cmt_ml) {
- $src .= $i[2] . str_to_phpstring($tmp_cmt_ml['start']). " => ". str_to_phpstring($tmp_cmt_ml['end']) . ",\n";
- }
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'COMMENT_REGEXP' => array(\n";
- foreach($lang['ld']['cmt']['rxc'] as $idx_cmt_rxc => $tmp_cmt_rxc) {
- $src .= $i[2] . ((int)$idx_cmt_rxc). " => ". str_to_phpstring($tmp_cmt_rxc['rx']) . ",\n";
- }
- $src .= $i[2] . "),\n";
-
- //Case Keywords
- $src .= $i[1] . "'CASE_KEYWORDS' => " . $lang['ld']['kw_case'] . ",\n";
-
- //Quotes \ Strings
- $src .= $i[1] . "'QUOTEMARKS' => array(\n";
- foreach($lang['ld']['str']['qm'] as $idx_str_qm => $tmp_str_qm) {
- $src .= $i[2] . ((int)$idx_str_qm). " => ". str_to_phpstring($tmp_str_qm['delim']) . ",\n";
- }
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'ESCAPE_CHAR' => " . str_to_phpstring($lang['ld']['str']['ec']['char']) . ",\n";
- $src .= $i[1] . "'ESCAPE_REGEXP' => array(\n";
- foreach($lang['ld']['str']['erx'] as $idx_str_erx => $tmp_str_erx) {
- $src .= $i[2] . ((int)$idx_str_erx). " => ". str_to_phpstring($tmp_str_erx['rx']) . ",\n";
- }
- $src .= $i[2] . "),\n";
-
- //HardQuotes
- $src .= $i[1] . "'HARDQUOTE' => array(\n";
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'HARDESCAPE' => array(\n";
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'HARDCHAR' => '',\n";
-
- //Numbers
- $src .= $i[1] . "'NUMBERS' =>\n";
- $src .= $i[2] . "GESHI_NUMBER_INT_BASIC | GESHI_NUMBER_OCT_PREFIX | GESHI_NUMBER_HEX_PREFIX |\n";
- $src .= $i[2] . "GESHI_NUMBER_FLT_SCI_ZERO,\n";
-
- //Keywords
- $src .= $i[1] . "'KEYWRODS' => array(\n";
- foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
- $src .= $i[2] . ((int)$idx_kw) . " => array(\n";
- if(!is_array($tmp_kw['list'])) {
- $tmp_kw['list'] = explode("\n", $tmp_kw['list']);
- }
- $tmp_kw['list'] = array_map('trim', $tmp_kw['list']);
- sort($tmp_kw['list']);
- $kw_esc = array_map('str_to_phpstring', $tmp_kw['list']);
- $kw_nl = true;
- $kw_pos = 0;
- foreach($kw_esc as $kw_data) {
- if((strlen($kw_data) + $kw_pos > 79) && $kw_pos > strlen($i[3])) {
- $src .= "\n";
- $kw_nl = true;
- $kw_pos = 0;
- }
- if($kw_nl) {
- $src .= $i[3];
- $kw_pos += strlen($i[3]);
- $kw_nl = false;
- }
- $src .= $kw_data . ', ';
- $kw_pos += strlen($kw_data) + 2;
- }
- $src .= "\n";
- $src .= $i[3] . "),\n";
- }
- $src .= $i[2] . "),\n";
-
- //Case Sensitivity
- $src .= $i[1] . "'CASE_SENSITIVE' => array(\n";
- foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
- $src .= $i[2] . ((int)$idx_kw) . " => " . ($tmp_kw['case'] ? 'true' : 'false') . ",\n";
- }
- $src .= $i[2] . "),\n";
-
- //Symbols
- $src .= $i[1] . "'SYMBOLS' => array(\n";
- foreach($lang['ld']['sy'] as $idx_kw => $tmp_kw) {
- $src .= $i[2] . ((int)$idx_kw) . " => array(\n";
- $tmp_kw['list'] = (array)$tmp_kw['list'];
- sort($tmp_kw['list']);
- $kw_esc = array_map('str_to_phpstring', $tmp_kw['list']);
- $kw_nl = true;
- $kw_pos = strlen($i[3]);
- foreach($kw_esc as $kw_data) {
- if((strlen($kw_data) + $kw_pos > 79) && $kw_pos > strlen($i[3])) {
- $src .= "\n";
- $kw_nl = true;
- $kw_pos = 0;
- }
- if($kw_nl) {
- $src .= $i[3];
- $kw_pos += strlen($i[3]);
- $kw_nl = false;
- }
- $src .= $kw_data . ', ';
- $kw_pos += strlen($kw_data) + 2;
- }
- $src .= "\n";
- $src .= $i[3] . "),\n";
- }
- $src .= $i[2] . "),\n";
-
- //Styles \ CSS
- $src .= $i[1] . "'STYLES' => array(\n";
- $src .= $i[2] . "'KEYWRODS' => array(\n";
- foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
- $src .= $i[3] . ((int)$idx_kw) . " => " . str_to_phpstring($tmp_kw['style']) . ",\n";
- }
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'COMMENTS' => array(\n";
- foreach($lang['ld']['cmt']['sl'] as $idx_cmt_sl => $tmp_cmt_sl) {
- $src .= $i[3] . ((int)$idx_cmt_sl) . " => " . str_to_phpstring($tmp_cmt_sl['style']) . ",\n";
- }
- foreach($lang['ld']['cmt']['rxc'] as $idx_cmt_rxc => $tmp_cmt_rxc) {
- $src .= $i[3] . ((int)$idx_cmt_rxc) . " => " . str_to_phpstring($tmp_cmt_rxc['style']) . ",\n";
- }
- $src .= $i[3] . "'MULTI' => " . str_to_phpstring($lang['ld']['cmt']['ml'][1]['style']) . "\n";
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'ESCAPE_CHAR' => array(\n";
- foreach($lang['ld']['str']['erx'] as $idx_str_erx => $tmp_str_erx) {
- $src .= $i[3] . ((int)$idx_str_erx). " => ". str_to_phpstring($tmp_str_erx['style']) . ",\n";
- }
- // 'HARD' => 'color: #000099; font-weight: bold;'
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'BRACKETS' => array(\n";
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'STRINGS' => array(\n";
- foreach($lang['ld']['str']['qm'] as $idx_str_qm => $tmp_str_qm) {
- $src .= $i[3] . ((int)$idx_str_qm). " => ". str_to_phpstring($tmp_str_qm['style']) . ",\n";
- }
- // 'HARD' => 'color: #0000ff;'
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'NUMBERS' => array(\n";
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'METHODS' => array(\n";
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'SYMBOLS' => array(\n";
- foreach($lang['ld']['sy'] as $idx_kw => $tmp_kw) {
- $src .= $i[3] . ((int)$idx_kw) . " => " . str_to_phpstring($tmp_kw['style']) . ",\n";
- }
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'REGEXPS' => array(\n";
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "'SCRIPT' => array(\n";
- $src .= $i[3] . "),\n";
- $src .= $i[2] . "),\n";
-
- //Keyword Documentation
- $src .= $i[1] . "'URLS' => array(\n";
- foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
- $src .= $i[2] . ((int)$idx_kw) . " => " . str_to_phpstring($tmp_kw['docs']) . ",\n";
- }
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'OOLANG' => false,\n";
- $src .= $i[1] . "'OBJECT_SPLITTERS' => array(\n";
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'REGEXPS' => array(\n";
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'STRICT_MODE_APPLIES' => GESHI_MAYBE,\n";
- $src .= $i[1] . "'SCRIPT_DELIMITERS' => array(\n";
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'HIGHLIGHT_STRICT_BLOCK' => array(\n";
- $src .= $i[2] . "),\n";
- $src .= $i[1] . "'TAB_WIDTH' => 4,\n";
-
- $src .= <<
-GESHI_LANGFILE_FOOTER;
-
- //Reduce source ...
- $src = preg_replace('/array\(\s*\)/s', 'array()', $src);
- $src = preg_replace('/\,(\s*\))/s', '\1', $src);
- $src = preg_replace('/\s+$/m', '', $src);
-
- return $src;
-}
-
-// vim: shiftwidth=4 softtabstop=4
-?>
diff --git a/extensions/TitleBlacklist/.gitreview b/extensions/TitleBlacklist/.gitreview
new file mode 100644
index 00000000..964229c3
--- /dev/null
+++ b/extensions/TitleBlacklist/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.wikimedia.org
+port=29418
+project=mediawiki/extensions/TitleBlacklist.git
+defaultbranch=master
diff --git a/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php b/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php
new file mode 100644
index 00000000..1f8164e7
--- /dev/null
+++ b/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php
@@ -0,0 +1,110 @@
+
+ */
+
+ini_set( 'include_path', ini_get( 'include_path' ) . ':' . __DIR__ . '/../../../tests/phpunit/includes/api' );
+
+/**
+ * @group medium
+ **/
+class ApiQueryTitleBlacklistTest extends ApiTestCase {
+
+ function setUp() {
+ global $wgTitleBlacklistSources;
+ parent::setUp();
+ $this->doLogin();
+
+ $wgTitleBlacklistSources = array(
+ array(
+ 'type' => TBLSRC_FILE,
+ 'src' => __DIR__ . '/testSource',
+ ),
+ );
+ }
+
+ /**
+ * Verify we allow a title which is not blacklisted
+ */
+ function testCheckingUnlistedTitle() {
+ $unlisted = $this->doApiRequest( array(
+ 'action' => 'titleblacklist',
+ // evil_acc is blacklisted as
+ 'tbtitle' => 'evil_acc',
+ 'tbaction' => 'create',
+ 'tbnooverride' => true,
+ ) );
+
+ $this->assertEquals(
+ 'ok',
+ $unlisted[0]['titleblacklist']['result'],
+ 'Not blacklisted title returns ok'
+ );
+ }
+
+ /**
+ * Verify tboverride works
+ */
+ function testTboverride() {
+ global $wgGroupPermissions;
+
+ // Allow all users to override the titleblacklist
+ $wgGroupPermissions['*']['tboverride'] = true;
+
+ $unlisted = $this->doApiRequest( array(
+ 'action' => 'titleblacklist',
+ 'tbtitle' => 'bar',
+ 'tbaction' => 'create',
+ ) );
+
+ $this->assertEquals(
+ 'ok',
+ $unlisted[0]['titleblacklist']['result'],
+ 'Blacklisted title returns ok if the user is allowd to tboverride'
+ );
+ }
+
+ /**
+ * Verify a blacklisted title gives out an error.
+ */
+ function testCheckingBlackListedTitle() {
+ $listed = $this->doApiRequest( array(
+ 'action' => 'titleblacklist',
+ 'tbtitle' => 'bar',
+ 'tbaction' => 'create',
+ 'tbnooverride' => true,
+ ) );
+
+ $this->assertEquals(
+ 'blacklisted',
+ $listed[0]['titleblacklist']['result'],
+ 'Listed title returns error'
+ );
+ $this->assertEquals(
+ "The title \"bar\" has been banned from creation.\nIt matches the following blacklist entry: [Bb]ar #example blacklist entry
",
+ $listed[0]['titleblacklist']['reason'],
+ 'Listed title error text is as expected'
+ );
+
+ $this->assertEquals(
+ "titleblacklist-forbidden-edit",
+ $listed[0]['titleblacklist']['message'],
+ 'Correct blacklist message name is returned'
+ );
+
+ $this->assertEquals(
+ "[Bb]ar #example blacklist entry",
+ $listed[0]['titleblacklist']['line'],
+ 'Correct blacklist line is returned'
+ );
+
+ }
+}
diff --git a/extensions/TitleBlacklist/tests/testSource b/extensions/TitleBlacklist/tests/testSource
new file mode 100644
index 00000000..f73d9dd7
--- /dev/null
+++ b/extensions/TitleBlacklist/tests/testSource
@@ -0,0 +1,4 @@
+[Bb]ar #example blacklist entry
+.*[Ff]ail.*
+.*[Nn]yancat.*
+.*evil_acc.*
diff --git a/extensions/Vector/.gitreview b/extensions/Vector/.gitreview
new file mode 100644
index 00000000..8d06d24a
--- /dev/null
+++ b/extensions/Vector/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.wikimedia.org
+port=29418
+project=mediawiki/extensions/Vector.git
+defaultbranch=master
diff --git a/extensions/WikiEditor/.gitreview b/extensions/WikiEditor/.gitreview
new file mode 100644
index 00000000..622413f0
--- /dev/null
+++ b/extensions/WikiEditor/.gitreview
@@ -0,0 +1,6 @@
+[gerrit]
+host=gerrit.wikimedia.org
+port=29418
+project=mediawiki/extensions/WikiEditor.git
+defaultbranch=master
+defaultrebase=0
diff --git a/extensions/WikiEditor/.jshintignore b/extensions/WikiEditor/.jshintignore
new file mode 100644
index 00000000..66a218b5
--- /dev/null
+++ b/extensions/WikiEditor/.jshintignore
@@ -0,0 +1,2 @@
+# upstream lib from Google
+modules/contentCollector.js
diff --git a/extensions/WikiEditor/.jshintrc b/extensions/WikiEditor/.jshintrc
new file mode 100644
index 00000000..64cd5087
--- /dev/null
+++ b/extensions/WikiEditor/.jshintrc
@@ -0,0 +1,9 @@
+{
+ "predef": [
+ "mediaWiki",
+ "jQuery"
+ ],
+ "browser": true,
+ "smarttabs": true,
+ "multistr": true
+}
diff --git a/extensions/WikiEditor/tests/selenium/WikiDialogs_Links.php b/extensions/WikiEditor/tests/selenium/WikiDialogs_Links.php
new file mode 100644
index 00000000..7153f49f
--- /dev/null
+++ b/extensions/WikiEditor/tests/selenium/WikiDialogs_Links.php
@@ -0,0 +1,67 @@
+createNewPage();
+ parent::verifyInternalLink();
+ }
+
+ // Add a internal link with different display text and verify
+ function testInternalLinkWithDisplayText() {
+ $this->createNewPage();
+ parent::verifyInternalLinkWithDisplayText();
+ }
+
+ // Add a internal link with blank display text and verify
+ function testInternalLinkWithBlankDisplayText() {
+ $this->createNewPage();
+ parent::verifyInternalLinkWithBlankDisplayText();
+ }
+
+ // Add external link and verify
+ function testExternalLink() {
+ $this->createNewPage();
+ parent::verifyExternalLink();
+ }
+
+ // Add external link with different display text and verify
+ function testExternalLinkWithDisplayText( ) {
+ $this->createNewPage();
+ parent::verifyExternalLinkWithDisplayText();
+ }
+
+ // Add external link with Blank display text and verify
+ function testExternalLinkWithBlankDisplayText() {
+ $this->createNewPage();
+ parent::verifyExternalLinkWithBlankDisplayText();
+ }
+
+}
diff --git a/extensions/WikiEditor/tests/selenium/WikiDialogs_Links_Setup.php b/extensions/WikiEditor/tests/selenium/WikiDialogs_Links_Setup.php
new file mode 100644
index 00000000..352ebec0
--- /dev/null
+++ b/extensions/WikiEditor/tests/selenium/WikiDialogs_Links_Setup.php
@@ -0,0 +1,295 @@
+open( $this->getUrl() . '/index.php' );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ }
+
+ // Expand advance tool bar section if its not
+ function doExpandAdvanceSection() {
+ if ( !$this->isTextPresent( TEXT_HEADING ) ) {
+ $this->click( LINK_ADVANCED );
+ }
+ }
+
+ // Log out from the application
+ function doLogout() {
+ $this->open( $this->getUrl() . '/index.php' );
+ if ( $this->isTextPresent( TEXT_LOGOUT ) ) {
+ $this->click( LINK_LOGOUT );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( TEXT_LOGOUT_CONFIRM, $this->getText( LINK_LOGIN ) );
+ $this->open( $this->getUrl() . '/index.php' );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ }
+ }
+
+ // Create a temporary fixture page
+ function doCreateInternalTestPageIfMissing() {
+ $this->type( INPUT_SEARCH_BOX, WIKI_INTERNAL_LINK );
+ $this->click( BUTTON_SEARCH );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->click( LINK_START . WIKI_INTERNAL_LINK );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $location = $this->getLocation() . "\n";
+ if ( strpos( $location, '&redlink=1' ) !== false ) {
+ $this->type( TEXT_EDITOR, "Test fixture page. No real content here" );
+ $this->click( BUTTON_SAVE_WATCH );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isTextPresent( WIKI_INTERNAL_LINK ),
+ $this->getText( TEXT_PAGE_HEADING ) );
+ }
+ }
+
+ // Create a temporary new page
+ function doCreateNewPageTemporary() {
+ $this->type( INPUT_SEARCH_BOX, WIKI_TEMP_NEWPAGE );
+ $this->click( BUTTON_SEARCH );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->click( LINK_START . WIKI_TEMP_NEWPAGE );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ }
+
+ // Add a internal link and verify
+ function verifyInternalLink() {
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDLINK );
+ $this->waitForPopup( 'addLink', WIKI_TEST_WAIT_TIME );
+ $this->type( TEXT_LINKNAME, ( WIKI_INTERNAL_LINK ) );
+ $this->assertTrue( $this->isElementPresent( ICON_PAGEEXISTS ), 'Element ' . ICON_PAGEEXISTS . 'Not found' );
+ $this->assertEquals( "on", $this->getValue( OPT_INTERNAL ) );
+ $this->click( BUTTON_INSERTLINK );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( ( WIKI_INTERNAL_LINK ), $this->getText( LINK_START . WIKI_INTERNAL_LINK ) );
+ $this->click( LINK_START . WIKI_INTERNAL_LINK );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isTextPresent( WIKI_INTERNAL_LINK ), $this->getText( TEXT_PAGE_HEADING ) );
+ }
+
+ // Add a internal link with different display text and verify
+ function verifyInternalLinkWithDisplayText() {
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDLINK );
+ $this->waitForPopup( 'addLink', WIKI_TEST_WAIT_TIME );
+ $this->type( TEXT_LINKNAME, WIKI_INTERNAL_LINK );
+ $this->type ( TEXT_LINKDISPLAYNAME, WIKI_INTERNAL_LINK . TEXT_LINKDISPLAYNAME_APPENDTEXT );
+ $this->assertTrue( $this->isElementPresent( ICON_PAGEEXISTS ) );
+ $this->assertEquals( "on", $this->getValue( OPT_INTERNAL ) );
+ $this->click( BUTTON_INSERTLINK );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_INTERNAL_LINK . TEXT_LINKDISPLAYNAME_APPENDTEXT,
+ $this->getText( LINK_START . WIKI_INTERNAL_LINK . TEXT_LINKDISPLAYNAME_APPENDTEXT ) );
+ $this->click( LINK_START . WIKI_INTERNAL_LINK . TEXT_LINKDISPLAYNAME_APPENDTEXT );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isTextPresent( WIKI_INTERNAL_LINK ), $this->getText( TEXT_PAGE_HEADING ) );
+
+ }
+
+ // Add a internal link with blank display text and verify
+ function verifyInternalLinkWithBlankDisplayText() {
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDLINK );
+ $this->waitForPopup( 'addLink', WIKI_TEST_WAIT_TIME );
+ $this->type( TEXT_LINKNAME, WIKI_INTERNAL_LINK );
+ $this->type( TEXT_LINKDISPLAYNAME, "" );
+ $this->assertTrue( $this->isElementPresent( ICON_PAGEEXISTS ) );
+ $this->assertEquals( "on", $this->getValue( OPT_INTERNAL ) );
+ $this->click( BUTTON_INSERTLINK );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_INTERNAL_LINK, $this->getText( LINK_START . WIKI_INTERNAL_LINK ) );
+ $this->click( LINK_START . WIKI_INTERNAL_LINK );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_INTERNAL_LINK, $this->getText( TEXT_PAGE_HEADING ) );
+
+ }
+
+ // Add external link and verify
+ function verifyExternalLink() {
+ $this->type( LINK_PREVIEW, "" );
+ $this->click( LINK_ADDLINK );
+ $this->type( TEXT_LINKNAME, WIKI_EXTERNAL_LINK );
+ $this->assertTrue( $this->isElementPresent( ICON_PAGEEXTERNAL ) );
+ $this->assertEquals( "on", $this->getValue( OPT_EXTERNAL ) );
+ $this->click( BUTTON_INSERTLINK );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_EXTERNAL_LINK, $this->getText( LINK_START . WIKI_EXTERNAL_LINK ) );
+
+ $this->click( LINK_START . WIKI_EXTERNAL_LINK );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_EXTERNAL_LINK_TITLE, $this->getTitle() );
+ }
+
+ // Add external link with different display text and verify
+ function verifyExternalLinkWithDisplayText() {
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDLINK );
+ $this->type( TEXT_LINKNAME, WIKI_EXTERNAL_LINK );
+ $this->type( TEXT_LINKDISPLAYNAME, WIKI_EXTERNAL_LINK_TITLE );
+ $this->assertTrue( $this->isElementPresent( ICON_PAGEEXTERNAL ) );
+ $this->assertEquals( "on", $this->getValue( OPT_EXTERNAL ) );
+ $this->click( BUTTON_INSERTLINK );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_EXTERNAL_LINK_TITLE, $this->getText( LINK_START . WIKI_EXTERNAL_LINK_TITLE ) );
+ $this->click( LINK_START . ( WIKI_EXTERNAL_LINK_TITLE ) );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_EXTERNAL_LINK_TITLE , $this->getTitle() );
+ }
+
+ // Add external link with Blank display text and verify
+ function verifyExternalLinkWithBlankDisplayText() {
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDLINK );
+ $this->type( TEXT_LINKNAME, WIKI_EXTERNAL_LINK );
+ $this->type( TEXT_LINKDISPLAYNAME, "" );
+ $this->assertTrue( $this->isElementPresent( ICON_PAGEEXTERNAL ) );
+ $this->assertEquals( "on", $this->getValue( OPT_EXTERNAL ) );
+ $this->click( BUTTON_INSERTLINK );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "[1]", $this->getText( LINK_START . "[1]" ) );
+ $this->click( LINK_START . "[1]" );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( WIKI_EXTERNAL_LINK_TITLE, $this->getTitle() );
+ }
+
+ // Add a table and verify
+ function verifyCreateTable() {
+ $WIKI_TABLE_ROW = 2;
+ $WIKI_TABLE_COL = "5";
+ $this->doExpandAdvanceSection();
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDTABLE );
+ $this->click( CHK_SORT );
+ $this->type( TEXT_ROW, $WIKI_TABLE_ROW );
+ $this->type( TEXT_COL, $WIKI_TABLE_COL );
+ $this->click( BUTTON_INSERTABLE );
+ $this->click( CHK_SORT );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $WIKI_TABLE_ROW = $WIKI_TABLE_ROW + 1;
+ $this->assertTrue( $this->isElementPresent( TEXT_TABLEID_OTHER .
+ TEXT_VALIDATE_TABLE_PART1 . $WIKI_TABLE_ROW .
+ TEXT_VALIDATE_TABLE_PART2 . $WIKI_TABLE_COL .
+ TEXT_VALIDATE_TABLE_PART3 ) );
+ }
+
+ // Add a table and verify only with head row
+ function verifyCreateTableWithHeadRow() {
+ $WIKI_TABLE_ROW = 3;
+ $WIKI_TABLE_COL = "4";
+ $this->doExpandAdvanceSection();
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDTABLE );
+ $this->click( CHK_BOARDER );
+ $this->type( TEXT_ROW, $WIKI_TABLE_ROW );
+ $this->type( TEXT_COL, $WIKI_TABLE_COL );
+ $this->click( BUTTON_INSERTABLE );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $WIKI_TABLE_ROW = $WIKI_TABLE_ROW + 1;
+ $this->assertTrue( $this->isElementPresent( TEXT_TABLEID_OTHER .
+ TEXT_VALIDATE_TABLE_PART1 . $WIKI_TABLE_ROW .
+ TEXT_VALIDATE_TABLE_PART2 . $WIKI_TABLE_COL .
+ TEXT_VALIDATE_TABLE_PART3 ) );
+ }
+
+ // Add a table and verify only with borders
+ function verifyCreateTableWithBorders() {
+ $WIKI_TABLE_ROW = "4";
+ $WIKI_TABLE_COL = "6";
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDTABLE );
+ $this->click( CHK_HEADER );
+ $this->type( TEXT_ROW, $WIKI_TABLE_ROW );
+ $this->type( TEXT_COL, $WIKI_TABLE_COL );
+ $this->click( BUTTON_INSERTABLE );
+ $this->click( CHK_HEADER );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isElementPresent( TEXT_TABLEID_OTHER .
+ TEXT_VALIDATE_TABLE_PART1 . $WIKI_TABLE_ROW .
+ TEXT_VALIDATE_TABLE_PART2 . $WIKI_TABLE_COL .
+ TEXT_VALIDATE_TABLE_PART3 ) );
+ }
+
+ // Add a table and verify only with sort row
+ function verifyCreateTableWithSortRow() {
+ $WIKI_TABLE_ROW = "2";
+ $WIKI_TABLE_COL = "5";
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDTABLE );
+ $this->click( CHK_HEADER );
+ $this->click( CHK_BOARDER );
+ $this->click( CHK_SORT );
+ $this->type( TEXT_ROW, $WIKI_TABLE_ROW );
+ $this->type( TEXT_COL, $WIKI_TABLE_COL );
+ $this->click( BUTTON_INSERTABLE );
+ $this->click( CHK_HEADER );
+ $this->click( CHK_BOARDER );
+ $this->click( CHK_SORT );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isElementPresent( TEXT_TABLEID_WITHALLFEATURES .
+ TEXT_VALIDATE_TABLE_PART1 . $WIKI_TABLE_ROW .
+ TEXT_VALIDATE_TABLE_PART2 . $WIKI_TABLE_COL .
+ TEXT_VALIDATE_TABLE_PART3 ) );
+ }
+
+ // Add a table without headers,borders and sort rows
+ function verifyCreateTableWithNoSpecialEffects() {
+ $WIKI_TABLE_ROW = "6";
+ $WIKI_TABLE_COL = "2";
+ $this->
+ $this->doExpandAdvanceSection();
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDTABLE );
+ $this->click( CHK_BOARDER );
+ $this->click( CHK_HEADER );
+ $this->type( TEXT_ROW, $WIKI_TABLE_ROW );
+ $this->type( TEXT_COL, $WIKI_TABLE_COL );
+ $this->click( BUTTON_INSERTABLE );
+ $this->click( CHK_BOARDER );
+ $this->click( CHK_HEADER );
+ $this->click( INK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isElementPresent( TEXT_TABLEID_OTHER .
+ TEXT_VALIDATE_TABLE_PART1 . $WIKI_TABLE_ROW .
+ TEXT_VALIDATE_TABLE_PART2 . $WIKI_TABLE_COL .
+ TEXT_VALIDATE_TABLE_PART3 ) );
+ }
+
+ // Add a table with headers,borders and sort rows
+ function verifyCreateTableWithAllSpecialEffects() {
+ $WIKI_TABLE_ROW = 6;
+ $WIKI_TABLE_COL = "2";
+ $this->doExpandAdvanceSection();
+ $this->type( TEXT_EDITOR, "" );
+ $this->click( LINK_ADDTABLE );
+ $this->click( CHK_SORT );
+ $this->type( TEXT_ROW, $WIKI_TABLE_ROW );
+ $this->type( TEXT_COL, $WIKI_TABLE_COL );
+ $this->click( BUTTON_INSERTABLE );
+ $this->click( CHK_SORT );
+ $this->click( LINK_PREVIEW );
+ $this->waitForPageToLoad( WIKI_TEST_WAIT_TIME );
+ $WIKI_TABLE_ROW = $WIKI_TABLE_ROW + 1;
+ $this->assertTrue( $this->isElementPresent( TEXT_TABLEID_WITHALLFEATURES .
+ TEXT_VALIDATE_TABLE_PART1 . $WIKI_TABLE_ROW .
+ TEXT_VALIDATE_TABLE_PART2 . $WIKI_TABLE_COL .
+ TEXT_VALIDATE_TABLE_PART3 ) );
+ }
+
+}
diff --git a/extensions/WikiEditor/tests/selenium/WikiEditorConstants.php b/extensions/WikiEditor/tests/selenium/WikiEditorConstants.php
new file mode 100644
index 00000000..090f96bf
--- /dev/null
+++ b/extensions/WikiEditor/tests/selenium/WikiEditorConstants.php
@@ -0,0 +1,84 @@
+ 'vector',
+ 'wgWikiEditorFeatures' => array(
+ 'toolbar' => array( 'global' => true, 'user' => true ),
+ 'toc' => array( 'global' => false, 'user' => false ),
+ 'highlight' => array( 'global' => false, 'user' => false ),
+ 'templateEditor' => array( 'global' => false, 'user' => false ),
+ 'dialogs' => array( 'global' => true, 'user' => true )
+ ),
+ 'wgVectorFeatures' => array(
+ 'editwarning' => array( 'global' => false, 'user' => false )
+ )
+ );
+ $includeFiles = array_merge( $includeFiles, $includes );
+ $globalConfigs = array_merge( $globalConfigs, $configs );
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/extensions/WikiEditor/tests/selenium/WikiEditorTestSuite.php b/extensions/WikiEditor/tests/selenium/WikiEditorTestSuite.php
new file mode 100644
index 00000000..b4029d3a
--- /dev/null
+++ b/extensions/WikiEditor/tests/selenium/WikiEditorTestSuite.php
@@ -0,0 +1,35 @@
+ false, 'user' => false );
+ * $wgWikiEditorFeatures['templateEditor'] = array( 'global' => false, 'user' => false );
+ * $wgWikiEditorFeatures['toolbar'] = array( 'global' => true, 'user' => true );
+ * $wgWikiEditorFeatures['toc'] = array( 'global' => false, 'user' => false );
+ * $wgWikiEditorFeatures['highlight'] = array( 'global' => false, 'user' => false );
+ * $wgWikiEditorFeatures['dialogs'] = array( 'global' => true, 'user' => true );
+ *
+ */
+class WikiEditorTestSuite extends SeleniumTestSuite
+{
+ public function setUp() {
+ $this->setLoginBeforeTests( false );
+ parent::setUp();
+ }
+ public function addTests() {
+ $testFiles = array(
+ 'extensions/WikiEditor/tests/selenium/WikiDialogs_Links.php'
+ );
+ parent::addTestFiles( $testFiles );
+ }
+
+
+}
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 9d024c86..0e493eb0 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -63,7 +63,7 @@ $wgConf = new SiteConfiguration;
* MediaWiki version number
* @since 1.2
*/
-$wgVersion = '1.21.1';
+$wgVersion = '1.21.2';
/**
* Name of the site. It must be changed in LocalSettings.php
diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php
index 90432b95..6f3d1e4f 100644
--- a/includes/api/ApiBlock.php
+++ b/includes/api/ApiBlock.php
@@ -42,12 +42,6 @@ class ApiBlock extends ApiBase {
$user = $this->getUser();
$params = $this->extractRequestParams();
- if ( $params['gettoken'] ) {
- $res['blocktoken'] = $user->getEditToken();
- $this->getResult()->addValue( null, $this->getModuleName(), $res );
- return;
- }
-
if ( !$user->isAllowed( 'block' ) ) {
$this->dieUsageMsg( 'cantblock' );
}
@@ -156,10 +150,6 @@ class ApiBlock extends ApiBase {
ApiBase::PARAM_REQUIRED => true
),
'token' => null,
- 'gettoken' => array(
- ApiBase::PARAM_DFLT => false,
- ApiBase::PARAM_DEPRECATED => true,
- ),
'expiry' => 'never',
'reason' => '',
'anononly' => false,
@@ -177,7 +167,6 @@ class ApiBlock extends ApiBase {
return array(
'user' => 'Username, IP address or IP range you want to block',
'token' => 'A block token previously obtained through prop=info',
- 'gettoken' => 'If set, a block token will be returned, and no other action will be taken',
'expiry' => 'Relative expiry time, e.g. \'5 months\' or \'2 weeks\'. If set to \'infinite\', \'indefinite\' or \'never\', the block will never expire.',
'reason' => 'Reason for block',
'anononly' => 'Block anonymous users only (i.e. disable anonymous edits for this IP)',
@@ -194,10 +183,6 @@ class ApiBlock extends ApiBase {
public function getResultProperties() {
return array(
'' => array(
- 'blocktoken' => array(
- ApiBase::PROP_TYPE => 'string',
- ApiBase::PROP_NULLABLE => true
- ),
'user' => array(
ApiBase::PROP_TYPE => 'string',
ApiBase::PROP_NULLABLE => true
diff --git a/includes/api/ApiCreateAccount.php b/includes/api/ApiCreateAccount.php
index 55c60cce..69748c93 100644
--- a/includes/api/ApiCreateAccount.php
+++ b/includes/api/ApiCreateAccount.php
@@ -29,6 +29,10 @@
*/
class ApiCreateAccount extends ApiBase {
public function execute() {
+ // If we're in JSON callback mode, no tokens can be obtained
+ if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+ $this->dieUsage( 'Cannot create account when using a callback', 'aborted' );
+ }
// $loginForm->addNewaccountInternal will throw exceptions
// if wiki is read only (already handled by api), user is blocked or does not have rights.
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index b936d3be..b51d441d 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -46,6 +46,15 @@ class ApiLogin extends ApiBase {
* is reached. The expiry is $this->mLoginThrottle.
*/
public function execute() {
+ // If we're in JSON callback mode, no tokens can be obtained
+ if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+ $this->getResult()->addValue( null, 'login', array(
+ 'result' => 'Aborted',
+ 'reason' => 'Cannot log in when using a callback',
+ ) );
+ return;
+ }
+
$params = $this->extractRequestParams();
$result = array();
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 80bca2f6..7b2fd914 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -714,15 +714,9 @@ class ApiMain extends ApiBase {
}
$moduleParams = $module->extractRequestParams();
- // Die if token required, but not provided (unless there is a gettoken parameter)
- if ( isset( $moduleParams['gettoken'] ) ) {
- $gettoken = $moduleParams['gettoken'];
- } else {
- $gettoken = false;
- }
-
+ // Die if token required, but not provided
$salt = $module->getTokenSalt();
- if ( $salt !== false && !$gettoken ) {
+ if ( $salt !== false ) {
if ( !isset( $moduleParams['token'] ) ) {
$this->dieUsageMsg( array( 'missingparam', 'token' ) );
} else {
diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php
index 31ca1ef5..890e4ecf 100644
--- a/includes/api/ApiQueryDeletedrevs.php
+++ b/includes/api/ApiQueryDeletedrevs.php
@@ -57,6 +57,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$fld_content = isset( $prop['content'] );
$fld_token = isset( $prop['token'] );
+ // If we're in JSON callback mode, no tokens can be obtained
+ if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+ $fld_token = false;
+ }
+
$result = $this->getResult();
$pageSet = $this->getPageSet();
$titles = $pageSet->getTitles();
diff --git a/includes/api/ApiTokens.php b/includes/api/ApiTokens.php
index 7080f547..d220a5e6 100644
--- a/includes/api/ApiTokens.php
+++ b/includes/api/ApiTokens.php
@@ -48,6 +48,11 @@ class ApiTokens extends ApiBase {
}
private function getTokenTypes() {
+ // If we're in JSON callback mode, no tokens can be obtained
+ if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
+ return array();
+ }
+
static $types = null;
if ( $types ) {
return $types;
diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php
index 55e7331d..6a739a2f 100644
--- a/includes/api/ApiUnblock.php
+++ b/includes/api/ApiUnblock.php
@@ -39,12 +39,6 @@ class ApiUnblock extends ApiBase {
$user = $this->getUser();
$params = $this->extractRequestParams();
- if ( $params['gettoken'] ) {
- $res['unblocktoken'] = $user->getEditToken();
- $this->getResult()->addValue( null, $this->getModuleName(), $res );
- return;
- }
-
if ( is_null( $params['id'] ) && is_null( $params['user'] ) ) {
$this->dieUsageMsg( 'unblock-notarget' );
}
@@ -96,10 +90,6 @@ class ApiUnblock extends ApiBase {
),
'user' => null,
'token' => null,
- 'gettoken' => array(
- ApiBase::PARAM_DFLT => false,
- ApiBase::PARAM_DEPRECATED => true,
- ),
'reason' => '',
);
}
@@ -110,7 +100,6 @@ class ApiUnblock extends ApiBase {
'id' => "ID of the block you want to unblock (obtained through list=blocks). Cannot be used together with {$p}user",
'user' => "Username, IP address or IP range you want to unblock. Cannot be used together with {$p}id",
'token' => "An unblock token previously obtained through prop=info",
- 'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken',
'reason' => 'Reason for unblock',
);
}
@@ -118,10 +107,6 @@ class ApiUnblock extends ApiBase {
public function getResultProperties() {
return array(
'' => array(
- 'unblocktoken' => array(
- ApiBase::PROP_TYPE => 'string',
- ApiBase::PROP_NULLABLE => true
- ),
'id' => array(
ApiBase::PROP_TYPE => 'integer',
ApiBase::PROP_NULLABLE => true
diff --git a/includes/filerepo/file/LocalFile.php b/includes/filerepo/file/LocalFile.php
index 639228b9..4f50bfaa 100644
--- a/includes/filerepo/file/LocalFile.php
+++ b/includes/filerepo/file/LocalFile.php
@@ -1484,6 +1484,7 @@ class LocalFile extends File {
* @return FileRepoStatus object.
*/
function delete( $reason, $suppress = false ) {
+ global $wgUseSquid;
if ( $this->getRepo()->getReadOnlyReason() !== false ) {
return $this->readOnlyFatalStatus();
}
@@ -1506,6 +1507,15 @@ class LocalFile extends File {
$this->purgeOldThumbnails( $archiveName );
}
+ if ( $wgUseSquid ) {
+ // Purge the squid
+ $purgeUrls = array();
+ foreach ($archiveNames as $archiveName ) {
+ $purgeUrls[] = $this->getArchiveUrl( $archiveName );
+ }
+ SquidUpdate::purge( $purgeUrls );
+ }
+
return $status;
}
@@ -1524,6 +1534,7 @@ class LocalFile extends File {
* @return FileRepoStatus object.
*/
function deleteOld( $archiveName, $reason, $suppress = false ) {
+ global $wgUseSquid;
if ( $this->getRepo()->getReadOnlyReason() !== false ) {
return $this->readOnlyFatalStatus();
}
@@ -1541,6 +1552,11 @@ class LocalFile extends File {
$this->purgeHistory();
}
+ if ( $wgUseSquid ) {
+ // Purge the squid
+ SquidUpdate::purge( array( $this->getArchiveUrl( $archiveName ) ) );
+ }
+
return $status;
}
diff --git a/includes/installer/Installer.php b/includes/installer/Installer.php
index 4d8e5f0d..9fd67c93 100644
--- a/includes/installer/Installer.php
+++ b/includes/installer/Installer.php
@@ -46,6 +46,14 @@ abstract class Installer {
*/
protected $settings;
+
+ /**
+ * List of detected DBs, access using getCompiledDBs().
+ *
+ * @var array
+ */
+ protected $compiledDBs;
+
/**
* Cached DB installer instances, access using getDBInstaller().
*
@@ -173,7 +181,6 @@ abstract class Installer {
protected $internalDefaults = array(
'_UserLang' => 'en',
'_Environment' => false,
- '_CompiledDBs' => array(),
'_SafeMode' => false,
'_RaiseMemory' => false,
'_UpgradeDone' => false,
@@ -368,7 +375,7 @@ abstract class Installer {
}
}
}
- $this->setVar( '_CompiledDBs', $compiledDBs );
+ $this->compiledDBs = $compiledDBs;
$this->parserTitle = Title::newFromText( 'Installer' );
$this->parserOptions = new ParserOptions; // language will be wrong :(
@@ -449,6 +456,15 @@ abstract class Installer {
}
}
+ /**
+ * Get a list of DBs supported by current PHP setup
+ *
+ * @return array
+ */
+ public function getCompiledDBs() {
+ return $this->compiledDBs;
+ }
+
/**
* Get an instance of DatabaseInstaller for the specified DB type.
*
@@ -647,13 +663,7 @@ abstract class Installer {
$allNames[] = wfMessage( "config-type-$name" )->text();
}
- // cache initially available databases to make sure that everything will be displayed correctly
- // after a refresh on env checks page
- $databases = $this->getVar( '_CompiledDBs-preFilter' );
- if ( !$databases ) {
- $databases = $this->getVar( '_CompiledDBs' );
- $this->setVar( '_CompiledDBs-preFilter', $databases );
- }
+ $databases = $this->getCompiledDBs();
$databases = array_flip ( $databases );
foreach ( array_keys( $databases ) as $db ) {
@@ -672,7 +682,6 @@ abstract class Installer {
// @todo FIXME: This only works for the web installer!
return false;
}
- $this->setVar( '_CompiledDBs', $databases );
return true;
}
diff --git a/includes/installer/MysqlUpdater.php b/includes/installer/MysqlUpdater.php
index 9d73e629..030c57fc 100644
--- a/includes/installer/MysqlUpdater.php
+++ b/includes/installer/MysqlUpdater.php
@@ -200,9 +200,9 @@ class MysqlUpdater extends DatabaseUpdater {
// 1.19
array( 'addIndex', 'logging', 'type_action', 'patch-logging-type-action-index.sql'),
+ array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1.sql' ),
array( 'doMigrateUserOptions' ),
array( 'dropField', 'user', 'user_options', 'patch-drop-user_options.sql' ),
- array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1.sql' ),
array( 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1.sql' ),
array( 'addIndex', 'page', 'page_redirect_namespace_len', 'patch-page_redirect_namespace_len.sql' ),
array( 'addField', 'uploadstash', 'us_chunk_inx', 'patch-uploadstash_chunk.sql' ),
diff --git a/includes/installer/WebInstallerPage.php b/includes/installer/WebInstallerPage.php
index 78830293..06d16a5a 100644
--- a/includes/installer/WebInstallerPage.php
+++ b/includes/installer/WebInstallerPage.php
@@ -462,7 +462,7 @@ class WebInstaller_DBConnect extends WebInstallerPage {
// It's possible that the library for the default DB type is not compiled in.
// In that case, instead select the first supported DB type in the list.
- $compiledDBs = $this->parent->getVar( '_CompiledDBs' );
+ $compiledDBs = $this->parent->getCompiledDBs();
if ( !in_array( $defaultType, $compiledDBs ) ) {
$defaultType = $compiledDBs[0];
}
diff --git a/includes/libs/IEUrlExtension.php b/includes/libs/IEUrlExtension.php
index 79387e63..49d05d4b 100644
--- a/includes/libs/IEUrlExtension.php
+++ b/includes/libs/IEUrlExtension.php
@@ -232,7 +232,7 @@ class IEUrlExtension {
}
// We found an illegal character or another dot
// Skip to that character and continue the loop
- $pos = $nextPos + 1;
+ $pos = $nextPos;
$remainingLength = $urlLength - $pos;
}
return false;
diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php
index 27f682c2..4e047be4 100644
--- a/includes/resourceloader/ResourceLoader.php
+++ b/includes/resourceloader/ResourceLoader.php
@@ -175,7 +175,7 @@ class ResourceLoader {
$cache->set( $key, $result );
} catch ( Exception $exception ) {
// Return exception as a comment
- $result = $this->makeComment( $exception->__toString() );
+ $result = $this->formatException( $exception );
$this->hasErrors = true;
}
@@ -461,7 +461,7 @@ class ResourceLoader {
$this->preloadModuleInfo( array_keys( $modules ), $context );
} catch( Exception $e ) {
// Add exception to the output as a comment
- $errors .= $this->makeComment( $e->__toString() );
+ $errors .= $this->formatException( $e );
$this->hasErrors = true;
}
@@ -479,7 +479,7 @@ class ResourceLoader {
$mtime = max( $mtime, $module->getModifiedTime( $context ) );
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $errors .= $this->makeComment( $e->__toString() );
+ $errors .= $this->formatException( $e );
$this->hasErrors = true;
}
}
@@ -662,6 +662,22 @@ class ResourceLoader {
return "/*\n$encText\n*/\n";
}
+ /**
+ * Handle exception display
+ *
+ * @param Exception $e to be shown to the user
+ * @return string sanitized text that can be returned to the user
+ */
+ protected function formatException( $e ) {
+ global $wgShowExceptionDetails;
+
+ if ( $wgShowExceptionDetails ) {
+ return $this->makeComment( $e->__toString() );
+ } else {
+ return $this->makeComment( wfMessage( 'internalerror' )->text() );
+ }
+ }
+
/**
* Generates code for a response
*
@@ -686,7 +702,7 @@ class ResourceLoader {
$blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= $this->makeComment( $e->__toString() );
+ $exceptions .= $this->formatException( $e );
$this->hasErrors = true;
}
} else {
@@ -792,7 +808,7 @@ class ResourceLoader {
}
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= $this->makeComment( $e->__toString() );
+ $exceptions .= $this->formatException( $e );
$this->hasErrors = true;
// Register module as missing
diff --git a/includes/revisiondelete/RevisionDelete.php b/includes/revisiondelete/RevisionDelete.php
index 1ace3836..f4c07968 100644
--- a/includes/revisiondelete/RevisionDelete.php
+++ b/includes/revisiondelete/RevisionDelete.php
@@ -499,9 +499,20 @@ class RevDel_FileList extends RevDel_List {
}
public function doPostCommitUpdates() {
+ global $wgUseSquid;
$file = wfLocalFile( $this->title );
$file->purgeCache();
$file->purgeDescription();
+ $purgeUrls = array();
+ foreach ( $this->ids as $timestamp ) {
+ $archiveName = $timestamp . '!' . $this->title->getDBkey();
+ $file->purgeOldThumbnails( $archiveName );
+ $purgeUrls[] = $file->getArchiveUrl( $archiveName );
+ }
+ if ( $wgUseSquid ) {
+ // purge full images from cache
+ SquidUpdate::purge( $purgeUrls );
+ }
return Status::newGood();
}
diff --git a/includes/zhtable/trad2simp_supp_unset.manual b/includes/zhtable/trad2simp_supp_unset.manual
deleted file mode 100644
index e69de29b..00000000
diff --git a/maintenance/language/zhtable/trad2simp_supp_unset.manual b/maintenance/language/zhtable/trad2simp_supp_unset.manual
new file mode 100644
index 00000000..e69de29b
diff --git a/maintenance/postgres/archives/patch-ipb_address_unique.sql b/maintenance/postgres/archives/patch-ipb_address_unique.sql
deleted file mode 100644
index e69de29b..00000000
diff --git a/skins/common/images/icons/fileicon-djvu.xcf b/skins/common/images/icons/fileicon-djvu.xcf
new file mode 100644
index 00000000..8043dcdb
Binary files /dev/null and b/skins/common/images/icons/fileicon-djvu.xcf differ
diff --git a/skins/common/images/icons/fileicon-ogg.xcf b/skins/common/images/icons/fileicon-ogg.xcf
new file mode 100644
index 00000000..a91024bf
Binary files /dev/null and b/skins/common/images/icons/fileicon-ogg.xcf differ
diff --git a/tests/.htaccess b/tests/.htaccess
new file mode 100644
index 00000000..3a428827
--- /dev/null
+++ b/tests/.htaccess
@@ -0,0 +1 @@
+Deny from all
diff --git a/tests/RunSeleniumTests.php b/tests/RunSeleniumTests.php
new file mode 100644
index 00000000..b7320cb1
--- /dev/null
+++ b/tests/RunSeleniumTests.php
@@ -0,0 +1,258 @@
+#!/usr/bin/env php
+=' ) ) {
+ # PHPUnit 3.5.0 introduced a nice autoloader based on class name
+ require_once( 'PHPUnit/Autoload.php' );
+} else {
+ # Keep the old pre PHPUnit 3.5.0 behavior for compatibility
+ require_once( 'PHPUnit/TextUI/Command.php' );
+}
+
+require_once( 'PHPUnit/Extensions/SeleniumTestCase.php' );
+include_once( 'PHPUnit/Util/Log/JUnit.php' );
+
+require_once( __DIR__ . "/selenium/SeleniumServerManager.php" );
+
+class SeleniumTester extends Maintenance {
+ protected $selenium;
+ protected $serverManager;
+ protected $seleniumServerExecPath;
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Selenium Test Runner. For documentation, visit http://www.mediawiki.org/wiki/SeleniumFramework";
+ $this->addOption( 'port', 'Port used by selenium server. Default: 4444', false, true );
+ $this->addOption( 'host', 'Host selenium server. Default: $wgServer . $wgScriptPath', false, true );
+ $this->addOption( 'testBrowser', 'The browser used during testing. Default: firefox', false, true );
+ $this->addOption( 'wikiUrl', 'The Mediawiki installation to point to. Default: http://localhost', false, true );
+ $this->addOption( 'username', 'The login username for sunning tests. Default: empty', false, true );
+ $this->addOption( 'userPassword', 'The login password for running tests. Default: empty', false, true );
+ $this->addOption( 'seleniumConfig', 'Location of the selenium config file. Default: empty', false, true );
+ $this->addOption( 'list-browsers', 'List the available browsers.' );
+ $this->addOption( 'verbose', 'Be noisier.' );
+ $this->addOption( 'startserver', 'Start Selenium Server (on localhost) before the run.' );
+ $this->addOption( 'stopserver', 'Stop Selenium Server (on localhost) after the run.' );
+ $this->addOption( 'jUnitLogFile', 'Log results in a specified JUnit log file. Default: empty', false, true );
+ $this->addOption( 'runAgainstGrid', 'The test will be run against a Selenium Grid. Default: false.', false, true );
+ $this->deleteOption( 'dbpass' );
+ $this->deleteOption( 'dbuser' );
+ $this->deleteOption( 'globals' );
+ $this->deleteOption( 'wiki' );
+ }
+
+ public function listBrowsers() {
+ $desc = "Available browsers:\n";
+
+ foreach ( $this->selenium->getAvailableBrowsers() as $k => $v ) {
+ $desc .= " $k => $v\n";
+ }
+
+ echo $desc;
+ }
+
+ protected function startServer() {
+ if ( $this->seleniumServerExecPath == '' ) {
+ die ( "The selenium server exec path is not set in " .
+ "selenium_settings.ini. Cannot start server \n" .
+ "as requested - terminating RunSeleniumTests\n" );
+ }
+ $this->serverManager = new SeleniumServerManager( 'true',
+ $this->selenium->getPort(),
+ $this->seleniumServerExecPath );
+ switch ( $this->serverManager->start() ) {
+ case 'started':
+ break;
+ case 'failed':
+ die ( "Unable to start the Selenium Server - " .
+ "terminating RunSeleniumTests\n" );
+ case 'running':
+ echo ( "Warning: The Selenium Server is " .
+ "already running\n" );
+ break;
+ }
+
+ return;
+ }
+
+ protected function stopServer() {
+ if ( !isset ( $this->serverManager ) ) {
+ echo ( "Warning: Request to stop Selenium Server, but it was " .
+ "not stared by RunSeleniumTests\n" .
+ "RunSeleniumTests cannot stop a Selenium Server it " .
+ "did not start\n" );
+ } else {
+ switch ( $this->serverManager->stop() ) {
+ case 'stopped':
+ break;
+ case 'failed':
+ echo ( "unable to stop the Selenium Server\n" );
+ }
+ }
+ return;
+ }
+
+ protected function runTests( $seleniumTestSuites = array() ) {
+ $result = new PHPUnit_Framework_TestResult;
+ $result->addListener( new SeleniumTestListener( $this->selenium->getLogger() ) );
+ if ( $this->selenium->getJUnitLogFile() ) {
+ $jUnitListener = new PHPUnit_Util_Log_JUnit( $this->selenium->getJUnitLogFile(), true );
+ $result->addListener( $jUnitListener );
+ }
+
+ foreach ( $seleniumTestSuites as $testSuiteName => $testSuiteFile ) {
+ require( $testSuiteFile );
+ $suite = new $testSuiteName();
+ $suite->setName( $testSuiteName );
+ $suite->addTests();
+
+ try {
+ $suite->run( $result );
+ } catch ( Testing_Selenium_Exception $e ) {
+ $suite->tearDown();
+ throw new MWException( $e->getMessage() );
+ }
+ }
+
+ if ( $this->selenium->getJUnitLogFile() ) {
+ $jUnitListener->flush();
+ }
+ }
+
+ public function execute() {
+ global $wgServer, $wgScriptPath, $wgHooks;
+
+ $seleniumSettings = array();
+ $seleniumBrowsers = array();
+ $seleniumTestSuites = array();
+
+ $configFile = $this->getOption( 'seleniumConfig', '' );
+ if ( strlen( $configFile ) > 0 ) {
+ $this->output( "Using Selenium Configuration file: " . $configFile . "\n" );
+ SeleniumConfig::getSeleniumSettings( $seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites,
+ $configFile );
+ } elseif ( !isset( $wgHooks['SeleniumSettings'] ) ) {
+ $this->output( "No command line, configuration file or configuration hook found.\n" );
+ SeleniumConfig::getSeleniumSettings( $seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites
+ );
+ } else {
+ $this->output( "Using 'SeleniumSettings' hook for configuration.\n" );
+ wfRunHooks( 'SeleniumSettings', array( $seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites ) );
+ }
+
+ // State for starting/stopping the Selenium server has nothing to do with the Selenium
+ // class. Keep this state local to SeleniumTester class. Using getOption() is clumsy, but
+ // the Maintenance class does not have a setOption()
+ if ( !isset( $seleniumSettings['startserver'] ) ) {
+ $this->getOption( 'startserver', true );
+ }
+ if ( !isset( $seleniumSettings['stopserver'] ) ) {
+ $this->getOption( 'stopserver', true );
+ }
+ if ( !isset( $seleniumSettings['seleniumserverexecpath'] ) ) {
+ $seleniumSettings['seleniumserverexecpath'] = '';
+ }
+ $this->seleniumServerExecPath = $seleniumSettings['seleniumserverexecpath'];
+
+ //set reasonable defaults if we did not find the settings
+ if ( !isset( $seleniumBrowsers ) ) {
+ $seleniumBrowsers = array( 'firefox' => '*firefox' );
+ }
+ if ( !isset( $seleniumSettings['host'] ) ) {
+ $seleniumSettings['host'] = $wgServer . $wgScriptPath;
+ }
+ if ( !isset( $seleniumSettings['port'] ) ) {
+ $seleniumSettings['port'] = '4444';
+ }
+ if ( !isset( $seleniumSettings['wikiUrl'] ) ) {
+ $seleniumSettings['wikiUrl'] = 'http://localhost';
+ }
+ if ( !isset( $seleniumSettings['username'] ) ) {
+ $seleniumSettings['username'] = '';
+ }
+ if ( !isset( $seleniumSettings['userPassword'] ) ) {
+ $seleniumSettings['userPassword'] = '';
+ }
+ if ( !isset( $seleniumSettings['testBrowser'] ) ) {
+ $seleniumSettings['testBrowser'] = 'firefox';
+ }
+ if ( !isset( $seleniumSettings['jUnitLogFile'] ) ) {
+ $seleniumSettings['jUnitLogFile'] = false;
+ }
+ if ( !isset( $seleniumSettings['runAgainstGrid'] ) ) {
+ $seleniumSettings['runAgainstGrid'] = false;
+ }
+
+ // Setup Selenium class
+ $this->selenium = new Selenium();
+ $this->selenium->setAvailableBrowsers( $seleniumBrowsers );
+ $this->selenium->setRunAgainstGrid( $this->getOption( 'runAgainstGrid', $seleniumSettings['runAgainstGrid'] ) );
+ $this->selenium->setUrl( $this->getOption( 'wikiUrl', $seleniumSettings['wikiUrl'] ) );
+ $this->selenium->setBrowser( $this->getOption( 'testBrowser', $seleniumSettings['testBrowser'] ) );
+ $this->selenium->setPort( $this->getOption( 'port', $seleniumSettings['port'] ) );
+ $this->selenium->setHost( $this->getOption( 'host', $seleniumSettings['host'] ) );
+ $this->selenium->setUser( $this->getOption( 'username', $seleniumSettings['username'] ) );
+ $this->selenium->setPass( $this->getOption( 'userPassword', $seleniumSettings['userPassword'] ) );
+ $this->selenium->setVerbose( $this->hasOption( 'verbose' ) );
+ $this->selenium->setJUnitLogFile( $this->getOption( 'jUnitLogFile', $seleniumSettings['jUnitLogFile'] ) );
+
+ if ( $this->hasOption( 'list-browsers' ) ) {
+ $this->listBrowsers();
+ exit( 0 );
+ }
+ if ( $this->hasOption( 'startserver' ) ) {
+ $this->startServer();
+ }
+
+ $logger = new SeleniumTestConsoleLogger;
+ $this->selenium->setLogger( $logger );
+
+ $this->runTests( $seleniumTestSuites );
+
+ if ( $this->hasOption( 'stopserver' ) ) {
+ $this->stopServer();
+ }
+ }
+}
+
+$maintClass = "SeleniumTester";
+
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/tests/TestsAutoLoader.php b/tests/TestsAutoLoader.php
new file mode 100644
index 00000000..c1c301f6
--- /dev/null
+++ b/tests/TestsAutoLoader.php
@@ -0,0 +1,104 @@
+ "$testDir/testHelpers.inc",
+ 'DbTestRecorder' => "$testDir/testHelpers.inc",
+ 'DelayedParserTest' => "$testDir/testHelpers.inc",
+ 'TestFileIterator' => "$testDir/testHelpers.inc",
+ 'TestRecorder' => "$testDir/testHelpers.inc",
+
+ # tests/phpunit
+ 'MediaWikiTestCase' => "$testDir/phpunit/MediaWikiTestCase.php",
+ 'MediaWikiPHPUnitCommand' => "$testDir/phpunit/MediaWikiPHPUnitCommand.php",
+ 'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
+ 'MediaWikiProvide' => "$testDir/phpunit/includes/Providers.php",
+ 'TestUser' => "$testDir/phpunit/includes/TestUser.php",
+
+ # tests/phpunit/includes
+ 'BlockTest' => "$testDir/phpunit/includes/BlockTest.php",
+ 'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php",
+ 'WikiPageTest' => "$testDir/phpunit/includes/WikiPageTest.php",
+
+ //db
+ 'ORMTableTest' => "$testDir/phpunit/includes/db/ORMTableTest.php",
+ 'PageORMTableForTesting' => "$testDir/phpunit/includes/db/ORMTableTest.php",
+
+ //Selenium
+ 'SeleniumTestConstants' => "$testDir/selenium/SeleniumTestConstants.php",
+
+ # tests/phpunit/includes/api
+ 'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
+ 'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+ 'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+ 'MockApi' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+ 'RandomImageGenerator' => "$testDir/phpunit/includes/api/RandomImageGenerator.php",
+ 'UserWrapper' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+
+ # tests/phpunit/includes/content
+ 'DummyContentHandlerForTesting' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
+ 'DummyContentForTesting' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
+ 'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
+ 'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
+ 'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
+ 'WikitextContentTest' => "$testDir/phpunit/includes/content/WikitextContentTest.php",
+
+ # tests/phpunit/includes/db
+ 'ORMRowTest' => "$testDir/phpunit/includes/db/ORMRowTest.php",
+
+ # tests/phpunit/includes/parser
+ 'NewParserTest' => "$testDir/phpunit/includes/parser/NewParserTest.php",
+
+ # tests/phpunit/includes/libs
+ 'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
+
+ # tests/phpunit/includes/site
+ 'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
+ 'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
+
+ # tests/phpunit/languages
+ 'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
+
+ # tests/phpunit/maintenance
+ 'DumpTestCase' => "$testDir/phpunit/maintenance/DumpTestCase.php",
+
+ # tests/parser
+ 'ParserTest' => "$testDir/parser/parserTest.inc",
+ 'ParserTestParserHook' => "$testDir/parser/parserTestsParserHook.php",
+
+ # tests/selenium
+ 'Selenium' => "$testDir/selenium/Selenium.php",
+ 'SeleniumLoader' => "$testDir/selenium/SeleniumLoader.php",
+ 'SeleniumTestCase' => "$testDir/selenium/SeleniumTestCase.php",
+ 'SeleniumTestConsoleLogger' => "$testDir/selenium/SeleniumTestConsoleLogger.php",
+ 'SeleniumTestConstants' => "$testDir/selenium/SeleniumTestConstants.php",
+ 'SeleniumTestHTMLLogger' => "$testDir/selenium/SeleniumTestHTMLLogger.php",
+ 'SeleniumTestListener' => "$testDir/selenium/SeleniumTestListener.php",
+ 'SeleniumTestSuite' => "$testDir/selenium/SeleniumTestSuite.php",
+ 'SeleniumConfig' => "$testDir/selenium/SeleniumConfig.php",
+);
diff --git a/tests/parser/README b/tests/parser/README
new file mode 100644
index 00000000..8b413376
--- /dev/null
+++ b/tests/parser/README
@@ -0,0 +1,8 @@
+Parser tests are run using our PHPUnit test suite in tests/phpunit:
+
+ $ cd tests/phpunit
+ ./phpunit.php --group Parser
+
+You can optionally filter by title using --regex. I.e. :
+
+ ./phpunit.php --group Parser --regex="Bug 6200"
diff --git a/tests/parser/extraParserTests.txt b/tests/parser/extraParserTests.txt
new file mode 100644
index 00000000..bef8f506
Binary files /dev/null and b/tests/parser/extraParserTests.txt differ
diff --git a/tests/parser/parserTest.inc b/tests/parser/parserTest.inc
new file mode 100644
index 00000000..ce621f4e
--- /dev/null
+++ b/tests/parser/parserTest.inc
@@ -0,0 +1,1349 @@
+
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @todo Make this more independent of the configuration (and if possible the database)
+ * @todo document
+ * @file
+ * @ingroup Testing
+ */
+
+/**
+ * @ingroup Testing
+ */
+class ParserTest {
+ /**
+ * boolean $color whereas output should be colorized
+ */
+ private $color;
+
+ /**
+ * boolean $showOutput Show test output
+ */
+ private $showOutput;
+
+ /**
+ * boolean $useTemporaryTables Use temporary tables for the temporary database
+ */
+ private $useTemporaryTables = true;
+
+ /**
+ * boolean $databaseSetupDone True if the database has been set up
+ */
+ private $databaseSetupDone = false;
+
+ /**
+ * Our connection to the database
+ * @var DatabaseBase
+ */
+ private $db;
+
+ /**
+ * Database clone helper
+ * @var CloneDatabase
+ */
+ private $dbClone;
+
+ /**
+ * string $oldTablePrefix Original table prefix
+ */
+ private $oldTablePrefix;
+
+ private $maxFuzzTestLength = 300;
+ private $fuzzSeed = 0;
+ private $memoryLimit = 50;
+ private $uploadDir = null;
+
+ public $regex = "";
+ private $savedGlobals = array();
+
+ /**
+ * Sets terminal colorization and diff/quick modes depending on OS and
+ * command-line options (--color and --quick).
+ */
+ public function __construct( $options = array() ) {
+ # Only colorize output if stdout is a terminal.
+ $this->color = !wfIsWindows() && Maintenance::posix_isatty( 1 );
+
+ if ( isset( $options['color'] ) ) {
+ switch ( $options['color'] ) {
+ case 'no':
+ $this->color = false;
+ break;
+ case 'yes':
+ default:
+ $this->color = true;
+ break;
+ }
+ }
+
+ $this->term = $this->color
+ ? new AnsiTermColorer()
+ : new DummyTermColorer();
+
+ $this->showDiffs = !isset( $options['quick'] );
+ $this->showProgress = !isset( $options['quiet'] );
+ $this->showFailure = !(
+ isset( $options['quiet'] )
+ && ( isset( $options['record'] )
+ || isset( $options['compare'] ) ) ); // redundant output
+
+ $this->showOutput = isset( $options['show-output'] );
+
+ if ( isset( $options['filter'] ) ) {
+ $options['regex'] = $options['filter'];
+ }
+
+ if ( isset( $options['regex'] ) ) {
+ if ( isset( $options['record'] ) ) {
+ echo "Warning: --record cannot be used with --regex, disabling --record\n";
+ unset( $options['record'] );
+ }
+ $this->regex = $options['regex'];
+ } else {
+ # Matches anything
+ $this->regex = '';
+ }
+
+ $this->setupRecorder( $options );
+ $this->keepUploads = isset( $options['keep-uploads'] );
+
+ if ( isset( $options['seed'] ) ) {
+ $this->fuzzSeed = intval( $options['seed'] ) - 1;
+ }
+
+ $this->runDisabled = isset( $options['run-disabled'] );
+ $this->runParsoid = isset( $options['run-parsoid'] );
+
+ $this->hooks = array();
+ $this->functionHooks = array();
+ self::setUp();
+ }
+
+ static function setUp() {
+ global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc,
+ $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache,
+ $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
+ $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
+ $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
+ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgLockManagers;
+
+ $wgScript = '/index.php';
+ $wgScriptPath = '/';
+ $wgArticlePath = '/wiki/$1';
+ $wgStyleSheetPath = '/skins';
+ $wgStylePath = '/skins';
+ $wgExtensionAssetsPath = '/extensions';
+ $wgThumbnailScriptPath = false;
+ $wgLockManagers = array( array(
+ 'name' => 'fsLockManager',
+ 'class' => 'FSLockManager',
+ 'lockDirectory' => wfTempDir() . '/test-repo/lockdir',
+ ), array(
+ 'name' => 'nullLockManager',
+ 'class' => 'NullLockManager',
+ ) );
+ $wgLocalFileRepo = array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'url' => 'http://example.com/images',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ 'backend' => new FSFileBackend( array(
+ 'name' => 'local-backend',
+ 'lockManager' => 'fsLockManager',
+ 'containerPaths' => array(
+ 'local-public' => wfTempDir() . '/test-repo/public',
+ 'local-thumb' => wfTempDir() . '/test-repo/thumb',
+ 'local-temp' => wfTempDir() . '/test-repo/temp',
+ 'local-deleted' => wfTempDir() . '/test-repo/deleted',
+ )
+ ) )
+ );
+ $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
+ $wgNamespaceAliases['Image'] = NS_FILE;
+ $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
+
+ // XXX: tests won't run without this (for CACHE_DB)
+ if ( $wgMainCacheType === CACHE_DB ) {
+ $wgMainCacheType = CACHE_NONE;
+ }
+ if ( $wgMessageCacheType === CACHE_DB ) {
+ $wgMessageCacheType = CACHE_NONE;
+ }
+ if ( $wgParserCacheType === CACHE_DB ) {
+ $wgParserCacheType = CACHE_NONE;
+ }
+
+ $wgEnableParserCache = false;
+ DeferredUpdates::clearPendingUpdates();
+ $wgMemc = wfGetMainCache(); // checks $wgMainCacheType
+ $messageMemc = wfGetMessageCacheStorage();
+ $parserMemc = wfGetParserCacheStorage();
+
+ // $wgContLang = new StubContLang;
+ $wgUser = new User;
+ $context = new RequestContext;
+ $wgLang = $context->getLanguage();
+ $wgOut = $context->getOutput();
+ $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) );
+ $wgRequest = $context->getRequest();
+
+ if ( $wgStyleDirectory === false ) {
+ $wgStyleDirectory = "$IP/skins";
+ }
+
+ }
+
+ public function setupRecorder( $options ) {
+ if ( isset( $options['record'] ) ) {
+ $this->recorder = new DbTestRecorder( $this );
+ $this->recorder->version = isset( $options['setversion'] ) ?
+ $options['setversion'] : SpecialVersion::getVersion();
+ } elseif ( isset( $options['compare'] ) ) {
+ $this->recorder = new DbTestPreviewer( $this );
+ } else {
+ $this->recorder = new TestRecorder( $this );
+ }
+ }
+
+ /**
+ * Remove last character if it is a newline
+ * @group utility
+ */
+ public static function chomp( $s ) {
+ if ( substr( $s, -1 ) === "\n" ) {
+ return substr( $s, 0, -1 );
+ } else {
+ return $s;
+ }
+ }
+
+ /**
+ * Run a fuzz test series
+ * Draw input from a set of test files
+ */
+ function fuzzTest( $filenames ) {
+ $GLOBALS['wgContLang'] = Language::factory( 'en' );
+ $dict = $this->getFuzzInput( $filenames );
+ $dictSize = strlen( $dict );
+ $logMaxLength = log( $this->maxFuzzTestLength );
+ $this->setupDatabase();
+ ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
+
+ $numTotal = 0;
+ $numSuccess = 0;
+ $user = new User;
+ $opts = ParserOptions::newFromUser( $user );
+ $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
+
+ while ( true ) {
+ // Generate test input
+ mt_srand( ++$this->fuzzSeed );
+ $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
+ $input = '';
+
+ while ( strlen( $input ) < $totalLength ) {
+ $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
+ $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
+ $offset = mt_rand( 0, $dictSize - $hairLength );
+ $input .= substr( $dict, $offset, $hairLength );
+ }
+
+ $this->setupGlobals();
+ $parser = $this->getParser();
+
+ // Run the test
+ try {
+ $parser->parse( $input, $title, $opts );
+ $fail = false;
+ } catch ( Exception $exception ) {
+ $fail = true;
+ }
+
+ if ( $fail ) {
+ echo "Test failed with seed {$this->fuzzSeed}\n";
+ echo "Input:\n";
+ printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
+ echo "$exception\n";
+ } else {
+ $numSuccess++;
+ }
+
+ $numTotal++;
+ $this->teardownGlobals();
+ $parser->__destruct();
+
+ if ( $numTotal % 100 == 0 ) {
+ $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
+ echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
+ if ( $usage > 90 ) {
+ echo "Out of memory:\n";
+ $memStats = $this->getMemoryBreakdown();
+
+ foreach ( $memStats as $name => $usage ) {
+ echo "$name: $usage\n";
+ }
+ $this->abort();
+ }
+ }
+ }
+ }
+
+ /**
+ * Get an input dictionary from a set of parser test files
+ */
+ function getFuzzInput( $filenames ) {
+ $dict = '';
+
+ foreach ( $filenames as $filename ) {
+ $contents = file_get_contents( $filename );
+ preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches );
+
+ foreach ( $matches[1] as $match ) {
+ $dict .= $match . "\n";
+ }
+ }
+
+ return $dict;
+ }
+
+ /**
+ * Get a memory usage breakdown
+ */
+ function getMemoryBreakdown() {
+ $memStats = array();
+
+ foreach ( $GLOBALS as $name => $value ) {
+ $memStats['$' . $name] = strlen( serialize( $value ) );
+ }
+
+ $classes = get_declared_classes();
+
+ foreach ( $classes as $class ) {
+ $rc = new ReflectionClass( $class );
+ $props = $rc->getStaticProperties();
+ $memStats[$class] = strlen( serialize( $props ) );
+ $methods = $rc->getMethods();
+
+ foreach ( $methods as $method ) {
+ $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
+ }
+ }
+
+ $functions = get_defined_functions();
+
+ foreach ( $functions['user'] as $function ) {
+ $rf = new ReflectionFunction( $function );
+ $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
+ }
+
+ asort( $memStats );
+
+ return $memStats;
+ }
+
+ function abort() {
+ $this->abort();
+ }
+
+ /**
+ * Run a series of tests listed in the given text files.
+ * Each test consists of a brief description, wikitext input,
+ * and the expected HTML output.
+ *
+ * Prints status updates on stdout and counts up the total
+ * number and percentage of passed tests.
+ *
+ * @param $filenames Array of strings
+ * @return Boolean: true if passed all tests, false if any tests failed.
+ */
+ public function runTestsFromFiles( $filenames ) {
+ $ok = false;
+ $GLOBALS['wgContLang'] = Language::factory( 'en' );
+ $this->recorder->start();
+ try {
+ $this->setupDatabase();
+ $ok = true;
+
+ foreach ( $filenames as $filename ) {
+ $tests = new TestFileIterator( $filename, $this );
+ $ok = $this->runTests( $tests ) && $ok;
+ }
+
+ $this->teardownDatabase();
+ $this->recorder->report();
+ } catch ( DBError $e ) {
+ echo $e->getMessage();
+ }
+ $this->recorder->end();
+
+ return $ok;
+ }
+
+ function runTests( $tests ) {
+ $ok = true;
+
+ foreach ( $tests as $t ) {
+ $result =
+ $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] );
+ $ok = $ok && $result;
+ $this->recorder->record( $t['test'], $result );
+ }
+
+ if ( $this->showProgress ) {
+ print "\n";
+ }
+
+ return $ok;
+ }
+
+ /**
+ * Get a Parser object
+ */
+ function getParser( $preprocessor = null ) {
+ global $wgParserConf;
+
+ $class = $wgParserConf['class'];
+ $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf );
+
+ foreach ( $this->hooks as $tag => $callback ) {
+ $parser->setHook( $tag, $callback );
+ }
+
+ foreach ( $this->functionHooks as $tag => $bits ) {
+ list( $callback, $flags ) = $bits;
+ $parser->setFunctionHook( $tag, $callback, $flags );
+ }
+
+ wfRunHooks( 'ParserTestParser', array( &$parser ) );
+
+ return $parser;
+ }
+
+ /**
+ * Run a given wikitext input through a freshly-constructed wiki parser,
+ * and compare the output against the expected results.
+ * Prints status and explanatory messages to stdout.
+ *
+ * @param $desc String: test's description
+ * @param $input String: wikitext to try rendering
+ * @param $result String: result to output
+ * @param $opts Array: test's options
+ * @param $config String: overrides for global variables, one per line
+ * @return Boolean
+ */
+ public function runTest( $desc, $input, $result, $opts, $config ) {
+ if ( $this->showProgress ) {
+ $this->showTesting( $desc );
+ }
+
+ $opts = $this->parseOptions( $opts );
+ $context = $this->setupGlobals( $opts, $config );
+
+ $user = $context->getUser();
+ $options = ParserOptions::newFromContext( $context );
+
+ if ( isset( $opts['title'] ) ) {
+ $titleText = $opts['title'];
+ } else {
+ $titleText = 'Parser test';
+ }
+
+ $local = isset( $opts['local'] );
+ $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
+ $parser = $this->getParser( $preprocessor );
+ $title = Title::newFromText( $titleText );
+
+ if ( isset( $opts['pst'] ) ) {
+ $out = $parser->preSaveTransform( $input, $title, $user, $options );
+ } elseif ( isset( $opts['msg'] ) ) {
+ $out = $parser->transformMsg( $input, $options, $title );
+ } elseif ( isset( $opts['section'] ) ) {
+ $section = $opts['section'];
+ $out = $parser->getSection( $input, $section );
+ } elseif ( isset( $opts['replace'] ) ) {
+ $section = $opts['replace'][0];
+ $replace = $opts['replace'][1];
+ $out = $parser->replaceSection( $input, $section, $replace );
+ } elseif ( isset( $opts['comment'] ) ) {
+ $out = Linker::formatComment( $input, $title, $local );
+ } elseif ( isset( $opts['preload'] ) ) {
+ $out = $parser->getpreloadText( $input, $title, $options );
+ } else {
+ $output = $parser->parse( $input, $title, $options, true, true, 1337 );
+ $out = $output->getText();
+
+ if ( isset( $opts['showtitle'] ) ) {
+ if ( $output->getTitleText() ) {
+ $title = $output->getTitleText();
+ }
+
+ $out = "$title\n$out";
+ }
+
+ if ( isset( $opts['ill'] ) ) {
+ $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) );
+ } elseif ( isset( $opts['cat'] ) ) {
+ $outputPage = $context->getOutput();
+ $outputPage->addCategoryLinks( $output->getCategories() );
+ $cats = $outputPage->getCategoryLinks();
+
+ if ( isset( $cats['normal'] ) ) {
+ $out = $this->tidy( implode( ' ', $cats['normal'] ) );
+ } else {
+ $out = '';
+ }
+ }
+
+ $result = $this->tidy( $result );
+ }
+
+ $this->teardownGlobals();
+ return $this->showTestResult( $desc, $result, $out );
+ }
+
+ /**
+ *
+ */
+ function showTestResult( $desc, $result, $out ) {
+ if ( $result === $out ) {
+ $this->showSuccess( $desc );
+ return true;
+ } else {
+ $this->showFailure( $desc, $result, $out );
+ return false;
+ }
+ }
+
+ /**
+ * Use a regex to find out the value of an option
+ * @param $key String: name of option val to retrieve
+ * @param $opts Options array to look in
+ * @param $default Mixed: default value returned if not found
+ */
+ private static function getOptionValue( $key, $opts, $default ) {
+ $key = strtolower( $key );
+
+ if ( isset( $opts[$key] ) ) {
+ return $opts[$key];
+ } else {
+ return $default;
+ }
+ }
+
+ private function parseOptions( $instring ) {
+ $opts = array();
+ // foo
+ // foo=bar
+ // foo="bar baz"
+ // foo=[[bar baz]]
+ // foo=bar,"baz quux"
+ $regex = '/\b
+ ([\w-]+) # Key
+ \b
+ (?:\s*
+ = # First sub-value
+ \s*
+ (
+ "
+ [^"]* # Quoted val
+ "
+ |
+ \[\[
+ [^]]* # Link target
+ \]\]
+ |
+ [\w-]+ # Plain word
+ )
+ (?:\s*
+ , # Sub-vals 1..N
+ \s*
+ (
+ "[^"]*" # Quoted val
+ |
+ \[\[[^]]*\]\] # Link target
+ |
+ [\w-]+ # Plain word
+ )
+ )*
+ )?
+ /x';
+
+ if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
+ foreach ( $matches as $bits ) {
+ array_shift( $bits );
+ $key = strtolower( array_shift( $bits ) );
+ if ( count( $bits ) == 0 ) {
+ $opts[$key] = true;
+ } elseif ( count( $bits ) == 1 ) {
+ $opts[$key] = $this->cleanupOption( array_shift( $bits ) );
+ } else {
+ // Array!
+ $opts[$key] = array_map( array( $this, 'cleanupOption' ), $bits );
+ }
+ }
+ }
+ return $opts;
+ }
+
+ private function cleanupOption( $opt ) {
+ if ( substr( $opt, 0, 1 ) == '"' ) {
+ return substr( $opt, 1, -1 );
+ }
+
+ if ( substr( $opt, 0, 2 ) == '[[' ) {
+ return substr( $opt, 2, -2 );
+ }
+ return $opt;
+ }
+
+ /**
+ * Set up the global variables for a consistent environment for each test.
+ * Ideally this should replace the global configuration entirely.
+ */
+ private function setupGlobals( $opts = '', $config = '' ) {
+ # Find out values for some special options.
+ $lang =
+ self::getOptionValue( 'language', $opts, 'en' );
+ $variant =
+ self::getOptionValue( 'variant', $opts, false );
+ $maxtoclevel =
+ self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
+ $linkHolderBatchSize =
+ self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
+
+ $settings = array(
+ 'wgServer' => 'http://example.org',
+ 'wgScript' => '/index.php',
+ 'wgScriptPath' => '/',
+ 'wgArticlePath' => '/wiki/$1',
+ 'wgActionPaths' => array(),
+ 'wgLockManagers' => array( array(
+ 'name' => 'fsLockManager',
+ 'class' => 'FSLockManager',
+ 'lockDirectory' => $this->uploadDir . '/lockdir',
+ ), array(
+ 'name' => 'nullLockManager',
+ 'class' => 'NullLockManager',
+ ) ),
+ 'wgLocalFileRepo' => array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'url' => 'http://example.com/images',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ 'backend' => new FSFileBackend( array(
+ 'name' => 'local-backend',
+ 'lockManager' => 'fsLockManager',
+ 'containerPaths' => array(
+ 'local-public' => $this->uploadDir,
+ 'local-thumb' => $this->uploadDir . '/thumb',
+ 'local-temp' => $this->uploadDir . '/temp',
+ 'local-deleted' => $this->uploadDir . '/delete',
+ )
+ ) )
+ ),
+ 'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
+ 'wgStylePath' => '/skins',
+ 'wgStyleSheetPath' => '/skins',
+ 'wgSitename' => 'MediaWiki',
+ 'wgLanguageCode' => $lang,
+ 'wgDBprefix' => $this->db->getType() != 'oracle' ? 'parsertest_' : 'pt_',
+ 'wgRawHtml' => isset( $opts['rawhtml'] ),
+ 'wgLang' => null,
+ 'wgContLang' => null,
+ 'wgNamespacesWithSubpages' => array( 0 => isset( $opts['subpage'] ) ),
+ 'wgMaxTocLevel' => $maxtoclevel,
+ 'wgCapitalLinks' => true,
+ 'wgNoFollowLinks' => true,
+ 'wgNoFollowDomainExceptions' => array(),
+ 'wgThumbnailScriptPath' => false,
+ 'wgUseImageResize' => true,
+ 'wgLocaltimezone' => 'UTC',
+ 'wgAllowExternalImages' => true,
+ 'wgUseTidy' => false,
+ 'wgDefaultLanguageVariant' => $variant,
+ 'wgVariantArticlePath' => false,
+ 'wgGroupPermissions' => array( '*' => array(
+ 'createaccount' => true,
+ 'read' => true,
+ 'edit' => true,
+ 'createpage' => true,
+ 'createtalk' => true,
+ ) ),
+ 'wgNamespaceProtection' => array( NS_MEDIAWIKI => 'editinterface' ),
+ 'wgDefaultExternalStore' => array(),
+ 'wgForeignFileRepos' => array(),
+ 'wgLinkHolderBatchSize' => $linkHolderBatchSize,
+ 'wgExperimentalHtmlIds' => false,
+ 'wgExternalLinkTarget' => false,
+ 'wgAlwaysUseTidy' => false,
+ 'wgHtml5' => true,
+ 'wgWellFormedXml' => true,
+ 'wgAllowMicrodataAttributes' => true,
+ 'wgAdaptiveMessageCache' => true,
+ 'wgDisableLangConversion' => false,
+ 'wgDisableTitleConversion' => false,
+ );
+
+ if ( $config ) {
+ $configLines = explode( "\n", $config );
+
+ foreach ( $configLines as $line ) {
+ list( $var, $value ) = explode( '=', $line, 2 );
+
+ $settings[$var] = eval( "return $value;" );
+ }
+ }
+
+ $this->savedGlobals = array();
+
+ /** @since 1.20 */
+ wfRunHooks( 'ParserTestGlobals', array( &$settings ) );
+
+ foreach ( $settings as $var => $val ) {
+ if ( array_key_exists( $var, $GLOBALS ) ) {
+ $this->savedGlobals[$var] = $GLOBALS[$var];
+ }
+
+ $GLOBALS[$var] = $val;
+ }
+
+ $GLOBALS['wgContLang'] = Language::factory( $lang );
+ $GLOBALS['wgMemc'] = new EmptyBagOStuff;
+
+ $context = new RequestContext();
+ $GLOBALS['wgLang'] = $context->getLanguage();
+ $GLOBALS['wgOut'] = $context->getOutput();
+
+ $GLOBALS['wgUser'] = new User();
+
+ global $wgHooks;
+
+ $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
+ $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
+
+ MagicWord::clearCache();
+
+ return $context;
+ }
+
+ /**
+ * List of temporary tables to create, without prefix.
+ * Some of these probably aren't necessary.
+ */
+ private function listTables() {
+ $tables = array( 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
+ 'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
+ 'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
+ 'site_stats', 'hitcounter', 'ipblocks', 'image', 'oldimage',
+ 'recentchanges', 'watchlist', 'interwiki', 'logging',
+ 'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
+ 'archive', 'user_groups', 'page_props', 'category', 'msg_resource', 'msg_resource_links'
+ );
+
+ if ( in_array( $this->db->getType(), array( 'mysql', 'sqlite', 'oracle' ) ) ) {
+ array_push( $tables, 'searchindex' );
+ }
+
+ // Allow extensions to add to the list of tables to duplicate;
+ // may be necessary if they hook into page save or other code
+ // which will require them while running tests.
+ wfRunHooks( 'ParserTestTables', array( &$tables ) );
+
+ return $tables;
+ }
+
+ /**
+ * Set up a temporary set of wiki tables to work with for the tests.
+ * Currently this will only be done once per run, and any changes to
+ * the db will be visible to later tests in the run.
+ */
+ public function setupDatabase() {
+ global $wgDBprefix;
+
+ if ( $this->databaseSetupDone ) {
+ return;
+ }
+
+ $this->db = wfGetDB( DB_MASTER );
+ $dbType = $this->db->getType();
+
+ if ( $wgDBprefix === 'parsertest_' || ( $dbType == 'oracle' && $wgDBprefix === 'pt_' ) ) {
+ throw new MWException( 'setupDatabase should be called before setupGlobals' );
+ }
+
+ $this->databaseSetupDone = true;
+ $this->oldTablePrefix = $wgDBprefix;
+
+ # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
+ # It seems to have been fixed since (r55079?), but regressed at some point before r85701.
+ # This works around it for now...
+ ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
+
+ # CREATE TEMPORARY TABLE breaks if there is more than one server
+ if ( wfGetLB()->getServerCount() != 1 ) {
+ $this->useTemporaryTables = false;
+ }
+
+ $temporary = $this->useTemporaryTables || $dbType == 'postgres';
+ $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
+
+ $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
+ $this->dbClone->useTemporaryTables( $temporary );
+ $this->dbClone->cloneTableStructure();
+
+ if ( $dbType == 'oracle' ) {
+ $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+ # Insert 0 user to prevent FK violations
+
+ # Anonymous user
+ $this->db->insert( 'user', array(
+ 'user_id' => 0,
+ 'user_name' => 'Anonymous' ) );
+ }
+
+ # Hack: insert a few Wikipedia in-project interwiki prefixes,
+ # for testing inter-language links
+ $this->db->insert( 'interwiki', array(
+ array( 'iw_prefix' => 'wikipedia',
+ 'iw_url' => 'http://en.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 0 ),
+ array( 'iw_prefix' => 'meatball',
+ 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 0 ),
+ array( 'iw_prefix' => 'zh',
+ 'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'es',
+ 'iw_url' => 'http://es.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'fr',
+ 'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'ru',
+ 'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ ) );
+
+ # Update certain things in site_stats
+ $this->db->insert( 'site_stats', array( 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ) );
+
+ # Reinitialise the LocalisationCache to match the database state
+ Language::getLocalisationCache()->unloadAll();
+
+ # Clear the message cache
+ MessageCache::singleton()->clear();
+
+ $this->uploadDir = $this->setupUploadDir();
+ $user = User::createNew( 'WikiSysop' );
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
+ $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array(
+ 'size' => 12345,
+ 'width' => 1941,
+ 'height' => 220,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_BITMAP,
+ 'mime' => 'image/jpeg',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+
+ # This image will be blacklisted in [[MediaWiki:Bad image list]]
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
+ $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
+ 'size' => 12345,
+ 'width' => 320,
+ 'height' => 240,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_BITMAP,
+ 'mime' => 'image/jpeg',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+ }
+
+ public function teardownDatabase() {
+ if ( !$this->databaseSetupDone ) {
+ $this->teardownGlobals();
+ return;
+ }
+ $this->teardownUploadDir( $this->uploadDir );
+
+ $this->dbClone->destroy();
+ $this->databaseSetupDone = false;
+
+ if ( $this->useTemporaryTables ) {
+ if ( $this->db->getType() == 'sqlite' ) {
+ # Under SQLite the searchindex table is virtual and need
+ # to be explicitly destroyed. See bug 29912
+ # See also MediaWikiTestCase::destroyDB()
+ wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
+ $this->db->query( "DROP TABLE `parsertest_searchindex`" );
+ }
+ # Don't need to do anything
+ $this->teardownGlobals();
+ return;
+ }
+
+ $tables = $this->listTables();
+
+ foreach ( $tables as $table ) {
+ $sql = $this->db->getType() == 'oracle' ? "DROP TABLE pt_$table DROP CONSTRAINTS" : "DROP TABLE `parsertest_$table`";
+ $this->db->query( $sql );
+ }
+
+ if ( $this->db->getType() == 'oracle' ) {
+ $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+ }
+
+ $this->teardownGlobals();
+ }
+
+ /**
+ * Create a dummy uploads directory which will contain a couple
+ * of files in order to pass existence tests.
+ *
+ * @return String: the directory
+ */
+ private function setupUploadDir() {
+ global $IP;
+
+ if ( $this->keepUploads ) {
+ $dir = wfTempDir() . '/mwParser-images';
+
+ if ( is_dir( $dir ) ) {
+ return $dir;
+ }
+ } else {
+ $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
+ }
+
+ // wfDebug( "Creating upload directory $dir\n" );
+ if ( file_exists( $dir ) ) {
+ wfDebug( "Already exists!\n" );
+ return $dir;
+ }
+
+ wfMkdirParents( $dir . '/3/3a', null, __METHOD__ );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
+ wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
+
+ return $dir;
+ }
+
+ /**
+ * Restore default values and perform any necessary clean-up
+ * after each test runs.
+ */
+ private function teardownGlobals() {
+ RepoGroup::destroySingleton();
+ FileBackendGroup::destroySingleton();
+ LockManagerGroup::destroySingletons();
+ LinkCache::singleton()->clear();
+
+ foreach ( $this->savedGlobals as $var => $val ) {
+ $GLOBALS[$var] = $val;
+ }
+ }
+
+ /**
+ * Remove the dummy uploads directory
+ */
+ private function teardownUploadDir( $dir ) {
+ if ( $this->keepUploads ) {
+ return;
+ }
+
+ // delete the files first, then the dirs.
+ self::deleteFiles(
+ array(
+ "$dir/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/1280px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/30px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/400px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/40px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/960px-Foobar.jpg",
+
+ "$dir/0/09/Bad.jpg",
+
+ "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
+ )
+ );
+
+ self::deleteDirs(
+ array(
+ "$dir/3/3a",
+ "$dir/3",
+ "$dir/thumb/6/65",
+ "$dir/thumb/6",
+ "$dir/thumb/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a",
+ "$dir/thumb/3",
+
+ "$dir/0/09/",
+ "$dir/0/",
+ "$dir/thumb",
+ "$dir/math/f/a/5",
+ "$dir/math/f/a",
+ "$dir/math/f",
+ "$dir/math",
+ "$dir",
+ )
+ );
+ }
+
+ /**
+ * Delete the specified files, if they exist.
+ * @param $files Array: full paths to files to delete.
+ */
+ private static function deleteFiles( $files ) {
+ foreach ( $files as $file ) {
+ if ( file_exists( $file ) ) {
+ unlink( $file );
+ }
+ }
+ }
+
+ /**
+ * Delete the specified directories, if they exist. Must be empty.
+ * @param $dirs Array: full paths to directories to delete.
+ */
+ private static function deleteDirs( $dirs ) {
+ foreach ( $dirs as $dir ) {
+ if ( is_dir( $dir ) ) {
+ rmdir( $dir );
+ }
+ }
+ }
+
+ /**
+ * "Running test $desc..."
+ */
+ protected function showTesting( $desc ) {
+ print "Running test $desc... ";
+ }
+
+ /**
+ * Print a happy success message.
+ *
+ * @param $desc String: the test name
+ * @return Boolean
+ */
+ protected function showSuccess( $desc ) {
+ if ( $this->showProgress ) {
+ print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
+ }
+
+ return true;
+ }
+
+ /**
+ * Print a failure message and provide some explanatory output
+ * about what went wrong if so configured.
+ *
+ * @param $desc String: the test name
+ * @param $result String: expected HTML output
+ * @param $html String: actual HTML output
+ * @return Boolean
+ */
+ protected function showFailure( $desc, $result, $html ) {
+ if ( $this->showFailure ) {
+ if ( !$this->showProgress ) {
+ # In quiet mode we didn't show the 'Testing' message before the
+ # test, in case it succeeded. Show it now:
+ $this->showTesting( $desc );
+ }
+
+ print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
+
+ if ( $this->showOutput ) {
+ print "--- Expected ---\n$result\n--- Actual ---\n$html\n";
+ }
+
+ if ( $this->showDiffs ) {
+ print $this->quickDiff( $result, $html );
+ if ( !$this->wellFormed( $html ) ) {
+ print "XML error: $this->mXmlError\n";
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Run given strings through a diff and return the (colorized) output.
+ * Requires writable /tmp directory and a 'diff' command in the PATH.
+ *
+ * @param $input String
+ * @param $output String
+ * @param $inFileTail String: tailing for the input file name
+ * @param $outFileTail String: tailing for the output file name
+ * @return String
+ */
+ protected function quickDiff( $input, $output, $inFileTail = 'expected', $outFileTail = 'actual' ) {
+ # Windows, or at least the fc utility, is retarded
+ $slash = wfIsWindows() ? '\\' : '/';
+ $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
+
+ $infile = "$prefix-$inFileTail";
+ $this->dumpToFile( $input, $infile );
+
+ $outfile = "$prefix-$outFileTail";
+ $this->dumpToFile( $output, $outfile );
+
+ $shellInfile = wfEscapeShellArg( $infile );
+ $shellOutfile = wfEscapeShellArg( $outfile );
+
+ global $wgDiff3;
+ // we assume that people with diff3 also have usual diff
+ $diff = ( wfIsWindows() && !$wgDiff3 )
+ ? `fc $shellInfile $shellOutfile`
+ : `diff -au $shellInfile $shellOutfile`;
+ unlink( $infile );
+ unlink( $outfile );
+
+ return $this->colorDiff( $diff );
+ }
+
+ /**
+ * Write the given string to a file, adding a final newline.
+ *
+ * @param $data String
+ * @param $filename String
+ */
+ private function dumpToFile( $data, $filename ) {
+ $file = fopen( $filename, "wt" );
+ fwrite( $file, $data . "\n" );
+ fclose( $file );
+ }
+
+ /**
+ * Colorize unified diff output if set for ANSI color output.
+ * Subtractions are colored blue, additions red.
+ *
+ * @param $text String
+ * @return String
+ */
+ protected function colorDiff( $text ) {
+ return preg_replace(
+ array( '/^(-.*)$/m', '/^(\+.*)$/m' ),
+ array( $this->term->color( 34 ) . '$1' . $this->term->reset(),
+ $this->term->color( 31 ) . '$1' . $this->term->reset() ),
+ $text );
+ }
+
+ /**
+ * Show "Reading tests from ..."
+ *
+ * @param $path String
+ */
+ public function showRunFile( $path ) {
+ print $this->term->color( 1 ) .
+ "Reading tests from \"$path\"..." .
+ $this->term->reset() .
+ "\n";
+ }
+
+ /**
+ * Insert a temporary test article
+ * @param $name String: the title, including any prefix
+ * @param $text String: the article text
+ * @param $line Integer: the input line number, for reporting errors
+ * @param $ignoreDuplicate Boolean: whether to silently ignore duplicate pages
+ */
+ public static function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
+ global $wgCapitalLinks;
+
+ $oldCapitalLinks = $wgCapitalLinks;
+ $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
+
+ $text = self::chomp( $text );
+ $name = self::chomp( $name );
+
+ $title = Title::newFromText( $name );
+
+ if ( is_null( $title ) ) {
+ throw new MWException( "invalid title '$name' at line $line\n" );
+ }
+
+ $page = WikiPage::factory( $title );
+ $page->loadPageData( 'fromdbmaster' );
+
+ if ( $page->exists() ) {
+ if ( $ignoreDuplicate == 'ignoreduplicate' ) {
+ return;
+ } else {
+ throw new MWException( "duplicate article '$name' at line $line\n" );
+ }
+ }
+
+ $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
+
+ $wgCapitalLinks = $oldCapitalLinks;
+ }
+
+ /**
+ * Steal a callback function from the primary parser, save it for
+ * application to our scary parser. If the hook is not installed,
+ * abort processing of this file.
+ *
+ * @param $name String
+ * @return Bool true if tag hook is present
+ */
+ public function requireHook( $name ) {
+ global $wgParser;
+
+ $wgParser->firstCallInit(); // make sure hooks are loaded.
+
+ if ( isset( $wgParser->mTagHooks[$name] ) ) {
+ $this->hooks[$name] = $wgParser->mTagHooks[$name];
+ } else {
+ echo " This test suite requires the '$name' hook extension, skipping.\n";
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Steal a callback function from the primary parser, save it for
+ * application to our scary parser. If the hook is not installed,
+ * abort processing of this file.
+ *
+ * @param $name String
+ * @return Bool true if function hook is present
+ */
+ public function requireFunctionHook( $name ) {
+ global $wgParser;
+
+ $wgParser->firstCallInit(); // make sure hooks are loaded.
+
+ if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
+ $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
+ } else {
+ echo " This test suite requires the '$name' function hook extension, skipping.\n";
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Run the "tidy" command on text if the $wgUseTidy
+ * global is true
+ *
+ * @param $text String: the text to tidy
+ * @return String
+ */
+ private function tidy( $text ) {
+ global $wgUseTidy;
+
+ if ( $wgUseTidy ) {
+ $text = MWTidy::tidy( $text );
+ }
+
+ return $text;
+ }
+
+ private function wellFormed( $text ) {
+ $html =
+ Sanitizer::hackDocType() .
+ '' .
+ $text .
+ '';
+
+ $parser = xml_parser_create( "UTF-8" );
+
+ # case folding violates XML standard, turn it off
+ xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+ if ( !xml_parse( $parser, $html, true ) ) {
+ $err = xml_error_string( xml_get_error_code( $parser ) );
+ $position = xml_get_current_byte_index( $parser );
+ $fragment = $this->extractFragment( $html, $position );
+ $this->mXmlError = "$err at byte $position:\n$fragment";
+ xml_parser_free( $parser );
+
+ return false;
+ }
+
+ xml_parser_free( $parser );
+
+ return true;
+ }
+
+ private function extractFragment( $text, $position ) {
+ $start = max( 0, $position - 10 );
+ $before = $position - $start;
+ $fragment = '...' .
+ $this->term->color( 34 ) .
+ substr( $text, $start, $before ) .
+ $this->term->color( 0 ) .
+ $this->term->color( 31 ) .
+ $this->term->color( 1 ) .
+ substr( $text, $position, 1 ) .
+ $this->term->color( 0 ) .
+ $this->term->color( 34 ) .
+ substr( $text, $position + 1, 9 ) .
+ $this->term->color( 0 ) .
+ '...';
+ $display = str_replace( "\n", ' ', $fragment );
+ $caret = ' ' .
+ str_repeat( ' ', $before ) .
+ $this->term->color( 31 ) .
+ '^' .
+ $this->term->color( 0 );
+
+ return "$display\n$caret";
+ }
+
+ static function getFakeTimestamp( &$parser, &$ts ) {
+ $ts = 123;
+ return true;
+ }
+}
diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt
new file mode 100644
index 00000000..e9218dec
--- /dev/null
+++ b/tests/parser/parserTests.txt
@@ -0,0 +1,13859 @@
+# MediaWiki Parser test cases
+# Some taken from http://meta.wikimedia.org/wiki/Parser_testing
+# All (C) their respective authors and released under the GPL
+#
+# The syntax should be fairly self-explanatory.
+#
+# Currently supported test options:
+# One of the following three:
+#
+# (default) generate HTML output
+# pst apply pre-save transform
+# msg apply message transform
+#
+# Plus any combination of these:
+#
+# cat add category links
+# ill add inter-language links
+# subpage enable subpages (disabled by default)
+# noxml don't check for XML well formdness
+# title=[[XXX]] run test using article title XXX
+# language=XXX set content language to XXX for this test
+# variant=XXX set the variant of language for this test (eg zh-tw)
+# disabled do not run test
+# parsoid parsoid-only test (not run by PHP parser)
+# php php-only test (not run by the parsoid parser)
+# showtitle make the first line the title
+# comment run through Linker::formatComment() instead of main parser
+# local format section links in edit comment text as local links
+#
+# For testing purposes, temporary articles can created:
+# !!article / NAMESPACE:TITLE / !!text / ARTICLE TEXT / !!endarticle
+# where '/' denotes a newline.
+
+# This is the standard article assumed to exist.
+!! article
+Main Page
+!! text
+blah blah
+!! endarticle
+
+!!article
+Template:Foo
+!!text
+FOO
+!!endarticle
+
+!! article
+Template:Blank
+!! text
+!! endarticle
+
+!! article
+Template:pipe
+!! text
+|
+!! endarticle
+
+!!article
+MediaWiki:bad image list
+!!text
+* [[File:Bad.jpg]] except [[Nasty page]]
+!!endarticle
+
+!! article
+Template:inner list
+!! text
+* item 1
+!! endarticle
+
+!! article
+Template:tbl-start
+!! text
+{|
+!! endarticle
+
+!! article
+Template:tbl-end
+!! text
+|}
+!! endarticle
+
+!! article
+Template:!
+!! text
+|
+!! endarticle
+
+!! article
+Template:echo
+!! text
+{{{1}}}
+!! endarticle
+
+!! article
+Template:echo_with_span
+!! text
+{{{1}}}
+!! endarticle
+
+!! article
+Template:echo_with_div
+!! text
+{{{1}}}
+!! endarticle
+
+!! article
+Template:attr_str
+!! text
+{{{1}}}="{{{2}}}"
+!! endarticle
+
+!! article
+Template:table_attribs
+!! text
+
+|style="color: red"| Foo
+!! endarticle
+
+!! article
+A?b
+!! text
+Weirdo titles!
+!! endarticle
+
+###
+### Basic tests
+###
+!! test
+Blank input
+!! input
+!! result
+!! end
+
+
+!! test
+Simple paragraph
+!! input
+This is a simple paragraph.
+!! result
+This is a simple paragraph.
+
+!! end
+
+!! test
+Paragraphs with extra newline spacing
+!! input
+foo
+
+bar
+
+
+baz
+
+
+
+booz
+!! result
+foo
+
bar
+
+baz
+
+
booz
+
+!! end
+
+!! test
+Parsing an URL
+!! input
+http://fr.wikipedia.org/wiki/🍺
+
+!! result
+http://fr.wikipedia.org/wiki/🍺
+
+!! end
+
+!! test
+Simple list
+!! input
+* Item 1
+* Item 2
+!! result
+
+
+!! end
+
+!! test
+Italics and bold
+!! input
+* plain
+* plain''italic''plain
+* plain''italic''plain''italic''plain
+* plain'''bold'''plain
+* plain'''bold'''plain'''bold'''plain
+* plain''italic''plain'''bold'''plain
+* plain'''bold'''plain''italic''plain
+* plain''italic'''bold-italic'''italic''plain
+* plain'''bold''bold-italic''bold'''plain
+* plain'''''bold-italic'''italic''plain
+* plain'''''bold-italic''bold'''plain
+* plain''italic'''bold-italic'''''plain
+* plain'''bold''bold-italic'''''plain
+* plain l'''italic''plain
+* plain l''''bold''' plain
+!! result
+- plain
+
- plainitalicplain
+
- plainitalicplainitalicplain
+
- plainboldplain
+
- plainboldplainboldplain
+
- plainitalicplainboldplain
+
- plainboldplainitalicplain
+
- plainitalicbold-italicitalicplain
+
- plainboldbold-italicboldplain
+
- plainbold-italicitalicplain
+
- plainbold-italicboldplain
+
- plainitalicbold-italicplain
+
- plainboldbold-italicplain
+
- plain l'italicplain
+
- plain l'bold plain
+
+
+!! end
+
+###
+### 2-quote opening sequence tests
+###
+!! test
+Italics and bold: 2-quote opening sequence: (2,2)
+!! input
+''foo''
+!! result
+foo
+
+!!end
+
+
+!! test
+Italics and bold: 2-quote opening sequence: (2,3)
+!! input
+''foo'''
+!! result
+foo'
+
+!!end
+
+
+!! test
+Italics and bold: 2-quote opening sequence: (2,4)
+!! input
+''foo''''
+!! result
+foo''
+
+!!end
+
+
+!! test
+Italics and bold: 2-quote opening sequence: (2,5)
+!! input
+''foo'''''
+!! result
+foo
+
+!!end
+
+
+###
+### 3-quote opening sequence tests
+###
+
+!! test
+Italics and bold: 3-quote opening sequence: (3,2)
+!! input
+'''foo''
+!! result
+'foo
+
+!!end
+
+
+!! test
+Italics and bold: 3-quote opening sequence: (3,3)
+!! input
+'''foo'''
+!! result
+foo
+
+!!end
+
+
+!! test
+Italics and bold: 3-quote opening sequence: (3,4)
+!! input
+'''foo''''
+!! result
+foo'
+
+!!end
+
+
+!! test
+Italics and bold: 3-quote opening sequence: (3,5)
+!! input
+'''foo'''''
+!! result
+foo
+
+!!end
+
+
+###
+### 4-quote opening sequence tests
+###
+
+!! test
+Italics and bold: 4-quote opening sequence: (4,2)
+!! input
+''''foo''
+!! result
+''foo
+
+!!end
+
+
+!! test
+Italics and bold: 4-quote opening sequence: (4,3)
+!! input
+''''foo'''
+!! result
+'foo
+
+!!end
+
+
+!! test
+Italics and bold: 4-quote opening sequence: (4,4)
+!! input
+''''foo''''
+!! result
+'foo'
+
+!!end
+
+
+!! test
+Italics and bold: 4-quote opening sequence: (4,5)
+!! input
+''''foo'''''
+!! result
+'foo
+
+!!end
+
+
+###
+### 5-quote opening sequence tests
+###
+
+!! test
+Italics and bold: 5-quote opening sequence: (5,2)
+!! input
+'''''foo''
+!! result
+foo
+
+!!end
+
+
+!! test
+Italics and bold: 5-quote opening sequence: (5,3)
+!! input
+'''''foo'''
+!! result
+foo
+
+!!end
+
+
+!! test
+Italics and bold: 5-quote opening sequence: (5,4)
+!! input
+'''''foo''''
+!! result
+foo'
+
+!!end
+
+
+!! test
+Italics and bold: 5-quote opening sequence: (5,5)
+!! input
+'''''foo'''''
+!! result
+foo
+
+!!end
+
+###
+### multiple quote sequences in a line
+###
+!! test
+Italics and bold: multiple quote sequences: (2,4,2)
+!! input
+''foo''''bar''
+!! result
+foo'bar
+
+!!end
+
+
+!! test
+Italics and bold: multiple quote sequences: (2,4,3)
+!! input
+''foo''''bar'''
+!! result
+foo'bar
+
+!!end
+
+
+!! test
+Italics and bold: multiple quote sequences: (2,4,4)
+!! input
+''foo''''bar''''
+!! result
+foo'bar'
+
+!!end
+
+
+!! test
+Italics and bold: multiple quote sequences: (3,4,2)
+!! input
+'''foo''''bar''
+!! result
+foo'bar
+
+!!end
+
+
+!! test
+Italics and bold: multiple quote sequences: (3,4,3)
+!! input
+'''foo''''bar'''
+!! result
+foo'bar
+
+!!end
+
+###
+### other quote tests
+###
+!! test
+Italics and bold: other quote tests: (2,3,5)
+!! input
+''this is about '''foo's family'''''
+!! result
+this is about foo's family
+
+!!end
+
+
+!! test
+Italics and bold: other quote tests: (2,(3,3),2)
+!! input
+''this is about '''foo's''' family''
+!! result
+this is about foo's family
+
+!!end
+
+
+!! test
+Italics and bold: other quote tests: (3,2,3,2)
+!! input
+'''this is about ''foo'''s family''
+!! result
+this is about foos family
+
+!!end
+
+
+!! test
+Italics and bold: other quote tests: (3,2,3,3)
+!! input
+'''this is about ''foo'''s family'''
+!! result
+'this is about foos family
+
+!!end
+
+
+!! test
+Italics and bold: other quote tests: (3,(2,2),3)
+!! input
+'''this is about ''foo's'' family'''
+!! result
+this is about foo's family
+
+!!end
+
+
+!! test
+Italicized possessive
+!! input
+The ''[[Main Page]]'''s talk page.
+!! result
+The Main Page's talk page.
+
+!! end
+
+###
+### Non-html5 tags
+###
+
+!! test
+Non-html5 tags should be accepted
+!! input
+''foo''
+''foo''
+''foo''
+''foo''
+''foo''
+!! result
+foo
+foo
+foo
+foo
+foo
+
+!! end
+
+###
+### test cases
+###
+
+!! test
+ unordered list
+!! input
+* This is not an unordered list item.
+!! result
+* This is not an unordered list item.
+
+!! end
+
+!! test
+ spacing
+!! input
+Lorem ipsum dolor
+
+sed abit.
+ sed nullum.
+
+:and a colon
+
+!! result
+Lorem ipsum dolor
+
+sed abit.
+ sed nullum.
+
+:and a colon
+
+
+!! end
+
+!! test
+nowiki 3
+!! input
+:There is not nowiki.
+:There is nowiki.
+
+#There is not nowiki.
+#There is nowiki.
+
+*There is not nowiki.
+*There is nowiki.
+!! result
+- There is not nowiki.
+
- There is nowiki.
+
+- There is not nowiki.
+
- There is nowiki.
+
+- There is not nowiki.
+
- There is nowiki.
+
+
+!! end
+
+!! test
+Entities inside
+!! input
+<
+!! result
+<
+
+!! end
+
+
+###
+### Comments
+###
+!! test
+Comments and Indent-Pre
+!! input
+ asdf
+
+ asdf
+
+
+ asdf
+xyz
+
+ asdf
+ xyz
+!! result
+asdf
+
+asdf
+
+asdf
+
+xyz
+
+asdf
+xyz
+
+!! end
+
+!! test
+Comment test 2a
+!! input
+asdf
+
+jkl
+!! result
+asdf
+jkl
+
+!! end
+
+!! test
+Comment test 2b
+!! input
+asdf
+
+
+jkl
+!! result
+asdf
+
jkl
+
+!! end
+
+!! test
+Comment test 3
+!! input
+asdf
+
+
+jkl
+!! result
+asdf
+jkl
+
+!! end
+
+!! test
+Comment test 4
+!! input
+asdfjkl
+!! result
+asdfjkl
+
+!! end
+
+!! test
+Comment spacing
+!! input
+a
+ b
+c
+!! result
+a
+
+ b
+
+c
+
+!! end
+
+!! test
+Comment whitespace
+!! input
+
+!! result
+
+!! end
+
+!! test
+Comment semantics and delimiters
+!! input
+
+!! result
+
+!! end
+
+!! test
+Comment semantics and delimiters, redux
+!! input
+
+!! result
+
+!! end
+
+!! test
+Comment semantics and delimiters: directors cut
+!! input
+-->
+!! result
+-->
+
+!! end
+
+!! test
+Comment semantics: nesting
+!! input
+-->
+!! result
+-->
+
+!! end
+
+!! test
+Comment semantics: unclosed comment at end
+!! input
+oo}}
+!! result
+FOO
+
+!! end
+
+!! test
+Comment on its own line post-expand
+!! input
+a
+{{blank}}
+b
+!! result
+a
+
b
+
+!! end
+
+!! test
+Comment on its own line post-expand with non-significant whitespace
+!! input
+a
+ {{blank}}
+b
+!! result
+a
+
b
+
+!! end
+
+###
+### paragraph wraping tests
+###
+!! test
+No block tags
+!! input
+a
+
+b
+!! result
+a
+
b
+
+!! end
+!! test
+Block tag on one line
+!! input
+a foo
+
+b
+!! result
+a foo
+b
+
+!! end
+
+!! test
+Block tag on both lines
+!! input
+a foo
+
+b foo
+!! result
+a foo
+b foo
+
+!! end
+
+!! test
+Multiple lines without block tags
+!! input
+foo
a
+b
+c
+d e
+x foo
z
+!! result
+foo
a
+b
+c
+d e
+
+x foo
z
+
+!! end
+
+!! test
+Empty lines between block tags to test open p-tags are closed between the block tags
+!! input
+
+
+
+a
+
+b
+!! result
+
+
+
+a
+b
+
+!! end
+
+###
+### Preformatted text
+###
+!! test
+Preformatted text
+!! input
+ This is some
+ Preformatted text
+ With ''italic''
+ And '''bold'''
+ And a [[Main Page|link]]
+!! result
+This is some
+Preformatted text
+With italic
+And bold
+And a link
+
+!! end
+
+!! test
+Ident preformatting with inline content
+!! input
+ a
+ ''b''
+!! result
+a
+b
+
+!! end
+
+!! test
+ with inside (compatibility with 1.6 and earlier)
+!! input
+
+
+
+
+
+!! result
+
+<b>
+<cite>
+<em>
+
+
+!! end
+
+!! test
+Regression with preformatted in
+!! input
+
+ Blah
+
+!! result
+
+Blah
+
+
+
+!! end
+
+# Expected output in the following test is not really expected (there should be
+# in the output) -- it's only testing for well-formedness.
+!! test
+Bug 6200: Preformatted in
+!! input
+
+ Blah
+
+!! result
+
+ Blah
+
+
+!! end
+
+!! test
+ with attributes (bug 3202)
+!! input
+Bluescreen of WikiDeath
+!! result
+Bluescreen of WikiDeath
+
+!! end
+
+!! test
+ with width attribute (bug 3202)
+!! input
+Narrow screen goodies
+!! result
+Narrow screen goodies
+
+!! end
+
+!! test
+ with forbidden attribute (bug 3202)
+!! input
+Narrow screen goodies
+!! result
+Narrow screen goodies
+
+!! end
+
+!! test
+Entities inside
+!! input
+<
+!! result
+<
+
+!! end
+
+!! test
+ with forbidden attribute values (bug 3202)
+!! input
+Narrow screen goodies
+!! result
+Narrow screen goodies
+
+!! end
+
+!! test
+ inside (bug 13238)
+!! input
+
+
+
+
+
+
+Foo
+!! result
+
+<nowiki>
+
+
+
+
+<nowiki>Foo</nowiki>
+
+!! end
+
+!! test
+ and preference (first one wins)
+!! input
+
+
+
+
+
+
+
+
+
+
+
+
+
+!! result
+
+<nowiki>
+
+</nowiki>
+</pre>
+
+<pre>
+<nowiki>
+</pre>
+
+</pre>
+
+!! end
+
+!! test
+
inside nowiki
+!! input
+
+!! result
+</pre>
+
+!! end
+
+!!test
+Templates: Indent-Pre: 1a. Templates that break a line should suppress
+!!input
+ {{echo|}}
+!!result
+
+!!end
+
+!!test
+Templates: Indent-Pre: 1b. Templates that break a line should suppress
+!!input
+ {{echo|
+foo}}
+!!result
+foo
+
+!!end
+
+!! test
+Templates: Indent-Pre: 1c: Wrapping should be based on expanded content
+!! input
+ {{echo|a
+b}}
+!!result
+a
+
+b
+
+!!end
+
+!! test
+Templates: Indent-Pre: 1d: Wrapping should be based on expanded content
+!! input
+ {{echo|a
+b
+c
+ d
+e
+}}
+!!result
+a
+
+b
+c
+
+d
+
+e
+
+!!end
+
+!!test
+Templates: Indent-Pre: 1e. Wrapping should be based on expanded content
+!!input
+{{echo| foo}}
+
+{{echo| foo}}{{echo| bar}}
+
+{{echo| foo}}
+{{echo| bar}}
+
+{{echo| foo}}
+
+{{echo| foo}}
+
+{{echo|{{echo| }}bar}}
+!!result
+foo
+
+foo bar
+
+foo
+bar
+
+foo
+
+foo
+
+bar
+
+!!end
+
+!! test
+Templates: Indent-Pre: 1f: Wrapping should be based on expanded content
+!! input
+{{echo| }}a
+
+{{echo|
+ }}a
+
+{{echo|
+ b}}
+
+{{echo|a
+ }}b
+
+{{echo|a
+}} b
+!!result
+a
+
+
+
+a
+
+
+
+b
+
+a
+
+b
+
+a
+
+b
+
+!!end
+
+!! test
+Templates: Single-line variant of parameter whitespace stripping test
+!! input
+{{echo| a}}
+
+{{echo|1= a}}
+
+{{echo|{{echo| a}}}}
+
+{{echo|1={{echo| a}}}}
+!! result
+a
+
+a
+
+a
+
+a
+
+!! end
+
+!! test
+Templates: Strip whitespace from named parameters, but not positional ones
+!! input
+{{echo|
+ foo}}
+
+{{echo|
+* foo}}
+
+{{echo| 1 =
+ foo}}
+
+{{echo| 1 =
+* foo}}
+!! result
+foo
+
+
+
+
+foo
+
+
+
+!! end
+
+###
+### Parsoid-centric tests for testing RT edge cases for pre
+###
+
+!!test
+1a. Indent-Pre and Comments
+!!input
+ a
+
+c
+!!result
+a
+
+c
+
+!!end
+
+!!test
+1b. Indent-Pre and Comments
+!!input
+ a
+
+c
+!!result
+a
+
+c
+
+!!end
+
+!!test
+1c. Indent-Pre and Comments
+!!input
+ a
+
+ a
+!!result
+ a
+
+ a
+
+!!end
+
+!!test
+2a. Indent-Pre and tables
+!!input
+ {|
+ |-
+ !h1!!h2
+ |foo||bar
+ |}
+!!result
+
+
+
+h1 |
+h2
+ |
+foo |
+bar
+ |
+
+!!end
+
+!!test
+2b. Indent-Pre and tables
+!!input
+ {|
+ |-
+|foo
+|}
+!!result
+
+
+!!end
+
+!!test
+2c. Indent-Pre and tables (bug 42252)
+!!input
+{|
+ |+ foo
+ ! | bar
+|}
+!!result
+
+
+!!end
+
+!!test
+3a. Indent-Pre and block tags (single-line html)
+!!input
+ foo
+ foo
+ foo
+!!result
+ foo
+ foo
+ foo
+
+!!end
+
+!!test
+3b. Indent-Pre and block tags (pre-content on separate line)
+!!input
+
+ foo
+
+
+
+ foo
+
+
+
+ foo
+
+
+
+ foo
+
+
+
+
+
+
+!!result
+
+ foo
+
+
+
+foo
+
+
+
+ foo
+
+
+
+
+!!end
+
+!!test
+4. Multiple spaces at start-of-line
+!!input
+ foo
+ foo
+ {|
+|foo
+|}
+!!result
+ foo
+ foo
+
+
+
+!!end
+
+!! test
+5. White-space in indent-pre
+NOTE: the white-space char on 2nd line is significant
+!! input
+ a
+
+ b
+!! result
+a
+
+b
+
+!! end
+
+###
+### HTML-pre (some to spec PHP parser behavior and some Parsoid-RT-centric)
+###
+
+!!test
+HTML-pre: 1. embedded newlines
+!!input
+foo
+
+
+foo
+
+
+
+
+foo
+
+
+
+
+
+foo
+
+!!result
+foo
+
+foo
+
+
+
+foo
+
+
+
+
+foo
+
+
+!!end
+
+!!test
+HTML-pre: 2: indented text
+!!input
+
+ foo
+
+!!result
+
+ foo
+
+
+!!end
+
+!!test
+HTML-pre: 3: other wikitext
+!!input
+
+* foo
+# bar
+= no-h =
+'' no-italic ''
+[[ NoLink ]]
+
+!!result
+
+* foo
+# bar
+= no-h =
+'' no-italic ''
+[[ NoLink ]]
+
+
+!!end
+
+###
+### Definition lists
+###
+!! test
+Simple definition
+!! input
+; name : Definition
+!! result
+- name
- Definition
+
+
+!! end
+
+!! test
+Definition list for indentation only
+!! input
+: Indented text
+!! result
+- Indented text
+
+
+!! end
+
+!! test
+Definition list with no space
+!! input
+;name:Definition
+!! result
+- name
- Definition
+
+
+!!end
+
+!! test
+Definition list with URL link
+!! input
+; http://example.com/ : definition
+!! result
+- http://example.com/
- definition
+
+
+!! end
+
+!! test
+Definition list with bracketed URL link
+!! input
+;[http://www.example.com/ Example]:Something about it
+!! result
+- Example
- Something about it
+
+
+!! end
+
+!! test
+Definition list with wikilink containing colon
+!! input
+; [[Help:FAQ]]: The least-read page on Wikipedia
+!! result
+- Help:FAQ
- The least-read page on Wikipedia
+
+
+!! end
+
+# At Brion's and JeLuF's insistence... :)
+!! test
+Definition list with news link containing colon
+!! input
+; news:alt.wikipedia.rox: This isn't even a real newsgroup!
+!! result
+- news:alt.wikipedia.rox
- This isn't even a real newsgroup!
+
+
+!! end
+
+!! test
+Malformed definition list with colon
+!! input
+; news:alt.wikipedia.rox -- don't crash or enter an infinite loop
+!! result
+- news:alt.wikipedia.rox -- don't crash or enter an infinite loop
+
+
+!! end
+
+!! test
+Definition lists: colon in external link text
+!! input
+; [http://www.wikipedia2.org/ Wikipedia : The Next Generation]: OK, I made that up
+!! result
+- Wikipedia : The Next Generation
- OK, I made that up
+
+
+!! end
+
+!! test
+Definition lists: colon in HTML attribute
+!! input
+;bold
+!! result
+- bold
+
+
+!! end
+
+!! test
+Definition lists: self-closed tag
+!! input
+;one
two : two-line fun
+!! result
+- one
two - two-line fun
+
+
+!! end
+
+!! test
+Bug 11748: Literal closing tags
+!! input
+
+- test 1
+- test test test test test
+- test 2
+- test test test test test
+
+!! result
+
+- test 1
+- test test test test test
+- test 2
+- test test test test test
+
+
+!! end
+
+!! test
+Definition and unordered list using wiki syntax nested in unordered list using html tags.
+!! input
+-
+; term : description
+* unordered
+
+
+!! result
+-
+
- term
- description
+
+
+
+
+
+!! end
+
+!! test
+
+Definition list with empty definition and following paragraph
+!! input
+; term:
+Paragraph text
+!! result
+- term
-
+
+Paragraph text
+
+!! end
+
+!! test
+Nested definition lists using html syntax
+!! input
+-
+
+- Foo
+
+
+!! result
+-
+
+- Foo
+
+
+
+!! end
+
+!! test
+Definition Lists: No nesting: Multiple dd's
+!! input
+;x
+:a
+:b
+!! result
+- x
+
- a
+
- b
+
+
+!! end
+
+!! test
+Definition Lists: Indentation: Regular
+!! input
+:i1
+::i2
+:::i3
+!! result
+- i1
+
- i2
+
- i3
+
+
+
+
+!! end
+
+!! test
+Definition Lists: Indentation: Missing 1st level
+!! input
+::i2
+:::i3
+!! result
+- i2
+
- i3
+
+
+
+
+!! end
+
+!! test
+Definition Lists: Indentation: Multi-level indent
+!! input
+:::i3
+!! result
+- i3
+
+
+
+
+!! end
+
+!! test
+Definition Lists: Hacky use to indent tables
+!! input
+::{|
+|foo
+|bar
+|}
+this text
+should be left alone
+!! result
+
+this text
+should be left alone
+
+!! end
+## The PHP parser treats : items (dd) without a corresponding ; item (dt)
+## as an empty dt item. It also ignores all but the last ";" when followed
+## by ":" later on. So, ";" are not ignored in ";;;t3" but are ignored in
+## ";;;t3 :d1". So, PHP parser behavior is a little inconsistent wrt multiple
+## ";"s.
+##
+## Ex: ";;t2 ::d2" is transformed into:
+##
+##
+## - t2
+## -
+##
+##
+## - d2
+##
+##
+##
+##
+## But, Parsoid treats "; :" as a tight atomic unit and excess ":" as plain text
+## So, the same wikitext above (;;t2 ::d2) is transformed into:
+##
+##
+## -
+##
+## - t2
+## - :d2
+##
+##
+##
+##
+## All Parsoid only definition list tests have this difference.
+##
+## See also: https://bugzilla.wikimedia.org/show_bug.cgi?id=6569
+## and http://lists.wikimedia.org/pipermail/wikitext-l/2011-November/000483.html
+
+!! test
+Table / list interaction: indented table with lists in table contents
+!! input
+:{|
+|-
+| a
+* b
+|-
+| c
+* d
+|}
+!! result
+
+
+!! end
+
+!!test
+Table / list interaction: lists nested in tables nested in indented lists
+!!input
+:{|
+|
+:a
+:b
+|
+*c
+*d
+|}
+
+*e
+*f
+!!result
+
+
+
+!!end
+
+!! test
+Definition Lists: Nesting: Multi-level (Parsoid only)
+!! options
+parsoid
+!! input
+;t1 :d1
+;;t2 ::d2
+;;;t3 :::d3
+!! result
+
+ - t1
+ - d1
+ -
+
+ - t2
+ - :d2
+ -
+
+ - t3
+ - ::d3
+
+
+
+
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Nesting: Test 2 (Parsoid only)
+!! options
+parsoid
+!! input
+;t1
+::d2
+!! result
+
+ - t1
+ -
+
+ - d2
+
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Nesting: Test 3 (Parsoid only)
+!! options
+parsoid
+!! input
+:;t1
+::::d2
+!! result
+
+ -
+
+ - t1
+ -
+
+ -
+
+ - d2
+
+
+
+
+
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Nesting: Test 4
+!! input
+::;t3
+:::d3
+!! result
+- t3
+
- d3
+
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 1
+!! input
+:;* foo
+::* bar
+:; baz
+!! result
+-
+
+- baz
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 2
+!! input
+*: d1
+*: d2
+!! result
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 3
+!! input
+*::: d1
+*::: d2
+!! result
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 4
+!! input
+*;d1 :d2
+*;d3 :d4
+!! result
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 5
+!! input
+*:d1
+*:: d2
+!! result
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 6
+!! input
+#*:d1
+#*::: d3
+!! result
+-
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 7
+!! input
+:* d1
+:* d2
+!! result
+-
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 8
+!! input
+:* d1
+::* d2
+!! result
+-
+
-
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 9
+!! input
+*;foo :bar
+!! result
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 10
+!! input
+*#;foo :bar
+!! result
+
+
+!! end
+
+
+!! test
+Definition Lists: Mixed Lists: Test 11
+!! input
+*#*#;*;;foo :bar
+*#*#;boo :baz
+!! result
+
+
+
+
+!! end
+
+
+!! test
+Definition Lists: Weird Ones: Test 1
+!! input
+*#;*::;; foo : bar (who uses this?)
+!! result
+- foo
- bar (who uses this?)
+
+
+
+
+
+
+
+
+
+!! end
+
+###
+### External links
+###
+!! test
+External links: non-bracketed
+!! input
+Non-bracketed: http://example.com
+!! result
+Non-bracketed: http://example.com
+
+!! end
+
+!! test
+External links: numbered
+!! input
+Numbered: [http://example.com]
+Numbered: [http://example.net]
+Numbered: [http://example.com]
+!! result
+Numbered: [1]
+Numbered: [2]
+Numbered: [3]
+
+!!end
+
+!! test
+External links: specified text
+!! input
+Specified text: [http://example.com link]
+!! result
+Specified text: link
+
+!!end
+
+!! test
+External links: trail
+!! input
+Linktrails should not work for external links: [http://example.com link]s
+!! result
+Linktrails should not work for external links: links
+
+!! end
+
+!! test
+External links: dollar sign in URL
+!! input
+http://example.com/1$2345
+!! result
+http://example.com/1$2345
+
+!! end
+
+!! test
+External links: dollar sign in URL (named)
+!! input
+[http://example.com/1$2345]
+!! result
+[1]
+
+!!end
+
+!! test
+External links: open square bracket forbidden in URL (bug 4377)
+!! input
+http://example.com/1[2345
+!! result
+http://example.com/1[2345
+
+!! end
+
+!! test
+External links: open square bracket forbidden in URL (named) (bug 4377)
+!! input
+[http://example.com/1[2345]
+!! result
+[2345
+
+!!end
+
+!! test
+External links: nowiki in URL link text (bug 6230)
+!!input
+[http://example.com/ ''example site'']
+!! result
+''example site''
+
+!! end
+
+!! test
+External links: newline forbidden in text (bug 6230 regression check)
+!! input
+[http://example.com/ first
+second]
+!! result
+[http://example.com/ first
+second]
+
+!!end
+
+!! test
+External links: Pipe char between url and text
+!! input
+[http://example.com | link]
+!! result
+| link
+
+!!end
+
+!! test
+External links: protocol-relative URL in brackets
+!! input
+[//example.com/ Test]
+!! result
+Test
+
+!! end
+
+!! test
+External links: protocol-relative URL in brackets without text
+!! input
+[//example.com]
+!! result
+[1]
+
+!! end
+
+!! test
+External links: protocol-relative URL in free text is left alone
+!! input
+//example.com/Foo
+!! result
+//example.com/Foo
+
+!!end
+
+!! test
+External links: protocol-relative URL in the middle of a word is left alone (bug 30269)
+!! input
+foo//example.com/Foo
+!! result
+foo//example.com/Foo
+
+!! end
+
+!! test
+External image
+!! input
+External image: http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png
+!! result
+External image:
+
+!! end
+
+!! test
+External image from https
+!! input
+External image from https: https://meta.wikimedia.org/upload/f/f1/Ncwikicol.png
+!! result
+External image from https:
+
+!! end
+
+!! test
+Link to non-http image, no img tag
+!! input
+Link to non-http image, no img tag: ftp://example.com/test.jpg
+!! result
+Link to non-http image, no img tag: ftp://example.com/test.jpg
+
+!! end
+
+!! test
+External links: terminating separator
+!! input
+Terminating separator: http://example.com/thing,
+!! result
+Terminating separator: http://example.com/thing,
+
+!! end
+
+!! test
+External links: intervening separator
+!! input
+Intervening separator: http://example.com/1,2,3
+!! result
+Intervening separator: http://example.com/1,2,3
+
+!! end
+
+!! test
+External links: old bug with URL in query
+!! input
+Old bug with URL in query: [http://example.com/thing?url=http://example.com link]
+!! result
+Old bug with URL in query: link
+
+!! end
+
+!! test
+External links: old URL-in-URL bug, mixed protocols
+!! input
+And again with mixed protocols: [ftp://example.com?url=http://example.com link]
+!! result
+And again with mixed protocols: link
+
+!!end
+
+!! test
+External links: URL in text
+!! input
+URL in text: [http://example.com http://example.com]
+!! result
+URL in text: http://example.com
+
+!! end
+
+!! test
+External links: Clickable images
+!! input
+ja-style clickable images: [http://example.com http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png]
+!! result
+ja-style clickable images:
+
+!!end
+
+!! test
+External links: raw ampersand
+!! input
+Old & use: http://x&y
+!! result
+Old & use: http://x&y
+
+!! end
+
+!! test
+External links: encoded ampersand
+!! input
+Old & use: http://x&y
+!! result
+Old & use: http://x&y
+
+!! end
+
+!! test
+External links: encoded equals (bug 6102)
+!! input
+http://example.com/?foo=bar
+!! result
+http://example.com/?foo=bar
+
+!! end
+
+!! test
+External links: [raw ampersand]
+!! input
+Old & use: [http://x&y]
+!! result
+Old & use: [1]
+
+!! end
+
+!! test
+External links: [encoded ampersand]
+!! input
+Old & use: [http://x&y]
+!! result
+Old & use: [1]
+
+!! end
+
+!! test
+External links: [encoded equals] (bug 6102)
+!! input
+[http://example.com/?foo=bar]
+!! result
+[1]
+
+!! end
+
+!! test
+External links: [IDN ignored character reference in hostname; strip it right off]
+!! input
+[http://example.com/]
+!! result
+[1]
+
+!! end
+
+# FIXME: This test (the IDN characters in the text of a link) is an inconsistency.
+# Where an external link could easily circumvent the sanitization of the text of
+# a link like this (where an IDN-ignore character is in the URL somewhere), this
+# test demands a higher standard. That's a bit strange.
+#
+# Example:
+#
+# http://example.com -> [http://example.com|http://example.com]
+# [http://example.com|http://example.com] -> [http://example.com|http://example.com]
+#
+# The first example is sanitized, but the second is not. Any security benefits
+# from this production are trivial to circumvent. Either remove this test and
+# let the parser(s) do their thing unaccosted, or fix the inconsistency and change
+# the test accordingly.
+#
+# All our love,
+# The Parsoid team.
+!! test
+External links: IDN ignored character reference in hostname; strip it right off
+!! input
+http://example.com/
+!! result
+http://example.com/
+
+!! end
+
+!! test
+External links: www.jpeg.org (bug 554)
+!! input
+http://www.jpeg.org
+!!result
+http://www.jpeg.org
+
+!! end
+
+!! test
+External links: URL within URL (original bug 2)
+!! input
+[http://www.unausa.org/newindex.asp?place=http://www.unausa.org/programs/mun.asp]
+!! result
+[1]
+
+!! end
+
+!! test
+BUG 361: URL inside bracketed URL
+!! input
+[http://www.example.com/foo http://www.example.com/bar]
+!! result
+http://www.example.com/bar
+
+!! end
+
+!! test
+BUG 361: URL within URL, not bracketed
+!! input
+http://www.example.com/foo?=http://www.example.com/bar
+!! result
+http://www.example.com/foo?=http://www.example.com/bar
+
+!! end
+
+!! test
+BUG 289: ">"-token in URL-tail
+!! input
+http://www.example.com/
+!! result
+http://www.example.com/<hello>
+
+!!end
+
+!! test
+BUG 289: literal ">"-token in URL-tail
+!! input
+http://www.example.com/html
+!! result
+http://www.example.com/html
+
+!!end
+
+!! test
+BUG 289: ">"-token in bracketed URL
+!! input
+[http://www.example.com/ stuff]
+!! result
+<hello> stuff
+
+!!end
+
+!! test
+BUG 289: literal ">"-token in bracketed URL
+!! input
+[http://www.example.com/html stuff]
+!! result
+html stuff
+
+!!end
+
+!! test
+BUG 289: literal double quote at end of URL
+!! input
+http://www.example.com/"hello"
+!! result
+http://www.example.com/"hello"
+
+!!end
+
+!! test
+BUG 289: literal double quote in bracketed URL
+!! input
+[http://www.example.com/"hello" stuff]
+!! result
+"hello" stuff
+
+!!end
+
+!! test
+External links: multiple legal whitespace is fine, Magnus. Don't break it please. (bug 5081)
+!! input
+[http://www.example.com test]
+!! result
+test
+
+!! end
+
+!! test
+External links: link text with spaces
+!! input
+[http://www.example.com a b c]
+[http://www.example.com ''a'' ''b'']
+!! result
+a b c
+a b
+
+!! end
+
+!! test
+External links: wiki links within external link (Bug 3695)
+!! input
+[http://example.com [[wikilink]] embedded in ext link]
+!! result
+wikilink embedded in ext link
+
+!! end
+
+!! test
+BUG 787: Links with one slash after the url protocol are invalid
+!! input
+http:/example.com
+
+[http:/example.com title]
+!! result
+http:/example.com
+
[http:/example.com title]
+
+!! end
+
+!! test
+Bracketed external links with template-generated invalid target
+!! input
+[{{echo|http:/example.com}} title]
+!! result
+[http:/example.com title]
+
+!! end
+
+!! test
+Bug 2702: Mismatched , and tags are invalid
+!! input
+''[http://example.com text'']
+[http://example.com '''text]'''
+''Something [http://example.com in italic'']
+''Something [http://example.com mixed''''', even bold]'''
+'''''Now [http://example.com both''''']
+!! result
+text
+text
+Something in italic
+Something mixed, even bold
+Now both
+
+!! end
+
+
+!! test
+Bug 4781: %26 in URL
+!! input
+http://www.example.com/?title=AT%26T
+!! result
+http://www.example.com/?title=AT%26T
+
+!! end
+
+# According to http://dev.w3.org/html5/spec/Overview.html#parsing-urls a plain
+# % is actually legal in HTML5. Any change in output would need testing though.
+!! test
+Bug 4781, 5267: %25 in URL
+!! input
+http://www.example.com/?title=100%25_Bran
+!! result
+http://www.example.com/?title=100%25_Bran
+
+!! end
+
+!! test
+Bug 4781, 5267: %28, %29 in URL
+!! input
+http://www.example.com/?title=Ben-Hur_%281959_film%29
+!! result
+http://www.example.com/?title=Ben-Hur_%281959_film%29
+
+!! end
+
+
+!! test
+Bug 4781: %26 in autonumber URL
+!! input
+[http://www.example.com/?title=AT%26T]
+!! result
+[1]
+
+!! end
+
+!! test
+Bug 4781, 5267: %26 in autonumber URL
+!! input
+[http://www.example.com/?title=100%25_Bran]
+!! result
+[1]
+
+!! end
+
+!! test
+Bug 4781, 5267: %28, %29 in autonumber URL
+!! input
+[http://www.example.com/?title=Ben-Hur_%281959_film%29]
+!! result
+[1]
+
+!! end
+
+
+!! test
+Bug 4781: %26 in bracketed URL
+!! input
+[http://www.example.com/?title=AT%26T link]
+!! result
+link
+
+!! end
+
+!! test
+Bug 4781, 5267: %26 in bracketed URL
+!! input
+[http://www.example.com/?title=100%25_Bran link]
+!! result
+link
+
+!! end
+
+!! test
+Bug 4781, 5267: %28, %29 in bracketed URL
+!! input
+[http://www.example.com/?title=Ben-Hur_%281959_film%29 link]
+!! result
+link
+
+!! end
+
+!! test
+External link containing double-single-quotes in text '' (bug 4598 sanity check)
+!! input
+Some [http://example.com/ pretty ''italics'' and stuff]!
+!! result
+Some pretty italics and stuff!
+
+!! end
+
+!! test
+External link containing double-single-quotes in text embedded in italics (bug 4598 sanity check)
+!! input
+''Some [http://example.com/ pretty ''italics'' and stuff]!''
+!! result
+Some pretty italics and stuff!
+
+!! end
+
+!! test
+External link containing double-single-quotes with no space separating the url from text in italics
+!! input
+[http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm''La muerte de Casagemas'' (1901) en el sitio de [[Museo Picasso (París)|Museo Picasso]].]
+!! result
+La muerte de Casagemas (1901) en el sitio de Museo Picasso.
+
+!! end
+
+!! test
+URL-encoding in URL functions (single parameter)
+!! input
+{{localurl:Some page|amp=&}}
+!! result
+/index.php?title=Some_page&=&
+
+!! end
+
+!! test
+URL-encoding in URL functions (multiple parameters)
+!! input
+{{localurl:Some page|q=?&=&}}
+!! result
+/index.php?title=Some_page&q=?&=&
+
+!! end
+
+!! test
+Brackets in urls
+!! input
+http://example.com/index.php?foozoid%5B%5D=bar
+
+http://example.com/index.php?foozoid[]=bar
+!! result
+http://example.com/index.php?foozoid%5B%5D=bar
+
http://example.com/index.php?foozoid%5B%5D=bar
+
+!! end
+
+!! test
+IPv6 urls (bug 21261)
+!! options
+disabled
+!! input
+http://[2404:130:0:1000::187:2]/index.php
+!! result
+http://[2404:130:0:1000::187:2]/index.php
+
+!! end
+
+!! test
+Non-extlinks in brackets
+!! input
+[foo]
+[foo bar]
+[foo ''bar'']
+[fool's] errand
+[fool's errand]
+[{{echo|foo}}]
+[{{echo|foo}} bar]
+[{{echo|foo}} ''bar'']
+[{{echo|foo}}l's] errand
+[{{echo|foo}}l's errand]
+[url={{echo|foo}}]
+[url=http://example.com]
+!! result
+[foo]
+[foo bar]
+[foo bar]
+[fool's] errand
+[fool's errand]
+[foo]
+[foo bar]
+[foo bar]
+[fool's] errand
+[fool's errand]
+[url=foo]
+[url=http://example.com]
+
+!! end
+
+###
+### Quotes
+###
+
+!! test
+Quotes
+!! input
+Normal text. '''Bold text.''' Normal text. ''Italic text.''
+
+Normal text. '''''Bold italic text.''''' Normal text.
+!!result
+Normal text. Bold text. Normal text. Italic text.
+
Normal text. Bold italic text. Normal text.
+
+!! end
+
+
+!! test
+Unclosed and unmatched quotes
+!! input
+'''''Bold italic text '''with bold deactivated''' in between.'''''
+
+'''''Bold italic text ''with italic deactivated'' in between.'''''
+
+'''Bold text..
+
+..spanning two paragraphs (should not work).'''
+
+'''Bold tag left open
+
+''Italic tag left open
+
+Normal text.
+
+
+'''This year''''s election ''should'' beat '''last year''''s.
+
+''Tom'''s car is bigger than ''Susan'''s.
+
+Plain ''italic'''s plain
+!! result
+Bold italic text with bold deactivated in between.
+
Bold italic text with italic deactivated in between.
+
Bold text..
+
..spanning two paragraphs (should not work).
+
Bold tag left open
+
Italic tag left open
+
Normal text.
+
This year's election should beat last year's.
+
Toms car is bigger than Susans.
+
Plain italic's plain
+
+!! end
+
+###
+### Tables
+###
+### some content taken from http://meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide:_Using_tables
+###
+
+# This should not produce as
+# is the bare minimun required by the spec, see:
+# http://www.w3.org/TR/xhtml-modularization/dtd_module_defs.html#a_module_Basic_Tables
+!! test
+A table with no data.
+!! input
+{||}
+!! result
+!! end
+
+# A table with nothing but a caption is invalid XHTML, we might want to render
+# this as caption
+!! test
+A table with nothing but a caption
+!! input
+{|
+|+ caption
+|}
+!! result
+
+
+!! end
+
+!! test
+A table with caption with default-spaced attributes and a table row
+!! input
+{|
+|+ style="color: red;" | caption1
+|-
+| foo
+|}
+!! result
+
+
+!! end
+
+!! test
+A table with captions with non-default spaced attributes and a table row
+!! input
+{|
+|+style="color: red;"|caption2
+|+ style="color: red;"| caption3
+|-
+| foo
+|}
+!! result
+
+caption2
+
+ caption3
+
+
+ foo
+ |
+
+!! end
+
+!! test
+Table td-cell syntax variations
+!! input
+{|
+| foo bar foo | baz
+| foo bar foo || baz
+| style='color:red;' | baz
+| style='color:red;' || baz
+|}
+!! result
+
+
+ baz
+ |
+ foo bar foo |
+ baz
+ |
+ baz
+ |
+ style='color:red;' |
+ baz
+ |
+
+!! end
+
+!! test
+Simple table
+!! input
+{|
+| 1 || 2
+|-
+| 3 || 4
+|}
+!! result
+
+
+!! end
+
+!! test
+Simple table but with multiple dashes for row wikitext
+!! input
+{|
+| foo
+|-----
+| bar
+|}
+!! result
+
+
+!! end
+!! test
+Multiplication table
+!! input
+{| border="1" cellpadding="2"
+|+Multiplication table
+|-
+! × !! 1 !! 2 !! 3
+|-
+! 1
+| 1 || 2 || 3
+|-
+! 2
+| 2 || 4 || 6
+|-
+! 3
+| 3 || 6 || 9
+|-
+! 4
+| 4 || 8 || 12
+|-
+! 5
+| 5 || 10 || 15
+|}
+!! result
+
+Multiplication table
+
+
+ × |
+ 1 |
+ 2 |
+ 3
+ |
+
+ 1
+ |
+ 1 |
+ 2 |
+ 3
+ |
+
+ 2
+ |
+ 2 |
+ 4 |
+ 6
+ |
+
+ 3
+ |
+ 3 |
+ 6 |
+ 9
+ |
+
+ 4
+ |
+ 4 |
+ 8 |
+ 12
+ |
+
+ 5
+ |
+ 5 |
+ 10 |
+ 15
+ |
+
+!! end
+
+!! test
+Accept "||" in table headings
+!! input
+{|
+!h1 || h2
+|}
+!! result
+
+
+!! end
+
+!! test
+Accept "||" in indented table headings
+!! input
+:{|
+!h1 || h2
+|}
+!! result
+
+
+!! end
+
+!! test
+Accept empty attributes in td/th cells (td/th cells starting with leading ||)
+!! input
+{|
+!| h1
+|| a
+|}
+!! result
+
+
+!! end
+
+!!test
+Accept "| !" at start of line in tables (ignore !-attribute)
+!!input
+{|
+|-
+| !style="color:red" | bar
+|}
+!!result
+
+
+!!end
+
+!!test
+Allow +/- in 2nd and later cells in a row, in 1st cell when td-attrs are present, or in 1st cell when there is a space between "|" and +/-
+!!input
+{|
+|-
+|style='color:red;'|+1
+|style='color:blue;'|-1
+|-
+| 1 || 2 || 3
+| 1 ||+2 ||-3
+|-
+| +1
+| -1
+|}
+!!result
+
+
+
++1
+ |
+-1
+ |
+
+ 1 |
+ 2 |
+ 3
+ |
+ 1 |
++2 |
+-3
+ |
+
+ +1
+ |
+ -1
+ |
+
+!!end
+
+!! test
+Table rowspan
+!! input
+{| border=1
+| Cell 1, row 1
+|rowspan=2| Cell 2, row 1 (and 2)
+| Cell 3, row 1
+|-
+| Cell 1, row 2
+| Cell 3, row 2
+|}
+!! result
+
+
+ Cell 1, row 1
+ |
+ Cell 2, row 1 (and 2)
+ |
+ Cell 3, row 1
+ |
+
+ Cell 1, row 2
+ |
+ Cell 3, row 2
+ |
+
+!! end
+
+!! test
+Nested table
+!! input
+{| border=1
+| α
+|
+{| bgcolor=#ABCDEF border=2
+|nested
+|-
+|table
+|}
+|the original table again
+|}
+!! result
+
+
+ α
+ |
+
+
+ |
+the original table again
+ |
+
+!! end
+
+!! test
+Invalid attributes in table cell (bug 1830)
+!! input
+{|
+|Cell:|broken
+|}
+!! result
+
+
+!! end
+
+
+!! test
+Table security: embedded pipes (http://lists.wikimedia.org/mailman/htdig/wikitech-l/2006-April/022293.html)
+!! input
+{|
+| |[ftp://|x||]" onmouseover="alert(document.cookie)">test
+!! result
+
+
+[ftp://%7Cx |
+]" onmouseover="alert(document.cookie)">test
+ |
+
+
+
+!! end
+
+
+!! test
+Indented table markup mixed with indented pre content (proposed in bug 6200)
+!! input
+
+
+
+ Text that should be rendered preformatted
+ |
+
+
+!! result
+
+
+
+Text that should be rendered preformatted
+
+ |
+
+
+
+!! end
+
+!! test
+Template-generated table cell attributes and cell content
+!! input
+{|
+|{{table_attribs}}
+|}
+!! result
+
+
+!! end
+
+!! test
+Table with row followed by newlines and table heading
+!! input
+{|
+|-
+
+! foo
+|}
+!! result
+
+
+!! end
+
+# FIXME: Preserve the attribute properly (with an empty string as value) in
+# the PHP parser. Parsoid implements the behavior below.
+!! test
+Table attributes with empty value
+!! options
+disabled
+!! input
+{|
+| style=| hello
+|}
+!! result
+
+
+!! end
+
+!! test
+Wikitext table with a lot of comments
+!! input
+{|
+
+| foo
+
+|-
+
+|
+
+|}
+!! result
+
+
+!! end
+
+###
+### Internal links
+###
+!! test
+Plain link, capitalized
+!! input
+[[Main Page]]
+!! result
+Main Page
+
+!! end
+
+!! test
+Plain link, uncapitalized
+!! input
+[[main Page]]
+!! result
+main Page
+
+!! end
+
+!! test
+Piped link
+!! input
+[[Main Page|The Main Page]]
+!! result
+The Main Page
+
+!! end
+
+!! test
+Broken link
+!! input
+[[Zigzagzogzagzig]]
+!! result
+Zigzagzogzagzig
+
+!! end
+
+!! test
+Broken link with fragment
+!! input
+[[Zigzagzogzagzig#zug]]
+!! result
+Zigzagzogzagzig#zug
+
+!! end
+
+!! test
+Special page link with fragment
+!! input
+[[Special:Version#anchor]]
+!! result
+Special:Version#anchor
+
+!! end
+
+!! test
+Nonexistent special page link with fragment
+!! input
+[[Special:ThisNameWillHopefullyNeverBeUsed#anchor]]
+!! result
+Special:ThisNameWillHopefullyNeverBeUsed#anchor
+
+!! end
+
+!! test
+Link with prefix
+!! input
+xxx[[main Page]], xxx[[Main Page]], Xxx[[main Page]] XXX[[main Page]], XXX[[Main Page]]
+!! result
+xxxmain Page, xxxMain Page, Xxxmain Page XXXmain Page, XXXMain Page
+
+!! end
+
+!! test
+Link with suffix
+!! input
+[[Main Page]]xxx, [[Main Page]]XXX, [[Main Page]]!!!
+!! result
+Main Pagexxx, Main PageXXX, Main Page!!!
+
+!! end
+
+!! article
+prefixed article
+!! text
+Some text
+!! endarticle
+
+!! test
+Bug 43661: Piped links with identical prefixes
+!! input
+[[prefixed article|prefixed articles with spaces]]
+
+[[prefixed article|prefixed articlesaoeu]]
+
+[[Main Page|Main Page test]]
+!! result
+prefixed articles with spaces
+
prefixed articlesaoeu
+
Main Page test
+
+!! end
+
+
+!! test
+Link with HTML entity in suffix / tail
+!! input
+[[Main Page]]", [[Main Page]]a
+!! result
+Main Page", Main Pagea
+
+!! end
+
+!! test
+Link with 3 brackets
+!! input
+[[[main page]]]
+!! result
+[[[main page]]]
+
+!! end
+
+!! test
+Piped link with 3 brackets
+!! input
+[[[main page|the main page]]]
+!! result
+[[[main page|the main page]]]
+
+!! end
+
+!! test
+Link with multiple pipes
+!! input
+[[Main Page|The|Main|Page]]
+!! result
+The|Main|Page
+
+!! end
+
+!! test
+Link to namespaces
+!! input
+[[Talk:Parser testing]], [[Meta:Disclaimers]]
+!! result
+Talk:Parser testing, Meta:Disclaimers
+
+!! end
+
+!! test
+Piped link to namespace
+!! input
+[[Meta:Disclaimers|The disclaimers]]
+!! result
+The disclaimers
+
+!! end
+
+!! test
+Link containing }
+!! input
+[[Usually caused by a typo (oops}]]
+!! result
+[[Usually caused by a typo (oops}]]
+
+!! end
+
+!! test
+Link containing % (not as a hex sequence)
+!! input
+[[7% Solution]]
+!! result
+7% Solution
+
+!! end
+
+!! test
+Link containing % as a single hex sequence interpreted to char
+!! input
+[[7%25 Solution]]
+!! result
+7% Solution
+
+!!end
+
+!! test
+Link containing % as a double hex sequence interpreted to hex sequence
+!! input
+[[7%2525 Solution]]
+!! result
+[[7%2525 Solution]]
+
+!!end
+
+!! test
+Link containing "#<" and "#>" % as a hex sequences- these are valid section anchors
+Example for such a section: == < ==
+!! input
+[[%23%3c]][[%23%3e]]
+!! result
+#<#>
+
+!! end
+
+!! test
+Link containing "<#" and ">#" as a hex sequences
+!! input
+[[%3c%23]][[%3e%23]]
+!! result
+[[%3c%23]][[%3e%23]]
+
+!! end
+
+!! test
+Link containing double-single-quotes '' (bug 4598)
+!! input
+[[Lista d''e paise d''o munno]]
+!! result
+Lista d''e paise d''o munno
+
+!! end
+
+!! test
+Link containing double-single-quotes '' in text (bug 4598 sanity check)
+!! input
+Some [[Link|pretty ''italics'' and stuff]]!
+!! result
+Some pretty italics and stuff!
+
+!! end
+
+!! test
+Link containing double-single-quotes '' in text embedded in italics (bug 4598 sanity check)
+!! input
+''Some [[Link|pretty ''italics'' and stuff]]!
+!! result
+Some pretty italics and stuff!
+
+!! end
+
+!! test
+Link with double quotes in title part (literal) and alternate part (interpreted)
+!! input
+[[File:Denys Savchenko ''Pentecoste''.jpg]]
+
+[[''Pentecoste'']]
+
+[[''Pentecoste''|Pentecoste]]
+
+[[''Pentecoste''|''Pentecoste'']]
+!! result
+File:Denys Savchenko Pentecoste.jpg
+
''Pentecoste''
+
Pentecoste
+
Pentecoste
+
+!! end
+
+!! test
+Broken image links with HTML captions (bug 39700)
+!! input
+[[File:Nonexistent|]]
+[[File:Nonexistent|100px|]]
+[[File:Nonexistent|<]]
+[[File:Nonexistent|abc]]
+!! result
+<script></script>
+<script></script>
+<
+abc
+
+!! end
+
+!! test
+Plain link to URL
+!! input
+[[http://www.example.com]]
+!! result
+[[1]]
+
+!! end
+
+!! test
+Plain link to URL with link text
+!! input
+[[http://www.example.com Link text]]
+!! result
+[Link text]
+
+!! end
+
+!! test
+Plain link to protocol-relative URL
+!! input
+[[//www.example.com]]
+!! result
+[[1]]
+
+!! end
+
+!! test
+Plain link to protocol-relative URL with link text
+!! input
+[[//www.example.com Link text]]
+!! result
+[Link text]
+
+!! end
+
+!! test
+Plain link to page with question mark in title
+!! input
+[[A?b]]
+
+[[A?b|Baz]]
+!! result
+A?b
+
Baz
+
+!! end
+
+
+# I'm fairly sure the expected result here is wrong.
+# We want these to be URL links, not pseudo-pages with URLs for titles....
+# However the current output is also pretty screwy.
+#
+# ----
+# I'm changing it to match the current output--it arguably makes more
+# sense in the light of the test above. Old expected result was:
+#Piped link to URL: an example URL
+#
+# But I think this test is bordering on "garbage in, garbage out" anyway.
+# -- wtm
+!! test
+Piped link to URL
+!! input
+Piped link to URL: [[http://www.example.com|an example URL]]
+!! result
+Piped link to URL: [example URL]
+
+!! end
+
+!! test
+BUG 2: [[page|http://url/]] should link to page, not http://url/
+!! input
+[[Main Page|http://url/]]
+!! result
+http://url/
+
+!! end
+
+!! test
+BUG 337: Escaped self-links should be bold
+!! options
+title=[[Bug462]]
+!! input
+[[Bug462]] [[Bug462]]
+!! result
+Bug462 Bug462
+
+!! end
+
+!! test
+Self-link to section should not be bold
+!! options
+title=[[Main Page]]
+!! input
+[[Main Page#section]]
+!! result
+Main Page#section
+
+!! end
+
+!! article
+00
+!! text
+This is 00.
+!! endarticle
+
+!!test
+Self-link to numeric title
+!!options
+title=[[0]]
+!!input
+[[0]]
+!!result
+0
+
+!!end
+
+!!test
+Link to numeric-equivalent title
+!!options
+title=[[0]]
+!!input
+[[00]]
+!!result
+00
+
+!!end
+
+!! test
+ inside a link
+!! input
+[[Main Page]] [[Main Page|the main page [it's not very good]]]
+!! result
+[[Main Page]] the main page [it's not very good]
+
+!! end
+
+!! test
+Non-breaking spaces in title
+!! input
+[[ Main Page ]]
+!! result
+ Main Page
+
+!!end
+
+!! test
+Internal link with ca linktrail, surrounded by bold apostrophes (bug 27473 primary issue)
+!! options
+language=ca
+!! input
+'''[[Main Page]]'''
+!! result
+Main Page
+
+!! end
+
+!! test
+Internal link with ca linktrail, surrounded by italic apostrophes (bug 27473 primary issue)
+!! options
+language=ca
+!! input
+''[[Main Page]]''
+!! result
+Main Page
+
+!! end
+
+!! test
+Internal link with en linktrail: no apostrophes (bug 27473)
+!! options
+language=en
+!! input
+[[Something]]'nice
+!! result
+Something'nice
+
+!! end
+
+!! test
+Internal link with ca linktrail with apostrophes (bug 27473)
+!! options
+language=ca
+!! input
+[[Something]]'nice
+!! result
+Something'nice
+
+!! end
+
+!! test
+Internal link with kaa linktrail with apostrophes (bug 27473)
+!! options
+language=kaa
+!! input
+[[Something]]'nice
+!! result
+Something'nice
+
+!! end
+
+!! test
+Parsoid-centric test: Whitespace in ext- and wiki-links should be preserved
+!! input
+[[Foo| bar]]
+
+[[Foo| ''bar'']]
+
+[http://wp.org foo]
+
+[http://wp.org ''foo'']
+!! result
+ bar
+
bar
+
foo
+
foo
+
+!! end
+
+###
+### Interwiki links (see maintenance/interwiki.sql)
+###
+
+!! test
+Inline interwiki link
+!! input
+[[MeatBall:SoftSecurity]]
+!! result
+MeatBall:SoftSecurity
+
+!! end
+
+!! test
+Inline interwiki link with empty title (bug 2372)
+!! input
+[[MeatBall:]]
+!! result
+MeatBall:
+
+!! end
+
+!! test
+Interwiki link encoding conversion (bug 1636)
+!! input
+*[[Wikipedia:ro:Olteniţa]]
+*[[Wikipedia:ro:Olteniţa]]
+!! result
+
+
+!! end
+
+!! test
+Interwiki link with fragment (bug 2130)
+!! input
+[[MeatBall:SoftSecurity#foo]]
+!! result
+MeatBall:SoftSecurity#foo
+
+!! end
+
+!! test
+Interlanguage link
+!! input
+Blah blah blah
+[[zh:Chinese]]
+!!result
+Blah blah blah
+
+!! end
+
+!! test
+Double interlanguage link
+!! input
+Blah blah blah
+[[es:Spanish]]
+[[zh:Chinese]]
+!!result
+Blah blah blah
+
+!! end
+
+!! test
+Interlanguage link, with prefix links
+!! options
+language=ln
+!! input
+Blah blah blah
+[[zh:Chinese]]
+!!result
+Blah blah blah
+
+!! end
+
+!! test
+Double interlanguage link, with prefix links (bug 8897)
+!! options
+language=ln
+!! input
+Blah blah blah
+[[es:Spanish]]
+[[zh:Chinese]]
+!!result
+Blah blah blah
+
+!! end
+
+!! test
+Parsoid-specific test: Wikilinks with should RT properly
+!! options
+language=ln
+!! input
+[[WW II]]
+!!result
+WW II
+
+!! end
+
+##
+## XHTML tidiness
+###
+
+!! test
+
to
+!! input
+1
2
3
+!! result
+1
2
3
+
+!! end
+
+!! test
+Broken br tag sanitization
+!! input
+
+!! result
+</br>
+
+!! end
+
+!! test
+Incorrecly removing closing slashes from correctly formed XHTML
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Failing to transform badly formed HTML into correct XHTML
+!! input
+
+
+
+!! result
+
+
+
+
+!!end
+
+!! test
+Handling html with a div self-closing tag
+!! input
+
+
+
+
+
+
+!! result
+
<div title />
+<div title/>
+
+
+
<div title=bar />
+<div title=bar/>
+
+
+
+
+!! end
+
+!! test
+Handling html with a br self-closing tag
+!! input
+
+
+
+
+
+
+!! result
+
+
+
+
+
+
+
+!! end
+
+!! test
+Horizontal ruler (should it add that extra space?)
+!! input
+
+
+foo
bar
+!! result
+
+
+foo
bar
+
+!! end
+
+!! test
+Horizontal ruler -- 4+ dashes render hr
+!! input
+----
+!! result
+
+
+!! end
+
+!! test
+Horizontal ruler -- eats additional dashes on the same line
+!! input
+---------
+!! result
+
+
+!! end
+
+!! test
+Horizontal ruler -- does not collaps dashes on consecutive lines
+!! input
+----
+----
+!! result
+
+
+
+!! end
+
+!! test
+Horizontal ruler -- <4 dashes render as plain text
+!! input
+---
+!! result
+
---
+
+!! end
+
+!! test
+Horizontal ruler -- Supports content following dashes on same line
+!! input
+---- Foo
+!! result
+
Foo
+
+!! end
+
+###
+### Block-level elements
+###
+!! test
+Common list
+!! input
+*Common list
+* item 2
+*item 3
+!! result
+
- Common list
+
- item 2
+
- item 3
+
+
+!! end
+
+!! test
+Numbered list
+!! input
+#Numbered list
+#item 2
+# item 3
+!! result
+
- Numbered list
+
- item 2
+
- item 3
+
+
+!! end
+
+!! test
+Mixed list
+!! input
+*Mixed list
+*# with numbers
+** and bullets
+*# and numbers
+*bullets again
+**bullet level 2
+***bullet level 3
+***#Number on level 4
+**bullet level 2
+**#Number on level 3
+**#Number on level 3
+*#number level 2
+*Level 1
+*** Level 3
+#** Level 3, but ordered
+!! result
+
- Mixed list
+
- with numbers
+
+
+- and numbers
+
+ - bullets again
+
- bullet level 2
+
- bullet level 3
+
- Number on level 4
+
+
+ - bullet level 2
+
- Number on level 3
+
- Number on level 3
+
+
+- number level 2
+
+ - Level 1
+
+
+
-
+
+
+!! end
+
+!! test
+Nested lists 1
+!! input
+*foo
+**bar
+!! result
+
+
+!! end
+
+!! test
+Nested lists 2
+!! input
+**foo
+*bar
+!! result
+
+
+!! end
+
+!! test
+Nested lists 3 (first element empty)
+!! input
+*
+**bar
+!! result
+
+
+!! end
+
+!! test
+Nested lists 4 (first element empty)
+!! input
+**
+*bar
+!! result
+
+
+!! end
+
+!! test
+Nested lists 5 (both elements empty)
+!! input
+**
+*
+!! result
+
+
+!! end
+
+!! test
+Nested lists 6 (both elements empty)
+!! input
+*
+**
+!! result
+
+
+!! end
+
+!! test
+Nested lists 7 (skip initial nesting levels)
+!! input
+*** foo
+!! result
+
+
+!! end
+
+!! test
+Nested lists 8 (multiple nesting transitions)
+!! input
+* foo
+*** bar
+** baz
+* boo
+!! result
+
+
+!! end
+
+!! test
+1. Lists with start-of-line-transparent tokens before bullets: Comments
+!! input
+*foo
+*bar
+*baz
+!! result
+
+
+!! end
+
+!! test
+2. Lists with start-of-line-transparent tokens before bullets: Template close
+!! input
+*foo {{echo|bar
+}}*baz
+!! result
+
+
+!! end
+
+!! test
+Unbalanced closing block tags break a list
+(Disabled since php parser generates broken html -- relies on Tidy to fix up)
+!! options
+disabled
+!! input
+
+*a
+*b
+!! result
+
+!! end
+
+!! test
+Unbalanced closing non-block tags don't break a list
+(Disabled since php parser generates broken html -- relies on Tidy to fix up)
+!! options
+disabled
+!! input
+
+*a
+*b
+!! result
+
+
+
+!! end
+
+!! test
+Unclosed formatting tags that straddle lists are closed and reopened
+(Disabled since php parser generates broken html -- relies on Tidy to fix up)
+!! options
+disabled
+!! input
+#
a
+# b
+!! result
+
-
a
+ -
b
+
+!! end
+
+!!test
+List embedded in a non-block tag
+(Ugly Parsoid output -- worth fixing; Disabled for PHP parser since it relies on Tidy)
+!! options
+parsoid
+!!input
+
+* foo
+
+!!result
+
+
+
+
+
+!!end
+
+!! test
+List items are not parsed correctly following a
block (bug 785)
+!! input
+* foo
+* bar
+* zar
+!! result
+
+
+!! end
+
+!! test
+List items from template
+!! input
+
+{{inner list}}
+* item 2
+
+* item 0
+{{inner list}}
+* item 2
+
+* item 0
+* notSOL{{inner list}}
+* item 2
+!! result
+
+
- item 0
+
- item 1
+
- item 2
+
+
- item 0
+
- notSOL
+
- item 1
+
- item 2
+
+
+!! end
+
+!! test
+List interrupted by empty line or heading
+!! input
+* foo
+
+** bar
+== A heading ==
+* Another list item
+!! result
+
+
+
[edit] A heading
+
+
+!!end
+
+!!test
+Multiple list tags generated by templates
+!!input
+{{echo|
- }}a
+{{echo|
- }}b
+{{echo|
- }}c
+!!result
+
- a
+
- b
+
- c
+
+
+
+!!end
+
+###
+### Magic Words
+###
+
+!! test
+Magic Word: {{CURRENTDAY}}
+!! input
+{{CURRENTDAY}}
+!! result
+
1
+
+!! end
+
+!! test
+Magic Word: {{CURRENTDAY2}}
+!! input
+{{CURRENTDAY2}}
+!! result
+
01
+
+!! end
+
+!! test
+Magic Word: {{CURRENTDAYNAME}}
+!! input
+{{CURRENTDAYNAME}}
+!! result
+
Thursday
+
+!! end
+
+!! test
+Magic Word: {{CURRENTDOW}}
+!! input
+{{CURRENTDOW}}
+!! result
+
4
+
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTH}}
+!! input
+{{CURRENTMONTH}}
+!! result
+
01
+
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTHABBREV}}
+!! input
+{{CURRENTMONTHABBREV}}
+!! result
+
Jan
+
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTHNAME}}
+!! input
+{{CURRENTMONTHNAME}}
+!! result
+
January
+
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTHNAMEGEN}}
+!! input
+{{CURRENTMONTHNAMEGEN}}
+!! result
+
January
+
+!! end
+
+!! test
+Magic Word: {{CURRENTTIME}}
+!! input
+{{CURRENTTIME}}
+!! result
+
00:02
+
+!! end
+
+!! test
+Magic Word: {{CURRENTWEEK}} (@bug 4594)
+!! input
+{{CURRENTWEEK}}
+!! result
+
1
+
+!! end
+
+!! test
+Magic Word: {{CURRENTYEAR}}
+!! input
+{{CURRENTYEAR}}
+!! result
+
1970
+
+!! end
+
+!! test
+Magic Word: {{FULLPAGENAME}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{FULLPAGENAME}}
+!! result
+
User:Ævar Arnfjörð Bjarmason
+
+!! end
+
+!! test
+Magic Word: {{FULLPAGENAMEE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{FULLPAGENAMEE}}
+!! result
+
User:%C3%86var_Arnfj%C3%B6r%C3%B0_Bjarmason
+
+!! end
+
+!! test
+Magic Word: {{NAMESPACE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{NAMESPACE}}
+!! result
+
User
+
+!! end
+
+!! test
+Magic Word: {{NAMESPACEE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{NAMESPACEE}}
+!! result
+
User
+
+!! end
+
+!! test
+Magic Word: {{NAMESPACENUMBER}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{NAMESPACENUMBER}}
+!! result
+
2
+
+!! end
+
+!! test
+Magic Word: {{NUMBEROFFILES}}
+!! input
+{{NUMBEROFFILES}}
+!! result
+
2
+
+!! end
+
+!! test
+Magic Word: {{PAGENAME}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{PAGENAME}}
+!! result
+
Ævar Arnfjörð Bjarmason
+
+!! end
+
+!! test
+Magic Word: {{PAGENAME}} with metacharacters
+!! options
+title=[['foo & bar = baz']]
+!! input
+''{{PAGENAME}}''
+!! result
+
'foo & bar = baz'
+
+!! end
+
+!! test
+Magic Word: {{PAGENAME}} with metacharacters (bug 26781)
+!! options
+title=[[*RFC 1234 http://example.com/]]
+!! input
+{{PAGENAME}}
+!! result
+
*RFC 1234 http://example.com/
+
+!! end
+
+!! test
+Magic Word: {{PAGENAMEE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{PAGENAMEE}}
+!! result
+
%C3%86var_Arnfj%C3%B6r%C3%B0_Bjarmason
+
+!! end
+
+!! test
+Magic Word: {{PAGENAMEE}} with metacharacters (bug 26781)
+!! options
+title=[[*RFC 1234 http://example.com/]]
+!! input
+{{PAGENAMEE}}
+!! result
+
*RFC_1234_http://example.com/
+
+!! end
+
+!! test
+Magic Word: {{REVISIONID}}
+!! input
+{{REVISIONID}}
+!! result
+
1337
+
+!! end
+
+!! test
+Magic Word: {{SCRIPTPATH}}
+!! input
+{{SCRIPTPATH}}
+!! result
+
/
+
+!! end
+
+!! test
+Magic Word: {{SERVER}}
+!! input
+{{SERVER}}
+!! result
+
http://example.org
+
+!! end
+
+!! test
+Magic Word: {{SERVERNAME}}
+!! input
+{{SERVERNAME}}
+!! result
+
example.org
+
+!! end
+
+!! test
+Magic Word: {{SITENAME}}
+!! input
+{{SITENAME}}
+!! result
+
MediaWiki
+
+!! end
+
+!! test
+Namespace 1 {{ns:1}}
+!! input
+{{ns:1}}
+!! result
+
Talk
+
+!! end
+
+!! test
+Namespace 1 {{ns:01}}
+!! input
+{{ns:01}}
+!! result
+
Talk
+
+!! end
+
+!! test
+Namespace 0 {{ns:0}} (bug 4783)
+!! input
+{{ns:0}}
+!! result
+
+!! end
+
+!! test
+Namespace 0 {{ns:00}} (bug 4783)
+!! input
+{{ns:00}}
+!! result
+
+!! end
+
+!! test
+Namespace -1 {{ns:-1}}
+!! input
+{{ns:-1}}
+!! result
+
Special
+
+!! end
+
+!! test
+Namespace User {{ns:User}}
+!! input
+{{ns:User}}
+!! result
+
User
+
+!! end
+
+!! test
+Namespace User talk {{ns:User_talk}}
+!! input
+{{ns:User_talk}}
+!! result
+
User talk
+
+!! end
+
+!! test
+Namespace User talk {{ns:uSeR tAlK}}
+!! input
+{{ns:uSeR tAlK}}
+!! result
+
User talk
+
+!! end
+
+!! test
+Namespace File {{ns:File}}
+!! input
+{{ns:File}}
+!! result
+
File
+
+!! end
+
+!! test
+Namespace File {{ns:Image}}
+!! input
+{{ns:Image}}
+!! result
+
File
+
+!! end
+
+!! test
+Namespace (lang=de) Benutzer {{ns:User}}
+!! options
+language=de
+!! input
+{{ns:User}}
+!! result
+
Benutzer
+
+!! end
+
+!! test
+Namespace (lang=de) Benutzer Diskussion {{ns:3}}
+!! options
+language=de
+!! input
+{{ns:3}}
+!! result
+
Benutzer Diskussion
+
+!! end
+
+
+!! test
+Urlencode
+!! input
+{{urlencode:hi world?!}}
+{{urlencode:hi world?!|WIKI}}
+{{urlencode:hi world?!|PATH}}
+{{urlencode:hi world?!|QUERY}}
+!! result
+
hi+world%3F%21
+hi_world%3F!
+hi%20world%3F%21
+hi+world%3F%21
+
+!! end
+
+###
+### Magic links
+###
+!! test
+Magic links: internal link to RFC (bug 479)
+!! input
+[[RFC 123]]
+!! result
+
RFC 123
+
+!! end
+
+!! test
+Magic links: RFC (bug 479)
+!! input
+RFC 822
+!! result
+
RFC 822
+
+!! end
+
+!! test
+Magic links: ISBN (bug 1937)
+!! input
+ISBN 0-306-40615-2
+!! result
+
ISBN 0-306-40615-2
+
+!! end
+
+!! test
+Magic links: PMID incorrectly converts space to underscore
+!! input
+PMID 1234
+!! result
+
PMID 1234
+
+!! end
+
+###
+### Templates
+####
+
+!! test
+Nonexistent template
+!! input
+{{thistemplatedoesnotexist}}
+!! result
+
Template:Thistemplatedoesnotexist
+
+!! end
+
+!! test
+Template with invalid target containing tags
+!! input
+{{a
b|{{echo|foo}}|{{echo|a}}={{echo|b}}|a = b}}
+!! result
+
{{ab|foo|a=b|a = b}}
+
+!! end
+
+!! test
+Template with invalid target containing unclosed tag
+!! input
+{{a
|{{echo|foo}}|{{echo|a}}={{echo|b}}|a = b}}
+!! result
+{{a|foo|a=b|a = b}}
+
+!! end
+
+!! article
+Template:test
+!! text
+This is a test template
+!! endarticle
+
+!! test
+Simple template
+!! input
+{{test}}
+!! result
+
This is a test template
+
+!! end
+
+!! test
+Template with explicit namespace
+!! input
+{{Template:test}}
+!! result
+
This is a test template
+
+!! end
+
+
+!! article
+Template:paramtest
+!! text
+This is a test template with parameter {{{param}}}
+!! endarticle
+
+!! test
+Template parameter
+!! input
+{{paramtest|param=foo}}
+!! result
+
This is a test template with parameter foo
+
+!! end
+
+!! article
+Template:paramtestnum
+!! text
+[[{{{1}}}|{{{2}}}]]
+!! endarticle
+
+!! test
+Template unnamed parameter
+!! input
+{{paramtestnum|Main Page|the main page}}
+!! result
+
the main page
+
+!! end
+
+!! article
+Template:templatesimple
+!! text
+(test)
+!! endarticle
+
+!! article
+Template:templateredirect
+!! text
+#redirect [[Template:templatesimple]]
+!! endarticle
+
+!! article
+Template:templateasargtestnum
+!! text
+{{{{{1}}}}}
+!! endarticle
+
+!! article
+Template:templateasargtest
+!! text
+{{template{{{templ}}}}}
+!! endarticle
+
+!! article
+Template:templateasargtest2
+!! text
+{{{{{templ}}}}}
+!! endarticle
+
+!! test
+Template with template name as unnamed argument
+!! input
+{{templateasargtestnum|templatesimple}}
+!! result
+
(test)
+
+!! end
+
+!! test
+Template with template name as argument
+!! input
+{{templateasargtest|templ=simple}}
+!! result
+
(test)
+
+!! end
+
+!! test
+Template with template name as argument (2)
+!! input
+{{templateasargtest2|templ=templatesimple}}
+!! result
+
(test)
+
+!! end
+
+!! article
+Template:templateasargtestdefault
+!! text
+{{{{{templ|templatesimple}}}}}
+!! endarticle
+
+!! article
+Template:templa
+!! text
+'''templ'''
+!! endarticle
+
+!! test
+Template with default value
+!! input
+{{templateasargtestdefault}}
+!! result
+
(test)
+
+!! end
+
+!! test
+Template with default value (value set)
+!! input
+{{templateasargtestdefault|templ=templa}}
+!! result
+
templ
+
+!! end
+
+!! test
+Template redirect
+!! input
+{{templateredirect}}
+!! result
+
(test)
+
+!! end
+
+!! test
+Template with argument in separate line
+!! input
+{{ templateasargtest |
+ templ = simple }}
+!! result
+
(test)
+
+!! end
+
+!! test
+Template with complex template as argument
+!! input
+{{paramtest|
+ param ={{ templateasargtest |
+ templ = simple }}}}
+!! result
+
This is a test template with parameter (test)
+
+!! end
+
+!! test
+Template with thumb image (with link in description)
+!! input
+{{paramtest|
+ param =[[Image:noimage.png|thumb|[[no link|link]] [[no link|caption]]]]}}
+!! result
+This is a test template with parameter
+
+!! end
+
+!! article
+Template:complextemplate
+!! text
+{{{1}}} {{paramtest|
+ param ={{{param}}}}}
+!! endarticle
+
+!! test
+Template with complex arguments
+!! input
+{{complextemplate|
+ param ={{ templateasargtest |
+ templ = simple }}|[[Template:complextemplate|link]]}}
+!! result
+
link This is a test template with parameter (test)
+
+!! end
+
+!! test
+BUG 553: link with two variables in a piped link
+!! input
+{|
+|[[{{{1}}}|{{{2}}}]]
+|}
+!! result
+
+
+!! end
+
+!! test
+Magic variable as template parameter
+!! input
+{{paramtest|param={{SITENAME}}}}
+!! result
+
This is a test template with parameter MediaWiki
+
+!! end
+
+!! article
+Template:linktest
+!! text
+[[{{{param}}}|link]]
+!! endarticle
+
+!! test
+Template parameter as link source
+!! input
+{{linktest|param=Main Page}}
+!! result
+
link
+
+!! end
+
+!!test
+Template-generated attribute string (k='v')
+!!input
+
bar
+!!result
+
bar
+
+!!end
+
+!!article
+Template:paramtest2
+!! text
+including another template, {{paramtest|param={{{arg}}}}}
+!! endarticle
+
+!! test
+Template passing argument to another template
+!! input
+{{paramtest2|arg='hmm'}}
+!! result
+
including another template, This is a test template with parameter 'hmm'
+
+!! end
+
+!! article
+Template:Linktest2
+!! text
+Main Page
+!! endarticle
+
+!! test
+Template as link source
+!! input
+[[{{linktest2}}]]
+
+[[{{linktest2}}|Main Page]]
+
+[[{{linktest2}}]]Page
+!! result
+
Main Page
+
Main Page
+
Main PagePage
+
+!! end
+
+
+!! article
+Template:loop1
+!! text
+{{loop2}}
+!! endarticle
+
+!! article
+Template:loop2
+!! text
+{{loop1}}
+!! endarticle
+
+!! test
+Template infinite loop
+!! input
+{{loop1}}
+!! result
+
Template loop detected: Template:Loop1
+
+!! end
+
+!! test
+Template from main namespace
+!! input
+{{:Main Page}}
+!! result
+
blah blah
+
+!! end
+
+!! article
+Template:table
+!! text
+{|
+| 1 || 2
+|-
+| 3 || 4
+|}
+!! endarticle
+
+!! test
+BUG 529: Template with table, not included at beginning of line
+!! input
+foo {{table}}
+!! result
+
foo
+
+
+
+!! end
+
+!! test
+BUG 523: Template shouldn't eat newline (or add an extra one before table)
+!! input
+foo
+{{table}}
+!! result
+
foo
+
+
+
+!! end
+
+!! test
+BUG 41: Template parameters shown as broken links
+!! input
+{{{parameter}}}
+!! result
+
{{{parameter}}}
+
+!! end
+
+!! test
+Template with targets containing wikilinks
+!! input
+{{[[foo]]}}
+
+{{[[{{echo|foo}}]]}}
+
+{{{{echo|[[foo}}]]}}
+!! result
+
{{foo}}
+
{{foo}}
+
{{[[foo}}]]
+
+!! end
+
+!! article
+Template:MSGNW test
+!! text
+''None'' of '''this''' should be
+* interpreted
+ but rather passed unmodified
+{{test}}
+!! endarticle
+
+# hmm, fix this or just deprecate msgnw and document its behavior?
+!! test
+msgnw keyword
+!! options
+disabled
+!! input
+{{msgnw:MSGNW test}}
+!! result
+
''None'' of '''this''' should be
+* interpreted
+ but rather passed unmodified
+{{test}}
+
+!! end
+
+!! test
+int keyword
+!! input
+{{int:youhavenewmessages|lots of money|not!}}
+!! result
+
You have lots of money (not!).
+
+!! end
+
+!! article
+Template:Includes
+!! text
+Foo
zarbar
+!! endarticle
+
+!! test
+
and being included
+!! input
+{{Includes}}
+!! result
+Foobar
+
+!! end
+
+!! article
+Template:Includes2
+!! text
+Foobar
+!! endarticle
+
+!! test
+ being included
+!! input
+{{Includes2}}
+!! result
+Foo
+
+!! end
+
+
+!! article
+Template:Includes3
+!! text
+Foobarzar
+!! endarticle
+
+!! test
+ and being included
+!! input
+{{Includes3}}
+!! result
+Foo
+
+!! end
+
+!! test
+ and on a page
+!! input
+Foozarbar
+!! result
+Foozar
+
+!! end
+
+!! test
+Un-closed
+!! input
+
+!! result
+!! end
+
+!! test
+ on a page
+!! input
+Foobar
+!! result
+Foobar
+
+!! end
+
+!! test
+Un-closed
+!! input
+
+!! result
+!! end
+
+!!test
+Self-closed noinclude, includeonly, onlyinclude tags
+!!input
+
+
+
+!!result
+
+
+!!end
+
+!!test
+Unbalanced includeonly and noinclude tags
+!!input
+{|
+|a
+|b
+|c
+|d
+|}
+!!result
+
+
+a
+ |
+b
+ |
+c</includeonly>
+ |
+d</includeonly></includeonly>
+ |
+
+!!end
+
+!! article
+Template:Includeonly section
+!! text
+
+==Includeonly section==
+
+==Section T-1==
+!!endarticle
+
+!! test
+Bug 6563: Edit link generation for section shown by
+!! input
+{{includeonly section}}
+!! result
+[edit] Includeonly section
+[edit] Section T-1
+
+!! end
+
+# Uses same input as the contents of [[Template:Includeonly section]]
+!! test
+Bug 6563: Section extraction for section shown by
+!! options
+section=T-2
+!! input
+
+==Includeonly section==
+
+==Section T-2==
+!! result
+==Section T-2==
+!! end
+
+!! test
+Bug 6563: Edit link generation for section suppressed by
+!! input
+
+==Includeonly section==
+
+==Section 1==
+!! result
+[edit] Section 1
+
+!! end
+
+!! test
+Bug 6563: Section extraction for section suppressed by
+!! options
+section=1
+!! input
+
+==Includeonly section==
+
+==Section 1==
+!! result
+==Section 1==
+!! end
+
+!! test
+Un-closed
+!! input
+
+!! result
+!! end
+
+###
+### and in attributes
+###
+!!test
+0. includeonly around the entire attribute
+!!input
+id="v1"id="v2">bar
+!!result
+bar
+
+!!end
+
+!!test
+1. includeonly in html attr key
+!!input
+idabout="foo">bar
+!!result
+bar
+
+!!end
+
+!!test
+2. includeonly in html attr value
+!!input
+bar
+"v1""v2">bar
+!!result
+bar
+bar
+
+!!end
+
+!!test
+3. includeonly in part of an attr value
+!!input
+bar
+!!result
+bar
+
+!!end
+
+###
+### Testing parsing of templates where a template arg
+### has the same name as the template itself.
+###
+
+!! article
+Template:quote
+!! text
+{{{quote|{{{1}}}}}}
+!! endarticle
+
+!!test
+Templates: Template Name/Arg clash: 1. Use of positional param
+!!input
+{{quote|foo}}
+!!result
+foo
+
+!!end
+
+!!test
+Templates: Template Name/Arg clash: 2. Use of named param
+!!input
+{{quote|quote=foo}}
+!!result
+foo
+
+!!end
+
+!!test
+Templates: Template Name/Arg clash: 3. Use of named param with empty input
+!!input
+{{quote|quote}}
+!!result
+quote
+
+!!end
+
+###
+### Parsoid-centric tests to stress Parsoid's ability to RT them unchanged
+###
+
+!!test
+Templates: 1. Simple use
+!!input
+{{echo|Foo}}
+!!result
+Foo
+
+!!end
+
+!!test
+Templates: 2. Inside a block tag
+!!input
+{{echo|Foo}}
+!!result
+Foo
+
+!!end
+
+!!test
+Templates: P-wrapping: 1a. Templates on consecutive lines
+!!input
+{{echo|Foo}}
+{{echo|bar}}
+!!result
+Foo
+bar
+
+!!end
+
+!!test
+Templates: P-wrapping: 1b. Templates on consecutive lines
+!!input
+Foo
+
+{{echo|bar}}
+{{echo|baz}}
+!!result
+Foo
+
bar
+baz
+
+!!end
+
+!!test
+Templates: P-wrapping: 1c. Templates on consecutive lines
+!!input
+{{echo|Foo}}
+{{echo|bar}} baz
+!!result
+Foo
+
+bar baz
+
+!!end
+
+!!test
+Templates: Inline Text: 1. Multiple tmeplate uses
+!!input
+{{echo|Foo}}bar{{echo|baz}}
+!!result
+Foobarbaz
+
+!!end
+
+!!test
+Templates: Inline Text: 2. Back-to-back template uses
+!!input
+{{echo|Foo}}{{echo|bar}}
+!!result
+Foobar
+
+!!end
+
+!!test
+Templates: Block Tags: 1. Multiple template uses
+!!input
+{{echo|Foo
}}bar
{{echo|baz
}}
+!!result
+Foo
bar
baz
+
+!!end
+
+!!test
+Templates: Block Tags: 2. Back-to-back template uses
+!!input
+{{echo|Foo
}}{{echo|bar
}}
+!!result
+Foo
bar
+
+!!end
+
+!!test
+Templates: Links: 1. Simple example
+!!input
+{{echo|[[Foo|bar]]}}
+!!result
+bar
+
+!!end
+
+!!test
+Templates: Links: 2. Generation of link href
+!!input
+[[{{echo|Foo}}|bar]]
+!!result
+bar
+
+!!end
+
+!!test
+Templates: Links: 3. Generation of part of a link href
+!!input
+[[Fo{{echo|o}}|bar]]
+
+[[Foo{{echo|bar}}]]
+
+[[Foo{{echo|bar}}baz]]
+
+[[Foo{{echo|bar}}|bar]]
+
+[[:Foo{{echo|bar}}]]
+
+[[:Foo{{echo|bar}}|bar]]
+!!result
+bar
+
Foobar
+
Foobarbaz
+
bar
+
Foobar
+
bar
+
+!!end
+
+!!test
+Templates: Links: 4. Multiple templates generating link href
+!!input
+[[{{echo|F}}{{echo|o}}ob{{echo|ar}}]]
+!!result
+Foobar
+
+!!end
+
+!!test
+Templates: Links: 5. Generation of link text
+!!input
+[[Foo|{{echo|bar}}]]
+!!result
+bar
+
+!!end
+
+!!test
+Templates: Links: 5. Nested templates (only outermost template should be marked)
+!!input
+{{echo|[[{{echo|Foo}}|bar]]}}
+!!result
+bar
+
+!!end
+
+!!test
+Templates: HTML Tag: 1. Generation of HTML attr. key
+!!input
+foo
+!!result
+foo
+
+!!end
+
+!!test
+Templates: HTML Tag: 2. Generation of HTML attr. value
+!!input
+foo
+!!result
+foo
+
+!!end
+
+!!test
+Templates: HTML Tag: 3. Generation of HTML attr key and value
+!!input
+foo
+!!result
+foo
+
+!!end
+
+!!test
+Templates: HTML Tag: 4. Generation of starting piece of HTML attr value
+!!input
+foo
+!!result
+foo
+
+!!end
+
+!!test
+Templates: HTML Tag: 5. Generation of middle piece of HTML attr value
+!!input
+foo
+!!result
+foo
+
+!!end
+
+!!test
+Templates: HTML Tag: 6. Generation of end piece of HTML attr value
+!!input
+foo
+!!result
+foo
+
+!!end
+
+!!test
+Templates: HTML Tables: 1. Generating start of a HTML table
+!!input
+{{echo|
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 2a. Generating middle of a HTML table
+!!input
+
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 2b. Generating middle of a HTML table
+!!input
+
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 3. Generating end of a HTML table
+!!input
+}}
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 4a. Generating a single tag of a HTML table
+!!input
+{{echo|
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 4b. Generating a single tag of a HTML table
+!!input
+
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 4c. Generating a single tag of a HTML table
+!!input
+
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 4d. Generating a single tag of a HTML table
+!!input
+
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 4e. Generating a single tag of a HTML table
+!!input
+
+!!result
+
+
+!!end
+
+!!test
+Templates: HTML Tables: 4f. Generating a single tag of a HTML table
+!!input
+}}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 1a. Fostering of entire template content
+!!input
+{|
+{{echo|a}}
+|}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 1b. Fostering of entire template content
+!!input
+{|
+{{echo|}}
+foo
+{{echo|
}}
+|}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 2. Fostering of partial template content
+!!input
+{|
+{{echo|a
+b
}}
+|}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 3. td-content via multiple templates
+!!input
+{|
+{{echo|{{pipe}}a}}{{echo|b}}
+|}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 4. Templated tags, no content
+!!input
+{{tbl-start}}
+{{tbl-end}}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 5. Templated tags, regular td-tags
+!!input
+{{tbl-start}}
+|foo
+{{tbl-end}}
+!!result
+
+
+!!end
+
+!!test
+Templates: Wiki Tables: 6. Templated tags, templated td-tags
+!!input
+{{tbl-start}}
+{{!}}foo
+{{tbl-end}}
+!!result
+
+
+!!end
+
+!!test
+Templates: Lists: Multi-line list-items via templates
+!!input
+*{{echo|a {{nonexistent|
+unused}}}}
+*{{echo|b {{nonexistent|
+unused}}}}
+!!result
+
+
+!!end
+
+!!test
+Templates: Ugly nesting: 1. Quotes opened/closed across templates (echo)
+!!input
+{{echo|''a}}{{echo|b''c''d}}{{echo|''e}}
+!!result
+abcde
+
+!!end
+
+!!test
+Templates: Ugly nesting: 2. Quotes opened/closed across templates (echo_with_span)
+(PHP parser generates misnested html)
+!! options
+disabled
+!!input
+{{echo_with_span|''a}}{{echo_with_span|b''c''d}}{{echo_with_span|''e}}
+!!result
+abcde
+!!end
+
+!!test
+Templates: Ugly nesting: 3. Quotes opened/closed across templates (echo_with_div)
+(PHP parser generates misnested html)
+!! options
+disabled
+!!input
+{{echo_with_div|''a}}{{echo_with_div|b''c''d}}{{echo_with_div|''e}}
+!!result
+a
+bcd
+e
+!!end
+
+!!test
+Templates: Ugly nesting: 4. Divs opened/closed across templates
+!!input
+ab{{echo|c
d}}e
+!!result
+abc
de
+
+!!end
+
+!!test
+Templates: Ugly templates: 1. Navbox template parses badly leading to table misnesting
+(Parsoid-centric)
+!! options
+parsoid
+!!input
+{|
+|{{echo|foo}}
+|bar
+|}
+!!result
+
+bar
+
+!!end
+
+!!test
+Templates: Ugly templates: 2. Navbox template parses badly leading to table misnesting
+(Parsoid-centric)
+!! options
+parsoid
+!!input
+
+
+
+ }} |
+ bar |
+ 2. {{echo|baz |
}}
+
+
+ abc |
+
+
+
+
+
+ xyz |
+
+
+!!result
+
+
+
+ abc
+
+
+
+
+
+ xyz
+
+
+!!end
+
+!! test
+Templates: Ugly templates: 3. newline-only template parameter
+!! input
+foo {{echo|
+}}
+!! result
+foo
+
+!! end
+
+# This looks like a bug: a single newline triggers p/br for some reason.
+!! test
+Templates: Ugly templates: 4. newline-only template parameter inconsistency
+!! input
+{{echo|
+}}
+!! result
+
+
+!! end
+
+
+!!test
+Parser Functions: 1. Simple example
+!!input
+{{uc:foo}}
+!!result
+FOO
+
+!!end
+
+!!test
+Parser Functions: 2. Nested use (only outermost should be marked up)
+!!input
+{{uc:{{lc:FOO}}}}
+!!result
+FOO
+
+!!end
+
+###
+### Pre-save transform tests
+###
+!! test
+pre-save transform: subst:
+!! options
+PST
+!! input
+{{subst:test}}
+!! result
+This is a test template
+!! end
+
+!! test
+pre-save transform: normal template
+!! options
+PST
+!! input
+{{test}}
+!! result
+{{test}}
+!! end
+
+!! test
+pre-save transform: nonexistent template
+!! options
+PST
+!! input
+{{thistemplatedoesnotexist}}
+!! result
+{{thistemplatedoesnotexist}}
+!! end
+
+
+!! test
+pre-save transform: subst magic variables
+!! options
+PST
+!! input
+{{subst:SITENAME}}
+!! result
+MediaWiki
+!! end
+
+# This is bug 89, which I fixed. -- wtm
+!! test
+pre-save transform: subst: templates with parameters
+!! options
+pst
+!! input
+{{subst:paramtest|param="something else"}}
+!! result
+This is a test template with parameter "something else"
+!! end
+
+!! article
+Template:nowikitest
+!! text
+'''not wiki'''
+!! endarticle
+
+!! test
+pre-save transform: nowiki in subst (bug 1188)
+!! options
+pst
+!! input
+{{subst:nowikitest}}
+!! result
+'''not wiki'''
+!! end
+
+
+!! article
+Template:commenttest
+!! text
+This template has in it.
+!! endarticle
+
+!! test
+pre-save transform: comment in subst (bug 1936)
+!! options
+pst
+!! input
+{{subst:commenttest}}
+!! result
+This template has in it.
+!! end
+
+!! test
+pre-save transform: unclosed tag
+!! options
+pst noxml
+!! input
+'''not wiki'''
+!! result
+'''not wiki'''
+!! end
+
+!! test
+pre-save transform: mixed tag case
+!! options
+pst noxml
+!! input
+'''not wiki'''
+!! result
+'''not wiki'''
+!! end
+
+!! test
+pre-save transform: unclosed comment in
+!! options
+pst noxml
+!! input
+wikinowiki
+!!result
+
+!!end
+
+!! test
+pre-save transform: comment containing extension
+!! options
+pst
+!! input
+
+!!result
+
+!!end
+
+!! test
+pre-save transform: comment containing nowiki
+!! options
+pst
+!! input
+
+!!result
+
+!!end
+
+!! test
+pre-save transform: in subst (bug 3298)
+!! options
+pst
+!! input
+{{subst:Includes}}
+!! result
+Foobar
+!! end
+
+!! test
+pre-save transform: in subst (bug 3298)
+!! options
+pst
+!! input
+{{subst:Includes2}}
+!! result
+Foo
+!! end
+
+!! article
+Template:SubstTest
+!!text
+{{subst:Includes}}
+!! endarticle
+
+!! article
+Template:SafeSubstTest
+!! text
+{{safesubst:Includes}}
+!! endarticle
+
+!! test
+bug 22297: safesubst: works during PST
+!! options
+pst
+!! input
+{{subst:SafeSubstTest}}{{safesubst:SubstTest}}
+!! result
+FoobarFoobar
+!! end
+
+!! test
+bug 22297: safesubst: works during normal parse
+!! input
+{{SafeSubstTest}}
+!! result
+Foobar
+
+!! end
+
+!! test:
+subst: does not work during normal parse
+!! input
+{{SubstTest}}
+!! result
+{{subst:Includes}}
+
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick")
+!! options
+pst
+!! input
+[[Article (context)|]]
+[[Bar:Article|]]
+[[:Bar:Article|]]
+[[Bar:Article (context)|]]
+[[:Bar:Article (context)|]]
+[[|Article]]
+[[|Article (context)]]
+[[Bar:X (Y) Z|]]
+[[:Bar:X (Y) Z|]]
+!! result
+[[Article (context)|Article]]
+[[Bar:Article|Article]]
+[[:Bar:Article|Article]]
+[[Bar:Article (context)|Article]]
+[[:Bar:Article (context)|Article]]
+[[Article]]
+[[Article (context)]]
+[[Bar:X (Y) Z|X (Y) Z]]
+[[:Bar:X (Y) Z|X (Y) Z]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with interwiki prefix
+!! options
+pst
+!! input
+[[interwiki:Article|]]
+[[:interwiki:Article|]]
+[[interwiki:Bar:Article|]]
+[[:interwiki:Bar:Article|]]
+!! result
+[[interwiki:Article|Article]]
+[[:interwiki:Article|Article]]
+[[interwiki:Bar:Article|Bar:Article]]
+[[:interwiki:Bar:Article|Bar:Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with parens in title
+!! options
+pst title=[[Somearticle (context)]]
+!! input
+[[|Article]]
+!! result
+[[Article (context)|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with comma in title
+!! options
+pst title=[[Someplace, Somewhere]]
+!! input
+[[|Otherplace]]
+[[Otherplace, Elsewhere|]]
+[[Otherplace, Elsewhere, Anywhere|]]
+!! result
+[[Otherplace, Somewhere|Otherplace]]
+[[Otherplace, Elsewhere|Otherplace]]
+[[Otherplace, Elsewhere, Anywhere|Otherplace]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with parens and comma
+!! options
+pst title=[[Someplace (IGNORED), Somewhere]]
+!! input
+[[|Otherplace]]
+[[Otherplace (place), Elsewhere|]]
+!! result
+[[Otherplace, Somewhere|Otherplace]]
+[[Otherplace (place), Elsewhere|Otherplace]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with comma and parens
+!! options
+pst title=[[Who, me? (context)]]
+!! input
+[[|Yes, you.]]
+[[Me, Myself, and I (1937 song)|]]
+!! result
+[[Yes, you. (context)|Yes, you.]]
+[[Me, Myself, and I (1937 song)|Me, Myself, and I]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace
+!! options
+pst title=[[Ns:Somearticle]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace and parens
+!! options
+pst title=[[Ns:Somearticle (context)]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article (context)|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace and comma
+!! options
+pst title=[[Ns:Somearticle, Context, Whatever]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article, Context, Whatever|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace, comma and parens
+!! options
+pst title=[[Ns:Somearticle, Context (context)]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article (context)|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace, parens and comma
+!! options
+pst title=[[Ns:Somearticle (IGNORED), Context]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article, Context|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with full-width parens and no space (Japanese and Chinese style, bug 30149)
+!! options
+pst
+!! input
+[[Article(context)|]]
+[[Bar:Article(context)|]]
+[[:Bar:Article(context)|]]
+[[|Article(context)]]
+[[Bar:X(Y)Z|]]
+[[:Bar:X(Y)Z|]]
+!! result
+[[Article(context)|Article]]
+[[Bar:Article(context)|Article]]
+[[:Bar:Article(context)|Article]]
+[[Article(context)]]
+[[Bar:X(Y)Z|X(Y)Z]]
+[[:Bar:X(Y)Z|X(Y)Z]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with full-width parens and space (Japanese and Chinese style, bug 30149)
+!! options
+pst
+!! input
+[[Article (context)|]]
+[[Bar:Article (context)|]]
+[[:Bar:Article (context)|]]
+[[|Article (context)]]
+[[Bar:X (Y) Z|]]
+[[:Bar:X (Y) Z|]]
+!! result
+[[Article (context)|Article]]
+[[Bar:Article (context)|Article]]
+[[:Bar:Article (context)|Article]]
+[[Article (context)]]
+[[Bar:X (Y) Z|X (Y) Z]]
+[[:Bar:X (Y) Z|X (Y) Z]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with parens and no space (Korean style, bug 30149)
+!! options
+pst
+!! input
+[[Article(context)|]]
+[[Bar:Article(context)|]]
+[[:Bar:Article(context)|]]
+[[|Article(context)]]
+[[Bar:X(Y)Z|]]
+[[:Bar:X(Y)Z|]]
+!! result
+[[Article(context)|Article]]
+[[Bar:Article(context)|Article]]
+[[:Bar:Article(context)|Article]]
+[[Article(context)]]
+[[Bar:X(Y)Z|X(Y)Z]]
+[[:Bar:X(Y)Z|X(Y)Z]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with commas (bug 21660)
+!! options
+pst
+!! input
+[[Article (context), context|]]
+[[Article (context),context|]]
+[[Bar:Article (context), context|]]
+[[Bar:Article (context),context|]]
+[[:Bar:Article (context), context|]]
+[[:Bar:Article (context),context|]]
+!! result
+[[Article (context), context|Article]]
+[[Article (context),context|Article]]
+[[Bar:Article (context), context|Article]]
+[[Bar:Article (context),context|Article]]
+[[:Bar:Article (context), context|Article]]
+[[:Bar:Article (context),context|Article]]
+!! end
+
+!! test
+pre-save transform: trim trailing empty lines
+!! options
+pst
+!! input
+Empty lines are trimmed
+
+
+
+
+!! result
+Empty lines are trimmed
+!! end
+
+!! test
+pre-save transform: Signature expansion
+!! options
+pst
+!! input
+* ~~~
+* ~~~
+* ~~~
+* ~~~
+!! result
+* [[Special:Contributions/127.0.0.1|127.0.0.1]]
+* [[Special:Contributions/127.0.0.1|127.0.0.1]]
+* [[Special:Contributions/127.0.0.1|127.0.0.1]]
+* [[Special:Contributions/127.0.0.1|127.0.0.1]]
+!! end
+
+
+!! test
+pre-save transform: Signature expansion in nowiki tags (bug 93)
+!! options
+pst disabled
+!! input
+Shall not expand:
+
+~~~~
+
+~~~~
+
+~~~~
+
+~~~~
+
+{{subst:Foo}} shall be converted to FOO
+
+As well as inside noinclude/onlyinclude
+{{subst:Foo}}
+{{subst:Foo}}
+
+But not inside includeonly
+{{subst:Foo}}
+!! result
+Shall not expand:
+
+~~~~
+
+~~~~
+
+~~~~
+
+~~~~
+
+FOO shall be converted to FOO
+
+As well as inside noinclude/onlyinclude
+FOO
+FOO
+
+But not inside includeonly
+{{subst:Foo}}
+!! end
+
+###
+### Message transform tests
+###
+!! test
+message transform: magic variables
+!! options
+msg
+!! input
+{{SITENAME}}
+!! result
+MediaWiki
+!! end
+
+!! test
+message transform: should not transform wiki markup
+!! options
+msg
+!! input
+''test''
+!! result
+''test''
+!! end
+
+!! test
+message transform: in transcluded template (bug 4926)
+!! options
+msg
+!! input
+{{Includes}}
+!! result
+Foobar
+!! end
+
+!! test
+message transform: in transcluded template (bug 4926)
+!! options
+msg
+!! input
+{{Includes2}}
+!! result
+Foo
+!! end
+
+!! test
+{{#special:}} page name, known
+!! options
+msg
+!! input
+{{#special:Recentchanges}}
+!! result
+Special:RecentChanges
+!! end
+
+!! test
+{{#special:}} page name with subpage, known
+!! options
+msg
+!! input
+{{#special:Recentchanges/param}}
+!! result
+Special:RecentChanges/param
+!! end
+
+!! test
+{{#special:}} page name, unknown
+!! options
+msg
+!! input
+{{#special:foobarnonexistent}}
+!! result
+No such special page
+!! end
+
+!! test
+{{#speciale:}} page name, known
+!! options
+msg
+!! input
+{{#speciale:Recentchanges}}
+!! result
+Special:RecentChanges
+!! end
+
+!! test
+{{#speciale:}} page name with subpage, known
+!! options
+msg
+!! input
+{{#speciale:Recentchanges/param}}
+!! result
+Special:RecentChanges/param
+!! end
+
+!! test
+{{#speciale:}} page name, unknown
+!! options
+msg
+!! input
+{{#speciale:foobarnonexistent}}
+!! result
+No_such_special_page
+!! end
+
+###
+### Images
+###
+!! test
+Simple image
+!! input
+[[Image:foobar.jpg]]
+!! result
+
+
+!! end
+
+!! test
+Right-aligned image
+!! input
+[[Image:foobar.jpg|right]]
+!! result
+
+
+!! end
+
+!! test
+Simple image (using File: namespace, now canonical)
+!! input
+[[File:foobar.jpg]]
+!! result
+
+
+!! end
+
+!! test
+Image with caption
+!! input
+[[Image:foobar.jpg|right|Caption text]]
+!! result
+
+
+!! end
+
+!! test
+Image with empty attribute
+!! input
+[[Image:foobar.jpg|right||Caption text]]
+!! result
+
+
+!! end
+
+!! test
+Image with link tails
+!! input
+123[[Image:foobar.jpg]]456
+123[[Image:foobar.jpg|right]]456
+123[[Image:foobar.jpg|thumb]]456
+!! result
+123
456
+
+123456
+123456
+
+!! end
+
+!! test
+Image with multiple captions -- only last one is accepted
+!! input
+[[Image:foobar.jpg|right|Caption1 - ignored|[[Caption2]] - ignored|Caption3 - accepted]]
+!! result
+
+
+!! end
+
+!! test
+Image with width attribute at different positions
+!! input
+[[Image:foobar.jpg|200px|right|Caption]]
+[[Image:foobar.jpg|right|200px|Caption]]
+[[Image:foobar.jpg|right|Caption|200px]]
+!! result
+
+
+
+
+!! end
+
+!! test
+Image with link parameter, wiki target
+!! input
+[[Image:foobar.jpg|link=Target page]]
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter, URL target
+!! input
+[[Image:foobar.jpg|link=http://example.com/]]
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter, wgExternalLinkTarget
+!! input
+[[Image:foobar.jpg|link=http://example.com/]]
+!! config
+wgExternalLinkTarget='foobar'
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter, wgNoFollowLinks set to false
+!! input
+[[Image:foobar.jpg|link=http://example.com/]]
+!! config
+wgNoFollowLinks=false
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter, wgNoFollowDomainExceptions
+!! input
+[[Image:foobar.jpg|link=http://example.com/]]
+!! config
+wgNoFollowDomainExceptions='example.com'
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter, wgExternalLinkTarget, unnamed parameter
+!! input
+[[Image:foobar.jpg|link=http://example.com/|Title]]
+!! config
+wgExternalLinkTarget='foobar'
+!! result
+
+
+!! end
+
+!! test
+Image with empty link parameter
+!! input
+[[Image:foobar.jpg|link=]]
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter (wiki target) and unnamed parameter
+!! input
+[[Image:foobar.jpg|link=Target page|Title]]
+!! result
+
+
+!! end
+
+!! test
+Image with link parameter (URL target) and unnamed parameter
+!! input
+[[Image:foobar.jpg|link=http://example.com/|Title]]
+!! result
+
+
+!! end
+
+!! test
+Thumbnail image with link parameter
+!! input
+[[Image:foobar.jpg|thumb|link=http://example.com/|Title]]
+!! result
+
+
+!! end
+
+!! test
+Image with frame and link
+!! input
+[[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]]]
+!! result
+
+
+!! end
+
+!! test
+Image with frame and link and explicit alt
+!! input
+[[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]|alt=Altitude]]
+!! result
+
+
+!! end
+
+!! test
+Image with wiki markup in implicit alt
+!! input
+[[Image:Foobar.jpg|testing '''bold''' in alt]]
+!! result
+
+
+!! end
+
+!! test
+Image with wiki markup in explicit alt
+!! input
+[[Image:Foobar.jpg|alt=testing '''bold''' in alt]]
+!! result
+
+
+!! end
+
+!! test
+Link to image page- image page normally doesn't exists, hence edit link
+Add test with existing image page
+#Image:test
+!! input
+[[:Image:test]]
+!! result
+
Image:test
+
+!! end
+
+!! test
+bug 18784 Link to non-existent image page with caption should use caption as link text
+!! input
+[[:Image:test|caption]]
+!! result
+caption
+
+!! end
+
+!! test
+Frameless image caption with a free URL
+!! input
+[[Image:foobar.jpg|http://example.com]]
+!! result
+
+
+!! end
+
+!! test
+Thumbnail image caption with a free URL
+!! input
+[[Image:foobar.jpg|thumb|http://example.com]]
+!! result
+
+
+!! end
+
+!! test
+Thumbnail image caption with a free URL and explicit alt
+!! input
+[[Image:foobar.jpg|thumb|http://example.com|alt=Alteration]]
+!! result
+
+
+!! end
+
+!! test
+BUG 1887: A ISBN with a thumbnail
+!! input
+[[Image:foobar.jpg|thumb|ISBN 1235467890]]
+!! result
+
+
+!! end
+
+!! test
+BUG 1887: A RFC with a thumbnail
+!! input
+[[Image:foobar.jpg|thumb|This is RFC 12354]]
+!! result
+
+
+!! end
+
+!! test
+BUG 1887: A mailto link with a thumbnail
+!! input
+[[Image:foobar.jpg|thumb|Please mailto:nobody@example.com]]
+!! result
+
+
+!! end
+
+# Pending resolution to bug 368
+!! test
+BUG 648: Frameless image caption with a link
+!! input
+[[Image:foobar.jpg|text with a [[link]] in it]]
+!! result
+
+
+!! end
+
+!! test
+BUG 648: Frameless image caption with a link (suffix)
+!! input
+[[Image:foobar.jpg|text with a [[link]]foo in it]]
+!! result
+
+
+!! end
+
+!! test
+BUG 648: Frameless image caption with an interwiki link
+!! input
+[[Image:foobar.jpg|text with a [[MeatBall:Link]] in it]]
+!! result
+
+
+!! end
+
+!! test
+BUG 648: Frameless image caption with a piped interwiki link
+!! input
+[[Image:foobar.jpg|text with a [[MeatBall:Link|link]] in it]]
+!! result
+
+
+!! end
+
+!! test
+Escape HTML special chars in image alt text
+!! input
+[[Image:foobar.jpg|& < > "]]
+!! result
+
+
+!! end
+
+!! test
+BUG 499: Alt text should have Ӓ, not &1234;
+!! input
+[[Image:foobar.jpg|♀]]
+!! result
+
+
+!! end
+
+!! test
+Broken image caption with link
+!! input
+[[Image:Foobar.jpg|thumb|This is a broken caption. But [[Main Page|this]] is just an ordinary link.
+!! result
+[[Image:Foobar.jpg|thumb|This is a broken caption. But this is just an ordinary link.
+
+!! end
+
+!! test
+Image caption containing another image
+!! input
+[[Image:Foobar.jpg|thumb|This is a caption with another [[Image:icon.png|image]] inside it!]]
+!! result
+
This is a caption with another
image inside it!
+
+!! end
+
+!! test
+Image caption containing a newline
+!! input
+[[Image:Foobar.jpg|This
+*is some text]]
+!! result
+
+
+!!end
+
+!!test
+Parsoid: Image caption containing leading space
+(The leading space should not trigger nowiki escaping in wt2wt mode)
+!! input
+[[Image:Foobar.jpg|thumb| bar]]
+!! result
+
+
+!!end
+
+!! test
+Bug 3090: External links other than http: in image captions
+!! input
+[[Image:Foobar.jpg|thumb|200px|This caption has [irc://example.net irc] and [https://example.com Secure] ext links in it.]]
+!! result
+
This caption has
irc and
Secure ext links in it.
+
+!! end
+
+!! test
+Custom class
+!! input
+[[Image:foobar.jpg|a|class=b]]
+!! result
+
+
+!! end
+
+!! test
+Localized image handling (1).
+!! options
+language=es
+!! input
+[[Archivo:Foobar.jpg|izquierda|enlace=foo|caption]]
+!! result
+
+
+!! end
+
+!! test
+Localized image handling (2).
+!! options
+language=es
+!! input
+[[Archivo:Foobar.jpg|miniatura|izquierda|enlace=foo|caption]]
+!! result
+
+
+!! end
+
+!! test
+"border", "frameless" and "class" attributes on an image.
+!! input
+[[File:Foobar.jpg|frameless|border|class=extra|caption]]
+!! result
+
+
+!! end
+
+!! article
+File:Barfoo.jpg
+!! text
+#REDIRECT [[File:Barfoo.jpg]]
+!! endarticle
+
+!! test
+Redirected image
+!! input
+[[Image:Barfoo.jpg]]
+!! result
+File:Barfoo.jpg
+
+!! end
+
+!! test
+Missing image with uploads disabled
+!! options
+wgEnableUploads=0
+!! input
+[[Image:Foobaz.jpg]]
+!! result
+File:Foobaz.jpg
+
+!! end
+
+
+###
+### Subpages
+###
+!! article
+Subpage test/subpage
+!! text
+foo
+!! endarticle
+
+!! test
+Subpage link
+!! options
+subpage title=[[Subpage test]]
+!! input
+[[/subpage]]
+!! result
+/subpage
+
+!! end
+
+!! test
+Subpage noslash link
+!! options
+subpage title=[[Subpage test]]
+!!input
+[[/subpage/]]
+!! result
+subpage
+
+!! end
+
+!! test
+Disabled subpages
+!! input
+[[/subpage]]
+!! result
+/subpage
+
+!! end
+
+!! test
+BUG 561: {{/Subpage}}
+!! options
+subpage title=[[Page]]
+!! input
+{{/Subpage}}
+!! result
+Page/Subpage
+
+!! end
+
+###
+### Categories
+###
+!! article
+Category:MediaWiki User's Guide
+!! text
+blah
+!! endarticle
+
+!! test
+Link to category
+!! input
+[[:Category:MediaWiki User's Guide]]
+!! result
+Category:MediaWiki User's Guide
+
+!! end
+
+!! test
+Simple category
+!! options
+cat
+!! input
+[[Category:MediaWiki User's Guide]]
+!! result
+MediaWiki User's Guide
+!! end
+
+!! test
+PAGESINCATEGORY invalid title fatal (r33546 fix)
+!! input
+{{PAGESINCATEGORY:}}
+!! result
+0
+
+!! end
+
+!! test
+Category with different sort key
+!! options
+cat
+!! input
+[[Category:MediaWiki User's Guide|Foo]]
+!! result
+MediaWiki User's Guide
+!! end
+
+!! test
+Category with identical sort key
+!! options
+cat
+!! input
+[[Category:MediaWiki User's Guide|MediaWiki User's Guide]]
+!! result
+MediaWiki User's Guide
+!! end
+
+!! test
+Category with empty sort key
+!! options
+cat
+pst
+!! input
+[[Category:MediaWiki User's Guide|]]
+!! result
+[[Category:MediaWiki User's Guide|MediaWiki User's Guide]]
+!! end
+
+!! test
+Category with empty sort key and parentheses
+!! options
+cat
+pst
+!! input
+[[Category:Foo (bar)|]]
+!! result
+[[Category:Foo (bar)|Foo]]
+!! end
+
+!! test
+Category with link tail
+!! options
+cat
+pst
+!! input
+123[[Category:Foo]]456
+!! result
+123[[Category:Foo]]456
+!! end
+
+!! test
+Category with template
+!! options
+cat
+pst
+!! input
+[[Category:{{echo|Foo}}]]
+!! result
+[[Category:{{echo|Foo}}]]
+!! end
+
+!! test
+Category with template in sort key
+!! options
+cat
+pst
+!! input
+[[Category:Foo|{{echo|Bar}}]]
+!! result
+[[Category:Foo|{{echo|Bar}}]]
+!! end
+
+!! test
+Category with template in sort key and title
+!! options
+cat
+pst
+!! input
+[[Category:{{echo|Foo}}|{{echo|Bar}}]]
+!! result
+[[Category:{{echo|Foo}}|{{echo|Bar}}]]
+!! end
+
+!! test
+Category / paragraph interactions
+!! input
+Foo [[Category:Baz]] Bar
+
+Foo [[Category:Baz]]
+Bar
+
+Foo
+[[Category:Baz]]
+Bar
+
+Foo
+[[Category:Baz]] Bar
+
+Foo
+[[Category:Baz]]
+ [[Category:Baz]]
+[[Category:Baz]]
+Bar
+
+[[Category:Baz]]
+ [[Category:Baz]]
+[[Category:Baz]]
+
+[[Category:Baz]]
+ {{echo|[[Category:Baz]]}}
+[[Category:Baz]]
+!! result
+Foo Bar
+
Foo
+Bar
+
Foo
+Bar
+
Foo Bar
+
Foo
+Bar
+
+!! end
+
+###
+### Inter-language links
+###
+!! test
+Inter-language links
+!! options
+ill
+!! input
+[[es:Alimento]]
+[[fr:Nourriture]]
+[[zh:食品]]
+!! result
+es:Alimento fr:Nourriture zh:食品
+!! end
+
+!! test
+Duplicate interlanguage links (bug 24502)
+!! options
+ill
+!! input
+[[es:1]]
+[[es:2]]
+[[fr:1]]
+[[fr:2]]
+!! result
+es:1 fr:1
+!! end
+
+###
+### Sections
+###
+!! test
+Basic section headings
+!! input
+== Headline 1 ==
+Some text
+
+==Headline 2==
+More
+===Smaller headline===
+Blah blah
+!! result
+[edit] Headline 1
+Some text
+
+[edit] Headline 2
+More
+
+[edit] Smaller headline
+Blah blah
+
+!! end
+
+!! test
+Section headings with TOC
+!! input
+== Headline 1 ==
+=== Subheadline 1 ===
+===== Skipping a level =====
+====== Skipping a level ======
+
+== Headline 2 ==
+Some text
+===Another headline===
+!! result
+
+[edit] Headline 1
+[edit] Subheadline 1
+[edit] Skipping a level
+[edit] Skipping a level
+[edit] Headline 2
+Some text
+
+[edit] Another headline
+
+!! end
+
+# perl -e 'print "="x$_," Level $_ heading","="x$_,"\n" for 1..10'
+!! test
+Handling of sections up to level 6 and beyond
+!! input
+= Level 1 Heading=
+== Level 2 Heading==
+=== Level 3 Heading===
+==== Level 4 Heading====
+===== Level 5 Heading=====
+====== Level 6 Heading======
+======= Level 7 Heading=======
+======== Level 8 Heading========
+========= Level 9 Heading=========
+========== Level 10 Heading==========
+!! result
+
+[edit] Level 1 Heading
+[edit] Level 2 Heading
+[edit] Level 3 Heading
+[edit] Level 4 Heading
+[edit] Level 5 Heading
+[edit] Level 6 Heading
+[edit] = Level 7 Heading=
+[edit] == Level 8 Heading==
+[edit] === Level 9 Heading===
+[edit] ==== Level 10 Heading====
+
+!! end
+
+!! test
+TOC regression (bug 9764)
+!! input
+== title 1 ==
+=== title 1.1 ===
+==== title 1.1.1 ====
+=== title 1.2 ===
+== title 2 ==
+=== title 2.1 ===
+!! result
+
+[edit] title 1
+[edit] title 1.1
+[edit] title 1.1.1
+[edit] title 1.2
+[edit] title 2
+[edit] title 2.1
+
+!! end
+
+!! test
+TOC with wgMaxTocLevel=3 (bug 6204)
+!! options
+wgMaxTocLevel=3
+!! input
+== title 1 ==
+=== title 1.1 ===
+==== title 1.1.1 ====
+=== title 1.2 ===
+== title 2 ==
+=== title 2.1 ===
+!! result
+
+[edit] title 1
+[edit] title 1.1
+[edit] title 1.1.1
+[edit] title 1.2
+[edit] title 2
+[edit] title 2.1
+
+!! end
+
+!! test
+TOC with wgMaxTocLevel=3 and two level four headings (bug 6204)
+!! options
+wgMaxTocLevel=3
+!! input
+==Section 1==
+===Section 1.1===
+====Section 1.1.1====
+====Section 1.1.1.1====
+==Section 2==
+!! result
+
+[edit] Section 1
+[edit] Section 1.1
+[edit] Section 1.1.1
+[edit] Section 1.1.1.1
+[edit] Section 2
+
+!! end
+
+
+!! test
+Resolving duplicate section names
+!! input
+== Foo bar ==
+== Foo bar ==
+!! result
+[edit] Foo bar
+[edit] Foo bar
+
+!! end
+
+!! test
+Resolving duplicate section names with differing case (bug 10721)
+!! input
+== Foo bar ==
+== Foo Bar ==
+!! result
+[edit] Foo bar
+[edit] Foo Bar
+
+!! end
+
+!! article
+Template:sections
+!! text
+===Section 1===
+==Section 2==
+!! endarticle
+
+!! test
+Template with sections, __NOTOC__
+!! input
+__NOTOC__
+==Section 0==
+{{sections}}
+==Section 4==
+!! result
+[edit] Section 0
+[edit] Section 1
+[edit] Section 2
+[edit] Section 4
+
+!! end
+
+!! test
+__NOEDITSECTION__ keyword
+!! input
+__NOEDITSECTION__
+==Section 1==
+==Section 2==
+!! result
+ Section 1
+ Section 2
+
+!! end
+
+!! test
+Link inside a section heading
+!! input
+==Section with a [[Main Page|link]] in it==
+!! result
+[edit] Section with a link in it
+
+!! end
+
+!! test
+TOC regression (bug 12077)
+!! input
+__TOC__
+== title 1 ==
+=== title 1.1 ===
+== title 2 ==
+!! result
+
+[edit] title 1
+[edit] title 1.1
+[edit] title 2
+
+!! end
+
+!! test
+BUG 1219 URL next to image (good)
+!! input
+http://example.com [[Image:foobar.jpg]]
+!! result
+http://example.com
+
+!!end
+
+!! test
+Short headings with trailing space should match behavior of Parser::doHeadings (bug 19910)
+!! input
+===
+The line above must have a trailing space!
+===
+But just in case it doesn't...
+!! result
+
+The line above must have a trailing space!
+
+
+But just in case it doesn't...
+
+!! end
+
+!! test
+Header with special characters (bug 25462)
+!! input
+The tooltips shall not show entities to the user (ie. be double escaped)
+
+== text > text ==
+section 1
+
+== text < text ==
+section 2
+
+== text & text ==
+section 3
+
+== text ' text ==
+section 4
+
+== text " text ==
+section 5
+!! result
+The tooltips shall not show entities to the user (ie. be double escaped)
+
+
+[edit] text > text
+section 1
+
+[edit] text < text
+section 2
+
+[edit] text & text
+section 3
+
+[edit] text ' text
+section 4
+
+[edit] text " text
+section 5
+
+!! end
+
+!! test
+Headers with excess '=' characters
+(Are similar tests necessary beyond the 1st level?)
+!! input
+=foo==
+==foo=
+=''italic'' heading==
+==''italic'' heading=
+!! result
+
+
+
+[edit] italic heading=
+[edit] =italic heading
+
+!! end
+
+!! test
+BUG 1219 URL next to image (broken)
+!! input
+http://example.com[[Image:foobar.jpg]]
+!! result
+http://example.com
+
+!!end
+
+!! test
+Bug 1186 news: in the middle of text
+!! input
+http://en.wikinews.org/wiki/Wikinews:Workplace
+!! result
+http://en.wikinews.org/wiki/Wikinews:Workplace
+
+!!end
+
+
+!! test
+Namespaced link must have a title
+!! input
+[[Project:]]
+!! result
+[[Project:]]
+
+!!end
+
+!! test
+Namespaced link must have a title (bad fragment version)
+!! input
+[[Project:#fragment]]
+!! result
+[[Project:#fragment]]
+
+!!end
+
+
+###
+### HTML tags and HTML attributes
+###
+
+!! test
+div with no attributes
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!! end
+
+!! test
+div with double-quoted attribute
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!! end
+
+!! test
+div with single-quoted attribute
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!! end
+
+!! test
+div with unquoted attribute
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!! end
+
+!! test
+div with illegal double attributes
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!!end
+
+# FIXME: produce empty string instead of "class" in the PHP parser, following
+# the HTML5 spec.
+!! test
+div with empty attribute value, space before equals
+!! options
+disabled
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!! end
+
+# The PHP parser escapes the opening brace to { for some reason, so
+# disabled this test for it.
+!! test
+div with braces in attribute value
+!! options
+disabled
+!! input
+Foo
+!! result
+Foo
+!! end
+
+# This it very inconsistent in the PHP parser: it returns
+# class="class" if there is a space between the name and the equal sign (see
+# 'div with empty attribute value, space before equals'), but strips the
+# attribute completely if the space is missing. We hope that not much content
+# depends on this, so are implementing the behavior below in Parsoid for
+# consistencies' sake. Disabled for the PHP parser.
+# FIXME: fix this behavior in the PHP parser?
+!! test
+div with empty attribute value, no space before equals
+!! options
+disabled
+!! input
+HTML rocks
+!! result
+HTML rocks
+
+!! end
+
+!! test
+HTML multiple attributes correction
+!! input
+Awesome!
+!! result
+Awesome!
+
+!!end
+
+!! test
+Table multiple attributes correction
+!! input
+{|
+!+ class="error" class="awesome"| status
+|}
+!! result
+
+
+!!end
+
+!! test
+DIV IN UPPERCASE
+!! input
+HTML ROCKS
+!! result
+HTML ROCKS
+
+!!end
+
+!! test
+Non-ASCII pseudo-tags are rendered as text
+!! input
+
+!! result
+<khyô>
+
+!! end
+
+!! test
+Pseudo-tag with URL 'name' renders as url link
+!! input
+
+!! result
+<http://example.com/>
+
+!! end
+
+!! test
+text with amp in the middle of nowhere
+!! input
+Remember AT&T?
+!!result
+Remember AT&T?
+
+!! end
+
+!! test
+text with character entity: eacute
+!! input
+I always thought é was a cute letter.
+!! result
+I always thought é was a cute letter.
+
+!! end
+
+!! test
+text with entity-escaped character entity-like string: eacute
+!! input
+I always thought é was a cute letter.
+!! result
+I always thought é was a cute letter.
+
+!! end
+
+!! test
+text with undefined character entity: xacute
+!! input
+I always thought &xacute; was a cute letter.
+!! result
+I always thought &xacute; was a cute letter.
+
+!! end
+
+
+###
+### Media links
+###
+
+!! test
+Media link
+!! input
+[[Media:Foobar.jpg]]
+!! result
+Media:Foobar.jpg
+
+!! end
+
+!! test
+Media link with text
+!! input
+[[Media:Foobar.jpg|A neat file to look at]]
+!! result
+A neat file to look at
+
+!! end
+
+# FIXME: this is still bad HTML tag nesting
+!! test
+Media link with nasty text
+fixme: doBlockLevels won't wrap this in a paragraph because it contains a div
+!! input
+[[Media:Foobar.jpg|Safe Link" onmouseover="alert(document.cookie)" onfoo="
]]
+!! result
+Safe Link<div style="display:none">" onmouseover="alert(document.cookie)" onfoo="</div>
+
+!! end
+
+!! test
+Media link to nonexistent file (bug 1702)
+!! input
+[[Media:No such.jpg]]
+!! result
+Media:No such.jpg
+
+!! end
+
+!! test
+Image link to nonexistent file (bug 1850 - good)
+!! input
+[[Image:No such.jpg]]
+!! result
+File:No such.jpg
+
+!! end
+
+!! test
+:Image link to nonexistent file (bug 1850 - bad)
+!! input
+[[:Image:No such.jpg]]
+!! result
+Image:No such.jpg
+
+!! end
+
+
+
+!! test
+Character reference normalization in link text (bug 1938)
+!! input
+[[Main Page|this&that]]
+!! result
+this&that
+
+!!end
+
+!! article
+אַ
+!! text
+Test for unicode normalization
+
+The page's name is U+05d0 U+05b7, with non-canonical form U+FB2E
+!! endarticle
+
+!! test
+(bug 19451) Links should refer to the normalized form.
+!! input
+[[אַ]]
+[[אַ]]
+[[אַ]]
+[[אַ]]
+[[אַ]]
+!! result
+אַ
+אַ
+אַ
+אַ
+אַ
+
+!! end
+
+!! test
+Empty attribute crash test (bug 2067)
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Empty attribute crash test single-quotes (bug 2067)
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Attribute test: equals, then nothing
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Attribute test: unquoted value
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Attribute test: unquoted but illegal value (hash)
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Attribute test: no value
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Bug 2095: link with three closing brackets
+!! input
+[[Main Page]]]
+!! result
+Main Page]
+
+!! end
+
+!! test
+Bug 2095: link with pipe and three closing brackets
+!! input
+[[Main Page|link]]]
+!! result
+link]
+
+!! end
+
+!! test
+Bug 2095: link with pipe and three closing brackets, version 2
+!! input
+[[Main Page|[http://example.com/]]]
+!! result
+[http://example.com/]
+
+!! end
+
+
+###
+### Safety
+###
+
+!! article
+Template:Dangerous attribute
+!! text
+" onmouseover="alert(document.cookie)
+!! endarticle
+
+!! article
+Template:Dangerous style attribute
+!! text
+border-size: expression(alert(document.cookie))
+!! endarticle
+
+!! article
+Template:Div style
+!! text
+Magic div
+!! endarticle
+
+!! test
+Bug 2304: HTML attribute safety (safe template; regression bug 2309)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (dangerous template; 2309)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (dangerous style template; 2309)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (safe parameter; 2309)
+!! input
+{{div style|width: 200px}}
+!! result
+Magic div
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (unsafe parameter; 2309)
+!! input
+{{div style|width: expression(alert(document.cookie))}}
+!! result
+Magic div
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (unsafe breakout parameter; 2309)
+!! input
+{{div style|">}}
+!! result
+<script>alert(document.cookie)</script>">Magic div
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (unsafe breakout parameter 2; 2309)
+!! input
+{{div style|" >}}
+!! result
+<script>alert(document.cookie)</script>">Magic div
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (link)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (italics)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (bold)
+!! input
+
+!! result
+
+
+!! end
+
+
+!! test
+Bug 2304: HTML attribute safety (ISBN)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (RFC)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (PMID)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (web link)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (named web link)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 3244: HTML attribute safety (extension; safe)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Bug 3244: HTML attribute safety (extension; unsafe)
+!! input
+
+!! result
+
+
+!! end
+
+# More MSIE fun discovered by Tom Gilder
+
+!! test
+MSIE CSS safety test: spurious slash
+!! input
+evil
+!! result
+evil
+
+!! end
+
+!! test
+MSIE CSS safety test: hex code
+!! input
+evil
+!! result
+evil
+
+!! end
+
+!! test
+MSIE CSS safety test: comment in url
+!! input
+evil
+!! result
+evil
+
+!! end
+
+!! test
+MSIE CSS safety test: comment in expression
+!! input
+evil4
+!! result
+evil4
+
+!! end
+
+
+!! test
+Table attribute legitimate extension
+!! input
+{|
+!+ style="color:blue"| status
+|}
+!! result
+
+
+!!end
+
+!! test
+Table attribute safety
+!! input
+{|
+!+ style="border-width:expression(0+alert(document.cookie))"| status
+|}
+!! result
+
+
+!! end
+
+!! test
+CSS line continuation 1
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+CSS line continuation 2
+!! input
+
+!! result
+
+
+!! end
+
+!! article
+Template:Identity
+!! text
+{{{1}}}
+!! endarticle
+
+!! test
+Expansion of multi-line templates in attribute values (bug 6255)
+!! input
+-
+!! result
+-
+
+!! end
+
+
+!! test
+Expansion of multi-line templates in attribute values (bug 6255 sanity check)
+!! input
+-
+!! result
+-
+
+!! end
+
+!! test
+Expansion of multi-line templates in attribute values (bug 6255 sanity check 2)
+!! input
+-
+!! result
+-
+
+!! end
+
+###
+### Parser hooks (see maintenance/parserTestsParserHook.php for the extension)
+###
+!! test
+Parser hook: empty input
+!! input
+
+!! result
+
+''
+array (
+)
+
+
+!! end
+
+!! test
+Parser hook: empty input using terminated empty elements
+!! input
+
+!! result
+
+NULL
+array (
+)
+
+
+!! end
+
+!! test
+Parser hook: empty input using terminated empty elements (space before)
+!! input
+
+!! result
+
+NULL
+array (
+)
+
+
+!! end
+
+!! test
+Parser hook: basic input
+!! input
+input
+!! result
+
+'input'
+array (
+)
+
+
+!! end
+
+
+!! test
+Parser hook: case insensitive
+!! input
+input
+!! result
+
+'input'
+array (
+)
+
+
+!! end
+
+
+!! test
+Parser hook: case insensitive, redux
+!! input
+input
+!! result
+
+'input'
+array (
+)
+
+
+!! end
+
+!! test
+Parser hook: nested tags
+!! options
+noxml
+!! input
+
+!! result
+
+''
+array (
+)
+
</tag>
+
+!! end
+
+!! test
+Parser hook: basic arguments
+!! input
+
+!! result
+
+''
+array (
+ 'width' => '200',
+ 'height' => '100',
+ 'depth' => '50',
+ 'square' => 'square',
+)
+
+
+!! end
+
+!! test
+Parser hook: argument containing a forward slash (bug 5344)
+!! input
+
+!! result
+
+''
+array (
+ 'filename' => '/tmp/bla',
+)
+
+
+!! end
+
+!! test
+Parser hook: empty input using terminated empty elements (bug 2374)
+!! input
+text
+!! result
+
+NULL
+array (
+ 'foo' => 'bar',
+)
+
text
+
+!! end
+
+# should be output literally since there is no matching tag that begins it
+!! test
+Parser hook: basic arguments using terminated empty elements (bug 2374)
+!! input
+
+other stuff
+
+!! result
+
+NULL
+array (
+ 'width' => '200',
+ 'height' => '100',
+ 'depth' => '50',
+ 'square' => 'square',
+)
+
+other stuff
+</tag>
+
+!! end
+
+###
+### (see maintenance/parserTestsStaticParserHook.php for the extension)
+###
+
+!! test
+Parser hook: static parser hook not inside a comment
+!! input
+hello, world
+
+!! result
+hello, world
+
+!! end
+
+
+!! test
+Parser hook: static parser hook inside a comment
+!! input
+
+
+!! result
+
+
+!! end
+
+# Nested template calls; this case was broken by Parser.php rev 1.506,
+# since reverted.
+
+!! article
+Template:One-parameter
+!! text
+(My parameter is: {{{1}}})
+!! endarticle
+
+!! article
+Template:Map-one-parameter
+!! text
+{{{{{1}}}|{{{2}}}}}
+!! endarticle
+
+!! test
+Nested template calls
+!! input
+{{Map-one-parameter|One-parameter|param}}
+!! result
+(My parameter is: param)
+
+!! end
+
+
+###
+### Sanitizer
+###
+!! test
+Sanitizer: Closing of open tags
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Sanitizer: Closing of open but not closed tags
+!! input
+foo
+!! result
+foo
+
+!! end
+
+!! test
+Sanitizer: Closing of closed but not open tags
+!! input
+
+!! result
+</s>
+
+!! end
+
+!! test
+Sanitizer: Closing of closed but not open table tags
+!! input
+Table not started
+!! result
+Table not started</td></tr></table>
+
+!! end
+
+!! test
+Sanitizer: Escaping of spaces, multibyte characters, colons & other stuff in id=""
+!! input
+byte[[#æ: v|backlink]]
+!! result
+bytebacklink
+
+!! end
+
+!! test
+Sanitizer: Validating the contents of the id attribute (bug 4515)
+!! options
+disabled
+!! input
+
+!! result
+Something, but definitely not
...
+!! end
+
+!! test
+Sanitizer: Validating id attribute uniqueness (bug 4515, bug 6301)
+!! options
+disabled
+!! input
+
+!! result
+Something need to be done. foo-2 ?
+!! end
+
+!! test
+Sanitizer: Validating that and work, but only for Microdata
+!! input
+
+
+
+
+
+
+
+
+!! result
+
+
+ <meta http-equiv="refresh" content="5">
+
+
+
+ <link rel="stylesheet" href="
http://example.org">
+
+
+
+!! end
+
+!! test
+Language converter: output gets cut off unexpectedly (bug 5757)
+!! options
+language=zh
+!! input
+this bit is safe: }-
+
+but if we add a conversion instance: -{zh-cn:xxx;zh-tw:yyy}-
+
+then we get cut off here: }-
+
+all additional text is vanished
+!! result
+this bit is safe: }-
+
but if we add a conversion instance: xxx
+
then we get cut off here: }-
+
all additional text is vanished
+
+!! end
+
+!! test
+Self closed html pairs (bug 5487)
+!! options
+!! input
+Centered text
+In div text
+!! result
+<font id="bug" />Centered text
+<font id="bug2" />In div text
+
+!! end
+
+#
+#
+#
+
+!! test
+Punctuation: nbsp before exclamation
+!! input
+C'est grave !
+!! result
+C'est grave !
+
+!! end
+
+!! test
+Punctuation: CSS !important (bug 11874)
+!! input
+important
+!! result
+important
+
+!!end
+
+!! test
+Punctuation: CSS ! important (bug 11874; with space after)
+!! input
+important
+!! result
+important
+
+!!end
+
+
+!! test
+HTML bullet list, closed tags (bug 5497)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+HTML bullet list, unclosed tags (bug 5497)
+!! options
+disabled
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+HTML ordered list, closed tags (bug 5497)
+!! input
+
+- One
+- Two
+
+!! result
+
+- One
+- Two
+
+
+!! end
+
+!! test
+HTML ordered list, unclosed tags (bug 5497)
+!! options
+disabled
+!! input
+
+- One
+
- Two
+
+!! result
+
+- One
+
- Two
+
+
+!! end
+
+!! test
+HTML nested bullet list, closed tags (bug 5497)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+HTML nested bullet list, open tags (bug 5497)
+!! options
+disabled
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+HTML nested ordered list, closed tags (bug 5497)
+!! input
+
+- One
+- Two:
+
+- Sub-one
+- Sub-two
+
+
+
+!! result
+
+- One
+- Two:
+
+- Sub-one
+- Sub-two
+
+
+
+
+!! end
+
+!! test
+HTML nested ordered list, open tags (bug 5497)
+!! options
+disabled
+!! input
+
+- One
+
- Two:
+
+- Sub-one
+
- Sub-two
+
+
+!! result
+
+- One
+
- Two:
+
+- Sub-one
+
- Sub-two
+
+
+
+!! end
+
+!! test
+HTML ordered list item with parameters oddity
+!! input
+- One
+!! result
+- One
+
+!! end
+
+!!test
+bug 5918: autonumbering
+!! input
+[http://first/] [http://second] [ftp://ftp]
+
+ftp://inlineftp
+
+[mailto:enclosed@mail.tld With target]
+
+[mailto:enclosed@mail.tld]
+
+mailto:inline@mail.tld
+!! result
+[1] [2] [3]
+
ftp://inlineftp
+
With target
+
[4]
+
mailto:inline@mail.tld
+
+!! end
+
+
+#
+# Security and HTML correctness
+# From Nick Jenkins' fuzz testing
+#
+
+!! test
+Fuzz testing: Parser13
+!! input
+{|
+| http://a|
+!! result
+
+
+!! end
+
+!! test
+Fuzz testing: Parser14
+!! input
+== onmouseover= ==
+http://__TOC__
+!! result
+[edit] onmouseover=
+http://
+
+!! end
+
+!! test
+Fuzz testing: Parser14-table
+!! input
+==a==
+{| STYLE=__TOC__
+!! result
+
+
+
+!! end
+
+# Known to produce bogus xml (extra )
+!! test
+Fuzz testing: Parser16
+!! options
+noxml
+!! input
+{|
+!https://||||||
+!! result
+
+
+!! end
+
+!! test
+Fuzz testing: Parser21
+!! input
+{|
+! irc://{{ftp://a" onmouseover="alert('hello world');"
+|
+!! result
+
+
+!! end
+
+!! test
+Fuzz testing: Parser22
+!! input
+http://===r:::https://b
+
+{|
+!!result
+http://===r:::https://b
+
+
+
+!! end
+
+# Known to produce bad XML for now
+!! test
+Fuzz testing: Parser24
+!! options
+noxml
+!! input
+{|
+{{{|
+}}}} >
+
+
+MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+|
+!! result
+
+{{{|
+}}}} >
+
+
+MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+
+
+ |
+
+
+
+!! end
+
+# Note: the current result listed for this is not what the original one was,
+# but the original bug was JavaScript injection, which is fixed in any case.
+# It's not clear that the original result listed was any more correct than the
+# current one. Original result:
+# {{{|
+#
+# -
+# }}}blah" onmouseover="alert('hello world');" align="left"MOVE MOUSE CURSOR OVER HERE
+!!test
+Fuzz testing: Parser25 (bug 6055)
+!! input
+{{{
+|
+
-
+}}}blah" onmouseover="alert('hello world');" align="left"'''MOVE MOUSE CURSOR OVER HERE
+!! result
+
<LI CLASS=blah" onmouseover="alert('hello world');" align="left"MOVE MOUSE CURSOR OVER HERE
+
+!! end
+
+!!test
+Fuzz testing: URL adjacent extension (with space, clean)
+!! options
+!! input
+http://example.com junk
+!! result
+http://example.com junk
+
+!!end
+
+!!test
+Fuzz testing: URL adjacent extension (no space, dirty; nowiki)
+!! options
+!! input
+http://example.comjunk
+!! result
+http://example.comjunk
+
+!!end
+
+!!test
+Fuzz testing: URL adjacent extension (no space, dirty; pre)
+!! options
+!! input
+http://example.comjunk
+!! result
+http://example.comjunk
+
+!!end
+
+!!test
+Fuzz testing: image with bogus manual thumbnail
+!!input
+[[Image:foobar.jpg|thumbnail= ]]
+!!result
+Error creating thumbnail:
+
+!!end
+
+!! test
+Fuzz testing: encoded newline in generated HTML replacements (bug 6577)
+!! input
+
+!! result
+
+
+!! end
+
+!! test
+Parsing optional HTML elements (Bug 6171)
+!! options
+!! input
+
+
+ Some tabular data |
+ More tabular data ...
+ | And yet som tabular data |
+
+
+!! result
+
+
+ Some tabular data |
+ More tabular data ...
+ | And yet som tabular data |
+
+
+
+!! end
+
+!! test
+Correct handling of , | (Bug 6171)
+!! options
+!! input
+
+
+ Some tabular data |
+ More tabular data ... |
+ And yet som tabular data |
+
+
+!! result
+
+
+ Some tabular data |
+ More tabular data ... |
+ And yet som tabular data |
+
+
+
+!! end
+
+
+!! test
+Parsing crashing regression (fr:JavaScript)
+!! input
+