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