* @file
*/
-use MediaWiki\MediaWikiServices;
+use MediaWiki\Config\ServiceOptions;
+use MediaWiki\Linker\LinkTarget;
class NamespaceInfoTest extends MediaWikiTestCase {
+ /**********************************************************************************************
+ * Shared code
+ * %{
+ */
+ private $scopedCallback;
- /** @var NamespaceInfo */
- private $obj;
-
- protected function setUp() {
+ public function setUp() {
parent::setUp();
- $this->setMwGlobals( [
- 'wgContentNamespaces' => [ NS_MAIN ],
- 'wgNamespacesWithSubpages' => [
- NS_TALK => true,
- NS_USER => true,
- NS_USER_TALK => true,
- ],
- 'wgCapitalLinks' => true,
- 'wgCapitalLinkOverrides' => [],
- 'wgNonincludableNamespaces' => [],
- ] );
+ // Boo, there's still some global state in the class :(
+ global $wgHooks;
+ $hooks = $wgHooks;
+ unset( $hooks['CanonicalNamespaces'] );
+ $this->setMwGlobals( 'wgHooks', $hooks );
- $this->obj = MediaWikiServices::getInstance()->getNamespaceInfo();
+ $this->scopedCallback =
+ ExtensionRegistry::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
}
- /**
- * @todo Write more texts, handle $wgAllowImageMoving setting
- * @covers NamespaceInfo::isMovable
- */
- public function testIsMovable() {
- $this->assertFalse( $this->obj->isMovable( NS_SPECIAL ) );
- }
-
- private function assertIsSubject( $ns ) {
- $this->assertTrue( $this->obj->isSubject( $ns ) );
- }
+ public function tearDown() {
+ $this->scopedCallback = null;
- private function assertIsNotSubject( $ns ) {
- $this->assertFalse( $this->obj->isSubject( $ns ) );
+ parent::tearDown();
}
/**
- * Please make sure to change testIsTalk() if you change the assertions below
- * @covers NamespaceInfo::isSubject
+ * TODO Make this a const once HHVM support is dropped (T192166)
*/
- public function testIsSubject() {
- // Special namespaces
- $this->assertIsSubject( NS_MEDIA );
- $this->assertIsSubject( NS_SPECIAL );
-
- // Subject pages
- $this->assertIsSubject( NS_MAIN );
- $this->assertIsSubject( NS_USER );
- $this->assertIsSubject( 100 ); # user defined
-
- // Talk pages
- $this->assertIsNotSubject( NS_TALK );
- $this->assertIsNotSubject( NS_USER_TALK );
- $this->assertIsNotSubject( 101 ); # user defined
+ private static $defaultOptions = [
+ 'AllowImageMoving' => true,
+ 'CanonicalNamespaceNames' => [
+ NS_TALK => 'Talk',
+ NS_USER => 'User',
+ NS_USER_TALK => 'User_talk',
+ NS_SPECIAL => 'Special',
+ NS_MEDIA => 'Media',
+ ],
+ 'CapitalLinkOverrides' => [],
+ 'CapitalLinks' => true,
+ 'ContentNamespaces' => [ NS_MAIN ],
+ 'ExtraNamespaces' => [],
+ 'ExtraSignatureNamespaces' => [],
+ 'NamespaceContentModels' => [],
+ 'NamespaceProtection' => [],
+ 'NamespacesWithSubpages' => [
+ NS_TALK => true,
+ NS_USER => true,
+ NS_USER_TALK => true,
+ ],
+ 'NonincludableNamespaces' => [],
+ 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
+ ];
+
+ private function newObj( array $options = [] ) : NamespaceInfo {
+ return new NamespaceInfo( new ServiceOptions( NamespaceInfo::$constructorOptions,
+ $options, self::$defaultOptions ) );
}
- private function assertIsTalk( $ns ) {
- $this->assertTrue( $this->obj->isTalk( $ns ) );
- }
+ // %} End shared code
- private function assertIsNotTalk( $ns ) {
- $this->assertFalse( $this->obj->isTalk( $ns ) );
- }
+ /**********************************************************************************************
+ * Basic methods
+ * %{
+ */
/**
- * Reverse of testIsSubject().
- * Please update testIsSubject() if you change assertions below
- * @covers NamespaceInfo::isTalk
+ * @covers NamespaceInfo::__construct
+ * @dataProvider provideConstructor
+ * @param ServiceOptions $options
+ * @param string|null $expectedExceptionText
*/
- public function testIsTalk() {
- // Special namespaces
- $this->assertIsNotTalk( NS_MEDIA );
- $this->assertIsNotTalk( NS_SPECIAL );
-
- // Subject pages
- $this->assertIsNotTalk( NS_MAIN );
- $this->assertIsNotTalk( NS_USER );
- $this->assertIsNotTalk( 100 ); # user defined
+ public function testConstructor( ServiceOptions $options, $expectedExceptionText = null ) {
+ if ( $expectedExceptionText !== null ) {
+ $this->setExpectedException( \Wikimedia\Assert\PreconditionException::class,
+ $expectedExceptionText );
+ }
+ new NamespaceInfo( $options );
+ $this->assertTrue( true );
+ }
- // Talk pages
- $this->assertIsTalk( NS_TALK );
- $this->assertIsTalk( NS_USER_TALK );
- $this->assertIsTalk( 101 ); # user defined
+ public function provideConstructor() {
+ return [
+ [ new ServiceOptions( NamespaceInfo::$constructorOptions, self::$defaultOptions ) ],
+ [ new ServiceOptions( [], [] ), 'Required options missing: ' ],
+ [ new ServiceOptions(
+ array_merge( NamespaceInfo::$constructorOptions, [ 'invalid' ] ),
+ self::$defaultOptions,
+ [ 'invalid' => '' ]
+ ), 'Unsupported options passed: invalid' ],
+ ];
}
/**
- * @covers NamespaceInfo::getSubject
+ * @dataProvider provideIsMovable
+ * @covers NamespaceInfo::isMovable
+ *
+ * @param bool $expected
+ * @param int $ns
+ * @param bool $allowImageMoving
*/
- public function testGetSubject() {
- // Special namespaces are their own subjects
- $this->assertEquals( NS_MEDIA, $this->obj->getSubject( NS_MEDIA ) );
- $this->assertEquals( NS_SPECIAL, $this->obj->getSubject( NS_SPECIAL ) );
-
- $this->assertEquals( NS_MAIN, $this->obj->getSubject( NS_TALK ) );
- $this->assertEquals( NS_USER, $this->obj->getSubject( NS_USER_TALK ) );
+ public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
+ $obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
+ $this->assertSame( $expected, $obj->isMovable( $ns ) );
}
- /**
- * Regular getTalk() calls
- * Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
- * the function testGetTalkExceptions()
- * @covers NamespaceInfo::getTalk
- */
- public function testGetTalk() {
- $this->assertEquals( NS_TALK, $this->obj->getTalk( NS_MAIN ) );
- $this->assertEquals( NS_TALK, $this->obj->getTalk( NS_TALK ) );
- $this->assertEquals( NS_USER_TALK, $this->obj->getTalk( NS_USER ) );
- $this->assertEquals( NS_USER_TALK, $this->obj->getTalk( NS_USER_TALK ) );
+ public function provideIsMovable() {
+ return [
+ 'Main' => [ true, NS_MAIN ],
+ 'Talk' => [ true, NS_TALK ],
+ 'Special' => [ false, NS_SPECIAL ],
+ 'Nonexistent even namespace' => [ true, 1234 ],
+ 'Nonexistent odd namespace' => [ true, 12345 ],
+
+ 'Media with image moving' => [ false, NS_MEDIA, true ],
+ 'Media with no image moving' => [ false, NS_MEDIA, false ],
+ 'File with image moving' => [ true, NS_FILE, true ],
+ 'File with no image moving' => [ false, NS_FILE, false ],
+ ];
}
/**
- * Exceptions with getTalk()
- * NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
- * @expectedException MWException
- * @covers NamespaceInfo::getTalk
+ * @param int $ns
+ * @param bool $expected
+ * @dataProvider provideIsSubject
+ * @covers NamespaceInfo::isSubject
*/
- public function testGetTalkExceptionsForNsMedia() {
- $this->assertNull( $this->obj->getTalk( NS_MEDIA ) );
+ public function testIsSubject( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
}
/**
- * Exceptions with getTalk()
- * NS_SPECIAL does not have talk pages. MediaWiki raise an exception for them.
- * @expectedException MWException
- * @covers NamespaceInfo::getTalk
+ * @param int $ns
+ * @param bool $expected
+ * @dataProvider provideIsSubject
+ * @covers NamespaceInfo::isTalk
*/
- public function testGetTalkExceptionsForNsSpecial() {
- $this->assertNull( $this->obj->getTalk( NS_SPECIAL ) );
+ public function testIsTalk( $ns, $expected ) {
+ $this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
}
- /**
- * Regular getAssociated() calls
- * Namespaces without an associated page (NS_MEDIA, NS_SPECIAL) are tested in
- * the function testGetAssociatedExceptions()
- * @covers NamespaceInfo::getAssociated
- */
- public function testGetAssociated() {
- $this->assertEquals( NS_TALK, $this->obj->getAssociated( NS_MAIN ) );
- $this->assertEquals( NS_MAIN, $this->obj->getAssociated( NS_TALK ) );
+ public function provideIsSubject() {
+ return [
+ // Special namespaces
+ [ NS_MEDIA, true ],
+ [ NS_SPECIAL, true ],
+
+ // Subject pages
+ [ NS_MAIN, true ],
+ [ NS_USER, true ],
+ [ 100, true ],
+
+ // Talk pages
+ [ NS_TALK, false ],
+ [ NS_USER_TALK, false ],
+ [ 101, false ],
+ ];
}
- # ## Exceptions with getAssociated()
- # ## NS_MEDIA and NS_SPECIAL do not have talk pages. MediaWiki raises
- # ## an exception for them.
/**
- * @expectedException MWException
- * @covers NamespaceInfo::getAssociated
+ * @covers NamespaceInfo::exists
+ * @dataProvider provideExists
+ * @param int $ns
+ * @param bool $expected
*/
- public function testGetAssociatedExceptionsForNsMedia() {
- $this->assertNull( $this->obj->getAssociated( NS_MEDIA ) );
+ public function testExists( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
}
- /**
- * @expectedException MWException
- * @covers NamespaceInfo::getAssociated
- */
- public function testGetAssociatedExceptionsForNsSpecial() {
- $this->assertNull( $this->obj->getAssociated( NS_SPECIAL ) );
+ public function provideExists() {
+ return [
+ 'Main' => [ NS_MAIN, true ],
+ 'Talk' => [ NS_TALK, true ],
+ 'Media' => [ NS_MEDIA, true ],
+ 'Special' => [ NS_SPECIAL, true ],
+ 'Nonexistent' => [ 12345, false ],
+ 'Negative nonexistent' => [ -12345, false ],
+ ];
}
/**
* Note if we add a namespace registration system with keys like 'MAIN'
- * we should add tests here for equivilance on things like 'MAIN' == 0
+ * we should add tests here for equivalence on things like 'MAIN' == 0
* and 'MAIN' == NS_MAIN.
* @covers NamespaceInfo::equals
*/
public function testEquals() {
- $this->assertTrue( $this->obj->equals( NS_MAIN, NS_MAIN ) );
- $this->assertTrue( $this->obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
- $this->assertTrue( $this->obj->equals( NS_USER, NS_USER ) );
- $this->assertTrue( $this->obj->equals( NS_USER, 2 ) );
- $this->assertTrue( $this->obj->equals( NS_USER_TALK, NS_USER_TALK ) );
- $this->assertTrue( $this->obj->equals( NS_SPECIAL, NS_SPECIAL ) );
- $this->assertFalse( $this->obj->equals( NS_MAIN, NS_TALK ) );
- $this->assertFalse( $this->obj->equals( NS_USER, NS_USER_TALK ) );
- $this->assertFalse( $this->obj->equals( NS_PROJECT, NS_TEMPLATE ) );
+ $obj = $this->newObj();
+ $this->assertTrue( $obj->equals( NS_MAIN, NS_MAIN ) );
+ $this->assertTrue( $obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
+ $this->assertTrue( $obj->equals( NS_USER, NS_USER ) );
+ $this->assertTrue( $obj->equals( NS_USER, 2 ) );
+ $this->assertTrue( $obj->equals( NS_USER_TALK, NS_USER_TALK ) );
+ $this->assertTrue( $obj->equals( NS_SPECIAL, NS_SPECIAL ) );
+ $this->assertFalse( $obj->equals( NS_MAIN, NS_TALK ) );
+ $this->assertFalse( $obj->equals( NS_USER, NS_USER_TALK ) );
+ $this->assertFalse( $obj->equals( NS_PROJECT, NS_TEMPLATE ) );
}
/**
+ * @param int $ns1
+ * @param int $ns2
+ * @param bool $expected
+ * @dataProvider provideSubjectEquals
* @covers NamespaceInfo::subjectEquals
*/
- public function testSubjectEquals() {
- $this->assertSameSubject( NS_MAIN, NS_MAIN );
- $this->assertSameSubject( NS_MAIN, 0 ); // In case we make NS_MAIN 'MAIN'
- $this->assertSameSubject( NS_USER, NS_USER );
- $this->assertSameSubject( NS_USER, 2 );
- $this->assertSameSubject( NS_USER_TALK, NS_USER_TALK );
- $this->assertSameSubject( NS_SPECIAL, NS_SPECIAL );
- $this->assertSameSubject( NS_MAIN, NS_TALK );
- $this->assertSameSubject( NS_USER, NS_USER_TALK );
+ public function testSubjectEquals( $ns1, $ns2, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
+ }
- $this->assertDifferentSubject( NS_PROJECT, NS_TEMPLATE );
- $this->assertDifferentSubject( NS_SPECIAL, NS_MAIN );
+ public function provideSubjectEquals() {
+ return [
+ [ NS_MAIN, NS_MAIN, true ],
+ // In case we make NS_MAIN 'MAIN'
+ [ NS_MAIN, 0, true ],
+ [ NS_USER, NS_USER, true ],
+ [ NS_USER, 2, true ],
+ [ NS_USER_TALK, NS_USER_TALK, true ],
+ [ NS_SPECIAL, NS_SPECIAL, true ],
+ [ NS_MAIN, NS_TALK, true ],
+ [ NS_USER, NS_USER_TALK, true ],
+
+ [ NS_PROJECT, NS_TEMPLATE, false ],
+ [ NS_SPECIAL, NS_MAIN, false ],
+ [ NS_MEDIA, NS_SPECIAL, false ],
+ [ NS_SPECIAL, NS_MEDIA, false ],
+ ];
}
/**
- * @covers NamespaceInfo::subjectEquals
+ * @dataProvider provideHasTalkNamespace
+ * @covers NamespaceInfo::hasTalkNamespace
+ *
+ * @param int $ns
+ * @param bool $expected
*/
- public function testSpecialAndMediaAreDifferentSubjects() {
- $this->assertDifferentSubject(
- NS_MEDIA, NS_SPECIAL,
- "NS_MEDIA and NS_SPECIAL are different subject namespaces"
- );
- $this->assertDifferentSubject(
- NS_SPECIAL, NS_MEDIA,
- "NS_SPECIAL and NS_MEDIA are different subject namespaces"
- );
+ public function testHasTalkNamespace( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
}
public function provideHasTalkNamespace() {
}
/**
- * @dataProvider provideHasTalkNamespace
- * @covers NamespaceInfo::hasTalkNamespace
+ * @param int $ns
+ * @param bool $expected
+ * @param array $contentNamespaces
+ * @covers NamespaceInfo::isContent
+ * @dataProvider provideIsContent
+ */
+ public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN ] ) {
+ $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
+ $this->assertSame( $expected, $obj->isContent( $ns ) );
+ }
+
+ public function provideIsContent() {
+ return [
+ [ NS_MAIN, true ],
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
+ [ NS_TALK, false ],
+ [ NS_USER, false ],
+ [ NS_CATEGORY, false ],
+ [ 100, false ],
+ [ 100, true, [ NS_MAIN, 100, 252 ] ],
+ [ 252, true, [ NS_MAIN, 100, 252 ] ],
+ [ NS_MAIN, true, [ NS_MAIN, 100, 252 ] ],
+ // NS_MAIN is always content
+ [ NS_MAIN, true, [] ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideWantSignatures
+ * @covers NamespaceInfo::wantSignatures
*
* @param int $index
* @param bool $expected
*/
- public function testHasTalkNamespace( $index, $expected ) {
- $actual = $this->obj->hasTalkNamespace( $index );
- $this->assertSame( $actual, $expected, "NS $index" );
+ public function testWantSignatures( $index, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
+ }
+
+ public function provideWantSignatures() {
+ return [
+ 'Main' => [ NS_MAIN, false ],
+ 'Talk' => [ NS_TALK, true ],
+ 'User' => [ NS_USER, false ],
+ 'User talk' => [ NS_USER_TALK, true ],
+ 'Special' => [ NS_SPECIAL, false ],
+ 'Media' => [ NS_MEDIA, false ],
+ 'Nonexistent talk' => [ 12345, true ],
+ 'Nonexistent subject' => [ 123456, false ],
+ 'Nonexistent negative odd' => [ -12345, false ],
+ ];
}
/**
- * @dataProvider provideHasTalkNamespace
- * @covers MWNamespace::canTalk
+ * @dataProvider provideWantSignatures_ExtraSignatureNamespaces
+ * @covers NamespaceInfo::wantSignatures
*
* @param int $index
- * @param bool $expected
+ * @param int $expected
*/
- public function testCanTalk( $index, $expected ) {
- $this->hideDeprecated( 'MWNamespace::canTalk' );
- $actual = MWNamespace::canTalk( $index );
- $this->assertSame( $actual, $expected, "NS $index" );
+ public function testWantSignatures_ExtraSignatureNamespaces( $index, $expected ) {
+ $obj = $this->newObj( [ 'ExtraSignatureNamespaces' =>
+ [ NS_MAIN, NS_USER, NS_SPECIAL, NS_MEDIA, 123456, -12345 ] ] );
+ $this->assertSame( $expected, $obj->wantSignatures( $index ) );
}
- private function assertIsContent( $ns ) {
- $this->assertTrue( $this->obj->isContent( $ns ) );
+ public function provideWantSignatures_ExtraSignatureNamespaces() {
+ $ret = array_map(
+ function ( $arr ) {
+ // We've added all these as extra signature namespaces, so expect true
+ return [ $arr[0], true ];
+ },
+ self::provideWantSignatures()
+ );
+
+ // Add one more that's false
+ $ret['Another nonexistent subject'] = [ 12345678, false ];
+ return $ret;
+ }
+
+ /**
+ * @param int $ns
+ * @param bool $expected
+ * @covers NamespaceInfo::isWatchable
+ * @dataProvider provideIsWatchable
+ */
+ public function testIsWatchable( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
}
- private function assertIsNotContent( $ns ) {
- $this->assertFalse( $this->obj->isContent( $ns ) );
+ public function provideIsWatchable() {
+ return [
+ // Specials namespaces are not watchable
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
+
+ // Core defined namespaces are watchables
+ [ NS_MAIN, true ],
+ [ NS_TALK, true ],
+
+ // Additional, user defined namespaces are watchables
+ [ 100, true ],
+ [ 101, true ],
+ ];
}
/**
- * @covers NamespaceInfo::isContent
+ * @param int $ns
+ * @param int $expected
+ * @param array|null $namespacesWithSubpages To pass to constructor
+ * @covers NamespaceInfo::hasSubpages
+ * @dataProvider provideHasSubpages
*/
- public function testIsContent() {
- // NS_MAIN is a content namespace per DefaultSettings.php
- // and per function definition.
+ public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
+ $obj = $this->newObj( $namespacesWithSubpages
+ ? [ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
+ : [] );
+ $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
+ }
- $this->assertIsContent( NS_MAIN );
+ public function provideHasSubpages() {
+ return [
+ // Special namespaces:
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
- // Other namespaces which are not expected to be content
+ // Namespaces without subpages
+ [ NS_MAIN, false ],
+ [ NS_MAIN, true, [ NS_MAIN => true ] ],
+ [ NS_MAIN, false, [ NS_MAIN => false ] ],
- $this->assertIsNotContent( NS_MEDIA );
- $this->assertIsNotContent( NS_SPECIAL );
- $this->assertIsNotContent( NS_TALK );
- $this->assertIsNotContent( NS_USER );
- $this->assertIsNotContent( NS_CATEGORY );
- $this->assertIsNotContent( 100 );
+ // Some namespaces with subpages
+ [ NS_TALK, true ],
+ [ NS_USER, true ],
+ [ NS_USER_TALK, true ],
+ ];
}
/**
- * Similar to testIsContent() but alters the $wgContentNamespaces
- * global variable.
- * @covers NamespaceInfo::isContent
+ * @param mixed $contentNamespaces To pass to constructor
+ * @param array $expected
+ * @dataProvider provideGetContentNamespaces
+ * @covers NamespaceInfo::getContentNamespaces
*/
- public function testIsContentAdvanced() {
- global $wgContentNamespaces;
+ public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
+ $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
+ $this->assertSame( $expected, $obj->getContentNamespaces() );
+ }
- // Test that user defined namespace #252 is not content
- $this->assertIsNotContent( 252 );
+ public function provideGetContentNamespaces() {
+ return [
+ // Non-array
+ [ '', [ NS_MAIN ] ],
+ [ false, [ NS_MAIN ] ],
+ [ null, [ NS_MAIN ] ],
+ [ 5, [ NS_MAIN ] ],
- // Bless namespace # 252 as a content namespace
- $wgContentNamespaces[] = 252;
+ // Empty array
+ [ [], [ NS_MAIN ] ],
- $this->assertIsContent( 252 );
+ // NS_MAIN is forced to be content even if unwanted
+ [ [ NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
- // Makes sure NS_MAIN was not impacted
- $this->assertIsContent( NS_MAIN );
+ // In other cases, return as-is
+ [ [ NS_MAIN ], [ NS_MAIN ] ],
+ [ [ NS_MAIN, NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
+ ];
}
- private function assertIsWatchable( $ns ) {
- $this->assertTrue( $this->obj->isWatchable( $ns ) );
+ /**
+ * @covers NamespaceInfo::getSubjectNamespaces
+ */
+ public function testGetSubjectNamespaces() {
+ $subjectsNS = $this->newObj()->getSubjectNamespaces();
+ $this->assertContains( NS_MAIN, $subjectsNS,
+ "Talk namespaces should have NS_MAIN" );
+ $this->assertNotContains( NS_TALK, $subjectsNS,
+ "Talk namespaces should have NS_TALK" );
+
+ $this->assertNotContains( NS_MEDIA, $subjectsNS,
+ "Talk namespaces should not have NS_MEDIA" );
+ $this->assertNotContains( NS_SPECIAL, $subjectsNS,
+ "Talk namespaces should not have NS_SPECIAL" );
}
- private function assertIsNotWatchable( $ns ) {
- $this->assertFalse( $this->obj->isWatchable( $ns ) );
+ /**
+ * @covers NamespaceInfo::getTalkNamespaces
+ */
+ public function testGetTalkNamespaces() {
+ $talkNS = $this->newObj()->getTalkNamespaces();
+ $this->assertContains( NS_TALK, $talkNS,
+ "Subject namespaces should have NS_TALK" );
+ $this->assertNotContains( NS_MAIN, $talkNS,
+ "Subject namespaces should not have NS_MAIN" );
+
+ $this->assertNotContains( NS_MEDIA, $talkNS,
+ "Subject namespaces should not have NS_MEDIA" );
+ $this->assertNotContains( NS_SPECIAL, $talkNS,
+ "Subject namespaces should not have NS_SPECIAL" );
}
/**
- * @covers NamespaceInfo::isWatchable
+ * @param int $ns
+ * @param bool $expected
+ * @param bool $capitalLinks To pass to constructor
+ * @param array $capitalLinkOverrides To pass to constructor
+ * @dataProvider provideIsCapitalized
+ * @covers NamespaceInfo::isCapitalized
+ */
+ public function testIsCapitalized(
+ $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
+ ) {
+ $obj = $this->newObj( [
+ 'CapitalLinks' => $capitalLinks,
+ 'CapitalLinkOverrides' => $capitalLinkOverrides,
+ ] );
+ $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
+ }
+
+ public function provideIsCapitalized() {
+ return [
+ // Test default settings
+ [ NS_PROJECT, true ],
+ [ NS_PROJECT_TALK, true ],
+ [ NS_MEDIA, true ],
+ [ NS_FILE, true ],
+
+ // Always capitalized no matter what
+ [ NS_SPECIAL, true, false ],
+ [ NS_USER, true, false ],
+ [ NS_MEDIAWIKI, true, false ],
+
+ // Even with an override too
+ [ NS_SPECIAL, true, false, [ NS_SPECIAL => false ] ],
+ [ NS_USER, true, false, [ NS_USER => false ] ],
+ [ NS_MEDIAWIKI, true, false, [ NS_MEDIAWIKI => false ] ],
+
+ // Overrides work for other namespaces
+ [ NS_PROJECT, false, true, [ NS_PROJECT => false ] ],
+ [ NS_PROJECT, true, false, [ NS_PROJECT => true ] ],
+
+ // NS_MEDIA is treated like NS_FILE, and ignores NS_MEDIA overrides
+ [ NS_MEDIA, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
+ [ NS_MEDIA, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
+ [ NS_FILE, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
+ [ NS_FILE, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
+ ];
+ }
+
+ /**
+ * @covers NamespaceInfo::hasGenderDistinction
*/
- public function testIsWatchable() {
- // Specials namespaces are not watchable
- $this->assertIsNotWatchable( NS_MEDIA );
- $this->assertIsNotWatchable( NS_SPECIAL );
+ public function testHasGenderDistinction() {
+ $obj = $this->newObj();
- // Core defined namespaces are watchables
- $this->assertIsWatchable( NS_MAIN );
- $this->assertIsWatchable( NS_TALK );
+ // Namespaces with gender distinctions
+ $this->assertTrue( $obj->hasGenderDistinction( NS_USER ) );
+ $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK ) );
- // Additional, user defined namespaces are watchables
- $this->assertIsWatchable( 100 );
- $this->assertIsWatchable( 101 );
+ // Other ones, "genderless"
+ $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_TALK ) );
}
- private function assertHasSubpages( $ns ) {
- $this->assertTrue( $this->obj->hasSubpages( $ns ) );
+ /**
+ * @covers NamespaceInfo::isNonincludable
+ */
+ public function testIsNonincludable() {
+ $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER ] ] );
+ $this->assertTrue( $obj->isNonincludable( NS_USER ) );
+ $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE ) );
}
- private function assertHasNotSubpages( $ns ) {
- $this->assertFalse( $this->obj->hasSubpages( $ns ) );
+ /**
+ * @dataProvider provideGetNamespaceContentModel
+ * @covers NamespaceInfo::getNamespaceContentModel
+ *
+ * @param int $ns
+ * @param string $expected
+ */
+ public function testGetNamespaceContentModel( $ns, $expected ) {
+ $obj = $this->newObj( [ 'NamespaceContentModels' =>
+ [ NS_USER => CONTENT_MODEL_WIKITEXT, 123 => CONTENT_MODEL_JSON, 1234 => 'abcdef' ],
+ ] );
+ $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
+ }
+
+ public function provideGetNamespaceContentModel() {
+ return [
+ [ NS_MAIN, null ],
+ [ NS_TALK, null ],
+ [ NS_USER, CONTENT_MODEL_WIKITEXT ],
+ [ NS_USER_TALK, null ],
+ [ NS_SPECIAL, null ],
+ [ 122, null ],
+ [ 123, CONTENT_MODEL_JSON ],
+ [ 1234, 'abcdef' ],
+ [ 1235, null ],
+ ];
}
/**
- * @covers NamespaceInfo::hasSubpages
+ * @dataProvider provideGetCategoryLinkType
+ * @covers NamespaceInfo::getCategoryLinkType
+ *
+ * @param int $ns
+ * @param string $expected
*/
- public function testHasSubpages() {
- global $wgNamespacesWithSubpages;
+ public function testGetCategoryLinkType( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
+ }
+
+ public function provideGetCategoryLinkType() {
+ return [
+ [ NS_MAIN, 'page' ],
+ [ NS_TALK, 'page' ],
+ [ NS_USER, 'page' ],
+ [ NS_USER_TALK, 'page' ],
+
+ [ NS_FILE, 'file' ],
+ [ NS_FILE_TALK, 'page' ],
+
+ [ NS_CATEGORY, 'subcat' ],
+ [ NS_CATEGORY_TALK, 'page' ],
+
+ [ 100, 'page' ],
+ [ 101, 'page' ],
+ ];
+ }
- // Special namespaces:
- $this->assertHasNotSubpages( NS_MEDIA );
- $this->assertHasNotSubpages( NS_SPECIAL );
+ // %} End basic methods
- // Namespaces without subpages
- $this->assertHasNotSubpages( NS_MAIN );
+ /**********************************************************************************************
+ * getSubject/Talk/Associated
+ * %{
+ */
- $wgNamespacesWithSubpages[NS_MAIN] = true;
- $this->assertHasSubpages( NS_MAIN );
+ /**
+ * @dataProvider provideSubjectTalk
+ * @covers NamespaceInfo::getSubject
+ * @covers NamespaceInfo::getSubjectPage
+ * @covers NamespaceInfo::isMethodValidFor
+ * @covers Title::getSubjectPage
+ *
+ * @param int $subject
+ * @param int $talk
+ */
+ public function testGetSubject( $subject, $talk ) {
+ $obj = $this->newObj();
+ $this->assertSame( $subject, $obj->getSubject( $subject ) );
+ $this->assertSame( $subject, $obj->getSubject( $talk ) );
+
+ $subjectTitleVal = new TitleValue( $subject, 'A' );
+ $talkTitleVal = new TitleValue( $talk, 'A' );
+ // Object will be the same one passed in if it's a subject, different but equal object if
+ // it's talk
+ $this->assertSame( $subjectTitleVal, $obj->getSubjectPage( $subjectTitleVal ) );
+ $this->assertEquals( $subjectTitleVal, $obj->getSubjectPage( $talkTitleVal ) );
+
+ $subjectTitle = Title::makeTitle( $subject, 'A' );
+ $talkTitle = Title::makeTitle( $talk, 'A' );
+ $this->assertSame( $subjectTitle, $subjectTitle->getSubjectPage() );
+ $this->assertEquals( $subjectTitle, $talkTitle->getSubjectPage() );
+ }
- $wgNamespacesWithSubpages[NS_MAIN] = false;
- $this->assertHasNotSubpages( NS_MAIN );
+ /**
+ * @dataProvider provideSpecialNamespaces
+ * @covers NamespaceInfo::getSubject
+ * @covers NamespaceInfo::getSubjectPage
+ *
+ * @param int $ns
+ */
+ public function testGetSubject_special( $ns ) {
+ $obj = $this->newObj();
+ $this->assertSame( $ns, $obj->getSubject( $ns ) );
- // Some namespaces with subpages
- $this->assertHasSubpages( NS_TALK );
- $this->assertHasSubpages( NS_USER );
- $this->assertHasSubpages( NS_USER_TALK );
+ $title = new TitleValue( $ns, 'A' );
+ $this->assertSame( $title, $obj->getSubjectPage( $title ) );
}
/**
- * @covers NamespaceInfo::getContentNamespaces
+ * @dataProvider provideSubjectTalk
+ * @covers NamespaceInfo::getTalk
+ * @covers NamespaceInfo::getTalkPage
+ * @covers NamespaceInfo::isMethodValidFor
+ * @covers Title::getTalkPage
+ *
+ * @param int $subject
+ * @param int $talk
*/
- public function testGetContentNamespaces() {
- global $wgContentNamespaces;
+ public function testGetTalk( $subject, $talk ) {
+ $obj = $this->newObj();
+ $this->assertSame( $talk, $obj->getTalk( $subject ) );
+ $this->assertSame( $talk, $obj->getTalk( $talk ) );
+
+ $subjectTitleVal = new TitleValue( $subject, 'A' );
+ $talkTitleVal = new TitleValue( $talk, 'A' );
+ // Object will be the same one passed in if it's a talk, different but equal object if it's
+ // subject
+ $this->assertEquals( $talkTitleVal, $obj->getTalkPage( $subjectTitleVal ) );
+ $this->assertSame( $talkTitleVal, $obj->getTalkPage( $talkTitleVal ) );
+
+ $subjectTitle = Title::makeTitle( $subject, 'A' );
+ $talkTitle = Title::makeTitle( $talk, 'A' );
+ $this->assertEquals( $talkTitle, $subjectTitle->getTalkPage() );
+ $this->assertSame( $talkTitle, $talkTitle->getTalkPage() );
+ }
+
+ /**
+ * @dataProvider provideSpecialNamespaces
+ * @covers NamespaceInfo::getTalk
+ * @covers NamespaceInfo::isMethodValidFor
+ *
+ * @param int $ns
+ */
+ public function testGetTalk_special( $ns ) {
+ $this->setExpectedException( MWException::class,
+ "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
+ $this->newObj()->getTalk( $ns );
+ }
- $this->assertEquals(
- [ NS_MAIN ],
- $this->obj->getContentNamespaces(),
- '$wgContentNamespaces is an array with only NS_MAIN by default'
+ /**
+ * @dataProvider provideSpecialNamespaces
+ * @covers NamespaceInfo::getAssociated
+ * @covers NamespaceInfo::isMethodValidFor
+ *
+ * @param int $ns
+ */
+ public function testGetAssociated_special( $ns ) {
+ $this->setExpectedException(
+ MWException::class,
+ "NamespaceInfo::getAssociated does not make any sense for given namespace $ns"
);
+ $this->newObj()->getAssociated( $ns );
+ }
+
+ public static function provideCanHaveTalkPage() {
+ return [
+ [ new TitleValue( NS_MAIN, 'Test' ), true ],
+ [ new TitleValue( NS_TALK, 'Test' ), true ],
+ [ new TitleValue( NS_USER, 'Test' ), true ],
+ [ new TitleValue( NS_SPECIAL, 'Test' ), false ],
+ [ new TitleValue( NS_MEDIA, 'Test' ), false ],
+ [ new TitleValue( NS_MAIN, '', 'Kittens' ), false ],
+ [ new TitleValue( NS_MAIN, 'Kittens', '', 'acme' ), false ],
+ ];
+ }
- # test !is_array( $wgcontentNamespaces )
- $wgContentNamespaces = '';
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+ /**
+ * @dataProvider provideCanHaveTalkPage
+ * @covers NamespaceInfo::canHaveTalkPage
+ */
+ public function testCanHaveTalkPage( LinkTarget $t, $expected ) {
+ $actual = $this->newObj()->canHaveTalkPage( $t );
+ $this->assertEquals( $expected, $actual, $t->getDBkey() );
+ }
- $wgContentNamespaces = false;
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+ public static function provideGetTalkPage_good() {
+ return [
+ [ new TitleValue( NS_MAIN, 'Test' ), new TitleValue( NS_TALK, 'Test' ) ],
+ [ new TitleValue( NS_TALK, 'Test' ), new TitleValue( NS_TALK, 'Test' ) ],
+ [ new TitleValue( NS_USER, 'Test' ), new TitleValue( NS_USER_TALK, 'Test' ) ],
+ ];
+ }
- $wgContentNamespaces = null;
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+ /**
+ * @dataProvider provideGetTalkPage_good
+ * @covers NamespaceInfo::getTalk
+ * @covers NamespaceInfo::getTalkPage
+ * @covers NamespaceInfo::isMethodValidFor
+ */
+ public function testGetTalkPage_good( LinkTarget $t, LinkTarget $expected ) {
+ $actual = $this->newObj()->getTalkPage( $t );
+ $this->assertEquals( $expected, $actual, $t->getDBkey() );
+ }
- $wgContentNamespaces = 5;
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+ public static function provideGetTalkPage_bad() {
+ return [
+ [ new TitleValue( NS_SPECIAL, 'Test' ) ],
+ [ new TitleValue( NS_MEDIA, 'Test' ) ],
+ [ new TitleValue( NS_MAIN, '', 'Kittens' ) ],
+ [ new TitleValue( NS_MAIN, 'Kittens', '', 'acme' ) ],
+ ];
+ }
- # test $wgContentNamespaces === []
- $wgContentNamespaces = [];
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+ /**
+ * @dataProvider provideGetTalkPage_bad
+ * @covers NamespaceInfo::getTalk
+ * @covers NamespaceInfo::getTalkPage
+ * @covers NamespaceInfo::isMethodValidFor
+ */
+ public function testGetTalkPage_bad( LinkTarget $t ) {
+ $this->setExpectedException( MWException::class );
+ $this->newObj()->getTalkPage( $t );
+ }
- # test !in_array( NS_MAIN, $wgContentNamespaces )
- $wgContentNamespaces = [ NS_USER, NS_CATEGORY ];
- $this->assertEquals(
- [ NS_MAIN, NS_USER, NS_CATEGORY ],
- $this->obj->getContentNamespaces(),
- 'NS_MAIN is forced in $wgContentNamespaces even if unwanted'
- );
+ /**
+ * @dataProvider provideGetTalkPage_bad
+ * @covers NamespaceInfo::getAssociated
+ * @covers NamespaceInfo::getAssociatedPage
+ * @covers NamespaceInfo::isMethodValidFor
+ */
+ public function testGetAssociatedPage_bad( LinkTarget $t ) {
+ $this->setExpectedException( MWException::class );
+ $this->newObj()->getAssociatedPage( $t );
+ }
- # test other cases, return $wgcontentNamespaces as is
- $wgContentNamespaces = [ NS_MAIN ];
- $this->assertEquals(
- [ NS_MAIN ],
- $this->obj->getContentNamespaces()
+ /**
+ * @dataProvider provideSubjectTalk
+ * @covers NamespaceInfo::getAssociated
+ * @covers NamespaceInfo::getAssociatedPage
+ * @covers Title::getOtherPage
+ *
+ * @param int $subject
+ * @param int $talk
+ */
+ public function testGetAssociated( $subject, $talk ) {
+ $obj = $this->newObj();
+ $this->assertSame( $talk, $obj->getAssociated( $subject ) );
+ $this->assertSame( $subject, $obj->getAssociated( $talk ) );
+
+ $subjectTitle = new TitleValue( $subject, 'A' );
+ $talkTitle = new TitleValue( $talk, 'A' );
+ // Object will not be the same
+ $this->assertEquals( $talkTitle, $obj->getAssociatedPage( $subjectTitle ) );
+ $this->assertEquals( $subjectTitle, $obj->getAssociatedPage( $talkTitle ) );
+
+ $subjectTitle = Title::makeTitle( $subject, 'A' );
+ $talkTitle = Title::makeTitle( $talk, 'A' );
+ $this->assertEquals( $talkTitle, $subjectTitle->getOtherPage() );
+ $this->assertEquals( $subjectTitle, $talkTitle->getOtherPage() );
+ }
+
+ public static function provideSubjectTalk() {
+ return [
+ // Format: [ subject, talk ]
+ 'Main/talk' => [ NS_MAIN, NS_TALK ],
+ 'User/user talk' => [ NS_USER, NS_USER_TALK ],
+ 'Unknown namespaces also supported' => [ 106, 107 ],
+ ];
+ }
+
+ public static function provideSpecialNamespaces() {
+ return [
+ 'Special' => [ NS_SPECIAL ],
+ 'Media' => [ NS_MEDIA ],
+ 'Unknown negative index' => [ -613 ],
+ ];
+ }
+
+ // %} End getSubject/Talk/Associated
+
+ /**********************************************************************************************
+ * Canonical namespaces
+ * %{
+ */
+
+ // Default canonical namespaces
+ // %{
+ private function getDefaultNamespaces() {
+ return [ NS_MAIN => '' ] + self::$defaultOptions['CanonicalNamespaceNames'];
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces() {
+ $this->assertSame(
+ $this->getDefaultNamespaces(),
+ $this->newObj()->getCanonicalNamespaces()
);
+ }
+
+ /**
+ * @dataProvider provideGetCanonicalName
+ * @covers NamespaceInfo::getCanonicalName
+ *
+ * @param int $index
+ * @param string|bool $expected
+ */
+ public function testGetCanonicalName( $index, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
+ }
- $wgContentNamespaces = [ NS_MAIN, NS_USER, NS_CATEGORY ];
- $this->assertEquals(
- [ NS_MAIN, NS_USER, NS_CATEGORY ],
- $this->obj->getContentNamespaces()
+ public function provideGetCanonicalName() {
+ return [
+ 'Main' => [ NS_MAIN, '' ],
+ 'Talk' => [ NS_TALK, 'Talk' ],
+ 'With underscore not space' => [ NS_USER_TALK, 'User_talk' ],
+ 'Special' => [ NS_SPECIAL, 'Special' ],
+ 'Nonexistent' => [ 12345, false ],
+ 'Nonexistent negative' => [ -12345, false ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideGetCanonicalIndex
+ * @covers NamespaceInfo::getCanonicalIndex
+ *
+ * @param string $name
+ * @param int|null $expected
+ */
+ public function testGetCanonicalIndex( $name, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
+ }
+
+ public function provideGetCanonicalIndex() {
+ return [
+ 'Main' => [ '', NS_MAIN ],
+ 'Talk' => [ 'talk', NS_TALK ],
+ 'Not lowercase' => [ 'Talk', null ],
+ 'With underscore' => [ 'user_talk', NS_USER_TALK ],
+ 'Space is not recognized for underscore' => [ 'user talk', null ],
+ '0' => [ '0', null ],
+ ];
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces() {
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK ],
+ $this->newObj()->getValidNamespaces()
);
}
+ // %} End default canonical namespaces
+
+ // No canonical namespace names
+ // %{
+
/**
- * @covers NamespaceInfo::getSubjectNamespaces
+ * @covers NamespaceInfo::getCanonicalNamespaces
*/
- public function testGetSubjectNamespaces() {
- $subjectsNS = $this->obj->getSubjectNamespaces();
- $this->assertContains( NS_MAIN, $subjectsNS,
- "Talk namespaces should have NS_MAIN" );
- $this->assertNotContains( NS_TALK, $subjectsNS,
- "Talk namespaces should have NS_TALK" );
+ public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
- $this->assertNotContains( NS_MEDIA, $subjectsNS,
- "Talk namespaces should not have NS_MEDIA" );
- $this->assertNotContains( NS_SPECIAL, $subjectsNS,
- "Talk namespaces should not have NS_SPECIAL" );
+ $this->assertSame( [ NS_MAIN => '' ], $obj->getCanonicalNamespaces() );
}
/**
- * @covers NamespaceInfo::getTalkNamespaces
+ * @covers NamespaceInfo::getCanonicalName
*/
- public function testGetTalkNamespaces() {
- $talkNS = $this->obj->getTalkNamespaces();
- $this->assertContains( NS_TALK, $talkNS,
- "Subject namespaces should have NS_TALK" );
- $this->assertNotContains( NS_MAIN, $talkNS,
- "Subject namespaces should not have NS_MAIN" );
+ public function testGetCanonicalName_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
- $this->assertNotContains( NS_MEDIA, $talkNS,
- "Subject namespaces should not have NS_MEDIA" );
- $this->assertNotContains( NS_SPECIAL, $talkNS,
- "Subject namespaces should not have NS_SPECIAL" );
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertFalse( $obj->getCanonicalName( NS_TALK ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
+
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
}
- private function assertIsCapitalized( $ns ) {
- $this->assertTrue( $this->obj->isCapitalized( $ns ) );
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
+
+ $this->assertSame( [ NS_MAIN ], $obj->getValidNamespaces() );
}
- private function assertIsNotCapitalized( $ns ) {
- $this->assertFalse( $this->obj->isCapitalized( $ns ) );
+ // %} End no canonical namespace names
+
+ // Test extension namespaces
+ // %{
+ private function setupExtensionNamespaces() {
+ $this->scopedCallback = null;
+ $this->scopedCallback = ExtensionRegistry::getInstance()->setAttributeForTest(
+ 'ExtensionNamespaces',
+ [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 12345 => 'Extended' ]
+ );
}
/**
- * Some namespaces are always capitalized per code definition
- * in NamespaceInfo::$alwaysCapitalizedNamespaces
- * @covers NamespaceInfo::isCapitalized
+ * @covers NamespaceInfo::getCanonicalNamespaces
*/
- public function testIsCapitalizedHardcodedAssertions() {
- // NS_MEDIA and NS_FILE are treated the same
- $this->assertEquals(
- $this->obj->isCapitalized( NS_MEDIA ),
- $this->obj->isCapitalized( NS_FILE ),
- 'NS_MEDIA and NS_FILE have same capitalization rendering'
+ public function testGetCanonicalNamespaces_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+
+ $this->assertSame(
+ $this->getDefaultNamespaces() + [ 12345 => 'Extended' ],
+ $this->newObj()->getCanonicalNamespaces()
);
+ }
- // Boths are capitalized by default
- $this->assertIsCapitalized( NS_MEDIA );
- $this->assertIsCapitalized( NS_FILE );
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+ $obj = $this->newObj();
- // Always capitalized namespaces
- // @see NamespaceInfo::$alwaysCapitalizedNamespaces
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
+ $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
}
/**
- * Follows up for testIsCapitalizedHardcodedAssertions() but alter the
- * global $wgCapitalLink setting to have extended coverage.
- *
- * NamespaceInfo::isCapitalized() rely on two global settings:
- * $wgCapitalLinkOverrides = []; by default
- * $wgCapitalLinks = true; by default
- * This function test $wgCapitalLinks
- *
- * Global setting correctness is tested against the NS_PROJECT and
- * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
- * @covers NamespaceInfo::isCapitalized
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+ $obj = $this->newObj();
+
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
+ $this->assertSame( NS_TALK, $obj->getCanonicalIndex( 'talk' ) );
+ $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 12345 ],
+ $this->newObj()->getValidNamespaces()
+ );
+ }
+
+ // %} End extension namespaces
+
+ // Hook namespaces
+ // %{
+
+ /**
+ * @return array Expected canonical namespaces
*/
- public function testIsCapitalizedWithWgCapitalLinks() {
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
+ private function setupHookNamespaces() {
+ $callback =
+ function ( &$canonicalNamespaces ) {
+ $canonicalNamespaces[NS_MAIN] = 'Main';
+ unset( $canonicalNamespaces[NS_MEDIA] );
+ $canonicalNamespaces[123456] = 'Hooked';
+ };
+ $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
+ $expected = $this->getDefaultNamespaces();
+ ( $callback )( $expected );
+ return $expected;
+ }
- $this->setMwGlobals( 'wgCapitalLinks', false );
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_HookNamespaces() {
+ $expected = $this->setupHookNamespaces();
- // hardcoded namespaces (see above function) are still capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
+ }
- // setting is correctly applied
- $this->assertIsNotCapitalized( NS_PROJECT );
- $this->assertIsNotCapitalized( NS_PROJECT_TALK );
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_HookNamespaces() {
+ $this->setupHookNamespaces();
+ $obj = $this->newObj();
+
+ $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertFalse( $obj->getCanonicalName( NS_MEDIA ) );
+ $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
}
/**
- * Counter part for NamespaceInfo::testIsCapitalizedWithWgCapitalLinks() now
- * testing the $wgCapitalLinkOverrides global.
- *
- * @todo split groups of assertions in autonomous testing functions
- * @covers NamespaceInfo::isCapitalized
+ * @covers NamespaceInfo::getCanonicalIndex
*/
- public function testIsCapitalizedWithWgCapitalLinkOverrides() {
- global $wgCapitalLinkOverrides;
+ public function testGetCanonicalIndex_HookNamespaces() {
+ $this->setupHookNamespaces();
+ $obj = $this->newObj();
- // Test default settings
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( 'main' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
+ $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
+ }
- // hardcoded namespaces (see above function) are capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_HookNamespaces() {
+ $this->setupHookNamespaces();
- // Hardcoded namespaces remains capitalized
- $wgCapitalLinkOverrides[NS_SPECIAL] = false;
- $wgCapitalLinkOverrides[NS_USER] = false;
- $wgCapitalLinkOverrides[NS_MEDIAWIKI] = false;
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 123456 ],
+ $this->newObj()->getValidNamespaces()
+ );
+ }
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ // %} End hook namespaces
- $wgCapitalLinkOverrides[NS_PROJECT] = false;
- $this->assertIsNotCapitalized( NS_PROJECT );
+ // Extra namespaces
+ // %{
- $wgCapitalLinkOverrides[NS_PROJECT] = true;
- $this->assertIsCapitalized( NS_PROJECT );
+ /**
+ * @return NamespaceInfo
+ */
+ private function setupExtraNamespaces() {
+ return $this->newObj( [ 'ExtraNamespaces' =>
+ [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 1234567 => 'Extra' ]
+ ] );
+ }
- unset( $wgCapitalLinkOverrides[NS_PROJECT] );
- $this->assertIsCapitalized( NS_PROJECT );
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_ExtraNamespaces() {
+ $this->assertSame(
+ $this->getDefaultNamespaces() + [ 1234567 => 'Extra' ],
+ $this->setupExtraNamespaces()->getCanonicalNamespaces()
+ );
}
/**
- * @covers NamespaceInfo::hasGenderDistinction
+ * @covers NamespaceInfo::getCanonicalName
*/
- public function testHasGenderDistinction() {
- // Namespaces with gender distinctions
- $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER ) );
- $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER_TALK ) );
+ public function testGetCanonicalName_ExtraNamespaces() {
+ $obj = $this->setupExtraNamespaces();
- // Other ones, "genderless"
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_MEDIA ) );
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_SPECIAL ) );
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_MAIN ) );
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_TALK ) );
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
+ $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
}
/**
- * @covers NamespaceInfo::isNonincludable
+ * @covers NamespaceInfo::getCanonicalIndex
*/
- public function testIsNonincludable() {
- global $wgNonincludableNamespaces;
+ public function testGetCanonicalIndex_ExtraNamespaces() {
+ $obj = $this->setupExtraNamespaces();
- $wgNonincludableNamespaces = [ NS_USER ];
+ $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
+ $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
+ }
- $this->assertTrue( $this->obj->isNonincludable( NS_USER ) );
- $this->assertFalse( $this->obj->isNonincludable( NS_TEMPLATE ) );
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_ExtraNamespaces() {
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 1234567 ],
+ $this->setupExtraNamespaces()->getValidNamespaces()
+ );
}
- private function assertSameSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertTrue( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ // %} End extra namespaces
+
+ // Canonical namespace caching
+ // %{
+
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getCanonicalNamespaces();
+
+ // Now try to alter them through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
}
- private function assertDifferentSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertFalse( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getCanonicalName( NS_MAIN );
+
+ // Now try to alter them through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertSame( 'Media', $obj->getCanonicalName( NS_MEDIA ) );
+ $this->assertFalse( $obj->getCanonicalName( 12345 ) );
+ $this->assertFalse( $obj->getCanonicalName( 123456 ) );
}
- public function provideGetCategoryLinkType() {
- return [
- [ NS_MAIN, 'page' ],
- [ NS_TALK, 'page' ],
- [ NS_USER, 'page' ],
- [ NS_USER_TALK, 'page' ],
+ /**
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_caching() {
+ $obj = $this->newObj();
- [ NS_FILE, 'file' ],
- [ NS_FILE_TALK, 'page' ],
+ // This should cache the values
+ $obj->getCanonicalIndex( '' );
- [ NS_CATEGORY, 'subcat' ],
- [ NS_CATEGORY_TALK, 'page' ],
+ // Now try to alter them through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
- [ 100, 'page' ],
- [ 101, 'page' ],
+ // Should have no effect
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
+ $this->assertSame( NS_MEDIA, $obj->getCanonicalIndex( 'media' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'extended' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'hooked' ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getValidNamespaces();
+
+ // Now try to alter through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK ],
+ $obj->getValidNamespaces()
+ );
+ }
+
+ // %} End canonical namespace caching
+
+ // Miscellaneous
+ // %{
+
+ /**
+ * @dataProvider provideGetValidNamespaces_misc
+ * @covers NamespaceInfo::getValidNamespaces
+ *
+ * @param array $namespaces List of namespace indices to return from getCanonicalNamespaces()
+ * (list is overwritten by a hook, so NS_MAIN doesn't have to be present)
+ * @param array $expected
+ */
+ public function testGetValidNamespaces_misc( array $namespaces, array $expected ) {
+ // Each namespace's name is just its index
+ $this->setTemporaryHook( 'CanonicalNamespaces',
+ function ( &$canonicalNamespaces ) use ( $namespaces ) {
+ $canonicalNamespaces = array_combine( $namespaces, $namespaces );
+ }
+ );
+ $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
+ }
+
+ public function provideGetValidNamespaces_misc() {
+ return [
+ 'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
+ 'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
+ 'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
];
}
+ // %} End miscellaneous
+ // %} End canonical namespaces
+
+ /**********************************************************************************************
+ * Restriction levels
+ * %{
+ */
+
+ /**
+ * This mock user can only have isAllowed() called on it.
+ *
+ * @param array $groups Groups for the mock user to have
+ * @return User
+ */
+ private function getMockUser( array $groups = [] ) : User {
+ $groups[] = '*';
+
+ $mock = $this->createMock( User::class );
+ $mock->method( 'isAllowed' )->will( $this->returnCallback(
+ function ( $action ) use ( $groups ) {
+ global $wgGroupPermissions, $wgRevokePermissions;
+ if ( $action == '' ) {
+ return true;
+ }
+ foreach ( $wgRevokePermissions as $group => $rights ) {
+ if ( !in_array( $group, $groups ) ) {
+ continue;
+ }
+ if ( isset( $rights[$action] ) && $rights[$action] ) {
+ return false;
+ }
+ }
+ foreach ( $wgGroupPermissions as $group => $rights ) {
+ if ( !in_array( $group, $groups ) ) {
+ continue;
+ }
+ if ( isset( $rights[$action] ) && $rights[$action] ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ ) );
+ $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
+ return $mock;
+ }
+
/**
- * @dataProvider provideGetCategoryLinkType
- * @covers NamespaceInfo::getCategoryLinkType
+ * @dataProvider provideGetRestrictionLevels
+ * @covers NamespaceInfo::getRestrictionLevels
*
- * @param int $index
- * @param string $expected
+ * @param array $expected
+ * @param int $ns
+ * @param User|null $user
*/
- public function testGetCategoryLinkType( $index, $expected ) {
- $actual = $this->obj->getCategoryLinkType( $index );
- $this->assertSame( $expected, $actual, "NS $index" );
+ public function testGetRestrictionLevels( array $expected, $ns, User $user = null ) {
+ $this->setMwGlobals( [
+ 'wgGroupPermissions' => [
+ '*' => [ 'edit' => true ],
+ 'autoconfirmed' => [ 'editsemiprotected' => true ],
+ 'sysop' => [
+ 'editsemiprotected' => true,
+ 'editprotected' => true,
+ ],
+ 'privileged' => [ 'privileged' => true ],
+ ],
+ 'wgRevokePermissions' => [
+ 'noeditsemiprotected' => [ 'editsemiprotected' => true ],
+ ],
+ ] );
+ $obj = $this->newObj( [
+ 'NamespaceProtection' => [
+ NS_MAIN => 'autoconfirmed',
+ NS_USER => 'sysop',
+ 101 => [ 'editsemiprotected', 'privileged' ],
+ ],
+ ] );
+ $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
+ }
+
+ public function provideGetRestrictionLevels() {
+ return [
+ 'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK ],
+ 'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN ],
+ 'Restricted to sysop' => [ [ '' ], NS_USER ],
+ 'Restricted to someone in two groups' => [ [ '', 'sysop' ], 101 ],
+ 'No special permissions' => [ [ '' ], NS_TALK, $this->getMockUser() ],
+ 'autoconfirmed' => [
+ [ '', 'autoconfirmed' ],
+ NS_TALK,
+ $this->getMockUser( [ 'autoconfirmed' ] )
+ ],
+ 'autoconfirmed revoked' => [
+ [ '' ],
+ NS_TALK,
+ $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
+ ],
+ 'sysop' => [
+ [ '', 'autoconfirmed', 'sysop' ],
+ NS_TALK,
+ $this->getMockUser( [ 'sysop' ] )
+ ],
+ 'sysop with autoconfirmed revoked (a bit silly)' => [
+ [ '', 'sysop' ],
+ NS_TALK,
+ $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
+ ],
+ ];
}
+
+ // %} End restriction levels
}
+
+/**
+ * For really cool vim folding this needs to be at the end:
+ * vim: foldmarker=%{,%} foldmethod=marker
+ */