wiki.techinc.nl/tests/phpunit/includes/title/NamespaceInfoTest.php
Tim Starling d459add63d Introduce wfDeprecatedMsg()
Deprecating something means to say something nasty about it, or to draw
its character into question. For example, "this function is lazy and good
for nothing". Deprecatory remarks by a developer are generally taken as a
warning that violence will soon be done against the function in question.
Other developers are thus warned to avoid associating with the deprecated
function.

However, since wfDeprecated() was introduced, it has become obvious that
the targets of deprecation are not limited to functions. Developers can
deprecate literally anything: a parameter, a return value, a file
format, Mondays, the concept of being, etc. wfDeprecated() requires
every deprecatory statement to begin with "use of", leading to some
awkward sentences. For example, one might say: "Use of your mouth to
cough without it being covered by your arm is deprecated since 2020."

So, introduce wfDeprecatedMsg(), which allows deprecation messages to be
specified in plain text, with the caller description being optionally
appended. Migrate incorrect or gramatically awkward uses of wfDeprecated()
to wfDeprecatedMsg().

Change-Id: Ib3dd2fe37677d98425d0f3692db5c9e988943ae8
2020-06-22 14:34:39 +10:00

1341 lines
37 KiB
PHP

<?php
/**
* @author Antoine Musso
* @copyright Copyright © 2011, Antoine Musso
* @file
*/
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
class NamespaceInfoTest extends MediaWikiTestCase {
use TestAllServiceOptionsUsed;
/**********************************************************************************************
* Shared code
* %{
*/
private $scopedCallback;
protected function setUp() : void {
parent::setUp();
// Boo, there's still some global state in the class :(
global $wgHooks;
$hooks = $wgHooks;
unset( $hooks['CanonicalNamespaces'] );
$this->setMwGlobals( 'wgHooks', $hooks );
$this->scopedCallback =
ExtensionRegistry::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
}
protected function tearDown() : void {
$this->scopedCallback = null;
parent::tearDown();
}
private const DEFAULT_OPTIONS = [
'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' => [],
'NamespacesWithSubpages' => [
NS_TALK => true,
NS_USER => true,
NS_USER_TALK => true,
],
'NonincludableNamespaces' => [],
];
/**
* @return HookContainer
*/
private function getHookContainer() {
return MediaWikiServices::getInstance()->getHookContainer();
}
private function newObj( array $options = [] ) : NamespaceInfo {
return new NamespaceInfo(
new LoggedServiceOptions(
self::$serviceOptionsAccessLog,
NamespaceInfo::CONSTRUCTOR_OPTIONS,
$options, self::DEFAULT_OPTIONS
),
$this->getHookContainer()
);
}
// %} End shared code
/**********************************************************************************************
* Basic methods
* %{
*/
/**
* @covers NamespaceInfo::__construct
* @dataProvider provideConstructor
* @param ServiceOptions $options
* @param string|null $expectedExceptionText
*/
public function testConstructor( ServiceOptions $options, $expectedExceptionText = null ) {
if ( $expectedExceptionText !== null ) {
$this->expectException( \Wikimedia\Assert\PreconditionException::class );
$this->expectExceptionMessage( $expectedExceptionText );
}
new NamespaceInfo( $options, $this->getHookContainer() );
$this->assertTrue( true );
}
public function provideConstructor() {
return [
[ new ServiceOptions( NamespaceInfo::CONSTRUCTOR_OPTIONS, self::DEFAULT_OPTIONS ) ],
[ new ServiceOptions( [], [] ), 'Required options missing: ' ],
[ new ServiceOptions(
array_merge( NamespaceInfo::CONSTRUCTOR_OPTIONS, [ 'invalid' ] ),
self::DEFAULT_OPTIONS,
[ 'invalid' => '' ]
), 'Unsupported options passed: invalid' ],
];
}
/**
* @dataProvider provideIsMovable
* @covers NamespaceInfo::isMovable
*
* @param bool $expected
* @param int $ns
* @param bool $allowImageMoving
*/
public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
if ( $allowImageMoving === false ) {
$this->filterDeprecated( '/Setting \$wgAllowImageMoving to false/' );
}
$obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
$this->assertSame( $expected, $obj->isMovable( $ns ) );
}
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 ],
];
}
/**
* @param int $ns
* @param bool $expected
* @dataProvider provideIsSubject
* @covers NamespaceInfo::isSubject
*/
public function testIsSubject( $ns, $expected ) {
$this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
}
/**
* @param int $ns
* @param bool $expected
* @dataProvider provideIsSubject
* @covers NamespaceInfo::isTalk
*/
public function testIsTalk( $ns, $expected ) {
$this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
}
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 ],
];
}
/**
* @covers NamespaceInfo::exists
* @dataProvider provideExists
* @param int $ns
* @param bool $expected
*/
public function testExists( $ns, $expected ) {
$this->assertSame( $expected, $this->newObj()->exists( $ns ) );
}
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 equivalence on things like 'MAIN' == 0
* and 'MAIN' == NS_MAIN.
* @covers NamespaceInfo::equals
*/
public function testEquals() {
$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( $ns1, $ns2, $expected ) {
$this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
}
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 ],
];
}
/**
* @dataProvider provideHasTalkNamespace
* @covers NamespaceInfo::hasTalkNamespace
*
* @param int $ns
* @param bool $expected
*/
public function testHasTalkNamespace( $ns, $expected ) {
$this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
}
public function provideHasTalkNamespace() {
return [
[ NS_MEDIA, false ],
[ NS_SPECIAL, false ],
[ NS_MAIN, true ],
[ NS_TALK, true ],
[ NS_USER, true ],
[ NS_USER_TALK, true ],
[ 100, true ],
[ 101, true ],
];
}
/**
* @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 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 provideWantSignatures_ExtraSignatureNamespaces
* @covers NamespaceInfo::wantSignatures
*
* @param int $index
* @param int $expected
*/
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 ) );
}
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 ) );
}
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 ],
];
}
/**
* @param int $ns
* @param int $expected
* @param array|null $namespacesWithSubpages To pass to constructor
* @covers NamespaceInfo::hasSubpages
* @dataProvider provideHasSubpages
*/
public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
$obj = $this->newObj( $namespacesWithSubpages
? [ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
: [] );
$this->assertSame( $expected, $obj->hasSubpages( $ns ) );
}
public function provideHasSubpages() {
return [
// Special namespaces:
[ NS_MEDIA, false ],
[ NS_SPECIAL, false ],
// Namespaces without subpages
[ NS_MAIN, false ],
[ NS_MAIN, true, [ NS_MAIN => true ] ],
[ NS_MAIN, false, [ NS_MAIN => false ] ],
// Some namespaces with subpages
[ NS_TALK, true ],
[ NS_USER, true ],
[ NS_USER_TALK, true ],
];
}
/**
* @param mixed $contentNamespaces To pass to constructor
* @param array $expected
* @dataProvider provideGetContentNamespaces
* @covers NamespaceInfo::getContentNamespaces
*/
public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
$obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
$this->assertSame( $expected, $obj->getContentNamespaces() );
}
public function provideGetContentNamespaces() {
return [
// Non-array
[ '', [ NS_MAIN ] ],
[ false, [ NS_MAIN ] ],
[ null, [ NS_MAIN ] ],
[ 5, [ NS_MAIN ] ],
// Empty array
[ [], [ NS_MAIN ] ],
// NS_MAIN is forced to be content even if unwanted
[ [ NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
// In other cases, return as-is
[ [ NS_MAIN ], [ NS_MAIN ] ],
[ [ NS_MAIN, NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
];
}
/**
* @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" );
}
/**
* @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" );
}
/**
* @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 testHasGenderDistinction() {
$obj = $this->newObj();
// Namespaces with gender distinctions
$this->assertTrue( $obj->hasGenderDistinction( NS_USER ) );
$this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK ) );
// 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 ) );
}
/**
* @covers NamespaceInfo::isNonincludable
*/
public function testIsNonincludable() {
$obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER ] ] );
$this->assertTrue( $obj->isNonincludable( NS_USER ) );
$this->assertFalse( $obj->isNonincludable( NS_TEMPLATE ) );
}
/**
* @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 ],
];
}
/**
* @dataProvider provideGetCategoryLinkType
* @covers NamespaceInfo::getCategoryLinkType
*
* @param int $ns
* @param string $expected
*/
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' ],
];
}
// %} End basic methods
/**********************************************************************************************
* getSubject/Talk/Associated
* %{
*/
/**
* @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() );
}
/**
* @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 ) );
$title = new TitleValue( $ns, 'A' );
$this->assertSame( $title, $obj->getSubjectPage( $title ) );
}
/**
* @dataProvider provideSubjectTalk
* @covers NamespaceInfo::getTalk
* @covers NamespaceInfo::getTalkPage
* @covers NamespaceInfo::isMethodValidFor
* @covers Title::getTalkPage
*
* @param int $subject
* @param int $talk
*/
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->expectException( MWException::class );
$this->expectExceptionMessage(
"NamespaceInfo::getTalk does not make any sense for given namespace $ns"
);
$this->newObj()->getTalk( $ns );
}
/**
* @dataProvider provideSpecialNamespaces
* @covers NamespaceInfo::getAssociated
* @covers NamespaceInfo::isMethodValidFor
*
* @param int $ns
*/
public function testGetAssociated_special( $ns ) {
$this->expectException( MWException::class );
$this->expectExceptionMessage(
"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 ],
];
}
/**
* @dataProvider provideCanHaveTalkPage
* @covers NamespaceInfo::canHaveTalkPage
*/
public function testCanHaveTalkPage( LinkTarget $t, $expected ) {
$actual = $this->newObj()->canHaveTalkPage( $t );
$this->assertEquals( $expected, $actual, $t->getDBkey() );
}
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' ) ],
];
}
/**
* @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() );
}
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' ) ],
];
}
/**
* @dataProvider provideGetTalkPage_bad
* @covers NamespaceInfo::getTalk
* @covers NamespaceInfo::getTalkPage
* @covers NamespaceInfo::isMethodValidFor
*/
public function testGetTalkPage_bad( LinkTarget $t ) {
$this->expectException( MWException::class );
$this->newObj()->getTalkPage( $t );
}
/**
* @dataProvider provideGetTalkPage_bad
* @covers NamespaceInfo::getAssociated
* @covers NamespaceInfo::getAssociatedPage
* @covers NamespaceInfo::isMethodValidFor
*/
public function testGetAssociatedPage_bad( LinkTarget $t ) {
$this->expectException( MWException::class );
$this->newObj()->getAssociatedPage( $t );
}
/**
* @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::DEFAULT_OPTIONS['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 ) );
}
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::getCanonicalNamespaces
*/
public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
$obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
$this->assertSame( [ NS_MAIN => '' ], $obj->getCanonicalNamespaces() );
}
/**
* @covers NamespaceInfo::getCanonicalName
*/
public function testGetCanonicalName_NoCanonicalNamespaceNames() {
$obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
$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' ) );
}
/**
* @covers NamespaceInfo::getValidNamespaces
*/
public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
$obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
$this->assertSame( [ NS_MAIN ], $obj->getValidNamespaces() );
}
// %} 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' ]
);
}
/**
* @covers NamespaceInfo::getCanonicalNamespaces
*/
public function testGetCanonicalNamespaces_ExtensionNamespaces() {
$this->setupExtensionNamespaces();
$this->assertSame(
$this->getDefaultNamespaces() + [ 12345 => 'Extended' ],
$this->newObj()->getCanonicalNamespaces()
);
}
/**
* @covers NamespaceInfo::getCanonicalName
*/
public function testGetCanonicalName_ExtensionNamespaces() {
$this->setupExtensionNamespaces();
$obj = $this->newObj();
$this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
$this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
$this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
}
/**
* @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
*/
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;
}
/**
* @covers NamespaceInfo::getCanonicalNamespaces
*/
public function testGetCanonicalNamespaces_HookNamespaces() {
$expected = $this->setupHookNamespaces();
$this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
}
/**
* @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 ) );
}
/**
* @covers NamespaceInfo::getCanonicalIndex
*/
public function testGetCanonicalIndex_HookNamespaces() {
$this->setupHookNamespaces();
$obj = $this->newObj();
$this->assertSame( NS_MAIN, $obj->getCanonicalIndex( 'main' ) );
$this->assertNull( $obj->getCanonicalIndex( 'media' ) );
$this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
}
/**
* @covers NamespaceInfo::getValidNamespaces
*/
public function testGetValidNamespaces_HookNamespaces() {
$this->setupHookNamespaces();
$this->assertSame(
[ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 123456 ],
$this->newObj()->getValidNamespaces()
);
}
// %} End hook namespaces
// Extra namespaces
// %{
/**
* @return NamespaceInfo
*/
private function setupExtraNamespaces() {
return $this->newObj( [ 'ExtraNamespaces' =>
[ NS_MAIN => 'No effect', NS_TALK => 'No effect', 1234567 => 'Extra' ]
] );
}
/**
* @covers NamespaceInfo::getCanonicalNamespaces
*/
public function testGetCanonicalNamespaces_ExtraNamespaces() {
$this->assertSame(
$this->getDefaultNamespaces() + [ 1234567 => 'Extra' ],
$this->setupExtraNamespaces()->getCanonicalNamespaces()
);
}
/**
* @covers NamespaceInfo::getCanonicalName
*/
public function testGetCanonicalName_ExtraNamespaces() {
$obj = $this->setupExtraNamespaces();
$this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
$this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
$this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
}
/**
* @covers NamespaceInfo::getCanonicalIndex
*/
public function testGetCanonicalIndex_ExtraNamespaces() {
$obj = $this->setupExtraNamespaces();
$this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
$this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
$this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
}
/**
* @covers NamespaceInfo::getValidNamespaces
*/
public function testGetValidNamespaces_ExtraNamespaces() {
$this->assertSame(
[ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 1234567 ],
$this->setupExtraNamespaces()->getValidNamespaces()
);
}
// %} 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() );
}
/**
* @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 ) );
}
/**
* @covers NamespaceInfo::getCanonicalIndex
*/
public function testGetCanonicalIndex_caching() {
$obj = $this->newObj();
// This should cache the values
$obj->getCanonicalIndex( '' );
// Now try to alter them through nefarious means
$this->setupExtensionNamespaces();
$this->setupHookNamespaces();
// 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
* %{
*/
/**
* TODO: This is superceeded by PermissionManagerTest::testGetNamespaceRestrictionLevels
* Remove when deprecated method is removed.
* @dataProvider provideGetRestrictionLevels
* @covers NamespaceInfo::getRestrictionLevels
*
* @param array $expected
* @param int $ns
* @param array|null $groups
* @throws MWException
*/
public function testGetRestrictionLevels( array $expected, $ns, array $groups = null ) {
$this->setMwGlobals( [
'wgGroupPermissions' => [
'*' => [ 'edit' => true ],
'autoconfirmed' => [ 'editsemiprotected' => true ],
'sysop' => [
'editsemiprotected' => true,
'editprotected' => true,
],
'privileged' => [ 'privileged' => true ],
],
'wgRevokePermissions' => [
'noeditsemiprotected' => [ 'editsemiprotected' => true ],
],
'wgNamespaceProtection' => [
NS_MAIN => 'autoconfirmed',
NS_USER => 'sysop',
101 => [ 'editsemiprotected', 'privileged' ],
],
'wgRestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
'wgAutopromote' => []
] );
$obj = $this->newObj();
$user = $groups === null ? null : $this->getTestUser( $groups )->getUser();
$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, [] ],
'autoconfirmed' => [
[ '', 'autoconfirmed' ],
NS_TALK,
[ 'autoconfirmed' ]
],
'autoconfirmed revoked' => [
[ '' ],
NS_TALK,
[ 'autoconfirmed', 'noeditsemiprotected' ]
],
'sysop' => [
[ '', 'autoconfirmed', 'sysop' ],
NS_TALK,
[ 'sysop' ]
],
'sysop with autoconfirmed revoked (a bit silly)' => [
[ '', 'sysop' ],
NS_TALK,
[ 'sysop', 'noeditsemiprotected' ]
],
];
}
// %} End restriction levels
/**
* @coversNothing
*/
public function testAllServiceOptionsUsed() {
$this->assertAllServiceOptionsUsed();
}
}
/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=%{,%} foldmethod=marker
*/