r27669 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r27668‎ | r27669 | r27670 >
Date:11:07, 20 November 2007
Author:tstarling
Status:old
Tags:
Comment:
A differential fuzz tester for the wikitext preprocessor. My changes don't pass it completely, but it was useful anyway, it found lots of bugs for me. Maybe it'll be useful to someone else some day. Best used with Parser_DiffTest.
Modified paths:
  • /trunk/phase3/maintenance/preprocessorFuzzTest.php (added) (history)

Diff [purge]

Index: trunk/phase3/maintenance/preprocessorFuzzTest.php
@@ -0,0 +1,156 @@
 2+<?php
 3+
 4+require_once( dirname( __FILE__ ). '/../maintenance/commandLine.inc' );
 5+
 6+$wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'PPFuzzTester::templateHook';
 7+
 8+class PPFuzzTester {
 9+ var $hairs = array(
 10+ '[[', ']]', '{{', '}}', '{{{', '}}}',
 11+ '<', '>', '<nowiki', '<gallery', '</nowiki>', '</gallery>', '<nOwIkI>', '</NoWiKi>',
 12+ //'<!--' , '-->',
 13+ //'<ref>', '</ref>', '<references/>',
 14+ "\n==", "==\n",
 15+ '|', '=', "\n", ' ', "\t", "\x7f",
 16+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
 17+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
 18+ );
 19+ var $minLength = 0;
 20+ var $maxLength = 20;
 21+ var $maxTemplates = 5;
 22+ var $outputTypes = array( 'OT_HTML', 'OT_WIKI', 'OT_MSG', 'OT_PREPROCESS' );
 23+ static $currentTest = false;
 24+
 25+ function execute() {
 26+ if ( !file_exists( 'results' ) ) {
 27+ mkdir( 'results' );
 28+ }
 29+ if ( !is_dir( 'results' ) ) {
 30+ echo "Unable to create 'results' directory\n";
 31+ exit( 1 );
 32+ }
 33+ for ( $i = 0; true; $i++ ) {
 34+ try {
 35+ self::$currentTest = new PPFuzzTest( $this );
 36+ self::$currentTest->execute();
 37+ } catch ( MWException $e ) {
 38+ $testReport = self::$currentTest->getReport();
 39+ $exceptionReport = $e->getText();
 40+ $hash = md5( $testReport );
 41+ file_put_contents( "results/ppft-$hash.in", serialize( self::$currentTest ) );
 42+ file_put_contents( "results/ppft-$hash.fail",
 43+ "Input:\n$testReport\n\nException report:\n$exceptionReport\n" );
 44+ print "Test $hash failed\n";
 45+ }
 46+ if ( $i % 1000 == 0 ) {
 47+ print "$i tests done\n";
 48+ /*
 49+ $testReport = self::$currentTest->getReport();
 50+ $filename = 'results/ppft-' . md5( $testReport ) . '.pass';
 51+ file_put_contents( $filename, "Input:\n$testReport\n" );*/
 52+ }
 53+ }
 54+ }
 55+
 56+ function makeInputText() {
 57+ $length = mt_rand( $this->minLength, $this->maxLength );
 58+ $s = '';
 59+ for ( $i = 0; $i < $length; $i++ ) {
 60+ $hairIndex = mt_rand( 0, count( $this->hairs ) - 1 );
 61+ $s .= $this->hairs[$hairIndex];
 62+ }
 63+ // Send through the UTF-8 normaliser
 64+ // This resolves a few differences between the old preprocessor and the
 65+ // XML-based one, which doesn't like illegals and converts line endings.
 66+ // It's done by the MW UI, so it's a reasonably legitimate thing to do.
 67+ $s = UtfNormal::cleanUp( $s );
 68+ return $s;
 69+ }
 70+
 71+ function makeTitle() {
 72+ return Title::newFromText( mt_rand( 0, 1000000 ), mt_rand( 0, 10 ) );
 73+ }
 74+
 75+ function pickOutputType() {
 76+ $count = count( $this->outputTypes );
 77+ return $this->outputTypes[ mt_rand( 0, $count - 1 ) ];
 78+ }
 79+}
 80+
 81+class PPFuzzTest {
 82+ var $templates, $mainText, $title;
 83+
 84+ function __construct( $tester ) {
 85+ $this->parent = $tester;
 86+ $this->mainText = $tester->makeInputText();
 87+ $this->title = $tester->makeTitle();
 88+ $this->outputType = $tester->pickOutputType();
 89+ $this->templates = array();
 90+ }
 91+
 92+ function templateHook( $title ) {
 93+ $titleText = $title->getPrefixedDBkey();
 94+
 95+ if ( !isset( $this->templates[$titleText] ) ) {
 96+ $finalTitle = $title;
 97+ if ( count( $this->templates ) >= $this->parent->maxTemplates ) {
 98+ // Too many templates
 99+ $text = false;
 100+ } else {
 101+ if ( !mt_rand( 0, 1 ) ) {
 102+ // Redirect
 103+ $finalTitle = $this->parent->makeTitle();
 104+ }
 105+ if ( !mt_rand( 0, 5 ) ) {
 106+ // Doesn't exist
 107+ $text = false;
 108+ } else {
 109+ $text = $this->parent->makeInputText();
 110+ }
 111+ }
 112+ $this->templates[$titleText] = array(
 113+ 'text' => $text,
 114+ 'finalTitle' => $finalTitle );
 115+ }
 116+ return $this->templates[$titleText];
 117+ }
 118+
 119+ function execute() {
 120+ global $wgParser;
 121+ $options = new ParserOptions;
 122+ $options->setTemplateCallback( array( $this, 'templateHook' ) );
 123+ $wgParser->startExternalParse( $this->title, $options, constant( $this->outputType ) );
 124+ return $wgParser->srvus( $this->mainText );
 125+ }
 126+
 127+ function getReport() {
 128+ $s = "Title: " . $this->title->getPrefixedDBkey() . "\n" .
 129+ "Output type: {$this->outputType}\n" .
 130+ "Main text: " . var_export( $this->mainText, true ) . "\n";
 131+ foreach ( $this->templates as $titleText => $template ) {
 132+ $finalTitle = $template['finalTitle'];
 133+ if ( $finalTitle != $titleText ) {
 134+ $s .= "[[$titleText]] -> [[$finalTitle]]: " . var_export( $template['text'], true ) . "\n";
 135+ } else {
 136+ $s .= "[[$titleText]]: " . var_export( $template['text'], true ) . "\n";
 137+ }
 138+ }
 139+ return $s;
 140+ }
 141+}
 142+
 143+ini_set( 'memory_limit', '50M' );
 144+if ( isset( $args[0] ) ) {
 145+ $testText = file_get_contents( $args[0] );
 146+ if ( !$testText ) {
 147+ print "File not found\n";
 148+ exit( 1 );
 149+ }
 150+ $test = unserialize( $testText );
 151+ print $test->getReport();
 152+ $result = $test->execute();
 153+ print "Test passed.\nResult: $result\n";
 154+} else {
 155+ $tester = new PPFuzzTester;
 156+ $tester->execute();
 157+}
Property changes on: trunk/phase3/maintenance/preprocessorFuzzTest.php
___________________________________________________________________
Added: svn:eol-style
1158 + native

Status & tagging log