MediaWiki
REL1_23
|
00001 <?php 00030 class MediaWikiTitleCodecTest extends MediaWikiTestCase { 00031 00032 public function setUp() { 00033 parent::setUp(); 00034 00035 $this->setMwGlobals( array( 00036 'wgLanguageCode' => 'en', 00037 'wgContLang' => Language::factory( 'en' ), 00038 // User language 00039 'wgLang' => Language::factory( 'en' ), 00040 'wgAllowUserJs' => false, 00041 'wgDefaultLanguageVariant' => false, 00042 'wgLocalInterwikis' => array( 'localtestiw' ), 00043 00044 // NOTE: this is why global state is evil. 00045 // TODO: refactor access to the interwiki codes so it can be injected. 00046 'wgHooks' => array( 00047 'InterwikiLoadPrefix' => array( 00048 function ( $prefix, &$data ) { 00049 if ( $prefix === 'localtestiw' ) { 00050 $data = array( 'iw_url' => 'localtestiw' ); 00051 } elseif ( $prefix === 'remotetestiw' ) { 00052 $data = array( 'iw_url' => 'remotetestiw' ); 00053 } 00054 return false; 00055 } 00056 ) 00057 ) 00058 ) ); 00059 } 00060 00067 private function getGenderCache() { 00068 $genderCache = $this->getMockBuilder( 'GenderCache' ) 00069 ->disableOriginalConstructor() 00070 ->getMock(); 00071 00072 $genderCache->expects( $this->any() ) 00073 ->method( 'getGenderOf' ) 00074 ->will( $this->returnCallback( function( $userName ) { 00075 return preg_match( '/^[^- _]+a( |_|$)/u', $userName ) ? 'female' : 'male'; 00076 } ) ); 00077 00078 return $genderCache; 00079 } 00080 00081 protected function makeCodec( $lang ) { 00082 $gender = $this->getGenderCache(); 00083 $lang = Language::factory( $lang ); 00084 return new MediaWikiTitleCodec( $lang, $gender ); 00085 } 00086 00087 public function provideFormat() { 00088 return array( 00089 array( NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ), 00090 array( NS_USER, 'Hansi_Maier', 'stuff_and_so_on', 'en', 'User:Hansi Maier#stuff and so on' ), 00091 array( false, 'Hansi_Maier', '', 'en', 'Hansi Maier' ), 00092 array( NS_USER_TALK, 'hansi__maier', '', 'en', 'User talk:hansi maier', 'User talk:Hansi maier' ), 00093 00094 // getGenderCache() provides a mock that considers first 00095 // names ending in "a" to be female. 00096 array( NS_USER, 'Lisa_Müller', '', 'de', 'Benutzerin:Lisa Müller' ), 00097 ); 00098 } 00099 00103 public function testFormat( $namespace, $text, $fragment, $lang, $expected, $normalized = null ) { 00104 if ( $normalized === null ) { 00105 $normalized = $expected; 00106 } 00107 00108 $codec = $this->makeCodec( $lang ); 00109 $actual = $codec->formatTitle( $namespace, $text, $fragment ); 00110 00111 $this->assertEquals( $expected, $actual, 'formatted' ); 00112 00113 // test round trip 00114 $parsed = $codec->parseTitle( $actual, NS_MAIN ); 00115 $actual2 = $codec->formatTitle( $parsed->getNamespace(), $parsed->getText(), $parsed->getFragment() ); 00116 00117 $this->assertEquals( $normalized, $actual2, 'normalized after round trip' ); 00118 } 00119 00120 public function provideGetText() { 00121 return array( 00122 array( NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ), 00123 array( NS_USER, 'Hansi_Maier', 'stuff_and_so_on', 'en', 'Hansi Maier' ), 00124 ); 00125 } 00126 00130 public function testGetText( $namespace, $dbkey, $fragment, $lang, $expected ) { 00131 $codec = $this->makeCodec( $lang ); 00132 $title = new TitleValue( $namespace, $dbkey, $fragment ); 00133 00134 $actual = $codec->getText( $title ); 00135 00136 $this->assertEquals( $expected, $actual ); 00137 } 00138 00139 public function provideGetPrefixedText() { 00140 return array( 00141 array( NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ), 00142 array( NS_USER, 'Hansi_Maier', 'stuff_and_so_on', 'en', 'User:Hansi Maier' ), 00143 00144 // No capitalization or normalization is applied while formatting! 00145 array( NS_USER_TALK, 'hansi__maier', '', 'en', 'User talk:hansi maier' ), 00146 00147 // getGenderCache() provides a mock that considers first 00148 // names ending in "a" to be female. 00149 array( NS_USER, 'Lisa_Müller', '', 'de', 'Benutzerin:Lisa Müller' ), 00150 ); 00151 } 00152 00156 public function testGetPrefixedText( $namespace, $dbkey, $fragment, $lang, $expected ) { 00157 $codec = $this->makeCodec( $lang ); 00158 $title = new TitleValue( $namespace, $dbkey, $fragment ); 00159 00160 $actual = $codec->getPrefixedText( $title ); 00161 00162 $this->assertEquals( $expected, $actual ); 00163 } 00164 00165 public function provideGetFullText() { 00166 return array( 00167 array( NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ), 00168 array( NS_USER, 'Hansi_Maier', 'stuff_and_so_on', 'en', 'User:Hansi Maier#stuff and so on' ), 00169 00170 // No capitalization or normalization is applied while formatting! 00171 array( NS_USER_TALK, 'hansi__maier', '', 'en', 'User talk:hansi maier' ), 00172 ); 00173 } 00174 00178 public function testGetFullText( $namespace, $dbkey, $fragment, $lang, $expected ) { 00179 $codec = $this->makeCodec( $lang ); 00180 $title = new TitleValue( $namespace, $dbkey, $fragment ); 00181 00182 $actual = $codec->getFullText( $title ); 00183 00184 $this->assertEquals( $expected, $actual ); 00185 } 00186 00187 public function provideParseTitle() { 00188 //TODO: test capitalization and trimming 00189 //TODO: test unicode normalization 00190 00191 return array( 00192 array( ' : Hansi_Maier _ ', NS_MAIN, 'en', 00193 new TitleValue( NS_MAIN, 'Hansi_Maier', '' ) ), 00194 array( 'User:::1', NS_MAIN, 'de', 00195 new TitleValue( NS_USER, '0:0:0:0:0:0:0:1', '' ) ), 00196 array( ' lisa Müller', NS_USER, 'de', 00197 new TitleValue( NS_USER, 'Lisa_Müller', '' ) ), 00198 array( 'benutzerin:lisa Müller#stuff', NS_MAIN, 'de', 00199 new TitleValue( NS_USER, 'Lisa_Müller', 'stuff' ) ), 00200 00201 array( ':Category:Quux', NS_MAIN, 'en', 00202 new TitleValue( NS_CATEGORY, 'Quux', '' ) ), 00203 array( 'Category:Quux', NS_MAIN, 'en', 00204 new TitleValue( NS_CATEGORY, 'Quux', '' ) ), 00205 array( 'Category:Quux', NS_CATEGORY, 'en', 00206 new TitleValue( NS_CATEGORY, 'Quux', '' ) ), 00207 array( 'Quux', NS_CATEGORY, 'en', 00208 new TitleValue( NS_CATEGORY, 'Quux', '' ) ), 00209 array( ':Quux', NS_CATEGORY, 'en', 00210 new TitleValue( NS_MAIN, 'Quux', '' ) ), 00211 00212 // getGenderCache() provides a mock that considers first 00213 // names ending in "a" to be female. 00214 00215 array( 'a b c', NS_MAIN, 'en', 00216 new TitleValue( NS_MAIN, 'A_b_c' ) ), 00217 array( ' a b c ', NS_MAIN, 'en', 00218 new TitleValue( NS_MAIN, 'A_b_c' ) ), 00219 array( ' _ Foo __ Bar_ _', NS_MAIN, 'en', 00220 new TitleValue( NS_MAIN, 'Foo_Bar' ) ), 00221 00222 //NOTE: cases copied from TitleTest::testSecureAndSplit. Keep in sync. 00223 array( 'Sandbox', NS_MAIN, 'en', ), 00224 array( 'A "B"', NS_MAIN, 'en', ), 00225 array( 'A \'B\'', NS_MAIN, 'en', ), 00226 array( '.com', NS_MAIN, 'en', ), 00227 array( '~', NS_MAIN, 'en', ), 00228 array( '"', NS_MAIN, 'en', ), 00229 array( '\'', NS_MAIN, 'en', ), 00230 00231 array( 'Talk:Sandbox', NS_MAIN, 'en', 00232 new TitleValue( NS_TALK, 'Sandbox' ) ), 00233 array( 'Talk:Foo:Sandbox', NS_MAIN, 'en', 00234 new TitleValue( NS_TALK, 'Foo:Sandbox' ) ), 00235 array( 'File:Example.svg', NS_MAIN, 'en', 00236 new TitleValue( NS_FILE, 'Example.svg' ) ), 00237 array( 'File_talk:Example.svg', NS_MAIN, 'en', 00238 new TitleValue( NS_FILE_TALK, 'Example.svg' ) ), 00239 array( 'Foo/.../Sandbox', NS_MAIN, 'en', 00240 'Foo/.../Sandbox' ), 00241 array( 'Sandbox/...', NS_MAIN, 'en', 00242 'Sandbox/...' ), 00243 array( 'A~~', NS_MAIN, 'en', 00244 'A~~' ), 00245 // Length is 256 total, but only title part matters 00246 array( 'Category:' . str_repeat( 'x', 248 ), NS_MAIN, 'en', 00247 new TitleValue( NS_CATEGORY, 00248 'X' . str_repeat( 'x', 247 ) ) ), 00249 array( str_repeat( 'x', 252 ), NS_MAIN, 'en', 00250 'X' . str_repeat( 'x', 251 ) ) 00251 ); 00252 } 00253 00257 public function testParseTitle( $text, $ns, $lang, $title = null ) { 00258 if ( $title === null ) { 00259 $title = str_replace( ' ', '_', trim( $text ) ); 00260 } 00261 00262 if ( is_string( $title ) ) { 00263 $title = new TitleValue( NS_MAIN, $title, '' ); 00264 } 00265 00266 $codec = $this->makeCodec( $lang ); 00267 $actual = $codec->parseTitle( $text, $ns ); 00268 00269 $this->assertEquals( $title, $actual ); 00270 } 00271 00272 public function provideParseTitle_invalid() { 00273 //TODO: test unicode errors 00274 00275 return array( 00276 array( '#' ), 00277 array( '::' ), 00278 array( '::xx' ), 00279 array( '::##' ), 00280 array( ' :: x' ), 00281 00282 array( 'Talk:File:Foo.jpg' ), 00283 array( 'Talk:localtestiw:Foo' ), 00284 array( 'remotetestiw:Foo' ), 00285 array( '::1' ), // only valid in user namespace 00286 array( 'User::x' ), // leading ":" in a user name is only valid of IPv6 addresses 00287 00288 //NOTE: cases copied from TitleTest::testSecureAndSplit. Keep in sync. 00289 array( '' ), 00290 array( ':' ), 00291 array( '__ __' ), 00292 array( ' __ ' ), 00293 // Bad characters forbidden regardless of wgLegalTitleChars 00294 array( 'A [ B' ), 00295 array( 'A ] B' ), 00296 array( 'A { B' ), 00297 array( 'A } B' ), 00298 array( 'A < B' ), 00299 array( 'A > B' ), 00300 array( 'A | B' ), 00301 // URL encoding 00302 array( 'A%20B' ), 00303 array( 'A%23B' ), 00304 array( 'A%2523B' ), 00305 // XML/HTML character entity references 00306 // Note: Commented out because they are not marked invalid by the PHP test as 00307 // Title::newFromText runs Sanitizer::decodeCharReferencesAndNormalize first. 00308 //array( 'A é B' ), 00309 //array( 'A é B' ), 00310 //array( 'A é B' ), 00311 // Subject of NS_TALK does not roundtrip to NS_MAIN 00312 array( 'Talk:File:Example.svg' ), 00313 // Directory navigation 00314 array( '.' ), 00315 array( '..' ), 00316 array( './Sandbox' ), 00317 array( '../Sandbox' ), 00318 array( 'Foo/./Sandbox' ), 00319 array( 'Foo/../Sandbox' ), 00320 array( 'Sandbox/.' ), 00321 array( 'Sandbox/..' ), 00322 // Tilde 00323 array( 'A ~~~ Name' ), 00324 array( 'A ~~~~ Signature' ), 00325 array( 'A ~~~~~ Timestamp' ), 00326 array( str_repeat( 'x', 256 ) ), 00327 // Namespace prefix without actual title 00328 array( 'Talk:' ), 00329 array( 'Category: ' ), 00330 array( 'Category: #bar' ) 00331 ); 00332 } 00333 00337 public function testParseTitle_invalid( $text ) { 00338 $this->setExpectedException( 'MalformedTitleException' ); 00339 00340 $codec = $this->makeCodec( 'en' ); 00341 $codec->parseTitle( $text, NS_MAIN ); 00342 } 00343 00344 public function provideGetNamespaceName() { 00345 return array( 00346 array( NS_MAIN, 'Foo', 'en', '' ), 00347 array( NS_USER, 'Foo', 'en', 'User' ), 00348 array( NS_USER, 'Hansi Maier', 'de', 'Benutzer' ), 00349 00350 // getGenderCache() provides a mock that considers first 00351 // names ending in "a" to be female. 00352 array( NS_USER, 'Lisa Müller', 'de', 'Benutzerin' ), 00353 ); 00354 } 00355 00366 public function testGetNamespaceName( $namespace, $text, $lang, $expected ) { 00367 $codec = $this->makeCodec( $lang ); 00368 $name = $codec->getNamespaceName( $namespace, $text ); 00369 00370 $this->assertEquals( $expected, $name ); 00371 } 00372 }