3 * @author Antoine Musso
4 * @copyright Copyright © 2011, Antoine Musso
8 use MediaWiki\Config\ServiceOptions
;
9 use MediaWiki\Linker\LinkTarget
;
11 class NamespaceInfoTest
extends MediaWikiTestCase
{
12 /**********************************************************************************************
16 private $scopedCallback;
18 public function setUp() {
21 // Boo, there's still some global state in the class :(
24 unset( $hooks['CanonicalNamespaces'] );
25 $this->setMwGlobals( 'wgHooks', $hooks );
27 $this->scopedCallback
=
28 ExtensionRegistry
::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
31 public function tearDown() {
32 $this->scopedCallback
= null;
38 * TODO Make this a const once HHVM support is dropped (T192166)
40 private static $defaultOptions = [
41 'AllowImageMoving' => true,
42 'CanonicalNamespaceNames' => [
45 NS_USER_TALK
=> 'User_talk',
46 NS_SPECIAL
=> 'Special',
49 'CapitalLinkOverrides' => [],
50 'CapitalLinks' => true,
51 'ContentNamespaces' => [ NS_MAIN
],
52 'ExtraNamespaces' => [],
53 'ExtraSignatureNamespaces' => [],
54 'NamespaceContentModels' => [],
55 'NamespaceProtection' => [],
56 'NamespacesWithSubpages' => [
61 'NonincludableNamespaces' => [],
62 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
65 private function newObj( array $options = [] ) : NamespaceInfo
{
66 return new NamespaceInfo( new ServiceOptions( NamespaceInfo
::$constructorOptions,
67 $options, self
::$defaultOptions ) );
72 /**********************************************************************************************
78 * @covers NamespaceInfo::__construct
79 * @dataProvider provideConstructor
80 * @param ServiceOptions $options
81 * @param string|null $expectedExceptionText
83 public function testConstructor( ServiceOptions
$options, $expectedExceptionText = null ) {
84 if ( $expectedExceptionText !== null ) {
85 $this->setExpectedException( \Wikimedia\Assert\PreconditionException
::class,
86 $expectedExceptionText );
88 new NamespaceInfo( $options );
89 $this->assertTrue( true );
92 public function provideConstructor() {
94 [ new ServiceOptions( NamespaceInfo
::$constructorOptions, self
::$defaultOptions ) ],
95 [ new ServiceOptions( [], [] ), 'Required options missing: ' ],
97 array_merge( NamespaceInfo
::$constructorOptions, [ 'invalid' ] ),
98 self
::$defaultOptions,
100 ), 'Unsupported options passed: invalid' ],
105 * @dataProvider provideIsMovable
106 * @covers NamespaceInfo::isMovable
108 * @param bool $expected
110 * @param bool $allowImageMoving
112 public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
113 $obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
114 $this->assertSame( $expected, $obj->isMovable( $ns ) );
117 public function provideIsMovable() {
119 'Main' => [ true, NS_MAIN
],
120 'Talk' => [ true, NS_TALK
],
121 'Special' => [ false, NS_SPECIAL
],
122 'Nonexistent even namespace' => [ true, 1234 ],
123 'Nonexistent odd namespace' => [ true, 12345 ],
125 'Media with image moving' => [ false, NS_MEDIA
, true ],
126 'Media with no image moving' => [ false, NS_MEDIA
, false ],
127 'File with image moving' => [ true, NS_FILE
, true ],
128 'File with no image moving' => [ false, NS_FILE
, false ],
134 * @param bool $expected
135 * @dataProvider provideIsSubject
136 * @covers NamespaceInfo::isSubject
138 public function testIsSubject( $ns, $expected ) {
139 $this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
144 * @param bool $expected
145 * @dataProvider provideIsSubject
146 * @covers NamespaceInfo::isTalk
148 public function testIsTalk( $ns, $expected ) {
149 $this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
152 public function provideIsSubject() {
154 // Special namespaces
156 [ NS_SPECIAL
, true ],
165 [ NS_USER_TALK
, false ],
171 * @covers NamespaceInfo::exists
172 * @dataProvider provideExists
174 * @param bool $expected
176 public function testExists( $ns, $expected ) {
177 $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
180 public function provideExists() {
182 'Main' => [ NS_MAIN
, true ],
183 'Talk' => [ NS_TALK
, true ],
184 'Media' => [ NS_MEDIA
, true ],
185 'Special' => [ NS_SPECIAL
, true ],
186 'Nonexistent' => [ 12345, false ],
187 'Negative nonexistent' => [ -12345, false ],
192 * Note if we add a namespace registration system with keys like 'MAIN'
193 * we should add tests here for equivalence on things like 'MAIN' == 0
194 * and 'MAIN' == NS_MAIN.
195 * @covers NamespaceInfo::equals
197 public function testEquals() {
198 $obj = $this->newObj();
199 $this->assertTrue( $obj->equals( NS_MAIN
, NS_MAIN
) );
200 $this->assertTrue( $obj->equals( NS_MAIN
, 0 ) ); // In case we make NS_MAIN 'MAIN'
201 $this->assertTrue( $obj->equals( NS_USER
, NS_USER
) );
202 $this->assertTrue( $obj->equals( NS_USER
, 2 ) );
203 $this->assertTrue( $obj->equals( NS_USER_TALK
, NS_USER_TALK
) );
204 $this->assertTrue( $obj->equals( NS_SPECIAL
, NS_SPECIAL
) );
205 $this->assertFalse( $obj->equals( NS_MAIN
, NS_TALK
) );
206 $this->assertFalse( $obj->equals( NS_USER
, NS_USER_TALK
) );
207 $this->assertFalse( $obj->equals( NS_PROJECT
, NS_TEMPLATE
) );
213 * @param bool $expected
214 * @dataProvider provideSubjectEquals
215 * @covers NamespaceInfo::subjectEquals
217 public function testSubjectEquals( $ns1, $ns2, $expected ) {
218 $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
221 public function provideSubjectEquals() {
223 [ NS_MAIN
, NS_MAIN
, true ],
224 // In case we make NS_MAIN 'MAIN'
225 [ NS_MAIN
, 0, true ],
226 [ NS_USER
, NS_USER
, true ],
227 [ NS_USER
, 2, true ],
228 [ NS_USER_TALK
, NS_USER_TALK
, true ],
229 [ NS_SPECIAL
, NS_SPECIAL
, true ],
230 [ NS_MAIN
, NS_TALK
, true ],
231 [ NS_USER
, NS_USER_TALK
, true ],
233 [ NS_PROJECT
, NS_TEMPLATE
, false ],
234 [ NS_SPECIAL
, NS_MAIN
, false ],
235 [ NS_MEDIA
, NS_SPECIAL
, false ],
236 [ NS_SPECIAL
, NS_MEDIA
, false ],
241 * @dataProvider provideHasTalkNamespace
242 * @covers NamespaceInfo::hasTalkNamespace
245 * @param bool $expected
247 public function testHasTalkNamespace( $ns, $expected ) {
248 $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
251 public function provideHasTalkNamespace() {
254 [ NS_SPECIAL
, false ],
259 [ NS_USER_TALK
, true ],
268 * @param bool $expected
269 * @param array $contentNamespaces
270 * @covers NamespaceInfo::isContent
271 * @dataProvider provideIsContent
273 public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN
] ) {
274 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
275 $this->assertSame( $expected, $obj->isContent( $ns ) );
278 public function provideIsContent() {
282 [ NS_SPECIAL
, false ],
285 [ NS_CATEGORY
, false ],
287 [ 100, true, [ NS_MAIN
, 100, 252 ] ],
288 [ 252, true, [ NS_MAIN
, 100, 252 ] ],
289 [ NS_MAIN
, true, [ NS_MAIN
, 100, 252 ] ],
290 // NS_MAIN is always content
291 [ NS_MAIN
, true, [] ],
296 * @dataProvider provideWantSignatures
297 * @covers NamespaceInfo::wantSignatures
300 * @param bool $expected
302 public function testWantSignatures( $index, $expected ) {
303 $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
306 public function provideWantSignatures() {
308 'Main' => [ NS_MAIN
, false ],
309 'Talk' => [ NS_TALK
, true ],
310 'User' => [ NS_USER
, false ],
311 'User talk' => [ NS_USER_TALK
, true ],
312 'Special' => [ NS_SPECIAL
, false ],
313 'Media' => [ NS_MEDIA
, false ],
314 'Nonexistent talk' => [ 12345, true ],
315 'Nonexistent subject' => [ 123456, false ],
316 'Nonexistent negative odd' => [ -12345, false ],
321 * @dataProvider provideWantSignatures_ExtraSignatureNamespaces
322 * @covers NamespaceInfo::wantSignatures
325 * @param int $expected
327 public function testWantSignatures_ExtraSignatureNamespaces( $index, $expected ) {
328 $obj = $this->newObj( [ 'ExtraSignatureNamespaces' =>
329 [ NS_MAIN
, NS_USER
, NS_SPECIAL
, NS_MEDIA
, 123456, -12345 ] ] );
330 $this->assertSame( $expected, $obj->wantSignatures( $index ) );
333 public function provideWantSignatures_ExtraSignatureNamespaces() {
336 // We've added all these as extra signature namespaces, so expect true
337 return [ $arr[0], true ];
339 self
::provideWantSignatures()
342 // Add one more that's false
343 $ret['Another nonexistent subject'] = [ 12345678, false ];
349 * @param bool $expected
350 * @covers NamespaceInfo::isWatchable
351 * @dataProvider provideIsWatchable
353 public function testIsWatchable( $ns, $expected ) {
354 $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
357 public function provideIsWatchable() {
359 // Specials namespaces are not watchable
361 [ NS_SPECIAL
, false ],
363 // Core defined namespaces are watchables
367 // Additional, user defined namespaces are watchables
375 * @param int $expected
376 * @param array|null $namespacesWithSubpages To pass to constructor
377 * @covers NamespaceInfo::hasSubpages
378 * @dataProvider provideHasSubpages
380 public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
381 $obj = $this->newObj( $namespacesWithSubpages
382 ?
[ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
384 $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
387 public function provideHasSubpages() {
389 // Special namespaces:
391 [ NS_SPECIAL
, false ],
393 // Namespaces without subpages
395 [ NS_MAIN
, true, [ NS_MAIN
=> true ] ],
396 [ NS_MAIN
, false, [ NS_MAIN
=> false ] ],
398 // Some namespaces with subpages
401 [ NS_USER_TALK
, true ],
406 * @param mixed $contentNamespaces To pass to constructor
407 * @param array $expected
408 * @dataProvider provideGetContentNamespaces
409 * @covers NamespaceInfo::getContentNamespaces
411 public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
412 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
413 $this->assertSame( $expected, $obj->getContentNamespaces() );
416 public function provideGetContentNamespaces() {
420 [ false, [ NS_MAIN
] ],
421 [ null, [ NS_MAIN
] ],
427 // NS_MAIN is forced to be content even if unwanted
428 [ [ NS_USER
, NS_CATEGORY
], [ NS_MAIN
, NS_USER
, NS_CATEGORY
] ],
430 // In other cases, return as-is
431 [ [ NS_MAIN
], [ NS_MAIN
] ],
432 [ [ NS_MAIN
, NS_USER
, NS_CATEGORY
], [ NS_MAIN
, NS_USER
, NS_CATEGORY
] ],
437 * @covers NamespaceInfo::getSubjectNamespaces
439 public function testGetSubjectNamespaces() {
440 $subjectsNS = $this->newObj()->getSubjectNamespaces();
441 $this->assertContains( NS_MAIN
, $subjectsNS,
442 "Talk namespaces should have NS_MAIN" );
443 $this->assertNotContains( NS_TALK
, $subjectsNS,
444 "Talk namespaces should have NS_TALK" );
446 $this->assertNotContains( NS_MEDIA
, $subjectsNS,
447 "Talk namespaces should not have NS_MEDIA" );
448 $this->assertNotContains( NS_SPECIAL
, $subjectsNS,
449 "Talk namespaces should not have NS_SPECIAL" );
453 * @covers NamespaceInfo::getTalkNamespaces
455 public function testGetTalkNamespaces() {
456 $talkNS = $this->newObj()->getTalkNamespaces();
457 $this->assertContains( NS_TALK
, $talkNS,
458 "Subject namespaces should have NS_TALK" );
459 $this->assertNotContains( NS_MAIN
, $talkNS,
460 "Subject namespaces should not have NS_MAIN" );
462 $this->assertNotContains( NS_MEDIA
, $talkNS,
463 "Subject namespaces should not have NS_MEDIA" );
464 $this->assertNotContains( NS_SPECIAL
, $talkNS,
465 "Subject namespaces should not have NS_SPECIAL" );
470 * @param bool $expected
471 * @param bool $capitalLinks To pass to constructor
472 * @param array $capitalLinkOverrides To pass to constructor
473 * @dataProvider provideIsCapitalized
474 * @covers NamespaceInfo::isCapitalized
476 public function testIsCapitalized(
477 $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
479 $obj = $this->newObj( [
480 'CapitalLinks' => $capitalLinks,
481 'CapitalLinkOverrides' => $capitalLinkOverrides,
483 $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
486 public function provideIsCapitalized() {
488 // Test default settings
489 [ NS_PROJECT
, true ],
490 [ NS_PROJECT_TALK
, true ],
494 // Always capitalized no matter what
495 [ NS_SPECIAL
, true, false ],
496 [ NS_USER
, true, false ],
497 [ NS_MEDIAWIKI
, true, false ],
499 // Even with an override too
500 [ NS_SPECIAL
, true, false, [ NS_SPECIAL
=> false ] ],
501 [ NS_USER
, true, false, [ NS_USER
=> false ] ],
502 [ NS_MEDIAWIKI
, true, false, [ NS_MEDIAWIKI
=> false ] ],
504 // Overrides work for other namespaces
505 [ NS_PROJECT
, false, true, [ NS_PROJECT
=> false ] ],
506 [ NS_PROJECT
, true, false, [ NS_PROJECT
=> true ] ],
508 // NS_MEDIA is treated like NS_FILE, and ignores NS_MEDIA overrides
509 [ NS_MEDIA
, false, true, [ NS_FILE
=> false, NS_MEDIA
=> true ] ],
510 [ NS_MEDIA
, true, false, [ NS_FILE
=> true, NS_MEDIA
=> false ] ],
511 [ NS_FILE
, false, true, [ NS_FILE
=> false, NS_MEDIA
=> true ] ],
512 [ NS_FILE
, true, false, [ NS_FILE
=> true, NS_MEDIA
=> false ] ],
517 * @covers NamespaceInfo::hasGenderDistinction
519 public function testHasGenderDistinction() {
520 $obj = $this->newObj();
522 // Namespaces with gender distinctions
523 $this->assertTrue( $obj->hasGenderDistinction( NS_USER
) );
524 $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK
) );
526 // Other ones, "genderless"
527 $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA
) );
528 $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL
) );
529 $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN
) );
530 $this->assertFalse( $obj->hasGenderDistinction( NS_TALK
) );
534 * @covers NamespaceInfo::isNonincludable
536 public function testIsNonincludable() {
537 $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER
] ] );
538 $this->assertTrue( $obj->isNonincludable( NS_USER
) );
539 $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE
) );
543 * @dataProvider provideGetNamespaceContentModel
544 * @covers NamespaceInfo::getNamespaceContentModel
547 * @param string $expected
549 public function testGetNamespaceContentModel( $ns, $expected ) {
550 $obj = $this->newObj( [ 'NamespaceContentModels' =>
551 [ NS_USER
=> CONTENT_MODEL_WIKITEXT
, 123 => CONTENT_MODEL_JSON
, 1234 => 'abcdef' ],
553 $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
556 public function provideGetNamespaceContentModel() {
560 [ NS_USER
, CONTENT_MODEL_WIKITEXT
],
561 [ NS_USER_TALK
, null ],
562 [ NS_SPECIAL
, null ],
564 [ 123, CONTENT_MODEL_JSON
],
571 * @dataProvider provideGetCategoryLinkType
572 * @covers NamespaceInfo::getCategoryLinkType
575 * @param string $expected
577 public function testGetCategoryLinkType( $ns, $expected ) {
578 $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
581 public function provideGetCategoryLinkType() {
586 [ NS_USER_TALK
, 'page' ],
589 [ NS_FILE_TALK
, 'page' ],
591 [ NS_CATEGORY
, 'subcat' ],
592 [ NS_CATEGORY_TALK
, 'page' ],
599 // %} End basic methods
601 /**********************************************************************************************
602 * getSubject/Talk/Associated
607 * @dataProvider provideSubjectTalk
608 * @covers NamespaceInfo::getSubject
609 * @covers NamespaceInfo::getSubjectPage
610 * @covers NamespaceInfo::isMethodValidFor
611 * @covers Title::getSubjectPage
613 * @param int $subject
616 public function testGetSubject( $subject, $talk ) {
617 $obj = $this->newObj();
618 $this->assertSame( $subject, $obj->getSubject( $subject ) );
619 $this->assertSame( $subject, $obj->getSubject( $talk ) );
621 $subjectTitleVal = new TitleValue( $subject, 'A' );
622 $talkTitleVal = new TitleValue( $talk, 'A' );
623 // Object will be the same one passed in if it's a subject, different but equal object if
625 $this->assertSame( $subjectTitleVal, $obj->getSubjectPage( $subjectTitleVal ) );
626 $this->assertEquals( $subjectTitleVal, $obj->getSubjectPage( $talkTitleVal ) );
628 $subjectTitle = Title
::makeTitle( $subject, 'A' );
629 $talkTitle = Title
::makeTitle( $talk, 'A' );
630 $this->assertSame( $subjectTitle, $subjectTitle->getSubjectPage() );
631 $this->assertEquals( $subjectTitle, $talkTitle->getSubjectPage() );
635 * @dataProvider provideSpecialNamespaces
636 * @covers NamespaceInfo::getSubject
637 * @covers NamespaceInfo::getSubjectPage
641 public function testGetSubject_special( $ns ) {
642 $obj = $this->newObj();
643 $this->assertSame( $ns, $obj->getSubject( $ns ) );
645 $title = new TitleValue( $ns, 'A' );
646 $this->assertSame( $title, $obj->getSubjectPage( $title ) );
650 * @dataProvider provideSubjectTalk
651 * @covers NamespaceInfo::getTalk
652 * @covers NamespaceInfo::getTalkPage
653 * @covers NamespaceInfo::isMethodValidFor
654 * @covers Title::getTalkPage
656 * @param int $subject
659 public function testGetTalk( $subject, $talk ) {
660 $obj = $this->newObj();
661 $this->assertSame( $talk, $obj->getTalk( $subject ) );
662 $this->assertSame( $talk, $obj->getTalk( $talk ) );
664 $subjectTitleVal = new TitleValue( $subject, 'A' );
665 $talkTitleVal = new TitleValue( $talk, 'A' );
666 // Object will be the same one passed in if it's a talk, different but equal object if it's
668 $this->assertEquals( $talkTitleVal, $obj->getTalkPage( $subjectTitleVal ) );
669 $this->assertSame( $talkTitleVal, $obj->getTalkPage( $talkTitleVal ) );
671 $subjectTitle = Title
::makeTitle( $subject, 'A' );
672 $talkTitle = Title
::makeTitle( $talk, 'A' );
673 $this->assertEquals( $talkTitle, $subjectTitle->getTalkPage() );
674 $this->assertSame( $talkTitle, $talkTitle->getTalkPage() );
678 * @dataProvider provideSpecialNamespaces
679 * @covers NamespaceInfo::getTalk
680 * @covers NamespaceInfo::isMethodValidFor
684 public function testGetTalk_special( $ns ) {
685 $this->setExpectedException( MWException
::class,
686 "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
687 $this->newObj()->getTalk( $ns );
691 * @dataProvider provideSpecialNamespaces
692 * @covers NamespaceInfo::getAssociated
693 * @covers NamespaceInfo::isMethodValidFor
697 public function testGetAssociated_special( $ns ) {
698 $this->setExpectedException(
700 "NamespaceInfo::getAssociated does not make any sense for given namespace $ns"
702 $this->newObj()->getAssociated( $ns );
705 public static function provideCanHaveTalkPage() {
707 [ new TitleValue( NS_MAIN
, 'Test' ), true ],
708 [ new TitleValue( NS_TALK
, 'Test' ), true ],
709 [ new TitleValue( NS_USER
, 'Test' ), true ],
710 [ new TitleValue( NS_SPECIAL
, 'Test' ), false ],
711 [ new TitleValue( NS_MEDIA
, 'Test' ), false ],
712 [ new TitleValue( NS_MAIN
, '', 'Kittens' ), false ],
713 [ new TitleValue( NS_MAIN
, 'Kittens', '', 'acme' ), false ],
718 * @dataProvider provideCanHaveTalkPage
719 * @covers NamespaceInfo::canHaveTalkPage
721 public function testCanHaveTalkPage( LinkTarget
$t, $expected ) {
722 $actual = $this->newObj()->canHaveTalkPage( $t );
723 $this->assertEquals( $expected, $actual, $t->getDBkey() );
726 public static function provideGetTalkPage_good() {
728 [ new TitleValue( NS_MAIN
, 'Test' ), new TitleValue( NS_TALK
, 'Test' ) ],
729 [ new TitleValue( NS_TALK
, 'Test' ), new TitleValue( NS_TALK
, 'Test' ) ],
730 [ new TitleValue( NS_USER
, 'Test' ), new TitleValue( NS_USER_TALK
, 'Test' ) ],
735 * @dataProvider provideGetTalkPage_good
736 * @covers NamespaceInfo::getTalk
737 * @covers NamespaceInfo::getTalkPage
738 * @covers NamespaceInfo::isMethodValidFor
740 public function testGetTalkPage_good( LinkTarget
$t, LinkTarget
$expected ) {
741 $actual = $this->newObj()->getTalkPage( $t );
742 $this->assertEquals( $expected, $actual, $t->getDBkey() );
745 public static function provideGetTalkPage_bad() {
747 [ new TitleValue( NS_SPECIAL
, 'Test' ) ],
748 [ new TitleValue( NS_MEDIA
, 'Test' ) ],
749 [ new TitleValue( NS_MAIN
, '', 'Kittens' ) ],
750 [ new TitleValue( NS_MAIN
, 'Kittens', '', 'acme' ) ],
755 * @dataProvider provideGetTalkPage_bad
756 * @covers NamespaceInfo::getTalk
757 * @covers NamespaceInfo::getTalkPage
758 * @covers NamespaceInfo::isMethodValidFor
760 public function testGetTalkPage_bad( LinkTarget
$t ) {
761 $this->setExpectedException( MWException
::class );
762 $this->newObj()->getTalkPage( $t );
766 * @dataProvider provideGetTalkPage_bad
767 * @covers NamespaceInfo::getAssociated
768 * @covers NamespaceInfo::getAssociatedPage
769 * @covers NamespaceInfo::isMethodValidFor
771 public function testGetAssociatedPage_bad( LinkTarget
$t ) {
772 $this->setExpectedException( MWException
::class );
773 $this->newObj()->getAssociatedPage( $t );
777 * @dataProvider provideSubjectTalk
778 * @covers NamespaceInfo::getAssociated
779 * @covers NamespaceInfo::getAssociatedPage
780 * @covers Title::getOtherPage
782 * @param int $subject
785 public function testGetAssociated( $subject, $talk ) {
786 $obj = $this->newObj();
787 $this->assertSame( $talk, $obj->getAssociated( $subject ) );
788 $this->assertSame( $subject, $obj->getAssociated( $talk ) );
790 $subjectTitle = new TitleValue( $subject, 'A' );
791 $talkTitle = new TitleValue( $talk, 'A' );
792 // Object will not be the same
793 $this->assertEquals( $talkTitle, $obj->getAssociatedPage( $subjectTitle ) );
794 $this->assertEquals( $subjectTitle, $obj->getAssociatedPage( $talkTitle ) );
796 $subjectTitle = Title
::makeTitle( $subject, 'A' );
797 $talkTitle = Title
::makeTitle( $talk, 'A' );
798 $this->assertEquals( $talkTitle, $subjectTitle->getOtherPage() );
799 $this->assertEquals( $subjectTitle, $talkTitle->getOtherPage() );
802 public static function provideSubjectTalk() {
804 // Format: [ subject, talk ]
805 'Main/talk' => [ NS_MAIN
, NS_TALK
],
806 'User/user talk' => [ NS_USER
, NS_USER_TALK
],
807 'Unknown namespaces also supported' => [ 106, 107 ],
811 public static function provideSpecialNamespaces() {
813 'Special' => [ NS_SPECIAL
],
814 'Media' => [ NS_MEDIA
],
815 'Unknown negative index' => [ -613 ],
819 // %} End getSubject/Talk/Associated
821 /**********************************************************************************************
822 * Canonical namespaces
826 // Default canonical namespaces
828 private function getDefaultNamespaces() {
829 return [ NS_MAIN
=> '' ] + self
::$defaultOptions['CanonicalNamespaceNames'];
833 * @covers NamespaceInfo::getCanonicalNamespaces
835 public function testGetCanonicalNamespaces() {
837 $this->getDefaultNamespaces(),
838 $this->newObj()->getCanonicalNamespaces()
843 * @dataProvider provideGetCanonicalName
844 * @covers NamespaceInfo::getCanonicalName
847 * @param string|bool $expected
849 public function testGetCanonicalName( $index, $expected ) {
850 $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
853 public function provideGetCanonicalName() {
855 'Main' => [ NS_MAIN
, '' ],
856 'Talk' => [ NS_TALK
, 'Talk' ],
857 'With underscore not space' => [ NS_USER_TALK
, 'User_talk' ],
858 'Special' => [ NS_SPECIAL
, 'Special' ],
859 'Nonexistent' => [ 12345, false ],
860 'Nonexistent negative' => [ -12345, false ],
865 * @dataProvider provideGetCanonicalIndex
866 * @covers NamespaceInfo::getCanonicalIndex
868 * @param string $name
869 * @param int|null $expected
871 public function testGetCanonicalIndex( $name, $expected ) {
872 $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
875 public function provideGetCanonicalIndex() {
877 'Main' => [ '', NS_MAIN
],
878 'Talk' => [ 'talk', NS_TALK
],
879 'Not lowercase' => [ 'Talk', null ],
880 'With underscore' => [ 'user_talk', NS_USER_TALK
],
881 'Space is not recognized for underscore' => [ 'user talk', null ],
882 '0' => [ '0', null ],
887 * @covers NamespaceInfo::getValidNamespaces
889 public function testGetValidNamespaces() {
891 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
],
892 $this->newObj()->getValidNamespaces()
896 // %} End default canonical namespaces
898 // No canonical namespace names
902 * @covers NamespaceInfo::getCanonicalNamespaces
904 public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
905 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
907 $this->assertSame( [ NS_MAIN
=> '' ], $obj->getCanonicalNamespaces() );
911 * @covers NamespaceInfo::getCanonicalName
913 public function testGetCanonicalName_NoCanonicalNamespaceNames() {
914 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
916 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
917 $this->assertFalse( $obj->getCanonicalName( NS_TALK
) );
921 * @covers NamespaceInfo::getCanonicalIndex
923 public function testGetCanonicalIndex_NoCanonicalNamespaceNames() {
924 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
926 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
927 $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
931 * @covers NamespaceInfo::getValidNamespaces
933 public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
934 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
936 $this->assertSame( [ NS_MAIN
], $obj->getValidNamespaces() );
939 // %} End no canonical namespace names
941 // Test extension namespaces
943 private function setupExtensionNamespaces() {
944 $this->scopedCallback
= null;
945 $this->scopedCallback
= ExtensionRegistry
::getInstance()->setAttributeForTest(
946 'ExtensionNamespaces',
947 [ NS_MAIN
=> 'No effect', NS_TALK
=> 'No effect', 12345 => 'Extended' ]
952 * @covers NamespaceInfo::getCanonicalNamespaces
954 public function testGetCanonicalNamespaces_ExtensionNamespaces() {
955 $this->setupExtensionNamespaces();
958 $this->getDefaultNamespaces() +
[ 12345 => 'Extended' ],
959 $this->newObj()->getCanonicalNamespaces()
964 * @covers NamespaceInfo::getCanonicalName
966 public function testGetCanonicalName_ExtensionNamespaces() {
967 $this->setupExtensionNamespaces();
968 $obj = $this->newObj();
970 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
971 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK
) );
972 $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
976 * @covers NamespaceInfo::getCanonicalIndex
978 public function testGetCanonicalIndex_ExtensionNamespaces() {
979 $this->setupExtensionNamespaces();
980 $obj = $this->newObj();
982 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
983 $this->assertSame( NS_TALK
, $obj->getCanonicalIndex( 'talk' ) );
984 $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
988 * @covers NamespaceInfo::getValidNamespaces
990 public function testGetValidNamespaces_ExtensionNamespaces() {
991 $this->setupExtensionNamespaces();
994 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 12345 ],
995 $this->newObj()->getValidNamespaces()
999 // %} End extension namespaces
1005 * @return array Expected canonical namespaces
1007 private function setupHookNamespaces() {
1009 function ( &$canonicalNamespaces ) {
1010 $canonicalNamespaces[NS_MAIN
] = 'Main';
1011 unset( $canonicalNamespaces[NS_MEDIA
] );
1012 $canonicalNamespaces[123456] = 'Hooked';
1014 $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
1015 $expected = $this->getDefaultNamespaces();
1016 ( $callback )( $expected );
1021 * @covers NamespaceInfo::getCanonicalNamespaces
1023 public function testGetCanonicalNamespaces_HookNamespaces() {
1024 $expected = $this->setupHookNamespaces();
1026 $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
1030 * @covers NamespaceInfo::getCanonicalName
1032 public function testGetCanonicalName_HookNamespaces() {
1033 $this->setupHookNamespaces();
1034 $obj = $this->newObj();
1036 $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN
) );
1037 $this->assertFalse( $obj->getCanonicalName( NS_MEDIA
) );
1038 $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
1042 * @covers NamespaceInfo::getCanonicalIndex
1044 public function testGetCanonicalIndex_HookNamespaces() {
1045 $this->setupHookNamespaces();
1046 $obj = $this->newObj();
1048 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( 'main' ) );
1049 $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
1050 $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
1054 * @covers NamespaceInfo::getValidNamespaces
1056 public function testGetValidNamespaces_HookNamespaces() {
1057 $this->setupHookNamespaces();
1060 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 123456 ],
1061 $this->newObj()->getValidNamespaces()
1065 // %} End hook namespaces
1071 * @return NamespaceInfo
1073 private function setupExtraNamespaces() {
1074 return $this->newObj( [ 'ExtraNamespaces' =>
1075 [ NS_MAIN
=> 'No effect', NS_TALK
=> 'No effect', 1234567 => 'Extra' ]
1080 * @covers NamespaceInfo::getCanonicalNamespaces
1082 public function testGetCanonicalNamespaces_ExtraNamespaces() {
1084 $this->getDefaultNamespaces() +
[ 1234567 => 'Extra' ],
1085 $this->setupExtraNamespaces()->getCanonicalNamespaces()
1090 * @covers NamespaceInfo::getCanonicalName
1092 public function testGetCanonicalName_ExtraNamespaces() {
1093 $obj = $this->setupExtraNamespaces();
1095 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
1096 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK
) );
1097 $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
1101 * @covers NamespaceInfo::getCanonicalIndex
1103 public function testGetCanonicalIndex_ExtraNamespaces() {
1104 $obj = $this->setupExtraNamespaces();
1106 $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
1107 $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
1108 $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
1112 * @covers NamespaceInfo::getValidNamespaces
1114 public function testGetValidNamespaces_ExtraNamespaces() {
1116 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
, 1234567 ],
1117 $this->setupExtraNamespaces()->getValidNamespaces()
1121 // %} End extra namespaces
1123 // Canonical namespace caching
1127 * @covers NamespaceInfo::getCanonicalNamespaces
1129 public function testGetCanonicalNamespaces_caching() {
1130 $obj = $this->newObj();
1132 // This should cache the values
1133 $obj->getCanonicalNamespaces();
1135 // Now try to alter them through nefarious means
1136 $this->setupExtensionNamespaces();
1137 $this->setupHookNamespaces();
1139 // Should have no effect
1140 $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
1144 * @covers NamespaceInfo::getCanonicalName
1146 public function testGetCanonicalName_caching() {
1147 $obj = $this->newObj();
1149 // This should cache the values
1150 $obj->getCanonicalName( NS_MAIN
);
1152 // Now try to alter them through nefarious means
1153 $this->setupExtensionNamespaces();
1154 $this->setupHookNamespaces();
1156 // Should have no effect
1157 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN
) );
1158 $this->assertSame( 'Media', $obj->getCanonicalName( NS_MEDIA
) );
1159 $this->assertFalse( $obj->getCanonicalName( 12345 ) );
1160 $this->assertFalse( $obj->getCanonicalName( 123456 ) );
1164 * @covers NamespaceInfo::getCanonicalIndex
1166 public function testGetCanonicalIndex_caching() {
1167 $obj = $this->newObj();
1169 // This should cache the values
1170 $obj->getCanonicalIndex( '' );
1172 // Now try to alter them through nefarious means
1173 $this->setupExtensionNamespaces();
1174 $this->setupHookNamespaces();
1176 // Should have no effect
1177 $this->assertSame( NS_MAIN
, $obj->getCanonicalIndex( '' ) );
1178 $this->assertSame( NS_MEDIA
, $obj->getCanonicalIndex( 'media' ) );
1179 $this->assertNull( $obj->getCanonicalIndex( 'extended' ) );
1180 $this->assertNull( $obj->getCanonicalIndex( 'hooked' ) );
1184 * @covers NamespaceInfo::getValidNamespaces
1186 public function testGetValidNamespaces_caching() {
1187 $obj = $this->newObj();
1189 // This should cache the values
1190 $obj->getValidNamespaces();
1192 // Now try to alter through nefarious means
1193 $this->setupExtensionNamespaces();
1194 $this->setupHookNamespaces();
1196 // Should have no effect
1198 [ NS_MAIN
, NS_TALK
, NS_USER
, NS_USER_TALK
],
1199 $obj->getValidNamespaces()
1203 // %} End canonical namespace caching
1209 * @dataProvider provideGetValidNamespaces_misc
1210 * @covers NamespaceInfo::getValidNamespaces
1212 * @param array $namespaces List of namespace indices to return from getCanonicalNamespaces()
1213 * (list is overwritten by a hook, so NS_MAIN doesn't have to be present)
1214 * @param array $expected
1216 public function testGetValidNamespaces_misc( array $namespaces, array $expected ) {
1217 // Each namespace's name is just its index
1218 $this->setTemporaryHook( 'CanonicalNamespaces',
1219 function ( &$canonicalNamespaces ) use ( $namespaces ) {
1220 $canonicalNamespaces = array_combine( $namespaces, $namespaces );
1223 $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
1226 public function provideGetValidNamespaces_misc() {
1228 'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
1229 'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
1230 'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
1234 // %} End miscellaneous
1235 // %} End canonical namespaces
1237 /**********************************************************************************************
1238 * Restriction levels
1243 * This mock user can only have isAllowed() called on it.
1245 * @param array $groups Groups for the mock user to have
1248 private function getMockUser( array $groups = [] ) : User
{
1251 $mock = $this->createMock( User
::class );
1252 $mock->method( 'isAllowed' )->will( $this->returnCallback(
1253 function ( $action ) use ( $groups ) {
1254 global $wgGroupPermissions, $wgRevokePermissions;
1255 if ( $action == '' ) {
1258 foreach ( $wgRevokePermissions as $group => $rights ) {
1259 if ( !in_array( $group, $groups ) ) {
1262 if ( isset( $rights[$action] ) && $rights[$action] ) {
1266 foreach ( $wgGroupPermissions as $group => $rights ) {
1267 if ( !in_array( $group, $groups ) ) {
1270 if ( isset( $rights[$action] ) && $rights[$action] ) {
1277 $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
1282 * @dataProvider provideGetRestrictionLevels
1283 * @covers NamespaceInfo::getRestrictionLevels
1285 * @param array $expected
1287 * @param User|null $user
1289 public function testGetRestrictionLevels( array $expected, $ns, User
$user = null ) {
1290 $this->setMwGlobals( [
1291 'wgGroupPermissions' => [
1292 '*' => [ 'edit' => true ],
1293 'autoconfirmed' => [ 'editsemiprotected' => true ],
1295 'editsemiprotected' => true,
1296 'editprotected' => true,
1298 'privileged' => [ 'privileged' => true ],
1300 'wgRevokePermissions' => [
1301 'noeditsemiprotected' => [ 'editsemiprotected' => true ],
1304 $obj = $this->newObj( [
1305 'NamespaceProtection' => [
1306 NS_MAIN
=> 'autoconfirmed',
1308 101 => [ 'editsemiprotected', 'privileged' ],
1311 $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
1314 public function provideGetRestrictionLevels() {
1316 'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK
],
1317 'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN
],
1318 'Restricted to sysop' => [ [ '' ], NS_USER
],
1319 'Restricted to someone in two groups' => [ [ '', 'sysop' ], 101 ],
1320 'No special permissions' => [ [ '' ], NS_TALK
, $this->getMockUser() ],
1321 'autoconfirmed' => [
1322 [ '', 'autoconfirmed' ],
1324 $this->getMockUser( [ 'autoconfirmed' ] )
1326 'autoconfirmed revoked' => [
1329 $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
1332 [ '', 'autoconfirmed', 'sysop' ],
1334 $this->getMockUser( [ 'sysop' ] )
1336 'sysop with autoconfirmed revoked (a bit silly)' => [
1339 $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
1344 // %} End restriction levels
1348 * For really cool vim folding this needs to be at the end:
1349 * vim: foldmarker=%{,%} foldmethod=marker