MediaWiki
REL1_22
|
00001 <?php 00002 00007 class TitleTest extends MediaWikiTestCase { 00008 protected function setUp() { 00009 parent::setUp(); 00010 00011 $this->setMwGlobals( array( 00012 'wgLanguageCode' => 'en', 00013 'wgContLang' => Language::factory( 'en' ), 00014 // User language 00015 'wgLang' => Language::factory( 'en' ), 00016 'wgAllowUserJs' => false, 00017 'wgDefaultLanguageVariant' => false, 00018 ) ); 00019 } 00020 00024 public function testLegalChars() { 00025 $titlechars = Title::legalChars(); 00026 00027 foreach ( range( 1, 255 ) as $num ) { 00028 $chr = chr( $num ); 00029 if ( strpos( "#[]{}<>|", $chr ) !== false || preg_match( "/[\\x00-\\x1f\\x7f]/", $chr ) ) { 00030 $this->assertFalse( (bool)preg_match( "/[$titlechars]/", $chr ), "chr($num) = $chr is not a valid titlechar" ); 00031 } else { 00032 $this->assertTrue( (bool)preg_match( "/[$titlechars]/", $chr ), "chr($num) = $chr is a valid titlechar" ); 00033 } 00034 } 00035 } 00036 00042 public function testSecureAndSplit() { 00043 // Valid 00044 foreach ( array( 00045 'Sandbox', 00046 'A "B"', 00047 'A \'B\'', 00048 '.com', 00049 '~', 00050 '"', 00051 '\'', 00052 'Talk:Sandbox', 00053 'Talk:Foo:Sandbox', 00054 'File:Example.svg', 00055 'File_talk:Example.svg', 00056 'Foo/.../Sandbox', 00057 'Sandbox/...', 00058 'A~~', 00059 // Length is 256 total, but only title part matters 00060 'Category:' . str_repeat( 'x', 248 ), 00061 str_repeat( 'x', 252 ) 00062 ) as $text ) { 00063 $this->assertInstanceOf( 'Title', Title::newFromText( $text ), "Valid: $text" ); 00064 } 00065 00066 // Invalid 00067 foreach ( array( 00068 '', 00069 '__ __', 00070 ' __ ', 00071 // Bad characters forbidden regardless of wgLegalTitleChars 00072 'A [ B', 00073 'A ] B', 00074 'A { B', 00075 'A } B', 00076 'A < B', 00077 'A > B', 00078 'A | B', 00079 // URL encoding 00080 'A%20B', 00081 'A%23B', 00082 'A%2523B', 00083 // XML/HTML character entity references 00084 // Note: Commented out because they are not marked invalid by the PHP test as 00085 // Title::newFromText runs Sanitizer::decodeCharReferencesAndNormalize first. 00086 //'A é B', 00087 //'A é B', 00088 //'A é B', 00089 // Subject of NS_TALK does not roundtrip to NS_MAIN 00090 'Talk:File:Example.svg', 00091 // Directory navigation 00092 '.', 00093 '..', 00094 './Sandbox', 00095 '../Sandbox', 00096 'Foo/./Sandbox', 00097 'Foo/../Sandbox', 00098 'Sandbox/.', 00099 'Sandbox/..', 00100 // Tilde 00101 'A ~~~ Name', 00102 'A ~~~~ Signature', 00103 'A ~~~~~ Timestamp', 00104 str_repeat( 'x', 256 ), 00105 // Namespace prefix without actual title 00106 // ':', // bug 54044 00107 'Talk:', 00108 'Category: ', 00109 'Category: #bar' 00110 ) as $text ) { 00111 $this->assertNull( Title::newFromText( $text ), "Invalid: $text" ); 00112 } 00113 } 00114 00115 public static function provideConvertByteClassToUnicodeClass() { 00116 return array( 00117 array( 00118 ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 00119 ' %!"$&\'()*,\\-./0-9:;=?@A-Z\\\\\\^_`a-z~+\\u0080-\\uFFFF', 00120 ), 00121 array( 00122 'QWERTYf-\\xFF+', 00123 'QWERTYf-\\x7F+\\u0080-\\uFFFF', 00124 ), 00125 array( 00126 'QWERTY\\x66-\\xFD+', 00127 'QWERTYf-\\x7F+\\u0080-\\uFFFF', 00128 ), 00129 array( 00130 'QWERTYf-y+', 00131 'QWERTYf-y+', 00132 ), 00133 array( 00134 'QWERTYf-\\x80+', 00135 'QWERTYf-\\x7F+\\u0080-\\uFFFF', 00136 ), 00137 array( 00138 'QWERTY\\x66-\\x80+\\x23', 00139 'QWERTYf-\\x7F+#\\u0080-\\uFFFF', 00140 ), 00141 array( 00142 'QWERTY\\x66-\\x80+\\xD3', 00143 'QWERTYf-\\x7F+\\u0080-\\uFFFF', 00144 ), 00145 array( 00146 '\\\\\\x99', 00147 '\\\\\\u0080-\\uFFFF', 00148 ), 00149 array( 00150 '-\\x99', 00151 '\\-\\u0080-\\uFFFF', 00152 ), 00153 array( 00154 'QWERTY\\-\\x99', 00155 'QWERTY\\-\\u0080-\\uFFFF', 00156 ), 00157 array( 00158 '\\\\x99', 00159 '\\\\x99', 00160 ), 00161 array( 00162 'A-\\x9F', 00163 'A-\\x7F\\u0080-\\uFFFF', 00164 ), 00165 array( 00166 '\\x66-\\x77QWERTY\\x88-\\x91FXZ', 00167 'f-wQWERTYFXZ\\u0080-\\uFFFF', 00168 ), 00169 array( 00170 '\\x66-\\x99QWERTY\\xAA-\\xEEFXZ', 00171 'f-\\x7FQWERTYFXZ\\u0080-\\uFFFF', 00172 ), 00173 ); 00174 } 00175 00180 public function testConvertByteClassToUnicodeClass( $byteClass, $unicodeClass ) { 00181 $this->assertEquals( $unicodeClass, Title::convertByteClassToUnicodeClass( $byteClass ) ); 00182 } 00183 00188 public function testBug31100FixSpecialName( $text, $expectedParam ) { 00189 $title = Title::newFromText( $text ); 00190 $fixed = $title->fixSpecialName(); 00191 $stuff = explode( '/', $fixed->getDBkey(), 2 ); 00192 if ( count( $stuff ) == 2 ) { 00193 $par = $stuff[1]; 00194 } else { 00195 $par = null; 00196 } 00197 $this->assertEquals( $expectedParam, $par, "Bug 31100 regression check: Title->fixSpecialName() should preserve parameter" ); 00198 } 00199 00200 public static function provideBug31100() { 00201 return array( 00202 array( 'Special:Version', null ), 00203 array( 'Special:Version/', '' ), 00204 array( 'Special:Version/param', 'param' ), 00205 ); 00206 } 00207 00218 public function testIsValidMoveOperation( $source, $target, $expected ) { 00219 $title = Title::newFromText( $source ); 00220 $nt = Title::newFromText( $target ); 00221 $errors = $title->isValidMoveOperation( $nt, false ); 00222 if ( $expected === true ) { 00223 $this->assertTrue( $errors ); 00224 } else { 00225 $errors = $this->flattenErrorsArray( $errors ); 00226 foreach ( (array)$expected as $error ) { 00227 $this->assertContains( $error, $errors ); 00228 } 00229 } 00230 } 00231 00235 public function dataTestIsValidMoveOperation() { 00236 return array( 00237 array( 'Test', 'Test', 'selfmove' ), 00238 array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' ) 00239 ); 00240 } 00241 00253 public function testWgWhitelistReadRegexp( $whitelistRegexp, $source, $action, $expected ) { 00254 // $wgWhitelistReadRegexp must be an array. Since the provided test cases 00255 // usually have only one regex, it is more concise to write the lonely regex 00256 // as a string. Thus we cast to an array() to honor $wgWhitelistReadRegexp 00257 // type requisite. 00258 if ( is_string( $whitelistRegexp ) ) { 00259 $whitelistRegexp = array( $whitelistRegexp ); 00260 } 00261 00262 $title = Title::newFromDBkey( $source ); 00263 00264 global $wgGroupPermissions; 00265 $oldPermissions = $wgGroupPermissions; 00266 // Disallow all so we can ensure our regex works 00267 $wgGroupPermissions = array(); 00268 $wgGroupPermissions['*']['read'] = false; 00269 00270 global $wgWhitelistRead; 00271 $oldWhitelist = $wgWhitelistRead; 00272 // Undo any LocalSettings explicite whitelists so they won't cause a 00273 // failing test to succeed. Set it to some random non sense just 00274 // to make sure we properly test Title::checkReadPermissions() 00275 $wgWhitelistRead = array( 'some random non sense title' ); 00276 00277 global $wgWhitelistReadRegexp; 00278 $oldWhitelistRegexp = $wgWhitelistReadRegexp; 00279 $wgWhitelistReadRegexp = $whitelistRegexp; 00280 00281 // Just use $wgUser which in test is a user object for '127.0.0.1' 00282 global $wgUser; 00283 // Invalidate user rights cache to take in account $wgGroupPermissions 00284 // change above. 00285 $wgUser->clearInstanceCache(); 00286 $errors = $title->userCan( $action, $wgUser ); 00287 00288 // Restore globals 00289 $wgGroupPermissions = $oldPermissions; 00290 $wgWhitelistRead = $oldWhitelist; 00291 $wgWhitelistReadRegexp = $oldWhitelistRegexp; 00292 00293 if ( is_bool( $expected ) ) { 00294 # Forge the assertion message depending on the assertion expectation 00295 $allowableness = $expected 00296 ? " should be allowed" 00297 : " should NOT be allowed"; 00298 $this->assertEquals( $expected, $errors, "User action '$action' on [[$source]] $allowableness." ); 00299 } else { 00300 $errors = $this->flattenErrorsArray( $errors ); 00301 foreach ( (array)$expected as $error ) { 00302 $this->assertContains( $error, $errors ); 00303 } 00304 } 00305 } 00306 00310 public function dataWgWhitelistReadRegexp() { 00311 $ALLOWED = true; 00312 $DISALLOWED = false; 00313 00314 return array( 00315 // Everything, if this doesn't work, we're really in trouble 00316 array( '/.*/', 'Main_Page', 'read', $ALLOWED ), 00317 array( '/.*/', 'Main_Page', 'edit', $DISALLOWED ), 00318 00319 // We validate against the title name, not the db key 00320 array( '/^Main_Page$/', 'Main_Page', 'read', $DISALLOWED ), 00321 // Main page 00322 array( '/^Main/', 'Main_Page', 'read', $ALLOWED ), 00323 array( '/^Main.*/', 'Main_Page', 'read', $ALLOWED ), 00324 // With spaces 00325 array( '/Mic\sCheck/', 'Mic Check', 'read', $ALLOWED ), 00326 // Unicode multibyte 00327 // ...without unicode modifier 00328 array( '/Unicode Test . Yes/', 'Unicode Test Ñ Yes', 'read', $DISALLOWED ), 00329 // ...with unicode modifier 00330 array( '/Unicode Test . Yes/u', 'Unicode Test Ñ Yes', 'read', $ALLOWED ), 00331 // Case insensitive 00332 array( '/MiC ChEcK/', 'mic check', 'read', $DISALLOWED ), 00333 array( '/MiC ChEcK/i', 'mic check', 'read', $ALLOWED ), 00334 00335 // From DefaultSettings.php: 00336 array( "@^UsEr.*@i", 'User is banned', 'read', $ALLOWED ), 00337 array( "@^UsEr.*@i", 'User:John Doe', 'read', $ALLOWED ), 00338 00339 // With namespaces: 00340 array( '/^Special:NewPages$/', 'Special:NewPages', 'read', $ALLOWED ), 00341 array( null, 'Special:Newpages', 'read', $DISALLOWED ), 00342 00343 ); 00344 } 00345 00346 public function flattenErrorsArray( $errors ) { 00347 $result = array(); 00348 foreach ( $errors as $error ) { 00349 $result[] = $error[0]; 00350 } 00351 00352 return $result; 00353 } 00354 00355 public static function provideTestIsValidMoveOperation() { 00356 return array( 00357 array( 'Test', 'Test', 'selfmove' ), 00358 array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' ) 00359 ); 00360 } 00361 00366 public function testGetPageViewLanguage( $expected, $titleText, $contLang, $lang, $variant, $msg = '' ) { 00367 global $wgLanguageCode, $wgContLang, $wgLang, $wgDefaultLanguageVariant, $wgAllowUserJs; 00368 00369 // Setup environnement for this test 00370 $wgLanguageCode = $contLang; 00371 $wgContLang = Language::factory( $contLang ); 00372 $wgLang = Language::factory( $lang ); 00373 $wgDefaultLanguageVariant = $variant; 00374 $wgAllowUserJs = true; 00375 00376 $title = Title::newFromText( $titleText ); 00377 $this->assertInstanceOf( 'Title', $title, 00378 "Test must be passed a valid title text, you gave '$titleText'" 00379 ); 00380 $this->assertEquals( $expected, 00381 $title->getPageViewLanguage()->getCode(), 00382 $msg 00383 ); 00384 } 00385 00386 public static function provideGetPageViewLanguage() { 00387 # Format: 00388 # - expected 00389 # - Title name 00390 # - wgContLang (expected in most case) 00391 # - wgLang (on some specific pages) 00392 # - wgDefaultLanguageVariant 00393 # - Optional message 00394 return array( 00395 array( 'fr', 'Help:I_need_somebody', 'fr', 'fr', false ), 00396 array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', false ), 00397 array( 'zh', 'Help:I_need_somebody', 'zh', 'zh-tw', false ), 00398 00399 array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', 'zh-cn' ), 00400 array( 'es', 'MediaWiki:About', 'es', 'zh-tw', 'zh-cn' ), 00401 array( 'es', 'MediaWiki:About/', 'es', 'zh-tw', 'zh-cn' ), 00402 array( 'de', 'MediaWiki:About/de', 'es', 'zh-tw', 'zh-cn' ), 00403 array( 'en', 'MediaWiki:Common.js', 'es', 'zh-tw', 'zh-cn' ), 00404 array( 'en', 'MediaWiki:Common.css', 'es', 'zh-tw', 'zh-cn' ), 00405 array( 'en', 'User:JohnDoe/Common.js', 'es', 'zh-tw', 'zh-cn' ), 00406 array( 'en', 'User:JohnDoe/Monobook.css', 'es', 'zh-tw', 'zh-cn' ), 00407 00408 array( 'zh-cn', 'Help:I_need_somebody', 'zh', 'zh-tw', 'zh-cn' ), 00409 array( 'zh', 'MediaWiki:About', 'zh', 'zh-tw', 'zh-cn' ), 00410 array( 'zh', 'MediaWiki:About/', 'zh', 'zh-tw', 'zh-cn' ), 00411 array( 'de', 'MediaWiki:About/de', 'zh', 'zh-tw', 'zh-cn' ), 00412 array( 'zh-cn', 'MediaWiki:About/zh-cn', 'zh', 'zh-tw', 'zh-cn' ), 00413 array( 'zh-tw', 'MediaWiki:About/zh-tw', 'zh', 'zh-tw', 'zh-cn' ), 00414 array( 'en', 'MediaWiki:Common.js', 'zh', 'zh-tw', 'zh-cn' ), 00415 array( 'en', 'MediaWiki:Common.css', 'zh', 'zh-tw', 'zh-cn' ), 00416 array( 'en', 'User:JohnDoe/Common.js', 'zh', 'zh-tw', 'zh-cn' ), 00417 array( 'en', 'User:JohnDoe/Monobook.css', 'zh', 'zh-tw', 'zh-cn' ), 00418 00419 array( 'zh-tw', 'Special:NewPages', 'es', 'zh-tw', 'zh-cn' ), 00420 array( 'zh-tw', 'Special:NewPages', 'zh', 'zh-tw', 'zh-cn' ), 00421 00422 ); 00423 } 00424 00429 public function testGetBaseText( $title, $expected, $msg = '' ) { 00430 $title = Title::newFromText( $title ); 00431 $this->assertEquals( $expected, 00432 $title->getBaseText(), 00433 $msg 00434 ); 00435 } 00436 00437 public static function provideBaseTitleCases() { 00438 return array( 00439 # Title, expected base, optional message 00440 array( 'User:John_Doe/subOne/subTwo', 'John Doe/subOne' ), 00441 array( 'User:Foo/Bar/Baz', 'Foo/Bar' ), 00442 ); 00443 } 00444 00449 public function testGetRootText( $title, $expected, $msg = '' ) { 00450 $title = Title::newFromText( $title ); 00451 $this->assertEquals( $expected, 00452 $title->getRootText(), 00453 $msg 00454 ); 00455 } 00456 00457 public static function provideRootTitleCases() { 00458 return array( 00459 # Title, expected base, optional message 00460 array( 'User:John_Doe/subOne/subTwo', 'John Doe' ), 00461 array( 'User:Foo/Bar/Baz', 'Foo' ), 00462 ); 00463 } 00464 00470 public function testGetSubpageText( $title, $expected, $msg = '' ) { 00471 $title = Title::newFromText( $title ); 00472 $this->assertEquals( $expected, 00473 $title->getSubpageText(), 00474 $msg 00475 ); 00476 } 00477 00478 public static function provideSubpageTitleCases() { 00479 return array( 00480 # Title, expected base, optional message 00481 array( 'User:John_Doe/subOne/subTwo', 'subTwo' ), 00482 array( 'User:John_Doe/subOne', 'subOne' ), 00483 ); 00484 } 00485 }