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::exists
171 * @dataProvider provideExists
173 * @param bool $expected
175 public function testExists( $ns, $expected ) {
176 $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
179 public function provideExists() {
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 ],
191 * Note if we add a namespace registration system with keys like 'MAIN'
192 * we should add tests here for equivalence on things like 'MAIN' == 0
193 * and 'MAIN' == NS_MAIN.
194 * @covers NamespaceInfo::equals
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
) );
212 * @param bool $expected
213 * @dataProvider provideSubjectEquals
214 * @covers NamespaceInfo::subjectEquals
216 public function testSubjectEquals( $ns1, $ns2, $expected ) {
217 $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
220 public function provideSubjectEquals() {
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 ],
227 [ NS_USER_TALK
, NS_USER_TALK
, true ],
228 [ NS_SPECIAL
, NS_SPECIAL
, true ],
229 [ NS_MAIN
, NS_TALK
, true ],
230 [ NS_USER
, NS_USER_TALK
, true ],
232 [ NS_PROJECT
, NS_TEMPLATE
, false ],
233 [ NS_SPECIAL
, NS_MAIN
, false ],
234 [ NS_MEDIA
, NS_SPECIAL
, false ],
235 [ NS_SPECIAL
, NS_MEDIA
, false ],
240 * @dataProvider provideHasTalkNamespace
241 * @covers NamespaceInfo::hasTalkNamespace
244 * @param bool $expected
246 public function testHasTalkNamespace( $ns, $expected ) {
247 $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
250 public function provideHasTalkNamespace() {
253 [ NS_SPECIAL
, false ],
258 [ NS_USER_TALK
, true ],
267 * @param bool $expected
268 * @param array $contentNamespaces
269 * @covers NamespaceInfo::isContent
270 * @dataProvider provideIsContent
272 public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN
] ) {
273 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
274 $this->assertSame( $expected, $obj->isContent( $ns ) );
277 public function provideIsContent() {
281 [ NS_SPECIAL
, false ],
284 [ NS_CATEGORY
, 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, [] ],
295 * @dataProvider provideWantSignatures
296 * @covers NamespaceInfo::wantSignatures
299 * @param bool $expected
301 public function testWantSignatures( $index, $expected ) {
302 $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
305 public function provideWantSignatures() {
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 ],
320 * @dataProvider provideWantSignatures_ExtraSignatureNamespaces
321 * @covers NamespaceInfo::wantSignatures
324 * @param int $expected
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 ) );
332 public function provideWantSignatures_ExtraSignatureNamespaces() {
335 // We've added all these as extra signature namespaces, so expect true
336 return [ $arr[0], true ];
338 self
::provideWantSignatures()
341 // Add one more that's false
342 $ret['Another nonexistent subject'] = [ 12345678, false ];
348 * @param bool $expected
349 * @covers NamespaceInfo::isWatchable
350 * @dataProvider provideIsWatchable
352 public function testIsWatchable( $ns, $expected ) {
353 $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
356 public function provideIsWatchable() {
358 // Specials namespaces are not watchable
360 [ NS_SPECIAL
, false ],
362 // Core defined namespaces are watchables
366 // Additional, user defined namespaces are watchables
374 * @param int $expected
375 * @param array|null $namespacesWithSubpages To pass to constructor
376 * @covers NamespaceInfo::hasSubpages
377 * @dataProvider provideHasSubpages
379 public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
380 $obj = $this->newObj( $namespacesWithSubpages
381 ?
[ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
383 $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
386 public function provideHasSubpages() {
388 // Special namespaces:
390 [ NS_SPECIAL
, false ],
392 // Namespaces without subpages
394 [ NS_MAIN
, true, [ NS_MAIN
=> true ] ],
395 [ NS_MAIN
, false, [ NS_MAIN
=> false ] ],
397 // Some namespaces with subpages
400 [ NS_USER_TALK
, true ],
405 * @param $contentNamespaces To pass to constructor
406 * @param array $expected
407 * @dataProvider provideGetContentNamespaces
408 * @covers NamespaceInfo::getContentNamespaces
410 public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
411 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
412 $this->assertSame( $expected, $obj->getContentNamespaces() );
415 public function provideGetContentNamespaces() {
419 [ false, [ NS_MAIN
] ],
420 [ null, [ NS_MAIN
] ],
426 // NS_MAIN is forced to be content even if unwanted
427 [ [ NS_USER
, NS_CATEGORY
], [ NS_MAIN
, NS_USER
, NS_CATEGORY
] ],
429 // In other cases, return as-is
430 [ [ NS_MAIN
], [ NS_MAIN
] ],
431 [ [ NS_MAIN
, NS_USER
, NS_CATEGORY
], [ NS_MAIN
, NS_USER
, NS_CATEGORY
] ],
436 * @covers NamespaceInfo::getSubjectNamespaces
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" );
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" );
452 * @covers NamespaceInfo::getTalkNamespaces
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" );
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" );
469 * @param bool $expected
470 * @param bool $capitalLinks To pass to constructor
471 * @param array $capitalLinkOverrides To pass to constructor
472 * @dataProvider provideIsCapitalized
473 * @covers NamespaceInfo::isCapitalized
475 public function testIsCapitalized(
476 $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
478 $obj = $this->newObj( [
479 'CapitalLinks' => $capitalLinks,
480 'CapitalLinkOverrides' => $capitalLinkOverrides,
482 $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
485 public function provideIsCapitalized() {
487 // Test default settings
488 [ NS_PROJECT
, true ],
489 [ NS_PROJECT_TALK
, true ],
493 // Always capitalized no matter what
494 [ NS_SPECIAL
, true, false ],
495 [ NS_USER
, true, false ],
496 [ NS_MEDIAWIKI
, true, false ],
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 ] ],
503 // Overrides work for other namespaces
504 [ NS_PROJECT
, false, true, [ NS_PROJECT
=> false ] ],
505 [ NS_PROJECT
, true, false, [ NS_PROJECT
=> true ] ],
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 ] ],
516 * @covers NamespaceInfo::hasGenderDistinction
518 public function testHasGenderDistinction() {
519 $obj = $this->newObj();
521 // Namespaces with gender distinctions
522 $this->assertTrue( $obj->hasGenderDistinction( NS_USER
) );
523 $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK
) );
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
) );
533 * @covers NamespaceInfo::isNonincludable
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
) );
542 * @dataProvider provideGetNamespaceContentModel
543 * @covers NamespaceInfo::getNamespaceContentModel
546 * @param string $expected
548 public function testGetNamespaceContentModel( $ns, $expected ) {
549 $obj = $this->newObj( [ 'NamespaceContentModels' =>
550 [ NS_USER
=> CONTENT_MODEL_WIKITEXT
, 123 => CONTENT_MODEL_JSON
, 1234 => 'abcdef' ],
552 $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
555 public function provideGetNamespaceContentModel() {
559 [ NS_USER
, CONTENT_MODEL_WIKITEXT
],
560 [ NS_USER_TALK
, null ],
561 [ NS_SPECIAL
, null ],
563 [ 123, CONTENT_MODEL_JSON
],
570 * @dataProvider provideGetCategoryLinkType
571 * @covers NamespaceInfo::getCategoryLinkType
574 * @param string $expected
576 public function testGetCategoryLinkType( $ns, $expected ) {
577 $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
580 public function provideGetCategoryLinkType() {
585 [ NS_USER_TALK
, 'page' ],
588 [ NS_FILE_TALK
, 'page' ],
590 [ NS_CATEGORY
, 'subcat' ],
591 [ NS_CATEGORY_TALK
, 'page' ],
598 // %} End basic methods
600 /**********************************************************************************************
601 * getSubject/Talk/Associated
606 * @dataProvider provideSubjectTalk
607 * @covers NamespaceInfo::getSubject
608 * @covers NamespaceInfo::getSubjectPage
609 * @covers NamespaceInfo::isMethodValidFor
610 * @covers Title::getSubjectPage
612 * @param int $subject
615 public function testGetSubject( $subject, $talk ) {
616 $obj = $this->newObj();
617 $this->assertSame( $subject, $obj->getSubject( $subject ) );
618 $this->assertSame( $subject, $obj->getSubject( $talk ) );
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
624 $this->assertSame( $subjectTitleVal, $obj->getSubjectPage( $subjectTitleVal ) );
625 $this->assertEquals( $subjectTitleVal, $obj->getSubjectPage( $talkTitleVal ) );
627 $subjectTitle = Title
::makeTitle( $subject, 'A' );
628 $talkTitle = Title
::makeTitle( $talk, 'A' );
629 $this->assertSame( $subjectTitle, $subjectTitle->getSubjectPage() );
630 $this->assertEquals( $subjectTitle, $talkTitle->getSubjectPage() );
634 * @dataProvider provideSpecialNamespaces
635 * @covers NamespaceInfo::getSubject
636 * @covers NamespaceInfo::getSubjectPage
640 public function testGetSubject_special( $ns ) {
641 $obj = $this->newObj();
642 $this->assertSame( $ns, $obj->getSubject( $ns ) );
644 $title = new TitleValue( $ns, 'A' );
645 $this->assertSame( $title, $obj->getSubjectPage( $title ) );
649 * @dataProvider provideSubjectTalk
650 * @covers NamespaceInfo::getTalk
651 * @covers NamespaceInfo::getTalkPage
652 * @covers NamespaceInfo::isMethodValidFor
653 * @covers Title::getTalkPage
655 * @param int $subject
658 public function testGetTalk( $subject, $talk ) {
659 $obj = $this->newObj();
660 $this->assertSame( $talk, $obj->getTalk( $subject ) );
661 $this->assertSame( $talk, $obj->getTalk( $talk ) );
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
667 $this->assertEquals( $talkTitleVal, $obj->getTalkPage( $subjectTitleVal ) );
668 $this->assertSame( $talkTitleVal, $obj->getTalkPage( $talkTitleVal ) );
670 $subjectTitle = Title
::makeTitle( $subject, 'A' );
671 $talkTitle = Title
::makeTitle( $talk, 'A' );
672 $this->assertEquals( $talkTitle, $subjectTitle->getTalkPage() );
673 $this->assertSame( $talkTitle, $talkTitle->getTalkPage() );
677 * @dataProvider provideSpecialNamespaces
678 * @covers NamespaceInfo::getTalk
679 * @covers NamespaceInfo::isMethodValidFor
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 );
690 * @dataProvider provideSpecialNamespaces
691 * @covers NamespaceInfo::getTalk
692 * @covers NamespaceInfo::getTalkPage
693 * @covers NamespaceInfo::isMethodValidFor
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' ) );
704 * @dataProvider provideSpecialNamespaces
705 * @covers NamespaceInfo::getTalk
706 * @covers NamespaceInfo::getTalkPage
707 * @covers NamespaceInfo::isMethodValidFor
708 * @covers Title::getTalkPage
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();
719 * @dataProvider provideSpecialNamespaces
720 * @covers NamespaceInfo::getAssociated
721 * @covers NamespaceInfo::isMethodValidFor
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 );
732 * @dataProvider provideSpecialNamespaces
733 * @covers NamespaceInfo::getAssociated
734 * @covers NamespaceInfo::getAssociatedPage
735 * @covers NamespaceInfo::isMethodValidFor
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' ) );
746 * @dataProvider provideSpecialNamespaces
747 * @covers NamespaceInfo::getAssociated
748 * @covers NamespaceInfo::getAssociatedPage
749 * @covers NamespaceInfo::isMethodValidFor
750 * @covers Title::getOtherPage
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();
761 * @dataProvider provideSubjectTalk
762 * @covers NamespaceInfo::getAssociated
763 * @covers NamespaceInfo::getAssociatedPage
764 * @covers Title::getOtherPage
766 * @param int $subject
769 public function testGetAssociated( $subject, $talk ) {
770 $obj = $this->newObj();
771 $this->assertSame( $talk, $obj->getAssociated( $subject ) );
772 $this->assertSame( $subject, $obj->getAssociated( $talk ) );
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 ) );
780 $subjectTitle = Title
::makeTitle( $subject, 'A' );
781 $talkTitle = Title
::makeTitle( $talk, 'A' );
782 $this->assertEquals( $talkTitle, $subjectTitle->getOtherPage() );
783 $this->assertEquals( $subjectTitle, $talkTitle->getOtherPage() );
786 public static function provideSubjectTalk() {
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 ],
795 public static function provideSpecialNamespaces() {
797 'Special' => [ NS_SPECIAL
],
798 'Media' => [ NS_MEDIA
],
799 'Unknown negative index' => [ -613 ],
803 // %} End getSubject/Talk/Associated
805 /**********************************************************************************************
806 * Canonical namespaces
810 // Default canonical namespaces
812 private function getDefaultNamespaces() {
813 return [ NS_MAIN
=> '' ] + self
::$defaultOptions['CanonicalNamespaceNames'];
817 * @covers NamespaceInfo::getCanonicalNamespaces
819 public function testGetCanonicalNamespaces() {
821 $this->getDefaultNamespaces(),
822 $this->newObj()->getCanonicalNamespaces()
827 * @dataProvider provideGetCanonicalName
828 * @covers NamespaceInfo::getCanonicalName
831 * @param string|bool $expected
833 public function testGetCanonicalName( $index, $expected ) {
834 $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
837 public function provideGetCanonicalName() {
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 ],
849 * @dataProvider provideGetCanonicalIndex
850 * @covers NamespaceInfo::getCanonicalIndex
852 * @param string $name
853 * @param int|null $expected
855 public function testGetCanonicalIndex( $name, $expected ) {
856 $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
859 public function provideGetCanonicalIndex() {
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 ],
871 * @covers NamespaceInfo::getValidNamespaces
873 public function testGetValidNamespaces() {
875 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
],
876 $this->newObj()->getValidNamespaces()
880 // %} End default canonical namespaces
882 // No canonical namespace names
886 * @covers NamespaceInfo::getCanonicalNamespaces
888 public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
889 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
891 $this->assertSame( [ NS_MAIN
=> '' ], $obj->getCanonicalNamespaces() );
895 * @covers NamespaceInfo::getCanonicalName
897 public function testGetCanonicalName_NoCanonicalNamespaceNames() {
898 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
900 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
901 $this->assertFalse( $obj->getCanonicalName( NS_TALK
) );
905 * @covers NamespaceInfo::getCanonicalIndex
907 public function testGetCanonicalIndex_NoCanonicalNamespaceNames() {
908 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
910 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
911 $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
915 * @covers NamespaceInfo::getValidNamespaces
917 public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
918 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
920 $this->assertSame( [ NS_MAIN
], $obj->getValidNamespaces() );
923 // %} End no canonical namespace names
925 // Test extension namespaces
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' ]
936 * @covers NamespaceInfo::getCanonicalNamespaces
938 public function testGetCanonicalNamespaces_ExtensionNamespaces() {
939 $this->setupExtensionNamespaces();
942 $this->getDefaultNamespaces() +
[ 12345 => 'Extended' ],
943 $this->newObj()->getCanonicalNamespaces()
948 * @covers NamespaceInfo::getCanonicalName
950 public function testGetCanonicalName_ExtensionNamespaces() {
951 $this->setupExtensionNamespaces();
952 $obj = $this->newObj();
954 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
955 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK
) );
956 $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
960 * @covers NamespaceInfo::getCanonicalIndex
962 public function testGetCanonicalIndex_ExtensionNamespaces() {
963 $this->setupExtensionNamespaces();
964 $obj = $this->newObj();
966 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
967 $this->assertSame( NS_TALK
, $obj->getCanonicalIndex( 'talk' ) );
968 $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
972 * @covers NamespaceInfo::getValidNamespaces
974 public function testGetValidNamespaces_ExtensionNamespaces() {
975 $this->setupExtensionNamespaces();
978 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 12345 ],
979 $this->newObj()->getValidNamespaces()
983 // %} End extension namespaces
989 * @return array Expected canonical namespaces
991 private function setupHookNamespaces() {
993 function ( &$canonicalNamespaces ) {
994 $canonicalNamespaces[NS_MAIN
] = 'Main';
995 unset( $canonicalNamespaces[NS_MEDIA
] );
996 $canonicalNamespaces[123456] = 'Hooked';
998 $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
999 $expected = $this->getDefaultNamespaces();
1000 ( $callback )( $expected );
1005 * @covers NamespaceInfo::getCanonicalNamespaces
1007 public function testGetCanonicalNamespaces_HookNamespaces() {
1008 $expected = $this->setupHookNamespaces();
1010 $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
1014 * @covers NamespaceInfo::getCanonicalName
1016 public function testGetCanonicalName_HookNamespaces() {
1017 $this->setupHookNamespaces();
1018 $obj = $this->newObj();
1020 $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN
) );
1021 $this->assertFalse( $obj->getCanonicalName( NS_MEDIA
) );
1022 $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
1026 * @covers NamespaceInfo::getCanonicalIndex
1028 public function testGetCanonicalIndex_HookNamespaces() {
1029 $this->setupHookNamespaces();
1030 $obj = $this->newObj();
1032 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( 'main' ) );
1033 $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
1034 $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
1038 * @covers NamespaceInfo::getValidNamespaces
1040 public function testGetValidNamespaces_HookNamespaces() {
1041 $this->setupHookNamespaces();
1044 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 123456 ],
1045 $this->newObj()->getValidNamespaces()
1049 // %} End hook namespaces
1055 * @return NamespaceInfo
1057 private function setupExtraNamespaces() {
1058 return $this->newObj( [ 'ExtraNamespaces' =>
1059 [ NS_MAIN
=> 'No effect', NS_TALK
=> 'No effect', 1234567 => 'Extra' ]
1064 * @covers NamespaceInfo::getCanonicalNamespaces
1066 public function testGetCanonicalNamespaces_ExtraNamespaces() {
1068 $this->getDefaultNamespaces() +
[ 1234567 => 'Extra' ],
1069 $this->setupExtraNamespaces()->getCanonicalNamespaces()
1074 * @covers NamespaceInfo::getCanonicalName
1076 public function testGetCanonicalName_ExtraNamespaces() {
1077 $obj = $this->setupExtraNamespaces();
1079 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
1080 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK
) );
1081 $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
1085 * @covers NamespaceInfo::getCanonicalIndex
1087 public function testGetCanonicalIndex_ExtraNamespaces() {
1088 $obj = $this->setupExtraNamespaces();
1090 $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
1091 $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
1092 $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
1096 * @covers NamespaceInfo::getValidNamespaces
1098 public function testGetValidNamespaces_ExtraNamespaces() {
1100 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 1234567 ],
1101 $this->setupExtraNamespaces()->getValidNamespaces()
1105 // %} End extra namespaces
1107 // Canonical namespace caching
1111 * @covers NamespaceInfo::getCanonicalNamespaces
1113 public function testGetCanonicalNamespaces_caching() {
1114 $obj = $this->newObj();
1116 // This should cache the values
1117 $obj->getCanonicalNamespaces();
1119 // Now try to alter them through nefarious means
1120 $this->setupExtensionNamespaces();
1121 $this->setupHookNamespaces();
1123 // Should have no effect
1124 $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
1128 * @covers NamespaceInfo::getCanonicalName
1130 public function testGetCanonicalName_caching() {
1131 $obj = $this->newObj();
1133 // This should cache the values
1134 $obj->getCanonicalName( NS_MAIN
);
1136 // Now try to alter them through nefarious means
1137 $this->setupExtensionNamespaces();
1138 $this->setupHookNamespaces();
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 ) );
1148 * @covers NamespaceInfo::getCanonicalIndex
1150 public function testGetCanonicalIndex_caching() {
1151 $obj = $this->newObj();
1153 // This should cache the values
1154 $obj->getCanonicalIndex( '' );
1156 // Now try to alter them through nefarious means
1157 $this->setupExtensionNamespaces();
1158 $this->setupHookNamespaces();
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' ) );
1168 * @covers NamespaceInfo::getValidNamespaces
1170 public function testGetValidNamespaces_caching() {
1171 $obj = $this->newObj();
1173 // This should cache the values
1174 $obj->getValidNamespaces();
1176 // Now try to alter through nefarious means
1177 $this->setupExtensionNamespaces();
1178 $this->setupHookNamespaces();
1180 // Should have no effect
1182 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
],
1183 $obj->getValidNamespaces()
1187 // %} End canonical namespace caching
1193 * @dataProvider provideGetValidNamespaces_misc
1194 * @covers NamespaceInfo::getValidNamespaces
1196 * @param array $namespaces List of namespace indices to return from getCanonicalNamespaces()
1197 * (list is overwritten by a hook, so NS_MAIN doesn't have to be present)
1198 * @param array $expected
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 );
1207 $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
1210 public function provideGetValidNamespaces_misc() {
1212 'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
1213 'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
1214 'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
1218 // %} End miscellaneous
1219 // %} End canonical namespaces
1221 /**********************************************************************************************
1222 * Restriction levels
1227 * This mock user can only have isAllowed() called on it.
1229 * @param array $groups Groups for the mock user to have
1232 private function getMockUser( array $groups = [] ) : User
{
1235 $mock = $this->createMock( User
::class );
1236 $mock->method( 'isAllowed' )->will( $this->returnCallback(
1237 function ( $action ) use ( $groups ) {
1238 global $wgGroupPermissions, $wgRevokePermissions;
1239 if ( $action == '' ) {
1242 foreach ( $wgRevokePermissions as $group => $rights ) {
1243 if ( !in_array( $group, $groups ) ) {
1246 if ( isset( $rights[$action] ) && $rights[$action] ) {
1250 foreach ( $wgGroupPermissions as $group => $rights ) {
1251 if ( !in_array( $group, $groups ) ) {
1254 if ( isset( $rights[$action] ) && $rights[$action] ) {
1261 $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
1266 * @dataProvider provideGetRestrictionLevels
1267 * @covers NamespaceInfo::getRestrictionLevels
1269 * @param array $expected
1271 * @param User|null $user
1273 public function testGetRestrictionLevels( array $expected, $ns, User
$user = null ) {
1274 $this->setMwGlobals( [
1275 'wgGroupPermissions' => [
1276 '*' => [ 'edit' => true ],
1277 'autoconfirmed' => [ 'editsemiprotected' => true ],
1279 'editsemiprotected' => true,
1280 'editprotected' => true,
1282 'privileged' => [ 'privileged' => true ],
1284 'wgRevokePermissions' => [
1285 'noeditsemiprotected' => [ 'editsemiprotected' => true ],
1288 $obj = $this->newObj( [
1289 'NamespaceProtection' => [
1290 NS_MAIN
=> 'autoconfirmed',
1292 101 => [ 'editsemiprotected', 'privileged' ],
1295 $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
1298 public function provideGetRestrictionLevels() {
1300 'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK
],
1301 'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN
],
1302 'Restricted to sysop' => [ [ '' ], NS_USER
],
1303 // @todo Bug -- 'sysop' protection should be allowed in this case. Someone who's
1304 // autoconfirmed and also privileged can edit this namespace, and would be blocked by
1305 // the sysop protection.
1306 'Restricted to someone in two groups' => [ [ '' ], 101 ],
1308 'No special permissions' => [ [ '' ], NS_TALK
, $this->getMockUser() ],
1309 'autoconfirmed' => [
1310 [ '', 'autoconfirmed' ],
1312 $this->getMockUser( [ 'autoconfirmed' ] )
1314 'autoconfirmed revoked' => [
1317 $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
1320 [ '', 'autoconfirmed', 'sysop' ],
1322 $this->getMockUser( [ 'sysop' ] )
1324 'sysop with autoconfirmed revoked (a bit silly)' => [
1327 $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
1332 // %} End restriction levels
1336 * For really cool vim folding this needs to be at the end:
1337 * vim: foldmarker=%{,%} foldmethod=marker