Merge "Implement wfArrayPlus2d which combines 2d arrays"
[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 $this->assertEquals(
245 "123XXX",
246 $this->getLang()->truncate( "123 ", 9, 'XXX' ),
247 'truncate prefix, with spaces'
248 );
249 $this->assertEquals(
250 "12345XXX",
251 $this->getLang()->truncate( "12345 8", 11, 'XXX' ),
252 'truncate prefix, with spaces and non-space ending'
253 );
254 $this->assertEquals(
255 "XXX234",
256 $this->getLang()->truncate( "1 234", -8, 'XXX' ),
257 'truncate suffix, with spaces'
258 );
259 $this->assertEquals(
260 "12345XXX",
261 $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
262 'truncate without adjustment'
263 );
264 }
265
266 /**
267 * @dataProvider provideHTMLTruncateData
268 * @covers Language::truncateHTML
269 */
270 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
271 // Actual HTML...
272 $this->assertEquals(
273 $expected,
274 $this->getLang()->truncateHTML( $input, $len, $ellipsis )
275 );
276 }
277
278 /**
279 * @return array Format is ($len, $ellipsis, $input, $expected)
280 */
281 public static function provideHTMLTruncateData() {
282 return array(
283 array( 0, 'XXX', "1234567890", "XXX" ),
284 array( 8, 'XXX', "1234567890", "12345XXX" ),
285 array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
286 array( 2, '***',
287 '<p><span style="font-weight:bold;"></span></p>',
288 '<p><span style="font-weight:bold;"></span></p>',
289 ),
290 array( 2, '***',
291 '<p><span style="font-weight:bold;">123456789</span></p>',
292 '<p><span style="font-weight:bold;">***</span></p>',
293 ),
294 array( 2, '***',
295 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
296 '<p><span style="font-weight:bold;">***</span></p>',
297 ),
298 array( 3, '***',
299 '<p><span style="font-weight:bold;">123456789</span></p>',
300 '<p><span style="font-weight:bold;">***</span></p>',
301 ),
302 array( 4, '***',
303 '<p><span style="font-weight:bold;">123456789</span></p>',
304 '<p><span style="font-weight:bold;">1***</span></p>',
305 ),
306 array( 5, '***',
307 '<tt><span style="font-weight:bold;">123456789</span></tt>',
308 '<tt><span style="font-weight:bold;">12***</span></tt>',
309 ),
310 array( 6, '***',
311 '<p><a href="www.mediawiki.org">123456789</a></p>',
312 '<p><a href="www.mediawiki.org">123***</a></p>',
313 ),
314 array( 6, '***',
315 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
316 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
317 ),
318 array( 7, '***',
319 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
320 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
321 ),
322 array( 8, '***',
323 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
324 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
325 ),
326 array( 9, '***',
327 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
328 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
329 ),
330 array( 10, '***',
331 '<p><font style="font-weight:bold;">123456789</font></p>',
332 '<p><font style="font-weight:bold;">123456789</font></p>',
333 ),
334 );
335 }
336
337 /**
338 * Test Language::isWellFormedLanguageTag()
339 * @dataProvider provideWellFormedLanguageTags
340 * @covers Language::isWellFormedLanguageTag
341 */
342 public function testWellFormedLanguageTag( $code, $message = '' ) {
343 $this->assertTrue(
344 Language::isWellFormedLanguageTag( $code ),
345 "validating code $code $message"
346 );
347 }
348
349 /**
350 * The test cases are based on the tests in the GaBuZoMeu parser
351 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
352 * and distributed as free software, under the GNU General Public Licence.
353 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
354 */
355 public static function provideWellFormedLanguageTags() {
356 return array(
357 array( 'fr', 'two-letter code' ),
358 array( 'fr-latn', 'two-letter code with lower case script code' ),
359 array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
360 array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
361 array( 'fr-FR', 'two-letter code with uppercase' ),
362 array( 'ax-TZ', 'Not in the registry, but well-formed' ),
363 array( 'fr-shadok', 'two-letter code with variant' ),
364 array( 'fr-y-myext-myext2', 'non-x singleton' ),
365 array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
366 array( 'fra', 'three-letter language code' ),
367 array( 'fra-FX', 'three-letter language code with country code' ),
368 array( 'i-klingon', 'grandfathered with singleton' ),
369 array( 'I-kLINgon', 'tags are case-insensitive...' ),
370 array( 'no-bok', 'grandfathered without singleton' ),
371 array( 'i-enochian', 'Grandfathered' ),
372 array( 'x-fr-CH', 'private use' ),
373 array( 'es-419', 'two-letter code with region number' ),
374 array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
375 array( 'ab-x-abc-x-abc', 'anything goes after x' ),
376 array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
377 array( 'i-default', 'grandfathered' ),
378 array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
379 array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
380 array( 'de-CH-1901', 'with country and year' ),
381 array( 'en-US-x-twain', 'with country and singleton' ),
382 array( 'zh-cmn', 'three-letter variant' ),
383 array( 'zh-cmn-Hant', 'three-letter variant and script' ),
384 array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
385 array( 'xr-p-lze', 'Extension' ),
386 );
387 }
388
389 /**
390 * Negative test for Language::isWellFormedLanguageTag()
391 * @dataProvider provideMalformedLanguageTags
392 * @covers Language::isWellFormedLanguageTag
393 */
394 public function testMalformedLanguageTag( $code, $message = '' ) {
395 $this->assertFalse(
396 Language::isWellFormedLanguageTag( $code ),
397 "validating that code $code is a malformed language tag - $message"
398 );
399 }
400
401 /**
402 * The test cases are based on the tests in the GaBuZoMeu parser
403 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
404 * and distributed as free software, under the GNU General Public Licence.
405 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
406 */
407 public static function provideMalformedLanguageTags() {
408 return array(
409 array( 'f', 'language too short' ),
410 array( 'f-Latn', 'language too short with script' ),
411 array( 'xr-lxs-qut', 'variants too short' ), # extlangS
412 array( 'fr-Latn-F', 'region too short' ),
413 array( 'a-value', 'language too short with region' ),
414 array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
415 array(
416 'i-notexist',
417 'grandfathered but not registered: invalid, even if we only test well-formedness'
418 ),
419 array( 'abcdefghi-012345678', 'numbers too long' ),
420 array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
421 array( 'ab-abcd-abc', 'invalid extensions' ),
422 array( 'ab-ab-abc', 'invalid extensions' ),
423 array( 'ab-123-abc', 'invalid extensions' ),
424 array( 'a-Hant-ZH', 'short language with valid extensions' ),
425 array( 'a1-Hant-ZH', 'invalid character in language' ),
426 array( 'ab-abcde-abc', 'invalid extensions' ),
427 array( 'ab-1abc-abc', 'invalid characters in extensions' ),
428 array( 'ab-ab-abcd', 'invalid order of extensions' ),
429 array( 'ab-123-abcd', 'invalid order of extensions' ),
430 array( 'ab-abcde-abcd', 'invalid extensions' ),
431 array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
432 array( 'ab-a-b', 'extensions too short' ),
433 array( 'ab-a-x', 'extensions too short, even with singleton' ),
434 array( 'ab--ab', 'two separators' ),
435 array( 'ab-abc-', 'separator in the end' ),
436 array( '-ab-abc', 'separator in the beginning' ),
437 array( 'abcd-efg', 'language too long' ),
438 array( 'aabbccddE', 'tag too long' ),
439 array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
440 array( 'de-f', 'subtag too short' ),
441 );
442 }
443
444 /**
445 * Negative test for Language::isWellFormedLanguageTag()
446 * @covers Language::isWellFormedLanguageTag
447 */
448 public function testLenientLanguageTag() {
449 $this->assertTrue(
450 Language::isWellFormedLanguageTag( 'pa_guru', true ),
451 'pa_guru is a well-formed language tag in lenient mode'
452 );
453 }
454
455 /**
456 * Test Language::isValidBuiltInCode()
457 * @dataProvider provideLanguageCodes
458 * @covers Language::isValidBuiltInCode
459 */
460 public function testBuiltInCodeValidation( $code, $expected, $message = '' ) {
461 $this->assertEquals( $expected,
462 (bool)Language::isValidBuiltInCode( $code ),
463 "validating code $code $message"
464 );
465 }
466
467 public static function provideLanguageCodes() {
468 return array(
469 array( 'fr', true, 'Two letters, minor case' ),
470 array( 'EN', false, 'Two letters, upper case' ),
471 array( 'tyv', true, 'Three letters' ),
472 array( 'tokipona', true, 'long language code' ),
473 array( 'be-tarask', true, 'With dash' ),
474 array( 'be-x-old', true, 'With extension (two dashes)' ),
475 array( 'be_tarask', false, 'Reject underscores' ),
476 );
477 }
478
479 /**
480 * Test Language::isKnownLanguageTag()
481 * @dataProvider provideKnownLanguageTags
482 * @covers Language::isKnownLanguageTag
483 */
484 public function testKnownLanguageTag( $code, $message = '' ) {
485 $this->assertTrue(
486 (bool)Language::isKnownLanguageTag( $code ),
487 "validating code $code - $message"
488 );
489 }
490
491 public static function provideKnownLanguageTags() {
492 return array(
493 array( 'fr', 'simple code' ),
494 array( 'bat-smg', 'an MW legacy tag' ),
495 array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
496 );
497 }
498
499 /**
500 * @covers Language::isKnownLanguageTag
501 */
502 public function testKnownCldrLanguageTag() {
503 if ( !class_exists( 'LanguageNames' ) ) {
504 $this->markTestSkipped( 'The LanguageNames class is not available. '
505 . 'The CLDR extension is probably not installed.' );
506 }
507
508 $this->assertTrue(
509 (bool)Language::isKnownLanguageTag( 'pal' ),
510 'validating code "pal" an ancient language, which probably will '
511 . 'not appear in Names.php, but appears in CLDR in English'
512 );
513 }
514
515 /**
516 * Negative tests for Language::isKnownLanguageTag()
517 * @dataProvider provideUnKnownLanguageTags
518 * @covers Language::isKnownLanguageTag
519 */
520 public function testUnknownLanguageTag( $code, $message = '' ) {
521 $this->assertFalse(
522 (bool)Language::isKnownLanguageTag( $code ),
523 "checking that code $code is invalid - $message"
524 );
525 }
526
527 public static function provideUnknownLanguageTags() {
528 return array(
529 array( 'mw', 'non-existent two-letter code' ),
530 array( 'foo"<bar', 'very invalid language code' ),
531 );
532 }
533
534 /**
535 * Test too short timestamp
536 * @expectedException MWException
537 * @covers Language::sprintfDate
538 */
539 public function testSprintfDateTooShortTimestamp() {
540 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
541 }
542
543 /**
544 * Test too long timestamp
545 * @expectedException MWException
546 * @covers Language::sprintfDate
547 */
548 public function testSprintfDateTooLongTimestamp() {
549 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
550 }
551
552 /**
553 * Test too short timestamp
554 * @expectedException MWException
555 * @covers Language::sprintfDate
556 */
557 public function testSprintfDateNotAllDigitTimestamp() {
558 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
559 }
560
561 /**
562 * @dataProvider provideSprintfDateSamples
563 * @covers Language::sprintfDate
564 */
565 public function testSprintfDate( $format, $ts, $expected, $msg ) {
566 $ttl = null;
567 $this->assertEquals(
568 $expected,
569 $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
570 "sprintfDate('$format', '$ts'): $msg"
571 );
572 if ( $ttl ) {
573 $dt = new DateTime( $ts );
574 $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
575 $this->assertEquals(
576 $expected,
577 $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
578 "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
579 );
580 } else {
581 // advance the time enough to make all of the possible outputs different (except possibly L)
582 $dt = new DateTime( $ts );
583 $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
584 $this->assertEquals(
585 $expected,
586 $this->getLang()->sprintfDate( $format, $newTS, null ),
587 "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
588 );
589 }
590 }
591
592 /**
593 * sprintfDate should always use UTC when no zone is given.
594 * @dataProvider provideSprintfDateSamples
595 * @covers Language::sprintfDate
596 */
597 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
598 $oldTZ = date_default_timezone_get();
599 $res = date_default_timezone_set( 'Asia/Seoul' );
600 if ( !$res ) {
601 $this->markTestSkipped( "Error setting Timezone" );
602 }
603
604 $this->assertEquals(
605 $expected,
606 $this->getLang()->sprintfDate( $format, $ts ),
607 "sprintfDate('$format', '$ts'): $msg"
608 );
609
610 date_default_timezone_set( $oldTZ );
611 }
612
613 /**
614 * sprintfDate should use passed timezone
615 * @dataProvider provideSprintfDateSamples
616 * @covers Language::sprintfDate
617 */
618 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
619 $tz = new DateTimeZone( 'Asia/Seoul' );
620 if ( !$tz ) {
621 $this->markTestSkipped( "Error getting Timezone" );
622 }
623
624 $this->assertEquals(
625 $expected,
626 $this->getLang()->sprintfDate( $format, $ts, $tz ),
627 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
628 );
629 }
630
631 public static function provideSprintfDateSamples() {
632 return array(
633 array(
634 'xiY',
635 '20111212000000',
636 '1390', // note because we're testing English locale we get Latin-standard digits
637 '1390',
638 'Iranian calendar full year'
639 ),
640 array(
641 'xiy',
642 '20111212000000',
643 '90',
644 '90',
645 'Iranian calendar short year'
646 ),
647 array(
648 'o',
649 '20120101235000',
650 '2011',
651 '2011',
652 'ISO 8601 (week) year'
653 ),
654 array(
655 'W',
656 '20120101235000',
657 '52',
658 '52',
659 'Week number'
660 ),
661 array(
662 'W',
663 '20120102235000',
664 '1',
665 '1',
666 'Week number'
667 ),
668 array(
669 'o-\\WW-N',
670 '20091231235000',
671 '2009-W53-4',
672 '2009-W53-4',
673 'leap week'
674 ),
675 // What follows is mostly copied from
676 // https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
677 array(
678 'Y',
679 '20120102090705',
680 '2012',
681 '2012',
682 'Full year'
683 ),
684 array(
685 'y',
686 '20120102090705',
687 '12',
688 '12',
689 '2 digit year'
690 ),
691 array(
692 'L',
693 '20120102090705',
694 '1',
695 '1',
696 'Leap year'
697 ),
698 array(
699 'n',
700 '20120102090705',
701 '1',
702 '1',
703 'Month index, not zero pad'
704 ),
705 array(
706 'N',
707 '20120102090705',
708 '01',
709 '01',
710 'Month index. Zero pad'
711 ),
712 array(
713 'M',
714 '20120102090705',
715 'Jan',
716 'Jan',
717 'Month abbrev'
718 ),
719 array(
720 'F',
721 '20120102090705',
722 'January',
723 'January',
724 'Full month'
725 ),
726 array(
727 'xg',
728 '20120102090705',
729 'January',
730 'January',
731 'Genitive month name (same in EN)'
732 ),
733 array(
734 'j',
735 '20120102090705',
736 '2',
737 '2',
738 'Day of month (not zero pad)'
739 ),
740 array(
741 'd',
742 '20120102090705',
743 '02',
744 '02',
745 'Day of month (zero-pad)'
746 ),
747 array(
748 'z',
749 '20120102090705',
750 '1',
751 '1',
752 'Day of year (zero-indexed)'
753 ),
754 array(
755 'D',
756 '20120102090705',
757 'Mon',
758 'Mon',
759 'Day of week (abbrev)'
760 ),
761 array(
762 'l',
763 '20120102090705',
764 'Monday',
765 'Monday',
766 'Full day of week'
767 ),
768 array(
769 'N',
770 '20120101090705',
771 '7',
772 '7',
773 'Day of week (Mon=1, Sun=7)'
774 ),
775 array(
776 'w',
777 '20120101090705',
778 '0',
779 '0',
780 'Day of week (Sun=0, Sat=6)'
781 ),
782 array(
783 'N',
784 '20120102090705',
785 '1',
786 '1',
787 'Day of week'
788 ),
789 array(
790 'a',
791 '20120102090705',
792 'am',
793 'am',
794 'am vs pm'
795 ),
796 array(
797 'A',
798 '20120102120000',
799 'PM',
800 'PM',
801 'AM vs PM'
802 ),
803 array(
804 'a',
805 '20120102000000',
806 'am',
807 'am',
808 'AM vs PM'
809 ),
810 array(
811 'g',
812 '20120102090705',
813 '9',
814 '9',
815 '12 hour, not Zero'
816 ),
817 array(
818 'h',
819 '20120102090705',
820 '09',
821 '09',
822 '12 hour, zero padded'
823 ),
824 array(
825 'G',
826 '20120102090705',
827 '9',
828 '9',
829 '24 hour, not zero'
830 ),
831 array(
832 'H',
833 '20120102090705',
834 '09',
835 '09',
836 '24 hour, zero'
837 ),
838 array(
839 'H',
840 '20120102110705',
841 '11',
842 '11',
843 '24 hour, zero'
844 ),
845 array(
846 'i',
847 '20120102090705',
848 '07',
849 '07',
850 'Minutes'
851 ),
852 array(
853 's',
854 '20120102090705',
855 '05',
856 '05',
857 'seconds'
858 ),
859 array(
860 'U',
861 '20120102090705',
862 '1325495225',
863 '1325462825',
864 'unix time'
865 ),
866 array(
867 't',
868 '20120102090705',
869 '31',
870 '31',
871 'Days in current month'
872 ),
873 array(
874 'c',
875 '20120102090705',
876 '2012-01-02T09:07:05+00:00',
877 '2012-01-02T09:07:05+09:00',
878 'ISO 8601 timestamp'
879 ),
880 array(
881 'r',
882 '20120102090705',
883 'Mon, 02 Jan 2012 09:07:05 +0000',
884 'Mon, 02 Jan 2012 09:07:05 +0900',
885 'RFC 5322'
886 ),
887 array(
888 'e',
889 '20120102090705',
890 'UTC',
891 'Asia/Seoul',
892 'Timezone identifier'
893 ),
894 array(
895 'I',
896 '19880602090705',
897 '0',
898 '1',
899 'DST indicator'
900 ),
901 array(
902 'O',
903 '20120102090705',
904 '+0000',
905 '+0900',
906 'Timezone offset'
907 ),
908 array(
909 'P',
910 '20120102090705',
911 '+00:00',
912 '+09:00',
913 'Timezone offset with colon'
914 ),
915 array(
916 'T',
917 '20120102090705',
918 'UTC',
919 'KST',
920 'Timezone abbreviation'
921 ),
922 array(
923 'Z',
924 '20120102090705',
925 '0',
926 '32400',
927 'Timezone offset in seconds'
928 ),
929 array(
930 'xmj xmF xmn xmY',
931 '20120102090705',
932 '7 Safar 2 1433',
933 '7 Safar 2 1433',
934 'Islamic'
935 ),
936 array(
937 'xij xiF xin xiY',
938 '20120102090705',
939 '12 Dey 10 1390',
940 '12 Dey 10 1390',
941 'Iranian'
942 ),
943 array(
944 'xjj xjF xjn xjY',
945 '20120102090705',
946 '7 Tevet 4 5772',
947 '7 Tevet 4 5772',
948 'Hebrew'
949 ),
950 array(
951 'xjt',
952 '20120102090705',
953 '29',
954 '29',
955 'Hebrew number of days in month'
956 ),
957 array(
958 'xjx',
959 '20120102090705',
960 'Tevet',
961 'Tevet',
962 'Hebrew genitive month name (No difference in EN)'
963 ),
964 array(
965 'xkY',
966 '20120102090705',
967 '2555',
968 '2555',
969 'Thai year'
970 ),
971 array(
972 'xoY',
973 '20120102090705',
974 '101',
975 '101',
976 'Minguo'
977 ),
978 array(
979 'xtY',
980 '20120102090705',
981 '平成24',
982 '平成24',
983 'nengo'
984 ),
985 array(
986 'xrxkYY',
987 '20120102090705',
988 'MMDLV2012',
989 'MMDLV2012',
990 'Roman numerals'
991 ),
992 array(
993 'xhxjYY',
994 '20120102090705',
995 \'תשע"ב2012',
996 \'תשע"ב2012',
997 'Hebrew numberals'
998 ),
999 array(
1000 'xnY',
1001 '20120102090705',
1002 '2012',
1003 '2012',
1004 'Raw numerals (doesn\'t mean much in EN)'
1005 ),
1006 array(
1007 '[[Y "(yea"\\r)]] \\"xx\\"',
1008 '20120102090705',
1009 '[[2012 (year)]] "x"',
1010 '[[2012 (year)]] "x"',
1011 'Various escaping'
1012 ),
1013
1014 );
1015 }
1016
1017 /**
1018 * @dataProvider provideFormatSizes
1019 * @covers Language::formatSize
1020 */
1021 public function testFormatSize( $size, $expected, $msg ) {
1022 $this->assertEquals(
1023 $expected,
1024 $this->getLang()->formatSize( $size ),
1025 "formatSize('$size'): $msg"
1026 );
1027 }
1028
1029 public static function provideFormatSizes() {
1030 return array(
1031 array(
1032 0,
1033 "0 bytes",
1034 "Zero bytes"
1035 ),
1036 array(
1037 1024,
1038 "1 KB",
1039 "1 kilobyte"
1040 ),
1041 array(
1042 1024 * 1024,
1043 "1 MB",
1044 "1,024 megabytes"
1045 ),
1046 array(
1047 1024 * 1024 * 1024,
1048 "1 GB",
1049 "1 gigabyte"
1050 ),
1051 array(
1052 pow( 1024, 4 ),
1053 "1 TB",
1054 "1 terabyte"
1055 ),
1056 array(
1057 pow( 1024, 5 ),
1058 "1 PB",
1059 "1 petabyte"
1060 ),
1061 array(
1062 pow( 1024, 6 ),
1063 "1 EB",
1064 "1,024 exabyte"
1065 ),
1066 array(
1067 pow( 1024, 7 ),
1068 "1 ZB",
1069 "1 zetabyte"
1070 ),
1071 array(
1072 pow( 1024, 8 ),
1073 "1 YB",
1074 "1 yottabyte"
1075 ),
1076 // How big!? THIS BIG!
1077 );
1078 }
1079
1080 /**
1081 * @dataProvider provideFormatBitrate
1082 * @covers Language::formatBitrate
1083 */
1084 public function testFormatBitrate( $bps, $expected, $msg ) {
1085 $this->assertEquals(
1086 $expected,
1087 $this->getLang()->formatBitrate( $bps ),
1088 "formatBitrate('$bps'): $msg"
1089 );
1090 }
1091
1092 public static function provideFormatBitrate() {
1093 return array(
1094 array(
1095 0,
1096 "0 bps",
1097 "0 bits per second"
1098 ),
1099 array(
1100 999,
1101 "999 bps",
1102 "999 bits per second"
1103 ),
1104 array(
1105 1000,
1106 "1 kbps",
1107 "1 kilobit per second"
1108 ),
1109 array(
1110 1000 * 1000,
1111 "1 Mbps",
1112 "1 megabit per second"
1113 ),
1114 array(
1115 pow( 10, 9 ),
1116 "1 Gbps",
1117 "1 gigabit per second"
1118 ),
1119 array(
1120 pow( 10, 12 ),
1121 "1 Tbps",
1122 "1 terabit per second"
1123 ),
1124 array(
1125 pow( 10, 15 ),
1126 "1 Pbps",
1127 "1 petabit per second"
1128 ),
1129 array(
1130 pow( 10, 18 ),
1131 "1 Ebps",
1132 "1 exabit per second"
1133 ),
1134 array(
1135 pow( 10, 21 ),
1136 "1 Zbps",
1137 "1 zetabit per second"
1138 ),
1139 array(
1140 pow( 10, 24 ),
1141 "1 Ybps",
1142 "1 yottabit per second"
1143 ),
1144 array(
1145 pow( 10, 27 ),
1146 "1,000 Ybps",
1147 "1,000 yottabits per second"
1148 ),
1149 );
1150 }
1151
1152 /**
1153 * @dataProvider provideFormatDuration
1154 * @covers Language::formatDuration
1155 */
1156 public function testFormatDuration( $duration, $expected, $intervals = array() ) {
1157 $this->assertEquals(
1158 $expected,
1159 $this->getLang()->formatDuration( $duration, $intervals ),
1160 "formatDuration('$duration'): $expected"
1161 );
1162 }
1163
1164 public static function provideFormatDuration() {
1165 return array(
1166 array(
1167 0,
1168 '0 seconds',
1169 ),
1170 array(
1171 1,
1172 '1 second',
1173 ),
1174 array(
1175 2,
1176 '2 seconds',
1177 ),
1178 array(
1179 60,
1180 '1 minute',
1181 ),
1182 array(
1183 2 * 60,
1184 '2 minutes',
1185 ),
1186 array(
1187 3600,
1188 '1 hour',
1189 ),
1190 array(
1191 2 * 3600,
1192 '2 hours',
1193 ),
1194 array(
1195 24 * 3600,
1196 '1 day',
1197 ),
1198 array(
1199 2 * 86400,
1200 '2 days',
1201 ),
1202 array(
1203 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1204 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
1205 '1 year',
1206 ),
1207 array(
1208 2 * 31556952,
1209 '2 years',
1210 ),
1211 array(
1212 10 * 31556952,
1213 '1 decade',
1214 ),
1215 array(
1216 20 * 31556952,
1217 '2 decades',
1218 ),
1219 array(
1220 100 * 31556952,
1221 '1 century',
1222 ),
1223 array(
1224 200 * 31556952,
1225 '2 centuries',
1226 ),
1227 array(
1228 1000 * 31556952,
1229 '1 millennium',
1230 ),
1231 array(
1232 2000 * 31556952,
1233 '2 millennia',
1234 ),
1235 array(
1236 9001,
1237 '2 hours, 30 minutes and 1 second'
1238 ),
1239 array(
1240 3601,
1241 '1 hour and 1 second'
1242 ),
1243 array(
1244 31556952 + 2 * 86400 + 9000,
1245 '1 year, 2 days, 2 hours and 30 minutes'
1246 ),
1247 array(
1248 42 * 1000 * 31556952 + 42,
1249 '42 millennia and 42 seconds'
1250 ),
1251 array(
1252 60,
1253 '60 seconds',
1254 array( 'seconds' ),
1255 ),
1256 array(
1257 61,
1258 '61 seconds',
1259 array( 'seconds' ),
1260 ),
1261 array(
1262 1,
1263 '1 second',
1264 array( 'seconds' ),
1265 ),
1266 array(
1267 31556952 + 2 * 86400 + 9000,
1268 '1 year, 2 days and 150 minutes',
1269 array( 'years', 'days', 'minutes' ),
1270 ),
1271 array(
1272 42,
1273 '0 days',
1274 array( 'years', 'days' ),
1275 ),
1276 array(
1277 31556952 + 2 * 86400 + 9000,
1278 '1 year, 2 days and 150 minutes',
1279 array( 'minutes', 'days', 'years' ),
1280 ),
1281 array(
1282 42,
1283 '0 days',
1284 array( 'days', 'years' ),
1285 ),
1286 );
1287 }
1288
1289 /**
1290 * @dataProvider provideCheckTitleEncodingData
1291 * @covers Language::checkTitleEncoding
1292 */
1293 public function testCheckTitleEncoding( $s ) {
1294 $this->assertEquals(
1295 $s,
1296 $this->getLang()->checkTitleEncoding( $s ),
1297 "checkTitleEncoding('$s')"
1298 );
1299 }
1300
1301 public static function provideCheckTitleEncodingData() {
1302 // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
1303 return array(
1304 array( "" ),
1305 array( "United States of America" ), // 7bit ASCII
1306 array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
1307 array(
1308 rawurldecode(
1309 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1310 )
1311 ),
1312 // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
1313 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1314 // uses mb_check_encoding for its test.
1315 array(
1316 rawurldecode(
1317 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1318 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1319 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1320 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1321 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1322 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1323 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1324 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1325 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1326 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1327 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1328 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1329 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1330 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1331 ),
1332 ),
1333 array(
1334 rawurldecode(
1335 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1336 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1337 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1338 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1339 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1340 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1341 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1342 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1343 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1344 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1345 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1346 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1347 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1348 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1349 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1350 )
1351 )
1352 );
1353 // @codingStandardsIgnoreEnd
1354 }
1355
1356 /**
1357 * @dataProvider provideRomanNumeralsData
1358 * @covers Language::romanNumeral
1359 */
1360 public function testRomanNumerals( $num, $numerals ) {
1361 $this->assertEquals(
1362 $numerals,
1363 Language::romanNumeral( $num ),
1364 "romanNumeral('$num')"
1365 );
1366 }
1367
1368 public static function provideRomanNumeralsData() {
1369 return array(
1370 array( 1, 'I' ),
1371 array( 2, 'II' ),
1372 array( 3, 'III' ),
1373 array( 4, 'IV' ),
1374 array( 5, 'V' ),
1375 array( 6, 'VI' ),
1376 array( 7, 'VII' ),
1377 array( 8, 'VIII' ),
1378 array( 9, 'IX' ),
1379 array( 10, 'X' ),
1380 array( 20, 'XX' ),
1381 array( 30, 'XXX' ),
1382 array( 40, 'XL' ),
1383 array( 49, 'XLIX' ),
1384 array( 50, 'L' ),
1385 array( 60, 'LX' ),
1386 array( 70, 'LXX' ),
1387 array( 80, 'LXXX' ),
1388 array( 90, 'XC' ),
1389 array( 99, 'XCIX' ),
1390 array( 100, 'C' ),
1391 array( 200, 'CC' ),
1392 array( 300, 'CCC' ),
1393 array( 400, 'CD' ),
1394 array( 500, 'D' ),
1395 array( 600, 'DC' ),
1396 array( 700, 'DCC' ),
1397 array( 800, 'DCCC' ),
1398 array( 900, 'CM' ),
1399 array( 999, 'CMXCIX' ),
1400 array( 1000, 'M' ),
1401 array( 1989, 'MCMLXXXIX' ),
1402 array( 2000, 'MM' ),
1403 array( 3000, 'MMM' ),
1404 array( 4000, 'MMMM' ),
1405 array( 5000, 'MMMMM' ),
1406 array( 6000, 'MMMMMM' ),
1407 array( 7000, 'MMMMMMM' ),
1408 array( 8000, 'MMMMMMMM' ),
1409 array( 9000, 'MMMMMMMMM' ),
1410 array( 9999, 'MMMMMMMMMCMXCIX' ),
1411 array( 10000, 'MMMMMMMMMM' ),
1412 );
1413 }
1414
1415 /**
1416 * @dataProvider provideHebrewNumeralsData
1417 * @covers Language::hebrewNumeral
1418 */
1419 public function testHebrewNumeral( $num, $numerals ) {
1420 $this->assertEquals(
1421 $numerals,
1422 Language::hebrewNumeral( $num ),
1423 "hebrewNumeral('$num')"
1424 );
1425 }
1426
1427 public static function provideHebrewNumeralsData() {
1428 return array(
1429 array( -1, -1 ),
1430 array( 0, 0 ),
1431 array( 1, "א'" ),
1432 array( 2, "ב'" ),
1433 array( 3, "ג'" ),
1434 array( 4, "ד'" ),
1435 array( 5, "ה'" ),
1436 array( 6, "ו'" ),
1437 array( 7, "ז'" ),
1438 array( 8, "ח'" ),
1439 array( 9, "ט'" ),
1440 array( 10, "י'" ),
1441 array( 11, 'י"א' ),
1442 array( 14, 'י"ד' ),
1443 array( 15, 'ט"ו' ),
1444 array( 16, 'ט"ז' ),
1445 array( 17, 'י"ז' ),
1446 array( 20, "כ'" ),
1447 array( 21, 'כ"א' ),
1448 array( 30, "ל'" ),
1449 array( 40, "מ'" ),
1450 array( 50, "נ'" ),
1451 array( 60, "ס'" ),
1452 array( 70, "ע'" ),
1453 array( 80, "פ'" ),
1454 array( 90, "צ'" ),
1455 array( 99, 'צ"ט' ),
1456 array( 100, "ק'" ),
1457 array( 101, 'ק"א' ),
1458 array( 110, 'ק"י' ),
1459 array( 200, "ר'" ),
1460 array( 300, "ש'" ),
1461 array( 400, "ת'" ),
1462 array( 500, 'ת"ק' ),
1463 array( 800, 'ת"ת' ),
1464 array( 1000, "א' אלף" ),
1465 array( 1001, "א'א'" ),
1466 array( 1012, "א'י\"ב" ),
1467 array( 1020, "א'ך'" ),
1468 array( 1030, "א'ל'" ),
1469 array( 1081, "א'פ\"א" ),
1470 array( 2000, "ב' אלפים" ),
1471 array( 2016, "ב'ט\"ז" ),
1472 array( 3000, "ג' אלפים" ),
1473 array( 4000, "ד' אלפים" ),
1474 array( 4904, "ד'תתק\"ד" ),
1475 array( 5000, "ה' אלפים" ),
1476 array( 5680, "ה'תר\"ף" ),
1477 array( 5690, "ה'תר\"ץ" ),
1478 array( 5708, "ה'תש\"ח" ),
1479 array( 5720, "ה'תש\"ך" ),
1480 array( 5740, "ה'תש\"ם" ),
1481 array( 5750, "ה'תש\"ן" ),
1482 array( 5775, "ה'תשע\"ה" ),
1483 );
1484 }
1485
1486 /**
1487 * @dataProvider providePluralData
1488 * @covers Language::convertPlural
1489 */
1490 public function testConvertPlural( $expected, $number, $forms ) {
1491 $chosen = $this->getLang()->convertPlural( $number, $forms );
1492 $this->assertEquals( $expected, $chosen );
1493 }
1494
1495 public static function providePluralData() {
1496 // Params are: [expected text, number given, [the plural forms]]
1497 return array(
1498 array( 'plural', 0, array(
1499 'singular', 'plural'
1500 ) ),
1501 array( 'explicit zero', 0, array(
1502 '0=explicit zero', 'singular', 'plural'
1503 ) ),
1504 array( 'explicit one', 1, array(
1505 'singular', 'plural', '1=explicit one',
1506 ) ),
1507 array( 'singular', 1, array(
1508 'singular', 'plural', '0=explicit zero',
1509 ) ),
1510 array( 'plural', 3, array(
1511 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1512 ) ),
1513 array( 'explicit eleven', 11, array(
1514 'singular', 'plural', '11=explicit eleven',
1515 ) ),
1516 array( 'plural', 12, array(
1517 'singular', 'plural', '11=explicit twelve',
1518 ) ),
1519 array( 'plural', 12, array(
1520 'singular', 'plural', '=explicit form',
1521 ) ),
1522 array( 'other', 2, array(
1523 'kissa=kala', '1=2=3', 'other',
1524 ) ),
1525 array( '', 2, array(
1526 '0=explicit zero', '1=explicit one',
1527 ) ),
1528 );
1529 }
1530
1531 /**
1532 * @covers Language::embedBidi()
1533 */
1534 public function testEmbedBidi() {
1535 $lre = "\xE2\x80\xAA"; // U+202A LEFT-TO-RIGHT EMBEDDING
1536 $rle = "\xE2\x80\xAB"; // U+202B RIGHT-TO-LEFT EMBEDDING
1537 $pdf = "\xE2\x80\xAC"; // U+202C POP DIRECTIONAL FORMATTING
1538 $lang = $this->getLang();
1539 $this->assertEquals(
1540 '123',
1541 $lang->embedBidi( '123' ),
1542 'embedBidi with neutral argument'
1543 );
1544 $this->assertEquals(
1545 $lre . 'Ben_(WMF)' . $pdf,
1546 $lang->embedBidi( 'Ben_(WMF)' ),
1547 'embedBidi with LTR argument'
1548 );
1549 $this->assertEquals(
1550 $rle . 'יהודי (מנוחין)' . $pdf,
1551 $lang->embedBidi( 'יהודי (מנוחין)' ),
1552 'embedBidi with RTL argument'
1553 );
1554 }
1555
1556 /**
1557 * @covers Language::translateBlockExpiry()
1558 * @dataProvider provideTranslateBlockExpiry
1559 */
1560 public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
1561 $lang = $this->getLang();
1562 if ( is_array( $expectedData ) ) {
1563 list( $func, $arg ) = $expectedData;
1564 $expected = $lang->$func( $arg );
1565 } else {
1566 $expected = $expectedData;
1567 }
1568 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1569 }
1570
1571 public static function provideTranslateBlockExpiry() {
1572 return array(
1573 array( '2 hours', '2 hours', 'simple data from ipboptions' ),
1574 array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
1575 array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
1576 array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
1577 array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
1578 array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
1579 array( array( 'formatDuration', 0 ), 'now', 'now' ),
1580 array(
1581 array( 'timeanddate', '20120102070000' ),
1582 '2012-1-1 7:00 +1 day',
1583 'mixed, handled as absolute'
1584 ),
1585 array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
1586 array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
1587 array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
1588 array( 'dummy', 'dummy', 'return garbage as is' ),
1589 );
1590 }
1591
1592 /**
1593 * @dataProvider parseFormattedNumberProvider
1594 */
1595 public function testParseFormattedNumber( $langCode, $number ) {
1596 $lang = Language::factory( $langCode );
1597
1598 $localisedNum = $lang->formatNum( $number );
1599 $normalisedNum = $lang->parseFormattedNumber( $localisedNum );
1600
1601 $this->assertEquals( $number, $normalisedNum );
1602 }
1603
1604 public function parseFormattedNumberProvider() {
1605 return array(
1606 array( 'de', 377.01 ),
1607 array( 'fa', 334 ),
1608 array( 'fa', 382.772 ),
1609 array( 'ar', 1844 ),
1610 array( 'lzh', 3731 ),
1611 array( 'zh-classical', 7432 )
1612 );
1613 }
1614
1615 /**
1616 * @covers Language::commafy()
1617 * @dataProvider provideCommafyData
1618 */
1619 public function testCommafy( $number, $numbersWithCommas ) {
1620 $this->assertEquals(
1621 $numbersWithCommas,
1622 $this->getLang()->commafy( $number ),
1623 "commafy('$number')"
1624 );
1625 }
1626
1627 public static function provideCommafyData() {
1628 return array(
1629 array( -1, '-1' ),
1630 array( 10, '10' ),
1631 array( 100, '100' ),
1632 array( 1000, '1,000' ),
1633 array( 10000, '10,000' ),
1634 array( 100000, '100,000' ),
1635 array( 1000000, '1,000,000' ),
1636 array( -1.0001, '-1.0001' ),
1637 array( 1.0001, '1.0001' ),
1638 array( 10.0001, '10.0001' ),
1639 array( 100.0001, '100.0001' ),
1640 array( 1000.0001, '1,000.0001' ),
1641 array( 10000.0001, '10,000.0001' ),
1642 array( 100000.0001, '100,000.0001' ),
1643 array( 1000000.0001, '1,000,000.0001' ),
1644 array( '200000000000000000000', '200,000,000,000,000,000,000' ),
1645 array( '-200000000000000000000', '-200,000,000,000,000,000,000' ),
1646 );
1647 }
1648
1649 /**
1650 * @covers Language::listToText
1651 */
1652 public function testListToText() {
1653 $lang = $this->getLang();
1654 $and = $lang->getMessageFromDB( 'and' );
1655 $s = $lang->getMessageFromDB( 'word-separator' );
1656 $c = $lang->getMessageFromDB( 'comma-separator' );
1657
1658 $this->assertEquals( '', $lang->listToText( array() ) );
1659 $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
1660 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
1661 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
1662 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
1663 }
1664
1665 /**
1666 * @dataProvider provideIsSupportedLanguage
1667 * @covers Language::isSupportedLanguage
1668 */
1669 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1670 $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
1671 }
1672
1673 public static function provideIsSupportedLanguage() {
1674 return array(
1675 array( 'en', true, 'is supported language' ),
1676 array( 'fi', true, 'is supported language' ),
1677 array( 'bunny', false, 'is not supported language' ),
1678 array( 'FI', false, 'is not supported language, input should be in lower case' ),
1679 );
1680 }
1681
1682 /**
1683 * @dataProvider provideGetParentLanguage
1684 * @covers Language::getParentLanguage
1685 */
1686 public function testGetParentLanguage( $code, $expected, $comment ) {
1687 $lang = Language::factory( $code );
1688 if ( is_null( $expected ) ) {
1689 $this->assertNull( $lang->getParentLanguage(), $comment );
1690 } else {
1691 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1692 }
1693 }
1694
1695 public static function provideGetParentLanguage() {
1696 return array(
1697 array( 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ),
1698 array( 'zh', 'zh', 'zh is defined as the parent language of zh, '
1699 . 'because zh converter can convert zh-cn to zh' ),
1700 array( 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ),
1701 array( 'en-gb', null, 'en does not have converter' ),
1702 array( 'en', null, 'en does not have converter. Although FakeConverter '
1703 . 'handles en -> en conversion but it is useless' ),
1704 );
1705 }
1706
1707 /**
1708 * @dataProvider provideGetNamespaceAliases
1709 * @covers Language::getNamespaceAliases
1710 */
1711 public function testGetNamespaceAliases( $languageCode, $subset ) {
1712 $language = Language::factory( $languageCode );
1713 $aliases = $language->getNamespaceAliases();
1714 foreach ( $subset as $alias => $nsId ) {
1715 $this->assertEquals( $nsId, $aliases[$alias] );
1716 }
1717 }
1718
1719 public static function provideGetNamespaceAliases() {
1720 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
1721 return array(
1722 array(
1723 'zh',
1724 array(
1725 '文件' => NS_FILE,
1726 '檔案' => NS_FILE,
1727 ),
1728 ),
1729 );
1730 }
1731 }