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