3 class LanguageTest
extends LanguageClassesTestCase
{
5 * @covers Language::convertDoubleWidth
6 * @covers Language::normalizeForSearch
8 public function testLanguageConvertDoubleWidthToSingleWidth() {
10 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
11 $this->getLang()->normalizeForSearch(
12 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
14 'convertDoubleWidth() with the full alphabet and digits'
19 * @dataProvider provideFormattableTimes#
20 * @covers Language::formatTimePeriod
22 public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
23 $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
26 public static function provideFormattableTimes() {
32 'formatTimePeriod() rounding (<10s)'
36 array( 'noabbrevs' => true ),
38 'formatTimePeriod() rounding (<10s)'
44 'formatTimePeriod() rounding (<10s)'
48 array( 'noabbrevs' => true ),
50 'formatTimePeriod() rounding (<10s)'
56 'formatTimePeriod() rounding (<60s)'
60 array( 'noabbrevs' => true ),
62 'formatTimePeriod() rounding (<60s)'
68 'formatTimePeriod() rounding (<1h)'
72 array( 'noabbrevs' => true ),
73 '2 minutes 0 seconds',
74 'formatTimePeriod() rounding (<1h)'
80 'formatTimePeriod() rounding (<1h)'
84 array( 'noabbrevs' => true ),
85 '1 hour 0 minutes 0 seconds',
86 'formatTimePeriod() rounding (<1h)'
92 'formatTimePeriod() rounding (>=1h)'
96 array( 'noabbrevs' => true ),
97 '2 hours 0 minutes 0 seconds',
98 'formatTimePeriod() rounding (>=1h)'
104 'formatTimePeriod() rounding (>=1h), avoidseconds'
108 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
110 'formatTimePeriod() rounding (>=1h), avoidseconds'
116 'formatTimePeriod() rounding (>=1h), avoidminutes'
120 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
122 'formatTimePeriod() rounding (>=1h), avoidminutes'
128 'formatTimePeriod() rounding (=48h), avoidseconds'
132 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
133 '48 hours 0 minutes',
134 'formatTimePeriod() rounding (=48h), avoidseconds'
140 'formatTimePeriod() rounding (>48h), avoidminutes'
144 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
146 'formatTimePeriod() rounding (>48h), avoidminutes'
152 'formatTimePeriod() rounding (>48h), avoidseconds'
156 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
157 '2 days 1 hour 0 minutes',
158 'formatTimePeriod() rounding (>48h), avoidseconds'
164 'formatTimePeriod() rounding (>48h), avoidminutes'
168 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
170 'formatTimePeriod() rounding (>48h), avoidminutes'
176 'formatTimePeriod() rounding (>48h), avoidseconds'
180 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
181 '3 days 0 hours 0 minutes',
182 'formatTimePeriod() rounding (>48h), avoidseconds'
188 'formatTimePeriod() rounding, (>48h), avoidseconds'
192 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
193 '2 days 0 hours 0 minutes',
194 'formatTimePeriod() rounding, (>48h), avoidseconds'
200 'formatTimePeriod() rounding, recursion, (>48h)'
204 array( 'noabbrevs' => true ),
205 '2 days 1 hour 1 minute 1 second',
206 'formatTimePeriod() rounding, recursion, (>48h)'
212 * @covers Language::truncate
214 public function testTruncate() {
217 $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
218 'truncate prefix, len 0, small ellipsis'
223 $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
224 'truncate prefix, small ellipsis'
229 $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
230 'truncate prefix, large ellipsis'
235 $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
236 'truncate suffix, small ellipsis'
241 $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
242 'truncate suffix, large ellipsis'
247 * @dataProvider provideHTMLTruncateData
248 * @covers Language::truncateHTML
250 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
254 $this->getLang()->truncateHTML( $input, $len, $ellipsis )
259 * @return array format is ($len, $ellipsis, $input, $expected)
261 public static function provideHTMLTruncateData() {
263 array( 0, 'XXX', "1234567890", "XXX" ),
264 array( 8, 'XXX', "1234567890", "12345XXX" ),
265 array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
267 '<p><span style="font-weight:bold;"></span></p>',
268 '<p><span style="font-weight:bold;"></span></p>',
271 '<p><span style="font-weight:bold;">123456789</span></p>',
272 '<p><span style="font-weight:bold;">***</span></p>',
275 '<p><span style="font-weight:bold;"> 23456789</span></p>',
276 '<p><span style="font-weight:bold;">***</span></p>',
279 '<p><span style="font-weight:bold;">123456789</span></p>',
280 '<p><span style="font-weight:bold;">***</span></p>',
283 '<p><span style="font-weight:bold;">123456789</span></p>',
284 '<p><span style="font-weight:bold;">1***</span></p>',
287 '<tt><span style="font-weight:bold;">123456789</span></tt>',
288 '<tt><span style="font-weight:bold;">12***</span></tt>',
291 '<p><a href="www.mediawiki.org">123456789</a></p>',
292 '<p><a href="www.mediawiki.org">123***</a></p>',
295 '<p><a href="www.mediawiki.org">12 456789</a></p>',
296 '<p><a href="www.mediawiki.org">12 ***</a></p>',
299 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
300 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
303 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
304 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
307 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
308 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
311 '<p><font style="font-weight:bold;">123456789</font></p>',
312 '<p><font style="font-weight:bold;">123456789</font></p>',
318 * Test Language::isWellFormedLanguageTag()
319 * @dataProvider provideWellFormedLanguageTags
320 * @covers Language::isWellFormedLanguageTag
322 public function testWellFormedLanguageTag( $code, $message = '' ) {
324 Language
::isWellFormedLanguageTag( $code ),
325 "validating code $code $message"
330 * The test cases are based on the tests in the GaBuZoMeu parser
331 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
332 * and distributed as free software, under the GNU General Public Licence.
333 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
335 public static function provideWellFormedLanguageTags() {
337 array( 'fr', 'two-letter code' ),
338 array( 'fr-latn', 'two-letter code with lower case script code' ),
339 array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
340 array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
341 array( 'fr-FR', 'two-letter code with uppercase' ),
342 array( 'ax-TZ', 'Not in the registry, but well-formed' ),
343 array( 'fr-shadok', 'two-letter code with variant' ),
344 array( 'fr-y-myext-myext2', 'non-x singleton' ),
345 array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
346 array( 'fra', 'three-letter language code' ),
347 array( 'fra-FX', 'three-letter language code with country code' ),
348 array( 'i-klingon', 'grandfathered with singleton' ),
349 array( 'I-kLINgon', 'tags are case-insensitive...' ),
350 array( 'no-bok', 'grandfathered without singleton' ),
351 array( 'i-enochian', 'Grandfathered' ),
352 array( 'x-fr-CH', 'private use' ),
353 array( 'es-419', 'two-letter code with region number' ),
354 array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
355 array( 'ab-x-abc-x-abc', 'anything goes after x' ),
356 array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
357 array( 'i-default', 'grandfathered' ),
358 array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
359 array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
360 array( 'de-CH-1901', 'with country and year' ),
361 array( 'en-US-x-twain', 'with country and singleton' ),
362 array( 'zh-cmn', 'three-letter variant' ),
363 array( 'zh-cmn-Hant', 'three-letter variant and script' ),
364 array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
365 array( 'xr-p-lze', 'Extension' ),
370 * Negative test for Language::isWellFormedLanguageTag()
371 * @dataProvider provideMalformedLanguageTags
372 * @covers Language::isWellFormedLanguageTag
374 public function testMalformedLanguageTag( $code, $message = '' ) {
376 Language
::isWellFormedLanguageTag( $code ),
377 "validating that code $code is a malformed language tag - $message"
382 * The test cases are based on the tests in the GaBuZoMeu parser
383 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
384 * and distributed as free software, under the GNU General Public Licence.
385 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
387 public static function provideMalformedLanguageTags() {
389 array( 'f', 'language too short' ),
390 array( 'f-Latn', 'language too short with script' ),
391 array( 'xr-lxs-qut', 'variants too short' ), # extlangS
392 array( 'fr-Latn-F', 'region too short' ),
393 array( 'a-value', 'language too short with region' ),
394 array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
395 array( 'i-notexist', 'grandfathered but not registered: invalid, even if we only test well-formedness' ),
396 array( 'abcdefghi-012345678', 'numbers too long' ),
397 array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
398 array( 'ab-abcd-abc', 'invalid extensions' ),
399 array( 'ab-ab-abc', 'invalid extensions' ),
400 array( 'ab-123-abc', 'invalid extensions' ),
401 array( 'a-Hant-ZH', 'short language with valid extensions' ),
402 array( 'a1-Hant-ZH', 'invalid character in language' ),
403 array( 'ab-abcde-abc', 'invalid extensions' ),
404 array( 'ab-1abc-abc', 'invalid characters in extensions' ),
405 array( 'ab-ab-abcd', 'invalid order of extensions' ),
406 array( 'ab-123-abcd', 'invalid order of extensions' ),
407 array( 'ab-abcde-abcd', 'invalid extensions' ),
408 array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
409 array( 'ab-a-b', 'extensions too short' ),
410 array( 'ab-a-x', 'extensions too short, even with singleton' ),
411 array( 'ab--ab', 'two separators' ),
412 array( 'ab-abc-', 'separator in the end' ),
413 array( '-ab-abc', 'separator in the beginning' ),
414 array( 'abcd-efg', 'language too long' ),
415 array( 'aabbccddE', 'tag too long' ),
416 array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
417 array( 'de-f', 'subtag too short' ),
422 * Negative test for Language::isWellFormedLanguageTag()
423 * @covers Language::isWellFormedLanguageTag
425 public function testLenientLanguageTag() {
427 Language
::isWellFormedLanguageTag( 'pa_guru', true ),
428 'pa_guru is a well-formed language tag in lenient mode'
433 * Test Language::isValidBuiltInCode()
434 * @dataProvider provideLanguageCodes
435 * @covers Language::isValidBuiltInCode
437 public function testBuiltInCodeValidation( $code, $message = '' ) {
439 (bool)Language
::isValidBuiltInCode( $code ),
440 "validating code $code $message"
445 * @covers Language::isValidBuiltInCode
447 public function testBuiltInCodeValidationRejectUnderscore() {
449 (bool)Language
::isValidBuiltInCode( 'be_tarask' ),
450 "reject underscore in language code"
454 public static function provideLanguageCodes() {
456 array( 'fr', 'Two letters, minor case' ),
457 array( 'EN', 'Two letters, upper case' ),
458 array( 'tyv', 'Three letters' ),
459 array( 'tokipona', 'long language code' ),
460 array( 'be-tarask', 'With dash' ),
461 array( 'Zh-classical', 'Begin with upper case, dash' ),
462 array( 'Be-x-old', 'With extension (two dashes)' ),
467 * Test Language::isKnownLanguageTag()
468 * @dataProvider provideKnownLanguageTags
469 * @covers Language::isKnownLanguageTag
471 public function testKnownLanguageTag( $code, $message = '' ) {
473 (bool)Language
::isKnownLanguageTag( $code ),
474 "validating code $code - $message"
478 public static function provideKnownLanguageTags() {
480 array( 'fr', 'simple code' ),
481 array( 'bat-smg', 'an MW legacy tag' ),
482 array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
487 * @covers Language::isKnownLanguageTag
489 public function testKnownCldrLanguageTag() {
490 if ( !class_exists( 'LanguageNames' ) ) {
491 $this->markTestSkipped( 'The LanguageNames class is not available. The cldr extension is probably not installed.' );
495 (bool)Language
::isKnownLanguageTag( 'pal' ),
496 'validating code "pal" an ancient language, which probably will not appear in Names.php, but appears in CLDR in English'
501 * Negative tests for Language::isKnownLanguageTag()
502 * @dataProvider provideUnKnownLanguageTags
503 * @covers Language::isKnownLanguageTag
505 public function testUnknownLanguageTag( $code, $message = '' ) {
507 (bool)Language
::isKnownLanguageTag( $code ),
508 "checking that code $code is invalid - $message"
512 public static function provideUnknownLanguageTags() {
514 array( 'mw', 'non-existent two-letter code' ),
515 array( 'foo"<bar', 'very invalid language code' ),
520 * Test too short timestamp
521 * @expectedException MWException
522 * @covers Language::sprintfDate
524 public function testSprintfDateTooShortTimestamp() {
525 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
529 * Test too long timestamp
530 * @expectedException MWException
531 * @covers Language::sprintfDate
533 public function testSprintfDateTooLongTimestamp() {
534 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
538 * Test too short timestamp
539 * @expectedException MWException
540 * @covers Language::sprintfDate
542 public function testSprintfDateNotAllDigitTimestamp() {
543 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
547 * @dataProvider provideSprintfDateSamples
548 * @covers Language::sprintfDate
550 public function testSprintfDate( $format, $ts, $expected, $msg ) {
553 $this->getLang()->sprintfDate( $format, $ts ),
554 "sprintfDate('$format', '$ts'): $msg"
559 * sprintfDate should always use UTC when no zone is given.
560 * @dataProvider provideSprintfDateSamples
561 * @covers Language::sprintfDate
563 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
564 $oldTZ = date_default_timezone_get();
565 $res = date_default_timezone_set( 'Asia/Seoul' );
567 $this->markTestSkipped( "Error setting Timezone" );
572 $this->getLang()->sprintfDate( $format, $ts ),
573 "sprintfDate('$format', '$ts'): $msg"
576 date_default_timezone_set( $oldTZ );
580 * sprintfDate should use passed timezone
581 * @dataProvider provideSprintfDateSamples
582 * @covers Language::sprintfDate
584 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
585 $tz = new DateTimeZone( 'Asia/Seoul' );
587 $this->markTestSkipped( "Error getting Timezone" );
592 $this->getLang()->sprintfDate( $format, $ts, $tz ),
593 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
597 public static function provideSprintfDateSamples() {
602 '1390', // note because we're testing English locale we get Latin-standard digits
604 'Iranian calendar full year'
611 'Iranian calendar short year'
618 'ISO 8601 (week) year'
641 // What follows is mostly copied from http://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
668 'Month index, not zero pad'
675 'Month index. Zero pad'
696 'Genitive month name (same in EN)'
703 'Day of month (not zero pad)'
710 'Day of month (zero-pad)'
717 'Day of year (zero-indexed)'
724 'Day of week (abbrev)'
738 'Day of week (Mon=1, Sun=7)'
745 'Day of week (Sun=0, Sat=6)'
787 '12 hour, zero padded'
836 'Days in current month'
841 '2012-01-02T09:07:05+00:00',
842 '2012-01-02T09:07:05+09:00',
848 'Mon, 02 Jan 2012 09:07:05 +0000',
849 'Mon, 02 Jan 2012 09:07:05 +0900',
857 'Timezone identifier'
878 'Timezone offset with colon'
885 'Timezone abbreviation'
892 'Timezone offset in seconds'
920 'Hebrew number of days in month'
927 'Hebrew genitive month name (No difference in EN)'
969 'Raw numerals (doesn\'t mean much in EN)'
972 '[[Y "(yea"\\r)]] \\"xx\\"',
974 '[[2012 (year)]] "x"',
975 '[[2012 (year)]] "x"',
983 * @dataProvider provideFormatSizes
984 * @covers Language::formatSize
986 public function testFormatSize( $size, $expected, $msg ) {
989 $this->getLang()->formatSize( $size ),
990 "formatSize('$size'): $msg"
994 public static function provideFormatSizes() {
1041 // How big!? THIS BIG!
1046 * @dataProvider provideFormatBitrate
1047 * @covers Language::formatBitrate
1049 public function testFormatBitrate( $bps, $expected, $msg ) {
1050 $this->assertEquals(
1052 $this->getLang()->formatBitrate( $bps ),
1053 "formatBitrate('$bps'): $msg"
1057 public static function provideFormatBitrate() {
1067 "999 bits per second"
1072 "1 kilobit per second"
1077 "1 megabit per second"
1082 "1 gigabit per second"
1087 "1 terabit per second"
1092 "1 petabit per second"
1097 "1 exabit per second"
1102 "1 zetabit per second"
1107 "1 yottabit per second"
1112 "1,000 yottabits per second"
1119 * @dataProvider provideFormatDuration
1120 * @covers Language::formatDuration
1122 public function testFormatDuration( $duration, $expected, $intervals = array() ) {
1123 $this->assertEquals(
1125 $this->getLang()->formatDuration( $duration, $intervals ),
1126 "formatDuration('$duration'): $expected"
1130 public static function provideFormatDuration() {
1169 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1170 ( 365 +
( 24 * 3 +
25 ) / 400.0 ) * 86400,
1203 '2 hours, 30 minutes and 1 second'
1207 '1 hour and 1 second'
1210 31556952 +
2 * 86400 +
9000,
1211 '1 year, 2 days, 2 hours and 30 minutes'
1214 42 * 1000 * 31556952 +
42,
1215 '42 millennia and 42 seconds'
1233 31556952 +
2 * 86400 +
9000,
1234 '1 year, 2 days and 150 minutes',
1235 array( 'years', 'days', 'minutes' ),
1240 array( 'years', 'days' ),
1243 31556952 +
2 * 86400 +
9000,
1244 '1 year, 2 days and 150 minutes',
1245 array( 'minutes', 'days', 'years' ),
1250 array( 'days', 'years' ),
1256 * @dataProvider provideCheckTitleEncodingData
1257 * @covers Language::checkTitleEncoding
1259 public function testCheckTitleEncoding( $s ) {
1260 $this->assertEquals(
1262 $this->getLang()->checkTitleEncoding( $s ),
1263 "checkTitleEncoding('$s')"
1267 public static function provideCheckTitleEncodingData() {
1270 array( "United States of America" ), // 7bit ASCII
1271 array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
1274 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1277 // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
1278 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1279 // uses mb_check_encoding for its test.
1282 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1283 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1284 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1285 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1286 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1287 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1288 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1289 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1290 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1291 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1292 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1293 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1294 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1295 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1300 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1301 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1302 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1303 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1304 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1305 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1306 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1307 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1308 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1309 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1310 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1311 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1312 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1313 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1314 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1321 * @dataProvider provideRomanNumeralsData
1322 * @covers Language::romanNumeral
1324 public function testRomanNumerals( $num, $numerals ) {
1325 $this->assertEquals(
1327 Language
::romanNumeral( $num ),
1328 "romanNumeral('$num')"
1332 public static function provideRomanNumeralsData() {
1347 array( 49, 'XLIX' ),
1351 array( 80, 'LXXX' ),
1353 array( 99, 'XCIX' ),
1356 array( 300, 'CCC' ),
1360 array( 700, 'DCC' ),
1361 array( 800, 'DCCC' ),
1363 array( 999, 'CMXCIX' ),
1365 array( 1989, 'MCMLXXXIX' ),
1366 array( 2000, 'MM' ),
1367 array( 3000, 'MMM' ),
1368 array( 4000, 'MMMM' ),
1369 array( 5000, 'MMMMM' ),
1370 array( 6000, 'MMMMMM' ),
1371 array( 7000, 'MMMMMMM' ),
1372 array( 8000, 'MMMMMMMM' ),
1373 array( 9000, 'MMMMMMMMM' ),
1374 array( 9999, 'MMMMMMMMMCMXCIX' ),
1375 array( 10000, 'MMMMMMMMMM' ),
1380 * @dataProvider providePluralData
1381 * @covers Language::convertPlural
1383 public function testConvertPlural( $expected, $number, $forms ) {
1384 $chosen = $this->getLang()->convertPlural( $number, $forms );
1385 $this->assertEquals( $expected, $chosen );
1388 public static function providePluralData() {
1389 // Params are: [expected text, number given, [the plural forms]]
1391 array( 'plural', 0, array(
1392 'singular', 'plural'
1394 array( 'explicit zero', 0, array(
1395 '0=explicit zero', 'singular', 'plural'
1397 array( 'explicit one', 1, array(
1398 'singular', 'plural', '1=explicit one',
1400 array( 'singular', 1, array(
1401 'singular', 'plural', '0=explicit zero',
1403 array( 'plural', 3, array(
1404 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1406 array( 'explicit eleven', 11, array(
1407 'singular', 'plural', '11=explicit eleven',
1409 array( 'plural', 12, array(
1410 'singular', 'plural', '11=explicit twelve',
1412 array( 'plural', 12, array(
1413 'singular', 'plural', '=explicit form',
1415 array( 'other', 2, array(
1416 'kissa=kala', '1=2=3', 'other',
1418 array( '', 2, array(
1419 '0=explicit zero', '1=explicit one',
1425 * @covers Language::translateBlockExpiry()
1426 * @dataProvider provideTranslateBlockExpiry
1428 public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
1429 $lang = $this->getLang();
1430 if ( is_array( $expectedData ) ) {
1431 list( $func, $arg ) = $expectedData;
1432 $expected = $lang->$func( $arg );
1434 $expected = $expectedData;
1436 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1439 public static function provideTranslateBlockExpiry() {
1441 array( '2 hours', '2 hours', 'simple data from ipboptions' ),
1442 array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
1443 array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
1444 array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
1445 array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
1446 array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
1447 array( array( 'formatDuration', 0 ), 'now', 'now' ),
1448 array( array( 'timeanddate', '20120102070000' ), '2012-1-1 7:00 +1 day', 'mixed, handled as absolute' ),
1449 array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
1450 array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
1451 array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
1452 array( 'dummy', 'dummy', 'return garbage as is' ),
1457 * @covers Language::commafy()
1458 * @dataProvider provideCommafyData
1460 public function testCommafy( $number, $numbersWithCommas ) {
1461 $this->assertEquals(
1463 $this->getLang()->commafy( $number ),
1464 "commafy('$number')"
1468 public static function provideCommafyData() {
1472 array( 100, '100' ),
1473 array( 1000, '1,000' ),
1474 array( 10000, '10,000' ),
1475 array( 100000, '100,000' ),
1476 array( 1000000, '1,000,000' ),
1477 array( 1.0001, '1.0001' ),
1478 array( 10.0001, '10.0001' ),
1479 array( 100.0001, '100.0001' ),
1480 array( 1000.0001, '1,000.0001' ),
1481 array( 10000.0001, '10,000.0001' ),
1482 array( 100000.0001, '100,000.0001' ),
1483 array( 1000000.0001, '1,000,000.0001' ),
1488 * @covers Language::listToText
1490 public function testListToText() {
1491 $lang = $this->getLang();
1492 $and = $lang->getMessageFromDB( 'and' );
1493 $s = $lang->getMessageFromDB( 'word-separator' );
1494 $c = $lang->getMessageFromDB( 'comma-separator' );
1496 $this->assertEquals( '', $lang->listToText( array() ) );
1497 $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
1498 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
1499 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
1500 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
1504 * @dataProvider provideIsSupportedLanguage
1505 * @covers Language::isSupportedLanguage
1507 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1508 $this->assertEquals( $expected, Language
::isSupportedLanguage( $code ), $comment );
1511 public static function provideIsSupportedLanguage() {
1513 array( 'en', true, 'is supported language' ),
1514 array( 'fi', true, 'is supported language' ),
1515 array( 'bunny', false, 'is not supported language' ),
1516 array( 'FI', false, 'is not supported language, input should be in lower case' ),
1521 * @dataProvider provideGetParentLanguage
1522 * @covers Language::getParentLanguage
1524 public function testGetParentLanguage( $code, $expected, $comment ) {
1525 $lang = Language
::factory( $code );
1526 if ( is_null( $expected ) ) {
1527 $this->assertNull( $lang->getParentLanguage(), $comment );
1529 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1533 public static function provideGetParentLanguage() {
1535 array( 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ),
1536 array( 'zh', 'zh', 'zh is defined as the parent language of zh, because zh converter can convert zh-cn to zh' ),
1537 array( 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ),
1538 array( 'en-gb', null, 'en does not have converter' ),
1539 array( 'en', null, 'en does not have converter. Although FakeConverter handles en -> en conversion but it is useless' ),
1544 * @dataProvider provideGetNamespaceAliases
1545 * @covers Language::getNamespaceAliases
1547 public function testGetNamespaceAliases( $languageCode, $subset ) {
1548 $language = Language
::factory( $languageCode );
1549 $aliases = $language->getNamespaceAliases();
1550 foreach ( $subset as $alias => $nsId ) {
1551 $this->assertEquals( $nsId, $aliases[$alias] );
1555 public static function provideGetNamespaceAliases() {
1556 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces