Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5979306
MediaWikiTitleCodecTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
28 KB
Referenced Files
None
Subscribers
None
MediaWikiTitleCodecTest.php
View Options
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @author Daniel Kinzler
*/
use
MediaWiki\Cache\GenderCache
;
use
MediaWiki\Interwiki\InterwikiLookup
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\Page\PageIdentity
;
use
MediaWiki\Page\PageIdentityValue
;
use
MediaWiki\Tests\Unit\DummyServicesTrait
;
use
MediaWiki\Title\MalformedTitleException
;
use
MediaWiki\Title\MediaWikiTitleCodec
;
use
MediaWiki\Title\NamespaceInfo
;
use
MediaWiki\Title\Title
;
use
MediaWiki\Title\TitleValue
;
/**
* @covers \MediaWiki\Title\MediaWikiTitleCodec
*
* @group Title
* @group Database
* ^--- needed because of global state in
*/
class
MediaWikiTitleCodecTest
extends
MediaWikiIntegrationTestCase
{
use
DummyServicesTrait
;
protected
function
setUp
():
void
{
parent
::
setUp
();
$this
->
overrideConfigValues
(
[
MainConfigNames
::
AllowUserJs
=>
false
,
MainConfigNames
::
DefaultLanguageVariant
=>
false
,
MainConfigNames
::
MetaNamespace
=>
'Project'
,
MainConfigNames
::
LocalInterwikis
=>
[
'localtestiw'
],
MainConfigNames
::
CapitalLinks
=>
true
,
MainConfigNames
::
LanguageCode
=>
'en'
,
]
);
$this
->
setUserLang
(
'en'
);
}
/**
* Returns a mock GenderCache that will consider a user "female" if the
* first part of the user name ends with "a".
*
* @return GenderCache
*/
private
function
getGenderCache
()
{
$genderCache
=
$this
->
createMock
(
GenderCache
::
class
);
$genderCache
->
method
(
'getGenderOf'
)
->
willReturnCallback
(
static
function
(
$userName
)
{
return
preg_match
(
'/^[^- _]+a( |_|$)/u'
,
$userName
)
?
'female'
:
'male'
;
}
);
return
$genderCache
;
}
/**
* Returns a InterwikiLookup where the only valid interwikis are 'localtestiw' and 'remotetestiw'.
* Only `isValidInterwiki` should actually be needed.
*
* @return InterwikiLookup
*/
private
function
getInterwikiLookup
():
InterwikiLookup
{
return
$this
->
getDummyInterwikiLookup
(
[
'localtestiw'
,
'remotetestiw'
]
);
}
/**
* Returns a NamespaceInfo where the only namespaces that exist are NS_SPECIAL, NS_MAIN, NS_TALK,
* NS_USER, and NS_USER_TALK. As per the real NamespaceInfo, NS_USER and NS_USER_TALK have
* gender distinctions. All namespaces are capitalized.
*
* @return NamespaceInfo
*/
private
function
getNamespaceInfo
():
NamespaceInfo
{
return
$this
->
getDummyNamespaceInfo
(
[
MainConfigNames
::
CanonicalNamespaceNames
=>
[
NS_SPECIAL
=>
'Special'
,
NS_MAIN
=>
''
,
NS_TALK
=>
'Talk'
,
NS_USER
=>
'User'
,
NS_USER_TALK
=>
'User_talk'
,
],
MainConfigNames
::
CapitalLinks
=>
true
,
]
);
}
protected
function
makeCodec
(
$lang
)
{
return
new
MediaWikiTitleCodec
(
$this
->
getServiceContainer
()->
getLanguageFactory
()->
getLanguage
(
$lang
),
$this
->
getGenderCache
(),
[
'localtestiw'
],
$this
->
getInterwikiLookup
(),
$this
->
getNamespaceInfo
()
);
}
public
static
function
provideFormat
()
{
return
[
[
NS_MAIN
,
'Foo_Bar'
,
''
,
''
,
'en'
,
'Foo Bar'
],
[
NS_USER
,
'Hansi_Maier'
,
'stuff_and_so_on'
,
''
,
'en'
,
'User:Hansi Maier#stuff and so on'
],
[
false
,
'Hansi_Maier'
,
''
,
''
,
'en'
,
'Hansi Maier'
],
[
NS_USER_TALK
,
'hansi__maier'
,
''
,
''
,
'en'
,
'User talk:hansi maier'
,
'User talk:Hansi maier'
],
// getGenderCache() provides a mock that considers first
// names ending in "a" to be female.
[
NS_USER
,
'Lisa_Müller'
,
''
,
''
,
'de'
,
'Benutzerin:Lisa Müller'
],
[
NS_MAIN
,
'FooBar'
,
''
,
'remotetestiw'
,
'en'
,
'remotetestiw:FooBar'
],
];
}
/**
* @dataProvider provideFormat
*/
public
function
testFormat
(
$namespace
,
$text
,
$fragment
,
$interwiki
,
$lang
,
$expected
,
$normalized
=
null
)
{
$normalized
??=
$expected
;
$codec
=
$this
->
makeCodec
(
$lang
);
$actual
=
$codec
->
formatTitle
(
$namespace
,
$text
,
$fragment
,
$interwiki
);
$this
->
assertEquals
(
$expected
,
$actual
,
'formatted'
);
// test round trip
$parsed
=
$codec
->
parseTitle
(
$actual
,
NS_MAIN
);
$actual2
=
$codec
->
formatTitle
(
$parsed
->
getNamespace
(),
$parsed
->
getText
(),
$parsed
->
getFragment
(),
$parsed
->
getInterwiki
()
);
$this
->
assertEquals
(
$normalized
,
$actual2
,
'normalized after round trip'
);
}
public
static
function
provideGetText
()
{
// $title = new TitleValue( $namespace, $dbkey, $fragment );
return
[
[
new
TitleValue
(
NS_MAIN
,
'Foo_Bar'
,
''
),
'en'
,
'Foo Bar'
],
[
new
TitleValue
(
NS_USER
,
'Hansi_Maier'
,
'stuff_and_so_on'
),
'en'
,
'Hansi Maier'
],
[
new
PageIdentityValue
(
37
,
NS_MAIN
,
'Foo_Bar'
,
PageIdentity
::
LOCAL
),
'en'
,
'Foo Bar'
],
[
new
PageIdentityValue
(
37
,
NS_USER
,
'Hansi_Maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'Hansi Maier'
],
];
}
/**
* @dataProvider provideGetText
*/
public
function
testGetText
(
$title
,
$lang
,
$expected
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$actual
=
$codec
->
getText
(
$title
);
$this
->
assertEquals
(
$expected
,
$actual
);
}
public
static
function
provideGetPrefixedText
()
{
return
[
[
new
TitleValue
(
NS_MAIN
,
'Foo_Bar'
,
''
),
'en'
,
'Foo Bar'
],
[
new
TitleValue
(
NS_USER
,
'Hansi_Maier'
,
'stuff_and_so_on'
),
'en'
,
'User:Hansi Maier'
],
// No capitalization or normalization is applied while formatting!
[
new
TitleValue
(
NS_USER_TALK
,
'hansi__maier'
,
''
),
'en'
,
'User talk:hansi maier'
],
// getGenderCache() provides a mock that considers first
// names ending in "a" to be female.
[
new
TitleValue
(
NS_USER
,
'Lisa_Müller'
,
''
),
'de'
,
'Benutzerin:Lisa Müller'
],
[
new
TitleValue
(
1000000
,
'Invalid_namespace'
,
''
),
'en'
,
'Special:Badtitle/NS1000000:Invalid namespace'
],
[
new
PageIdentityValue
(
37
,
NS_MAIN
,
'Foo_Bar'
,
PageIdentity
::
LOCAL
),
'en'
,
'Foo Bar'
],
[
new
PageIdentityValue
(
37
,
NS_USER
,
'Hansi_Maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'User:Hansi Maier'
],
[
new
PageIdentityValue
(
37
,
NS_USER_TALK
,
'hansi__maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'User talk:hansi maier'
],
[
new
PageIdentityValue
(
37
,
NS_USER
,
'Lisa_Müller'
,
PageIdentity
::
LOCAL
),
'de'
,
'Benutzerin:Lisa Müller'
],
[
new
PageIdentityValue
(
37
,
1000000
,
'Invalid_namespace'
,
PageIdentity
::
LOCAL
),
'en'
,
'Special:Badtitle/NS1000000:Invalid namespace'
],
];
}
/**
* @dataProvider provideGetPrefixedText
*/
public
function
testGetPrefixedText
(
$title
,
$lang
,
$expected
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$actual
=
$codec
->
getPrefixedText
(
$title
);
$this
->
assertEquals
(
$expected
,
$actual
);
}
public
static
function
provideGetPrefixedDBkey
()
{
return
[
[
new
TitleValue
(
NS_MAIN
,
'Foo_Bar'
,
''
,
''
),
'en'
,
'Foo_Bar'
],
[
new
TitleValue
(
NS_USER
,
'Hansi_Maier'
,
'stuff_and_so_on'
,
''
),
'en'
,
'User:Hansi_Maier'
],
// No capitalization or normalization is applied while formatting!
[
new
TitleValue
(
NS_USER_TALK
,
'hansi__maier'
,
''
,
''
),
'en'
,
'User_talk:hansi__maier'
],
// getGenderCache() provides a mock that considers first
// names ending in "a" to be female.
[
new
TitleValue
(
NS_USER
,
'Lisa_Müller'
,
''
,
''
),
'de'
,
'Benutzerin:Lisa_Müller'
],
[
new
TitleValue
(
NS_MAIN
,
'Remote_page'
,
''
,
'remotetestiw'
),
'en'
,
'remotetestiw:Remote_page'
],
// non-existent namespace
[
new
TitleValue
(
10000000
,
'Foobar'
,
''
,
''
),
'en'
,
'Special:Badtitle/NS10000000:Foobar'
],
[
new
PageIdentityValue
(
37
,
NS_MAIN
,
'Foo_Bar'
,
PageIdentity
::
LOCAL
),
'en'
,
'Foo_Bar'
],
[
new
PageIdentityValue
(
37
,
NS_USER
,
'Hansi_Maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'User:Hansi_Maier'
],
[
new
PageIdentityValue
(
37
,
NS_USER_TALK
,
'hansi__maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'User_talk:hansi__maier'
],
[
new
PageIdentityValue
(
37
,
NS_USER
,
'Lisa_Müller'
,
PageIdentity
::
LOCAL
),
'de'
,
'Benutzerin:Lisa_Müller'
],
[
new
PageIdentityValue
(
37
,
NS_MAIN
,
'Remote_page'
,
PageIdentity
::
LOCAL
),
'en'
,
'Remote_page'
],
[
new
PageIdentityValue
(
37
,
10000000
,
'Foobar'
,
PageIdentity
::
LOCAL
),
'en'
,
'Special:Badtitle/NS10000000:Foobar'
],
];
}
/**
* @dataProvider provideGetPrefixedDBkey
*/
public
function
testGetPrefixedDBkey
(
$title
,
$lang
,
$expected
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$actual
=
$codec
->
getPrefixedDBkey
(
$title
);
$this
->
assertEquals
(
$expected
,
$actual
);
}
public
static
function
provideGetFullText
()
{
return
[
[
new
TitleValue
(
NS_MAIN
,
'Foo_Bar'
,
''
),
'en'
,
'Foo Bar'
],
[
new
TitleValue
(
NS_USER
,
'Hansi_Maier'
,
'stuff_and_so_on'
),
'en'
,
'User:Hansi Maier#stuff and so on'
],
// No capitalization or normalization is applied while formatting!
[
new
TitleValue
(
NS_USER_TALK
,
'hansi__maier'
,
''
),
'en'
,
'User talk:hansi maier'
],
[
new
TitleValue
(
NS_MAIN
,
'Foo_Bar'
),
'en'
,
'Foo Bar'
],
[
new
TitleValue
(
NS_USER
,
'Hansi_Maier'
),
'en'
,
'User:Hansi Maier'
],
[
new
PageIdentityValue
(
37
,
NS_MAIN
,
'Foo_Bar'
,
PageIdentity
::
LOCAL
),
'en'
,
'Foo Bar'
],
[
new
PageIdentityValue
(
37
,
NS_USER
,
'Hansi_Maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'User:Hansi Maier'
],
[
new
PageIdentityValue
(
37
,
NS_USER_TALK
,
'hansi__maier'
,
PageIdentity
::
LOCAL
),
'en'
,
'User talk:hansi maier'
],
];
}
/**
* @dataProvider provideGetFullText
*/
public
function
testGetFullText
(
$title
,
$lang
,
$expected
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$actual
=
$codec
->
getFullText
(
$title
);
$this
->
assertEquals
(
$expected
,
$actual
);
}
public
static
function
provideParseTitle
()
{
// TODO: test capitalization and trimming
// TODO: test unicode normalization
return
[
[
' : Hansi_Maier _ '
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'Hansi_Maier'
,
''
)
],
[
'User:::1'
,
NS_MAIN
,
'de'
,
new
TitleValue
(
NS_USER
,
'0:0:0:0:0:0:0:1'
,
''
)
],
[
' lisa Müller'
,
NS_USER
,
'de'
,
new
TitleValue
(
NS_USER
,
'Lisa_Müller'
,
''
)
],
[
'benutzerin:lisa Müller#stuff'
,
NS_MAIN
,
'de'
,
new
TitleValue
(
NS_USER
,
'Lisa_Müller'
,
'stuff'
)
],
[
':Category:Quux'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_CATEGORY
,
'Quux'
,
''
)
],
[
'Category:Quux'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_CATEGORY
,
'Quux'
,
''
)
],
[
'Category:Quux'
,
NS_CATEGORY
,
'en'
,
new
TitleValue
(
NS_CATEGORY
,
'Quux'
,
''
)
],
[
'Quux'
,
NS_CATEGORY
,
'en'
,
new
TitleValue
(
NS_CATEGORY
,
'Quux'
,
''
)
],
[
':Quux'
,
NS_CATEGORY
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'Quux'
,
''
)
],
// getGenderCache() provides a mock that considers first
// names ending in "a" to be female.
[
'a b c'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'A_b_c'
)
],
[
' a b c '
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'A_b_c'
)
],
[
' _ Foo __ Bar_ _'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'Foo_Bar'
)
],
// NOTE: cases copied from TitleTest::testSecureAndSplit. Keep in sync.
[
'Sandbox'
,
NS_MAIN
,
'en'
,
],
[
'A "B"'
,
NS_MAIN
,
'en'
,
],
[
'A
\'
B
\'
'
,
NS_MAIN
,
'en'
,
],
[
'.com'
,
NS_MAIN
,
'en'
,
],
[
'~'
,
NS_MAIN
,
'en'
,
],
[
'"'
,
NS_MAIN
,
'en'
,
],
[
'
\'
'
,
NS_MAIN
,
'en'
,
],
[
'Talk:Sandbox'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_TALK
,
'Sandbox'
)
],
[
'Talk:Foo:Sandbox'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_TALK
,
'Foo:Sandbox'
)
],
[
'File:Example.svg'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_FILE
,
'Example.svg'
)
],
[
'File_talk:Example.svg'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_FILE_TALK
,
'Example.svg'
)
],
[
'Foo/.../Sandbox'
,
NS_MAIN
,
'en'
,
'Foo/.../Sandbox'
],
[
'Sandbox/...'
,
NS_MAIN
,
'en'
,
'Sandbox/...'
],
[
'A~~'
,
NS_MAIN
,
'en'
,
'A~~'
],
// Length is 256 total, but only title part matters
[
'Category:'
.
str_repeat
(
'x'
,
248
),
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_CATEGORY
,
'X'
.
str_repeat
(
'x'
,
247
)
)
],
[
str_repeat
(
'x'
,
252
),
NS_MAIN
,
'en'
,
'X'
.
str_repeat
(
'x'
,
251
)
],
// Test decoding and normalization
[
'"ñ"'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'"ñ"'
)
],
[
'X#ñ'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
,
'ñ'
)
],
// target section parsing
'empty fragment'
=>
[
'X#'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
)
],
'only fragment'
=>
[
'#'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
''
)
],
'double hash'
=>
[
'X##'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
,
'#'
)
],
'fragment with hash'
=>
[
'X#z#z'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
,
'z#z'
)
],
'fragment with space'
=>
[
'X#z z'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
,
'z z'
)
],
'fragment with percent'
=>
[
'X#z%z'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
,
'z%z'
)
],
'fragment with amp'
=>
[
'X#z&z'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_MAIN
,
'X'
,
'z&z'
)
],
'remotetestiw in user'
=>
[
'User:remotetestiw:'
,
NS_MAIN
,
'en'
,
new
TitleValue
(
NS_USER
,
'Remotetestiw:'
)
],
];
}
/**
* @dataProvider provideParseTitle
*/
public
function
testParseTitle
(
$text
,
$ns
,
$lang
,
$title
=
null
)
{
if
(
!(
$title
instanceof
TitleValue
)
)
{
$title
??=
str_replace
(
' '
,
'_'
,
trim
(
$text
)
);
$title
=
new
TitleValue
(
NS_MAIN
,
$title
,
''
);
}
$codec
=
$this
->
makeCodec
(
$lang
);
$actual
=
$codec
->
parseTitle
(
$text
,
$ns
);
$this
->
assertEquals
(
$title
,
$actual
);
}
public
static
function
provideParseTitle_invalid
()
{
return
[
[
'User:#'
],
[
'::'
],
[
'::xx'
],
[
'::##'
],
[
' :: x'
],
[
'Talk:File:Foo.jpg'
],
[
'Talk:localtestiw:Foo'
],
[
'::1'
],
// only valid in user namespace
[
'User::x'
],
// leading ":" in a user name is only valid of IPv6 addresses
[
'remotetestiw:'
,
NS_USER
],
// NOTE: cases copied from TitleTest::testSecureAndSplit. Keep in sync.
[
''
],
[
':'
],
[
'__ __'
],
[
' __ '
],
// Bad characters forbidden regardless of wgLegalTitleChars
[
'A [ B'
],
[
'A ] B'
],
[
'A { B'
],
[
'A } B'
],
[
'A < B'
],
[
'A > B'
],
[
'A | B'
],
// URL encoding
[
'A%20B'
],
[
'A%23B'
],
[
'A%2523B'
],
// XML/HTML character entity references
// Note: Commented out because they are not marked invalid by the PHP test as
// Title::newFromText runs Sanitizer::decodeCharReferencesAndNormalize first.
// [ 'A é B' ],
// [ 'A é B' ],
// [ 'A é B' ],
// Subject of NS_TALK does not roundtrip to NS_MAIN
[
'Talk:File:Example.svg'
],
// Directory navigation
[
'.'
],
[
'..'
],
[
'./Sandbox'
],
[
'../Sandbox'
],
[
'Foo/./Sandbox'
],
[
'Foo/../Sandbox'
],
[
'Sandbox/.'
],
[
'Sandbox/..'
],
// Tilde
[
'A ~~~ Name'
],
[
'A ~~~~ Signature'
],
[
'A ~~~~~ Timestamp'
],
[
str_repeat
(
'x'
,
256
)
],
// Namespace prefix without actual title
[
'Talk:'
],
[
'Category: '
],
[
'Category: #bar'
],
// Invalid Unicode
[
"Apollo
\x
96Soyuz"
],
// Input resulting from invalid Unicode being sanitized somewhere else
[
"Apollo
\u
{FFFD}Soyuz"
],
];
}
/**
* @dataProvider provideParseTitle_invalid
*/
public
function
testParseTitle_invalid
(
$text
,
$ns
=
NS_MAIN
)
{
$this
->
expectException
(
MalformedTitleException
::
class
);
$codec
=
$this
->
makeCodec
(
'en'
);
$codec
->
parseTitle
(
$text
,
$ns
);
}
/**
* @dataProvider provideMakeTitleValueSafe
*/
public
function
testMakeTitleValueSafe
(
$expected
,
$ns
,
$text
,
$fragment
=
''
,
$interwiki
=
''
,
$lang
=
'en'
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$this
->
assertEquals
(
$expected
,
$codec
->
makeTitleValueSafe
(
$ns
,
$text
,
$fragment
,
$interwiki
)
);
}
/**
* @dataProvider provideMakeTitleValueSafe
* @covers \MediaWiki\Title\Title::makeTitleSafe
* @covers \MediaWiki\Title\Title::makeName
* @covers \MediaWiki\Title\Title::secureAndSplit
*/
public
function
testMakeTitleSafe
(
$expected
,
$ns
,
$text
,
$fragment
=
''
,
$interwiki
=
''
,
$lang
=
'en'
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$this
->
setService
(
'TitleParser'
,
$codec
);
$this
->
setService
(
'TitleFormatter'
,
$codec
);
$actual
=
Title
::
makeTitleSafe
(
$ns
,
$text
,
$fragment
,
$interwiki
);
if
(
$expected
)
{
$this
->
assertNotNull
(
$actual
);
$expectedTitle
=
Title
::
newFromLinkTarget
(
$expected
);
$this
->
assertSame
(
$expectedTitle
->
getPrefixedDBkey
(),
$actual
->
getPrefixedDBkey
()
);
}
else
{
$this
->
assertNull
(
$actual
);
}
}
public
static
function
provideMakeTitleValueSafe
()
{
$ret
=
[
'Nonexistent NS'
=>
[
null
,
942929
,
'Test'
],
'Linebreak in title'
=>
[
null
,
NS_MAIN
,
"Test
\n
this"
],
'Pipe in title'
=>
[
null
,
NS_MAIN
,
"Test|this"
],
'Simple page'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'Test'
],
// Fragments
'Passed fragment'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
'Fragment'
),
NS_MAIN
,
'Test'
,
'Fragment'
],
'Embedded fragment'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
'Fragment'
),
NS_MAIN
,
'Test#Fragment'
],
'Passed fragment with spaces'
=>
[
// XXX Leading space is okay in fragment?
new
TitleValue
(
NS_MAIN
,
'Test'
,
' Frag ment'
),
NS_MAIN
,
' Test '
,
" Frag_ment "
],
'Embedded fragment with spaces'
=>
[
// XXX Leading space is okay in fragment?
new
TitleValue
(
NS_MAIN
,
'Test'
,
' Frag ment'
),
NS_MAIN
,
" Test # Frag_ment "
],
// XXX Is it correct that these aren't normalized to spaces?
'Passed fragment with leading tab'
=>
[
null
,
NS_MAIN
,
"
\t
Test
\t
"
,
"
\t
Fragment"
],
'Embedded fragment with leading tab'
=>
[
null
,
NS_MAIN
,
"
\t
Test
\t
#
\t
Fragment"
],
'Passed fragment with trailing tab'
=>
[
null
,
NS_MAIN
,
"
\t
Test
\t
"
,
"Fragment
\t
"
],
'Embedded fragment with trailing tab'
=>
[
null
,
NS_MAIN
,
"
\t
Test
\t
#Fragment
\t
"
],
'Passed fragment with interior tab'
=>
[
null
,
NS_MAIN
,
"
\t
Test
\t
"
,
"Frag
\t
ment"
],
'Embedded fragment with interior tab'
=>
[
null
,
NS_MAIN
,
"
\t
Test
\t
#
\t
Frag
\t
ment"
],
// Interwikis
'Passed local interwiki'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'Test'
,
''
,
'localtestiw'
],
'Embedded local interwiki'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'localtestiw:Test'
],
'Passed remote interwiki'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
],
'Embedded remote interwiki'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'remotetestiw:Test'
],
// Interwiki prefixes are not case sensitive
'Passed local interwiki with different case'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'Test'
,
''
,
'LocalTestIW'
],
'Embedded local interwiki with different case'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'LocalTestIW:Test'
],
'Passed remote interwiki with different case'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'Test'
,
''
,
'RemoteTestIW'
],
'Embedded remote interwiki with different case'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'RemoteTestIW:Test'
],
'Passed local interwiki with lowercase page name'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'test'
,
''
,
'localtestiw'
],
'Embedded local interwiki with lowercase page name'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
'localtestiw:test'
],
// For remote we don't auto-capitalize
'Passed remote interwiki with lowercase page name'
=>
[
new
TitleValue
(
NS_MAIN
,
'test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'test'
,
''
,
'remotetestiw'
],
'Embedded remote interwiki with lowercase page name'
=>
[
new
TitleValue
(
NS_MAIN
,
'test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'remotetestiw:test'
],
// Fragment and interwiki
'Fragment and local interwiki'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
'Fragment'
),
NS_MAIN
,
'Test'
,
'Fragment'
,
'localtestiw'
],
'Fragment and remote interwiki'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
'Fragment'
,
'remotetestiw'
),
NS_MAIN
,
'Test'
,
'Fragment'
,
'remotetestiw'
],
'Fragment and local interwiki and non-main namespace'
=>
[
new
TitleValue
(
NS_TALK
,
'Test'
,
'Fragment'
),
NS_TALK
,
'Test'
,
'Fragment'
,
'localtestiw'
],
// We don't know the foreign wiki's namespaces, so it will always be NS_MAIN
'Fragment and remote interwiki and non-main namespace'
=>
[
new
TitleValue
(
NS_MAIN
,
'Talk:Test'
,
'Fragment'
,
'remotetestiw'
),
NS_TALK
,
'Test'
,
'Fragment'
,
'remotetestiw'
],
// Whitespace normalization and Unicode stripping
'Name with space'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test_test'
),
NS_MAIN
,
'Test test'
],
'Unicode bidi override characters'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
"
\u
{200E}T
\u
{200F}e
\u
{202A}s
\u
{202B}t
\u
{202C}
\u
{202D}
\u
{202E}"
],
'Invalid UTF-8 sequence'
=>
[
null
,
NS_MAIN
,
"Te
\x
80
\x
f0st"
],
'Whitespace collapsing'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test_test'
),
NS_MAIN
,
"Test _
\u
{00A0}
\u
{1680}
\u
{180E}
\u
{2000}
\u
{2001}
\u
{2002}
\u
{2003}
\u
{2004}"
.
"
\u
{2005}
\u
{2006}
\u
{2007}
\u
{2008}
\u
{2009}
\u
{200A}
\u
{2028}
\u
{2029}
\u
{202F}"
.
"
\u
{205F}
\u
{3000}test"
],
'UTF8_REPLACEMENT'
=>
[
null
,
NS_MAIN
,
UtfNormal\Constants
::
UTF8_REPLACEMENT
],
// Namespace prefixes
'Talk:Test'
=>
[
new
TitleValue
(
NS_TALK
,
'Test'
),
NS_MAIN
,
'Talk:Test'
],
'Test in talk NS'
=>
[
new
TitleValue
(
NS_TALK
,
'Test'
),
NS_TALK
,
'Test'
],
'Talkk:Test'
=>
[
new
TitleValue
(
NS_MAIN
,
'Talkk:Test'
),
NS_MAIN
,
'Talkk:Test'
],
'Talk:Talk:Test'
=>
[
null
,
NS_MAIN
,
'Talk:Talk:Test'
],
'Talk:User:Test'
=>
[
null
,
NS_MAIN
,
'Talk:User:Test'
],
'User:Talk:Test'
=>
[
new
TitleValue
(
NS_USER
,
'Talk:Test'
),
NS_MAIN
,
'User:Talk:Test'
],
'User:Test in talk NS'
=>
[
null
,
NS_TALK
,
'User:Test'
],
'Talk:Test in talk NS'
=>
[
null
,
NS_TALK
,
'Talk:Test'
],
'User:Test in user NS'
=>
[
new
TitleValue
(
NS_USER
,
'User:Test'
),
NS_USER
,
'User:Test'
],
'Talk:Test in user NS'
=>
[
new
TitleValue
(
NS_USER
,
'Talk:Test'
),
NS_USER
,
'Talk:Test'
],
// Initial colon
':Test'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
':Test'
],
':Talk:Test'
=>
[
new
TitleValue
(
NS_TALK
,
'Test'
),
NS_MAIN
,
':Talk:Test'
],
':localtestiw:Test'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
),
NS_MAIN
,
':localtestiw:Test'
],
':remotetestiw:Test'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
':remotetestiw:Test'
],
// XXX Is this correct? Why is it different from remote?
'localtestiw::Test'
=>
[
null
,
NS_MAIN
,
'localtestiw::Test'
],
'remotetestiw::Test'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'remotetestiw::Test'
],
// XXX Is this correct? Why is it different from remote?
'localtestiw:: Test'
=>
[
null
,
NS_MAIN
,
'localtestiw:: Test'
],
'remotetestiw:: Test'
=>
[
new
TitleValue
(
NS_MAIN
,
'Test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'remotetestiw:: Test'
],
// Empty titles
'Empty title'
=>
[
null
,
NS_MAIN
,
''
],
'Empty title with namespace'
=>
[
null
,
NS_USER
,
''
],
'Local interwiki with empty page name'
=>
[
new
TitleValue
(
NS_MAIN
,
'Main_Page'
),
NS_MAIN
,
'localtestiw:'
],
'Remote interwiki with empty page name'
=>
[
// XXX Is this correct? This is supposed to redirect to the main page remotely?
new
TitleValue
(
NS_MAIN
,
''
,
''
,
'remotetestiw'
),
NS_MAIN
,
'remotetestiw:'
],
// Whitespace-only titles
'Whitespace-only title'
=>
[
null
,
NS_MAIN
,
"
\t\n
"
],
'Whitespace-only title with namespace'
=>
[
null
,
NS_USER
,
" _ "
],
'Local interwiki with whitespace-only page name'
=>
[
// XXX Is whitespace-only really supposed to be different from empty?
null
,
NS_MAIN
,
"localtestiw:_
\t
"
],
'Remote interwiki with whitespace-only page name'
=>
[
// XXX Is whitespace-only really supposed to be different from empty?
null
,
NS_MAIN
,
"remotetestiw:
\t
_
\n\r
"
],
// Namespace and interwiki
'Talk:localtestiw:Test'
=>
[
null
,
NS_MAIN
,
'Talk:localtestiw:Test'
],
'Talk:remotetestiw:Test'
=>
[
null
,
NS_MAIN
,
'Talk:remotetestiw:Test'
],
'User:localtestiw:Test'
=>
[
new
TitleValue
(
NS_USER
,
'Localtestiw:Test'
),
NS_MAIN
,
'User:localtestiw:Test'
],
'User:remotetestiw:Test'
=>
[
new
TitleValue
(
NS_USER
,
'Remotetestiw:Test'
),
NS_MAIN
,
'User:remotetestiw:Test'
],
'localtestiw:Test in user namespace'
=>
[
new
TitleValue
(
NS_USER
,
'Localtestiw:Test'
),
NS_USER
,
'localtestiw:Test'
],
'remotetestiw:Test in user namespace'
=>
[
new
TitleValue
(
NS_USER
,
'Remotetestiw:Test'
),
NS_USER
,
'remotetestiw:Test'
],
'localtestiw:talk:test'
=>
[
new
TitleValue
(
NS_TALK
,
'Test'
),
NS_MAIN
,
'localtestiw:talk:test'
],
'remotetestiw:talk:test'
=>
[
new
TitleValue
(
NS_MAIN
,
'talk:test'
,
''
,
'remotetestiw'
),
NS_MAIN
,
'remotetestiw:talk:test'
],
// Invalid chars
'Test[test'
=>
[
null
,
NS_MAIN
,
'Test[test'
],
// Long titles
'255 chars long'
=>
[
new
TitleValue
(
NS_MAIN
,
str_repeat
(
'A'
,
255
)
),
NS_MAIN
,
str_repeat
(
'A'
,
255
)
],
'255 chars long in user NS'
=>
[
new
TitleValue
(
NS_USER
,
str_repeat
(
'A'
,
255
)
),
NS_USER
,
str_repeat
(
'A'
,
255
)
],
'User:255 chars long'
=>
[
new
TitleValue
(
NS_USER
,
str_repeat
(
'A'
,
255
)
),
NS_MAIN
,
'User:'
.
str_repeat
(
'A'
,
255
)
],
'256 chars long'
=>
[
null
,
NS_MAIN
,
str_repeat
(
'A'
,
256
)
],
'256 chars long in user NS'
=>
[
null
,
NS_USER
,
str_repeat
(
'A'
,
256
)
],
'User:256 chars long'
=>
[
null
,
NS_MAIN
,
'User:'
.
str_repeat
(
'A'
,
256
)
],
'512 chars long in special NS'
=>
[
new
TitleValue
(
NS_SPECIAL
,
str_repeat
(
'A'
,
512
)
),
NS_SPECIAL
,
str_repeat
(
'A'
,
512
)
],
'Special:512 chars long'
=>
[
new
TitleValue
(
NS_SPECIAL
,
str_repeat
(
'A'
,
512
)
),
NS_MAIN
,
'Special:'
.
str_repeat
(
'A'
,
512
)
],
'513 chars long in special NS'
=>
[
null
,
NS_SPECIAL
,
str_repeat
(
'A'
,
513
)
],
'Special:513 chars long'
=>
[
null
,
NS_MAIN
,
'Special:'
.
str_repeat
(
'A'
,
513
)
],
// IP addresses
'User:000.000.000'
=>
[
new
TitleValue
(
NS_USER
,
'000.000.000'
),
NS_MAIN
,
'User:000.000.000'
],
'User:000.000.000.000'
=>
[
new
TitleValue
(
NS_USER
,
'0.0.0.0'
),
NS_MAIN
,
'User:000.000.000.000'
],
'000.000.000.000'
=>
[
new
TitleValue
(
NS_MAIN
,
'000.000.000.000'
),
NS_MAIN
,
'000.000.000.000'
],
'User:1.1.256.000'
=>
[
new
TitleValue
(
NS_USER
,
'1.1.256.000'
),
NS_MAIN
,
'User:1.1.256.000'
],
'User:1.1.255.000'
=>
[
new
TitleValue
(
NS_USER
,
'1.1.255.0'
),
NS_MAIN
,
'User:1.1.255.000'
],
// TODO More IP address sanitization tests
];
// Invalid and valid dots
foreach
(
[
'.'
,
'..'
,
'...'
]
as
$dots
)
{
foreach
(
[
'?'
,
'?/'
,
'?/Test'
,
'Test/?/Test'
,
'/?'
,
'Test/?'
,
'?Test'
,
'Test?Test'
,
'Test?'
]
as
$pattern
)
{
$test
=
str_replace
(
'?'
,
$dots
,
$pattern
);
if
(
$dots
===
'...'
||
in_array
(
$pattern
,
[
'?Test'
,
'Test?Test'
,
'Test?'
]
)
)
{
$expectedMain
=
new
TitleValue
(
NS_MAIN
,
$test
);
$expectedUser
=
new
TitleValue
(
NS_USER
,
$test
);
}
else
{
$expectedMain
=
$expectedUser
=
null
;
}
$ret
[
$test
]
=
[
$expectedMain
,
NS_MAIN
,
$test
];
$ret
[
"$test in user NS"
]
=
[
$expectedUser
,
NS_USER
,
$test
];
$ret
[
"User:$test"
]
=
[
$expectedUser
,
NS_MAIN
,
"User:$test"
];
}
}
// Invalid and valid tildes
foreach
(
[
'~~'
,
'~~~'
]
as
$tildes
)
{
foreach
(
[
'?'
,
'Test?'
,
'?Test'
,
'Test?Test'
]
as
$pattern
)
{
$test
=
str_replace
(
'?'
,
$tildes
,
$pattern
);
if
(
$tildes
===
'~~'
)
{
$expectedMain
=
new
TitleValue
(
NS_MAIN
,
$test
);
$expectedUser
=
new
TitleValue
(
NS_USER
,
$test
);
}
else
{
$expectedMain
=
$expectedUser
=
null
;
}
$ret
[
$test
]
=
[
$expectedMain
,
NS_MAIN
,
$test
];
$ret
[
"$test in user NS"
]
=
[
$expectedUser
,
NS_USER
,
$test
];
$ret
[
"User:$test"
]
=
[
$expectedUser
,
NS_MAIN
,
"User:$test"
];
}
}
return
$ret
;
}
public
static
function
provideGetNamespaceName
()
{
return
[
[
NS_MAIN
,
'Foo'
,
'en'
,
''
],
[
NS_USER
,
'Foo'
,
'en'
,
'User'
],
[
NS_USER
,
'Hansi Maier'
,
'de'
,
'Benutzer'
],
// getGenderCache() provides a mock that considers first
// names ending in "a" to be female.
[
NS_USER
,
'Lisa Müller'
,
'de'
,
'Benutzerin'
],
];
}
/**
* @dataProvider provideGetNamespaceName
*/
public
function
testGetNamespaceName
(
$namespace
,
$text
,
$lang
,
$expected
)
{
$codec
=
$this
->
makeCodec
(
$lang
);
$name
=
$codec
->
getNamespaceName
(
$namespace
,
$text
);
$this
->
assertEquals
(
$expected
,
$name
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Oct 3, 20:51 (4 d, 18 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ca/a0/b1b449de1a6b6477535bcfe26a25
Default Alt Text
MediaWikiTitleCodecTest.php (28 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment