Merge "Add DROP INDEX support to DatabaseSqlite::replaceVars method"
[lhc/web/wiklou.git] / tests / phpunit / languages / LanguageTest.php
1 <?php
2
3 class LanguageTest extends LanguageClassesTestCase {
4 /**
5 * @covers Language::convertDoubleWidth
6 * @covers Language::normalizeForSearch
7 */
8 public function testLanguageConvertDoubleWidthToSingleWidth() {
9 $this->assertEquals(
10 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
11 $this->getLang()->normalizeForSearch(
12 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
13 ),
14 'convertDoubleWidth() with the full alphabet and digits'
15 );
16 }
17
18 /**
19 * @dataProvider provideFormattableTimes#
20 * @covers Language::formatTimePeriod
21 */
22 public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
23 $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
24 }
25
26 public static function provideFormattableTimes() {
27 return array(
28 array(
29 9.45,
30 array(),
31 '9.5 s',
32 'formatTimePeriod() rounding (<10s)'
33 ),
34 array(
35 9.45,
36 array( 'noabbrevs' => true ),
37 '9.5 seconds',
38 'formatTimePeriod() rounding (<10s)'
39 ),
40 array(
41 9.95,
42 array(),
43 '10 s',
44 'formatTimePeriod() rounding (<10s)'
45 ),
46 array(
47 9.95,
48 array( 'noabbrevs' => true ),
49 '10 seconds',
50 'formatTimePeriod() rounding (<10s)'
51 ),
52 array(
53 59.55,
54 array(),
55 '1 min 0 s',
56 'formatTimePeriod() rounding (<60s)'
57 ),
58 array(
59 59.55,
60 array( 'noabbrevs' => true ),
61 '1 minute 0 seconds',
62 'formatTimePeriod() rounding (<60s)'
63 ),
64 array(
65 119.55,
66 array(),
67 '2 min 0 s',
68 'formatTimePeriod() rounding (<1h)'
69 ),
70 array(
71 119.55,
72 array( 'noabbrevs' => true ),
73 '2 minutes 0 seconds',
74 'formatTimePeriod() rounding (<1h)'
75 ),
76 array(
77 3599.55,
78 array(),
79 '1 h 0 min 0 s',
80 'formatTimePeriod() rounding (<1h)'
81 ),
82 array(
83 3599.55,
84 array( 'noabbrevs' => true ),
85 '1 hour 0 minutes 0 seconds',
86 'formatTimePeriod() rounding (<1h)'
87 ),
88 array(
89 7199.55,
90 array(),
91 '2 h 0 min 0 s',
92 'formatTimePeriod() rounding (>=1h)'
93 ),
94 array(
95 7199.55,
96 array( 'noabbrevs' => true ),
97 '2 hours 0 minutes 0 seconds',
98 'formatTimePeriod() rounding (>=1h)'
99 ),
100 array(
101 7199.55,
102 'avoidseconds',
103 '2 h 0 min',
104 'formatTimePeriod() rounding (>=1h), avoidseconds'
105 ),
106 array(
107 7199.55,
108 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
109 '2 hours 0 minutes',
110 'formatTimePeriod() rounding (>=1h), avoidseconds'
111 ),
112 array(
113 7199.55,
114 'avoidminutes',
115 '2 h 0 min',
116 'formatTimePeriod() rounding (>=1h), avoidminutes'
117 ),
118 array(
119 7199.55,
120 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
121 '2 hours 0 minutes',
122 'formatTimePeriod() rounding (>=1h), avoidminutes'
123 ),
124 array(
125 172799.55,
126 'avoidseconds',
127 '48 h 0 min',
128 'formatTimePeriod() rounding (=48h), avoidseconds'
129 ),
130 array(
131 172799.55,
132 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
133 '48 hours 0 minutes',
134 'formatTimePeriod() rounding (=48h), avoidseconds'
135 ),
136 array(
137 259199.55,
138 'avoidminutes',
139 '3 d 0 h',
140 'formatTimePeriod() rounding (>48h), avoidminutes'
141 ),
142 array(
143 259199.55,
144 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
145 '3 days 0 hours',
146 'formatTimePeriod() rounding (>48h), avoidminutes'
147 ),
148 array(
149 176399.55,
150 'avoidseconds',
151 '2 d 1 h 0 min',
152 'formatTimePeriod() rounding (>48h), avoidseconds'
153 ),
154 array(
155 176399.55,
156 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
157 '2 days 1 hour 0 minutes',
158 'formatTimePeriod() rounding (>48h), avoidseconds'
159 ),
160 array(
161 176399.55,
162 'avoidminutes',
163 '2 d 1 h',
164 'formatTimePeriod() rounding (>48h), avoidminutes'
165 ),
166 array(
167 176399.55,
168 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
169 '2 days 1 hour',
170 'formatTimePeriod() rounding (>48h), avoidminutes'
171 ),
172 array(
173 259199.55,
174 'avoidseconds',
175 '3 d 0 h 0 min',
176 'formatTimePeriod() rounding (>48h), avoidseconds'
177 ),
178 array(
179 259199.55,
180 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
181 '3 days 0 hours 0 minutes',
182 'formatTimePeriod() rounding (>48h), avoidseconds'
183 ),
184 array(
185 172801.55,
186 'avoidseconds',
187 '2 d 0 h 0 min',
188 'formatTimePeriod() rounding, (>48h), avoidseconds'
189 ),
190 array(
191 172801.55,
192 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
193 '2 days 0 hours 0 minutes',
194 'formatTimePeriod() rounding, (>48h), avoidseconds'
195 ),
196 array(
197 176460.55,
198 array(),
199 '2 d 1 h 1 min 1 s',
200 'formatTimePeriod() rounding, recursion, (>48h)'
201 ),
202 array(
203 176460.55,
204 array( 'noabbrevs' => true ),
205 '2 days 1 hour 1 minute 1 second',
206 'formatTimePeriod() rounding, recursion, (>48h)'
207 ),
208 );
209 }
210
211 /**
212 * @covers Language::truncate
213 */
214 public function testTruncate() {
215 $this->assertEquals(
216 "XXX",
217 $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
218 'truncate prefix, len 0, small ellipsis'
219 );
220
221 $this->assertEquals(
222 "12345XXX",
223 $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
224 'truncate prefix, small ellipsis'
225 );
226
227 $this->assertEquals(
228 "123456789",
229 $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
230 'truncate prefix, large ellipsis'
231 );
232
233 $this->assertEquals(
234 "XXX67890",
235 $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
236 'truncate suffix, small ellipsis'
237 );
238
239 $this->assertEquals(
240 "123456789",
241 $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
242 'truncate suffix, large ellipsis'
243 );
244 }
245
246 /**
247 * @dataProvider provideHTMLTruncateData
248 * @covers Language::truncateHTML
249 */
250 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
251 // Actual HTML...
252 $this->assertEquals(
253 $expected,
254 $this->getLang()->truncateHTML( $input, $len, $ellipsis )
255 );
256 }
257
258 /**
259 * @return array format is ($len, $ellipsis, $input, $expected)
260 */
261 public static function provideHTMLTruncateData() {
262 return array(
263 array( 0, 'XXX', "1234567890", "XXX" ),
264 array( 8, 'XXX', "1234567890", "12345XXX" ),
265 array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
266 array( 2, '***',
267 '<p><span style="font-weight:bold;"></span></p>',
268 '<p><span style="font-weight:bold;"></span></p>',
269 ),
270 array( 2, '***',
271 '<p><span style="font-weight:bold;">123456789</span></p>',
272 '<p><span style="font-weight:bold;">***</span></p>',
273 ),
274 array( 2, '***',
275 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
276 '<p><span style="font-weight:bold;">***</span></p>',
277 ),
278 array( 3, '***',
279 '<p><span style="font-weight:bold;">123456789</span></p>',
280 '<p><span style="font-weight:bold;">***</span></p>',
281 ),
282 array( 4, '***',
283 '<p><span style="font-weight:bold;">123456789</span></p>',
284 '<p><span style="font-weight:bold;">1***</span></p>',
285 ),
286 array( 5, '***',
287 '<tt><span style="font-weight:bold;">123456789</span></tt>',
288 '<tt><span style="font-weight:bold;">12***</span></tt>',
289 ),
290 array( 6, '***',
291 '<p><a href="www.mediawiki.org">123456789</a></p>',
292 '<p><a href="www.mediawiki.org">123***</a></p>',
293 ),
294 array( 6, '***',
295 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
296 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
297 ),
298 array( 7, '***',
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>',
301 ),
302 array( 8, '***',
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>',
305 ),
306 array( 9, '***',
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>',
309 ),
310 array( 10, '***',
311 '<p><font style="font-weight:bold;">123456789</font></p>',
312 '<p><font style="font-weight:bold;">123456789</font></p>',
313 ),
314 );
315 }
316
317 /**
318 * Test Language::isWellFormedLanguageTag()
319 * @dataProvider provideWellFormedLanguageTags
320 * @covers Language::isWellFormedLanguageTag
321 */
322 public function testWellFormedLanguageTag( $code, $message = '' ) {
323 $this->assertTrue(
324 Language::isWellFormedLanguageTag( $code ),
325 "validating code $code $message"
326 );
327 }
328
329 /**
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
334 */
335 public static function provideWellFormedLanguageTags() {
336 return array(
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' ),
366 );
367 }
368
369 /**
370 * Negative test for Language::isWellFormedLanguageTag()
371 * @dataProvider provideMalformedLanguageTags
372 * @covers Language::isWellFormedLanguageTag
373 */
374 public function testMalformedLanguageTag( $code, $message = '' ) {
375 $this->assertFalse(
376 Language::isWellFormedLanguageTag( $code ),
377 "validating that code $code is a malformed language tag - $message"
378 );
379 }
380
381 /**
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
386 */
387 public static function provideMalformedLanguageTags() {
388 return array(
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' ),
418 );
419 }
420
421 /**
422 * Negative test for Language::isWellFormedLanguageTag()
423 * @covers Language::isWellFormedLanguageTag
424 */
425 public function testLenientLanguageTag() {
426 $this->assertTrue(
427 Language::isWellFormedLanguageTag( 'pa_guru', true ),
428 'pa_guru is a well-formed language tag in lenient mode'
429 );
430 }
431
432 /**
433 * Test Language::isValidBuiltInCode()
434 * @dataProvider provideLanguageCodes
435 * @covers Language::isValidBuiltInCode
436 */
437 public function testBuiltInCodeValidation( $code, $message = '' ) {
438 $this->assertTrue(
439 (bool)Language::isValidBuiltInCode( $code ),
440 "validating code $code $message"
441 );
442 }
443
444 /**
445 * @covers Language::isValidBuiltInCode
446 */
447 public function testBuiltInCodeValidationRejectUnderscore() {
448 $this->assertFalse(
449 (bool)Language::isValidBuiltInCode( 'be_tarask' ),
450 "reject underscore in language code"
451 );
452 }
453
454 public static function provideLanguageCodes() {
455 return array(
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)' ),
463 );
464 }
465
466 /**
467 * Test Language::isKnownLanguageTag()
468 * @dataProvider provideKnownLanguageTags
469 * @covers Language::isKnownLanguageTag
470 */
471 public function testKnownLanguageTag( $code, $message = '' ) {
472 $this->assertTrue(
473 (bool)Language::isKnownLanguageTag( $code ),
474 "validating code $code - $message"
475 );
476 }
477
478 public static function provideKnownLanguageTags() {
479 return array(
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' ),
483 );
484 }
485
486 /**
487 * @covers Language::isKnownLanguageTag
488 */
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.' );
492 }
493
494 $this->assertTrue(
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'
497 );
498 }
499
500 /**
501 * Negative tests for Language::isKnownLanguageTag()
502 * @dataProvider provideUnKnownLanguageTags
503 * @covers Language::isKnownLanguageTag
504 */
505 public function testUnknownLanguageTag( $code, $message = '' ) {
506 $this->assertFalse(
507 (bool)Language::isKnownLanguageTag( $code ),
508 "checking that code $code is invalid - $message"
509 );
510 }
511
512 public static function provideUnknownLanguageTags() {
513 return array(
514 array( 'mw', 'non-existent two-letter code' ),
515 array( 'foo"<bar', 'very invalid language code' ),
516 );
517 }
518
519 /**
520 * Test too short timestamp
521 * @expectedException MWException
522 * @covers Language::sprintfDate
523 */
524 public function testSprintfDateTooShortTimestamp() {
525 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
526 }
527
528 /**
529 * Test too long timestamp
530 * @expectedException MWException
531 * @covers Language::sprintfDate
532 */
533 public function testSprintfDateTooLongTimestamp() {
534 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
535 }
536
537 /**
538 * Test too short timestamp
539 * @expectedException MWException
540 * @covers Language::sprintfDate
541 */
542 public function testSprintfDateNotAllDigitTimestamp() {
543 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
544 }
545
546 /**
547 * @dataProvider provideSprintfDateSamples
548 * @covers Language::sprintfDate
549 */
550 public function testSprintfDate( $format, $ts, $expected, $msg ) {
551 $this->assertEquals(
552 $expected,
553 $this->getLang()->sprintfDate( $format, $ts ),
554 "sprintfDate('$format', '$ts'): $msg"
555 );
556 }
557
558 /**
559 * sprintfDate should always use UTC when no zone is given.
560 * @dataProvider provideSprintfDateSamples
561 * @covers Language::sprintfDate
562 */
563 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
564 $oldTZ = date_default_timezone_get();
565 $res = date_default_timezone_set( 'Asia/Seoul' );
566 if ( !$res ) {
567 $this->markTestSkipped( "Error setting Timezone" );
568 }
569
570 $this->assertEquals(
571 $expected,
572 $this->getLang()->sprintfDate( $format, $ts ),
573 "sprintfDate('$format', '$ts'): $msg"
574 );
575
576 date_default_timezone_set( $oldTZ );
577 }
578
579 /**
580 * sprintfDate should use passed timezone
581 * @dataProvider provideSprintfDateSamples
582 * @covers Language::sprintfDate
583 */
584 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
585 $tz = new DateTimeZone( 'Asia/Seoul' );
586 if ( !$tz ) {
587 $this->markTestSkipped( "Error getting Timezone" );
588 }
589
590 $this->assertEquals(
591 $expected,
592 $this->getLang()->sprintfDate( $format, $ts, $tz ),
593 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
594 );
595 }
596
597 public static function provideSprintfDateSamples() {
598 return array(
599 array(
600 'xiY',
601 '20111212000000',
602 '1390', // note because we're testing English locale we get Latin-standard digits
603 '1390',
604 'Iranian calendar full year'
605 ),
606 array(
607 'xiy',
608 '20111212000000',
609 '90',
610 '90',
611 'Iranian calendar short year'
612 ),
613 array(
614 'o',
615 '20120101235000',
616 '2011',
617 '2011',
618 'ISO 8601 (week) year'
619 ),
620 array(
621 'W',
622 '20120101235000',
623 '52',
624 '52',
625 'Week number'
626 ),
627 array(
628 'W',
629 '20120102235000',
630 '1',
631 '1',
632 'Week number'
633 ),
634 array(
635 'o-\\WW-N',
636 '20091231235000',
637 '2009-W53-4',
638 '2009-W53-4',
639 'leap week'
640 ),
641 // What follows is mostly copied from http://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
642 array(
643 'Y',
644 '20120102090705',
645 '2012',
646 '2012',
647 'Full year'
648 ),
649 array(
650 'y',
651 '20120102090705',
652 '12',
653 '12',
654 '2 digit year'
655 ),
656 array(
657 'L',
658 '20120102090705',
659 '1',
660 '1',
661 'Leap year'
662 ),
663 array(
664 'n',
665 '20120102090705',
666 '1',
667 '1',
668 'Month index, not zero pad'
669 ),
670 array(
671 'N',
672 '20120102090705',
673 '01',
674 '01',
675 'Month index. Zero pad'
676 ),
677 array(
678 'M',
679 '20120102090705',
680 'Jan',
681 'Jan',
682 'Month abbrev'
683 ),
684 array(
685 'F',
686 '20120102090705',
687 'January',
688 'January',
689 'Full month'
690 ),
691 array(
692 'xg',
693 '20120102090705',
694 'January',
695 'January',
696 'Genitive month name (same in EN)'
697 ),
698 array(
699 'j',
700 '20120102090705',
701 '2',
702 '2',
703 'Day of month (not zero pad)'
704 ),
705 array(
706 'd',
707 '20120102090705',
708 '02',
709 '02',
710 'Day of month (zero-pad)'
711 ),
712 array(
713 'z',
714 '20120102090705',
715 '1',
716 '1',
717 'Day of year (zero-indexed)'
718 ),
719 array(
720 'D',
721 '20120102090705',
722 'Mon',
723 'Mon',
724 'Day of week (abbrev)'
725 ),
726 array(
727 'l',
728 '20120102090705',
729 'Monday',
730 'Monday',
731 'Full day of week'
732 ),
733 array(
734 'N',
735 '20120101090705',
736 '7',
737 '7',
738 'Day of week (Mon=1, Sun=7)'
739 ),
740 array(
741 'w',
742 '20120101090705',
743 '0',
744 '0',
745 'Day of week (Sun=0, Sat=6)'
746 ),
747 array(
748 'N',
749 '20120102090705',
750 '1',
751 '1',
752 'Day of week'
753 ),
754 array(
755 'a',
756 '20120102090705',
757 'am',
758 'am',
759 'am vs pm'
760 ),
761 array(
762 'A',
763 '20120102120000',
764 'PM',
765 'PM',
766 'AM vs PM'
767 ),
768 array(
769 'a',
770 '20120102000000',
771 'am',
772 'am',
773 'AM vs PM'
774 ),
775 array(
776 'g',
777 '20120102090705',
778 '9',
779 '9',
780 '12 hour, not Zero'
781 ),
782 array(
783 'h',
784 '20120102090705',
785 '09',
786 '09',
787 '12 hour, zero padded'
788 ),
789 array(
790 'G',
791 '20120102090705',
792 '9',
793 '9',
794 '24 hour, not zero'
795 ),
796 array(
797 'H',
798 '20120102090705',
799 '09',
800 '09',
801 '24 hour, zero'
802 ),
803 array(
804 'H',
805 '20120102110705',
806 '11',
807 '11',
808 '24 hour, zero'
809 ),
810 array(
811 'i',
812 '20120102090705',
813 '07',
814 '07',
815 'Minutes'
816 ),
817 array(
818 's',
819 '20120102090705',
820 '05',
821 '05',
822 'seconds'
823 ),
824 array(
825 'U',
826 '20120102090705',
827 '1325495225',
828 '1325462825',
829 'unix time'
830 ),
831 array(
832 't',
833 '20120102090705',
834 '31',
835 '31',
836 'Days in current month'
837 ),
838 array(
839 'c',
840 '20120102090705',
841 '2012-01-02T09:07:05+00:00',
842 '2012-01-02T09:07:05+09:00',
843 'ISO 8601 timestamp'
844 ),
845 array(
846 'r',
847 '20120102090705',
848 'Mon, 02 Jan 2012 09:07:05 +0000',
849 'Mon, 02 Jan 2012 09:07:05 +0900',
850 'RFC 5322'
851 ),
852 array(
853 'e',
854 '20120102090705',
855 'UTC',
856 'Asia/Seoul',
857 'Timezone identifier'
858 ),
859 array(
860 'I',
861 '19880602090705',
862 '0',
863 '1',
864 'DST indicator'
865 ),
866 array(
867 'O',
868 '20120102090705',
869 '+0000',
870 '+0900',
871 'Timezone offset'
872 ),
873 array(
874 'P',
875 '20120102090705',
876 '+00:00',
877 '+09:00',
878 'Timezone offset with colon'
879 ),
880 array(
881 'T',
882 '20120102090705',
883 'UTC',
884 'KST',
885 'Timezone abbreviation'
886 ),
887 array(
888 'Z',
889 '20120102090705',
890 '0',
891 '32400',
892 'Timezone offset in seconds'
893 ),
894 array(
895 'xmj xmF xmn xmY',
896 '20120102090705',
897 '7 Safar 2 1433',
898 '7 Safar 2 1433',
899 'Islamic'
900 ),
901 array(
902 'xij xiF xin xiY',
903 '20120102090705',
904 '12 Dey 10 1390',
905 '12 Dey 10 1390',
906 'Iranian'
907 ),
908 array(
909 'xjj xjF xjn xjY',
910 '20120102090705',
911 '7 Tevet 4 5772',
912 '7 Tevet 4 5772',
913 'Hebrew'
914 ),
915 array(
916 'xjt',
917 '20120102090705',
918 '29',
919 '29',
920 'Hebrew number of days in month'
921 ),
922 array(
923 'xjx',
924 '20120102090705',
925 'Tevet',
926 'Tevet',
927 'Hebrew genitive month name (No difference in EN)'
928 ),
929 array(
930 'xkY',
931 '20120102090705',
932 '2555',
933 '2555',
934 'Thai year'
935 ),
936 array(
937 'xoY',
938 '20120102090705',
939 '101',
940 '101',
941 'Minguo'
942 ),
943 array(
944 'xtY',
945 '20120102090705',
946 '平成24',
947 '平成24',
948 'nengo'
949 ),
950 array(
951 'xrxkYY',
952 '20120102090705',
953 'MMDLV2012',
954 'MMDLV2012',
955 'Roman numerals'
956 ),
957 array(
958 'xhxjYY',
959 '20120102090705',
960 \'תשע"ב2012',
961 \'תשע"ב2012',
962 'Hebrew numberals'
963 ),
964 array(
965 'xnY',
966 '20120102090705',
967 '2012',
968 '2012',
969 'Raw numerals (doesn\'t mean much in EN)'
970 ),
971 array(
972 '[[Y "(yea"\\r)]] \\"xx\\"',
973 '20120102090705',
974 '[[2012 (year)]] "x"',
975 '[[2012 (year)]] "x"',
976 'Various escaping'
977 ),
978
979 );
980 }
981
982 /**
983 * @dataProvider provideFormatSizes
984 * @covers Language::formatSize
985 */
986 public function testFormatSize( $size, $expected, $msg ) {
987 $this->assertEquals(
988 $expected,
989 $this->getLang()->formatSize( $size ),
990 "formatSize('$size'): $msg"
991 );
992 }
993
994 public static function provideFormatSizes() {
995 return array(
996 array(
997 0,
998 "0 B",
999 "Zero bytes"
1000 ),
1001 array(
1002 1024,
1003 "1 KB",
1004 "1 kilobyte"
1005 ),
1006 array(
1007 1024 * 1024,
1008 "1 MB",
1009 "1,024 megabytes"
1010 ),
1011 array(
1012 1024 * 1024 * 1024,
1013 "1 GB",
1014 "1 gigabytes"
1015 ),
1016 array(
1017 pow( 1024, 4 ),
1018 "1 TB",
1019 "1 terabyte"
1020 ),
1021 array(
1022 pow( 1024, 5 ),
1023 "1 PB",
1024 "1 petabyte"
1025 ),
1026 array(
1027 pow( 1024, 6 ),
1028 "1 EB",
1029 "1,024 exabyte"
1030 ),
1031 array(
1032 pow( 1024, 7 ),
1033 "1 ZB",
1034 "1 zetabyte"
1035 ),
1036 array(
1037 pow( 1024, 8 ),
1038 "1 YB",
1039 "1 yottabyte"
1040 ),
1041 // How big!? THIS BIG!
1042 );
1043 }
1044
1045 /**
1046 * @dataProvider provideFormatBitrate
1047 * @covers Language::formatBitrate
1048 */
1049 public function testFormatBitrate( $bps, $expected, $msg ) {
1050 $this->assertEquals(
1051 $expected,
1052 $this->getLang()->formatBitrate( $bps ),
1053 "formatBitrate('$bps'): $msg"
1054 );
1055 }
1056
1057 public static function provideFormatBitrate() {
1058 return array(
1059 array(
1060 0,
1061 "0 bps",
1062 "0 bits per second"
1063 ),
1064 array(
1065 999,
1066 "999 bps",
1067 "999 bits per second"
1068 ),
1069 array(
1070 1000,
1071 "1 kbps",
1072 "1 kilobit per second"
1073 ),
1074 array(
1075 1000 * 1000,
1076 "1 Mbps",
1077 "1 megabit per second"
1078 ),
1079 array(
1080 pow( 10, 9 ),
1081 "1 Gbps",
1082 "1 gigabit per second"
1083 ),
1084 array(
1085 pow( 10, 12 ),
1086 "1 Tbps",
1087 "1 terabit per second"
1088 ),
1089 array(
1090 pow( 10, 15 ),
1091 "1 Pbps",
1092 "1 petabit per second"
1093 ),
1094 array(
1095 pow( 10, 18 ),
1096 "1 Ebps",
1097 "1 exabit per second"
1098 ),
1099 array(
1100 pow( 10, 21 ),
1101 "1 Zbps",
1102 "1 zetabit per second"
1103 ),
1104 array(
1105 pow( 10, 24 ),
1106 "1 Ybps",
1107 "1 yottabit per second"
1108 ),
1109 array(
1110 pow( 10, 27 ),
1111 "1,000 Ybps",
1112 "1,000 yottabits per second"
1113 ),
1114 );
1115 }
1116
1117
1118 /**
1119 * @dataProvider provideFormatDuration
1120 * @covers Language::formatDuration
1121 */
1122 public function testFormatDuration( $duration, $expected, $intervals = array() ) {
1123 $this->assertEquals(
1124 $expected,
1125 $this->getLang()->formatDuration( $duration, $intervals ),
1126 "formatDuration('$duration'): $expected"
1127 );
1128 }
1129
1130 public static function provideFormatDuration() {
1131 return array(
1132 array(
1133 0,
1134 '0 seconds',
1135 ),
1136 array(
1137 1,
1138 '1 second',
1139 ),
1140 array(
1141 2,
1142 '2 seconds',
1143 ),
1144 array(
1145 60,
1146 '1 minute',
1147 ),
1148 array(
1149 2 * 60,
1150 '2 minutes',
1151 ),
1152 array(
1153 3600,
1154 '1 hour',
1155 ),
1156 array(
1157 2 * 3600,
1158 '2 hours',
1159 ),
1160 array(
1161 24 * 3600,
1162 '1 day',
1163 ),
1164 array(
1165 2 * 86400,
1166 '2 days',
1167 ),
1168 array(
1169 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1170 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
1171 '1 year',
1172 ),
1173 array(
1174 2 * 31556952,
1175 '2 years',
1176 ),
1177 array(
1178 10 * 31556952,
1179 '1 decade',
1180 ),
1181 array(
1182 20 * 31556952,
1183 '2 decades',
1184 ),
1185 array(
1186 100 * 31556952,
1187 '1 century',
1188 ),
1189 array(
1190 200 * 31556952,
1191 '2 centuries',
1192 ),
1193 array(
1194 1000 * 31556952,
1195 '1 millennium',
1196 ),
1197 array(
1198 2000 * 31556952,
1199 '2 millennia',
1200 ),
1201 array(
1202 9001,
1203 '2 hours, 30 minutes and 1 second'
1204 ),
1205 array(
1206 3601,
1207 '1 hour and 1 second'
1208 ),
1209 array(
1210 31556952 + 2 * 86400 + 9000,
1211 '1 year, 2 days, 2 hours and 30 minutes'
1212 ),
1213 array(
1214 42 * 1000 * 31556952 + 42,
1215 '42 millennia and 42 seconds'
1216 ),
1217 array(
1218 60,
1219 '60 seconds',
1220 array( 'seconds' ),
1221 ),
1222 array(
1223 61,
1224 '61 seconds',
1225 array( 'seconds' ),
1226 ),
1227 array(
1228 1,
1229 '1 second',
1230 array( 'seconds' ),
1231 ),
1232 array(
1233 31556952 + 2 * 86400 + 9000,
1234 '1 year, 2 days and 150 minutes',
1235 array( 'years', 'days', 'minutes' ),
1236 ),
1237 array(
1238 42,
1239 '0 days',
1240 array( 'years', 'days' ),
1241 ),
1242 array(
1243 31556952 + 2 * 86400 + 9000,
1244 '1 year, 2 days and 150 minutes',
1245 array( 'minutes', 'days', 'years' ),
1246 ),
1247 array(
1248 42,
1249 '0 days',
1250 array( 'days', 'years' ),
1251 ),
1252 );
1253 }
1254
1255 /**
1256 * @dataProvider provideCheckTitleEncodingData
1257 * @covers Language::checkTitleEncoding
1258 */
1259 public function testCheckTitleEncoding( $s ) {
1260 $this->assertEquals(
1261 $s,
1262 $this->getLang()->checkTitleEncoding( $s ),
1263 "checkTitleEncoding('$s')"
1264 );
1265 }
1266
1267 public static function provideCheckTitleEncodingData() {
1268 return array(
1269 array( "" ),
1270 array( "United States of America" ), // 7bit ASCII
1271 array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
1272 array(
1273 rawurldecode(
1274 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1275 )
1276 ),
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.
1280 array(
1281 rawurldecode(
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"
1296 ),
1297 ),
1298 array(
1299 rawurldecode(
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"
1315 )
1316 )
1317 );
1318 }
1319
1320 /**
1321 * @dataProvider provideRomanNumeralsData
1322 * @covers Language::romanNumeral
1323 */
1324 public function testRomanNumerals( $num, $numerals ) {
1325 $this->assertEquals(
1326 $numerals,
1327 Language::romanNumeral( $num ),
1328 "romanNumeral('$num')"
1329 );
1330 }
1331
1332 public static function provideRomanNumeralsData() {
1333 return array(
1334 array( 1, 'I' ),
1335 array( 2, 'II' ),
1336 array( 3, 'III' ),
1337 array( 4, 'IV' ),
1338 array( 5, 'V' ),
1339 array( 6, 'VI' ),
1340 array( 7, 'VII' ),
1341 array( 8, 'VIII' ),
1342 array( 9, 'IX' ),
1343 array( 10, 'X' ),
1344 array( 20, 'XX' ),
1345 array( 30, 'XXX' ),
1346 array( 40, 'XL' ),
1347 array( 49, 'XLIX' ),
1348 array( 50, 'L' ),
1349 array( 60, 'LX' ),
1350 array( 70, 'LXX' ),
1351 array( 80, 'LXXX' ),
1352 array( 90, 'XC' ),
1353 array( 99, 'XCIX' ),
1354 array( 100, 'C' ),
1355 array( 200, 'CC' ),
1356 array( 300, 'CCC' ),
1357 array( 400, 'CD' ),
1358 array( 500, 'D' ),
1359 array( 600, 'DC' ),
1360 array( 700, 'DCC' ),
1361 array( 800, 'DCCC' ),
1362 array( 900, 'CM' ),
1363 array( 999, 'CMXCIX' ),
1364 array( 1000, 'M' ),
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' ),
1376 );
1377 }
1378
1379 /**
1380 * @dataProvider providePluralData
1381 * @covers Language::convertPlural
1382 */
1383 public function testConvertPlural( $expected, $number, $forms ) {
1384 $chosen = $this->getLang()->convertPlural( $number, $forms );
1385 $this->assertEquals( $expected, $chosen );
1386 }
1387
1388 public static function providePluralData() {
1389 // Params are: [expected text, number given, [the plural forms]]
1390 return array(
1391 array( 'plural', 0, array(
1392 'singular', 'plural'
1393 ) ),
1394 array( 'explicit zero', 0, array(
1395 '0=explicit zero', 'singular', 'plural'
1396 ) ),
1397 array( 'explicit one', 1, array(
1398 'singular', 'plural', '1=explicit one',
1399 ) ),
1400 array( 'singular', 1, array(
1401 'singular', 'plural', '0=explicit zero',
1402 ) ),
1403 array( 'plural', 3, array(
1404 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1405 ) ),
1406 array( 'explicit eleven', 11, array(
1407 'singular', 'plural', '11=explicit eleven',
1408 ) ),
1409 array( 'plural', 12, array(
1410 'singular', 'plural', '11=explicit twelve',
1411 ) ),
1412 array( 'plural', 12, array(
1413 'singular', 'plural', '=explicit form',
1414 ) ),
1415 array( 'other', 2, array(
1416 'kissa=kala', '1=2=3', 'other',
1417 ) ),
1418 array( '', 2, array(
1419 '0=explicit zero', '1=explicit one',
1420 ) ),
1421 );
1422 }
1423
1424 /**
1425 * @covers Language::translateBlockExpiry()
1426 * @dataProvider provideTranslateBlockExpiry
1427 */
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 );
1433 } else {
1434 $expected = $expectedData;
1435 }
1436 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1437 }
1438
1439 public static function provideTranslateBlockExpiry() {
1440 return array(
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' ),
1453 );
1454 }
1455
1456 /**
1457 * @covers Language::commafy()
1458 * @dataProvider provideCommafyData
1459 */
1460 public function testCommafy( $number, $numbersWithCommas ) {
1461 $this->assertEquals(
1462 $numbersWithCommas,
1463 $this->getLang()->commafy( $number ),
1464 "commafy('$number')"
1465 );
1466 }
1467
1468 public static function provideCommafyData() {
1469 return array(
1470 array( 1, '1' ),
1471 array( 10, '10' ),
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' ),
1484 );
1485 }
1486
1487 /**
1488 * @covers Language::listToText
1489 */
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' );
1495
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' ) ) );
1501 }
1502
1503 /**
1504 * @dataProvider provideIsSupportedLanguage
1505 * @covers Language::isSupportedLanguage
1506 */
1507 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1508 $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
1509 }
1510
1511 public static function provideIsSupportedLanguage() {
1512 return array(
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' ),
1517 );
1518 }
1519
1520 /**
1521 * @dataProvider provideGetParentLanguage
1522 * @covers Language::getParentLanguage
1523 */
1524 public function testGetParentLanguage( $code, $expected, $comment ) {
1525 $lang = Language::factory( $code );
1526 if ( is_null( $expected ) ) {
1527 $this->assertNull( $lang->getParentLanguage(), $comment );
1528 } else {
1529 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1530 }
1531 }
1532
1533 public static function provideGetParentLanguage() {
1534 return array(
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' ),
1540 );
1541 }
1542
1543 /**
1544 * @dataProvider provideGetNamespaceAliases
1545 * @covers Language::getNamespaceAliases
1546 */
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] );
1552 }
1553 }
1554
1555 public static function provideGetNamespaceAliases() {
1556 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
1557 return array(
1558 array(
1559 'zh',
1560 array(
1561 '文件' => NS_FILE,
1562 '檔案' => NS_FILE,
1563 ),
1564 ),
1565 );
1566 }
1567 }