MediaWiki  REL1_23
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 
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 &eacute; B' ),
00309             //array( 'A &#233; B' ),
00310             //array( 'A &#x00E9; 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 }