test: coverage recording now needs to be explicit
[mediawiki.git] / tests / phpunit / languages / LanguageTest.php
blob42be9364840688b94aefbc26ee723e116e7ef3c8
1 <?php
3 class LanguageTest extends LanguageClassesTestCase {
4 function testLanguageConvertDoubleWidthToSingleWidth() {
5 $this->assertEquals(
6 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7 $this->getLang()->normalizeForSearch(
8 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
9 ),
10 'convertDoubleWidth() with the full alphabet and digits'
14 /**
15 * @dataProvider provideFormattableTimes
17 function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
18 $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
21 public static function provideFormattableTimes() {
22 return array(
23 array(
24 9.45,
25 array(),
26 '9.5 s',
27 'formatTimePeriod() rounding (<10s)'
29 array(
30 9.45,
31 array( 'noabbrevs' => true ),
32 '9.5 seconds',
33 'formatTimePeriod() rounding (<10s)'
35 array(
36 9.95,
37 array(),
38 '10 s',
39 'formatTimePeriod() rounding (<10s)'
41 array(
42 9.95,
43 array( 'noabbrevs' => true ),
44 '10 seconds',
45 'formatTimePeriod() rounding (<10s)'
47 array(
48 59.55,
49 array(),
50 '1 min 0 s',
51 'formatTimePeriod() rounding (<60s)'
53 array(
54 59.55,
55 array( 'noabbrevs' => true ),
56 '1 minute 0 seconds',
57 'formatTimePeriod() rounding (<60s)'
59 array(
60 119.55,
61 array(),
62 '2 min 0 s',
63 'formatTimePeriod() rounding (<1h)'
65 array(
66 119.55,
67 array( 'noabbrevs' => true ),
68 '2 minutes 0 seconds',
69 'formatTimePeriod() rounding (<1h)'
71 array(
72 3599.55,
73 array(),
74 '1 h 0 min 0 s',
75 'formatTimePeriod() rounding (<1h)'
77 array(
78 3599.55,
79 array( 'noabbrevs' => true ),
80 '1 hour 0 minutes 0 seconds',
81 'formatTimePeriod() rounding (<1h)'
83 array(
84 7199.55,
85 array(),
86 '2 h 0 min 0 s',
87 'formatTimePeriod() rounding (>=1h)'
89 array(
90 7199.55,
91 array( 'noabbrevs' => true ),
92 '2 hours 0 minutes 0 seconds',
93 'formatTimePeriod() rounding (>=1h)'
95 array(
96 7199.55,
97 'avoidseconds',
98 '2 h 0 min',
99 'formatTimePeriod() rounding (>=1h), avoidseconds'
101 array(
102 7199.55,
103 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
104 '2 hours 0 minutes',
105 'formatTimePeriod() rounding (>=1h), avoidseconds'
107 array(
108 7199.55,
109 'avoidminutes',
110 '2 h 0 min',
111 'formatTimePeriod() rounding (>=1h), avoidminutes'
113 array(
114 7199.55,
115 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
116 '2 hours 0 minutes',
117 'formatTimePeriod() rounding (>=1h), avoidminutes'
119 array(
120 172799.55,
121 'avoidseconds',
122 '48 h 0 min',
123 'formatTimePeriod() rounding (=48h), avoidseconds'
125 array(
126 172799.55,
127 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
128 '48 hours 0 minutes',
129 'formatTimePeriod() rounding (=48h), avoidseconds'
131 array(
132 259199.55,
133 'avoidminutes',
134 '3 d 0 h',
135 'formatTimePeriod() rounding (>48h), avoidminutes'
137 array(
138 259199.55,
139 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
140 '3 days 0 hours',
141 'formatTimePeriod() rounding (>48h), avoidminutes'
143 array(
144 176399.55,
145 'avoidseconds',
146 '2 d 1 h 0 min',
147 'formatTimePeriod() rounding (>48h), avoidseconds'
149 array(
150 176399.55,
151 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
152 '2 days 1 hour 0 minutes',
153 'formatTimePeriod() rounding (>48h), avoidseconds'
155 array(
156 176399.55,
157 'avoidminutes',
158 '2 d 1 h',
159 'formatTimePeriod() rounding (>48h), avoidminutes'
161 array(
162 176399.55,
163 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
164 '2 days 1 hour',
165 'formatTimePeriod() rounding (>48h), avoidminutes'
167 array(
168 259199.55,
169 'avoidseconds',
170 '3 d 0 h 0 min',
171 'formatTimePeriod() rounding (>48h), avoidseconds'
173 array(
174 259199.55,
175 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
176 '3 days 0 hours 0 minutes',
177 'formatTimePeriod() rounding (>48h), avoidseconds'
179 array(
180 172801.55,
181 'avoidseconds',
182 '2 d 0 h 0 min',
183 'formatTimePeriod() rounding, (>48h), avoidseconds'
185 array(
186 172801.55,
187 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
188 '2 days 0 hours 0 minutes',
189 'formatTimePeriod() rounding, (>48h), avoidseconds'
191 array(
192 176460.55,
193 array(),
194 '2 d 1 h 1 min 1 s',
195 'formatTimePeriod() rounding, recursion, (>48h)'
197 array(
198 176460.55,
199 array( 'noabbrevs' => true ),
200 '2 days 1 hour 1 minute 1 second',
201 'formatTimePeriod() rounding, recursion, (>48h)'
206 function testTruncate() {
207 $this->assertEquals(
208 "XXX",
209 $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
210 'truncate prefix, len 0, small ellipsis'
213 $this->assertEquals(
214 "12345XXX",
215 $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
216 'truncate prefix, small ellipsis'
219 $this->assertEquals(
220 "123456789",
221 $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
222 'truncate prefix, large ellipsis'
225 $this->assertEquals(
226 "XXX67890",
227 $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
228 'truncate suffix, small ellipsis'
231 $this->assertEquals(
232 "123456789",
233 $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
234 'truncate suffix, large ellipsis'
239 * @dataProvider provideHTMLTruncateData()
241 function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
242 // Actual HTML...
243 $this->assertEquals(
244 $expected,
245 $this->getLang()->truncateHTML( $input, $len, $ellipsis )
250 * Array format is ($len, $ellipsis, $input, $expected)
252 public static function provideHTMLTruncateData() {
253 return array(
254 array( 0, 'XXX', "1234567890", "XXX" ),
255 array( 8, 'XXX', "1234567890", "12345XXX" ),
256 array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
257 array( 2, '***',
258 '<p><span style="font-weight:bold;"></span></p>',
259 '<p><span style="font-weight:bold;"></span></p>',
261 array( 2, '***',
262 '<p><span style="font-weight:bold;">123456789</span></p>',
263 '<p><span style="font-weight:bold;">***</span></p>',
265 array( 2, '***',
266 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
267 '<p><span style="font-weight:bold;">***</span></p>',
269 array( 3, '***',
270 '<p><span style="font-weight:bold;">123456789</span></p>',
271 '<p><span style="font-weight:bold;">***</span></p>',
273 array( 4, '***',
274 '<p><span style="font-weight:bold;">123456789</span></p>',
275 '<p><span style="font-weight:bold;">1***</span></p>',
277 array( 5, '***',
278 '<tt><span style="font-weight:bold;">123456789</span></tt>',
279 '<tt><span style="font-weight:bold;">12***</span></tt>',
281 array( 6, '***',
282 '<p><a href="www.mediawiki.org">123456789</a></p>',
283 '<p><a href="www.mediawiki.org">123***</a></p>',
285 array( 6, '***',
286 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
287 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
289 array( 7, '***',
290 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
291 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
293 array( 8, '***',
294 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
295 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
297 array( 9, '***',
298 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
299 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
301 array( 10, '***',
302 '<p><font style="font-weight:bold;">123456789</font></p>',
303 '<p><font style="font-weight:bold;">123456789</font></p>',
309 * Test Language::isWellFormedLanguageTag()
310 * @dataProvider provideWellFormedLanguageTags
312 function testWellFormedLanguageTag( $code, $message = '' ) {
313 $this->assertTrue(
314 Language::isWellFormedLanguageTag( $code ),
315 "validating code $code $message"
320 * The test cases are based on the tests in the GaBuZoMeu parser
321 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
322 * and distributed as free software, under the GNU General Public Licence.
323 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
325 public static function provideWellFormedLanguageTags() {
326 return array(
327 array( 'fr', 'two-letter code' ),
328 array( 'fr-latn', 'two-letter code with lower case script code' ),
329 array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
330 array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
331 array( 'fr-FR', 'two-letter code with uppercase' ),
332 array( 'ax-TZ', 'Not in the registry, but well-formed' ),
333 array( 'fr-shadok', 'two-letter code with variant' ),
334 array( 'fr-y-myext-myext2', 'non-x singleton' ),
335 array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
336 array( 'fra', 'three-letter language code' ),
337 array( 'fra-FX', 'three-letter language code with country code' ),
338 array( 'i-klingon', 'grandfathered with singleton' ),
339 array( 'I-kLINgon', 'tags are case-insensitive...' ),
340 array( 'no-bok', 'grandfathered without singleton' ),
341 array( 'i-enochian', 'Grandfathered' ),
342 array( 'x-fr-CH', 'private use' ),
343 array( 'es-419', 'two-letter code with region number' ),
344 array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
345 array( 'ab-x-abc-x-abc', 'anything goes after x' ),
346 array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
347 array( 'i-default', 'grandfathered' ),
348 array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
349 array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
350 array( 'de-CH-1901', 'with country and year' ),
351 array( 'en-US-x-twain', 'with country and singleton' ),
352 array( 'zh-cmn', 'three-letter variant' ),
353 array( 'zh-cmn-Hant', 'three-letter variant and script' ),
354 array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
355 array( 'xr-p-lze', 'Extension' ),
360 * Negative test for Language::isWellFormedLanguageTag()
361 * @dataProvider provideMalformedLanguageTags
363 function testMalformedLanguageTag( $code, $message = '' ) {
364 $this->assertFalse(
365 Language::isWellFormedLanguageTag( $code ),
366 "validating that code $code is a malformed language tag - $message"
371 * The test cases are based on the tests in the GaBuZoMeu parser
372 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
373 * and distributed as free software, under the GNU General Public Licence.
374 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
376 public static function provideMalformedLanguageTags() {
377 return array(
378 array( 'f', 'language too short' ),
379 array( 'f-Latn', 'language too short with script' ),
380 array( 'xr-lxs-qut', 'variants too short' ), # extlangS
381 array( 'fr-Latn-F', 'region too short' ),
382 array( 'a-value', 'language too short with region' ),
383 array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
384 array( 'i-notexist', 'grandfathered but not registered: invalid, even if we only test well-formedness' ),
385 array( 'abcdefghi-012345678', 'numbers too long' ),
386 array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
387 array( 'ab-abcd-abc', 'invalid extensions' ),
388 array( 'ab-ab-abc', 'invalid extensions' ),
389 array( 'ab-123-abc', 'invalid extensions' ),
390 array( 'a-Hant-ZH', 'short language with valid extensions' ),
391 array( 'a1-Hant-ZH', 'invalid character in language' ),
392 array( 'ab-abcde-abc', 'invalid extensions' ),
393 array( 'ab-1abc-abc', 'invalid characters in extensions' ),
394 array( 'ab-ab-abcd', 'invalid order of extensions' ),
395 array( 'ab-123-abcd', 'invalid order of extensions' ),
396 array( 'ab-abcde-abcd', 'invalid extensions' ),
397 array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
398 array( 'ab-a-b', 'extensions too short' ),
399 array( 'ab-a-x', 'extensions too short, even with singleton' ),
400 array( 'ab--ab', 'two separators' ),
401 array( 'ab-abc-', 'separator in the end' ),
402 array( '-ab-abc', 'separator in the beginning' ),
403 array( 'abcd-efg', 'language too long' ),
404 array( 'aabbccddE', 'tag too long' ),
405 array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
406 array( 'de-f', 'subtag too short' ),
411 * Negative test for Language::isWellFormedLanguageTag()
413 function testLenientLanguageTag() {
414 $this->assertTrue(
415 Language::isWellFormedLanguageTag( 'pa_guru', true ),
416 'pa_guru is a well-formed language tag in lenient mode'
421 * Test Language::isValidBuiltInCode()
422 * @dataProvider provideLanguageCodes
424 function testBuiltInCodeValidation( $code, $message = '' ) {
425 $this->assertTrue(
426 (bool)Language::isValidBuiltInCode( $code ),
427 "validating code $code $message"
431 function testBuiltInCodeValidationRejectUnderscore() {
432 $this->assertFalse(
433 (bool)Language::isValidBuiltInCode( 'be_tarask' ),
434 "reject underscore in language code"
438 public static function provideLanguageCodes() {
439 return array(
440 array( 'fr', 'Two letters, minor case' ),
441 array( 'EN', 'Two letters, upper case' ),
442 array( 'tyv', 'Three letters' ),
443 array( 'tokipona', 'long language code' ),
444 array( 'be-tarask', 'With dash' ),
445 array( 'Zh-classical', 'Begin with upper case, dash' ),
446 array( 'Be-x-old', 'With extension (two dashes)' ),
451 * Test Language::isKnownLanguageTag()
452 * @dataProvider provideKnownLanguageTags
454 function testKnownLanguageTag( $code, $message = '' ) {
455 $this->assertTrue(
456 (bool)Language::isKnownLanguageTag( $code ),
457 "validating code $code - $message"
461 public static function provideKnownLanguageTags() {
462 return array(
463 array( 'fr', 'simple code' ),
464 array( 'bat-smg', 'an MW legacy tag' ),
465 array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
470 * Test Language::isKnownLanguageTag()
472 function testKnownCldrLanguageTag() {
473 if ( !class_exists( 'LanguageNames' ) ) {
474 $this->markTestSkipped( 'The LanguageNames class is not available. The cldr extension is probably not installed.' );
477 $this->assertTrue(
478 (bool)Language::isKnownLanguageTag( 'pal' ),
479 'validating code "pal" an ancient language, which probably will not appear in Names.php, but appears in CLDR in English'
484 * Negative tests for Language::isKnownLanguageTag()
485 * @dataProvider provideUnKnownLanguageTags
487 function testUnknownLanguageTag( $code, $message = '' ) {
488 $this->assertFalse(
489 (bool)Language::isKnownLanguageTag( $code ),
490 "checking that code $code is invalid - $message"
494 public static function provideUnknownLanguageTags() {
495 return array(
496 array( 'mw', 'non-existent two-letter code' ),
497 array( 'foo"<bar', 'very invalid language code' ),
502 * Test too short timestamp
503 * @expectedException MWException
505 function testSprintfDateTooShortTimestamp() {
506 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
510 * Test too long timestamp
511 * @expectedException MWException
513 function testSprintfDateTooLongTimestamp() {
514 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
518 * Test too short timestamp
519 * @expectedException MWException
521 function testSprintfDateNotAllDigitTimestamp() {
522 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
526 * @dataProvider provideSprintfDateSamples
528 function testSprintfDate( $format, $ts, $expected, $msg ) {
529 $this->assertEquals(
530 $expected,
531 $this->getLang()->sprintfDate( $format, $ts ),
532 "sprintfDate('$format', '$ts'): $msg"
537 * sprintfDate should always use UTC when no zone is given.
538 * @dataProvider provideSprintfDateSamples
540 function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
541 $oldTZ = date_default_timezone_get();
542 $res = date_default_timezone_set( 'Asia/Seoul' );
543 if ( !$res ) {
544 $this->markTestSkipped( "Error setting Timezone" );
547 $this->assertEquals(
548 $expected,
549 $this->getLang()->sprintfDate( $format, $ts ),
550 "sprintfDate('$format', '$ts'): $msg"
553 date_default_timezone_set( $oldTZ );
557 * sprintfDate should use passed timezone
558 * @dataProvider provideSprintfDateSamples
560 function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
561 $tz = new DateTimeZone( 'Asia/Seoul' );
562 if ( !$tz ) {
563 $this->markTestSkipped( "Error getting Timezone" );
566 $this->assertEquals(
567 $expected,
568 $this->getLang()->sprintfDate( $format, $ts, $tz ),
569 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
573 public static function provideSprintfDateSamples() {
574 return array(
575 array(
576 'xiY',
577 '20111212000000',
578 '1390', // note because we're testing English locale we get Latin-standard digits
579 '1390',
580 'Iranian calendar full year'
582 array(
583 'xiy',
584 '20111212000000',
585 '90',
586 '90',
587 'Iranian calendar short year'
589 array(
590 'o',
591 '20120101235000',
592 '2011',
593 '2011',
594 'ISO 8601 (week) year'
596 array(
597 'W',
598 '20120101235000',
599 '52',
600 '52',
601 'Week number'
603 array(
604 'W',
605 '20120102235000',
606 '1',
607 '1',
608 'Week number'
610 array(
611 'o-\\WW-N',
612 '20091231235000',
613 '2009-W53-4',
614 '2009-W53-4',
615 'leap week'
617 // What follows is mostly copied from http://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
618 array(
619 'Y',
620 '20120102090705',
621 '2012',
622 '2012',
623 'Full year'
625 array(
626 'y',
627 '20120102090705',
628 '12',
629 '12',
630 '2 digit year'
632 array(
633 'L',
634 '20120102090705',
635 '1',
636 '1',
637 'Leap year'
639 array(
640 'n',
641 '20120102090705',
642 '1',
643 '1',
644 'Month index, not zero pad'
646 array(
647 'N',
648 '20120102090705',
649 '01',
650 '01',
651 'Month index. Zero pad'
653 array(
654 'M',
655 '20120102090705',
656 'Jan',
657 'Jan',
658 'Month abbrev'
660 array(
661 'F',
662 '20120102090705',
663 'January',
664 'January',
665 'Full month'
667 array(
668 'xg',
669 '20120102090705',
670 'January',
671 'January',
672 'Genitive month name (same in EN)'
674 array(
675 'j',
676 '20120102090705',
677 '2',
678 '2',
679 'Day of month (not zero pad)'
681 array(
682 'd',
683 '20120102090705',
684 '02',
685 '02',
686 'Day of month (zero-pad)'
688 array(
689 'z',
690 '20120102090705',
691 '1',
692 '1',
693 'Day of year (zero-indexed)'
695 array(
696 'D',
697 '20120102090705',
698 'Mon',
699 'Mon',
700 'Day of week (abbrev)'
702 array(
703 'l',
704 '20120102090705',
705 'Monday',
706 'Monday',
707 'Full day of week'
709 array(
710 'N',
711 '20120101090705',
712 '7',
713 '7',
714 'Day of week (Mon=1, Sun=7)'
716 array(
717 'w',
718 '20120101090705',
719 '0',
720 '0',
721 'Day of week (Sun=0, Sat=6)'
723 array(
724 'N',
725 '20120102090705',
726 '1',
727 '1',
728 'Day of week'
730 array(
731 'a',
732 '20120102090705',
733 'am',
734 'am',
735 'am vs pm'
737 array(
738 'A',
739 '20120102120000',
740 'PM',
741 'PM',
742 'AM vs PM'
744 array(
745 'a',
746 '20120102000000',
747 'am',
748 'am',
749 'AM vs PM'
751 array(
752 'g',
753 '20120102090705',
754 '9',
755 '9',
756 '12 hour, not Zero'
758 array(
759 'h',
760 '20120102090705',
761 '09',
762 '09',
763 '12 hour, zero padded'
765 array(
766 'G',
767 '20120102090705',
768 '9',
769 '9',
770 '24 hour, not zero'
772 array(
773 'H',
774 '20120102090705',
775 '09',
776 '09',
777 '24 hour, zero'
779 array(
780 'H',
781 '20120102110705',
782 '11',
783 '11',
784 '24 hour, zero'
786 array(
787 'i',
788 '20120102090705',
789 '07',
790 '07',
791 'Minutes'
793 array(
794 's',
795 '20120102090705',
796 '05',
797 '05',
798 'seconds'
800 array(
801 'U',
802 '20120102090705',
803 '1325495225',
804 '1325462825',
805 'unix time'
807 array(
808 't',
809 '20120102090705',
810 '31',
811 '31',
812 'Days in current month'
814 array(
815 'c',
816 '20120102090705',
817 '2012-01-02T09:07:05+00:00',
818 '2012-01-02T09:07:05+09:00',
819 'ISO 8601 timestamp'
821 array(
822 'r',
823 '20120102090705',
824 'Mon, 02 Jan 2012 09:07:05 +0000',
825 'Mon, 02 Jan 2012 09:07:05 +0900',
826 'RFC 5322'
828 array(
829 'e',
830 '20120102090705',
831 'UTC',
832 'Asia/Seoul',
833 'Timezone identifier'
835 array(
836 'I',
837 '19880602090705',
838 '0',
839 '1',
840 'DST indicator'
842 array(
843 'O',
844 '20120102090705',
845 '+0000',
846 '+0900',
847 'Timezone offset'
849 array(
850 'P',
851 '20120102090705',
852 '+00:00',
853 '+09:00',
854 'Timezone offset with colon'
856 array(
857 'T',
858 '20120102090705',
859 'UTC',
860 'KST',
861 'Timezone abbreviation'
863 array(
864 'Z',
865 '20120102090705',
866 '0',
867 '32400',
868 'Timezone offset in seconds'
870 array(
871 'xmj xmF xmn xmY',
872 '20120102090705',
873 '7 Safar 2 1433',
874 '7 Safar 2 1433',
875 'Islamic'
877 array(
878 'xij xiF xin xiY',
879 '20120102090705',
880 '12 Dey 10 1390',
881 '12 Dey 10 1390',
882 'Iranian'
884 array(
885 'xjj xjF xjn xjY',
886 '20120102090705',
887 '7 Tevet 4 5772',
888 '7 Tevet 4 5772',
889 'Hebrew'
891 array(
892 'xjt',
893 '20120102090705',
894 '29',
895 '29',
896 'Hebrew number of days in month'
898 array(
899 'xjx',
900 '20120102090705',
901 'Tevet',
902 'Tevet',
903 'Hebrew genitive month name (No difference in EN)'
905 array(
906 'xkY',
907 '20120102090705',
908 '2555',
909 '2555',
910 'Thai year'
912 array(
913 'xoY',
914 '20120102090705',
915 '101',
916 '101',
917 'Minguo'
919 array(
920 'xtY',
921 '20120102090705',
922 '平成24',
923 '平成24',
924 'nengo'
926 array(
927 'xrxkYY',
928 '20120102090705',
929 'MMDLV2012',
930 'MMDLV2012',
931 'Roman numerals'
933 array(
934 'xhxjYY',
935 '20120102090705',
936 \'תשע"ב2012',
937 \'תשע"ב2012',
938 'Hebrew numberals'
940 array(
941 'xnY',
942 '20120102090705',
943 '2012',
944 '2012',
945 'Raw numerals (doesn\'t mean much in EN)'
947 array(
948 '[[Y "(yea"\\r)]] \\"xx\\"',
949 '20120102090705',
950 '[[2012 (year)]] "x"',
951 '[[2012 (year)]] "x"',
952 'Various escaping'
959 * @dataProvider provideFormatSizes
961 function testFormatSize( $size, $expected, $msg ) {
962 $this->assertEquals(
963 $expected,
964 $this->getLang()->formatSize( $size ),
965 "formatSize('$size'): $msg"
969 public static function provideFormatSizes() {
970 return array(
971 array(
973 "0 B",
974 "Zero bytes"
976 array(
977 1024,
978 "1 KB",
979 "1 kilobyte"
981 array(
982 1024 * 1024,
983 "1 MB",
984 "1,024 megabytes"
986 array(
987 1024 * 1024 * 1024,
988 "1 GB",
989 "1 gigabytes"
991 array(
992 pow( 1024, 4 ),
993 "1 TB",
994 "1 terabyte"
996 array(
997 pow( 1024, 5 ),
998 "1 PB",
999 "1 petabyte"
1001 array(
1002 pow( 1024, 6 ),
1003 "1 EB",
1004 "1,024 exabyte"
1006 array(
1007 pow( 1024, 7 ),
1008 "1 ZB",
1009 "1 zetabyte"
1011 array(
1012 pow( 1024, 8 ),
1013 "1 YB",
1014 "1 yottabyte"
1016 // How big!? THIS BIG!
1021 * @dataProvider provideFormatBitrate
1023 function testFormatBitrate( $bps, $expected, $msg ) {
1024 $this->assertEquals(
1025 $expected,
1026 $this->getLang()->formatBitrate( $bps ),
1027 "formatBitrate('$bps'): $msg"
1031 public static function provideFormatBitrate() {
1032 return array(
1033 array(
1035 "0 bps",
1036 "0 bits per second"
1038 array(
1039 999,
1040 "999 bps",
1041 "999 bits per second"
1043 array(
1044 1000,
1045 "1 kbps",
1046 "1 kilobit per second"
1048 array(
1049 1000 * 1000,
1050 "1 Mbps",
1051 "1 megabit per second"
1053 array(
1054 pow( 10, 9 ),
1055 "1 Gbps",
1056 "1 gigabit per second"
1058 array(
1059 pow( 10, 12 ),
1060 "1 Tbps",
1061 "1 terabit per second"
1063 array(
1064 pow( 10, 15 ),
1065 "1 Pbps",
1066 "1 petabit per second"
1068 array(
1069 pow( 10, 18 ),
1070 "1 Ebps",
1071 "1 exabit per second"
1073 array(
1074 pow( 10, 21 ),
1075 "1 Zbps",
1076 "1 zetabit per second"
1078 array(
1079 pow( 10, 24 ),
1080 "1 Ybps",
1081 "1 yottabit per second"
1083 array(
1084 pow( 10, 27 ),
1085 "1,000 Ybps",
1086 "1,000 yottabits per second"
1093 * @dataProvider provideFormatDuration
1095 function testFormatDuration( $duration, $expected, $intervals = array() ) {
1096 $this->assertEquals(
1097 $expected,
1098 $this->getLang()->formatDuration( $duration, $intervals ),
1099 "formatDuration('$duration'): $expected"
1103 public static function provideFormatDuration() {
1104 return array(
1105 array(
1107 '0 seconds',
1109 array(
1111 '1 second',
1113 array(
1115 '2 seconds',
1117 array(
1119 '1 minute',
1121 array(
1122 2 * 60,
1123 '2 minutes',
1125 array(
1126 3600,
1127 '1 hour',
1129 array(
1130 2 * 3600,
1131 '2 hours',
1133 array(
1134 24 * 3600,
1135 '1 day',
1137 array(
1138 2 * 86400,
1139 '2 days',
1141 array(
1142 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1143 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
1144 '1 year',
1146 array(
1147 2 * 31556952,
1148 '2 years',
1150 array(
1151 10 * 31556952,
1152 '1 decade',
1154 array(
1155 20 * 31556952,
1156 '2 decades',
1158 array(
1159 100 * 31556952,
1160 '1 century',
1162 array(
1163 200 * 31556952,
1164 '2 centuries',
1166 array(
1167 1000 * 31556952,
1168 '1 millennium',
1170 array(
1171 2000 * 31556952,
1172 '2 millennia',
1174 array(
1175 9001,
1176 '2 hours, 30 minutes and 1 second'
1178 array(
1179 3601,
1180 '1 hour and 1 second'
1182 array(
1183 31556952 + 2 * 86400 + 9000,
1184 '1 year, 2 days, 2 hours and 30 minutes'
1186 array(
1187 42 * 1000 * 31556952 + 42,
1188 '42 millennia and 42 seconds'
1190 array(
1192 '60 seconds',
1193 array( 'seconds' ),
1195 array(
1197 '61 seconds',
1198 array( 'seconds' ),
1200 array(
1202 '1 second',
1203 array( 'seconds' ),
1205 array(
1206 31556952 + 2 * 86400 + 9000,
1207 '1 year, 2 days and 150 minutes',
1208 array( 'years', 'days', 'minutes' ),
1210 array(
1212 '0 days',
1213 array( 'years', 'days' ),
1215 array(
1216 31556952 + 2 * 86400 + 9000,
1217 '1 year, 2 days and 150 minutes',
1218 array( 'minutes', 'days', 'years' ),
1220 array(
1222 '0 days',
1223 array( 'days', 'years' ),
1229 * @dataProvider provideCheckTitleEncodingData
1231 function testCheckTitleEncoding( $s ) {
1232 $this->assertEquals(
1234 $this->getLang()->checkTitleEncoding( $s ),
1235 "checkTitleEncoding('$s')"
1239 public static function provideCheckTitleEncodingData() {
1240 return array(
1241 array( "" ),
1242 array( "United States of America" ), // 7bit ASCII
1243 array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
1244 array(
1245 rawurldecode(
1246 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1249 // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
1250 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1251 // uses mb_check_encoding for its test.
1252 array(
1253 rawurldecode(
1254 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1255 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1256 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1257 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1258 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1259 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1260 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1261 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1262 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1263 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1264 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1265 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1266 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1267 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1270 array(
1271 rawurldecode(
1272 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1273 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1274 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1275 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1276 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1277 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1278 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1279 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1280 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1281 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1282 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1283 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1284 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1285 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1286 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1293 * @dataProvider provideRomanNumeralsData
1295 function testRomanNumerals( $num, $numerals ) {
1296 $this->assertEquals(
1297 $numerals,
1298 Language::romanNumeral( $num ),
1299 "romanNumeral('$num')"
1303 public static function provideRomanNumeralsData() {
1304 return array(
1305 array( 1, 'I' ),
1306 array( 2, 'II' ),
1307 array( 3, 'III' ),
1308 array( 4, 'IV' ),
1309 array( 5, 'V' ),
1310 array( 6, 'VI' ),
1311 array( 7, 'VII' ),
1312 array( 8, 'VIII' ),
1313 array( 9, 'IX' ),
1314 array( 10, 'X' ),
1315 array( 20, 'XX' ),
1316 array( 30, 'XXX' ),
1317 array( 40, 'XL' ),
1318 array( 49, 'XLIX' ),
1319 array( 50, 'L' ),
1320 array( 60, 'LX' ),
1321 array( 70, 'LXX' ),
1322 array( 80, 'LXXX' ),
1323 array( 90, 'XC' ),
1324 array( 99, 'XCIX' ),
1325 array( 100, 'C' ),
1326 array( 200, 'CC' ),
1327 array( 300, 'CCC' ),
1328 array( 400, 'CD' ),
1329 array( 500, 'D' ),
1330 array( 600, 'DC' ),
1331 array( 700, 'DCC' ),
1332 array( 800, 'DCCC' ),
1333 array( 900, 'CM' ),
1334 array( 999, 'CMXCIX' ),
1335 array( 1000, 'M' ),
1336 array( 1989, 'MCMLXXXIX' ),
1337 array( 2000, 'MM' ),
1338 array( 3000, 'MMM' ),
1339 array( 4000, 'MMMM' ),
1340 array( 5000, 'MMMMM' ),
1341 array( 6000, 'MMMMMM' ),
1342 array( 7000, 'MMMMMMM' ),
1343 array( 8000, 'MMMMMMMM' ),
1344 array( 9000, 'MMMMMMMMM' ),
1345 array( 9999, 'MMMMMMMMMCMXCIX' ),
1346 array( 10000, 'MMMMMMMMMM' ),
1351 * @dataProvider providePluralData
1353 function testConvertPlural( $expected, $number, $forms ) {
1354 $chosen = $this->getLang()->convertPlural( $number, $forms );
1355 $this->assertEquals( $expected, $chosen );
1358 public static function providePluralData() {
1359 // Params are: [expected text, number given, [the plural forms]]
1360 return array(
1361 array( 'plural', 0, array(
1362 'singular', 'plural'
1363 ) ),
1364 array( 'explicit zero', 0, array(
1365 '0=explicit zero', 'singular', 'plural'
1366 ) ),
1367 array( 'explicit one', 1, array(
1368 'singular', 'plural', '1=explicit one',
1369 ) ),
1370 array( 'singular', 1, array(
1371 'singular', 'plural', '0=explicit zero',
1372 ) ),
1373 array( 'plural', 3, array(
1374 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1375 ) ),
1376 array( 'explicit eleven', 11, array(
1377 'singular', 'plural', '11=explicit eleven',
1378 ) ),
1379 array( 'plural', 12, array(
1380 'singular', 'plural', '11=explicit twelve',
1381 ) ),
1382 array( 'plural', 12, array(
1383 'singular', 'plural', '=explicit form',
1384 ) ),
1385 array( 'other', 2, array(
1386 'kissa=kala', '1=2=3', 'other',
1387 ) ),
1388 array( '', 2, array(
1389 '0=explicit zero', '1=explicit one',
1390 ) ),
1395 * @covers Language::translateBlockExpiry()
1396 * @dataProvider provideTranslateBlockExpiry
1398 function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
1399 $lang = $this->getLang();
1400 if ( is_array( $expectedData ) ) {
1401 list( $func, $arg ) = $expectedData;
1402 $expected = $lang->$func( $arg );
1403 } else {
1404 $expected = $expectedData;
1406 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1409 public static function provideTranslateBlockExpiry() {
1410 return array(
1411 array( '2 hours', '2 hours', 'simple data from ipboptions' ),
1412 array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
1413 array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
1414 array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
1415 array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
1416 array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
1417 array( array( 'formatDuration', 0 ), 'now', 'now' ),
1418 array( array( 'timeanddate', '20120102070000' ), '2012-1-1 7:00 +1 day', 'mixed, handled as absolute' ),
1419 array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
1420 array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
1421 array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
1422 array( 'dummy', 'dummy', 'return garbage as is' ),
1427 * @covers Language::commafy()
1428 * @dataProvider provideCommafyData
1430 function testCommafy( $number, $numbersWithCommas ) {
1431 $this->assertEquals(
1432 $numbersWithCommas,
1433 $this->getLang()->commafy( $number ),
1434 "commafy('$number')"
1438 public static function provideCommafyData() {
1439 return array(
1440 array( 1, '1' ),
1441 array( 10, '10' ),
1442 array( 100, '100' ),
1443 array( 1000, '1,000' ),
1444 array( 10000, '10,000' ),
1445 array( 100000, '100,000' ),
1446 array( 1000000, '1,000,000' ),
1447 array( 1.0001, '1.0001' ),
1448 array( 10.0001, '10.0001' ),
1449 array( 100.0001, '100.0001' ),
1450 array( 1000.0001, '1,000.0001' ),
1451 array( 10000.0001, '10,000.0001' ),
1452 array( 100000.0001, '100,000.0001' ),
1453 array( 1000000.0001, '1,000,000.0001' ),
1457 function testListToText() {
1458 $lang = $this->getLang();
1459 $and = $lang->getMessageFromDB( 'and' );
1460 $s = $lang->getMessageFromDB( 'word-separator' );
1461 $c = $lang->getMessageFromDB( 'comma-separator' );
1463 $this->assertEquals( '', $lang->listToText( array() ) );
1464 $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
1465 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
1466 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
1467 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
1471 * @dataProvider provideIsSupportedLanguage
1473 function testIsSupportedLanguage( $code, $expected, $comment ) {
1474 $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
1477 public static function provideIsSupportedLanguage() {
1478 return array(
1479 array( 'en', true, 'is supported language' ),
1480 array( 'fi', true, 'is supported language' ),
1481 array( 'bunny', false, 'is not supported language' ),
1482 array( 'FI', false, 'is not supported language, input should be in lower case' ),