Merge "Doc fixes for MediaHandler. No behaviour changes."
[lhc/web/wiklou.git] / tests / parser / parserTest.inc
index 05a6f15..4efd708 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 /**
- * Helper code for the MediaWiki parser test suite.
+ * Helper code for the MediaWiki parser test suite. Some code is duplicated
+ * in PHPUnit's NewParserTests.php, so you'll probably want to update both
+ * at the same time.
  *
  * Copyright © 2004, 2010 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
@@ -74,6 +76,7 @@ class ParserTest {
 
        public $regex = "";
        private $savedGlobals = array();
+
        /**
         * Sets terminal colorization and diff/quick modes depending on OS and
         * command-line options (--color and --quick).
@@ -83,14 +86,14 @@ class ParserTest {
                $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;
+                       switch ( $options['color'] ) {
+                               case 'no':
+                                       $this->color = false;
+                                       break;
+                               case 'yes':
+                               default:
+                                       $this->color = true;
+                                       break;
                        }
                }
 
@@ -102,7 +105,7 @@ class ParserTest {
                $this->showProgress = !isset( $options['quiet'] );
                $this->showFailure = !(
                        isset( $options['quiet'] )
-                       && ( isset( $options['record'] )
+                               && ( isset( $options['record'] )
                                || isset( $options['compare'] ) ) ); // redundant output
 
                $this->showOutput = isset( $options['show-output'] );
@@ -130,6 +133,7 @@ class ParserTest {
                }
 
                $this->runDisabled = isset( $options['run-disabled'] );
+               $this->runParsoid = isset( $options['run-parsoid'] );
 
                $this->hooks = array();
                $this->functionHooks = array();
@@ -141,37 +145,36 @@ class ParserTest {
                        $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache,
                        $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
                        $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
-                       $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
+                       $wgArticlePath, $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',
+                       'name' => 'fsLockManager',
+                       'class' => 'FSLockManager',
                        'lockDirectory' => wfTempDir() . '/test-repo/lockdir',
                ), array(
-                       'name'          => 'nullLockManager',
-                       'class'         => 'NullLockManager',
+                       'name' => 'nullLockManager',
+                       'class' => 'NullLockManager',
                ) );
                $wgLocalFileRepo = array(
-                       'class'           => 'LocalRepo',
-                       'name'            => 'local',
-                       'url'             => 'http://example.com/images',
-                       'hashLevels'      => 2,
+                       'class' => 'LocalRepo',
+                       'name' => 'local',
+                       'url' => 'http://example.com/images',
+                       'hashLevels' => 2,
                        'transformVia404' => false,
-                       'backend'         => new FSFileBackend( array(
-                               'name'        => 'local-backend',
+                       '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-public' => wfTempDir() . '/test-repo/public',
+                                       'local-thumb' => wfTempDir() . '/test-repo/thumb',
+                                       'local-temp' => wfTempDir() . '/test-repo/temp',
                                        'local-deleted' => wfTempDir() . '/test-repo/deleted',
                                )
                        ) )
@@ -206,16 +209,71 @@ class ParserTest {
                $wgRequest = $context->getRequest();
 
                if ( $wgStyleDirectory === false ) {
-                       $wgStyleDirectory   = "$IP/skins";
+                       $wgStyleDirectory = "$IP/skins";
                }
 
+               self::setupInterwikis();
        }
 
-       public function setupRecorder ( $options ) {
+       /**
+        * Insert hardcoded interwiki in the lookup table.
+        *
+        * This function insert a set of well known interwikis that are used in
+        * the parser tests. They can be considered has fixtures are injected in
+        * the interwiki cache by using the 'InterwikiLoadPrefix' hook.
+        * Since we are not interested in looking up interwikis in the database,
+        * the hook completely replace the existing mechanism (hook returns false).
+        */
+       public static function setupInterwikis() {
+               # Hack: insert a few Wikipedia in-project interwiki prefixes,
+               # for testing inter-language links
+               Hooks::register( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
+                       static $testInterwikis = array(
+                               'wikipedia' => array(
+                                       'iw_url' => 'http://en.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ),
+                               'meatball' => array(
+                                       'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ),
+                               'zh' => array(
+                                       'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ),
+                               'es' => array(
+                                       'iw_url' => 'http://es.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ),
+                               'fr' => array(
+                                       'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ),
+                               'ru' => array(
+                                       'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ),
+                       );
+                       if( array_key_exists( $prefix, $testInterwikis ) ) {
+                               $iwData = $testInterwikis[$prefix];
+                       }
+
+                       // We only want to rely on the above fixtures
+                       return false;
+               } );// hooks::register
+       }
+
+       public function setupRecorder( $options ) {
                if ( isset( $options['record'] ) ) {
                        $this->recorder = new DbTestRecorder( $this );
                        $this->recorder->version = isset( $options['setversion'] ) ?
-                                       $options['setversion'] : SpecialVersion::getVersion();
+                               $options['setversion'] : SpecialVersion::getVersion();
                } elseif ( isset( $options['compare'] ) ) {
                        $this->recorder = new DbTestPreviewer( $this );
                } else {
@@ -227,11 +285,10 @@ class ParserTest {
         * Remove last character if it is a newline
         * @group utility
         */
-       static public function chomp( $s ) {
+       public static function chomp( $s ) {
                if ( substr( $s, -1 ) === "\n" ) {
                        return substr( $s, 0, -1 );
-               }
-               else {
+               } else {
                        return $s;
                }
        }
@@ -390,7 +447,7 @@ class ParserTest {
 
                        $this->teardownDatabase();
                        $this->recorder->report();
-               } catch (DBError $e) {
+               } catch ( DBError $e ) {
                        echo $e->getMessage();
                }
                $this->recorder->end();
@@ -463,8 +520,7 @@ class ParserTest {
 
                if ( isset( $opts['title'] ) ) {
                        $titleText = $opts['title'];
-               }
-               else {
+               } else {
                        $titleText = 'Parser test';
                }
 
@@ -487,7 +543,7 @@ class ParserTest {
                } elseif ( isset( $opts['comment'] ) ) {
                        $out = Linker::formatComment( $input, $title, $local );
                } elseif ( isset( $opts['preload'] ) ) {
-                       $out = $parser->getpreloadText( $input, $title, $options );
+                       $out = $parser->getPreloadText( $input, $title, $options );
                } else {
                        $output = $parser->parse( $input, $title, $options, true, true, 1337 );
                        $out = $output->getText();
@@ -518,18 +574,23 @@ class ParserTest {
                }
 
                $this->teardownGlobals();
-               return $this->showTestResult( $desc, $result, $out );
+
+               $testResult = new ParserTestResult( $desc );
+               $testResult->expected = $result;
+               $testResult->actual = $out;
+
+               return $this->showTestResult( $testResult );
        }
 
        /**
-        *
+        * Refactored in 1.22 to use ParserTestResult
         */
-       function showTestResult( $desc, $result, $out ) {
-               if ( $result === $out ) {
-                       $this->showSuccess( $desc );
+       function showTestResult( ParserTestResult $testResult ) {
+               if ( $testResult->isSuccess() ) {
+                       $this->showSuccess( $testResult );
                        return true;
                } else {
-                       $this->showFailure( $desc, $result, $out );
+                       $this->showFailure( $testResult );
                        return false;
                }
        }
@@ -632,18 +693,18 @@ class ParserTest {
                        self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
 
                $settings = array(
-                       'wgServer' => 'http://Britney-Spears',
+                       'wgServer' => 'http://example.org',
                        'wgScript' => '/index.php',
                        'wgScriptPath' => '/',
                        'wgArticlePath' => '/wiki/$1',
                        'wgActionPaths' => array(),
                        'wgLockManagers' => array( array(
-                               'name'          => 'fsLockManager',
-                               'class'         => 'FSLockManager',
+                               'name' => 'fsLockManager',
+                               'class' => 'FSLockManager',
                                'lockDirectory' => $this->uploadDir . '/lockdir',
                        ), array(
-                               'name'          => 'nullLockManager',
-                               'class'         => 'NullLockManager',
+                               'name' => 'nullLockManager',
+                               'class' => 'NullLockManager',
                        ) ),
                        'wgLocalFileRepo' => array(
                                'class' => 'LocalRepo',
@@ -651,20 +712,19 @@ class ParserTest {
                                'url' => 'http://example.com/images',
                                'hashLevels' => 2,
                                'transformVia404' => false,
-                               'backend'         => new FSFileBackend( array(
-                                       'name'        => 'local-backend',
+                               '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-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_',
@@ -678,6 +738,8 @@ class ParserTest {
                        'wgNoFollowDomainExceptions' => array(),
                        'wgThumbnailScriptPath' => false,
                        'wgUseImageResize' => true,
+                       'wgSVGConverter' => 'null',
+                       'wgSVGConverters' => array( 'null' => 'echo "1">$output' ),
                        'wgLocaltimezone' => 'UTC',
                        'wgAllowExternalImages' => true,
                        'wgUseTidy' => false,
@@ -685,10 +747,10 @@ class ParserTest {
                        'wgVariantArticlePath' => false,
                        'wgGroupPermissions' => array( '*' => array(
                                'createaccount' => true,
-                               'read'          => true,
-                               'edit'          => true,
-                               'createpage'    => true,
-                               'createtalk'    => true,
+                               'read' => true,
+                               'edit' => true,
+                               'createpage' => true,
+                               'createtalk' => true,
                        ) ),
                        'wgNamespaceProtection' => array( NS_MEDIAWIKI => 'editinterface' ),
                        'wgDefaultExternalStore' => array(),
@@ -755,7 +817,7 @@ class ParserTest {
                $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',
+                       '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'
@@ -818,45 +880,10 @@ class ParserTest {
 
                        # Anonymous user
                        $this->db->insert( 'user', array(
-                               'user_id'         => 0,
-                               'user_name'       => 'Anonymous' ) );
+                               '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 ) );
 
@@ -866,34 +893,67 @@ class ParserTest {
                # Clear the message cache
                MessageCache::singleton()->clear();
 
+               // Remember to update newParserTests.php after changing the below
+               // (and it uses a slightly different syntax just for teh lulz)
                $this->uploadDir = $this->setupUploadDir();
                $user = User::createNew( 'WikiSysop' );
                $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
+               # note that the size/width/height/bits/etc of the file
+               # are actually set by inspecting the file itself; the arguments
+               # to recordUpload2 have no effect.  That said, we try to make things
+               # match up so it is less confusing to readers of the code & tests.
                $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 );
+                       'size' => 7881,
+                       'width' => 1941,
+                       'height' => 220,
+                       'bits' => 8,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/jpeg',
+                       'metadata' => serialize( array() ),
+                       'sha1' => wfBaseConvert( '1', 16, 36, 31 ),
+                       'fileExists' => true
+               ), $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
+               # again, note that size/width/height below are ignored; see above.
+               $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', array(
+                       'size' => 22589,
+                       'width' => 135,
+                       'height' => 135,
+                       'bits' => 8,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/png',
+                       'metadata' => serialize( array() ),
+                       'sha1' => wfBaseConvert( '2', 16, 36, 31 ),
+                       'fileExists' => true
+               ), $this->db->timestamp( '20130225203040' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+               $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
+                               'size'        => 12345,
+                               'width'       => 240,
+                               'height'      => 180,
+                               'bits'        => 24,
+                               'media_type'  => MEDIATYPE_DRAWING,
+                               'mime'        => 'image/svg+xml',
+                               '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 );
+                       'size' => 12345,
+                       'width' => 320,
+                       'height' => 240,
+                       'bits' => 24,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/jpeg',
+                       'metadata' => serialize( array() ),
+                       'sha1' => wfBaseConvert( '3', 16, 36, 31 ),
+                       'fileExists' => true
+               ), $this->db->timestamp( '20010115123500' ), $user );
        }
 
        public function teardownDatabase() {
@@ -907,7 +967,7 @@ class ParserTest {
                $this->databaseSetupDone = false;
 
                if ( $this->useTemporaryTables ) {
-                       if( $this->db->getType() == 'sqlite' ) {
+                       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()
@@ -926,8 +986,9 @@ class ParserTest {
                        $this->db->query( $sql );
                }
 
-               if ( $this->db->getType() == 'oracle' )
+               if ( $this->db->getType() == 'oracle' ) {
                        $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+               }
 
                $this->teardownGlobals();
        }
@@ -959,9 +1020,15 @@ class ParserTest {
 
                wfMkdirParents( $dir . '/3/3a', null, __METHOD__ );
                copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
+               wfMkdirParents( $dir . '/e/ea', null, __METHOD__ );
+               copy( "$IP/skins/monobook/wiki.png", "$dir/e/ea/Thumb.png" );
                wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
                copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
-
+               wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
+               copy( "$IP/skins/monobook/headbg.jpg", "$dir/f/ff/Foobar.svg" );
+               file_put_contents( "$dir/f/ff/Foobar.svg",
+                       '<?xml version="1.0" encoding="utf-8"?>' .
+                       '<svg xmlns="http://www.w3.org/2000/svg" />' );
                return $dir;
        }
 
@@ -990,7 +1057,7 @@ class ParserTest {
 
                // delete the files first, then the dirs.
                self::deleteFiles(
-                       array (
+                       array(
                                "$dir/3/3a/Foobar.jpg",
                                "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
                                "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
@@ -1007,14 +1074,24 @@ class ParserTest {
                                "$dir/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg",
                                "$dir/thumb/3/3a/Foobar.jpg/960px-Foobar.jpg",
 
+                               "$dir/e/ea/Thumb.png",
+
                                "$dir/0/09/Bad.jpg",
 
+                               "$dir/f/ff/Foobar.svg",
+                               "$dir/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
+
                                "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
                        )
                );
 
                self::deleteDirs(
-                       array (
+                       array(
                                "$dir/3/3a",
                                "$dir/3",
                                "$dir/thumb/6/65",
@@ -1022,7 +1099,13 @@ class ParserTest {
                                "$dir/thumb/3/3a/Foobar.jpg",
                                "$dir/thumb/3/3a",
                                "$dir/thumb/3",
-
+                               "$dir/e/ea",
+                               "$dir/e",
+                               "$dir/f/ff/",
+                               "$dir/f/",
+                               "$dir/thumb/f/ff/Foobar.svg",
+                               "$dir/thumb/f/ff/",
+                               "$dir/thumb/f/",
                                "$dir/0/09/",
                                "$dir/0/",
                                "$dir/thumb",
@@ -1069,10 +1152,12 @@ class ParserTest {
        /**
         * Print a happy success message.
         *
-        * @param $desc String: the test name
+        * Refactored in 1.22 to use ParserTestResult
+        *
+        * @param $testResult ParserTestResult
         * @return Boolean
         */
-       protected function showSuccess( $desc ) {
+       protected function showSuccess( ParserTestResult $testResult ) {
                if ( $this->showProgress ) {
                        print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
                }
@@ -1084,28 +1169,29 @@ class ParserTest {
         * 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
+        * Refactored in 1.22 to use ParserTestResult
+        *
+        * @param $testResult ParserTestResult
         * @return Boolean
         */
-       protected function showFailure( $desc, $result, $html ) {
+       protected function showFailure( ParserTestResult $testResult ) {
                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 );
+                               $this->showTesting( $testResult->description );
                        }
 
                        print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
 
                        if ( $this->showOutput ) {
-                               print "--- Expected ---\n$result\n--- Actual ---\n$html\n";
+                               print "--- Expected ---\n{$testResult->expected}\n";
+                               print "--- Actual ---\n{$testResult->actual}\n";
                        }
 
                        if ( $this->showDiffs ) {
-                               print $this->quickDiff( $result, $html );
-                               if ( !$this->wellFormed( $html ) ) {
+                               print $this->quickDiff( $testResult->expected, $testResult->actual );
+                               if ( !$this->wellFormed( $testResult->actual ) ) {
                                        print "XML error: $this->mXmlError\n";
                                }
                        }
@@ -1135,14 +1221,15 @@ class ParserTest {
                $outfile = "$prefix-$outFileTail";
                $this->dumpToFile( $output, $outfile );
 
-               $shellInfile = wfEscapeShellArg($infile);
-               $shellOutfile = wfEscapeShellArg($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`;
+               $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
+
+               $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
+
                unlink( $infile );
                unlink( $outfile );
 
@@ -1172,7 +1259,7 @@ class ParserTest {
                return preg_replace(
                        array( '/^(-.*)$/m', '/^(\+.*)$/m' ),
                        array( $this->term->color( 34 ) . '$1' . $this->term->reset(),
-                                  $this->term->color( 31 ) . '$1' . $this->term->reset() ),
+                               $this->term->color( 31 ) . '$1' . $this->term->reset() ),
                        $text );
        }
 
@@ -1195,7 +1282,7 @@ class ParserTest {
         * @param $line Integer: the input line number, for reporting errors
         * @param $ignoreDuplicate Boolean: whether to silently ignore duplicate pages
         */
-       static public function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
+       public static function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
                global $wgCapitalLinks;
 
                $oldCapitalLinks = $wgCapitalLinks;
@@ -1237,7 +1324,7 @@ class ParserTest {
        public function requireHook( $name ) {
                global $wgParser;
 
-               $wgParser->firstCallInit( ); // make sure hooks are loaded.
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
 
                if ( isset( $wgParser->mTagHooks[$name] ) ) {
                        $this->hooks[$name] = $wgParser->mTagHooks[$name];
@@ -1260,7 +1347,7 @@ class ParserTest {
        public function requireFunctionHook( $name ) {
                global $wgParser;
 
-               $wgParser->firstCallInit( ); // make sure hooks are loaded.
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
 
                if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
                        $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
@@ -1292,9 +1379,9 @@ class ParserTest {
        private function wellFormed( $text ) {
                $html =
                        Sanitizer::hackDocType() .
-                       '<html>' .
-                       $text .
-                       '</html>';
+                               '<html>' .
+                               $text .
+                               '</html>';
 
                $parser = xml_parser_create( "UTF-8" );