MediaWiki  REL1_22
LanguageTest.php
Go to the documentation of this file.
00001 <?php
00002 
00003 class LanguageTest extends LanguageClassesTestCase {
00008     public function testLanguageConvertDoubleWidthToSingleWidth() {
00009         $this->assertEquals(
00010             "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
00011             $this->getLang()->normalizeForSearch(
00012                 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
00013             ),
00014             'convertDoubleWidth() with the full alphabet and digits'
00015         );
00016     }
00017 
00022     public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
00023         $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
00024     }
00025 
00026     public static function provideFormattableTimes() {
00027         return array(
00028             array(
00029                 9.45,
00030                 array(),
00031                 '9.5 s',
00032                 'formatTimePeriod() rounding (<10s)'
00033             ),
00034             array(
00035                 9.45,
00036                 array( 'noabbrevs' => true ),
00037                 '9.5 seconds',
00038                 'formatTimePeriod() rounding (<10s)'
00039             ),
00040             array(
00041                 9.95,
00042                 array(),
00043                 '10 s',
00044                 'formatTimePeriod() rounding (<10s)'
00045             ),
00046             array(
00047                 9.95,
00048                 array( 'noabbrevs' => true ),
00049                 '10 seconds',
00050                 'formatTimePeriod() rounding (<10s)'
00051             ),
00052             array(
00053                 59.55,
00054                 array(),
00055                 '1 min 0 s',
00056                 'formatTimePeriod() rounding (<60s)'
00057             ),
00058             array(
00059                 59.55,
00060                 array( 'noabbrevs' => true ),
00061                 '1 minute 0 seconds',
00062                 'formatTimePeriod() rounding (<60s)'
00063             ),
00064             array(
00065                 119.55,
00066                 array(),
00067                 '2 min 0 s',
00068                 'formatTimePeriod() rounding (<1h)'
00069             ),
00070             array(
00071                 119.55,
00072                 array( 'noabbrevs' => true ),
00073                 '2 minutes 0 seconds',
00074                 'formatTimePeriod() rounding (<1h)'
00075             ),
00076             array(
00077                 3599.55,
00078                 array(),
00079                 '1 h 0 min 0 s',
00080                 'formatTimePeriod() rounding (<1h)'
00081             ),
00082             array(
00083                 3599.55,
00084                 array( 'noabbrevs' => true ),
00085                 '1 hour 0 minutes 0 seconds',
00086                 'formatTimePeriod() rounding (<1h)'
00087             ),
00088             array(
00089                 7199.55,
00090                 array(),
00091                 '2 h 0 min 0 s',
00092                 'formatTimePeriod() rounding (>=1h)'
00093             ),
00094             array(
00095                 7199.55,
00096                 array( 'noabbrevs' => true ),
00097                 '2 hours 0 minutes 0 seconds',
00098                 'formatTimePeriod() rounding (>=1h)'
00099             ),
00100             array(
00101                 7199.55,
00102                 'avoidseconds',
00103                 '2 h 0 min',
00104                 'formatTimePeriod() rounding (>=1h), avoidseconds'
00105             ),
00106             array(
00107                 7199.55,
00108                 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
00109                 '2 hours 0 minutes',
00110                 'formatTimePeriod() rounding (>=1h), avoidseconds'
00111             ),
00112             array(
00113                 7199.55,
00114                 'avoidminutes',
00115                 '2 h 0 min',
00116                 'formatTimePeriod() rounding (>=1h), avoidminutes'
00117             ),
00118             array(
00119                 7199.55,
00120                 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
00121                 '2 hours 0 minutes',
00122                 'formatTimePeriod() rounding (>=1h), avoidminutes'
00123             ),
00124             array(
00125                 172799.55,
00126                 'avoidseconds',
00127                 '48 h 0 min',
00128                 'formatTimePeriod() rounding (=48h), avoidseconds'
00129             ),
00130             array(
00131                 172799.55,
00132                 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
00133                 '48 hours 0 minutes',
00134                 'formatTimePeriod() rounding (=48h), avoidseconds'
00135             ),
00136             array(
00137                 259199.55,
00138                 'avoidminutes',
00139                 '3 d 0 h',
00140                 'formatTimePeriod() rounding (>48h), avoidminutes'
00141             ),
00142             array(
00143                 259199.55,
00144                 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
00145                 '3 days 0 hours',
00146                 'formatTimePeriod() rounding (>48h), avoidminutes'
00147             ),
00148             array(
00149                 176399.55,
00150                 'avoidseconds',
00151                 '2 d 1 h 0 min',
00152                 'formatTimePeriod() rounding (>48h), avoidseconds'
00153             ),
00154             array(
00155                 176399.55,
00156                 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
00157                 '2 days 1 hour 0 minutes',
00158                 'formatTimePeriod() rounding (>48h), avoidseconds'
00159             ),
00160             array(
00161                 176399.55,
00162                 'avoidminutes',
00163                 '2 d 1 h',
00164                 'formatTimePeriod() rounding (>48h), avoidminutes'
00165             ),
00166             array(
00167                 176399.55,
00168                 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
00169                 '2 days 1 hour',
00170                 'formatTimePeriod() rounding (>48h), avoidminutes'
00171             ),
00172             array(
00173                 259199.55,
00174                 'avoidseconds',
00175                 '3 d 0 h 0 min',
00176                 'formatTimePeriod() rounding (>48h), avoidseconds'
00177             ),
00178             array(
00179                 259199.55,
00180                 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
00181                 '3 days 0 hours 0 minutes',
00182                 'formatTimePeriod() rounding (>48h), avoidseconds'
00183             ),
00184             array(
00185                 172801.55,
00186                 'avoidseconds',
00187                 '2 d 0 h 0 min',
00188                 'formatTimePeriod() rounding, (>48h), avoidseconds'
00189             ),
00190             array(
00191                 172801.55,
00192                 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
00193                 '2 days 0 hours 0 minutes',
00194                 'formatTimePeriod() rounding, (>48h), avoidseconds'
00195             ),
00196             array(
00197                 176460.55,
00198                 array(),
00199                 '2 d 1 h 1 min 1 s',
00200                 'formatTimePeriod() rounding, recursion, (>48h)'
00201             ),
00202             array(
00203                 176460.55,
00204                 array( 'noabbrevs' => true ),
00205                 '2 days 1 hour 1 minute 1 second',
00206                 'formatTimePeriod() rounding, recursion, (>48h)'
00207             ),
00208         );
00209     }
00210 
00214     public function testTruncate() {
00215         $this->assertEquals(
00216             "XXX",
00217             $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
00218             'truncate prefix, len 0, small ellipsis'
00219         );
00220 
00221         $this->assertEquals(
00222             "12345XXX",
00223             $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
00224             'truncate prefix, small ellipsis'
00225         );
00226 
00227         $this->assertEquals(
00228             "123456789",
00229             $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
00230             'truncate prefix, large ellipsis'
00231         );
00232 
00233         $this->assertEquals(
00234             "XXX67890",
00235             $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
00236             'truncate suffix, small ellipsis'
00237         );
00238 
00239         $this->assertEquals(
00240             "123456789",
00241             $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
00242             'truncate suffix, large ellipsis'
00243         );
00244     }
00245 
00250     public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
00251         // Actual HTML...
00252         $this->assertEquals(
00253             $expected,
00254             $this->getLang()->truncateHTML( $input, $len, $ellipsis )
00255         );
00256     }
00257 
00261     public static function provideHTMLTruncateData() {
00262         return array(
00263             array( 0, 'XXX', "1234567890", "XXX" ),
00264             array( 8, 'XXX', "1234567890", "12345XXX" ),
00265             array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
00266             array( 2, '***',
00267                 '<p><span style="font-weight:bold;"></span></p>',
00268                 '<p><span style="font-weight:bold;"></span></p>',
00269             ),
00270             array( 2, '***',
00271                 '<p><span style="font-weight:bold;">123456789</span></p>',
00272                 '<p><span style="font-weight:bold;">***</span></p>',
00273             ),
00274             array( 2, '***',
00275                 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
00276                 '<p><span style="font-weight:bold;">***</span></p>',
00277             ),
00278             array( 3, '***',
00279                 '<p><span style="font-weight:bold;">123456789</span></p>',
00280                 '<p><span style="font-weight:bold;">***</span></p>',
00281             ),
00282             array( 4, '***',
00283                 '<p><span style="font-weight:bold;">123456789</span></p>',
00284                 '<p><span style="font-weight:bold;">1***</span></p>',
00285             ),
00286             array( 5, '***',
00287                 '<tt><span style="font-weight:bold;">123456789</span></tt>',
00288                 '<tt><span style="font-weight:bold;">12***</span></tt>',
00289             ),
00290             array( 6, '***',
00291                 '<p><a href="www.mediawiki.org">123456789</a></p>',
00292                 '<p><a href="www.mediawiki.org">123***</a></p>',
00293             ),
00294             array( 6, '***',
00295                 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
00296                 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
00297             ),
00298             array( 7, '***',
00299                 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
00300                 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
00301             ),
00302             array( 8, '***',
00303                 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
00304                 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
00305             ),
00306             array( 9, '***',
00307                 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
00308                 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
00309             ),
00310             array( 10, '***',
00311                 '<p><font style="font-weight:bold;">123456789</font></p>',
00312                 '<p><font style="font-weight:bold;">123456789</font></p>',
00313             ),
00314         );
00315     }
00316 
00322     public function testWellFormedLanguageTag( $code, $message = '' ) {
00323         $this->assertTrue(
00324             Language::isWellFormedLanguageTag( $code ),
00325             "validating code $code $message"
00326         );
00327     }
00328 
00335     public static function provideWellFormedLanguageTags() {
00336         return array(
00337             array( 'fr', 'two-letter code' ),
00338             array( 'fr-latn', 'two-letter code with lower case script code' ),
00339             array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
00340             array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
00341             array( 'fr-FR', 'two-letter code with uppercase' ),
00342             array( 'ax-TZ', 'Not in the registry, but well-formed' ),
00343             array( 'fr-shadok', 'two-letter code with variant' ),
00344             array( 'fr-y-myext-myext2', 'non-x singleton' ),
00345             array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
00346             array( 'fra', 'three-letter language code' ),
00347             array( 'fra-FX', 'three-letter language code with country code' ),
00348             array( 'i-klingon', 'grandfathered with singleton' ),
00349             array( 'I-kLINgon', 'tags are case-insensitive...' ),
00350             array( 'no-bok', 'grandfathered without singleton' ),
00351             array( 'i-enochian', 'Grandfathered' ),
00352             array( 'x-fr-CH', 'private use' ),
00353             array( 'es-419', 'two-letter code with region number' ),
00354             array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
00355             array( 'ab-x-abc-x-abc', 'anything goes after x' ),
00356             array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
00357             array( 'i-default', 'grandfathered' ),
00358             array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
00359             array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
00360             array( 'de-CH-1901', 'with country and year' ),
00361             array( 'en-US-x-twain', 'with country and singleton' ),
00362             array( 'zh-cmn', 'three-letter variant' ),
00363             array( 'zh-cmn-Hant', 'three-letter variant and script' ),
00364             array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
00365             array( 'xr-p-lze', 'Extension' ),
00366         );
00367     }
00368 
00374     public function testMalformedLanguageTag( $code, $message = '' ) {
00375         $this->assertFalse(
00376             Language::isWellFormedLanguageTag( $code ),
00377             "validating that code $code is a malformed language tag - $message"
00378         );
00379     }
00380 
00387     public static function provideMalformedLanguageTags() {
00388         return array(
00389             array( 'f', 'language too short' ),
00390             array( 'f-Latn', 'language too short with script' ),
00391             array( 'xr-lxs-qut', 'variants too short' ), # extlangS
00392             array( 'fr-Latn-F', 'region too short' ),
00393             array( 'a-value', 'language too short with region' ),
00394             array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
00395             array( 'i-notexist', 'grandfathered but not registered: invalid, even if we only test well-formedness' ),
00396             array( 'abcdefghi-012345678', 'numbers too long' ),
00397             array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
00398             array( 'ab-abcd-abc', 'invalid extensions' ),
00399             array( 'ab-ab-abc', 'invalid extensions' ),
00400             array( 'ab-123-abc', 'invalid extensions' ),
00401             array( 'a-Hant-ZH', 'short language with valid extensions' ),
00402             array( 'a1-Hant-ZH', 'invalid character in language' ),
00403             array( 'ab-abcde-abc', 'invalid extensions' ),
00404             array( 'ab-1abc-abc', 'invalid characters in extensions' ),
00405             array( 'ab-ab-abcd', 'invalid order of extensions' ),
00406             array( 'ab-123-abcd', 'invalid order of extensions' ),
00407             array( 'ab-abcde-abcd', 'invalid extensions' ),
00408             array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
00409             array( 'ab-a-b', 'extensions too short' ),
00410             array( 'ab-a-x', 'extensions too short, even with singleton' ),
00411             array( 'ab--ab', 'two separators' ),
00412             array( 'ab-abc-', 'separator in the end' ),
00413             array( '-ab-abc', 'separator in the beginning' ),
00414             array( 'abcd-efg', 'language too long' ),
00415             array( 'aabbccddE', 'tag too long' ),
00416             array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
00417             array( 'de-f', 'subtag too short' ),
00418         );
00419     }
00420 
00425     public function testLenientLanguageTag() {
00426         $this->assertTrue(
00427             Language::isWellFormedLanguageTag( 'pa_guru', true ),
00428             'pa_guru is a well-formed language tag in lenient mode'
00429         );
00430     }
00431 
00437     public function testBuiltInCodeValidation( $code, $message = '' ) {
00438         $this->assertTrue(
00439             (bool)Language::isValidBuiltInCode( $code ),
00440             "validating code $code $message"
00441         );
00442     }
00443 
00447     public function testBuiltInCodeValidationRejectUnderscore() {
00448         $this->assertFalse(
00449             (bool)Language::isValidBuiltInCode( 'be_tarask' ),
00450             "reject underscore in language code"
00451         );
00452     }
00453 
00454     public static function provideLanguageCodes() {
00455         return array(
00456             array( 'fr', 'Two letters, minor case' ),
00457             array( 'EN', 'Two letters, upper case' ),
00458             array( 'tyv', 'Three letters' ),
00459             array( 'tokipona', 'long language code' ),
00460             array( 'be-tarask', 'With dash' ),
00461             array( 'Zh-classical', 'Begin with upper case, dash' ),
00462             array( 'Be-x-old', 'With extension (two dashes)' ),
00463         );
00464     }
00465 
00471     public function testKnownLanguageTag( $code, $message = '' ) {
00472         $this->assertTrue(
00473             (bool)Language::isKnownLanguageTag( $code ),
00474             "validating code $code - $message"
00475         );
00476     }
00477 
00478     public static function provideKnownLanguageTags() {
00479         return array(
00480             array( 'fr', 'simple code' ),
00481             array( 'bat-smg', 'an MW legacy tag' ),
00482             array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
00483         );
00484     }
00485 
00489     public function testKnownCldrLanguageTag() {
00490         if ( !class_exists( 'LanguageNames' ) ) {
00491             $this->markTestSkipped( 'The LanguageNames class is not available. The cldr extension is probably not installed.' );
00492         }
00493 
00494         $this->assertTrue(
00495             (bool)Language::isKnownLanguageTag( 'pal' ),
00496             'validating code "pal" an ancient language, which probably will not appear in Names.php, but appears in CLDR in English'
00497         );
00498     }
00499 
00505     public function testUnknownLanguageTag( $code, $message = '' ) {
00506         $this->assertFalse(
00507             (bool)Language::isKnownLanguageTag( $code ),
00508             "checking that code $code is invalid - $message"
00509         );
00510     }
00511 
00512     public static function provideUnknownLanguageTags() {
00513         return array(
00514             array( 'mw', 'non-existent two-letter code' ),
00515             array( 'foo"<bar', 'very invalid language code' ),
00516         );
00517     }
00518 
00524     public function testSprintfDateTooShortTimestamp() {
00525         $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
00526     }
00527 
00533     public function testSprintfDateTooLongTimestamp() {
00534         $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
00535     }
00536 
00542     public function testSprintfDateNotAllDigitTimestamp() {
00543         $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
00544     }
00545 
00550     public function testSprintfDate( $format, $ts, $expected, $msg ) {
00551         $this->assertEquals(
00552             $expected,
00553             $this->getLang()->sprintfDate( $format, $ts ),
00554             "sprintfDate('$format', '$ts'): $msg"
00555         );
00556     }
00557 
00563     public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
00564         $oldTZ = date_default_timezone_get();
00565         $res = date_default_timezone_set( 'Asia/Seoul' );
00566         if ( !$res ) {
00567             $this->markTestSkipped( "Error setting Timezone" );
00568         }
00569 
00570         $this->assertEquals(
00571             $expected,
00572             $this->getLang()->sprintfDate( $format, $ts ),
00573             "sprintfDate('$format', '$ts'): $msg"
00574         );
00575 
00576         date_default_timezone_set( $oldTZ );
00577     }
00578 
00584     public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
00585         $tz = new DateTimeZone( 'Asia/Seoul' );
00586         if ( !$tz ) {
00587             $this->markTestSkipped( "Error getting Timezone" );
00588         }
00589 
00590         $this->assertEquals(
00591             $expected,
00592             $this->getLang()->sprintfDate( $format, $ts, $tz ),
00593             "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
00594         );
00595     }
00596 
00597     public static function provideSprintfDateSamples() {
00598         return array(
00599             array(
00600                 'xiY',
00601                 '20111212000000',
00602                 '1390', // note because we're testing English locale we get Latin-standard digits
00603                 '1390',
00604                 'Iranian calendar full year'
00605             ),
00606             array(
00607                 'xiy',
00608                 '20111212000000',
00609                 '90',
00610                 '90',
00611                 'Iranian calendar short year'
00612             ),
00613             array(
00614                 'o',
00615                 '20120101235000',
00616                 '2011',
00617                 '2011',
00618                 'ISO 8601 (week) year'
00619             ),
00620             array(
00621                 'W',
00622                 '20120101235000',
00623                 '52',
00624                 '52',
00625                 'Week number'
00626             ),
00627             array(
00628                 'W',
00629                 '20120102235000',
00630                 '1',
00631                 '1',
00632                 'Week number'
00633             ),
00634             array(
00635                 'o-\\WW-N',
00636                 '20091231235000',
00637                 '2009-W53-4',
00638                 '2009-W53-4',
00639                 'leap week'
00640             ),
00641             // What follows is mostly copied from http://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
00642             array(
00643                 'Y',
00644                 '20120102090705',
00645                 '2012',
00646                 '2012',
00647                 'Full year'
00648             ),
00649             array(
00650                 'y',
00651                 '20120102090705',
00652                 '12',
00653                 '12',
00654                 '2 digit year'
00655             ),
00656             array(
00657                 'L',
00658                 '20120102090705',
00659                 '1',
00660                 '1',
00661                 'Leap year'
00662             ),
00663             array(
00664                 'n',
00665                 '20120102090705',
00666                 '1',
00667                 '1',
00668                 'Month index, not zero pad'
00669             ),
00670             array(
00671                 'N',
00672                 '20120102090705',
00673                 '01',
00674                 '01',
00675                 'Month index. Zero pad'
00676             ),
00677             array(
00678                 'M',
00679                 '20120102090705',
00680                 'Jan',
00681                 'Jan',
00682                 'Month abbrev'
00683             ),
00684             array(
00685                 'F',
00686                 '20120102090705',
00687                 'January',
00688                 'January',
00689                 'Full month'
00690             ),
00691             array(
00692                 'xg',
00693                 '20120102090705',
00694                 'January',
00695                 'January',
00696                 'Genitive month name (same in EN)'
00697             ),
00698             array(
00699                 'j',
00700                 '20120102090705',
00701                 '2',
00702                 '2',
00703                 'Day of month (not zero pad)'
00704             ),
00705             array(
00706                 'd',
00707                 '20120102090705',
00708                 '02',
00709                 '02',
00710                 'Day of month (zero-pad)'
00711             ),
00712             array(
00713                 'z',
00714                 '20120102090705',
00715                 '1',
00716                 '1',
00717                 'Day of year (zero-indexed)'
00718             ),
00719             array(
00720                 'D',
00721                 '20120102090705',
00722                 'Mon',
00723                 'Mon',
00724                 'Day of week (abbrev)'
00725             ),
00726             array(
00727                 'l',
00728                 '20120102090705',
00729                 'Monday',
00730                 'Monday',
00731                 'Full day of week'
00732             ),
00733             array(
00734                 'N',
00735                 '20120101090705',
00736                 '7',
00737                 '7',
00738                 'Day of week (Mon=1, Sun=7)'
00739             ),
00740             array(
00741                 'w',
00742                 '20120101090705',
00743                 '0',
00744                 '0',
00745                 'Day of week (Sun=0, Sat=6)'
00746             ),
00747             array(
00748                 'N',
00749                 '20120102090705',
00750                 '1',
00751                 '1',
00752                 'Day of week'
00753             ),
00754             array(
00755                 'a',
00756                 '20120102090705',
00757                 'am',
00758                 'am',
00759                 'am vs pm'
00760             ),
00761             array(
00762                 'A',
00763                 '20120102120000',
00764                 'PM',
00765                 'PM',
00766                 'AM vs PM'
00767             ),
00768             array(
00769                 'a',
00770                 '20120102000000',
00771                 'am',
00772                 'am',
00773                 'AM vs PM'
00774             ),
00775             array(
00776                 'g',
00777                 '20120102090705',
00778                 '9',
00779                 '9',
00780                 '12 hour, not Zero'
00781             ),
00782             array(
00783                 'h',
00784                 '20120102090705',
00785                 '09',
00786                 '09',
00787                 '12 hour, zero padded'
00788             ),
00789             array(
00790                 'G',
00791                 '20120102090705',
00792                 '9',
00793                 '9',
00794                 '24 hour, not zero'
00795             ),
00796             array(
00797                 'H',
00798                 '20120102090705',
00799                 '09',
00800                 '09',
00801                 '24 hour, zero'
00802             ),
00803             array(
00804                 'H',
00805                 '20120102110705',
00806                 '11',
00807                 '11',
00808                 '24 hour, zero'
00809             ),
00810             array(
00811                 'i',
00812                 '20120102090705',
00813                 '07',
00814                 '07',
00815                 'Minutes'
00816             ),
00817             array(
00818                 's',
00819                 '20120102090705',
00820                 '05',
00821                 '05',
00822                 'seconds'
00823             ),
00824             array(
00825                 'U',
00826                 '20120102090705',
00827                 '1325495225',
00828                 '1325462825',
00829                 'unix time'
00830             ),
00831             array(
00832                 't',
00833                 '20120102090705',
00834                 '31',
00835                 '31',
00836                 'Days in current month'
00837             ),
00838             array(
00839                 'c',
00840                 '20120102090705',
00841                 '2012-01-02T09:07:05+00:00',
00842                 '2012-01-02T09:07:05+09:00',
00843                 'ISO 8601 timestamp'
00844             ),
00845             array(
00846                 'r',
00847                 '20120102090705',
00848                 'Mon, 02 Jan 2012 09:07:05 +0000',
00849                 'Mon, 02 Jan 2012 09:07:05 +0900',
00850                 'RFC 5322'
00851             ),
00852             array(
00853                 'e',
00854                 '20120102090705',
00855                 'UTC',
00856                 'Asia/Seoul',
00857                 'Timezone identifier'
00858             ),
00859             array(
00860                 'I',
00861                 '19880602090705',
00862                 '0',
00863                 '1',
00864                 'DST indicator'
00865             ),
00866             array(
00867                 'O',
00868                 '20120102090705',
00869                 '+0000',
00870                 '+0900',
00871                 'Timezone offset'
00872             ),
00873             array(
00874                 'P',
00875                 '20120102090705',
00876                 '+00:00',
00877                 '+09:00',
00878                 'Timezone offset with colon'
00879             ),
00880             array(
00881                 'T',
00882                 '20120102090705',
00883                 'UTC',
00884                 'KST',
00885                 'Timezone abbreviation'
00886             ),
00887             array(
00888                 'Z',
00889                 '20120102090705',
00890                 '0',
00891                 '32400',
00892                 'Timezone offset in seconds'
00893             ),
00894             array(
00895                 'xmj xmF xmn xmY',
00896                 '20120102090705',
00897                 '7 Safar 2 1433',
00898                 '7 Safar 2 1433',
00899                 'Islamic'
00900             ),
00901             array(
00902                 'xij xiF xin xiY',
00903                 '20120102090705',
00904                 '12 Dey 10 1390',
00905                 '12 Dey 10 1390',
00906                 'Iranian'
00907             ),
00908             array(
00909                 'xjj xjF xjn xjY',
00910                 '20120102090705',
00911                 '7 Tevet 4 5772',
00912                 '7 Tevet 4 5772',
00913                 'Hebrew'
00914             ),
00915             array(
00916                 'xjt',
00917                 '20120102090705',
00918                 '29',
00919                 '29',
00920                 'Hebrew number of days in month'
00921             ),
00922             array(
00923                 'xjx',
00924                 '20120102090705',
00925                 'Tevet',
00926                 'Tevet',
00927                 'Hebrew genitive month name (No difference in EN)'
00928             ),
00929             array(
00930                 'xkY',
00931                 '20120102090705',
00932                 '2555',
00933                 '2555',
00934                 'Thai year'
00935             ),
00936             array(
00937                 'xoY',
00938                 '20120102090705',
00939                 '101',
00940                 '101',
00941                 'Minguo'
00942             ),
00943             array(
00944                 'xtY',
00945                 '20120102090705',
00946                 '平成24',
00947                 '平成24',
00948                 'nengo'
00949             ),
00950             array(
00951                 'xrxkYY',
00952                 '20120102090705',
00953                 'MMDLV2012',
00954                 'MMDLV2012',
00955                 'Roman numerals'
00956             ),
00957             array(
00958                 'xhxjYY',
00959                 '20120102090705',
00960                 'ה\'תשע"ב2012',
00961                 'ה\'תשע"ב2012',
00962                 'Hebrew numberals'
00963             ),
00964             array(
00965                 'xnY',
00966                 '20120102090705',
00967                 '2012',
00968                 '2012',
00969                 'Raw numerals (doesn\'t mean much in EN)'
00970             ),
00971             array(
00972                 '[[Y "(yea"\\r)]] \\"xx\\"',
00973                 '20120102090705',
00974                 '[[2012 (year)]] "x"',
00975                 '[[2012 (year)]] "x"',
00976                 'Various escaping'
00977             ),
00978 
00979         );
00980     }
00981 
00986     public function testFormatSize( $size, $expected, $msg ) {
00987         $this->assertEquals(
00988             $expected,
00989             $this->getLang()->formatSize( $size ),
00990             "formatSize('$size'): $msg"
00991         );
00992     }
00993 
00994     public static function provideFormatSizes() {
00995         return array(
00996             array(
00997                 0,
00998                 "0 B",
00999                 "Zero bytes"
01000             ),
01001             array(
01002                 1024,
01003                 "1 KB",
01004                 "1 kilobyte"
01005             ),
01006             array(
01007                 1024 * 1024,
01008                 "1 MB",
01009                 "1,024 megabytes"
01010             ),
01011             array(
01012                 1024 * 1024 * 1024,
01013                 "1 GB",
01014                 "1 gigabytes"
01015             ),
01016             array(
01017                 pow( 1024, 4 ),
01018                 "1 TB",
01019                 "1 terabyte"
01020             ),
01021             array(
01022                 pow( 1024, 5 ),
01023                 "1 PB",
01024                 "1 petabyte"
01025             ),
01026             array(
01027                 pow( 1024, 6 ),
01028                 "1 EB",
01029                 "1,024 exabyte"
01030             ),
01031             array(
01032                 pow( 1024, 7 ),
01033                 "1 ZB",
01034                 "1 zetabyte"
01035             ),
01036             array(
01037                 pow( 1024, 8 ),
01038                 "1 YB",
01039                 "1 yottabyte"
01040             ),
01041             // How big!? THIS BIG!
01042         );
01043     }
01044 
01049     public function testFormatBitrate( $bps, $expected, $msg ) {
01050         $this->assertEquals(
01051             $expected,
01052             $this->getLang()->formatBitrate( $bps ),
01053             "formatBitrate('$bps'): $msg"
01054         );
01055     }
01056 
01057     public static function provideFormatBitrate() {
01058         return array(
01059             array(
01060                 0,
01061                 "0 bps",
01062                 "0 bits per second"
01063             ),
01064             array(
01065                 999,
01066                 "999 bps",
01067                 "999 bits per second"
01068             ),
01069             array(
01070                 1000,
01071                 "1 kbps",
01072                 "1 kilobit per second"
01073             ),
01074             array(
01075                 1000 * 1000,
01076                 "1 Mbps",
01077                 "1 megabit per second"
01078             ),
01079             array(
01080                 pow( 10, 9 ),
01081                 "1 Gbps",
01082                 "1 gigabit per second"
01083             ),
01084             array(
01085                 pow( 10, 12 ),
01086                 "1 Tbps",
01087                 "1 terabit per second"
01088             ),
01089             array(
01090                 pow( 10, 15 ),
01091                 "1 Pbps",
01092                 "1 petabit per second"
01093             ),
01094             array(
01095                 pow( 10, 18 ),
01096                 "1 Ebps",
01097                 "1 exabit per second"
01098             ),
01099             array(
01100                 pow( 10, 21 ),
01101                 "1 Zbps",
01102                 "1 zetabit per second"
01103             ),
01104             array(
01105                 pow( 10, 24 ),
01106                 "1 Ybps",
01107                 "1 yottabit per second"
01108             ),
01109             array(
01110                 pow( 10, 27 ),
01111                 "1,000 Ybps",
01112                 "1,000 yottabits per second"
01113             ),
01114         );
01115     }
01116 
01117 
01122     public function testFormatDuration( $duration, $expected, $intervals = array() ) {
01123         $this->assertEquals(
01124             $expected,
01125             $this->getLang()->formatDuration( $duration, $intervals ),
01126             "formatDuration('$duration'): $expected"
01127         );
01128     }
01129 
01130     public static function provideFormatDuration() {
01131         return array(
01132             array(
01133                 0,
01134                 '0 seconds',
01135             ),
01136             array(
01137                 1,
01138                 '1 second',
01139             ),
01140             array(
01141                 2,
01142                 '2 seconds',
01143             ),
01144             array(
01145                 60,
01146                 '1 minute',
01147             ),
01148             array(
01149                 2 * 60,
01150                 '2 minutes',
01151             ),
01152             array(
01153                 3600,
01154                 '1 hour',
01155             ),
01156             array(
01157                 2 * 3600,
01158                 '2 hours',
01159             ),
01160             array(
01161                 24 * 3600,
01162                 '1 day',
01163             ),
01164             array(
01165                 2 * 86400,
01166                 '2 days',
01167             ),
01168             array(
01169                 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
01170                 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
01171                 '1 year',
01172             ),
01173             array(
01174                 2 * 31556952,
01175                 '2 years',
01176             ),
01177             array(
01178                 10 * 31556952,
01179                 '1 decade',
01180             ),
01181             array(
01182                 20 * 31556952,
01183                 '2 decades',
01184             ),
01185             array(
01186                 100 * 31556952,
01187                 '1 century',
01188             ),
01189             array(
01190                 200 * 31556952,
01191                 '2 centuries',
01192             ),
01193             array(
01194                 1000 * 31556952,
01195                 '1 millennium',
01196             ),
01197             array(
01198                 2000 * 31556952,
01199                 '2 millennia',
01200             ),
01201             array(
01202                 9001,
01203                 '2 hours, 30 minutes and 1 second'
01204             ),
01205             array(
01206                 3601,
01207                 '1 hour and 1 second'
01208             ),
01209             array(
01210                 31556952 + 2 * 86400 + 9000,
01211                 '1 year, 2 days, 2 hours and 30 minutes'
01212             ),
01213             array(
01214                 42 * 1000 * 31556952 + 42,
01215                 '42 millennia and 42 seconds'
01216             ),
01217             array(
01218                 60,
01219                 '60 seconds',
01220                 array( 'seconds' ),
01221             ),
01222             array(
01223                 61,
01224                 '61 seconds',
01225                 array( 'seconds' ),
01226             ),
01227             array(
01228                 1,
01229                 '1 second',
01230                 array( 'seconds' ),
01231             ),
01232             array(
01233                 31556952 + 2 * 86400 + 9000,
01234                 '1 year, 2 days and 150 minutes',
01235                 array( 'years', 'days', 'minutes' ),
01236             ),
01237             array(
01238                 42,
01239                 '0 days',
01240                 array( 'years', 'days' ),
01241             ),
01242             array(
01243                 31556952 + 2 * 86400 + 9000,
01244                 '1 year, 2 days and 150 minutes',
01245                 array( 'minutes', 'days', 'years' ),
01246             ),
01247             array(
01248                 42,
01249                 '0 days',
01250                 array( 'days', 'years' ),
01251             ),
01252         );
01253     }
01254 
01259     public function testCheckTitleEncoding( $s ) {
01260         $this->assertEquals(
01261             $s,
01262             $this->getLang()->checkTitleEncoding( $s ),
01263             "checkTitleEncoding('$s')"
01264         );
01265     }
01266 
01267     public static function provideCheckTitleEncodingData() {
01268         return array(
01269             array( "" ),
01270             array( "United States of America" ), // 7bit ASCII
01271             array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
01272             array(
01273                 rawurldecode(
01274                     "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
01275                 )
01276             ),
01277             // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
01278             // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
01279             // uses mb_check_encoding for its test.
01280             array(
01281                 rawurldecode(
01282                     "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
01283                         . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
01284                         . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
01285                         . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
01286                         . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
01287                         . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
01288                         . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
01289                         . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
01290                         . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
01291                         . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
01292                         . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
01293                         . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
01294                         . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
01295                         . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
01296                 ),
01297             ),
01298             array(
01299                 rawurldecode(
01300                     "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
01301                         . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
01302                         . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
01303                         . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
01304                         . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
01305                         . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
01306                         . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
01307                         . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
01308                         . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
01309                         . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
01310                         . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
01311                         . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
01312                         . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
01313                         . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
01314                         . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
01315                 )
01316             )
01317         );
01318     }
01319 
01324     public function testRomanNumerals( $num, $numerals ) {
01325         $this->assertEquals(
01326             $numerals,
01327             Language::romanNumeral( $num ),
01328             "romanNumeral('$num')"
01329         );
01330     }
01331 
01332     public static function provideRomanNumeralsData() {
01333         return array(
01334             array( 1, 'I' ),
01335             array( 2, 'II' ),
01336             array( 3, 'III' ),
01337             array( 4, 'IV' ),
01338             array( 5, 'V' ),
01339             array( 6, 'VI' ),
01340             array( 7, 'VII' ),
01341             array( 8, 'VIII' ),
01342             array( 9, 'IX' ),
01343             array( 10, 'X' ),
01344             array( 20, 'XX' ),
01345             array( 30, 'XXX' ),
01346             array( 40, 'XL' ),
01347             array( 49, 'XLIX' ),
01348             array( 50, 'L' ),
01349             array( 60, 'LX' ),
01350             array( 70, 'LXX' ),
01351             array( 80, 'LXXX' ),
01352             array( 90, 'XC' ),
01353             array( 99, 'XCIX' ),
01354             array( 100, 'C' ),
01355             array( 200, 'CC' ),
01356             array( 300, 'CCC' ),
01357             array( 400, 'CD' ),
01358             array( 500, 'D' ),
01359             array( 600, 'DC' ),
01360             array( 700, 'DCC' ),
01361             array( 800, 'DCCC' ),
01362             array( 900, 'CM' ),
01363             array( 999, 'CMXCIX' ),
01364             array( 1000, 'M' ),
01365             array( 1989, 'MCMLXXXIX' ),
01366             array( 2000, 'MM' ),
01367             array( 3000, 'MMM' ),
01368             array( 4000, 'MMMM' ),
01369             array( 5000, 'MMMMM' ),
01370             array( 6000, 'MMMMMM' ),
01371             array( 7000, 'MMMMMMM' ),
01372             array( 8000, 'MMMMMMMM' ),
01373             array( 9000, 'MMMMMMMMM' ),
01374             array( 9999, 'MMMMMMMMMCMXCIX' ),
01375             array( 10000, 'MMMMMMMMMM' ),
01376         );
01377     }
01378 
01383     public function testConvertPlural( $expected, $number, $forms ) {
01384         $chosen = $this->getLang()->convertPlural( $number, $forms );
01385         $this->assertEquals( $expected, $chosen );
01386     }
01387 
01388     public static function providePluralData() {
01389         // Params are: [expected text, number given, [the plural forms]]
01390         return array(
01391             array( 'plural', 0, array(
01392                 'singular', 'plural'
01393             ) ),
01394             array( 'explicit zero', 0, array(
01395                 '0=explicit zero', 'singular', 'plural'
01396             ) ),
01397             array( 'explicit one', 1, array(
01398                 'singular', 'plural', '1=explicit one',
01399             ) ),
01400             array( 'singular', 1, array(
01401                 'singular', 'plural', '0=explicit zero',
01402             ) ),
01403             array( 'plural', 3, array(
01404                 '0=explicit zero', '1=explicit one', 'singular', 'plural'
01405             ) ),
01406             array( 'explicit eleven', 11, array(
01407                 'singular', 'plural', '11=explicit eleven',
01408             ) ),
01409             array( 'plural', 12, array(
01410                 'singular', 'plural', '11=explicit twelve',
01411             ) ),
01412             array( 'plural', 12, array(
01413                 'singular', 'plural', '=explicit form',
01414             ) ),
01415             array( 'other', 2, array(
01416                 'kissa=kala', '1=2=3', 'other',
01417             ) ),
01418             array( '', 2, array(
01419                 '0=explicit zero', '1=explicit one',
01420             ) ),
01421         );
01422     }
01423 
01428     public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
01429         $lang = $this->getLang();
01430         if ( is_array( $expectedData ) ) {
01431             list( $func, $arg ) = $expectedData;
01432             $expected = $lang->$func( $arg );
01433         } else {
01434             $expected = $expectedData;
01435         }
01436         $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
01437     }
01438 
01439     public static function provideTranslateBlockExpiry() {
01440         return array(
01441             array( '2 hours', '2 hours', 'simple data from ipboptions' ),
01442             array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
01443             array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
01444             array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
01445             array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
01446             array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
01447             array( array( 'formatDuration', 0 ), 'now', 'now' ),
01448             array( array( 'timeanddate', '20120102070000' ), '2012-1-1 7:00 +1 day', 'mixed, handled as absolute' ),
01449             array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
01450             array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
01451             array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
01452             array( 'dummy', 'dummy', 'return garbage as is' ),
01453         );
01454     }
01455 
01460     public function testCommafy( $number, $numbersWithCommas ) {
01461         $this->assertEquals(
01462             $numbersWithCommas,
01463             $this->getLang()->commafy( $number ),
01464             "commafy('$number')"
01465         );
01466     }
01467 
01468     public static function provideCommafyData() {
01469         return array(
01470             array( 1, '1' ),
01471             array( 10, '10' ),
01472             array( 100, '100' ),
01473             array( 1000, '1,000' ),
01474             array( 10000, '10,000' ),
01475             array( 100000, '100,000' ),
01476             array( 1000000, '1,000,000' ),
01477             array( 1.0001, '1.0001' ),
01478             array( 10.0001, '10.0001' ),
01479             array( 100.0001, '100.0001' ),
01480             array( 1000.0001, '1,000.0001' ),
01481             array( 10000.0001, '10,000.0001' ),
01482             array( 100000.0001, '100,000.0001' ),
01483             array( 1000000.0001, '1,000,000.0001' ),
01484         );
01485     }
01486 
01490     public function testListToText() {
01491         $lang = $this->getLang();
01492         $and = $lang->getMessageFromDB( 'and' );
01493         $s = $lang->getMessageFromDB( 'word-separator' );
01494         $c = $lang->getMessageFromDB( 'comma-separator' );
01495 
01496         $this->assertEquals( '', $lang->listToText( array() ) );
01497         $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
01498         $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
01499         $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
01500         $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
01501     }
01502 
01507     public function testIsSupportedLanguage( $code, $expected, $comment ) {
01508         $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
01509     }
01510 
01511     public static function provideIsSupportedLanguage() {
01512         return array(
01513             array( 'en', true, 'is supported language' ),
01514             array( 'fi', true, 'is supported language' ),
01515             array( 'bunny', false, 'is not supported language' ),
01516             array( 'FI', false, 'is not supported language, input should be in lower case' ),
01517         );
01518     }
01519 
01524     public function testGetParentLanguage( $code, $expected, $comment ) {
01525         $lang = Language::factory( $code );
01526         if ( is_null( $expected ) ) {
01527             $this->assertNull( $lang->getParentLanguage(), $comment );
01528         } else {
01529             $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
01530         }
01531     }
01532 
01533     public static function provideGetParentLanguage() {
01534         return array(
01535             array( 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ),
01536             array( 'zh', 'zh', 'zh is defined as the parent language of zh, because zh converter can convert zh-cn to zh' ),
01537             array( 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ),
01538             array( 'en-gb', null, 'en does not have converter' ),
01539             array( 'en', null, 'en does not have converter. Although FakeConverter handles en -> en conversion but it is useless' ),
01540         );
01541     }
01542 
01547     public function testGetNamespaceAliases( $languageCode, $subset ) {
01548         $language = Language::factory( $languageCode );
01549         $aliases = $language->getNamespaceAliases();
01550         foreach ( $subset as $alias => $nsId ) {
01551             $this->assertEquals( $nsId, $aliases[$alias] );
01552         }
01553     }
01554 
01555     public static function provideGetNamespaceAliases() {
01556         // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
01557         return array(
01558             array(
01559                 'zh',
01560                 array(
01561                     '文件' => NS_FILE,
01562                     '檔案' => NS_FILE,
01563                 ),
01564             ),
01565         );
01566     }
01567 }