MediaWiki  REL1_24
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         $this->assertEquals(
00245             "123XXX",
00246             $this->getLang()->truncate( "123                ", 9, 'XXX' ),
00247             'truncate prefix, with spaces'
00248         );
00249         $this->assertEquals(
00250             "12345XXX",
00251             $this->getLang()->truncate( "12345            8", 11, 'XXX' ),
00252             'truncate prefix, with spaces and non-space ending'
00253         );
00254         $this->assertEquals(
00255             "XXX234",
00256             $this->getLang()->truncate( "1              234", -8, 'XXX' ),
00257             'truncate suffix, with spaces'
00258         );
00259         $this->assertEquals(
00260             "12345XXX",
00261             $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
00262             'truncate without adjustment'
00263         );
00264     }
00265 
00270     public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
00271         // Actual HTML...
00272         $this->assertEquals(
00273             $expected,
00274             $this->getLang()->truncateHTML( $input, $len, $ellipsis )
00275         );
00276     }
00277 
00281     public static function provideHTMLTruncateData() {
00282         return array(
00283             array( 0, 'XXX', "1234567890", "XXX" ),
00284             array( 8, 'XXX', "1234567890", "12345XXX" ),
00285             array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
00286             array( 2, '***',
00287                 '<p><span style="font-weight:bold;"></span></p>',
00288                 '<p><span style="font-weight:bold;"></span></p>',
00289             ),
00290             array( 2, '***',
00291                 '<p><span style="font-weight:bold;">123456789</span></p>',
00292                 '<p><span style="font-weight:bold;">***</span></p>',
00293             ),
00294             array( 2, '***',
00295                 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
00296                 '<p><span style="font-weight:bold;">***</span></p>',
00297             ),
00298             array( 3, '***',
00299                 '<p><span style="font-weight:bold;">123456789</span></p>',
00300                 '<p><span style="font-weight:bold;">***</span></p>',
00301             ),
00302             array( 4, '***',
00303                 '<p><span style="font-weight:bold;">123456789</span></p>',
00304                 '<p><span style="font-weight:bold;">1***</span></p>',
00305             ),
00306             array( 5, '***',
00307                 '<tt><span style="font-weight:bold;">123456789</span></tt>',
00308                 '<tt><span style="font-weight:bold;">12***</span></tt>',
00309             ),
00310             array( 6, '***',
00311                 '<p><a href="www.mediawiki.org">123456789</a></p>',
00312                 '<p><a href="www.mediawiki.org">123***</a></p>',
00313             ),
00314             array( 6, '***',
00315                 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
00316                 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
00317             ),
00318             array( 7, '***',
00319                 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
00320                 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
00321             ),
00322             array( 8, '***',
00323                 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
00324                 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
00325             ),
00326             array( 9, '***',
00327                 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
00328                 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
00329             ),
00330             array( 10, '***',
00331                 '<p><font style="font-weight:bold;">123456789</font></p>',
00332                 '<p><font style="font-weight:bold;">123456789</font></p>',
00333             ),
00334         );
00335     }
00336 
00342     public function testWellFormedLanguageTag( $code, $message = '' ) {
00343         $this->assertTrue(
00344             Language::isWellFormedLanguageTag( $code ),
00345             "validating code $code $message"
00346         );
00347     }
00348 
00355     public static function provideWellFormedLanguageTags() {
00356         return array(
00357             array( 'fr', 'two-letter code' ),
00358             array( 'fr-latn', 'two-letter code with lower case script code' ),
00359             array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
00360             array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
00361             array( 'fr-FR', 'two-letter code with uppercase' ),
00362             array( 'ax-TZ', 'Not in the registry, but well-formed' ),
00363             array( 'fr-shadok', 'two-letter code with variant' ),
00364             array( 'fr-y-myext-myext2', 'non-x singleton' ),
00365             array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
00366             array( 'fra', 'three-letter language code' ),
00367             array( 'fra-FX', 'three-letter language code with country code' ),
00368             array( 'i-klingon', 'grandfathered with singleton' ),
00369             array( 'I-kLINgon', 'tags are case-insensitive...' ),
00370             array( 'no-bok', 'grandfathered without singleton' ),
00371             array( 'i-enochian', 'Grandfathered' ),
00372             array( 'x-fr-CH', 'private use' ),
00373             array( 'es-419', 'two-letter code with region number' ),
00374             array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
00375             array( 'ab-x-abc-x-abc', 'anything goes after x' ),
00376             array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
00377             array( 'i-default', 'grandfathered' ),
00378             array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
00379             array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
00380             array( 'de-CH-1901', 'with country and year' ),
00381             array( 'en-US-x-twain', 'with country and singleton' ),
00382             array( 'zh-cmn', 'three-letter variant' ),
00383             array( 'zh-cmn-Hant', 'three-letter variant and script' ),
00384             array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
00385             array( 'xr-p-lze', 'Extension' ),
00386         );
00387     }
00388 
00394     public function testMalformedLanguageTag( $code, $message = '' ) {
00395         $this->assertFalse(
00396             Language::isWellFormedLanguageTag( $code ),
00397             "validating that code $code is a malformed language tag - $message"
00398         );
00399     }
00400 
00407     public static function provideMalformedLanguageTags() {
00408         return array(
00409             array( 'f', 'language too short' ),
00410             array( 'f-Latn', 'language too short with script' ),
00411             array( 'xr-lxs-qut', 'variants too short' ), # extlangS
00412             array( 'fr-Latn-F', 'region too short' ),
00413             array( 'a-value', 'language too short with region' ),
00414             array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
00415             array(
00416                 'i-notexist',
00417                 'grandfathered but not registered: invalid, even if we only test well-formedness'
00418             ),
00419             array( 'abcdefghi-012345678', 'numbers too long' ),
00420             array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
00421             array( 'ab-abcd-abc', 'invalid extensions' ),
00422             array( 'ab-ab-abc', 'invalid extensions' ),
00423             array( 'ab-123-abc', 'invalid extensions' ),
00424             array( 'a-Hant-ZH', 'short language with valid extensions' ),
00425             array( 'a1-Hant-ZH', 'invalid character in language' ),
00426             array( 'ab-abcde-abc', 'invalid extensions' ),
00427             array( 'ab-1abc-abc', 'invalid characters in extensions' ),
00428             array( 'ab-ab-abcd', 'invalid order of extensions' ),
00429             array( 'ab-123-abcd', 'invalid order of extensions' ),
00430             array( 'ab-abcde-abcd', 'invalid extensions' ),
00431             array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
00432             array( 'ab-a-b', 'extensions too short' ),
00433             array( 'ab-a-x', 'extensions too short, even with singleton' ),
00434             array( 'ab--ab', 'two separators' ),
00435             array( 'ab-abc-', 'separator in the end' ),
00436             array( '-ab-abc', 'separator in the beginning' ),
00437             array( 'abcd-efg', 'language too long' ),
00438             array( 'aabbccddE', 'tag too long' ),
00439             array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
00440             array( 'de-f', 'subtag too short' ),
00441         );
00442     }
00443 
00448     public function testLenientLanguageTag() {
00449         $this->assertTrue(
00450             Language::isWellFormedLanguageTag( 'pa_guru', true ),
00451             'pa_guru is a well-formed language tag in lenient mode'
00452         );
00453     }
00454 
00460     public function testBuiltInCodeValidation( $code, $expected, $message = '' ) {
00461         $this->assertEquals( $expected,
00462             (bool)Language::isValidBuiltInCode( $code ),
00463             "validating code $code $message"
00464         );
00465     }
00466 
00467     public static function provideLanguageCodes() {
00468         return array(
00469             array( 'fr', true, 'Two letters, minor case' ),
00470             array( 'EN', false, 'Two letters, upper case' ),
00471             array( 'tyv', true, 'Three letters' ),
00472             array( 'tokipona', true, 'long language code' ),
00473             array( 'be-tarask', true, 'With dash' ),
00474             array( 'be-x-old', true, 'With extension (two dashes)' ),
00475             array( 'be_tarask', false, 'Reject underscores' ),
00476         );
00477     }
00478 
00484     public function testKnownLanguageTag( $code, $message = '' ) {
00485         $this->assertTrue(
00486             (bool)Language::isKnownLanguageTag( $code ),
00487             "validating code $code - $message"
00488         );
00489     }
00490 
00491     public static function provideKnownLanguageTags() {
00492         return array(
00493             array( 'fr', 'simple code' ),
00494             array( 'bat-smg', 'an MW legacy tag' ),
00495             array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
00496         );
00497     }
00498 
00502     public function testKnownCldrLanguageTag() {
00503         if ( !class_exists( 'LanguageNames' ) ) {
00504             $this->markTestSkipped( 'The LanguageNames class is not available. '
00505                 . 'The CLDR extension is probably not installed.' );
00506         }
00507 
00508         $this->assertTrue(
00509             (bool)Language::isKnownLanguageTag( 'pal' ),
00510             'validating code "pal" an ancient language, which probably will '
00511                 . 'not appear in Names.php, but appears in CLDR in English'
00512         );
00513     }
00514 
00520     public function testUnknownLanguageTag( $code, $message = '' ) {
00521         $this->assertFalse(
00522             (bool)Language::isKnownLanguageTag( $code ),
00523             "checking that code $code is invalid - $message"
00524         );
00525     }
00526 
00527     public static function provideUnknownLanguageTags() {
00528         return array(
00529             array( 'mw', 'non-existent two-letter code' ),
00530             array( 'foo"<bar', 'very invalid language code' ),
00531         );
00532     }
00533 
00539     public function testSprintfDateTooShortTimestamp() {
00540         $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
00541     }
00542 
00548     public function testSprintfDateTooLongTimestamp() {
00549         $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
00550     }
00551 
00557     public function testSprintfDateNotAllDigitTimestamp() {
00558         $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
00559     }
00560 
00565     public function testSprintfDate( $format, $ts, $expected, $msg ) {
00566         $ttl = null;
00567         $this->assertEquals(
00568             $expected,
00569             $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
00570             "sprintfDate('$format', '$ts'): $msg"
00571         );
00572         if ( $ttl ) {
00573             $dt = new DateTime( $ts );
00574             $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
00575             $this->assertEquals(
00576                 $expected,
00577                 $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
00578                 "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
00579             );
00580         } else {
00581             // advance the time enough to make all of the possible outputs different (except possibly L)
00582             $dt = new DateTime( $ts );
00583             $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
00584             $this->assertEquals(
00585                 $expected,
00586                 $this->getLang()->sprintfDate( $format, $newTS, null ),
00587                 "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
00588             );
00589         }
00590     }
00591 
00597     public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
00598         $oldTZ = date_default_timezone_get();
00599         $res = date_default_timezone_set( 'Asia/Seoul' );
00600         if ( !$res ) {
00601             $this->markTestSkipped( "Error setting Timezone" );
00602         }
00603 
00604         $this->assertEquals(
00605             $expected,
00606             $this->getLang()->sprintfDate( $format, $ts ),
00607             "sprintfDate('$format', '$ts'): $msg"
00608         );
00609 
00610         date_default_timezone_set( $oldTZ );
00611     }
00612 
00618     public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
00619         $tz = new DateTimeZone( 'Asia/Seoul' );
00620         if ( !$tz ) {
00621             $this->markTestSkipped( "Error getting Timezone" );
00622         }
00623 
00624         $this->assertEquals(
00625             $expected,
00626             $this->getLang()->sprintfDate( $format, $ts, $tz ),
00627             "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
00628         );
00629     }
00630 
00631     public static function provideSprintfDateSamples() {
00632         return array(
00633             array(
00634                 'xiY',
00635                 '20111212000000',
00636                 '1390', // note because we're testing English locale we get Latin-standard digits
00637                 '1390',
00638                 'Iranian calendar full year'
00639             ),
00640             array(
00641                 'xiy',
00642                 '20111212000000',
00643                 '90',
00644                 '90',
00645                 'Iranian calendar short year'
00646             ),
00647             array(
00648                 'o',
00649                 '20120101235000',
00650                 '2011',
00651                 '2011',
00652                 'ISO 8601 (week) year'
00653             ),
00654             array(
00655                 'W',
00656                 '20120101235000',
00657                 '52',
00658                 '52',
00659                 'Week number'
00660             ),
00661             array(
00662                 'W',
00663                 '20120102235000',
00664                 '1',
00665                 '1',
00666                 'Week number'
00667             ),
00668             array(
00669                 'o-\\WW-N',
00670                 '20091231235000',
00671                 '2009-W53-4',
00672                 '2009-W53-4',
00673                 'leap week'
00674             ),
00675             // What follows is mostly copied from
00676             // https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
00677             array(
00678                 'Y',
00679                 '20120102090705',
00680                 '2012',
00681                 '2012',
00682                 'Full year'
00683             ),
00684             array(
00685                 'y',
00686                 '20120102090705',
00687                 '12',
00688                 '12',
00689                 '2 digit year'
00690             ),
00691             array(
00692                 'L',
00693                 '20120102090705',
00694                 '1',
00695                 '1',
00696                 'Leap year'
00697             ),
00698             array(
00699                 'n',
00700                 '20120102090705',
00701                 '1',
00702                 '1',
00703                 'Month index, not zero pad'
00704             ),
00705             array(
00706                 'N',
00707                 '20120102090705',
00708                 '01',
00709                 '01',
00710                 'Month index. Zero pad'
00711             ),
00712             array(
00713                 'M',
00714                 '20120102090705',
00715                 'Jan',
00716                 'Jan',
00717                 'Month abbrev'
00718             ),
00719             array(
00720                 'F',
00721                 '20120102090705',
00722                 'January',
00723                 'January',
00724                 'Full month'
00725             ),
00726             array(
00727                 'xg',
00728                 '20120102090705',
00729                 'January',
00730                 'January',
00731                 'Genitive month name (same in EN)'
00732             ),
00733             array(
00734                 'j',
00735                 '20120102090705',
00736                 '2',
00737                 '2',
00738                 'Day of month (not zero pad)'
00739             ),
00740             array(
00741                 'd',
00742                 '20120102090705',
00743                 '02',
00744                 '02',
00745                 'Day of month (zero-pad)'
00746             ),
00747             array(
00748                 'z',
00749                 '20120102090705',
00750                 '1',
00751                 '1',
00752                 'Day of year (zero-indexed)'
00753             ),
00754             array(
00755                 'D',
00756                 '20120102090705',
00757                 'Mon',
00758                 'Mon',
00759                 'Day of week (abbrev)'
00760             ),
00761             array(
00762                 'l',
00763                 '20120102090705',
00764                 'Monday',
00765                 'Monday',
00766                 'Full day of week'
00767             ),
00768             array(
00769                 'N',
00770                 '20120101090705',
00771                 '7',
00772                 '7',
00773                 'Day of week (Mon=1, Sun=7)'
00774             ),
00775             array(
00776                 'w',
00777                 '20120101090705',
00778                 '0',
00779                 '0',
00780                 'Day of week (Sun=0, Sat=6)'
00781             ),
00782             array(
00783                 'N',
00784                 '20120102090705',
00785                 '1',
00786                 '1',
00787                 'Day of week'
00788             ),
00789             array(
00790                 'a',
00791                 '20120102090705',
00792                 'am',
00793                 'am',
00794                 'am vs pm'
00795             ),
00796             array(
00797                 'A',
00798                 '20120102120000',
00799                 'PM',
00800                 'PM',
00801                 'AM vs PM'
00802             ),
00803             array(
00804                 'a',
00805                 '20120102000000',
00806                 'am',
00807                 'am',
00808                 'AM vs PM'
00809             ),
00810             array(
00811                 'g',
00812                 '20120102090705',
00813                 '9',
00814                 '9',
00815                 '12 hour, not Zero'
00816             ),
00817             array(
00818                 'h',
00819                 '20120102090705',
00820                 '09',
00821                 '09',
00822                 '12 hour, zero padded'
00823             ),
00824             array(
00825                 'G',
00826                 '20120102090705',
00827                 '9',
00828                 '9',
00829                 '24 hour, not zero'
00830             ),
00831             array(
00832                 'H',
00833                 '20120102090705',
00834                 '09',
00835                 '09',
00836                 '24 hour, zero'
00837             ),
00838             array(
00839                 'H',
00840                 '20120102110705',
00841                 '11',
00842                 '11',
00843                 '24 hour, zero'
00844             ),
00845             array(
00846                 'i',
00847                 '20120102090705',
00848                 '07',
00849                 '07',
00850                 'Minutes'
00851             ),
00852             array(
00853                 's',
00854                 '20120102090705',
00855                 '05',
00856                 '05',
00857                 'seconds'
00858             ),
00859             array(
00860                 'U',
00861                 '20120102090705',
00862                 '1325495225',
00863                 '1325462825',
00864                 'unix time'
00865             ),
00866             array(
00867                 't',
00868                 '20120102090705',
00869                 '31',
00870                 '31',
00871                 'Days in current month'
00872             ),
00873             array(
00874                 'c',
00875                 '20120102090705',
00876                 '2012-01-02T09:07:05+00:00',
00877                 '2012-01-02T09:07:05+09:00',
00878                 'ISO 8601 timestamp'
00879             ),
00880             array(
00881                 'r',
00882                 '20120102090705',
00883                 'Mon, 02 Jan 2012 09:07:05 +0000',
00884                 'Mon, 02 Jan 2012 09:07:05 +0900',
00885                 'RFC 5322'
00886             ),
00887             array(
00888                 'e',
00889                 '20120102090705',
00890                 'UTC',
00891                 'Asia/Seoul',
00892                 'Timezone identifier'
00893             ),
00894             array(
00895                 'I',
00896                 '19880602090705',
00897                 '0',
00898                 '1',
00899                 'DST indicator'
00900             ),
00901             array(
00902                 'O',
00903                 '20120102090705',
00904                 '+0000',
00905                 '+0900',
00906                 'Timezone offset'
00907             ),
00908             array(
00909                 'P',
00910                 '20120102090705',
00911                 '+00:00',
00912                 '+09:00',
00913                 'Timezone offset with colon'
00914             ),
00915             array(
00916                 'T',
00917                 '20120102090705',
00918                 'UTC',
00919                 'KST',
00920                 'Timezone abbreviation'
00921             ),
00922             array(
00923                 'Z',
00924                 '20120102090705',
00925                 '0',
00926                 '32400',
00927                 'Timezone offset in seconds'
00928             ),
00929             array(
00930                 'xmj xmF xmn xmY',
00931                 '20120102090705',
00932                 '7 Safar 2 1433',
00933                 '7 Safar 2 1433',
00934                 'Islamic'
00935             ),
00936             array(
00937                 'xij xiF xin xiY',
00938                 '20120102090705',
00939                 '12 Dey 10 1390',
00940                 '12 Dey 10 1390',
00941                 'Iranian'
00942             ),
00943             array(
00944                 'xjj xjF xjn xjY',
00945                 '20120102090705',
00946                 '7 Tevet 4 5772',
00947                 '7 Tevet 4 5772',
00948                 'Hebrew'
00949             ),
00950             array(
00951                 'xjt',
00952                 '20120102090705',
00953                 '29',
00954                 '29',
00955                 'Hebrew number of days in month'
00956             ),
00957             array(
00958                 'xjx',
00959                 '20120102090705',
00960                 'Tevet',
00961                 'Tevet',
00962                 'Hebrew genitive month name (No difference in EN)'
00963             ),
00964             array(
00965                 'xkY',
00966                 '20120102090705',
00967                 '2555',
00968                 '2555',
00969                 'Thai year'
00970             ),
00971             array(
00972                 'xoY',
00973                 '20120102090705',
00974                 '101',
00975                 '101',
00976                 'Minguo'
00977             ),
00978             array(
00979                 'xtY',
00980                 '20120102090705',
00981                 '平成24',
00982                 '平成24',
00983                 'nengo'
00984             ),
00985             array(
00986                 'xrxkYY',
00987                 '20120102090705',
00988                 'MMDLV2012',
00989                 'MMDLV2012',
00990                 'Roman numerals'
00991             ),
00992             array(
00993                 'xhxjYY',
00994                 '20120102090705',
00995                 'ה\'תשע"ב2012',
00996                 'ה\'תשע"ב2012',
00997                 'Hebrew numberals'
00998             ),
00999             array(
01000                 'xnY',
01001                 '20120102090705',
01002                 '2012',
01003                 '2012',
01004                 'Raw numerals (doesn\'t mean much in EN)'
01005             ),
01006             array(
01007                 '[[Y "(yea"\\r)]] \\"xx\\"',
01008                 '20120102090705',
01009                 '[[2012 (year)]] "x"',
01010                 '[[2012 (year)]] "x"',
01011                 'Various escaping'
01012             ),
01013 
01014         );
01015     }
01016 
01021     public function testFormatSize( $size, $expected, $msg ) {
01022         $this->assertEquals(
01023             $expected,
01024             $this->getLang()->formatSize( $size ),
01025             "formatSize('$size'): $msg"
01026         );
01027     }
01028 
01029     public static function provideFormatSizes() {
01030         return array(
01031             array(
01032                 0,
01033                 "0 B",
01034                 "Zero bytes"
01035             ),
01036             array(
01037                 1024,
01038                 "1 KB",
01039                 "1 kilobyte"
01040             ),
01041             array(
01042                 1024 * 1024,
01043                 "1 MB",
01044                 "1,024 megabytes"
01045             ),
01046             array(
01047                 1024 * 1024 * 1024,
01048                 "1 GB",
01049                 "1 gigabytes"
01050             ),
01051             array(
01052                 pow( 1024, 4 ),
01053                 "1 TB",
01054                 "1 terabyte"
01055             ),
01056             array(
01057                 pow( 1024, 5 ),
01058                 "1 PB",
01059                 "1 petabyte"
01060             ),
01061             array(
01062                 pow( 1024, 6 ),
01063                 "1 EB",
01064                 "1,024 exabyte"
01065             ),
01066             array(
01067                 pow( 1024, 7 ),
01068                 "1 ZB",
01069                 "1 zetabyte"
01070             ),
01071             array(
01072                 pow( 1024, 8 ),
01073                 "1 YB",
01074                 "1 yottabyte"
01075             ),
01076             // How big!? THIS BIG!
01077         );
01078     }
01079 
01084     public function testFormatBitrate( $bps, $expected, $msg ) {
01085         $this->assertEquals(
01086             $expected,
01087             $this->getLang()->formatBitrate( $bps ),
01088             "formatBitrate('$bps'): $msg"
01089         );
01090     }
01091 
01092     public static function provideFormatBitrate() {
01093         return array(
01094             array(
01095                 0,
01096                 "0 bps",
01097                 "0 bits per second"
01098             ),
01099             array(
01100                 999,
01101                 "999 bps",
01102                 "999 bits per second"
01103             ),
01104             array(
01105                 1000,
01106                 "1 kbps",
01107                 "1 kilobit per second"
01108             ),
01109             array(
01110                 1000 * 1000,
01111                 "1 Mbps",
01112                 "1 megabit per second"
01113             ),
01114             array(
01115                 pow( 10, 9 ),
01116                 "1 Gbps",
01117                 "1 gigabit per second"
01118             ),
01119             array(
01120                 pow( 10, 12 ),
01121                 "1 Tbps",
01122                 "1 terabit per second"
01123             ),
01124             array(
01125                 pow( 10, 15 ),
01126                 "1 Pbps",
01127                 "1 petabit per second"
01128             ),
01129             array(
01130                 pow( 10, 18 ),
01131                 "1 Ebps",
01132                 "1 exabit per second"
01133             ),
01134             array(
01135                 pow( 10, 21 ),
01136                 "1 Zbps",
01137                 "1 zetabit per second"
01138             ),
01139             array(
01140                 pow( 10, 24 ),
01141                 "1 Ybps",
01142                 "1 yottabit per second"
01143             ),
01144             array(
01145                 pow( 10, 27 ),
01146                 "1,000 Ybps",
01147                 "1,000 yottabits per second"
01148             ),
01149         );
01150     }
01151 
01156     public function testFormatDuration( $duration, $expected, $intervals = array() ) {
01157         $this->assertEquals(
01158             $expected,
01159             $this->getLang()->formatDuration( $duration, $intervals ),
01160             "formatDuration('$duration'): $expected"
01161         );
01162     }
01163 
01164     public static function provideFormatDuration() {
01165         return array(
01166             array(
01167                 0,
01168                 '0 seconds',
01169             ),
01170             array(
01171                 1,
01172                 '1 second',
01173             ),
01174             array(
01175                 2,
01176                 '2 seconds',
01177             ),
01178             array(
01179                 60,
01180                 '1 minute',
01181             ),
01182             array(
01183                 2 * 60,
01184                 '2 minutes',
01185             ),
01186             array(
01187                 3600,
01188                 '1 hour',
01189             ),
01190             array(
01191                 2 * 3600,
01192                 '2 hours',
01193             ),
01194             array(
01195                 24 * 3600,
01196                 '1 day',
01197             ),
01198             array(
01199                 2 * 86400,
01200                 '2 days',
01201             ),
01202             array(
01203                 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
01204                 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
01205                 '1 year',
01206             ),
01207             array(
01208                 2 * 31556952,
01209                 '2 years',
01210             ),
01211             array(
01212                 10 * 31556952,
01213                 '1 decade',
01214             ),
01215             array(
01216                 20 * 31556952,
01217                 '2 decades',
01218             ),
01219             array(
01220                 100 * 31556952,
01221                 '1 century',
01222             ),
01223             array(
01224                 200 * 31556952,
01225                 '2 centuries',
01226             ),
01227             array(
01228                 1000 * 31556952,
01229                 '1 millennium',
01230             ),
01231             array(
01232                 2000 * 31556952,
01233                 '2 millennia',
01234             ),
01235             array(
01236                 9001,
01237                 '2 hours, 30 minutes and 1 second'
01238             ),
01239             array(
01240                 3601,
01241                 '1 hour and 1 second'
01242             ),
01243             array(
01244                 31556952 + 2 * 86400 + 9000,
01245                 '1 year, 2 days, 2 hours and 30 minutes'
01246             ),
01247             array(
01248                 42 * 1000 * 31556952 + 42,
01249                 '42 millennia and 42 seconds'
01250             ),
01251             array(
01252                 60,
01253                 '60 seconds',
01254                 array( 'seconds' ),
01255             ),
01256             array(
01257                 61,
01258                 '61 seconds',
01259                 array( 'seconds' ),
01260             ),
01261             array(
01262                 1,
01263                 '1 second',
01264                 array( 'seconds' ),
01265             ),
01266             array(
01267                 31556952 + 2 * 86400 + 9000,
01268                 '1 year, 2 days and 150 minutes',
01269                 array( 'years', 'days', 'minutes' ),
01270             ),
01271             array(
01272                 42,
01273                 '0 days',
01274                 array( 'years', 'days' ),
01275             ),
01276             array(
01277                 31556952 + 2 * 86400 + 9000,
01278                 '1 year, 2 days and 150 minutes',
01279                 array( 'minutes', 'days', 'years' ),
01280             ),
01281             array(
01282                 42,
01283                 '0 days',
01284                 array( 'days', 'years' ),
01285             ),
01286         );
01287     }
01288 
01293     public function testCheckTitleEncoding( $s ) {
01294         $this->assertEquals(
01295             $s,
01296             $this->getLang()->checkTitleEncoding( $s ),
01297             "checkTitleEncoding('$s')"
01298         );
01299     }
01300 
01301     public static function provideCheckTitleEncodingData() {
01302         // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
01303         return array(
01304             array( "" ),
01305             array( "United States of America" ), // 7bit ASCII
01306             array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
01307             array(
01308                 rawurldecode(
01309                     "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
01310                 )
01311             ),
01312             // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
01313             // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
01314             // uses mb_check_encoding for its test.
01315             array(
01316                 rawurldecode(
01317                     "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
01318                         . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
01319                         . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
01320                         . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
01321                         . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
01322                         . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
01323                         . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
01324                         . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
01325                         . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
01326                         . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
01327                         . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
01328                         . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
01329                         . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
01330                         . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
01331                 ),
01332             ),
01333             array(
01334                 rawurldecode(
01335                     "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
01336                         . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
01337                         . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
01338                         . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
01339                         . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
01340                         . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
01341                         . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
01342                         . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
01343                         . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
01344                         . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
01345                         . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
01346                         . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
01347                         . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
01348                         . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
01349                         . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
01350                 )
01351             )
01352         );
01353         // @codingStandardsIgnoreEnd
01354     }
01355 
01360     public function testRomanNumerals( $num, $numerals ) {
01361         $this->assertEquals(
01362             $numerals,
01363             Language::romanNumeral( $num ),
01364             "romanNumeral('$num')"
01365         );
01366     }
01367 
01368     public static function provideRomanNumeralsData() {
01369         return array(
01370             array( 1, 'I' ),
01371             array( 2, 'II' ),
01372             array( 3, 'III' ),
01373             array( 4, 'IV' ),
01374             array( 5, 'V' ),
01375             array( 6, 'VI' ),
01376             array( 7, 'VII' ),
01377             array( 8, 'VIII' ),
01378             array( 9, 'IX' ),
01379             array( 10, 'X' ),
01380             array( 20, 'XX' ),
01381             array( 30, 'XXX' ),
01382             array( 40, 'XL' ),
01383             array( 49, 'XLIX' ),
01384             array( 50, 'L' ),
01385             array( 60, 'LX' ),
01386             array( 70, 'LXX' ),
01387             array( 80, 'LXXX' ),
01388             array( 90, 'XC' ),
01389             array( 99, 'XCIX' ),
01390             array( 100, 'C' ),
01391             array( 200, 'CC' ),
01392             array( 300, 'CCC' ),
01393             array( 400, 'CD' ),
01394             array( 500, 'D' ),
01395             array( 600, 'DC' ),
01396             array( 700, 'DCC' ),
01397             array( 800, 'DCCC' ),
01398             array( 900, 'CM' ),
01399             array( 999, 'CMXCIX' ),
01400             array( 1000, 'M' ),
01401             array( 1989, 'MCMLXXXIX' ),
01402             array( 2000, 'MM' ),
01403             array( 3000, 'MMM' ),
01404             array( 4000, 'MMMM' ),
01405             array( 5000, 'MMMMM' ),
01406             array( 6000, 'MMMMMM' ),
01407             array( 7000, 'MMMMMMM' ),
01408             array( 8000, 'MMMMMMMM' ),
01409             array( 9000, 'MMMMMMMMM' ),
01410             array( 9999, 'MMMMMMMMMCMXCIX' ),
01411             array( 10000, 'MMMMMMMMMM' ),
01412         );
01413     }
01414 
01419     public function testConvertPlural( $expected, $number, $forms ) {
01420         $chosen = $this->getLang()->convertPlural( $number, $forms );
01421         $this->assertEquals( $expected, $chosen );
01422     }
01423 
01424     public static function providePluralData() {
01425         // Params are: [expected text, number given, [the plural forms]]
01426         return array(
01427             array( 'plural', 0, array(
01428                 'singular', 'plural'
01429             ) ),
01430             array( 'explicit zero', 0, array(
01431                 '0=explicit zero', 'singular', 'plural'
01432             ) ),
01433             array( 'explicit one', 1, array(
01434                 'singular', 'plural', '1=explicit one',
01435             ) ),
01436             array( 'singular', 1, array(
01437                 'singular', 'plural', '0=explicit zero',
01438             ) ),
01439             array( 'plural', 3, array(
01440                 '0=explicit zero', '1=explicit one', 'singular', 'plural'
01441             ) ),
01442             array( 'explicit eleven', 11, array(
01443                 'singular', 'plural', '11=explicit eleven',
01444             ) ),
01445             array( 'plural', 12, array(
01446                 'singular', 'plural', '11=explicit twelve',
01447             ) ),
01448             array( 'plural', 12, array(
01449                 'singular', 'plural', '=explicit form',
01450             ) ),
01451             array( 'other', 2, array(
01452                 'kissa=kala', '1=2=3', 'other',
01453             ) ),
01454             array( '', 2, array(
01455                 '0=explicit zero', '1=explicit one',
01456             ) ),
01457         );
01458     }
01459 
01464     public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
01465         $lang = $this->getLang();
01466         if ( is_array( $expectedData ) ) {
01467             list( $func, $arg ) = $expectedData;
01468             $expected = $lang->$func( $arg );
01469         } else {
01470             $expected = $expectedData;
01471         }
01472         $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
01473     }
01474 
01475     public static function provideTranslateBlockExpiry() {
01476         return array(
01477             array( '2 hours', '2 hours', 'simple data from ipboptions' ),
01478             array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
01479             array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
01480             array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
01481             array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
01482             array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
01483             array( array( 'formatDuration', 0 ), 'now', 'now' ),
01484             array(
01485                 array( 'timeanddate', '20120102070000' ),
01486                 '2012-1-1 7:00 +1 day',
01487                 'mixed, handled as absolute'
01488             ),
01489             array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
01490             array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
01491             array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
01492             array( 'dummy', 'dummy', 'return garbage as is' ),
01493         );
01494     }
01495 
01499     public function testParseFormattedNumber( $langCode, $number ) {
01500         $lang = Language::factory( $langCode );
01501 
01502         $localisedNum = $lang->formatNum( $number );
01503         $normalisedNum = $lang->parseFormattedNumber( $localisedNum );
01504 
01505         $this->assertEquals( $number, $normalisedNum );
01506     }
01507 
01508     public function parseFormattedNumberProvider() {
01509         return array(
01510             array( 'de', 377.01 ),
01511             array( 'fa', 334 ),
01512             array( 'fa', 382.772 ),
01513             array( 'ar', 1844 ),
01514             array( 'lzh', 3731 ),
01515             array( 'zh-classical', 7432 )
01516         );
01517     }
01518 
01523     public function testCommafy( $number, $numbersWithCommas ) {
01524         $this->assertEquals(
01525             $numbersWithCommas,
01526             $this->getLang()->commafy( $number ),
01527             "commafy('$number')"
01528         );
01529     }
01530 
01531     public static function provideCommafyData() {
01532         return array(
01533             array( -1, '-1' ),
01534             array( 10, '10' ),
01535             array( 100, '100' ),
01536             array( 1000, '1,000' ),
01537             array( 10000, '10,000' ),
01538             array( 100000, '100,000' ),
01539             array( 1000000, '1,000,000' ),
01540             array( -1.0001, '-1.0001' ),
01541             array( 1.0001, '1.0001' ),
01542             array( 10.0001, '10.0001' ),
01543             array( 100.0001, '100.0001' ),
01544             array( 1000.0001, '1,000.0001' ),
01545             array( 10000.0001, '10,000.0001' ),
01546             array( 100000.0001, '100,000.0001' ),
01547             array( 1000000.0001, '1,000,000.0001' ),
01548             array( '200000000000000000000', '200,000,000,000,000,000,000' ),
01549             array( '-200000000000000000000', '-200,000,000,000,000,000,000' ),
01550         );
01551     }
01552 
01556     public function testListToText() {
01557         $lang = $this->getLang();
01558         $and = $lang->getMessageFromDB( 'and' );
01559         $s = $lang->getMessageFromDB( 'word-separator' );
01560         $c = $lang->getMessageFromDB( 'comma-separator' );
01561 
01562         $this->assertEquals( '', $lang->listToText( array() ) );
01563         $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
01564         $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
01565         $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
01566         $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
01567     }
01568 
01573     public function testIsSupportedLanguage( $code, $expected, $comment ) {
01574         $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
01575     }
01576 
01577     public static function provideIsSupportedLanguage() {
01578         return array(
01579             array( 'en', true, 'is supported language' ),
01580             array( 'fi', true, 'is supported language' ),
01581             array( 'bunny', false, 'is not supported language' ),
01582             array( 'FI', false, 'is not supported language, input should be in lower case' ),
01583         );
01584     }
01585 
01590     public function testGetParentLanguage( $code, $expected, $comment ) {
01591         $lang = Language::factory( $code );
01592         if ( is_null( $expected ) ) {
01593             $this->assertNull( $lang->getParentLanguage(), $comment );
01594         } else {
01595             $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
01596         }
01597     }
01598 
01599     public static function provideGetParentLanguage() {
01600         return array(
01601             array( 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ),
01602             array( 'zh', 'zh', 'zh is defined as the parent language of zh, '
01603                 . 'because zh converter can convert zh-cn to zh' ),
01604             array( 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ),
01605             array( 'en-gb', null, 'en does not have converter' ),
01606             array( 'en', null, 'en does not have converter. Although FakeConverter '
01607                     . 'handles en -> en conversion but it is useless' ),
01608         );
01609     }
01610 
01615     public function testGetNamespaceAliases( $languageCode, $subset ) {
01616         $language = Language::factory( $languageCode );
01617         $aliases = $language->getNamespaceAliases();
01618         foreach ( $subset as $alias => $nsId ) {
01619             $this->assertEquals( $nsId, $aliases[$alias] );
01620         }
01621     }
01622 
01623     public static function provideGetNamespaceAliases() {
01624         // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
01625         return array(
01626             array(
01627                 'zh',
01628                 array(
01629                     '文件' => NS_FILE,
01630                     '檔案' => NS_FILE,
01631                 ),
01632             ),
01633         );
01634     }
01635 }