]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - maintenance/preprocessorFuzzTest.php
MediaWiki 1.17.0
[autoinstallsdev/mediawiki.git] / maintenance / preprocessorFuzzTest.php
1 <?php
2 /**
3  * @file
4  * @ingroup Maintenance
5  */
6
7 require_once( dirname( __FILE__ ) . '/commandLine.inc' );
8
9 $wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'PPFuzzTester::templateHook';
10
11 class PPFuzzTester {
12         var $hairs = array(
13                 '[[', ']]', '{{', '{{', '}}', '}}', '{{{', '}}}',
14                 '<', '>', '<nowiki', '<gallery', '</nowiki>', '</gallery>', '<nOwIkI>', '</NoWiKi>',
15                 '<!--' , '-->',
16                 "\n==", "==\n",
17                 '|', '=', "\n", ' ', "\t", "\x7f",
18                 '~~', '~~~', '~~~~', 'subst:',
19                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
20                 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
21
22                 // extensions
23                 // '<ref>', '</ref>', '<references/>',
24         );
25         var $minLength = 0;
26         var $maxLength = 20;
27         var $maxTemplates = 5;
28         // var $outputTypes = array( 'OT_HTML', 'OT_WIKI', 'OT_PREPROCESS' );
29         var $entryPoints = array( 'testSrvus', 'testPst', 'testPreprocess' );
30         var $verbose = false;
31         static $currentTest = false;
32
33         function execute() {
34                 if ( !file_exists( 'results' ) ) {
35                         mkdir( 'results' );
36                 }
37                 if ( !is_dir( 'results' ) ) {
38                         echo "Unable to create 'results' directory\n";
39                         exit( 1 );
40                 }
41                 $overallStart = microtime( true );
42                 $reportInterval = 1000;
43                 for ( $i = 1; true; $i++ ) {
44                         $t = -microtime( true );
45                         try {
46                                 self::$currentTest = new PPFuzzTest( $this );
47                                 self::$currentTest->execute();
48                                 $passed = 'passed';
49                         } catch ( MWException $e ) {
50                                 $testReport = self::$currentTest->getReport();
51                                 $exceptionReport = $e->getText();
52                                 $hash = md5( $testReport );
53                                 file_put_contents( "results/ppft-$hash.in", serialize( self::$currentTest ) );
54                                 file_put_contents( "results/ppft-$hash.fail",
55                                         "Input:\n$testReport\n\nException report:\n$exceptionReport\n" );
56                                 print "Test $hash failed\n";
57                                 $passed = 'failed';
58                         }
59                         $t += microtime( true );
60
61                         if ( $this->verbose ) {
62                                 printf( "Test $passed in %.3f seconds\n", $t );
63                                 print self::$currentTest->getReport();
64                         }
65
66                         $reportMetric = ( microtime( true ) - $overallStart ) / $i * $reportInterval;
67                         if ( $reportMetric > 25 ) {
68                                 if ( substr( $reportInterval, 0, 1 ) === '1' ) {
69                                         $reportInterval /= 2;
70                                 } else {
71                                         $reportInterval /= 5;
72                                 }
73                         } elseif ( $reportMetric < 4 ) {
74                                 if ( substr( $reportInterval, 0, 1 ) === '1' ) {
75                                         $reportInterval *= 5;
76                                 } else {
77                                         $reportInterval *= 2;
78                                 }
79                         }
80                         if ( $i % $reportInterval == 0 ) {
81                                 print "$i tests done\n";
82                                 /*
83                                 $testReport = self::$currentTest->getReport();
84                                 $filename = 'results/ppft-' . md5( $testReport ) . '.pass';
85                                 file_put_contents( $filename, "Input:\n$testReport\n" );*/
86                         }
87                 }
88                 wfLogProfilingData();
89         }
90
91         function makeInputText( $max = false ) {
92                 if ( $max === false ) {
93                         $max = $this->maxLength;
94                 }
95                 $length = mt_rand( $this->minLength, $max );
96                 $s = '';
97                 for ( $i = 0; $i < $length; $i++ ) {
98                         $hairIndex = mt_rand( 0, count( $this->hairs ) - 1 );
99                         $s .= $this->hairs[$hairIndex];
100                 }
101                 // Send through the UTF-8 normaliser
102                 // This resolves a few differences between the old preprocessor and the
103                 // XML-based one, which doesn't like illegals and converts line endings.
104                 // It's done by the MW UI, so it's a reasonably legitimate thing to do.
105                 global $wgContLang;
106                 $s = $wgContLang->normalize( $s );
107                 return $s;
108         }
109
110         function makeTitle() {
111                 return Title::newFromText( mt_rand( 0, 1000000 ), mt_rand( 0, 10 ) );
112         }
113
114         /*
115         function pickOutputType() {
116                 $count = count( $this->outputTypes );
117                 return $this->outputTypes[ mt_rand( 0, $count - 1 ) ];
118         }*/
119
120         function pickEntryPoint() {
121                 $count = count( $this->entryPoints );
122                 return $this->entryPoints[ mt_rand( 0, $count - 1 ) ];
123         }
124 }
125
126 class PPFuzzTest {
127         var $templates, $mainText, $title, $entryPoint, $output;
128
129         function __construct( $tester ) {
130                 global $wgMaxSigChars;
131                 $this->parent = $tester;
132                 $this->mainText = $tester->makeInputText();
133                 $this->title = $tester->makeTitle();
134                 // $this->outputType = $tester->pickOutputType();
135                 $this->entryPoint = $tester->pickEntryPoint();
136                 $this->nickname = $tester->makeInputText( $wgMaxSigChars + 10 );
137                 $this->fancySig = (bool)mt_rand( 0, 1 );
138                 $this->templates = array();
139         }
140
141         function templateHook( $title ) {
142                 $titleText = $title->getPrefixedDBkey();
143
144                 if ( !isset( $this->templates[$titleText] ) ) {
145                         $finalTitle = $title;
146                         if ( count( $this->templates ) >= $this->parent->maxTemplates ) {
147                                 // Too many templates
148                                 $text = false;
149                         } else {
150                                 if ( !mt_rand( 0, 1 ) ) {
151                                         // Redirect
152                                         $finalTitle = $this->parent->makeTitle();
153                                 }
154                                 if ( !mt_rand( 0, 5 ) ) {
155                                         // Doesn't exist
156                                         $text = false;
157                                 } else {
158                                         $text = $this->parent->makeInputText();
159                                 }
160                         }
161                         $this->templates[$titleText] = array(
162                                 'text' => $text,
163                                 'finalTitle' => $finalTitle );
164                 }
165                 return $this->templates[$titleText];
166         }
167
168         function execute() {
169                 global $wgParser, $wgUser;
170
171                 $wgUser = new PPFuzzUser;
172                 $wgUser->mName = 'Fuzz';
173                 $wgUser->mFrom = 'name';
174                 $wgUser->ppfz_test = $this;
175
176                 $options = new ParserOptions;
177                 $options->setTemplateCallback( array( $this, 'templateHook' ) );
178                 $options->setTimestamp( wfTimestampNow() );
179                 $this->output = call_user_func( array( $wgParser, $this->entryPoint ), $this->mainText, $this->title->getPrefixedText(), $options );
180                 return $this->output;
181         }
182
183         function getReport() {
184                 $s = "Title: " . $this->title->getPrefixedDBkey() . "\n" .
185 //                      "Output type: {$this->outputType}\n" .
186                         "Entry point: {$this->entryPoint}\n" .
187                         "User: " . ( $this->fancySig ? 'fancy' : 'no-fancy' ) . ' ' . var_export( $this->nickname, true ) . "\n" .
188                         "Main text: " . var_export( $this->mainText, true ) . "\n";
189                 foreach ( $this->templates as $titleText => $template ) {
190                         $finalTitle = $template['finalTitle'];
191                         if ( $finalTitle != $titleText ) {
192                                 $s .= "[[$titleText]] -> [[$finalTitle]]: " . var_export( $template['text'], true ) . "\n";
193                         } else {
194                                 $s .= "[[$titleText]]: " . var_export( $template['text'], true ) . "\n";
195                         }
196                 }
197                 $s .= "Output: " . var_export( $this->output, true ) . "\n";
198                 return $s;
199         }
200 }
201
202 class PPFuzzUser extends User {
203         var $ppfz_test;
204
205         function load() {
206                 if ( $this->mDataLoaded ) {
207                         return;
208                 }
209                 $this->mDataLoaded = true;
210                 $this->loadDefaults( $this->mName );
211         }
212
213         function getOption( $option, $defaultOverride = '' ) {
214                 if ( $option === 'fancysig' ) {
215                         return $this->ppfz_test->fancySig;
216                 } elseif ( $option === 'nickname' ) {
217                         return $this->ppfz_test->nickname;
218                 } else {
219                         return parent::getOption( $option, $defaultOverride );
220                 }
221         }
222 }
223
224 ini_set( 'memory_limit', '50M' );
225 if ( isset( $args[0] ) ) {
226         $testText = file_get_contents( $args[0] );
227         if ( !$testText ) {
228                 print "File not found\n";
229                 exit( 1 );
230         }
231         $test = unserialize( $testText );
232         $result = $test->execute();
233         print "Test passed.\n";
234 } else {
235         $tester = new PPFuzzTester;
236         $tester->verbose = isset( $options['verbose'] );
237         $tester->execute();
238 }