3 use MediaWiki\Api\ApiMessage
;
4 use MediaWiki\Language\RawMessage
;
5 use MediaWiki\MainConfigNames
;
6 use MediaWiki\MediaWikiServices
;
7 use MediaWiki\Message\Message
;
8 use MediaWiki\Page\PageReferenceValue
;
9 use MediaWiki\Title\Title
;
10 use Wikimedia\Assert\ParameterTypeException
;
11 use Wikimedia\Bcp47Code\Bcp47CodeValue
;
12 use Wikimedia\Message\MessageSpecifier
;
18 * @covers \MediaWiki\Message\Message
20 class MessageTest
extends MediaWikiLangTestCase
{
22 protected function setUp(): void
{
25 $this->overrideConfigValue( MainConfigNames
::ForceUIMsgAsContentMsg
, [] );
26 $this->setUserLang( 'en' );
30 * @dataProvider provideConstructor
32 public function testConstructor( $expectedLang, $key, $params, $language ) {
33 $message = new Message( $key, $params, $language );
35 $this->assertSame( $key, $message->getKey() );
36 $this->assertSame( $params, $message->getParams() );
37 $this->assertSame( $expectedLang->getCode(), $message->getLanguage()->getCode() );
39 $messageSpecifier = $this->getMockForAbstractClass( MessageSpecifier
::class );
40 $messageSpecifier->method( 'getKey' )->willReturn( $key );
41 $messageSpecifier->method( 'getParams' )->willReturn( $params );
42 $message = new Message( $messageSpecifier, [], $language );
44 $this->assertSame( $key, $message->getKey() );
45 $this->assertSame( $params, $message->getParams() );
46 $this->assertSame( $expectedLang->getCode(), $message->getLanguage()->getCode() );
49 public static function provideConstructor() {
50 $langDe = MediaWikiServices
::getInstance()->getLanguageFactory()->getLanguage( 'de' );
51 $langEn = MediaWikiServices
::getInstance()->getLanguageFactory()->getLanguage( 'en' );
54 [ $langDe, 'foo', [], $langDe ],
55 [ $langDe, 'foo', [ 'bar' ], $langDe ],
56 [ $langEn, 'foo', [ 'bar' ], null ]
60 public static function provideConstructorParams() {
87 [ Message
::rawParam( 'baz' ) ],
88 [ Message
::rawParam( 'baz' ) ],
91 [ Message
::rawParam( 'baz' ), 'foo' ],
92 [ Message
::rawParam( 'baz' ), 'foo' ],
95 [ Message
::rawParam( 'baz' ) ],
96 [ [ Message
::rawParam( 'baz' ) ] ],
99 [ Message
::rawParam( 'baz' ), 'foo' ],
100 [ [ Message
::rawParam( 'baz' ), 'foo' ] ],
103 // Test handling of erroneous input, to detect if it changes
105 [ [ 'baz', 'foo' ], 'hhh' ],
106 [ [ 'baz', 'foo' ], 'hhh' ],
109 [ [ 'baz', 'foo' ], 'hhh', [ 'ahahahahha' ] ],
110 [ [ 'baz', 'foo' ], 'hhh', [ 'ahahahahha' ] ],
113 [ [ 'baz', 'foo' ], [ 'ahahahahha' ] ],
114 [ [ 'baz', 'foo' ], [ 'ahahahahha' ] ],
117 [ [ 'baz' ], [ 'ahahahahha' ] ],
118 [ [ 'baz' ], [ 'ahahahahha' ] ],
124 * @dataProvider provideConstructorParams
126 public function testConstructorParams( $expected, $args ) {
127 $msg = new Message( 'imasomething' );
129 $returned = $msg->params( ...$args );
131 $this->assertSame( $msg, $returned );
132 $this->assertEquals( $expected, $msg->getParams() );
135 public static function provideConstructorLanguage() {
137 [ 'foo', [ 'bar' ], 'en' ],
138 [ 'foo', [ 'bar' ], 'de' ]
143 * @dataProvider provideConstructorLanguage
145 public function testConstructorLanguage( $key, $params, $languageCode ) {
146 $language = $this->getServiceContainer()->getLanguageFactory()
147 ->getLanguage( $languageCode );
148 $message = new Message( $key, $params, $language );
150 $this->assertEquals( $language, $message->getLanguage() );
153 public static function provideKeys() {
157 'expected' => [ 'mainpage' ],
160 'key' => [ 'mainpage' ],
161 'expected' => [ 'mainpage' ],
164 'key' => [ 'mainpage-foo', 'mainpage-bar', 'mainpage' ],
165 'expected' => [ 'mainpage-foo', 'mainpage-bar', 'mainpage' ],
170 'exception' => InvalidArgumentException
::class,
175 'exception' => InvalidArgumentException
::class,
180 'exception' => InvalidArgumentException
::class,
186 * @dataProvider provideKeys
188 public function testKeys( $key, $expected, $exception = null ) {
190 $this->expectException( $exception );
193 $msg = new Message( $key );
194 $this->assertContains( $msg->getKey(), $expected );
195 $this->assertSame( $expected, $msg->getKeysToTry() );
196 $this->assertSame( count( $expected ) > 1, $msg->isMultiKey() );
199 public function testWfMessage() {
200 $this->assertInstanceOf( Message
::class, wfMessage( 'mainpage' ) );
201 $this->assertInstanceOf( Message
::class, wfMessage( 'i-dont-exist-evar' ) );
204 public function testNewFromKey() {
205 $this->assertInstanceOf( Message
::class, Message
::newFromKey( 'mainpage' ) );
206 $this->assertInstanceOf( Message
::class, Message
::newFromKey( 'i-dont-exist-evar' ) );
209 public function testWfMessageParams() {
210 $this->assertSame( 'Return to $1.', wfMessage( 'returnto' )->text() );
211 $this->assertSame( 'Return to $1.', wfMessage( 'returnto', [] )->text() );
214 wfMessage( 'returnto', Message
::numParam( 1024 ) )->text()
218 wfMessage( 'returnto', [ Message
::numParam( 1024 ) ] )->text()
221 'You have foo (bar).',
222 wfMessage( 'new-messages', 'foo', 'bar' )->text()
225 'You have foo (bar).',
226 wfMessage( 'new-messages', [ 'foo', 'bar' ] )->text()
229 'You have 1,024 (bar).',
232 Message
::numParam( 1024 ), 'bar'
236 'You have foo (2,048).',
239 'foo', Message
::numParam( 2048 )
243 'You have 1,024 (2,048).',
246 [ Message
::numParam( 1024 ), Message
::numParam( 2048 ) ]
251 public function testExists() {
252 $this->assertTrue( wfMessage( 'mainpage' )->exists() );
253 $this->assertTrue( wfMessage( 'mainpage' )->params( [] )->exists() );
254 $this->assertTrue( wfMessage( 'mainpage' )->rawParams( 'foo', 123 )->exists() );
255 $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->exists() );
256 $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->params( [] )->exists() );
257 $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->rawParams( 'foo', 123 )->exists() );
260 public function testToStringKey() {
261 $this->assertSame( 'Main Page', wfMessage( 'mainpage' )->text() );
262 $this->assertSame( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->text() );
263 $this->assertSame( '⧼i<dont>exist-evar⧽', wfMessage( 'i<dont>exist-evar' )->text() );
264 $this->assertSame( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->plain() );
265 $this->assertSame( '⧼i<dont>exist-evar⧽', wfMessage( 'i<dont>exist-evar' )->plain() );
266 $this->assertSame( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->escaped() );
268 '⧼i<dont>exist-evar⧽',
269 wfMessage( 'i<dont>exist-evar' )->escaped()
273 public static function provideToString() {
275 // key, transformation, transformed, transformed implicitly
276 [ 'mainpage', 'plain', 'Main Page', 'Main Page' ],
277 [ 'i-dont-exist-evar', 'plain', '⧼i-dont-exist-evar⧽', '⧼i-dont-exist-evar⧽' ],
278 [ 'i-dont-exist-evar', 'escaped', '⧼i-dont-exist-evar⧽', '⧼i-dont-exist-evar⧽' ],
279 [ 'script>alert(1)</script', 'escaped', '⧼script>alert(1)</script⧽',
280 '⧼script>alert(1)</script⧽' ],
281 [ 'script>alert(1)</script', 'plain', '⧼script>alert(1)</script⧽',
282 '⧼script>alert(1)</script⧽' ],
287 * @dataProvider provideToString
289 public function testToString( $key, $format, $expect, $expectImplicit ) {
290 $msg = new Message( $key );
291 $this->assertSame( $expect, $msg->$format() );
293 // This used to behave the same as toString() and was a security risk.
294 // It now has a stable return value that is always parsed/sanitized. (T146416)
295 $this->assertSame( $expectImplicit, $msg->__toString(), '__toString is not affected by format call' );
298 public static function provideToString_raw() {
300 [ '<span>foo</span>', 'parse', '<span>foo</span>', '<span>foo</span>' ],
301 [ '<span>foo</span>', 'escaped', '<span>foo</span>',
302 '<span>foo</span>' ],
303 [ '<span>foo</span>', 'plain', '<span>foo</span>', '<span>foo</span>' ],
304 [ '<script>alert(1)</script>', 'parse', '<script>alert(1)</script>',
305 '<script>alert(1)</script>' ],
306 [ '<script>alert(1)</script>', 'escaped', '<script>alert(1)</script>',
307 '<script>alert(1)</script>' ],
308 [ '<script>alert(1)</script>', 'plain', '<script>alert(1)</script>',
309 '<script>alert(1)</script>' ],
314 * @dataProvider provideToString_raw
316 public function testToString_raw( $message, $format, $expect, $expectImplicit ) {
317 // make the message behave like RawMessage and use the key as-is
318 $msg = $this->getMockBuilder( Message
::class )->onlyMethods( [ 'fetchMessage' ] )
319 ->disableOriginalConstructor()
321 $msg->method( 'fetchMessage' )->willReturn( $message );
322 /** @var Message $msg */
324 $this->assertSame( $expect, $msg->$format() );
326 $this->assertSame( $expectImplicit, $msg->__toString() );
329 public function testInLanguage() {
330 $this->assertSame( 'Main Page', wfMessage( 'mainpage' )->inLanguage( 'en' )->text() );
331 $this->assertSame( 'Главна страна',
332 wfMessage( 'mainpage' )->inLanguage( 'sr-ec' )->text() );
334 // NOTE: make sure internal caching of the message text is reset appropriately
335 $msg = wfMessage( 'mainpage' );
336 $this->assertSame( 'Main Page', $msg->inLanguage( 'en' )->text() );
339 $msg->inLanguage( 'sr-ec' )->text()
343 public function testInLanguageBcp47() {
344 $en = new Bcp47CodeValue( 'en' );
345 $sr = new Bcp47CodeValue( 'sr-Cyrl' );
346 $this->assertSame( 'Main Page', wfMessage( 'mainpage' )->inLanguage( $en )->text() );
347 $this->assertSame( 'Главна страна',
348 wfMessage( 'mainpage' )->inLanguage( $sr )->text() );
350 // NOTE: make sure internal caching of the message text is reset appropriately
351 $msg = wfMessage( 'mainpage' );
352 $this->assertSame( 'Main Page', $msg->inLanguage( $en )->text() );
355 $msg->inLanguage( $sr )->text()
359 public function testRawParams() {
361 '(Заглавная страница)',
362 wfMessage( 'parentheses', 'Заглавная страница' )->plain()
365 '(Заглавная страница $1)',
366 wfMessage( 'parentheses', 'Заглавная страница $1' )->plain()
369 '(Заглавная страница)',
370 wfMessage( 'parentheses' )->rawParams( 'Заглавная страница' )->plain()
373 '(Заглавная страница $1)',
374 wfMessage( 'parentheses' )->rawParams( 'Заглавная страница $1' )->plain()
379 * @covers \MediaWiki\Language\RawMessage
381 public function testRawMessage() {
382 $msg = new RawMessage( 'example &' );
383 $this->assertSame( 'example &', $msg->plain() );
384 $this->assertSame( 'example &', $msg->escaped() );
387 public static function provideRawMessage() {
388 yield
'No params' => [
389 new RawMessage( 'Foo Bar' ),
392 yield
'Single param' => [
393 new RawMessage( '$1', [ 'Foo Bar' ] ),
396 yield
'Multiple params' => [
397 new RawMessage( '$2 and $1', [ 'One', 'Two' ] ),
403 * @dataProvider provideRawMessage
404 * @covers \MediaWiki\Language\RawMessage
406 public function testRawMessageParams( RawMessage
$m, string $param ) {
407 $this->assertEquals( [ $param ], $m->getParams() );
411 * @dataProvider provideRawMessage
412 * @covers \MediaWiki\Language\RawMessage
414 public function testRawMessageDisassembleSpecifier( RawMessage
$m, string $text ) {
415 // Check this just in case, although it's not really covered by this test.
416 $this->assertEquals( $text, $m->text(), 'output from RawMessage itself' );
417 // Verify that RawMessage can be used as a MessageSpecifier, producing the same output.
418 $msg = wfMessage( $m );
419 $this->assertEquals( $text, $msg->text(), 'output from RawMessage used as MessageSpecifier' );
420 // Verify that if you disassemble it using MessageSpecifier's getKey() and getParams() methods,
421 // then assemble a new MessageSpecifier using the return values, you will get the same output.
422 $msg2 = wfMessage( $m->getKey(), ...$m->getParams() );
423 $this->assertEquals( $text, $msg2->text(), 'output from RawMessage disassembled' );
427 * @covers \MediaWiki\Language\RawMessage
428 * @covers \MediaWiki\Parser\CoreTagHooks::html
430 public function testRawHtmlInMsg() {
431 $this->overrideConfigValue( MainConfigNames
::RawHtml
, true );
433 $msg = new RawMessage( '<html><script>alert("xss")</script></html>' );
434 $txt = '<span class="error"><html> tags cannot be' .
435 ' used outside of normal pages.</span>';
436 $this->assertSame( $txt, $msg->parse() );
439 public function testReplaceManyParams() {
440 $msg = new RawMessage( '$1$2$3$4$5$6$7$8$9$10$11$12' );
441 // One less than above has placeholders
442 $params = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' ];
445 $msg->params( $params )->plain(),
446 'Params > 9 are replaced correctly'
450 public function testNumParams() {
451 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
452 $msg = new RawMessage( '$1' );
455 $lang->formatNum( 123456.789 ),
456 $msg->inLanguage( $lang )->numParams( 123456.789 )->plain(),
457 'numParams is handled correctly'
461 public function testDurationParams() {
462 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
463 $msg = new RawMessage( '$1' );
466 $lang->formatDuration( 1234 ),
467 $msg->inLanguage( $lang )->durationParams( 1234 )->plain(),
468 'durationParams is handled correctly'
473 * FIXME: This should not need database, but Language#formatExpiry does (T57912)
475 public function testExpiryParams() {
476 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
477 $msg = new RawMessage( '$1' );
479 $ts = wfTimestampNow();
481 $lang->formatExpiry( $ts ),
482 $msg->inLanguage( $lang )->expiryParams( $ts )->plain(),
483 'expiryParams is handled correctly'
487 public function testDateTimeParams() {
488 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
489 $msg = new RawMessage( '$1' );
491 $ts = wfTimestampNow();
493 $lang->timeanddate( $ts ),
494 $msg->inLanguage( $lang )->dateTimeParams( $ts )->plain(),
495 'dateTime is handled correctly'
499 public function testDateParams() {
500 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
501 $msg = new RawMessage( '$1' );
503 $ts = wfTimestampNow();
506 $msg->inLanguage( $lang )->dateParams( $ts )->plain(),
507 'date is handled correctly'
511 public function testTimeParams() {
512 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
513 $msg = new RawMessage( '$1' );
515 $ts = wfTimestampNow();
518 $msg->inLanguage( $lang )->timeParams( $ts )->plain(),
519 'time is handled correctly'
523 public function testUserGroupParams() {
524 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'qqx' );
525 $msg = new RawMessage( '$1' );
526 $this->setUserLang( $lang );
529 $msg->userGroupParams( 'bot' )->plain(),
530 'user group is handled correctly'
534 public function testTimeperiodParams() {
535 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
536 $msg = new RawMessage( '$1' );
539 $lang->formatTimePeriod( 1234 ),
540 $msg->inLanguage( $lang )->timeperiodParams( 1234 )->plain(),
541 'timeperiodParams is handled correctly'
545 public function testSizeParams() {
546 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
547 $msg = new RawMessage( '$1' );
550 $lang->formatSize( 123456 ),
551 $msg->inLanguage( $lang )->sizeParams( 123456 )->plain(),
552 'sizeParams is handled correctly'
556 public function testBitrateParams() {
557 $lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' );
558 $msg = new RawMessage( '$1' );
561 $lang->formatBitrate( 123456 ),
562 $msg->inLanguage( $lang )->bitrateParams( 123456 )->plain(),
563 'bitrateParams is handled correctly'
567 public static function providePlaintextParams() {
570 'one $2 <div>foo</div> [[Bar]] {{Baz}} <',
576 'one $2 <div>foo</div> [[Bar]] {{Baz}} <',
581 'one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;',
586 'one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;',
591 "<p>one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;\n</p>",
598 * @dataProvider providePlaintextParams
600 public function testPlaintextParams( $expect, $format ) {
601 $msg = new RawMessage( '$1 $2' );
604 '<div>foo</div> [[Bar]] {{Baz}} <',
608 $msg->inLanguage( 'en' )->plaintextParams( $params )->$format(),
609 "Fail formatting for $format"
613 public static function provideListParam() {
614 $lang = MediaWikiServices
::getInstance()->getLanguageFactory()->getLanguage( 'de' );
615 $msg1 = new Message( 'mainpage', [], $lang );
616 $msg2 = new RawMessage( "''link''", [], $lang );
619 'Simple comma list' => [
626 'Simple semicolon list' => [
633 'Simple pipe list' => [
640 'Simple text list' => [
654 'List with all "before" params, ->text()' => [
655 [ "''link''", Message
::numParam( 12345678 ) ],
658 '\'\'link\'\'; 12,345,678'
661 'List with all "before" params, ->parse()' => [
662 [ "''link''", Message
::numParam( 12345678 ) ],
665 '<i>link</i>; 12,345,678'
668 'List with all "after" params, ->text()' => [
669 [ $msg1, $msg2, Message
::rawParam( '[[foo]]' ) ],
672 'Main Page; \'\'link\'\'; [[foo]]'
675 'List with all "after" params, ->parse()' => [
676 [ $msg1, $msg2, Message
::rawParam( '[[foo]]' ) ],
679 'Main Page; <i>link</i>; [[foo]]'
682 'List with both "before" and "after" params, ->text()' => [
683 [ $msg1, $msg2, Message
::rawParam( '[[foo]]' ), "''link''", Message
::numParam( 12345678 ) ],
686 'Main Page; \'\'link\'\'; [[foo]]; \'\'link\'\'; 12,345,678'
689 'List with both "before" and "after" params, ->parse()' => [
690 [ $msg1, $msg2, Message
::rawParam( '[[foo]]' ), "''link''", Message
::numParam( 12345678 ) ],
693 'Main Page; <i>link</i>; [[foo]]; <i>link</i>; 12,345,678'
699 * @dataProvider provideListParam
701 public function testListParam( $list, $type, $format, $expect ) {
702 $msg = new RawMessage( '$1' );
703 $msg->params( [ Message
::listParam( $list, $type ) ] );
706 $msg->inLanguage( 'en' )->$format()
710 public function testMessageAsParam() {
711 $msg = new Message( 'returnto', [
712 new Message( 'apihelp-link', [
713 'foo', new Message( 'mainpage', [],
714 $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'en' ) )
715 ], $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'de' ) )
716 ], $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'es' ) );
719 'Volver a [[Special:ApiHelp/foo|Página principal]].',
721 'Process with ->text()'
724 '<p>Volver a <a href="/wiki/Special:ApiHelp/foo" title="Special:ApiHelp/foo">Página '
725 . "principal</a>.\n</p>",
726 $msg->parseAsBlock(),
727 'Process with ->parseAsBlock()'
731 public static function provideParser() {
734 "''&'' <x><!-- x -->",
739 "''&'' <x><!-- x -->",
743 '<i>&</i> <x>',
748 "<p><i>&</i> <x>\n</p>",
755 * @dataProvider provideParser
757 public function testParser( $expect, $format ) {
758 $msg = new RawMessage( "''&'' <x><!-- x -->" );
761 $msg->inLanguage( 'en' )->$format()
766 * @covers \LanguageQqx
768 public function testQqxPlaceholders() {
771 wfMessage( 'test' )->inLanguage( 'qqx' )->text()
775 wfMessage( 'test' )->params( 'a', 'b' )->inLanguage( 'qqx' )->text()
778 '(test / other-test)',
779 wfMessageFallback( 'test', 'other-test' )->inLanguage( 'qqx' )->text()
782 '(test / other-test: a, b)',
783 wfMessageFallback( 'test', 'other-test' )->params( 'a', 'b' )->inLanguage( 'qqx' )->text()
787 public function testInContentLanguage() {
788 $this->setUserLang( 'fr' );
790 // NOTE: make sure internal caching of the message text is reset appropriately
791 $msg = wfMessage( 'mainpage' );
792 $this->assertSame( 'Hauptseite', $msg->inLanguage( 'de' )->plain(), "inLanguage( 'de' )" );
793 $this->assertSame( 'Main Page', $msg->inContentLanguage()->plain(), "inContentLanguage()" );
794 $this->assertSame( 'Accueil', $msg->inLanguage( 'fr' )->plain(), "inLanguage( 'fr' )" );
797 public function testInContentLanguageOverride() {
798 $this->overrideConfigValue( MainConfigNames
::ForceUIMsgAsContentMsg
, [ 'mainpage' ] );
799 $this->setUserLang( 'fr' );
801 // NOTE: make sure internal caching of the message text is reset appropriately.
802 // NOTE: wgForceUIMsgAsContentMsg forces the messages *current* language to be used.
803 $msg = wfMessage( 'mainpage' );
806 $msg->inContentLanguage()->plain(),
807 'inContentLanguage() with ForceUIMsg override enabled'
809 $this->assertSame( 'Main Page', $msg->inLanguage( 'en' )->plain(), "inLanguage( 'en' )" );
812 $msg->inContentLanguage()->plain(),
813 'inContentLanguage() with ForceUIMsg override enabled'
815 $this->assertSame( 'Hauptseite', $msg->inLanguage( 'de' )->plain(), "inLanguage( 'de' )" );
818 public function testInLanguageThrows() {
819 $this->expectException( ParameterTypeException
::class );
820 wfMessage( 'foo' )->inLanguage( 123 );
824 * @dataProvider provideSerializationRoundtrip
826 public function testSerialization( Message
$msg, $serialized, $parsed ) {
827 $this->assertSame( $serialized, serialize( $msg ) );
828 $this->assertSame( $parsed, $msg->parse() );
832 * @dataProvider provideSerializationRoundtrip
833 * @dataProvider provideSerializationLegacy
835 public function testUnserialization( Message
$msg, $serialized, $parsed ) {
836 $this->assertEquals( $msg, unserialize( $serialized ) );
837 $this->assertSame( $parsed, unserialize( $serialized )->parse() );
840 public function provideSerializationRoundtrip() {
841 // Test cases where we can test both serialization and unserialization.
842 // These really ought to use the MessageSerializationTestTrait, but
843 // doing so is complicated (T373719).
845 yield
"Serializing raw parameters" => [
846 ( new Message( 'parentheses' ) )->rawParams( '<a>foo</a>' ),
847 'O:25:"MediaWiki\Message\Message":7:{s:9:"interface";b:1;s:8:"language";N;s:3:"key";s:11:"parentheses";s:9:"keysToTry";a:1:{i:0;s:11:"parentheses";}s:10:"parameters";a:1:{i:0;O:29:"Wikimedia\Message\ScalarParam":2:{s:7:"' . chr( 0 ) . '*' . chr( 0 ) . 'type";s:3:"raw";s:8:"' . chr( 0 ) . '*' . chr( 0 ) . 'value";s:10:"<a>foo</a>";}}s:11:"useDatabase";b:1;s:10:"titlevalue";N;}',
851 yield
"Serializing message with a context page" => [
852 ( new Message( 'rawmessage', [ '{{PAGENAME}}' ] ) )->page( PageReferenceValue
::localReference( NS_MAIN
, 'Testing' ) ),
853 'O:25:"MediaWiki\Message\Message":7:{s:9:"interface";b:1;s:8:"language";N;s:3:"key";s:10:"rawmessage";s:9:"keysToTry";a:1:{i:0;s:10:"rawmessage";}s:10:"parameters";a:1:{i:0;s:12:"{{PAGENAME}}";}s:11:"useDatabase";b:1;s:10:"titlevalue";a:2:{i:0;i:0;i:1;s:7:"Testing";}}',
857 yield
"Serializing language" => [
858 ( new Message( 'mainpage' ) )->inLanguage( 'de' ),
859 'O:25:"MediaWiki\Message\Message":7:{s:9:"interface";b:0;s:8:"language";s:2:"de";s:3:"key";s:8:"mainpage";s:9:"keysToTry";a:1:{i:0;s:8:"mainpage";}s:10:"parameters";a:0:{}s:11:"useDatabase";b:1;s:10:"titlevalue";N;}',
864 public function provideSerializationLegacy() {
865 // Test cases where we can test only unserialization, because the serialization format changed.
867 yield
"MW 1.42: Magic arrays instead of MessageParam objects" => [
868 ( new Message( 'parentheses' ) )->rawParams( '<a>foo</a>' ),
869 'O:25:"MediaWiki\Message\Message":7:{s:9:"interface";b:1;s:8:"language";N;s:3:"key";s:11:"parentheses";s:9:"keysToTry";a:1:{i:0;s:11:"parentheses";}s:10:"parameters";a:1:{i:0;a:1:{s:3:"raw";s:10:"<a>foo</a>";}}s:11:"useDatabase";b:1;s:10:"titlevalue";N;}',
873 yield
"MW 1.41: Un-namespaced class" => [
874 new Message( 'mainpage' ),
875 'O:7:"Message":7:{s:9:"interface";b:1;s:8:"language";N;s:3:"key";s:8:"mainpage";s:9:"keysToTry";a:1:{i:0;s:8:"mainpage";}s:10:"parameters";a:0:{}s:11:"useDatabase";b:1;s:10:"titlevalue";N;}',
879 yield
"MW 1.34: 'titlestr' instead of 'titlevalue'" => [
880 ( new Message( 'rawmessage', [ '{{PAGENAME}}' ] ) )->title( Title
::newFromText( 'Testing' ) ),
881 'C:7:"Message":242:{a:8:{s:9:"interface";b:1;s:8:"language";b:0;s:3:"key";s:10:"rawmessage";s:9:"keysToTry";a:1:{i:0;s:10:"rawmessage";}s:10:"parameters";a:1:{i:0;s:12:"{{PAGENAME}}";}s:6:"format";s:5:"parse";s:11:"useDatabase";b:1;s:8:"titlestr";s:7:"Testing";}}',
887 * @dataProvider provideNewFromSpecifier
889 public function testNewFromSpecifier( $value, $expectedText ) {
890 $message = Message
::newFromSpecifier( $value );
891 $this->assertInstanceOf( Message
::class, $message );
892 if ( $value instanceof Message
) {
893 $this->assertInstanceOf( get_class( $value ), $message );
894 $this->assertEquals( $value, $message );
896 $this->assertSame( $expectedText, $message->text() );
899 public function provideNewFromSpecifier() {
900 $messageSpecifier = $this->getMockForAbstractClass( MessageSpecifier
::class );
901 $messageSpecifier->method( 'getKey' )->willReturn( 'mainpage' );
902 $messageSpecifier->method( 'getParams' )->willReturn( [] );
905 'string' => [ 'mainpage', 'Main Page' ],
906 'array' => [ [ 'new-messages', 'foo', 'bar' ], 'You have foo (bar).' ],
907 'Message' => [ new Message( 'new-messages', [ 'foo', 'bar' ] ), 'You have foo (bar).' ],
908 'RawMessage' => [ new RawMessage( 'foo ($1)', [ 'bar' ] ), 'foo (bar)' ],
909 'ApiMessage' => [ new ApiMessage( [ 'mainpage' ], 'code', [ 'data' ] ), 'Main Page' ],
910 'MessageSpecifier' => [ $messageSpecifier, 'Main Page' ],
911 'nested RawMessage' => [ [ new RawMessage( 'foo ($1)', [ 'bar' ] ) ], 'foo (bar)' ],