The handling of the 'editsection' option prior to I7fb9ffca9 was unusual: it was included in the cache key, but the getter didn't ever flag it as "used". This was overlooked in I7fb9ffca9. This fixes the handling to restore that behavior. It's no longer considered to be a real parser option, so changing it won't make isSafeToCache() fail while reading it won't flag it as 'used'. But to keep Wikibase working (see T85252), if 'editsection' is supplied in $forOptions optionsHash() will still include it in the hash so whatever Wikibase is doing by forcing that doesn't break. The hash when it is included is the same as was used in I7fb9ffca9 to reuse keys. Once optionsHashPre30() is removed, Wikibase should be changed to use some other method to fix T85252 so we can remove that hack from optionsHash(). Change-Id: I77b5519c5a1122a1fafbfc523b77b2268c0efeb1
238 lines
6.8 KiB
PHP
238 lines
6.8 KiB
PHP
<?php
|
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
use Wikimedia\ScopedCallback;
|
|
|
|
class ParserOptionsTest extends MediaWikiTestCase {
|
|
|
|
/**
|
|
* @dataProvider provideIsSafeToCache
|
|
* @param bool $expect Expected value
|
|
* @param array $options Options to set
|
|
*/
|
|
public function testIsSafeToCache( $expect, $options ) {
|
|
$popt = ParserOptions::newCanonical();
|
|
foreach ( $options as $name => $value ) {
|
|
$popt->setOption( $name, $value );
|
|
}
|
|
$this->assertSame( $expect, $popt->isSafeToCache() );
|
|
}
|
|
|
|
public static function provideIsSafeToCache() {
|
|
return [
|
|
'No overrides' => [ true, [] ],
|
|
'In-key options are ok' => [ true, [
|
|
'thumbsize' => 1e100,
|
|
'wrapclass' => false,
|
|
] ],
|
|
'Non-in-key options are not ok' => [ false, [
|
|
'removeComments' => false,
|
|
] ],
|
|
'Canonical override, not default (1)' => [ true, [
|
|
'tidy' => true,
|
|
] ],
|
|
'Canonical override, not default (2)' => [ false, [
|
|
'tidy' => false,
|
|
] ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideOptionsHashPre30
|
|
* @param array $usedOptions Used options
|
|
* @param string $expect Expected value
|
|
* @param array $options Options to set
|
|
* @param array $globals Globals to set
|
|
*/
|
|
public function testOptionsHashPre30( $usedOptions, $expect, $options, $globals = [] ) {
|
|
global $wgHooks;
|
|
|
|
$globals += [
|
|
'wgRenderHashAppend' => '',
|
|
'wgHooks' => [],
|
|
];
|
|
$globals['wgHooks'] += [
|
|
'PageRenderingHash' => [],
|
|
] + $wgHooks;
|
|
$this->setMwGlobals( $globals );
|
|
|
|
$popt = new ParserOptions();
|
|
foreach ( $options as $setter => $value ) {
|
|
$popt->$setter( $value );
|
|
}
|
|
$this->assertSame( $expect, $popt->optionsHashPre30( $usedOptions ) );
|
|
}
|
|
|
|
public static function provideOptionsHashPre30() {
|
|
$used = [ 'wrapclass', 'printable' ];
|
|
|
|
return [
|
|
'Canonical options, nothing used' => [ [], '*!*!*!*!*!*', [] ],
|
|
'Canonical options, used some options' => [ $used, '*!*!*!*!*!*', [] ],
|
|
'Used some options, non-default values' => [
|
|
$used,
|
|
'*!*!*!*!*!*!printable=1!wrapclass=foobar',
|
|
[
|
|
'setWrapOutputClass' => 'foobar',
|
|
'setIsPrintable' => true,
|
|
]
|
|
],
|
|
'Canonical options, nothing used, but with hooks and $wgRenderHashAppend' => [
|
|
[],
|
|
'*!*!*!*!*!wgRenderHashAppend!*!onPageRenderingHash',
|
|
[],
|
|
[
|
|
'wgRenderHashAppend' => '!wgRenderHashAppend',
|
|
'wgHooks' => [ 'PageRenderingHash' => [ [ __CLASS__ . '::onPageRenderingHash' ] ] ],
|
|
]
|
|
],
|
|
|
|
// Test weird historical behavior is still weird
|
|
'Canonical options, editsection=true used' => [ [ 'editsection' ], '*!*!*!*!*', [
|
|
'setEditSection' => true,
|
|
] ],
|
|
'Canonical options, editsection=false used' => [ [ 'editsection' ], '*!*!*!*!*!edit=0', [
|
|
'setEditSection' => false,
|
|
] ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideOptionsHash
|
|
* @param array $usedOptions Used options
|
|
* @param string $expect Expected value
|
|
* @param array $options Options to set
|
|
* @param array $globals Globals to set
|
|
*/
|
|
public function testOptionsHash( $usedOptions, $expect, $options, $globals = [] ) {
|
|
global $wgHooks;
|
|
|
|
$globals += [
|
|
'wgRenderHashAppend' => '',
|
|
'wgHooks' => [],
|
|
];
|
|
$globals['wgHooks'] += [
|
|
'PageRenderingHash' => [],
|
|
] + $wgHooks;
|
|
$this->setMwGlobals( $globals );
|
|
|
|
$popt = ParserOptions::newCanonical();
|
|
foreach ( $options as $name => $value ) {
|
|
$popt->setOption( $name, $value );
|
|
}
|
|
$this->assertSame( $expect, $popt->optionsHash( $usedOptions ) );
|
|
}
|
|
|
|
public static function provideOptionsHash() {
|
|
$used = [ 'wrapclass', 'printable' ];
|
|
|
|
$classWrapper = TestingAccessWrapper::newFromClass( ParserOptions::class );
|
|
$classWrapper->getDefaults();
|
|
$allUsableOptions = array_diff(
|
|
array_keys( $classWrapper->inCacheKey ),
|
|
array_keys( $classWrapper->lazyOptions )
|
|
);
|
|
|
|
return [
|
|
'Canonical options, nothing used' => [ [], 'canonical', [] ],
|
|
'Canonical options, used some options' => [ $used, 'canonical', [] ],
|
|
'Used some options, non-default values' => [
|
|
$used,
|
|
'printable=1!wrapclass=foobar',
|
|
[
|
|
'wrapclass' => 'foobar',
|
|
'printable' => true,
|
|
]
|
|
],
|
|
'Canonical options, used all non-lazy options' => [ $allUsableOptions, 'canonical', [] ],
|
|
'Canonical options, nothing used, but with hooks and $wgRenderHashAppend' => [
|
|
[],
|
|
'canonical!wgRenderHashAppend!onPageRenderingHash',
|
|
[],
|
|
[
|
|
'wgRenderHashAppend' => '!wgRenderHashAppend',
|
|
'wgHooks' => [ 'PageRenderingHash' => [ [ __CLASS__ . '::onPageRenderingHash' ] ] ],
|
|
]
|
|
],
|
|
];
|
|
}
|
|
|
|
public static function onPageRenderingHash( &$confstr ) {
|
|
$confstr .= '!onPageRenderingHash';
|
|
}
|
|
|
|
// Test weird historical behavior is still weird
|
|
public function testOptionsHashEditSection() {
|
|
global $wgHooks;
|
|
|
|
$this->setMwGlobals( [
|
|
'wgRenderHashAppend' => '',
|
|
'wgHooks' => [ 'PageRenderingHash' => [] ] + $wgHooks,
|
|
] );
|
|
|
|
$popt = ParserOptions::newCanonical();
|
|
$popt->registerWatcher( function ( $name ) {
|
|
$this->assertNotEquals( 'editsection', $name );
|
|
} );
|
|
|
|
$this->assertTrue( $popt->getEditSection() );
|
|
$this->assertSame( 'canonical', $popt->optionsHash( [] ) );
|
|
$this->assertSame( 'canonical', $popt->optionsHash( [ 'editsection' ] ) );
|
|
|
|
$popt->setEditSection( false );
|
|
$this->assertFalse( $popt->getEditSection() );
|
|
$this->assertSame( 'canonical', $popt->optionsHash( [] ) );
|
|
$this->assertSame( 'editsection=0', $popt->optionsHash( [ 'editsection' ] ) );
|
|
}
|
|
|
|
/**
|
|
* @expectedException InvalidArgumentException
|
|
* @expectedExceptionMessage Unknown parser option bogus
|
|
*/
|
|
public function testGetInvalidOption() {
|
|
$popt = ParserOptions::newCanonical();
|
|
$popt->getOption( 'bogus' );
|
|
}
|
|
|
|
/**
|
|
* @expectedException InvalidArgumentException
|
|
* @expectedExceptionMessage Unknown parser option bogus
|
|
*/
|
|
public function testSetInvalidOption() {
|
|
$popt = ParserOptions::newCanonical();
|
|
$popt->setOption( 'bogus', true );
|
|
}
|
|
|
|
public function testMatches() {
|
|
$classWrapper = TestingAccessWrapper::newFromClass( ParserOptions::class );
|
|
$oldDefaults = $classWrapper->defaults;
|
|
$oldLazy = $classWrapper->lazyOptions;
|
|
$reset = new ScopedCallback( function () use ( $classWrapper, $oldDefaults, $oldLazy ) {
|
|
$classWrapper->defaults = $oldDefaults;
|
|
$classWrapper->lazyOptions = $oldLazy;
|
|
} );
|
|
|
|
$popt1 = ParserOptions::newCanonical();
|
|
$popt2 = ParserOptions::newCanonical();
|
|
$this->assertTrue( $popt1->matches( $popt2 ) );
|
|
|
|
$popt1->enableLimitReport( true );
|
|
$popt2->enableLimitReport( false );
|
|
$this->assertTrue( $popt1->matches( $popt2 ) );
|
|
|
|
$popt2->setTidy( !$popt2->getTidy() );
|
|
$this->assertFalse( $popt1->matches( $popt2 ) );
|
|
|
|
$ctr = 0;
|
|
$classWrapper->defaults += [ __METHOD__ => null ];
|
|
$classWrapper->lazyOptions += [ __METHOD__ => function () use ( &$ctr ) {
|
|
return ++$ctr;
|
|
} ];
|
|
$popt1 = ParserOptions::newCanonical();
|
|
$popt2 = ParserOptions::newCanonical();
|
|
$this->assertFalse( $popt1->matches( $popt2 ) );
|
|
|
|
ScopedCallback::consume( $reset );
|
|
}
|
|
|
|
}
|