3 * @author Antoine Musso
4 * @copyright Copyright © 2011, Antoine Musso
8 use MediaWiki\Config\ServiceOptions
;
10 class NamespaceInfoTest
extends MediaWikiTestCase
{
11 /**********************************************************************************************
15 private $scopedCallback;
17 public function setUp() {
20 // Boo, there's still some global state in the class :(
23 unset( $hooks['CanonicalNamespaces'] );
24 $this->setMwGlobals( 'wgHooks', $hooks );
26 $this->scopedCallback
=
27 ExtensionRegistry
::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
30 public function tearDown() {
31 $this->scopedCallback
= null;
37 * TODO Make this a const once HHVM support is dropped (T192166)
39 private static $defaultOptions = [
40 'AllowImageMoving' => true,
41 'CanonicalNamespaceNames' => [
44 NS_USER_TALK
=> 'User_talk',
45 NS_SPECIAL
=> 'Special',
48 'CapitalLinkOverrides' => [],
49 'CapitalLinks' => true,
50 'ContentNamespaces' => [ NS_MAIN
],
51 'ExtraNamespaces' => [],
52 'ExtraSignatureNamespaces' => [],
53 'NamespaceContentModels' => [],
54 'NamespaceProtection' => [],
55 'NamespacesWithSubpages' => [
60 'NonincludableNamespaces' => [],
61 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
64 private function newObj( array $options = [] ) : NamespaceInfo
{
65 return new NamespaceInfo( new ServiceOptions( NamespaceInfo
::$constructorOptions,
66 $options, self
::$defaultOptions ) );
71 /**********************************************************************************************
77 * @covers NamespaceInfo::__construct
78 * @dataProvider provideConstructor
79 * @param ServiceOptions $options
80 * @param string|null $expectedExceptionText
82 public function testConstructor( ServiceOptions
$options, $expectedExceptionText = null ) {
83 if ( $expectedExceptionText !== null ) {
84 $this->setExpectedException( \Wikimedia\Assert\PreconditionException
::class,
85 $expectedExceptionText );
87 new NamespaceInfo( $options );
88 $this->assertTrue( true );
91 public function provideConstructor() {
93 [ new ServiceOptions( NamespaceInfo
::$constructorOptions, self
::$defaultOptions ) ],
94 [ new ServiceOptions( [], [] ), 'Required options missing: ' ],
96 array_merge( NamespaceInfo
::$constructorOptions, [ 'invalid' ] ),
97 self
::$defaultOptions,
99 ), 'Unsupported options passed: invalid' ],
104 * @dataProvider provideIsMovable
105 * @covers NamespaceInfo::isMovable
107 * @param bool $expected
109 * @param bool $allowImageMoving
111 public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
112 $obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
113 $this->assertSame( $expected, $obj->isMovable( $ns ) );
116 public function provideIsMovable() {
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 ],
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 ],
133 * @param bool $expected
134 * @dataProvider provideIsSubject
135 * @covers NamespaceInfo::isSubject
137 public function testIsSubject( $ns, $expected ) {
138 $this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
143 * @param bool $expected
144 * @dataProvider provideIsSubject
145 * @covers NamespaceInfo::isTalk
147 public function testIsTalk( $ns, $expected ) {
148 $this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
151 public function provideIsSubject() {
153 // Special namespaces
155 [ NS_SPECIAL
, true ],
164 [ NS_USER_TALK
, false ],
170 * @covers NamespaceInfo::getSubject
172 public function testGetSubject() {
173 // Special namespaces are their own subjects
174 $obj = $this->newObj();
175 $this->assertEquals( NS_MEDIA
, $obj->getSubject( NS_MEDIA
) );
176 $this->assertEquals( NS_SPECIAL
, $obj->getSubject( NS_SPECIAL
) );
178 $this->assertEquals( NS_MAIN
, $obj->getSubject( NS_TALK
) );
179 $this->assertEquals( NS_USER
, $obj->getSubject( NS_USER_TALK
) );
183 * Regular getTalk() calls
184 * Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
185 * the function testGetTalkExceptions()
186 * @covers NamespaceInfo::getTalk
187 * @covers NamespaceInfo::isMethodValidFor
189 public function testGetTalk() {
190 $obj = $this->newObj();
191 $this->assertEquals( NS_TALK
, $obj->getTalk( NS_MAIN
) );
192 $this->assertEquals( NS_TALK
, $obj->getTalk( NS_TALK
) );
193 $this->assertEquals( NS_USER_TALK
, $obj->getTalk( NS_USER
) );
194 $this->assertEquals( NS_USER_TALK
, $obj->getTalk( NS_USER_TALK
) );
198 * Exceptions with getTalk()
199 * NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
200 * @expectedException MWException
201 * @covers NamespaceInfo::getTalk
202 * @covers NamespaceInfo::isMethodValidFor
204 public function testGetTalkExceptionsForNsMedia() {
205 $this->assertNull( $this->newObj()->getTalk( NS_MEDIA
) );
209 * Exceptions with getTalk()
210 * NS_SPECIAL does not have talk pages. MediaWiki raise an exception for them.
211 * @expectedException MWException
212 * @covers NamespaceInfo::getTalk
214 public function testGetTalkExceptionsForNsSpecial() {
215 $this->assertNull( $this->newObj()->getTalk( NS_SPECIAL
) );
219 * Regular getAssociated() calls
220 * Namespaces without an associated page (NS_MEDIA, NS_SPECIAL) are tested in
221 * the function testGetAssociatedExceptions()
222 * @covers NamespaceInfo::getAssociated
224 public function testGetAssociated() {
225 $this->assertEquals( NS_TALK
, $this->newObj()->getAssociated( NS_MAIN
) );
226 $this->assertEquals( NS_MAIN
, $this->newObj()->getAssociated( NS_TALK
) );
229 # ## Exceptions with getAssociated()
230 # ## NS_MEDIA and NS_SPECIAL do not have talk pages. MediaWiki raises
231 # ## an exception for them.
233 * @expectedException MWException
234 * @covers NamespaceInfo::getAssociated
236 public function testGetAssociatedExceptionsForNsMedia() {
237 $this->assertNull( $this->newObj()->getAssociated( NS_MEDIA
) );
241 * @expectedException MWException
242 * @covers NamespaceInfo::getAssociated
244 public function testGetAssociatedExceptionsForNsSpecial() {
245 $this->assertNull( $this->newObj()->getAssociated( NS_SPECIAL
) );
249 * @covers NamespaceInfo::exists
250 * @dataProvider provideExists
252 * @param bool $expected
254 public function testExists( $ns, $expected ) {
255 $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
258 public function provideExists() {
260 'Main' => [ NS_MAIN
, true ],
261 'Talk' => [ NS_TALK
, true ],
262 'Media' => [ NS_MEDIA
, true ],
263 'Special' => [ NS_SPECIAL
, true ],
264 'Nonexistent' => [ 12345, false ],
265 'Negative nonexistent' => [ -12345, false ],
270 * Note if we add a namespace registration system with keys like 'MAIN'
271 * we should add tests here for equivalence on things like 'MAIN' == 0
272 * and 'MAIN' == NS_MAIN.
273 * @covers NamespaceInfo::equals
275 public function testEquals() {
276 $obj = $this->newObj();
277 $this->assertTrue( $obj->equals( NS_MAIN
, NS_MAIN
) );
278 $this->assertTrue( $obj->equals( NS_MAIN
, 0 ) ); // In case we make NS_MAIN 'MAIN'
279 $this->assertTrue( $obj->equals( NS_USER
, NS_USER
) );
280 $this->assertTrue( $obj->equals( NS_USER
, 2 ) );
281 $this->assertTrue( $obj->equals( NS_USER_TALK
, NS_USER_TALK
) );
282 $this->assertTrue( $obj->equals( NS_SPECIAL
, NS_SPECIAL
) );
283 $this->assertFalse( $obj->equals( NS_MAIN
, NS_TALK
) );
284 $this->assertFalse( $obj->equals( NS_USER
, NS_USER_TALK
) );
285 $this->assertFalse( $obj->equals( NS_PROJECT
, NS_TEMPLATE
) );
291 * @param bool $expected
292 * @dataProvider provideSubjectEquals
293 * @covers NamespaceInfo::subjectEquals
295 public function testSubjectEquals( $ns1, $ns2, $expected ) {
296 $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
299 public function provideSubjectEquals() {
301 [ NS_MAIN
, NS_MAIN
, true ],
302 // In case we make NS_MAIN 'MAIN'
303 [ NS_MAIN
, 0, true ],
304 [ NS_USER
, NS_USER
, true ],
305 [ NS_USER
, 2, true ],
306 [ NS_USER_TALK
, NS_USER_TALK
, true ],
307 [ NS_SPECIAL
, NS_SPECIAL
, true ],
308 [ NS_MAIN
, NS_TALK
, true ],
309 [ NS_USER
, NS_USER_TALK
, true ],
311 [ NS_PROJECT
, NS_TEMPLATE
, false ],
312 [ NS_SPECIAL
, NS_MAIN
, false ],
313 [ NS_MEDIA
, NS_SPECIAL
, false ],
314 [ NS_SPECIAL
, NS_MEDIA
, false ],
319 * @dataProvider provideHasTalkNamespace
320 * @covers NamespaceInfo::hasTalkNamespace
323 * @param bool $expected
325 public function testHasTalkNamespace( $ns, $expected ) {
326 $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
329 public function provideHasTalkNamespace() {
332 [ NS_SPECIAL
, false ],
337 [ NS_USER_TALK
, true ],
346 * @param bool $expected
347 * @param array $contentNamespaces
348 * @covers NamespaceInfo::isContent
349 * @dataProvider provideIsContent
351 public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN
] ) {
352 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
353 $this->assertSame( $expected, $obj->isContent( $ns ) );
356 public function provideIsContent() {
360 [ NS_SPECIAL
, false ],
363 [ NS_CATEGORY
, false ],
365 [ 100, true, [ NS_MAIN
, 100, 252 ] ],
366 [ 252, true, [ NS_MAIN
, 100, 252 ] ],
367 [ NS_MAIN
, true, [ NS_MAIN
, 100, 252 ] ],
368 // NS_MAIN is always content
369 [ NS_MAIN
, true, [] ],
374 * @dataProvider provideWantSignatures
375 * @covers NamespaceInfo::wantSignatures
378 * @param bool $expected
380 public function testWantSignatures( $index, $expected ) {
381 $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
384 public function provideWantSignatures() {
386 'Main' => [ NS_MAIN
, false ],
387 'Talk' => [ NS_TALK
, true ],
388 'User' => [ NS_USER
, false ],
389 'User talk' => [ NS_USER_TALK
, true ],
390 'Special' => [ NS_SPECIAL
, false ],
391 'Media' => [ NS_MEDIA
, false ],
392 'Nonexistent talk' => [ 12345, true ],
393 'Nonexistent subject' => [ 123456, false ],
394 'Nonexistent negative odd' => [ -12345, false ],
399 * @dataProvider provideWantSignatures_ExtraSignatureNamespaces
400 * @covers NamespaceInfo::wantSignatures
403 * @param int $expected
405 public function testWantSignatures_ExtraSignatureNamespaces( $index, $expected ) {
406 $obj = $this->newObj( [ 'ExtraSignatureNamespaces' =>
407 [ NS_MAIN
, NS_USER
, NS_SPECIAL
, NS_MEDIA
, 123456, -12345 ] ] );
408 $this->assertSame( $expected, $obj->wantSignatures( $index ) );
411 public function provideWantSignatures_ExtraSignatureNamespaces() {
414 // We've added all these as extra signature namespaces, so expect true
415 return [ $arr[0], true ];
417 self
::provideWantSignatures()
420 // Add one more that's false
421 $ret['Another nonexistent subject'] = [ 12345678, false ];
427 * @param bool $expected
428 * @covers NamespaceInfo::isWatchable
429 * @dataProvider provideIsWatchable
431 public function testIsWatchable( $ns, $expected ) {
432 $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
435 public function provideIsWatchable() {
437 // Specials namespaces are not watchable
439 [ NS_SPECIAL
, false ],
441 // Core defined namespaces are watchables
445 // Additional, user defined namespaces are watchables
453 * @param int $expected
454 * @param array|null $namespacesWithSubpages To pass to constructor
455 * @covers NamespaceInfo::hasSubpages
456 * @dataProvider provideHasSubpages
458 public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
459 $obj = $this->newObj( $namespacesWithSubpages
460 ?
[ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
462 $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
465 public function provideHasSubpages() {
467 // Special namespaces:
469 [ NS_SPECIAL
, false ],
471 // Namespaces without subpages
473 [ NS_MAIN
, true, [ NS_MAIN
=> true ] ],
474 [ NS_MAIN
, false, [ NS_MAIN
=> false ] ],
476 // Some namespaces with subpages
479 [ NS_USER_TALK
, true ],
484 * @param $contentNamespaces To pass to constructor
485 * @param array $expected
486 * @dataProvider provideGetContentNamespaces
487 * @covers NamespaceInfo::getContentNamespaces
489 public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
490 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
491 $this->assertSame( $expected, $obj->getContentNamespaces() );
494 public function provideGetContentNamespaces() {
498 [ false, [ NS_MAIN
] ],
499 [ null, [ NS_MAIN
] ],
505 // NS_MAIN is forced to be content even if unwanted
506 [ [ NS_USER
, NS_CATEGORY
], [ NS_MAIN
, NS_USER
, NS_CATEGORY
] ],
508 // In other cases, return as-is
509 [ [ NS_MAIN
], [ NS_MAIN
] ],
510 [ [ NS_MAIN
, NS_USER
, NS_CATEGORY
], [ NS_MAIN
, NS_USER
, NS_CATEGORY
] ],
515 * @covers NamespaceInfo::getSubjectNamespaces
517 public function testGetSubjectNamespaces() {
518 $subjectsNS = $this->newObj()->getSubjectNamespaces();
519 $this->assertContains( NS_MAIN
, $subjectsNS,
520 "Talk namespaces should have NS_MAIN" );
521 $this->assertNotContains( NS_TALK
, $subjectsNS,
522 "Talk namespaces should have NS_TALK" );
524 $this->assertNotContains( NS_MEDIA
, $subjectsNS,
525 "Talk namespaces should not have NS_MEDIA" );
526 $this->assertNotContains( NS_SPECIAL
, $subjectsNS,
527 "Talk namespaces should not have NS_SPECIAL" );
531 * @covers NamespaceInfo::getTalkNamespaces
533 public function testGetTalkNamespaces() {
534 $talkNS = $this->newObj()->getTalkNamespaces();
535 $this->assertContains( NS_TALK
, $talkNS,
536 "Subject namespaces should have NS_TALK" );
537 $this->assertNotContains( NS_MAIN
, $talkNS,
538 "Subject namespaces should not have NS_MAIN" );
540 $this->assertNotContains( NS_MEDIA
, $talkNS,
541 "Subject namespaces should not have NS_MEDIA" );
542 $this->assertNotContains( NS_SPECIAL
, $talkNS,
543 "Subject namespaces should not have NS_SPECIAL" );
548 * @param bool $expected
549 * @param bool $capitalLinks To pass to constructor
550 * @param array $capitalLinkOverrides To pass to constructor
551 * @dataProvider provideIsCapitalized
552 * @covers NamespaceInfo::isCapitalized
554 public function testIsCapitalized(
555 $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
557 $obj = $this->newObj( [
558 'CapitalLinks' => $capitalLinks,
559 'CapitalLinkOverrides' => $capitalLinkOverrides,
561 $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
564 public function provideIsCapitalized() {
566 // Test default settings
567 [ NS_PROJECT
, true ],
568 [ NS_PROJECT_TALK
, true ],
572 // Always capitalized no matter what
573 [ NS_SPECIAL
, true, false ],
574 [ NS_USER
, true, false ],
575 [ NS_MEDIAWIKI
, true, false ],
577 // Even with an override too
578 [ NS_SPECIAL
, true, false, [ NS_SPECIAL
=> false ] ],
579 [ NS_USER
, true, false, [ NS_USER
=> false ] ],
580 [ NS_MEDIAWIKI
, true, false, [ NS_MEDIAWIKI
=> false ] ],
582 // Overrides work for other namespaces
583 [ NS_PROJECT
, false, true, [ NS_PROJECT
=> false ] ],
584 [ NS_PROJECT
, true, false, [ NS_PROJECT
=> true ] ],
586 // NS_MEDIA is treated like NS_FILE, and ignores NS_MEDIA overrides
587 [ NS_MEDIA
, false, true, [ NS_FILE
=> false, NS_MEDIA
=> true ] ],
588 [ NS_MEDIA
, true, false, [ NS_FILE
=> true, NS_MEDIA
=> false ] ],
589 [ NS_FILE
, false, true, [ NS_FILE
=> false, NS_MEDIA
=> true ] ],
590 [ NS_FILE
, true, false, [ NS_FILE
=> true, NS_MEDIA
=> false ] ],
595 * @covers NamespaceInfo::hasGenderDistinction
597 public function testHasGenderDistinction() {
598 $obj = $this->newObj();
600 // Namespaces with gender distinctions
601 $this->assertTrue( $obj->hasGenderDistinction( NS_USER
) );
602 $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK
) );
604 // Other ones, "genderless"
605 $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA
) );
606 $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL
) );
607 $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN
) );
608 $this->assertFalse( $obj->hasGenderDistinction( NS_TALK
) );
612 * @covers NamespaceInfo::isNonincludable
614 public function testIsNonincludable() {
615 $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER
] ] );
616 $this->assertTrue( $obj->isNonincludable( NS_USER
) );
617 $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE
) );
621 * @dataProvider provideGetNamespaceContentModel
622 * @covers NamespaceInfo::getNamespaceContentModel
625 * @param string $expected
627 public function testGetNamespaceContentModel( $ns, $expected ) {
628 $obj = $this->newObj( [ 'NamespaceContentModels' =>
629 [ NS_USER
=> CONTENT_MODEL_WIKITEXT
, 123 => CONTENT_MODEL_JSON
, 1234 => 'abcdef' ],
631 $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
634 public function provideGetNamespaceContentModel() {
638 [ NS_USER
, CONTENT_MODEL_WIKITEXT
],
639 [ NS_USER_TALK
, null ],
640 [ NS_SPECIAL
, null ],
642 [ 123, CONTENT_MODEL_JSON
],
649 * @dataProvider provideGetCategoryLinkType
650 * @covers NamespaceInfo::getCategoryLinkType
653 * @param string $expected
655 public function testGetCategoryLinkType( $ns, $expected ) {
656 $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
659 public function provideGetCategoryLinkType() {
664 [ NS_USER_TALK
, 'page' ],
667 [ NS_FILE_TALK
, 'page' ],
669 [ NS_CATEGORY
, 'subcat' ],
670 [ NS_CATEGORY_TALK
, 'page' ],
677 // %} End basic methods
679 /**********************************************************************************************
680 * Canonical namespaces
684 // Default canonical namespaces
686 private function getDefaultNamespaces() {
687 return [ NS_MAIN
=> '' ] + self
::$defaultOptions['CanonicalNamespaceNames'];
691 * @covers NamespaceInfo::getCanonicalNamespaces
693 public function testGetCanonicalNamespaces() {
695 $this->getDefaultNamespaces(),
696 $this->newObj()->getCanonicalNamespaces()
701 * @dataProvider provideGetCanonicalName
702 * @covers NamespaceInfo::getCanonicalName
705 * @param string|bool $expected
707 public function testGetCanonicalName( $index, $expected ) {
708 $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
711 public function provideGetCanonicalName() {
713 'Main' => [ NS_MAIN
, '' ],
714 'Talk' => [ NS_TALK
, 'Talk' ],
715 'With underscore not space' => [ NS_USER_TALK
, 'User_talk' ],
716 'Special' => [ NS_SPECIAL
, 'Special' ],
717 'Nonexistent' => [ 12345, false ],
718 'Nonexistent negative' => [ -12345, false ],
723 * @dataProvider provideGetCanonicalIndex
724 * @covers NamespaceInfo::getCanonicalIndex
726 * @param string $name
727 * @param int|null $expected
729 public function testGetCanonicalIndex( $name, $expected ) {
730 $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
733 public function provideGetCanonicalIndex() {
735 'Main' => [ '', NS_MAIN
],
736 'Talk' => [ 'talk', NS_TALK
],
737 'Not lowercase' => [ 'Talk', null ],
738 'With underscore' => [ 'user_talk', NS_USER_TALK
],
739 'Space is not recognized for underscore' => [ 'user talk', null ],
740 '0' => [ '0', null ],
745 * @covers NamespaceInfo::getValidNamespaces
747 public function testGetValidNamespaces() {
749 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
],
750 $this->newObj()->getValidNamespaces()
754 // %} End default canonical namespaces
756 // No canonical namespace names
759 * @covers NamespaceInfo::getCanonicalNamespaces
761 public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
762 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
764 $this->assertSame( [ NS_MAIN
=> '' ], $obj->getCanonicalNamespaces() );
768 * @covers NamespaceInfo::getCanonicalName
770 public function testGetCanonicalName_NoCanonicalNamespaceNames() {
771 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
773 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
774 $this->assertFalse( $obj->getCanonicalName( NS_TALK
) );
778 * @covers NamespaceInfo::getCanonicalIndex
780 public function testGetCanonicalIndex_NoCanonicalNamespaceNames() {
781 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
783 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
784 $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
788 * @covers NamespaceInfo::getValidNamespaces
790 public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
791 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
793 $this->assertSame( [ NS_MAIN
], $obj->getValidNamespaces() );
796 // %} End no canonical namespace names
798 // Test extension namespaces
800 private function setupExtensionNamespaces() {
801 $this->scopedCallback
= null;
802 $this->scopedCallback
= ExtensionRegistry
::getInstance()->setAttributeForTest(
803 'ExtensionNamespaces',
804 [ NS_MAIN
=> 'No effect', NS_TALK
=> 'No effect', 12345 => 'Extended' ]
809 * @covers NamespaceInfo::getCanonicalNamespaces
811 public function testGetCanonicalNamespaces_ExtensionNamespaces() {
812 $this->setupExtensionNamespaces();
815 $this->getDefaultNamespaces() +
[ 12345 => 'Extended' ],
816 $this->newObj()->getCanonicalNamespaces()
821 * @covers NamespaceInfo::getCanonicalName
823 public function testGetCanonicalName_ExtensionNamespaces() {
824 $this->setupExtensionNamespaces();
825 $obj = $this->newObj();
827 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
828 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK
) );
829 $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
833 * @covers NamespaceInfo::getCanonicalIndex
835 public function testGetCanonicalIndex_ExtensionNamespaces() {
836 $this->setupExtensionNamespaces();
837 $obj = $this->newObj();
839 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
840 $this->assertSame( NS_TALK
, $obj->getCanonicalIndex( 'talk' ) );
841 $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
845 * @covers NamespaceInfo::getValidNamespaces
847 public function testGetValidNamespaces_ExtensionNamespaces() {
848 $this->setupExtensionNamespaces();
851 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 12345 ],
852 $this->newObj()->getValidNamespaces()
856 // %} End extension namespaces
861 * @return array Expected canonical namespaces
863 private function setupHookNamespaces() {
865 function ( &$canonicalNamespaces ) {
866 $canonicalNamespaces[NS_MAIN
] = 'Main';
867 unset( $canonicalNamespaces[NS_MEDIA
] );
868 $canonicalNamespaces[123456] = 'Hooked';
870 $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
871 $expected = $this->getDefaultNamespaces();
872 ( $callback )( $expected );
877 * @covers NamespaceInfo::getCanonicalNamespaces
879 public function testGetCanonicalNamespaces_HookNamespaces() {
880 $expected = $this->setupHookNamespaces();
882 $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
886 * @covers NamespaceInfo::getCanonicalName
888 public function testGetCanonicalName_HookNamespaces() {
889 $this->setupHookNamespaces();
890 $obj = $this->newObj();
892 $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN
) );
893 $this->assertFalse( $obj->getCanonicalName( NS_MEDIA
) );
894 $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
898 * @covers NamespaceInfo::getCanonicalIndex
900 public function testGetCanonicalIndex_HookNamespaces() {
901 $this->setupHookNamespaces();
902 $obj = $this->newObj();
904 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( 'main' ) );
905 $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
906 $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
910 * @covers NamespaceInfo::getValidNamespaces
912 public function testGetValidNamespaces_HookNamespaces() {
913 $this->setupHookNamespaces();
916 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 123456 ],
917 $this->newObj()->getValidNamespaces()
921 // %} End hook namespaces
926 * @return NamespaceInfo
928 private function setupExtraNamespaces() {
929 return $this->newObj( [ 'ExtraNamespaces' =>
930 [ NS_MAIN
=> 'No effect', NS_TALK
=> 'No effect', 1234567 => 'Extra' ]
935 * @covers NamespaceInfo::getCanonicalNamespaces
937 public function testGetCanonicalNamespaces_ExtraNamespaces() {
939 $this->getDefaultNamespaces() +
[ 1234567 => 'Extra' ],
940 $this->setupExtraNamespaces()->getCanonicalNamespaces()
945 * @covers NamespaceInfo::getCanonicalName
947 public function testGetCanonicalName_ExtraNamespaces() {
948 $obj = $this->setupExtraNamespaces();
950 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
951 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK
) );
952 $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
956 * @covers NamespaceInfo::getCanonicalIndex
958 public function testGetCanonicalIndex_ExtraNamespaces() {
959 $obj = $this->setupExtraNamespaces();
961 $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
962 $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
963 $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
967 * @covers NamespaceInfo::getValidNamespaces
969 public function testGetValidNamespaces_ExtraNamespaces() {
971 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 1234567 ],
972 $this->setupExtraNamespaces()->getValidNamespaces()
976 // %} End extra namespaces
978 // Canonical namespace caching
981 * @covers NamespaceInfo::getCanonicalNamespaces
983 public function testGetCanonicalNamespaces_caching() {
984 $obj = $this->newObj();
986 // This should cache the values
987 $obj->getCanonicalNamespaces();
989 // Now try to alter them through nefarious means
990 $this->setupExtensionNamespaces();
991 $this->setupHookNamespaces();
993 // Should have no effect
994 $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
998 * @covers NamespaceInfo::getCanonicalName
1000 public function testGetCanonicalName_caching() {
1001 $obj = $this->newObj();
1003 // This should cache the values
1004 $obj->getCanonicalName( NS_MAIN
);
1006 // Now try to alter them through nefarious means
1007 $this->setupExtensionNamespaces();
1008 $this->setupHookNamespaces();
1010 // Should have no effect
1011 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
1012 $this->assertSame( 'Media', $obj->getCanonicalName( NS_MEDIA
) );
1013 $this->assertFalse( $obj->getCanonicalName( 12345 ) );
1014 $this->assertFalse( $obj->getCanonicalName( 123456 ) );
1018 * @covers NamespaceInfo::getCanonicalIndex
1020 public function testGetCanonicalIndex_caching() {
1021 $obj = $this->newObj();
1023 // This should cache the values
1024 $obj->getCanonicalIndex( '' );
1026 // Now try to alter them through nefarious means
1027 $this->setupExtensionNamespaces();
1028 $this->setupHookNamespaces();
1030 // Should have no effect
1031 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
1032 $this->assertSame( NS_MEDIA
, $obj->getCanonicalIndex( 'media' ) );
1033 $this->assertNull( $obj->getCanonicalIndex( 'extended' ) );
1034 $this->assertNull( $obj->getCanonicalIndex( 'hooked' ) );
1038 * @covers NamespaceInfo::getValidNamespaces
1040 public function testGetValidNamespaces_caching() {
1041 $obj = $this->newObj();
1043 // This should cache the values
1044 $obj->getValidNamespaces();
1046 // Now try to alter through nefarious means
1047 $this->setupExtensionNamespaces();
1048 $this->setupHookNamespaces();
1050 // Should have no effect
1052 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
],
1053 $obj->getValidNamespaces()
1057 // %} End canonical namespace caching
1063 * @dataProvider provideGetValidNamespaces_misc
1064 * @covers NamespaceInfo::getValidNamespaces
1066 * @param array $namespaces List of namespace indices to return from getCanonicalNamespaces()
1067 * (list is overwritten by a hook, so NS_MAIN doesn't have to be present)
1068 * @param array $expected
1070 public function testGetValidNamespaces_misc( array $namespaces, array $expected ) {
1071 // Each namespace's name is just its index
1072 $this->setTemporaryHook( 'CanonicalNamespaces',
1073 function ( &$canonicalNamespaces ) use ( $namespaces ) {
1074 $canonicalNamespaces = array_combine( $namespaces, $namespaces );
1077 $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
1080 public function provideGetValidNamespaces_misc() {
1082 'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
1083 'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
1084 'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
1088 // %} End miscellaneous
1089 // %} End canonical namespaces
1091 /**********************************************************************************************
1092 * Restriction levels
1097 * This mock user can only have isAllowed() called on it.
1099 * @param array $groups Groups for the mock user to have
1102 private function getMockUser( array $groups = [] ) : User
{
1105 $mock = $this->createMock( User
::class );
1106 $mock->method( 'isAllowed' )->will( $this->returnCallback(
1107 function ( $action ) use ( $groups ) {
1108 global $wgGroupPermissions, $wgRevokePermissions;
1109 if ( $action == '' ) {
1112 foreach ( $wgRevokePermissions as $group => $rights ) {
1113 if ( !in_array( $group, $groups ) ) {
1116 if ( isset( $rights[$action] ) && $rights[$action] ) {
1120 foreach ( $wgGroupPermissions as $group => $rights ) {
1121 if ( !in_array( $group, $groups ) ) {
1124 if ( isset( $rights[$action] ) && $rights[$action] ) {
1131 $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
1136 * @dataProvider provideGetRestrictionLevels
1137 * @covers NamespaceInfo::getRestrictionLevels
1139 * @param array $expected
1141 * @param User|null $user
1143 public function testGetRestrictionLevels( array $expected, $ns, User
$user = null ) {
1144 $this->setMwGlobals( [
1145 'wgGroupPermissions' => [
1146 '*' => [ 'edit' => true ],
1147 'autoconfirmed' => [ 'editsemiprotected' => true ],
1149 'editsemiprotected' => true,
1150 'editprotected' => true,
1152 'privileged' => [ 'privileged' => true ],
1154 'wgRevokePermissions' => [
1155 'noeditsemiprotected' => [ 'editsemiprotected' => true ],
1158 $obj = $this->newObj( [
1159 'NamespaceProtection' => [
1160 NS_MAIN
=> 'autoconfirmed',
1162 101 => [ 'editsemiprotected', 'privileged' ],
1165 $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
1168 public function provideGetRestrictionLevels() {
1170 'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK
],
1171 'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN
],
1172 'Restricted to sysop' => [ [ '' ], NS_USER
],
1173 // @todo Bug -- 'sysop' protection should be allowed in this case. Someone who's
1174 // autoconfirmed and also privileged can edit this namespace, and would be blocked by
1175 // the sysop protection.
1176 'Restricted to someone in two groups' => [ [ '' ], 101 ],
1178 'No special permissions' => [ [ '' ], NS_TALK
, $this->getMockUser() ],
1179 'autoconfirmed' => [
1180 [ '', 'autoconfirmed' ],
1182 $this->getMockUser( [ 'autoconfirmed' ] )
1184 'autoconfirmed revoked' => [
1187 $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
1190 [ '', 'autoconfirmed', 'sysop' ],
1192 $this->getMockUser( [ 'sysop' ] )
1194 'sysop with autoconfirmed revoked (a bit silly)' => [
1197 $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
1202 // %} End restriction levels
1206 * For really cool vim folding this needs to be at the end:
1207 * vim: foldmarker=%{,%} foldmethod=marker