MediaWiki REL1_33
ParserOutputTest.php
Go to the documentation of this file.
1<?php
2use Wikimedia\TestingAccessWrapper;
3
9
10 public static function provideIsLinkInternal() {
11 return [
12 // Different domains
13 [ false, 'http://example.org', 'http://mediawiki.org' ],
14 // Same domains
15 [ true, 'http://example.org', 'http://example.org' ],
16 [ true, 'https://example.org', 'https://example.org' ],
17 [ true, '//example.org', '//example.org' ],
18 // Same domain different cases
19 [ true, 'http://example.org', 'http://EXAMPLE.ORG' ],
20 // Paths, queries, and fragments are not relevant
21 [ true, 'http://example.org', 'http://example.org/wiki/Main_Page' ],
22 [ true, 'http://example.org', 'http://example.org?my=query' ],
23 [ true, 'http://example.org', 'http://example.org#its-a-fragment' ],
24 // Different protocols
25 [ false, 'http://example.org', 'https://example.org' ],
26 [ false, 'https://example.org', 'http://example.org' ],
27 // Protocol relative servers always match http and https links
28 [ true, '//example.org', 'http://example.org' ],
29 [ true, '//example.org', 'https://example.org' ],
30 // But they don't match strange things like this
31 [ false, '//example.org', 'irc://example.org' ],
32 ];
33 }
34
35 public function tearDown() {
36 MWTimestamp::setFakeTime( false );
37
38 parent::tearDown();
39 }
40
46 public function testIsLinkInternal( $shouldMatch, $server, $url ) {
47 $this->assertEquals( $shouldMatch, ParserOutput::isLinkInternal( $server, $url ) );
48 }
49
54 public function testExtensionData() {
55 $po = new ParserOutput();
56
57 $po->setExtensionData( "one", "Foo" );
58
59 $this->assertEquals( "Foo", $po->getExtensionData( "one" ) );
60 $this->assertNull( $po->getExtensionData( "spam" ) );
61
62 $po->setExtensionData( "two", "Bar" );
63 $this->assertEquals( "Foo", $po->getExtensionData( "one" ) );
64 $this->assertEquals( "Bar", $po->getExtensionData( "two" ) );
65
66 $po->setExtensionData( "one", null );
67 $this->assertNull( $po->getExtensionData( "one" ) );
68 $this->assertEquals( "Bar", $po->getExtensionData( "two" ) );
69 }
70
77 public function testProperties() {
78 $po = new ParserOutput();
79
80 $po->setProperty( 'foo', 'val' );
81
82 $properties = $po->getProperties();
83 $this->assertEquals( $po->getProperty( 'foo' ), 'val' );
84 $this->assertEquals( $properties['foo'], 'val' );
85
86 $po->setProperty( 'foo', 'second val' );
87
88 $properties = $po->getProperties();
89 $this->assertEquals( $po->getProperty( 'foo' ), 'second val' );
90 $this->assertEquals( $properties['foo'], 'second val' );
91
92 $po->unsetProperty( 'foo' );
93
94 $properties = $po->getProperties();
95 $this->assertEquals( $po->getProperty( 'foo' ), false );
96 $this->assertArrayNotHasKey( 'foo', $properties );
97 }
98
105 public function testWrapperDivClass() {
106 $po = new ParserOutput();
107
108 $po->setText( 'Kittens' );
109 $this->assertContains( 'Kittens', $po->getText() );
110 $this->assertNotContains( '<div', $po->getText() );
111 $this->assertSame( 'Kittens', $po->getRawText() );
112
113 $po->addWrapperDivClass( 'foo' );
114 $text = $po->getText();
115 $this->assertContains( 'Kittens', $text );
116 $this->assertContains( '<div', $text );
117 $this->assertContains( 'class="foo"', $text );
118
119 $po->addWrapperDivClass( 'bar' );
120 $text = $po->getText();
121 $this->assertContains( 'Kittens', $text );
122 $this->assertContains( '<div', $text );
123 $this->assertContains( 'class="foo bar"', $text );
124
125 $po->addWrapperDivClass( 'bar' ); // second time does nothing, no "foo bar bar".
126 $text = $po->getText( [ 'unwrap' => true ] );
127 $this->assertContains( 'Kittens', $text );
128 $this->assertNotContains( '<div', $text );
129 $this->assertNotContains( 'class="foo bar"', $text );
130
131 $text = $po->getText( [ 'wrapperDivClass' => '' ] );
132 $this->assertContains( 'Kittens', $text );
133 $this->assertNotContains( '<div', $text );
134 $this->assertNotContains( 'class="foo bar"', $text );
135
136 $text = $po->getText( [ 'wrapperDivClass' => 'xyzzy' ] );
137 $this->assertContains( 'Kittens', $text );
138 $this->assertContains( '<div', $text );
139 $this->assertContains( 'class="xyzzy"', $text );
140 $this->assertNotContains( 'class="foo bar"', $text );
141
142 $text = $po->getRawText();
143 $this->assertSame( 'Kittens', $text );
144
145 $po->clearWrapperDivClass();
146 $text = $po->getText();
147 $this->assertContains( 'Kittens', $text );
148 $this->assertNotContains( '<div', $text );
149 $this->assertNotContains( 'class="foo bar"', $text );
150 }
151
159 public function testGetText( $options, $text, $expect ) {
160 $this->setMwGlobals( [
161 'wgArticlePath' => '/wiki/$1',
162 'wgScriptPath' => '/w',
163 'wgScript' => '/w/index.php',
164 ] );
165
166 $po = new ParserOutput( $text );
167 $actual = $po->getText( $options );
168 $this->assertSame( $expect, $actual );
169 }
170
171 public static function provideGetText() {
172 // phpcs:disable Generic.Files.LineLength
173 $text = <<<EOF
174<p>Test document.
175</p>
176<mw:toc><div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
177<ul>
178<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
179<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
180<ul>
181<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
182</ul>
183</li>
184<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
185</ul>
186</div>
187</mw:toc>
188<h2><span class="mw-headline" id="Section_1">Section 1</span><mw:editsection page="Test Page" section="1">Section 1</mw:editsection></h2>
189<p>One
190</p>
191<h2><span class="mw-headline" id="Section_2">Section 2</span><mw:editsection page="Test Page" section="2">Section 2</mw:editsection></h2>
192<p>Two
193</p>
194<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><mw:editsection page="Test Page" section="3">Section 2.1</mw:editsection></h3>
195<p>Two point one
196</p>
197<h2><span class="mw-headline" id="Section_3">Section 3</span><mw:editsection page="Test Page" section="4">Section 3</mw:editsection></h2>
198<p>Three
199</p>
200EOF;
201
202 $dedupText = <<<EOF
203<p>This is a test document.</p>
204<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
205<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
206<style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
207<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
208<style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
209<style data-mw-not-deduplicate="duplicate1">.Duplicate1 {}</style>
210<style data-mw-deduplicate="duplicate1">.Same-attribute-different-content {}</style>
211<style data-mw-deduplicate="duplicate3">.Duplicate1 {}</style>
212<style>.Duplicate1 {}</style>
213EOF;
214
215 return [
216 'No options' => [
217 [], $text, <<<EOF
218<p>Test document.
219</p>
220<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
221<ul>
222<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
223<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
224<ul>
225<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
226</ul>
227</li>
228<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
229</ul>
230</div>
231
232<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
233<p>One
234</p>
235<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
236<p>Two
237</p>
238<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
239<p>Two point one
240</p>
241<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
242<p>Three
243</p>
244EOF
245 ],
246 'Disable section edit links' => [
247 [ 'enableSectionEditLinks' => false ], $text, <<<EOF
248<p>Test document.
249</p>
250<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
251<ul>
252<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
253<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
254<ul>
255<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
256</ul>
257</li>
258<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
259</ul>
260</div>
261
262<h2><span class="mw-headline" id="Section_1">Section 1</span></h2>
263<p>One
264</p>
265<h2><span class="mw-headline" id="Section_2">Section 2</span></h2>
266<p>Two
267</p>
268<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
269<p>Two point one
270</p>
271<h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
272<p>Three
273</p>
274EOF
275 ],
276 'Disable TOC, but wrap' => [
277 [ 'allowTOC' => false, 'wrapperDivClass' => 'mw-parser-output' ], $text, <<<EOF
278<div class="mw-parser-output"><p>Test document.
279</p>
280
281<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
282<p>One
283</p>
284<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
285<p>Two
286</p>
287<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
288<p>Two point one
289</p>
290<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
291<p>Three
292</p></div>
293EOF
294 ],
295 'Style deduplication' => [
296 [], $dedupText, <<<EOF
297<p>This is a test document.</p>
298<style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
299<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
300<style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
301<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
302<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate2"/>
303<style data-mw-not-deduplicate="duplicate1">.Duplicate1 {}</style>
304<link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
305<style data-mw-deduplicate="duplicate3">.Duplicate1 {}</style>
306<style>.Duplicate1 {}</style>
307EOF
308 ],
309 'Style deduplication disabled' => [
310 [ 'deduplicateStyles' => false ], $dedupText, $dedupText
311 ],
312 ];
313 // phpcs:enable
314 }
315
319 public function testHasText() {
320 $po = new ParserOutput();
321 $this->assertTrue( $po->hasText() );
322
323 $po = new ParserOutput( null );
324 $this->assertFalse( $po->hasText() );
325
326 $po = new ParserOutput( '' );
327 $this->assertTrue( $po->hasText() );
328
329 $po = new ParserOutput( null );
330 $po->setText( '' );
331 $this->assertTrue( $po->hasText() );
332 }
333
337 public function testGetText_failsIfNoText() {
338 $po = new ParserOutput( null );
339
340 $this->setExpectedException( LogicException::class );
341 $po->getText();
342 }
343
348 $po = new ParserOutput( null );
349
350 $this->setExpectedException( LogicException::class );
351 $po->getRawText();
352 }
353
355 // title text ------------
356 $a = new ParserOutput();
357 $a->setTitleText( 'X' );
358 $b = new ParserOutput();
359 yield 'only left title text' => [ $a, $b, [ 'getTitleText' => 'X' ] ];
360
361 $a = new ParserOutput();
362 $b = new ParserOutput();
363 $b->setTitleText( 'Y' );
364 yield 'only right title text' => [ $a, $b, [ 'getTitleText' => 'Y' ] ];
365
366 $a = new ParserOutput();
367 $a->setTitleText( 'X' );
368 $b = new ParserOutput();
369 $b->setTitleText( 'Y' );
370 yield 'left title text wins' => [ $a, $b, [ 'getTitleText' => 'X' ] ];
371
372 // index policy ------------
373 $a = new ParserOutput();
374 $a->setIndexPolicy( 'index' );
375 $b = new ParserOutput();
376 yield 'only left index policy' => [ $a, $b, [ 'getIndexPolicy' => 'index' ] ];
377
378 $a = new ParserOutput();
379 $b = new ParserOutput();
380 $b->setIndexPolicy( 'index' );
381 yield 'only right index policy' => [ $a, $b, [ 'getIndexPolicy' => 'index' ] ];
382
383 $a = new ParserOutput();
384 $a->setIndexPolicy( 'noindex' );
385 $b = new ParserOutput();
386 $b->setIndexPolicy( 'index' );
387 yield 'left noindex wins' => [ $a, $b, [ 'getIndexPolicy' => 'noindex' ] ];
388
389 $a = new ParserOutput();
390 $a->setIndexPolicy( 'index' );
391 $b = new ParserOutput();
392 $b->setIndexPolicy( 'noindex' );
393 yield 'right noindex wins' => [ $a, $b, [ 'getIndexPolicy' => 'noindex' ] ];
394
395 // head items and friends ------------
396 $a = new ParserOutput();
397 $a->addHeadItem( '<foo1>' );
398 $a->addHeadItem( '<bar1>', 'bar' );
399 $a->addModules( 'test-module-a' );
400 $a->addModuleStyles( 'test-module-styles-a' );
401 $b->addJsConfigVars( 'test-config-var-a', 'a' );
402
403 $b = new ParserOutput();
404 $b->setIndexPolicy( 'noindex' );
405 $b->addHeadItem( '<foo2>' );
406 $b->addHeadItem( '<bar2>', 'bar' );
407 $b->addModules( 'test-module-b' );
408 $b->addModuleStyles( 'test-module-styles-b' );
409 $b->addJsConfigVars( 'test-config-var-b', 'b' );
410 $b->addJsConfigVars( 'test-config-var-a', 'X' );
411
412 yield 'head items and friends' => [ $a, $b, [
413 'getHeadItems' => [
414 '<foo1>',
415 '<foo2>',
416 'bar' => '<bar2>', // overwritten
417 ],
418 'getModules' => [
419 'test-module-a',
420 'test-module-b',
421 ],
422 'getModuleStyles' => [
423 'test-module-styles-a',
424 'test-module-styles-b',
425 ],
426 'getJsConfigVars' => [
427 'test-config-var-a' => 'X', // overwritten
428 'test-config-var-b' => 'b',
429 ],
430 ] ];
431
432 // TOC ------------
433 $a = new ParserOutput();
434 $a->setTOCHTML( '<p>TOC A</p>' );
435 $a->setSections( [ [ 'fromtitle' => 'A1' ], [ 'fromtitle' => 'A2' ] ] );
436
437 $b = new ParserOutput();
438 $b->setTOCHTML( '<p>TOC B</p>' );
439 $b->setSections( [ [ 'fromtitle' => 'B1' ], [ 'fromtitle' => 'B2' ] ] );
440
441 yield 'concat TOC' => [ $a, $b, [
442 'getTOCHTML' => '<p>TOC A</p><p>TOC B</p>',
443 'getSections' => [
444 [ 'fromtitle' => 'A1' ],
445 [ 'fromtitle' => 'A2' ],
446 [ 'fromtitle' => 'B1' ],
447 [ 'fromtitle' => 'B2' ]
448 ],
449 ] ];
450
451 // Skin Control ------------
452 $a = new ParserOutput();
453 $a->setNewSection( true );
454 $a->hideNewSection( true );
455 $a->setNoGallery( true );
456 $a->addWrapperDivClass( 'foo' );
457
458 $a->setIndicator( 'foo', 'Foo!' );
459 $a->setIndicator( 'bar', 'Bar!' );
460
461 $a->setExtensionData( 'foo', 'Foo!' );
462 $a->setExtensionData( 'bar', 'Bar!' );
463
464 $b = new ParserOutput();
465 $b->setNoGallery( true );
466 $b->setEnableOOUI( true );
467 $b->preventClickjacking( true );
468 $a->addWrapperDivClass( 'bar' );
469
470 $b->setIndicator( 'zoo', 'Zoo!' );
471 $b->setIndicator( 'bar', 'Barrr!' );
472
473 $b->setExtensionData( 'zoo', 'Zoo!' );
474 $b->setExtensionData( 'bar', 'Barrr!' );
475
476 yield 'skin control flags' => [ $a, $b, [
477 'getNewSection' => true,
478 'getHideNewSection' => true,
479 'getNoGallery' => true,
480 'getEnableOOUI' => true,
481 'preventClickjacking' => true,
482 'getIndicators' => [
483 'foo' => 'Foo!',
484 'bar' => 'Barrr!',
485 'zoo' => 'Zoo!',
486 ],
487 'getWrapperDivClass' => 'foo bar',
488 '$mExtensionData' => [
489 'foo' => 'Foo!',
490 'bar' => 'Barrr!',
491 'zoo' => 'Zoo!',
492 ],
493 ] ];
494 }
495
504 public function testMergeHtmlMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
505 $a->mergeHtmlMetaDataFrom( $b );
506
507 $this->assertFieldValues( $a, $expected );
508
509 // test twice, to make sure the operation is idempotent (except for the TOC, see below)
510 $a->mergeHtmlMetaDataFrom( $b );
511
512 // XXX: TOC joining should get smarter. Can we make it idempotent as well?
513 unset( $expected['getTOCHTML'] );
514 unset( $expected['getSections'] );
515
516 $this->assertFieldValues( $a, $expected );
517 }
518
519 private function assertFieldValues( ParserOutput $po, $expected ) {
520 $po = TestingAccessWrapper::newFromObject( $po );
521
522 foreach ( $expected as $method => $value ) {
523 if ( $method[0] === '$' ) {
524 $field = substr( $method, 1 );
525 $actual = $po->__get( $field );
526 } else {
527 $actual = $po->__call( $method, [] );
528 }
529
530 $this->assertEquals( $value, $actual, $method );
531 }
532 }
533
535 // links ------------
536 $a = new ParserOutput();
537 $a->addLink( Title::makeTitle( NS_MAIN, 'Kittens' ), 6 );
538 $a->addLink( Title::makeTitle( NS_TALK, 'Kittens' ), 16 );
539 $a->addLink( Title::makeTitle( NS_MAIN, 'Goats' ), 7 );
540
541 $a->addTemplate( Title::makeTitle( NS_TEMPLATE, 'Goats' ), 107, 1107 );
542
543 $a->addLanguageLink( 'de' );
544 $a->addLanguageLink( 'ru' );
545 $a->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens DE', '', 'de' ) );
546 $a->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens RU', '', 'ru' ) );
547 $a->addExternalLink( 'https://kittens.wikimedia.test' );
548 $a->addExternalLink( 'https://goats.wikimedia.test' );
549
550 $a->addCategory( 'Foo', 'X' );
551 $a->addImage( 'Billy.jpg', '20180101000013', 'DEAD' );
552
553 $b = new ParserOutput();
554 $b->addLink( Title::makeTitle( NS_MAIN, 'Goats' ), 7 );
555 $b->addLink( Title::makeTitle( NS_TALK, 'Goats' ), 17 );
556 $b->addLink( Title::makeTitle( NS_MAIN, 'Dragons' ), 8 );
557 $b->addLink( Title::makeTitle( NS_FILE, 'Dragons.jpg' ), 28 );
558
559 $b->addTemplate( Title::makeTitle( NS_TEMPLATE, 'Dragons' ), 108, 1108 );
560 $a->addTemplate( Title::makeTitle( NS_MAIN, 'Dragons' ), 118, 1118 );
561
562 $b->addLanguageLink( 'fr' );
563 $b->addLanguageLink( 'ru' );
564 $b->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens FR', '', 'fr' ) );
565 $b->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Dragons RU', '', 'ru' ) );
566 $b->addExternalLink( 'https://dragons.wikimedia.test' );
567 $b->addExternalLink( 'https://goats.wikimedia.test' );
568
569 $b->addCategory( 'Bar', 'Y' );
570 $b->addImage( 'Puff.jpg', '20180101000017', 'BEEF' );
571
572 yield 'all kinds of links' => [ $a, $b, [
573 'getLinks' => [
574 NS_MAIN => [
575 'Kittens' => 6,
576 'Goats' => 7,
577 'Dragons' => 8,
578 ],
579 NS_TALK => [
580 'Kittens' => 16,
581 'Goats' => 17,
582 ],
583 NS_FILE => [
584 'Dragons.jpg' => 28,
585 ],
586 ],
587 'getTemplates' => [
588 NS_MAIN => [
589 'Dragons' => 118,
590 ],
591 NS_TEMPLATE => [
592 'Dragons' => 108,
593 'Goats' => 107,
594 ],
595 ],
596 'getTemplateIds' => [
597 NS_MAIN => [
598 'Dragons' => 1118,
599 ],
600 NS_TEMPLATE => [
601 'Dragons' => 1108,
602 'Goats' => 1107,
603 ],
604 ],
605 'getLanguageLinks' => [ 'de', 'ru', 'fr' ],
606 'getInterwikiLinks' => [
607 'de' => [ 'Kittens_DE' => 1 ],
608 'ru' => [ 'Kittens_RU' => 1, 'Dragons_RU' => 1, ],
609 'fr' => [ 'Kittens_FR' => 1 ],
610 ],
611 'getCategories' => [ 'Foo' => 'X', 'Bar' => 'Y' ],
612 'getImages' => [ 'Billy.jpg' => 1, 'Puff.jpg' => 1 ],
613 'getFileSearchOptions' => [
614 'Billy.jpg' => [ 'time' => '20180101000013', 'sha1' => 'DEAD' ],
615 'Puff.jpg' => [ 'time' => '20180101000017', 'sha1' => 'BEEF' ],
616 ],
617 'getExternalLinks' => [
618 'https://dragons.wikimedia.test' => 1,
619 'https://kittens.wikimedia.test' => 1,
620 'https://goats.wikimedia.test' => 1,
621 ]
622 ] ];
623
624 // properties ------------
625 $a = new ParserOutput();
626
627 $a->setProperty( 'foo', 'Foo!' );
628 $a->setProperty( 'bar', 'Bar!' );
629
630 $a->setExtensionData( 'foo', 'Foo!' );
631 $a->setExtensionData( 'bar', 'Bar!' );
632
633 $b = new ParserOutput();
634
635 $b->setProperty( 'zoo', 'Zoo!' );
636 $b->setProperty( 'bar', 'Barrr!' );
637
638 $b->setExtensionData( 'zoo', 'Zoo!' );
639 $b->setExtensionData( 'bar', 'Barrr!' );
640
641 yield 'properties' => [ $a, $b, [
642 'getProperties' => [
643 'foo' => 'Foo!',
644 'bar' => 'Barrr!',
645 'zoo' => 'Zoo!',
646 ],
647 '$mExtensionData' => [
648 'foo' => 'Foo!',
649 'bar' => 'Barrr!',
650 'zoo' => 'Zoo!',
651 ],
652 ] ];
653 }
654
663 public function testMergeTrackingMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
665
666 $this->assertFieldValues( $a, $expected );
667
668 // test twice, to make sure the operation is idempotent
670
671 $this->assertFieldValues( $a, $expected );
672 }
673
675 // hooks
676 $a = new ParserOutput();
677
678 $a->addOutputHook( 'foo', 'X' );
679 $a->addOutputHook( 'bar' );
680
681 $b = new ParserOutput();
682
683 $b->addOutputHook( 'foo', 'Y' );
684 $b->addOutputHook( 'bar' );
685 $b->addOutputHook( 'zoo' );
686
687 yield 'hooks' => [ $a, $b, [
688 'getOutputHooks' => [
689 [ 'foo', 'X' ],
690 [ 'bar', false ],
691 [ 'foo', 'Y' ],
692 [ 'zoo', false ],
693 ],
694 ] ];
695
696 // flags & co
697 $a = new ParserOutput();
698
699 $a->addWarning( 'Oops' );
700 $a->addWarning( 'Whoops' );
701
702 $a->setFlag( 'foo' );
703 $a->setFlag( 'bar' );
704
705 $a->recordOption( 'Foo' );
706 $a->recordOption( 'Bar' );
707
708 $b = new ParserOutput();
709
710 $b->addWarning( 'Yikes' );
711 $b->addWarning( 'Whoops' );
712
713 $b->setFlag( 'zoo' );
714 $b->setFlag( 'bar' );
715
716 $b->recordOption( 'Zoo' );
717 $b->recordOption( 'Bar' );
718
719 yield 'flags' => [ $a, $b, [
720 'getWarnings' => [ 'Oops', 'Whoops', 'Yikes' ],
721 '$mFlags' => [ 'foo' => true, 'bar' => true, 'zoo' => true ],
722 'getUsedOptions' => [ 'Foo', 'Bar', 'Zoo' ],
723 ] ];
724
725 // timestamp ------------
726 $a = new ParserOutput();
727 $a->setTimestamp( '20180101000011' );
728 $b = new ParserOutput();
729 yield 'only left timestamp' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
730
731 $a = new ParserOutput();
732 $b = new ParserOutput();
733 $b->setTimestamp( '20180101000011' );
734 yield 'only right timestamp' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
735
736 $a = new ParserOutput();
737 $a->setTimestamp( '20180101000011' );
738 $b = new ParserOutput();
739 $b->setTimestamp( '20180101000001' );
740 yield 'left timestamp wins' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
741
742 $a = new ParserOutput();
743 $a->setTimestamp( '20180101000001' );
744 $b = new ParserOutput();
745 $b->setTimestamp( '20180101000011' );
746 yield 'right timestamp wins' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
747
748 // speculative rev id ------------
749 $a = new ParserOutput();
750 $a->setSpeculativeRevIdUsed( 9 );
751 $b = new ParserOutput();
752 yield 'only left speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
753
754 $a = new ParserOutput();
755 $b = new ParserOutput();
756 $b->setSpeculativeRevIdUsed( 9 );
757 yield 'only right speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
758
759 $a = new ParserOutput();
760 $a->setSpeculativeRevIdUsed( 9 );
761 $b = new ParserOutput();
762 $b->setSpeculativeRevIdUsed( 9 );
763 yield 'same speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
764
765 // limit report (recursive max) ------------
766 $a = new ParserOutput();
767
768 $a->setLimitReportData( 'naive1', 7 );
769 $a->setLimitReportData( 'naive2', 27 );
770
771 $a->setLimitReportData( 'limitreport-simple1', 7 );
772 $a->setLimitReportData( 'limitreport-simple2', 27 );
773
774 $a->setLimitReportData( 'limitreport-pair1', [ 7, 9 ] );
775 $a->setLimitReportData( 'limitreport-pair2', [ 27, 29 ] );
776
777 $a->setLimitReportData( 'limitreport-more1', [ 7, 9, 1 ] );
778 $a->setLimitReportData( 'limitreport-more2', [ 27, 29, 21 ] );
779
780 $a->setLimitReportData( 'limitreport-only-a', 13 );
781
782 $b = new ParserOutput();
783
784 $b->setLimitReportData( 'naive1', 17 );
785 $b->setLimitReportData( 'naive2', 17 );
786
787 $b->setLimitReportData( 'limitreport-simple1', 17 );
788 $b->setLimitReportData( 'limitreport-simple2', 17 );
789
790 $b->setLimitReportData( 'limitreport-pair1', [ 17, 19 ] );
791 $b->setLimitReportData( 'limitreport-pair2', [ 17, 19 ] );
792
793 $b->setLimitReportData( 'limitreport-more1', [ 17, 19, 11 ] );
794 $b->setLimitReportData( 'limitreport-more2', [ 17, 19, 11 ] );
795
796 $b->setLimitReportData( 'limitreport-only-b', 23 );
797
798 // first write wins
799 yield 'limit report' => [ $a, $b, [
800 'getLimitReportData' => [
801 'naive1' => 7,
802 'naive2' => 27,
803 'limitreport-simple1' => 7,
804 'limitreport-simple2' => 27,
805 'limitreport-pair1' => [ 7, 9 ],
806 'limitreport-pair2' => [ 27, 29 ],
807 'limitreport-more1' => [ 7, 9, 1 ],
808 'limitreport-more2' => [ 27, 29, 21 ],
809 'limitreport-only-a' => 13,
810 ],
811 'getLimitReportJSData' => [
812 'naive1' => 7,
813 'naive2' => 27,
814 'limitreport' => [
815 'simple1' => 7,
816 'simple2' => 27,
817 'pair1' => [ 'value' => 7, 'limit' => 9 ],
818 'pair2' => [ 'value' => 27, 'limit' => 29 ],
819 'more1' => [ 7, 9, 1 ],
820 'more2' => [ 27, 29, 21 ],
821 'only-a' => 13,
822 ],
823 ],
824 ] ];
825 }
826
835 public function testMergeInternalMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
837
838 $this->assertFieldValues( $a, $expected );
839
840 // test twice, to make sure the operation is idempotent
842
843 $this->assertFieldValues( $a, $expected );
844 }
845
853 $a = new ParserOutput();
854 $a = TestingAccessWrapper::newFromObject( $a );
855
856 $a->resetParseStartTime();
857 $aClocks = $a->mParseStartTime;
858
859 $b = new ParserOutput();
860
861 $a->mergeInternalMetaDataFrom( $b );
862 $mergedClocks = $a->mParseStartTime;
863
864 foreach ( $mergedClocks as $clock => $timestamp ) {
865 $this->assertSame( $aClocks[$clock], $timestamp, $clock );
866 }
867
868 // try again, with times in $b also set, and later than $a's
869 usleep( 1234 );
870
872 $b = new ParserOutput();
873 $b = TestingAccessWrapper::newFromObject( $b );
874
875 $b->resetParseStartTime();
876
877 $bClocks = $b->mParseStartTime;
878
879 $a->mergeInternalMetaDataFrom( $b->object, 'b' );
880 $mergedClocks = $a->mParseStartTime;
881
882 foreach ( $mergedClocks as $clock => $timestamp ) {
883 $this->assertSame( $aClocks[$clock], $timestamp, $clock );
884 $this->assertLessThanOrEqual( $bClocks[$clock], $timestamp, $clock );
885 }
886
887 // try again, with $a's times being later
888 usleep( 1234 );
889 $a->resetParseStartTime();
890 $aClocks = $a->mParseStartTime;
891
892 $a->mergeInternalMetaDataFrom( $b->object, 'b' );
893 $mergedClocks = $a->mParseStartTime;
894
895 foreach ( $mergedClocks as $clock => $timestamp ) {
896 $this->assertSame( $bClocks[$clock], $timestamp, $clock );
897 $this->assertLessThanOrEqual( $aClocks[$clock], $timestamp, $clock );
898 }
899
900 // try again, with no times in $a set
901 $a = new ParserOutput();
902 $a = TestingAccessWrapper::newFromObject( $a );
903
904 $a->mergeInternalMetaDataFrom( $b->object, 'b' );
905 $mergedClocks = $a->mParseStartTime;
906
907 foreach ( $mergedClocks as $clock => $timestamp ) {
908 $this->assertSame( $bClocks[$clock], $timestamp, $clock );
909 }
910 }
911
916 public function testGetCacheTime() {
917 $clock = MWTimestamp::convert( TS_UNIX, '20100101000000' );
918 MWTimestamp::setFakeTime( function () use ( &$clock ) {
919 return $clock++;
920 } );
921
922 $po = new ParserOutput();
923 $time = $po->getCacheTime();
924
925 // Use current (fake) time per default. Ignore the last digit.
926 // Subsequent calls must yield the exact same timestamp as the first.
927 $this->assertStringStartsWith( '2010010100000', $time );
928 $this->assertSame( $time, $po->getCacheTime() );
929
930 // After setting, the getter must return the time that was set.
931 $time = '20110606112233';
932 $po->setCacheTime( $time );
933 $this->assertSame( $time, $po->getCacheTime() );
934
935 // support -1 as a marker for "not cacheable"
936 $time = -1;
937 $po->setCacheTime( $time );
938 $this->assertSame( $time, $po->getCacheTime() );
939 }
940
941}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
shown</td >< td > a href
target page
Base class that store and restore the Language objects.
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
Database ^— trigger DB shadowing because we are using Title magic.
testMergeInternalMetaDataFrom_parseStartTime()
ParserOutput::mergeInternalMetaDataFrom ParserOutput::getTimes ParserOutput::resetParseStartTime.
testGetText( $options, $text, $expect)
ParserOutput::getText provideGetText.
assertFieldValues(ParserOutput $po, $expected)
testProperties()
ParserOutput::setProperty ParserOutput::getProperty ParserOutput::unsetProperty ParserOutput::getProp...
testGetRawText_failsIfNoText()
ParserOutput::getRawText.
testMergeHtmlMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeHtmlMetaDataFrom ParserOutput::mergeHtmlMetaDataFrom
testMergeTrackingMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeTrackingMetaDataFrom ParserOutput::mergeTrackingMetaDataFrom
static provideIsLinkInternal()
testExtensionData()
ParserOutput::setExtensionData ParserOutput::getExtensionData.
testMergeInternalMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeInternalMetaDataFrom ParserOutput::mergeInternalMetaDataFrom
testHasText()
ParserOutput::hasText.
testWrapperDivClass()
ParserOutput::getWrapperDivClass ParserOutput::addWrapperDivClass ParserOutput::clearWrapperDivClass ...
testGetText_failsIfNoText()
ParserOutput::getText.
testIsLinkInternal( $shouldMatch, $server, $url)
Test to make sure ParserOutput::isLinkInternal behaves properly provideIsLinkInternal ParserOutput::i...
testGetCacheTime()
ParserOutput::getCacheTime ParserOutput::setCacheTime.
mergeHtmlMetaDataFrom(ParserOutput $source)
Merges HTML metadata such as head items, JS config vars, and HTTP cache control info from $source int...
mergeTrackingMetaDataFrom(ParserOutput $source)
Merges dependency tracking metadata such as backlinks, images used, and extension data from $source i...
mergeInternalMetaDataFrom(ParserOutput $source)
Merges internal metadata such as flags, accessed options, and profiling info from $source into this P...
per default it will return the text for text based content
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
const NS_FILE
Definition Defines.php:79
const NS_MAIN
Definition Defines.php:73
const NS_TEMPLATE
Definition Defines.php:83
const NS_TALK
Definition Defines.php:74
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition hooks.txt:1802
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition hooks.txt:1999
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template to be included in the link
Definition hooks.txt:3071
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition hooks.txt:2004
processing should stop and the error should be shown to the user * false
Definition hooks.txt:187
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
This document provides an overview of the usage of PageUpdater and that is
title
Bar style
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN boolean columns are always mapped to as the code does not always treat the column as a and VARBINARY columns should simply be TEXT The only exception is when VARBINARY is used to store true binary data
Definition postgres.txt:37