MediaWiki  1.33.0
ParserOutputTest.php
Go to the documentation of this file.
1 <?php
2 use 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>
200 EOF;
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>
213 EOF;
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>
244 EOF
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>
274 EOF
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>
293 EOF
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>
307 EOF
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 
347  public function testGetRawText_failsIfNoText() {
348  $po = new ParserOutput( null );
349 
350  $this->setExpectedException( LogicException::class );
351  $po->getRawText();
352  }
353 
354  public function provideMergeHtmlMetaDataFrom() {
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 ) {
664  $a->mergeTrackingMetaDataFrom( $b );
665 
666  $this->assertFieldValues( $a, $expected );
667 
668  // test twice, to make sure the operation is idempotent
669  $a->mergeTrackingMetaDataFrom( $b );
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 ) {
836  $a->mergeInternalMetaDataFrom( $b );
837 
838  $this->assertFieldValues( $a, $expected );
839 
840  // test twice, to make sure the operation is idempotent
841  $a->mergeInternalMetaDataFrom( $b );
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 }
ParserOutputTest\testWrapperDivClass
testWrapperDivClass()
ParserOutput::getWrapperDivClass ParserOutput::addWrapperDivClass ParserOutput::clearWrapperDivClass ...
Definition: ParserOutputTest.php:105
ParserOutput
Definition: ParserOutput.php:25
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
content
per default it will return the text for text based content
Definition: contenthandler.txt:104
ParserOutputTest\testMergeInternalMetaDataFrom_parseStartTime
testMergeInternalMetaDataFrom_parseStartTime()
ParserOutput::mergeInternalMetaDataFrom ParserOutput::getTimes ParserOutput::resetParseStartTime.
Definition: ParserOutputTest.php:851
ParserOutputTest\provideMergeInternalMetaDataFrom
provideMergeInternalMetaDataFrom()
Definition: ParserOutputTest.php:674
ParserOutputTest\provideMergeHtmlMetaDataFrom
provideMergeHtmlMetaDataFrom()
Definition: ParserOutputTest.php:354
ParserOutput\mergeTrackingMetaDataFrom
mergeTrackingMetaDataFrom(ParserOutput $source)
Merges dependency tracking metadata such as backlinks, images used, and extension data from $source i...
Definition: ParserOutput.php:1381
NS_FILE
const NS_FILE
Definition: Defines.php:70
NS_TEMPLATE
const NS_TEMPLATE
Definition: Defines.php:74
link
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:3053
a
</source > ! result< div class="mw-highlight mw-content-ltr" dir="ltr">< pre >< span ></span >< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></pre ></div > ! end ! test Multiline< source/> in lists !input *< source > a b</source > *foo< source > a b</source > ! html< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! html tidy< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! end ! test Custom attributes !input< source lang="javascript" id="foo" class="bar" dir="rtl" style="font-size: larger;"> var a
Definition: parserTests.txt:85
page
target page
Definition: All_system_messages.txt:1267
ParserOutputTest\testProperties
testProperties()
ParserOutput::setProperty ParserOutput::getProperty ParserOutput::unsetProperty ParserOutput::getProp...
Definition: ParserOutputTest.php:77
data
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
Definition: hooks.txt:6
is
This document provides an overview of the usage of PageUpdater and that is
Definition: pageupdater.txt:3
php
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:35
ParserOutputTest\testMergeInternalMetaDataFrom
testMergeInternalMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeInternalMetaDataFrom ParserOutput::mergeInternalMetaDataFrom
Definition: ParserOutputTest.php:835
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
ParserOutputTest\testGetCacheTime
testGetCacheTime()
ParserOutput::getCacheTime ParserOutput::setCacheTime.
Definition: ParserOutputTest.php:916
ParserOutput\isLinkInternal
static isLinkInternal( $internal, $url)
Checks, if a url is pointing to the own server.
Definition: ParserOutput.php:685
MediaWikiTestCase\setMwGlobals
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
Definition: MediaWikiTestCase.php:709
ParserOutput\mergeHtmlMetaDataFrom
mergeHtmlMetaDataFrom(ParserOutput $source)
Merges HTML metadata such as head items, JS config vars, and HTTP cache control info from $source int...
Definition: ParserOutput.php:1324
not
if not
Definition: COPYING.txt:307
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
div
div
Definition: parserTests.txt:6850
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:576
ParserOutput\mergeInternalMetaDataFrom
mergeInternalMetaDataFrom(ParserOutput $source)
Merges internal metadata such as flags, accessed options, and profiling info from $source into this P...
Definition: ParserOutput.php:1283
ParserOutputTest\testGetText
testGetText( $options, $text, $expect)
ParserOutput::getText provideGetText.
Definition: ParserOutputTest.php:159
ParserOutputTest\testGetText_failsIfNoText
testGetText_failsIfNoText()
ParserOutput::getText.
Definition: ParserOutputTest.php:337
ParserOutputTest\provideGetText
static provideGetText()
Definition: ParserOutputTest.php:171
ParserOutputTest
Database ^— trigger DB shadowing because we are using Title magic.
Definition: ParserOutputTest.php:8
$value
$value
Definition: styleTest.css.php:49
ParserOutputTest\testMergeTrackingMetaDataFrom
testMergeTrackingMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeTrackingMetaDataFrom ParserOutput::mergeTrackingMetaDataFrom
Definition: ParserOutputTest.php:663
title
title
Definition: parserTests.txt:245
ParserOutputTest\provideIsLinkInternal
static provideIsLinkInternal()
Definition: ParserOutputTest.php:10
MediaWikiLangTestCase
Base class that store and restore the Language objects.
Definition: MediaWikiLangTestCase.php:8
ParserOutputTest\testHasText
testHasText()
ParserOutput::hasText.
Definition: ParserOutputTest.php:319
ParserOutputTest\tearDown
tearDown()
Definition: ParserOutputTest.php:35
ParserOutputTest\testIsLinkInternal
testIsLinkInternal( $shouldMatch, $server, $url)
Test to make sure ParserOutput::isLinkInternal behaves properly provideIsLinkInternal ParserOutput::i...
Definition: ParserOutputTest.php:46
captcha-old.p
p
Definition: captcha-old.py:275
style
Bar style
Definition: parserTests.txt:218
different
it is up to the author donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License If the distribution and or use of the Program is restricted in certain countries either by patents or by copyrighted the original copyright holder who places the Program under this License may add an geographical distribution limitation excluding those so that distribution is permitted only in or among countries not thus excluded In such this License incorporates the limitation as if written in the body of this License The Free Software Foundation may publish revised and or new versions of the General Public License from time to time Such new versions will be similar in spirit to the present but may differ in detail to address new problems or concerns Each version is given a distinguishing version number If the Program specifies a version number of this License which applies to it and any later you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation If the Program does not specify a version number of this you may choose any version ever published by the Free Software Foundation If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different
Definition: COPYING.txt:230
ParserOutputTest\provideMergeTrackingMetaDataFrom
provideMergeTrackingMetaDataFrom()
Definition: ParserOutputTest.php:534
$options
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:1985
ParserOutputTest\testMergeHtmlMetaDataFrom
testMergeHtmlMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeHtmlMetaDataFrom ParserOutput::mergeHtmlMetaDataFrom
Definition: ParserOutputTest.php:504
document
GNU GENERAL PUBLIC LICENSE June Free Software Franklin Fifth MA USA Everyone is permitted to copy and distribute verbatim copies of this license document
Definition: COPYING.txt:4
Test
as
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
Definition: distributors.txt:9
true
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:1985
NS_TALK
const NS_TALK
Definition: Defines.php:65
ParserOutputTest\testExtensionData
testExtensionData()
ParserOutput::setExtensionData ParserOutput::getExtensionData.
Definition: ParserOutputTest.php:54
attribute
</source > ! result< div id="foo" class="bar mw-highlight mw-content-rtl" dir="rtl" style="font-size: larger;">< pre >< span ></span >< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></pre ></div > ! end ! test Inline attribute(inline code) !!input Text< source lang
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1802
ParserOutputTest\testGetRawText_failsIfNoText
testGetRawText_failsIfNoText()
ParserOutput::getRawText.
Definition: ParserOutputTest.php:347
href
shown</td >< td > a href
Definition: All_system_messages.txt:2667
ParserOutputTest\assertFieldValues
assertFieldValues(ParserOutput $po, $expected)
Definition: ParserOutputTest.php:519