3use Wikimedia\ScopedCallback;
5require __DIR__ .
'/../../maintenance/Maintenance.php';
8define(
'MW_PARSER_TEST', 1 );
17 parent::__construct();
18 $this->
addDescription(
'Run a fuzz test on the parser, until it segfaults ' .
19 'or throws an exception' );
20 $this->
addOption(
'file',
'Use the specified file as a dictionary, ' .
21 ' or leave blank to use parserTests.txt',
false,
true,
true );
23 $this->
addOption(
'seed',
'Start the fuzz test from the specified seed',
false,
true );
32 $files = $this->
getOption(
'file', [ __DIR__ .
'/parserTests.txt' ] );
33 $this->seed = intval( $this->
getOption(
'seed', 1 ) ) - 1;
47 $dictSize = strlen( $dict );
48 $logMaxLength = log( $this->maxFuzzTestLength );
50 $teardown = $this->parserTest->staticSetup();
51 $teardown = $this->parserTest->setupDatabase( $teardown );
52 $teardown = $this->parserTest->setupUploads( $teardown );
63 ini_set(
'memory_limit', $this->
memoryLimit * 1048576 * 2 );
68 $opts = ParserOptions::newFromUser( $user );
69 $title = Title::makeTitle(
NS_MAIN,
'Parser_test' );
73 mt_srand( ++$this->seed );
74 $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
77 while ( strlen(
$input ) < $totalLength ) {
78 $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
79 $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
80 $offset = mt_rand( 0, $dictSize - $hairLength );
81 $input .= substr( $dict, $offset, $hairLength );
84 $perTestTeardown = $this->parserTest->perTestSetup( $fakeTest );
85 $parser = $this->parserTest->getParser();
91 }
catch ( Exception $exception ) {
96 echo
"Test failed with seed {$this->seed}\n";
98 printf(
"string(%d) \"%s\"\n\n", strlen(
$input ),
$input );
105 ScopedCallback::consume( $perTestTeardown );
107 if ( $numTotal % 100 == 0 ) {
108 $usage = intval( memory_get_usage(
true ) / $this->
memoryLimit / 1048576 * 100 );
109 echo
"{$this->seed}: $numSuccess/$numTotal (mem: $usage%)\n";
110 if ( $usage >= 100 ) {
111 echo
"Out of memory:\n";
114 foreach ( $memStats as $name => $usage ) {
115 echo
"$name: $usage\n";
117 if ( function_exists(
'hphpd_break' ) ) {
137 $classes = get_declared_classes();
139 foreach ( $classes as $class ) {
140 $rc =
new ReflectionClass( $class );
141 $props = $rc->getStaticProperties();
143 $methods = $rc->getMethods();
145 foreach ( $methods as $method ) {
146 $memStats[$class] += $this->
guessVarSize( $method->getStaticVariables() );
150 $functions = get_defined_functions();
152 foreach ( $functions[
'user'] as $function ) {
153 $rf =
new ReflectionFunction( $function );
154 $memStats[
"$function()"] = $this->
guessVarSize( $rf->getStaticVariables() );
168 MediaWiki\suppressWarnings();
170 MediaWiki\restoreWarnings();
171 }
catch ( Exception
$e ) {
184 foreach ( $filenames as $filename ) {
185 $contents = file_get_contents( $filename );
187 '/!!\s*(input|wikitext)\n(.*?)\n!!\s*(result|html|html\/\*|html\/php)/s',
193 $dict .= $match .
"\n";
$GLOBALS['wgAutoloadClasses']['LocalisationUpdate']
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
memoryLimit()
Normally we disable the memory_limit when running admin scripts.
static requireTestsAutoloader()
Call this to set up the autoloader to allow classes to be used from the tests directory.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
This is a TestRecorder representing a collection of other TestRecorders.
guessVarSize( $var)
Estimate the size of the input variable.
getFuzzInput( $filenames)
Get an input dictionary from a set of parser test files.
__construct()
Default constructor.
finalSetup()
Handle some last-minute setup here.
fuzzTest( $filenames)
Run a fuzz test series Draw input from a set of test files.
execute()
Do the actual work.
getMemoryBreakdown()
Get a memory usage breakdown.
static applyInitialConfig()
This should be called before Setup.php, e.g.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
do that in ParserLimitReportFormat instead $parser
Allows to change the fields on the form that will be generated $name
returning false will NOT prevent logging $e
require_once RUN_MAINTENANCE_IF_MAIN
if(is_array($mode)) switch( $mode) $input