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