[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/maintenance/ -> preprocessorFuzzTest.php (source)

   1  <?php
   2  /**
   3   * Performs fuzz-style testing of MediaWiki's preprocessor.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup Maintenance
  22   */
  23  
  24  require_once  __DIR__ . '/commandLine.inc';
  25  
  26  $wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'PPFuzzTester::templateHook';
  27  
  28  class PPFuzzTester {
  29      public $hairs = array(
  30          '[[', ']]', '{{', '{{', '}}', '}}', '{{{', '}}}',
  31          '<', '>', '<nowiki', '<gallery', '</nowiki>', '</gallery>', '<nOwIkI>', '</NoWiKi>',
  32          '<!--', '-->',
  33          "\n==", "==\n",
  34          '|', '=', "\n", ' ', "\t", "\x7f",
  35          '~~', '~~~', '~~~~', 'subst:',
  36          'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
  37          'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
  38  
  39          // extensions
  40          // '<ref>', '</ref>', '<references/>',
  41      );
  42      public $minLength = 0;
  43      public $maxLength = 20;
  44      public $maxTemplates = 5;
  45      // public $outputTypes = array( 'OT_HTML', 'OT_WIKI', 'OT_PREPROCESS' );
  46      public $entryPoints = array( 'testSrvus', 'testPst', 'testPreprocess' );
  47      public $verbose = false;
  48  
  49      private static $currentTest = false;
  50  
  51  	function execute() {
  52          if ( !file_exists( 'results' ) ) {
  53              mkdir( 'results' );
  54          }
  55          if ( !is_dir( 'results' ) ) {
  56              echo "Unable to create 'results' directory\n";
  57              exit( 1 );
  58          }
  59          $overallStart = microtime( true );
  60          $reportInterval = 1000;
  61          for ( $i = 1; true; $i++ ) {
  62              $t = -microtime( true );
  63              try {
  64                  self::$currentTest = new PPFuzzTest( $this );
  65                  self::$currentTest->execute();
  66                  $passed = 'passed';
  67              } catch ( MWException $e ) {
  68                  $testReport = self::$currentTest->getReport();
  69                  $exceptionReport = $e->getText();
  70                  $hash = md5( $testReport );
  71                  file_put_contents( "results/ppft-$hash.in", serialize( self::$currentTest ) );
  72                  file_put_contents( "results/ppft-$hash.fail",
  73                      "Input:\n$testReport\n\nException report:\n$exceptionReport\n" );
  74                  print "Test $hash failed\n";
  75                  $passed = 'failed';
  76              }
  77              $t += microtime( true );
  78  
  79              if ( $this->verbose ) {
  80                  printf( "Test $passed in %.3f seconds\n", $t );
  81                  print self::$currentTest->getReport();
  82              }
  83  
  84              $reportMetric = ( microtime( true ) - $overallStart ) / $i * $reportInterval;
  85              if ( $reportMetric > 25 ) {
  86                  if ( substr( $reportInterval, 0, 1 ) === '1' ) {
  87                      $reportInterval /= 2;
  88                  } else {
  89                      $reportInterval /= 5;
  90                  }
  91              } elseif ( $reportMetric < 4 ) {
  92                  if ( substr( $reportInterval, 0, 1 ) === '1' ) {
  93                      $reportInterval *= 5;
  94                  } else {
  95                      $reportInterval *= 2;
  96                  }
  97              }
  98              if ( $i % $reportInterval == 0 ) {
  99                  print "$i tests done\n";
 100                  /*
 101                  $testReport = self::$currentTest->getReport();
 102                  $filename = 'results/ppft-' . md5( $testReport ) . '.pass';
 103                  file_put_contents( $filename, "Input:\n$testReport\n" );*/
 104              }
 105          }
 106      }
 107  
 108  	function makeInputText( $max = false ) {
 109          if ( $max === false ) {
 110              $max = $this->maxLength;
 111          }
 112          $length = mt_rand( $this->minLength, $max );
 113          $s = '';
 114          for ( $i = 0; $i < $length; $i++ ) {
 115              $hairIndex = mt_rand( 0, count( $this->hairs ) - 1 );
 116              $s .= $this->hairs[$hairIndex];
 117          }
 118          // Send through the UTF-8 normaliser
 119          // This resolves a few differences between the old preprocessor and the
 120          // XML-based one, which doesn't like illegals and converts line endings.
 121          // It's done by the MW UI, so it's a reasonably legitimate thing to do.
 122          global $wgContLang;
 123          $s = $wgContLang->normalize( $s );
 124  
 125          return $s;
 126      }
 127  
 128  	function makeTitle() {
 129          return Title::newFromText( mt_rand( 0, 1000000 ), mt_rand( 0, 10 ) );
 130      }
 131  
 132      /*
 133      function pickOutputType() {
 134          $count = count( $this->outputTypes );
 135          return $this->outputTypes[ mt_rand( 0, $count - 1 ) ];
 136      }*/
 137  
 138  	function pickEntryPoint() {
 139          $count = count( $this->entryPoints );
 140  
 141          return $this->entryPoints[mt_rand( 0, $count - 1 )];
 142      }
 143  }
 144  
 145  class PPFuzzTest {
 146      public $templates, $mainText, $title, $entryPoint, $output;
 147  
 148  	function __construct( $tester ) {
 149          global $wgMaxSigChars;
 150          $this->parent = $tester;
 151          $this->mainText = $tester->makeInputText();
 152          $this->title = $tester->makeTitle();
 153          // $this->outputType = $tester->pickOutputType();
 154          $this->entryPoint = $tester->pickEntryPoint();
 155          $this->nickname = $tester->makeInputText( $wgMaxSigChars + 10 );
 156          $this->fancySig = (bool)mt_rand( 0, 1 );
 157          $this->templates = array();
 158      }
 159  
 160      /**
 161       * @param Title $title
 162       * @return array
 163       */
 164  	function templateHook( $title ) {
 165          $titleText = $title->getPrefixedDBkey();
 166  
 167          if ( !isset( $this->templates[$titleText] ) ) {
 168              $finalTitle = $title;
 169              if ( count( $this->templates ) >= $this->parent->maxTemplates ) {
 170                  // Too many templates
 171                  $text = false;
 172              } else {
 173                  if ( !mt_rand( 0, 1 ) ) {
 174                      // Redirect
 175                      $finalTitle = $this->parent->makeTitle();
 176                  }
 177                  if ( !mt_rand( 0, 5 ) ) {
 178                      // Doesn't exist
 179                      $text = false;
 180                  } else {
 181                      $text = $this->parent->makeInputText();
 182                  }
 183              }
 184              $this->templates[$titleText] = array(
 185                  'text' => $text,
 186                  'finalTitle' => $finalTitle );
 187          }
 188  
 189          return $this->templates[$titleText];
 190      }
 191  
 192  	function execute() {
 193          global $wgParser, $wgUser;
 194  
 195          $wgUser = new PPFuzzUser;
 196          $wgUser->mName = 'Fuzz';
 197          $wgUser->mFrom = 'name';
 198          $wgUser->ppfz_test = $this;
 199  
 200          $options = ParserOptions::newFromUser( $wgUser );
 201          $options->setTemplateCallback( array( $this, 'templateHook' ) );
 202          $options->setTimestamp( wfTimestampNow() );
 203          $this->output = call_user_func(
 204              array( $wgParser, $this->entryPoint ),
 205              $this->mainText,
 206              $this->title,
 207              $options
 208          );
 209  
 210          return $this->output;
 211      }
 212  
 213  	function getReport() {
 214          $s = "Title: " . $this->title->getPrefixedDBkey() . "\n" .
 215  //            "Output type: {$this->outputType}\n" .
 216              "Entry point: {$this->entryPoint}\n" .
 217              "User: " . ( $this->fancySig ? 'fancy' : 'no-fancy' ) .
 218              ' ' . var_export( $this->nickname, true ) . "\n" .
 219              "Main text: " . var_export( $this->mainText, true ) . "\n";
 220          foreach ( $this->templates as $titleText => $template ) {
 221              $finalTitle = $template['finalTitle'];
 222              if ( $finalTitle != $titleText ) {
 223                  $s .= "[[$titleText]] -> [[$finalTitle]]: " . var_export( $template['text'], true ) . "\n";
 224              } else {
 225                  $s .= "[[$titleText]]: " . var_export( $template['text'], true ) . "\n";
 226              }
 227          }
 228          $s .= "Output: " . var_export( $this->output, true ) . "\n";
 229  
 230          return $s;
 231      }
 232  }
 233  
 234  class PPFuzzUser extends User {
 235      public $ppfz_test, $mDataLoaded;
 236  
 237  	function load() {
 238          if ( $this->mDataLoaded ) {
 239              return;
 240          }
 241          $this->mDataLoaded = true;
 242          $this->loadDefaults( $this->mName );
 243      }
 244  
 245  	function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) {
 246          if ( $oname === 'fancysig' ) {
 247              return $this->ppfz_test->fancySig;
 248          } elseif ( $oname === 'nickname' ) {
 249              return $this->ppfz_test->nickname;
 250          } else {
 251              return parent::getOption( $oname, $defaultOverride, $ignoreHidden );
 252          }
 253      }
 254  }
 255  
 256  ini_set( 'memory_limit', '50M' );
 257  if ( isset( $args[0] ) ) {
 258      $testText = file_get_contents( $args[0] );
 259      if ( !$testText ) {
 260          print "File not found\n";
 261          exit( 1 );
 262      }
 263      $test = unserialize( $testText );
 264      $result = $test->execute();
 265      print "Test passed.\n";
 266  } else {
 267      $tester = new PPFuzzTester;
 268      $tester->verbose = isset( $options['verbose'] );
 269      $tester->execute();
 270  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1