Mass conversion of $wgContLang to service
[lhc/web/wiklou.git] / tests / phpunit / includes / parser / ParserOptionsTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4 use Wikimedia\ScopedCallback;
5
6 /**
7 * @covers ParserOptions
8 */
9 class ParserOptionsTest extends MediaWikiTestCase {
10
11 private static function clearCache() {
12 $wrap = TestingAccessWrapper::newFromClass( ParserOptions::class );
13 $wrap->defaults = null;
14 $wrap->lazyOptions = [
15 'dateformat' => [ ParserOptions::class, 'initDateFormat' ],
16 ];
17 $wrap->inCacheKey = [
18 'dateformat' => true,
19 'numberheadings' => true,
20 'thumbsize' => true,
21 'stubthreshold' => true,
22 'printable' => true,
23 'userlang' => true,
24 ];
25 }
26
27 protected function setUp() {
28 parent::setUp();
29 self::clearCache();
30
31 $this->setMwGlobals( [
32 'wgRenderHashAppend' => '',
33 ] );
34
35 // This is crazy, but registering false, null, or other falsey values
36 // as a hook callback "works".
37 $this->setTemporaryHook( 'PageRenderingHash', null );
38 }
39
40 protected function tearDown() {
41 self::clearCache();
42 parent::tearDown();
43 }
44
45 public function testNewCanonical() {
46 $wgUser = $this->getMutableTestUser()->getUser();
47 $wgLang = Language::factory( 'fr' );
48 $contLang = Language::factory( 'qqx' );
49
50 $this->setContentLang( $contLang );
51 $this->setMwGlobals( [
52 'wgUser' => $wgUser,
53 'wgLang' => $wgLang,
54 ] );
55
56 $user = $this->getMutableTestUser()->getUser();
57 $lang = Language::factory( 'de' );
58 $lang2 = Language::factory( 'bug' );
59 $context = new DerivativeContext( RequestContext::getMain() );
60 $context->setUser( $user );
61 $context->setLanguage( $lang );
62
63 // No parameters picks up $wgUser and $wgLang
64 $popt = ParserOptions::newCanonical();
65 $this->assertSame( $wgUser, $popt->getUser() );
66 $this->assertSame( $wgLang, $popt->getUserLangObj() );
67
68 // Just a user uses $wgLang
69 $popt = ParserOptions::newCanonical( $user );
70 $this->assertSame( $user, $popt->getUser() );
71 $this->assertSame( $wgLang, $popt->getUserLangObj() );
72
73 // Just a language uses $wgUser
74 $popt = ParserOptions::newCanonical( null, $lang );
75 $this->assertSame( $wgUser, $popt->getUser() );
76 $this->assertSame( $lang, $popt->getUserLangObj() );
77
78 // Passing both works
79 $popt = ParserOptions::newCanonical( $user, $lang );
80 $this->assertSame( $user, $popt->getUser() );
81 $this->assertSame( $lang, $popt->getUserLangObj() );
82
83 // Passing 'canonical' uses an anon and $contLang, and ignores any passed $userLang
84 $popt = ParserOptions::newCanonical( 'canonical' );
85 $this->assertTrue( $popt->getUser()->isAnon() );
86 $this->assertSame( $contLang, $popt->getUserLangObj() );
87 $popt = ParserOptions::newCanonical( 'canonical', $lang2 );
88 $this->assertSame( $contLang, $popt->getUserLangObj() );
89
90 // Passing an IContextSource uses the user and lang from it, and ignores
91 // any passed $userLang
92 $popt = ParserOptions::newCanonical( $context );
93 $this->assertSame( $user, $popt->getUser() );
94 $this->assertSame( $lang, $popt->getUserLangObj() );
95 $popt = ParserOptions::newCanonical( $context, $lang2 );
96 $this->assertSame( $lang, $popt->getUserLangObj() );
97
98 // Passing something else raises an exception
99 try {
100 $popt = ParserOptions::newCanonical( 'bogus' );
101 $this->fail( 'Excpected exception not thrown' );
102 } catch ( InvalidArgumentException $ex ) {
103 }
104 }
105
106 /**
107 * @dataProvider provideIsSafeToCache
108 * @param bool $expect Expected value
109 * @param array $options Options to set
110 */
111 public function testIsSafeToCache( $expect, $options ) {
112 $popt = ParserOptions::newCanonical();
113 foreach ( $options as $name => $value ) {
114 $popt->setOption( $name, $value );
115 }
116 $this->assertSame( $expect, $popt->isSafeToCache() );
117 }
118
119 public static function provideIsSafeToCache() {
120 return [
121 'No overrides' => [ true, [] ],
122 'In-key options are ok' => [ true, [
123 'thumbsize' => 1e100,
124 'printable' => false,
125 ] ],
126 'Non-in-key options are not ok' => [ false, [
127 'removeComments' => false,
128 ] ],
129 'Non-in-key options are not ok (2)' => [ false, [
130 'wrapclass' => 'foobar',
131 ] ],
132 'Canonical override, not default (1)' => [ true, [
133 'tidy' => true,
134 ] ],
135 'Canonical override, not default (2)' => [ false, [
136 'tidy' => false,
137 ] ],
138 ];
139 }
140
141 /**
142 * @dataProvider provideOptionsHash
143 * @param array $usedOptions Used options
144 * @param string $expect Expected value
145 * @param array $options Options to set
146 * @param array $globals Globals to set
147 * @param callable|null $hookFunc PageRenderingHash hook function
148 */
149 public function testOptionsHash(
150 $usedOptions, $expect, $options, $globals = [], $hookFunc = null
151 ) {
152 $this->setMwGlobals( $globals );
153 $this->setTemporaryHook( 'PageRenderingHash', $hookFunc );
154
155 $popt = ParserOptions::newCanonical();
156 foreach ( $options as $name => $value ) {
157 $popt->setOption( $name, $value );
158 }
159 $this->assertSame( $expect, $popt->optionsHash( $usedOptions ) );
160 }
161
162 public static function provideOptionsHash() {
163 $used = [ 'thumbsize', 'printable' ];
164
165 $classWrapper = TestingAccessWrapper::newFromClass( ParserOptions::class );
166 $classWrapper->getDefaults();
167 $allUsableOptions = array_diff(
168 array_keys( $classWrapper->inCacheKey ),
169 array_keys( $classWrapper->lazyOptions )
170 );
171
172 return [
173 'Canonical options, nothing used' => [ [], 'canonical', [] ],
174 'Canonical options, used some options' => [ $used, 'canonical', [] ],
175 'Used some options, non-default values' => [
176 $used,
177 'printable=1!thumbsize=200',
178 [
179 'thumbsize' => 200,
180 'printable' => true,
181 ]
182 ],
183 'Canonical options, used all non-lazy options' => [ $allUsableOptions, 'canonical', [] ],
184 'Canonical options, nothing used, but with hooks and $wgRenderHashAppend' => [
185 [],
186 'canonical!wgRenderHashAppend!onPageRenderingHash',
187 [],
188 [ 'wgRenderHashAppend' => '!wgRenderHashAppend' ],
189 [ __CLASS__ . '::onPageRenderingHash' ],
190 ],
191 ];
192 }
193
194 public function testUsedLazyOptionsInHash() {
195 $this->setTemporaryHook( 'ParserOptionsRegister',
196 function ( &$defaults, &$inCacheKey, &$lazyOptions ) {
197 $lazyFuncs = $this->getMockBuilder( stdClass::class )
198 ->setMethods( [ 'neverCalled', 'calledOnce' ] )
199 ->getMock();
200 $lazyFuncs->expects( $this->never() )->method( 'neverCalled' );
201 $lazyFuncs->expects( $this->once() )->method( 'calledOnce' )->willReturn( 'value' );
202
203 $defaults += [
204 'opt1' => null,
205 'opt2' => null,
206 'opt3' => null,
207 ];
208 $inCacheKey += [
209 'opt1' => true,
210 'opt2' => true,
211 ];
212 $lazyOptions += [
213 'opt1' => [ $lazyFuncs, 'calledOnce' ],
214 'opt2' => [ $lazyFuncs, 'neverCalled' ],
215 'opt3' => [ $lazyFuncs, 'neverCalled' ],
216 ];
217 }
218 );
219
220 self::clearCache();
221
222 $popt = ParserOptions::newCanonical();
223 $popt->registerWatcher( function () {
224 $this->fail( 'Watcher should not have been called' );
225 } );
226 $this->assertSame( 'opt1=value', $popt->optionsHash( [ 'opt1', 'opt3' ] ) );
227
228 // Second call to see that opt1 isn't resolved a second time
229 $this->assertSame( 'opt1=value', $popt->optionsHash( [ 'opt1', 'opt3' ] ) );
230 }
231
232 public static function onPageRenderingHash( &$confstr ) {
233 $confstr .= '!onPageRenderingHash';
234 }
235
236 /**
237 * @expectedException InvalidArgumentException
238 * @expectedExceptionMessage Unknown parser option bogus
239 */
240 public function testGetInvalidOption() {
241 $popt = ParserOptions::newCanonical();
242 $popt->getOption( 'bogus' );
243 }
244
245 /**
246 * @expectedException InvalidArgumentException
247 * @expectedExceptionMessage Unknown parser option bogus
248 */
249 public function testSetInvalidOption() {
250 $popt = ParserOptions::newCanonical();
251 $popt->setOption( 'bogus', true );
252 }
253
254 public function testMatches() {
255 $classWrapper = TestingAccessWrapper::newFromClass( ParserOptions::class );
256 $oldDefaults = $classWrapper->defaults;
257 $oldLazy = $classWrapper->lazyOptions;
258 $reset = new ScopedCallback( function () use ( $classWrapper, $oldDefaults, $oldLazy ) {
259 $classWrapper->defaults = $oldDefaults;
260 $classWrapper->lazyOptions = $oldLazy;
261 } );
262
263 $popt1 = ParserOptions::newCanonical();
264 $popt2 = ParserOptions::newCanonical();
265 $this->assertTrue( $popt1->matches( $popt2 ) );
266
267 $popt1->enableLimitReport( true );
268 $popt2->enableLimitReport( false );
269 $this->assertTrue( $popt1->matches( $popt2 ) );
270
271 $popt2->setTidy( !$popt2->getTidy() );
272 $this->assertFalse( $popt1->matches( $popt2 ) );
273
274 $ctr = 0;
275 $classWrapper->defaults += [ __METHOD__ => null ];
276 $classWrapper->lazyOptions += [ __METHOD__ => function () use ( &$ctr ) {
277 return ++$ctr;
278 } ];
279 $popt1 = ParserOptions::newCanonical();
280 $popt2 = ParserOptions::newCanonical();
281 $this->assertFalse( $popt1->matches( $popt2 ) );
282
283 ScopedCallback::consume( $reset );
284 }
285
286 public function testAllCacheVaryingOptions() {
287 $this->setTemporaryHook( 'ParserOptionsRegister', null );
288 $this->assertSame( [
289 'dateformat', 'numberheadings', 'printable', 'stubthreshold',
290 'thumbsize', 'userlang'
291 ], ParserOptions::allCacheVaryingOptions() );
292
293 self::clearCache();
294
295 $this->setTemporaryHook( 'ParserOptionsRegister', function ( &$defaults, &$inCacheKey ) {
296 $defaults += [
297 'foo' => 'foo',
298 'bar' => 'bar',
299 'baz' => 'baz',
300 ];
301 $inCacheKey += [
302 'foo' => true,
303 'bar' => false,
304 ];
305 } );
306 $this->assertSame( [
307 'dateformat', 'foo', 'numberheadings', 'printable', 'stubthreshold',
308 'thumbsize', 'userlang'
309 ], ParserOptions::allCacheVaryingOptions() );
310 }
311
312 }