MediaWiki  master
NamespaceInfoTest.php
Go to the documentation of this file.
1 <?php
9 
11  /**********************************************************************************************
12  * Shared code
13  * %{
14  */
15  private $scopedCallback;
16 
17  public function setUp() {
18  parent::setUp();
19 
20  // Boo, there's still some global state in the class :(
21  global $wgHooks;
22  $hooks = $wgHooks;
23  unset( $hooks['CanonicalNamespaces'] );
24  $this->setMwGlobals( 'wgHooks', $hooks );
25 
26  $this->scopedCallback =
27  ExtensionRegistry::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
28  }
29 
30  public function tearDown() {
31  $this->scopedCallback = null;
32 
33  parent::tearDown();
34  }
35 
39  private static $defaultOptions = [
40  'AllowImageMoving' => true,
41  'CanonicalNamespaceNames' => [
42  NS_TALK => 'Talk',
43  NS_USER => 'User',
44  NS_USER_TALK => 'User_talk',
45  NS_SPECIAL => 'Special',
46  NS_MEDIA => 'Media',
47  ],
48  'CapitalLinkOverrides' => [],
49  'CapitalLinks' => true,
50  'ContentNamespaces' => [ NS_MAIN ],
51  'ExtraNamespaces' => [],
52  'ExtraSignatureNamespaces' => [],
53  'NamespaceContentModels' => [],
54  'NamespaceProtection' => [],
55  'NamespacesWithSubpages' => [
56  NS_TALK => true,
57  NS_USER => true,
58  NS_USER_TALK => true,
59  ],
60  'NonincludableNamespaces' => [],
61  'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
62  ];
63 
64  private function newObj( array $options = [] ) : NamespaceInfo {
66  $options, self::$defaultOptions ) );
67  }
68 
69  // %} End shared code
70 
71  /**********************************************************************************************
72  * Basic methods
73  * %{
74  */
75 
82  public function testConstructor( ServiceOptions $options, $expectedExceptionText = null ) {
83  if ( $expectedExceptionText !== null ) {
84  $this->setExpectedException( \Wikimedia\Assert\PreconditionException::class,
85  $expectedExceptionText );
86  }
87  new NamespaceInfo( $options );
88  $this->assertTrue( true );
89  }
90 
91  public function provideConstructor() {
92  return [
93  [ new ServiceOptions( NamespaceInfo::$constructorOptions, self::$defaultOptions ) ],
94  [ new ServiceOptions( [], [] ), 'Required options missing: ' ],
95  [ new ServiceOptions(
96  array_merge( NamespaceInfo::$constructorOptions, [ 'invalid' ] ),
97  self::$defaultOptions,
98  [ 'invalid' => '' ]
99  ), 'Unsupported options passed: invalid' ],
100  ];
101  }
102 
111  public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
112  $obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
113  $this->assertSame( $expected, $obj->isMovable( $ns ) );
114  }
115 
116  public function provideIsMovable() {
117  return [
118  'Main' => [ true, NS_MAIN ],
119  'Talk' => [ true, NS_TALK ],
120  'Special' => [ false, NS_SPECIAL ],
121  'Nonexistent even namespace' => [ true, 1234 ],
122  'Nonexistent odd namespace' => [ true, 12345 ],
123 
124  'Media with image moving' => [ false, NS_MEDIA, true ],
125  'Media with no image moving' => [ false, NS_MEDIA, false ],
126  'File with image moving' => [ true, NS_FILE, true ],
127  'File with no image moving' => [ false, NS_FILE, false ],
128  ];
129  }
130 
137  public function testIsSubject( $ns, $expected ) {
138  $this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
139  }
140 
147  public function testIsTalk( $ns, $expected ) {
148  $this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
149  }
150 
151  public function provideIsSubject() {
152  return [
153  // Special namespaces
154  [ NS_MEDIA, true ],
155  [ NS_SPECIAL, true ],
156 
157  // Subject pages
158  [ NS_MAIN, true ],
159  [ NS_USER, true ],
160  [ 100, true ],
161 
162  // Talk pages
163  [ NS_TALK, false ],
164  [ NS_USER_TALK, false ],
165  [ 101, false ],
166  ];
167  }
168 
175  public function testExists( $ns, $expected ) {
176  $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
177  }
178 
179  public function provideExists() {
180  return [
181  'Main' => [ NS_MAIN, true ],
182  'Talk' => [ NS_TALK, true ],
183  'Media' => [ NS_MEDIA, true ],
184  'Special' => [ NS_SPECIAL, true ],
185  'Nonexistent' => [ 12345, false ],
186  'Negative nonexistent' => [ -12345, false ],
187  ];
188  }
189 
196  public function testEquals() {
197  $obj = $this->newObj();
198  $this->assertTrue( $obj->equals( NS_MAIN, NS_MAIN ) );
199  $this->assertTrue( $obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
200  $this->assertTrue( $obj->equals( NS_USER, NS_USER ) );
201  $this->assertTrue( $obj->equals( NS_USER, 2 ) );
202  $this->assertTrue( $obj->equals( NS_USER_TALK, NS_USER_TALK ) );
203  $this->assertTrue( $obj->equals( NS_SPECIAL, NS_SPECIAL ) );
204  $this->assertFalse( $obj->equals( NS_MAIN, NS_TALK ) );
205  $this->assertFalse( $obj->equals( NS_USER, NS_USER_TALK ) );
206  $this->assertFalse( $obj->equals( NS_PROJECT, NS_TEMPLATE ) );
207  }
208 
216  public function testSubjectEquals( $ns1, $ns2, $expected ) {
217  $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
218  }
219 
220  public function provideSubjectEquals() {
221  return [
222  [ NS_MAIN, NS_MAIN, true ],
223  // In case we make NS_MAIN 'MAIN'
224  [ NS_MAIN, 0, true ],
225  [ NS_USER, NS_USER, true ],
226  [ NS_USER, 2, true ],
228  [ NS_SPECIAL, NS_SPECIAL, true ],
229  [ NS_MAIN, NS_TALK, true ],
230  [ NS_USER, NS_USER_TALK, true ],
231 
233  [ NS_SPECIAL, NS_MAIN, false ],
234  [ NS_MEDIA, NS_SPECIAL, false ],
235  [ NS_SPECIAL, NS_MEDIA, false ],
236  ];
237  }
238 
246  public function testHasTalkNamespace( $ns, $expected ) {
247  $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
248  }
249 
250  public function provideHasTalkNamespace() {
251  return [
252  [ NS_MEDIA, false ],
253  [ NS_SPECIAL, false ],
254 
255  [ NS_MAIN, true ],
256  [ NS_TALK, true ],
257  [ NS_USER, true ],
258  [ NS_USER_TALK, true ],
259 
260  [ 100, true ],
261  [ 101, true ],
262  ];
263  }
264 
272  public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN ] ) {
273  $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
274  $this->assertSame( $expected, $obj->isContent( $ns ) );
275  }
276 
277  public function provideIsContent() {
278  return [
279  [ NS_MAIN, true ],
280  [ NS_MEDIA, false ],
281  [ NS_SPECIAL, false ],
282  [ NS_TALK, false ],
283  [ NS_USER, false ],
284  [ NS_CATEGORY, false ],
285  [ 100, false ],
286  [ 100, true, [ NS_MAIN, 100, 252 ] ],
287  [ 252, true, [ NS_MAIN, 100, 252 ] ],
288  [ NS_MAIN, true, [ NS_MAIN, 100, 252 ] ],
289  // NS_MAIN is always content
290  [ NS_MAIN, true, [] ],
291  ];
292  }
293 
301  public function testWantSignatures( $index, $expected ) {
302  $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
303  }
304 
305  public function provideWantSignatures() {
306  return [
307  'Main' => [ NS_MAIN, false ],
308  'Talk' => [ NS_TALK, true ],
309  'User' => [ NS_USER, false ],
310  'User talk' => [ NS_USER_TALK, true ],
311  'Special' => [ NS_SPECIAL, false ],
312  'Media' => [ NS_MEDIA, false ],
313  'Nonexistent talk' => [ 12345, true ],
314  'Nonexistent subject' => [ 123456, false ],
315  'Nonexistent negative odd' => [ -12345, false ],
316  ];
317  }
318 
326  public function testWantSignatures_ExtraSignatureNamespaces( $index, $expected ) {
327  $obj = $this->newObj( [ 'ExtraSignatureNamespaces' =>
328  [ NS_MAIN, NS_USER, NS_SPECIAL, NS_MEDIA, 123456, -12345 ] ] );
329  $this->assertSame( $expected, $obj->wantSignatures( $index ) );
330  }
331 
333  $ret = array_map(
334  function ( $arr ) {
335  // We've added all these as extra signature namespaces, so expect true
336  return [ $arr[0], true ];
337  },
338  self::provideWantSignatures()
339  );
340 
341  // Add one more that's false
342  $ret['Another nonexistent subject'] = [ 12345678, false ];
343  return $ret;
344  }
345 
352  public function testIsWatchable( $ns, $expected ) {
353  $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
354  }
355 
356  public function provideIsWatchable() {
357  return [
358  // Specials namespaces are not watchable
359  [ NS_MEDIA, false ],
360  [ NS_SPECIAL, false ],
361 
362  // Core defined namespaces are watchables
363  [ NS_MAIN, true ],
364  [ NS_TALK, true ],
365 
366  // Additional, user defined namespaces are watchables
367  [ 100, true ],
368  [ 101, true ],
369  ];
370  }
371 
379  public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
380  $obj = $this->newObj( $namespacesWithSubpages
381  ? [ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
382  : [] );
383  $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
384  }
385 
386  public function provideHasSubpages() {
387  return [
388  // Special namespaces:
389  [ NS_MEDIA, false ],
390  [ NS_SPECIAL, false ],
391 
392  // Namespaces without subpages
393  [ NS_MAIN, false ],
394  [ NS_MAIN, true, [ NS_MAIN => true ] ],
395  [ NS_MAIN, false, [ NS_MAIN => false ] ],
396 
397  // Some namespaces with subpages
398  [ NS_TALK, true ],
399  [ NS_USER, true ],
400  [ NS_USER_TALK, true ],
401  ];
402  }
403 
410  public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
411  $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
412  $this->assertSame( $expected, $obj->getContentNamespaces() );
413  }
414 
415  public function provideGetContentNamespaces() {
416  return [
417  // Non-array
418  [ '', [ NS_MAIN ] ],
419  [ false, [ NS_MAIN ] ],
420  [ null, [ NS_MAIN ] ],
421  [ 5, [ NS_MAIN ] ],
422 
423  // Empty array
424  [ [], [ NS_MAIN ] ],
425 
426  // NS_MAIN is forced to be content even if unwanted
428 
429  // In other cases, return as-is
430  [ [ NS_MAIN ], [ NS_MAIN ] ],
432  ];
433  }
434 
438  public function testGetSubjectNamespaces() {
439  $subjectsNS = $this->newObj()->getSubjectNamespaces();
440  $this->assertContains( NS_MAIN, $subjectsNS,
441  "Talk namespaces should have NS_MAIN" );
442  $this->assertNotContains( NS_TALK, $subjectsNS,
443  "Talk namespaces should have NS_TALK" );
444 
445  $this->assertNotContains( NS_MEDIA, $subjectsNS,
446  "Talk namespaces should not have NS_MEDIA" );
447  $this->assertNotContains( NS_SPECIAL, $subjectsNS,
448  "Talk namespaces should not have NS_SPECIAL" );
449  }
450 
454  public function testGetTalkNamespaces() {
455  $talkNS = $this->newObj()->getTalkNamespaces();
456  $this->assertContains( NS_TALK, $talkNS,
457  "Subject namespaces should have NS_TALK" );
458  $this->assertNotContains( NS_MAIN, $talkNS,
459  "Subject namespaces should not have NS_MAIN" );
460 
461  $this->assertNotContains( NS_MEDIA, $talkNS,
462  "Subject namespaces should not have NS_MEDIA" );
463  $this->assertNotContains( NS_SPECIAL, $talkNS,
464  "Subject namespaces should not have NS_SPECIAL" );
465  }
466 
475  public function testIsCapitalized(
476  $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
477  ) {
478  $obj = $this->newObj( [
479  'CapitalLinks' => $capitalLinks,
480  'CapitalLinkOverrides' => $capitalLinkOverrides,
481  ] );
482  $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
483  }
484 
485  public function provideIsCapitalized() {
486  return [
487  // Test default settings
488  [ NS_PROJECT, true ],
489  [ NS_PROJECT_TALK, true ],
490  [ NS_MEDIA, true ],
491  [ NS_FILE, true ],
492 
493  // Always capitalized no matter what
494  [ NS_SPECIAL, true, false ],
495  [ NS_USER, true, false ],
496  [ NS_MEDIAWIKI, true, false ],
497 
498  // Even with an override too
499  [ NS_SPECIAL, true, false, [ NS_SPECIAL => false ] ],
500  [ NS_USER, true, false, [ NS_USER => false ] ],
501  [ NS_MEDIAWIKI, true, false, [ NS_MEDIAWIKI => false ] ],
502 
503  // Overrides work for other namespaces
504  [ NS_PROJECT, false, true, [ NS_PROJECT => false ] ],
505  [ NS_PROJECT, true, false, [ NS_PROJECT => true ] ],
506 
507  // NS_MEDIA is treated like NS_FILE, and ignores NS_MEDIA overrides
508  [ NS_MEDIA, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
509  [ NS_MEDIA, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
510  [ NS_FILE, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
511  [ NS_FILE, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
512  ];
513  }
514 
518  public function testHasGenderDistinction() {
519  $obj = $this->newObj();
520 
521  // Namespaces with gender distinctions
522  $this->assertTrue( $obj->hasGenderDistinction( NS_USER ) );
523  $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK ) );
524 
525  // Other ones, "genderless"
526  $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA ) );
527  $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL ) );
528  $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN ) );
529  $this->assertFalse( $obj->hasGenderDistinction( NS_TALK ) );
530  }
531 
535  public function testIsNonincludable() {
536  $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER ] ] );
537  $this->assertTrue( $obj->isNonincludable( NS_USER ) );
538  $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE ) );
539  }
540 
548  public function testGetNamespaceContentModel( $ns, $expected ) {
549  $obj = $this->newObj( [ 'NamespaceContentModels' =>
550  [ NS_USER => CONTENT_MODEL_WIKITEXT, 123 => CONTENT_MODEL_JSON, 1234 => 'abcdef' ],
551  ] );
552  $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
553  }
554 
556  return [
557  [ NS_MAIN, null ],
558  [ NS_TALK, null ],
560  [ NS_USER_TALK, null ],
561  [ NS_SPECIAL, null ],
562  [ 122, null ],
563  [ 123, CONTENT_MODEL_JSON ],
564  [ 1234, 'abcdef' ],
565  [ 1235, null ],
566  ];
567  }
568 
576  public function testGetCategoryLinkType( $ns, $expected ) {
577  $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
578  }
579 
580  public function provideGetCategoryLinkType() {
581  return [
582  [ NS_MAIN, 'page' ],
583  [ NS_TALK, 'page' ],
584  [ NS_USER, 'page' ],
585  [ NS_USER_TALK, 'page' ],
586 
587  [ NS_FILE, 'file' ],
588  [ NS_FILE_TALK, 'page' ],
589 
590  [ NS_CATEGORY, 'subcat' ],
591  [ NS_CATEGORY_TALK, 'page' ],
592 
593  [ 100, 'page' ],
594  [ 101, 'page' ],
595  ];
596  }
597 
598  // %} End basic methods
599 
600  /**********************************************************************************************
601  * getSubject/Talk/Associated
602  * %{
603  */
604 
615  public function testGetSubject( $subject, $talk ) {
616  $obj = $this->newObj();
617  $this->assertSame( $subject, $obj->getSubject( $subject ) );
618  $this->assertSame( $subject, $obj->getSubject( $talk ) );
619 
620  $subjectTitleVal = new TitleValue( $subject, 'A' );
621  $talkTitleVal = new TitleValue( $talk, 'A' );
622  // Object will be the same one passed in if it's a subject, different but equal object if
623  // it's talk
624  $this->assertSame( $subjectTitleVal, $obj->getSubjectPage( $subjectTitleVal ) );
625  $this->assertEquals( $subjectTitleVal, $obj->getSubjectPage( $talkTitleVal ) );
626 
627  $subjectTitle = Title::makeTitle( $subject, 'A' );
628  $talkTitle = Title::makeTitle( $talk, 'A' );
629  $this->assertSame( $subjectTitle, $subjectTitle->getSubjectPage() );
630  $this->assertEquals( $subjectTitle, $talkTitle->getSubjectPage() );
631  }
632 
640  public function testGetSubject_special( $ns ) {
641  $obj = $this->newObj();
642  $this->assertSame( $ns, $obj->getSubject( $ns ) );
643 
644  $title = new TitleValue( $ns, 'A' );
645  $this->assertSame( $title, $obj->getSubjectPage( $title ) );
646  }
647 
658  public function testGetTalk( $subject, $talk ) {
659  $obj = $this->newObj();
660  $this->assertSame( $talk, $obj->getTalk( $subject ) );
661  $this->assertSame( $talk, $obj->getTalk( $talk ) );
662 
663  $subjectTitleVal = new TitleValue( $subject, 'A' );
664  $talkTitleVal = new TitleValue( $talk, 'A' );
665  // Object will be the same one passed in if it's a talk, different but equal object if it's
666  // subject
667  $this->assertEquals( $talkTitleVal, $obj->getTalkPage( $subjectTitleVal ) );
668  $this->assertSame( $talkTitleVal, $obj->getTalkPage( $talkTitleVal ) );
669 
670  $subjectTitle = Title::makeTitle( $subject, 'A' );
671  $talkTitle = Title::makeTitle( $talk, 'A' );
672  $this->assertEquals( $talkTitle, $subjectTitle->getTalkPage() );
673  $this->assertSame( $talkTitle, $talkTitle->getTalkPage() );
674  }
675 
683  public function testGetTalk_special( $ns ) {
684  $this->setExpectedException( MWException::class,
685  "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
686  $this->newObj()->getTalk( $ns );
687  }
688 
697  public function testGetTalkPage_special( $ns ) {
698  $this->setExpectedException( MWException::class,
699  "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
700  $this->newObj()->getTalkPage( new TitleValue( $ns, 'A' ) );
701  }
702 
712  public function testTitleGetTalkPage_special( $ns ) {
713  $this->setExpectedException( MWException::class,
714  "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
715  Title::makeTitle( $ns, 'A' )->getTalkPage();
716  }
717 
725  public function testGetAssociated_special( $ns ) {
726  $this->setExpectedException( MWException::class,
727  "NamespaceInfo::getAssociated does not make any sense for given namespace $ns" );
728  $this->newObj()->getAssociated( $ns );
729  }
730 
739  public function testGetAssociatedPage_special( $ns ) {
740  $this->setExpectedException( MWException::class,
741  "NamespaceInfo::getAssociated does not make any sense for given namespace $ns" );
742  $this->newObj()->getAssociatedPage( new TitleValue( $ns, 'A' ) );
743  }
744 
754  public function testTitleGetOtherPage_special( $ns ) {
755  $this->setExpectedException( MWException::class,
756  "NamespaceInfo::getAssociated does not make any sense for given namespace $ns" );
757  Title::makeTitle( $ns, 'A' )->getOtherPage();
758  }
759 
769  public function testGetAssociated( $subject, $talk ) {
770  $obj = $this->newObj();
771  $this->assertSame( $talk, $obj->getAssociated( $subject ) );
772  $this->assertSame( $subject, $obj->getAssociated( $talk ) );
773 
774  $subjectTitle = new TitleValue( $subject, 'A' );
775  $talkTitle = new TitleValue( $talk, 'A' );
776  // Object will not be the same
777  $this->assertEquals( $talkTitle, $obj->getAssociatedPage( $subjectTitle ) );
778  $this->assertEquals( $subjectTitle, $obj->getAssociatedPage( $talkTitle ) );
779 
780  $subjectTitle = Title::makeTitle( $subject, 'A' );
781  $talkTitle = Title::makeTitle( $talk, 'A' );
782  $this->assertEquals( $talkTitle, $subjectTitle->getOtherPage() );
783  $this->assertEquals( $subjectTitle, $talkTitle->getOtherPage() );
784  }
785 
786  public static function provideSubjectTalk() {
787  return [
788  // Format: [ subject, talk ]
789  'Main/talk' => [ NS_MAIN, NS_TALK ],
790  'User/user talk' => [ NS_USER, NS_USER_TALK ],
791  'Unknown namespaces also supported' => [ 106, 107 ],
792  ];
793  }
794 
795  public static function provideSpecialNamespaces() {
796  return [
797  'Special' => [ NS_SPECIAL ],
798  'Media' => [ NS_MEDIA ],
799  'Unknown negative index' => [ -613 ],
800  ];
801  }
802 
803  // %} End getSubject/Talk/Associated
804 
805  /**********************************************************************************************
806  * Canonical namespaces
807  * %{
808  */
809 
810  // Default canonical namespaces
811  // %{
812  private function getDefaultNamespaces() {
813  return [ NS_MAIN => '' ] + self::$defaultOptions['CanonicalNamespaceNames'];
814  }
815 
819  public function testGetCanonicalNamespaces() {
820  $this->assertSame(
821  $this->getDefaultNamespaces(),
822  $this->newObj()->getCanonicalNamespaces()
823  );
824  }
825 
833  public function testGetCanonicalName( $index, $expected ) {
834  $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
835  }
836 
837  public function provideGetCanonicalName() {
838  return [
839  'Main' => [ NS_MAIN, '' ],
840  'Talk' => [ NS_TALK, 'Talk' ],
841  'With underscore not space' => [ NS_USER_TALK, 'User_talk' ],
842  'Special' => [ NS_SPECIAL, 'Special' ],
843  'Nonexistent' => [ 12345, false ],
844  'Nonexistent negative' => [ -12345, false ],
845  ];
846  }
847 
855  public function testGetCanonicalIndex( $name, $expected ) {
856  $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
857  }
858 
859  public function provideGetCanonicalIndex() {
860  return [
861  'Main' => [ '', NS_MAIN ],
862  'Talk' => [ 'talk', NS_TALK ],
863  'Not lowercase' => [ 'Talk', null ],
864  'With underscore' => [ 'user_talk', NS_USER_TALK ],
865  'Space is not recognized for underscore' => [ 'user talk', null ],
866  '0' => [ '0', null ],
867  ];
868  }
869 
873  public function testGetValidNamespaces() {
874  $this->assertSame(
876  $this->newObj()->getValidNamespaces()
877  );
878  }
879 
880  // %} End default canonical namespaces
881 
882  // No canonical namespace names
883  // %{
884 
889  $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
890 
891  $this->assertSame( [ NS_MAIN => '' ], $obj->getCanonicalNamespaces() );
892  }
893 
898  $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
899 
900  $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
901  $this->assertFalse( $obj->getCanonicalName( NS_TALK ) );
902  }
903 
908  $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
909 
910  $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
911  $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
912  }
913 
918  $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
919 
920  $this->assertSame( [ NS_MAIN ], $obj->getValidNamespaces() );
921  }
922 
923  // %} End no canonical namespace names
924 
925  // Test extension namespaces
926  // %{
927  private function setupExtensionNamespaces() {
928  $this->scopedCallback = null;
929  $this->scopedCallback = ExtensionRegistry::getInstance()->setAttributeForTest(
930  'ExtensionNamespaces',
931  [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 12345 => 'Extended' ]
932  );
933  }
934 
939  $this->setupExtensionNamespaces();
940 
941  $this->assertSame(
942  $this->getDefaultNamespaces() + [ 12345 => 'Extended' ],
943  $this->newObj()->getCanonicalNamespaces()
944  );
945  }
946 
951  $this->setupExtensionNamespaces();
952  $obj = $this->newObj();
953 
954  $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
955  $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
956  $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
957  }
958 
963  $this->setupExtensionNamespaces();
964  $obj = $this->newObj();
965 
966  $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
967  $this->assertSame( NS_TALK, $obj->getCanonicalIndex( 'talk' ) );
968  $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
969  }
970 
975  $this->setupExtensionNamespaces();
976 
977  $this->assertSame(
978  [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 12345 ],
979  $this->newObj()->getValidNamespaces()
980  );
981  }
982 
983  // %} End extension namespaces
984 
985  // Hook namespaces
986  // %{
987 
991  private function setupHookNamespaces() {
992  $callback =
993  function ( &$canonicalNamespaces ) {
994  $canonicalNamespaces[NS_MAIN] = 'Main';
995  unset( $canonicalNamespaces[NS_MEDIA] );
996  $canonicalNamespaces[123456] = 'Hooked';
997  };
998  $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
999  $expected = $this->getDefaultNamespaces();
1000  ( $callback )( $expected );
1001  return $expected;
1002  }
1003 
1008  $expected = $this->setupHookNamespaces();
1009 
1010  $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
1011  }
1012 
1017  $this->setupHookNamespaces();
1018  $obj = $this->newObj();
1019 
1020  $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN ) );
1021  $this->assertFalse( $obj->getCanonicalName( NS_MEDIA ) );
1022  $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
1023  }
1024 
1029  $this->setupHookNamespaces();
1030  $obj = $this->newObj();
1031 
1032  $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( 'main' ) );
1033  $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
1034  $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
1035  }
1036 
1041  $this->setupHookNamespaces();
1042 
1043  $this->assertSame(
1044  [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 123456 ],
1045  $this->newObj()->getValidNamespaces()
1046  );
1047  }
1048 
1049  // %} End hook namespaces
1050 
1051  // Extra namespaces
1052  // %{
1053 
1057  private function setupExtraNamespaces() {
1058  return $this->newObj( [ 'ExtraNamespaces' =>
1059  [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 1234567 => 'Extra' ]
1060  ] );
1061  }
1062 
1067  $this->assertSame(
1068  $this->getDefaultNamespaces() + [ 1234567 => 'Extra' ],
1069  $this->setupExtraNamespaces()->getCanonicalNamespaces()
1070  );
1071  }
1072 
1077  $obj = $this->setupExtraNamespaces();
1078 
1079  $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
1080  $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
1081  $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
1082  }
1083 
1088  $obj = $this->setupExtraNamespaces();
1089 
1090  $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
1091  $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
1092  $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
1093  }
1094 
1099  $this->assertSame(
1100  [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 1234567 ],
1101  $this->setupExtraNamespaces()->getValidNamespaces()
1102  );
1103  }
1104 
1105  // %} End extra namespaces
1106 
1107  // Canonical namespace caching
1108  // %{
1109 
1114  $obj = $this->newObj();
1115 
1116  // This should cache the values
1117  $obj->getCanonicalNamespaces();
1118 
1119  // Now try to alter them through nefarious means
1120  $this->setupExtensionNamespaces();
1121  $this->setupHookNamespaces();
1122 
1123  // Should have no effect
1124  $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
1125  }
1126 
1130  public function testGetCanonicalName_caching() {
1131  $obj = $this->newObj();
1132 
1133  // This should cache the values
1134  $obj->getCanonicalName( NS_MAIN );
1135 
1136  // Now try to alter them through nefarious means
1137  $this->setupExtensionNamespaces();
1138  $this->setupHookNamespaces();
1139 
1140  // Should have no effect
1141  $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
1142  $this->assertSame( 'Media', $obj->getCanonicalName( NS_MEDIA ) );
1143  $this->assertFalse( $obj->getCanonicalName( 12345 ) );
1144  $this->assertFalse( $obj->getCanonicalName( 123456 ) );
1145  }
1146 
1150  public function testGetCanonicalIndex_caching() {
1151  $obj = $this->newObj();
1152 
1153  // This should cache the values
1154  $obj->getCanonicalIndex( '' );
1155 
1156  // Now try to alter them through nefarious means
1157  $this->setupExtensionNamespaces();
1158  $this->setupHookNamespaces();
1159 
1160  // Should have no effect
1161  $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
1162  $this->assertSame( NS_MEDIA, $obj->getCanonicalIndex( 'media' ) );
1163  $this->assertNull( $obj->getCanonicalIndex( 'extended' ) );
1164  $this->assertNull( $obj->getCanonicalIndex( 'hooked' ) );
1165  }
1166 
1171  $obj = $this->newObj();
1172 
1173  // This should cache the values
1174  $obj->getValidNamespaces();
1175 
1176  // Now try to alter through nefarious means
1177  $this->setupExtensionNamespaces();
1178  $this->setupHookNamespaces();
1179 
1180  // Should have no effect
1181  $this->assertSame(
1183  $obj->getValidNamespaces()
1184  );
1185  }
1186 
1187  // %} End canonical namespace caching
1188 
1189  // Miscellaneous
1190  // %{
1191 
1200  public function testGetValidNamespaces_misc( array $namespaces, array $expected ) {
1201  // Each namespace's name is just its index
1202  $this->setTemporaryHook( 'CanonicalNamespaces',
1203  function ( &$canonicalNamespaces ) use ( $namespaces ) {
1204  $canonicalNamespaces = array_combine( $namespaces, $namespaces );
1205  }
1206  );
1207  $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
1208  }
1209 
1211  return [
1212  'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
1213  'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
1214  'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
1215  ];
1216  }
1217 
1218  // %} End miscellaneous
1219  // %} End canonical namespaces
1220 
1221  /**********************************************************************************************
1222  * Restriction levels
1223  * %{
1224  */
1225 
1232  private function getMockUser( array $groups = [] ) : User {
1233  $groups[] = '*';
1234 
1235  $mock = $this->createMock( User::class );
1236  $mock->method( 'isAllowed' )->will( $this->returnCallback(
1237  function ( $action ) use ( $groups ) {
1239  if ( $action == '' ) {
1240  return true;
1241  }
1242  foreach ( $wgRevokePermissions as $group => $rights ) {
1243  if ( !in_array( $group, $groups ) ) {
1244  continue;
1245  }
1246  if ( isset( $rights[$action] ) && $rights[$action] ) {
1247  return false;
1248  }
1249  }
1250  foreach ( $wgGroupPermissions as $group => $rights ) {
1251  if ( !in_array( $group, $groups ) ) {
1252  continue;
1253  }
1254  if ( isset( $rights[$action] ) && $rights[$action] ) {
1255  return true;
1256  }
1257  }
1258  return false;
1259  }
1260  ) );
1261  $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
1262  return $mock;
1263  }
1264 
1273  public function testGetRestrictionLevels( array $expected, $ns, User $user = null ) {
1274  $this->setMwGlobals( [
1275  'wgGroupPermissions' => [
1276  '*' => [ 'edit' => true ],
1277  'autoconfirmed' => [ 'editsemiprotected' => true ],
1278  'sysop' => [
1279  'editsemiprotected' => true,
1280  'editprotected' => true,
1281  ],
1282  'privileged' => [ 'privileged' => true ],
1283  ],
1284  'wgRevokePermissions' => [
1285  'noeditsemiprotected' => [ 'editsemiprotected' => true ],
1286  ],
1287  ] );
1288  $obj = $this->newObj( [
1289  'NamespaceProtection' => [
1290  NS_MAIN => 'autoconfirmed',
1291  NS_USER => 'sysop',
1292  101 => [ 'editsemiprotected', 'privileged' ],
1293  ],
1294  ] );
1295  $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
1296  }
1297 
1298  public function provideGetRestrictionLevels() {
1299  return [
1300  'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK ],
1301  'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN ],
1302  'Restricted to sysop' => [ [ '' ], NS_USER ],
1303  'Restricted to someone in two groups' => [ [ '', 'sysop' ], 101 ],
1304  'No special permissions' => [ [ '' ], NS_TALK, $this->getMockUser() ],
1305  'autoconfirmed' => [
1306  [ '', 'autoconfirmed' ],
1307  NS_TALK,
1308  $this->getMockUser( [ 'autoconfirmed' ] )
1309  ],
1310  'autoconfirmed revoked' => [
1311  [ '' ],
1312  NS_TALK,
1313  $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
1314  ],
1315  'sysop' => [
1316  [ '', 'autoconfirmed', 'sysop' ],
1317  NS_TALK,
1318  $this->getMockUser( [ 'sysop' ] )
1319  ],
1320  'sysop with autoconfirmed revoked (a bit silly)' => [
1321  [ '', 'sysop' ],
1322  NS_TALK,
1323  $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
1324  ],
1325  ];
1326  }
1327 
1328  // %} End restriction levels
1329 }
1330 
testIsWatchable( $ns, $expected)
setTemporaryHook( $hookName, $handler)
Create a temporary hook handler which will be reset by tearDown.
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:231
testGetCanonicalIndex_ExtensionNamespaces()
NamespaceInfo::getCanonicalIndex.
static array $constructorOptions
TODO Make this const when HHVM support is dropped (T192166)
const NS_MAIN
Definition: Defines.php:60
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
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 & $ret
Definition: hooks.txt:1982
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
testIsCapitalized( $ns, $expected, $capitalLinks=true, array $capitalLinkOverrides=[])
testIsContent( $ns, $expected, $contentNamespaces=[NS_MAIN])
testIsSubject( $ns, $expected)
testGetCanonicalName_caching()
NamespaceInfo::getCanonicalName.
testGetSubject( $subject, $talk)
provideSubjectTalk NamespaceInfo::getSubject NamespaceInfo::getSubjectPage NamespaceInfo::isMethodVal...
testEquals()
Note if we add a namespace registration system with keys like &#39;MAIN&#39; we should add tests here for equ...
const NS_SPECIAL
Definition: Defines.php:49
static provideSpecialNamespaces()
testGetCanonicalName( $index, $expected)
provideGetCanonicalName NamespaceInfo::getCanonicalName
testGetCanonicalNamespaces_caching()
NamespaceInfo::getCanonicalNamespaces.
testGetNamespaceContentModel( $ns, $expected)
provideGetNamespaceContentModel NamespaceInfo::getNamespaceContentModel
$wgRevokePermissions
Permission keys revoked from users in each group.
const NS_TEMPLATE
Definition: Defines.php:70
const CONTENT_MODEL_JSON
Definition: Defines.php:235
static $defaultOptions
TODO Make this a const once HHVM support is dropped (T192166)
testGetCanonicalName_ExtensionNamespaces()
NamespaceInfo::getCanonicalName.
testGetValidNamespaces()
NamespaceInfo::getValidNamespaces.
testGetCanonicalNamespaces_ExtraNamespaces()
NamespaceInfo::getCanonicalNamespaces.
testHasGenderDistinction()
NamespaceInfo::hasGenderDistinction.
$wgGroupPermissions
Permission keys given to users in each group.
A class for passing options to services.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
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:1982
anythingBut(... $values)
Returns a PHPUnit constraint that matches anything other than a fixed set of values.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...
testGetValidNamespaces_misc(array $namespaces, array $expected)
provideGetValidNamespaces_misc NamespaceInfo::getValidNamespaces
const NS_PROJECT
Definition: Defines.php:64
testTitleGetTalkPage_special( $ns)
provideSpecialNamespaces NamespaceInfo::getTalk NamespaceInfo::getTalkPage NamespaceInfo::isMethodVal...
testGetCanonicalIndex_caching()
NamespaceInfo::getCanonicalIndex.
testIsNonincludable()
NamespaceInfo::isNonincludable.
testGetCanonicalNamespaces_NoCanonicalNamespaceNames()
NamespaceInfo::getCanonicalNamespaces.
testGetCanonicalName_ExtraNamespaces()
NamespaceInfo::getCanonicalName.
$wgHooks['ArticleShow'][]
Definition: hooks.txt:108
testGetCanonicalIndex( $name, $expected)
provideGetCanonicalIndex NamespaceInfo::getCanonicalIndex
testGetSubject_special( $ns)
provideSpecialNamespaces NamespaceInfo::getSubject NamespaceInfo::getSubjectPage
const NS_PROJECT_TALK
Definition: Defines.php:65
const NS_MEDIA
Definition: Defines.php:48
testSubjectEquals( $ns1, $ns2, $expected)
testGetAssociatedPage_special( $ns)
provideSpecialNamespaces NamespaceInfo::getAssociated NamespaceInfo::getAssociatedPage NamespaceInfo:...
const NS_CATEGORY
Definition: Defines.php:74
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:1982
testGetCategoryLinkType( $ns, $expected)
provideGetCategoryLinkType NamespaceInfo::getCategoryLinkType
testWantSignatures_ExtraSignatureNamespaces( $index, $expected)
provideWantSignatures_ExtraSignatureNamespaces NamespaceInfo::wantSignatures
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
testWantSignatures( $index, $expected)
provideWantSignatures NamespaceInfo::wantSignatures
testGetCanonicalName_HookNamespaces()
NamespaceInfo::getCanonicalName.
const NS_FILE
Definition: Defines.php:66
const NS_FILE_TALK
Definition: Defines.php:67
testHasSubpages( $ns, $expected, array $namespacesWithSubpages=null)
testGetCanonicalIndex_NoCanonicalNamespaceNames()
NamespaceInfo::getCanonicalIndex.
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown...
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:925
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
testGetCanonicalIndex_HookNamespaces()
NamespaceInfo::getCanonicalIndex.
const NS_MEDIAWIKI
Definition: Defines.php:68
testGetAssociated( $subject, $talk)
provideSubjectTalk NamespaceInfo::getAssociated NamespaceInfo::getAssociatedPage Title::getOtherPage ...
testTitleGetOtherPage_special( $ns)
provideSpecialNamespaces NamespaceInfo::getAssociated NamespaceInfo::getAssociatedPage NamespaceInfo:...
testGetRestrictionLevels(array $expected, $ns, User $user=null)
provideGetRestrictionLevels NamespaceInfo::getRestrictionLevels
testGetCanonicalName_NoCanonicalNamespaceNames()
NamespaceInfo::getCanonicalName.
testConstructor(ServiceOptions $options, $expectedExceptionText=null)
NamespaceInfo::__construct provideConstructor.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:589
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
const NS_CATEGORY_TALK
Definition: Defines.php:75
testGetValidNamespaces_ExtensionNamespaces()
NamespaceInfo::getValidNamespaces.
testGetValidNamespaces_caching()
NamespaceInfo::getValidNamespaces.
testGetValidNamespaces_ExtraNamespaces()
NamespaceInfo::getValidNamespaces.
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
testGetCanonicalNamespaces_ExtensionNamespaces()
NamespaceInfo::getCanonicalNamespaces.
testIsTalk( $ns, $expected)
testGetSubjectNamespaces()
NamespaceInfo::getSubjectNamespaces.
provideWantSignatures_ExtraSignatureNamespaces()
testHasTalkNamespace( $ns, $expected)
provideHasTalkNamespace NamespaceInfo::hasTalkNamespace
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
testExists( $ns, $expected)
NamespaceInfo::exists provideExists.
const NS_TALK
Definition: Defines.php:61
testGetTalkNamespaces()
NamespaceInfo::getTalkNamespaces.
getMockUser(array $groups=[])
This mock user can only have isAllowed() called on it.
testGetContentNamespaces( $contentNamespaces, array $expected)
static newObj(array $options=[])
testGetCanonicalIndex_ExtraNamespaces()
NamespaceInfo::getCanonicalIndex.
testGetCanonicalNamespaces_HookNamespaces()
NamespaceInfo::getCanonicalNamespaces.
testIsMovable( $expected, $ns, $allowImageMoving=true)
provideIsMovable NamespaceInfo::isMovable
testGetTalk( $subject, $talk)
provideSubjectTalk NamespaceInfo::getTalk NamespaceInfo::getTalkPage NamespaceInfo::isMethodValidFor ...
const NS_USER_TALK
Definition: Defines.php:63
testGetCanonicalNamespaces()
NamespaceInfo::getCanonicalNamespaces.
testGetTalk_special( $ns)
provideSpecialNamespaces NamespaceInfo::getTalk NamespaceInfo::isMethodValidFor
testGetTalkPage_special( $ns)
provideSpecialNamespaces NamespaceInfo::getTalk NamespaceInfo::getTalkPage NamespaceInfo::isMethodVal...
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
testGetValidNamespaces_HookNamespaces()
NamespaceInfo::getValidNamespaces.
testGetValidNamespaces_NoCanonicalNamespaceNames()
NamespaceInfo::getValidNamespaces.
testGetAssociated_special( $ns)
provideSpecialNamespaces NamespaceInfo::getAssociated NamespaceInfo::isMethodValidFor ...