3 use MediaWiki\MediaWikiServices
;
4 use Wikimedia\TestingAccessWrapper
;
11 class MessageCacheTest
extends MediaWikiLangTestCase
{
13 protected function setUp() {
15 $this->configureLanguages();
16 MessageCache
::singleton()->enable();
20 * Helper function -- setup site language for testing
22 protected function configureLanguages() {
23 // for the test, we need the content language to be anything but English,
24 // let's choose e.g. German (de)
25 $this->setUserLang( 'de' );
26 $this->setContentLang( 'de' );
29 function addDBDataOnce() {
30 $this->configureLanguages();
32 // Set up messages and fallbacks ab -> ru -> de
33 $this->makePage( 'FallbackLanguageTest-Full', 'ab' );
34 $this->makePage( 'FallbackLanguageTest-Full', 'ru' );
35 $this->makePage( 'FallbackLanguageTest-Full', 'de' );
37 // Fallbacks where ab does not exist
38 $this->makePage( 'FallbackLanguageTest-Partial', 'ru' );
39 $this->makePage( 'FallbackLanguageTest-Partial', 'de' );
41 // Fallback to the content language
42 $this->makePage( 'FallbackLanguageTest-ContLang', 'de' );
44 // Add customizations for an existing message.
45 $this->makePage( 'sunday', 'ru' );
47 // Full key tests -- always want russian
48 $this->makePage( 'MessageCacheTest-FullKeyTest', 'ab' );
49 $this->makePage( 'MessageCacheTest-FullKeyTest', 'ru' );
51 // In content language -- get base if no derivative
52 $this->makePage( 'FallbackLanguageTest-NoDervContLang', 'de', 'de/none' );
56 * Helper function for addDBData -- adds a simple page to the database
58 * @param string $title Title of page to be created
59 * @param string $lang Language and content of the created page
60 * @param string|null $content Content of the created page, or null for a generic string
64 protected function makePage( $title, $lang, $content = null ) {
65 if ( $content === null ) {
68 if ( $lang !== MediaWikiServices
::getInstance()->getContentLanguage()->getCode() ) {
69 $title = "$title/$lang";
72 $title = Title
::newFromText( $title, NS_MEDIAWIKI
);
73 $wikiPage = new WikiPage( $title );
74 $contentHandler = ContentHandler
::makeContent( $content, $title );
75 $status = $wikiPage->doEditContent( $contentHandler, "$lang translation test case" );
78 $this->assertTrue( $status->isOK(), 'Create page ' . $title->getPrefixedDBkey() );
79 return $status->value
['revision'];
83 * Test message fallbacks, T3495
85 * @dataProvider provideMessagesForFallback
87 public function testMessageFallbacks( $message, $lang, $expectedContent ) {
88 $result = MessageCache
::singleton()->get( $message, true, $lang );
89 $this->assertEquals( $expectedContent, $result, "Message fallback failed." );
92 function provideMessagesForFallback() {
94 [ 'FallbackLanguageTest-Full', 'ab', 'ab' ],
95 [ 'FallbackLanguageTest-Partial', 'ab', 'ru' ],
96 [ 'FallbackLanguageTest-ContLang', 'ab', 'de' ],
97 [ 'FallbackLanguageTest-None', 'ab', false ],
99 // Existing message with customizations on the fallbacks
100 [ 'sunday', 'ab', 'амҽыш' ],
103 [ 'FallbackLanguageTest-NoDervContLang', 'de', 'de/none' ],
104 // UI language different from content language should only use de/none as last option
105 [ 'FallbackLanguageTest-NoDervContLang', 'fit', 'de/none' ],
109 public function testReplaceMsg() {
110 $messageCache = MessageCache
::singleton();
112 $uckey = MediaWikiServices
::getInstance()->getContentLanguage()->ucfirst( $message );
113 $oldText = $messageCache->get( $message ); // "Ausführen"
115 $dbw = wfGetDB( DB_MASTER
);
116 $dbw->startAtomic( __METHOD__
); // simulate request and block deferred updates
117 $messageCache->replace( $uckey, 'Allez!' );
118 $this->assertEquals( 'Allez!',
119 $messageCache->getMsgFromNamespace( $uckey, 'de' ),
120 'Updates are reflected in-process immediately' );
121 $this->assertEquals( 'Allez!',
122 $messageCache->get( $message ),
123 'Updates are reflected in-process immediately' );
124 $this->makePage( 'Go', 'de', 'Race!' );
125 $dbw->endAtomic( __METHOD__
);
127 $this->assertEquals( 0,
128 DeferredUpdates
::pendingUpdatesCount(),
129 'Post-commit deferred update triggers a run of all updates' );
131 $this->assertEquals( 'Race!', $messageCache->get( $message ), 'Correct final contents' );
133 $this->makePage( 'Go', 'de', $oldText );
134 $messageCache->replace( $uckey, $oldText ); // deferred update runs immediately
135 $this->assertEquals( $oldText, $messageCache->get( $message ), 'Content restored' );
138 public function testReplaceCache() {
139 global $wgWANObjectCaches;
141 // We need a WAN cache for this.
142 $this->setMwGlobals( [
143 'wgMainWANCache' => 'hash',
144 'wgWANObjectCaches' => $wgWANObjectCaches +
[
146 'class' => WANObjectCache
::class,
153 $messageCache = MessageCache
::singleton();
154 $messageCache->enable();
157 $this->makePage( 'Key1', 'de', 'Value1' );
158 $this->assertEquals( 0,
159 DeferredUpdates
::pendingUpdatesCount(),
160 'Post-commit deferred update triggers a run of all updates' );
161 $this->assertEquals( 'Value1', $messageCache->get( 'Key1' ), 'Key1 was successfully edited' );
163 // Screw up the database so MessageCache::loadFromDB() will
164 // produce the wrong result for reloading Key1
166 'page', [ 'page_namespace' => NS_MEDIAWIKI
, 'page_title' => 'Key1' ], __METHOD__
169 // Populate the second key
170 $this->makePage( 'Key2', 'de', 'Value2' );
171 $this->assertEquals( 0,
172 DeferredUpdates
::pendingUpdatesCount(),
173 'Post-commit deferred update triggers a run of all updates' );
174 $this->assertEquals( 'Value2', $messageCache->get( 'Key2' ), 'Key2 was successfully edited' );
176 // Now test that the second edit didn't reload Key1
177 $this->assertEquals( 'Value1', $messageCache->get( 'Key1' ),
178 'Key1 wasn\'t reloaded by edit of Key2' );
182 * @dataProvider provideNormalizeKey
184 public function testNormalizeKey( $key, $expected ) {
185 $actual = MessageCache
::normalizeKey( $key );
186 $this->assertEquals( $expected, $actual );
189 public function provideNormalizeKey() {
195 [ 'Foo bar', 'foo_bar' ],
197 [ 'Ćab_e 3', 'ćab_e_3' ],
204 public function testNoDBAccessContentLanguage() {
205 global $wgContLanguageCode;
207 $dbr = wfGetDB( DB_REPLICA
);
209 MessageCache
::singleton()->getMsgFromNamespace( 'allpages', $wgContLanguageCode );
211 $this->assertEquals( 0, $dbr->trxLevel() );
212 $dbr->setFlag( DBO_TRX
, $dbr::REMEMBER_PRIOR
); // make queries trigger TRX
214 MessageCache
::singleton()->getMsgFromNamespace( 'go', $wgContLanguageCode );
216 $dbr->restoreFlags();
218 $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries (content language)" );
221 public function testNoDBAccessNonContentLanguage() {
222 $dbr = wfGetDB( DB_REPLICA
);
224 MessageCache
::singleton()->getMsgFromNamespace( 'allpages/nl', 'nl' );
226 $this->assertEquals( 0, $dbr->trxLevel() );
227 $dbr->setFlag( DBO_TRX
, $dbr::REMEMBER_PRIOR
); // make queries trigger TRX
229 MessageCache
::singleton()->getMsgFromNamespace( 'go/nl', 'nl' );
231 $dbr->restoreFlags();
233 $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries (non-content language)" );
237 * Regression test for T218918
239 public function testLoadFromDB_fetchLatestRevision() {
240 // Create three revisions of the same message page.
241 // Must be an existing message key.
243 $this->makePage( $key, 'de', 'Test eins' );
244 $this->makePage( $key, 'de', 'Test zwei' );
245 $r3 = $this->makePage( $key, 'de', 'Test drei' );
247 // Create an out-of-sequence revision by importing a
248 // revision with an old timestamp. Hacky.
249 $importRevision = new WikiRevision( new HashConfig() );
250 $importRevision->setTitle( $r3->getTitle() );
251 $importRevision->setComment( 'Imported edit' );
252 $importRevision->setTimestamp( '19991122001122' );
253 $importRevision->setText( 'IMPORTED OLD TEST' );
254 $importRevision->setUsername( 'ext>Alan Smithee' );
256 $importer = MediaWikiServices
::getInstance()->getWikiRevisionOldRevisionImporterNoUpdates();
257 $importer->import( $importRevision );
259 // Now, load the message from the wiki page
260 $messageCache = MessageCache
::singleton();
261 $messageCache->enable();
262 $messageCache = TestingAccessWrapper
::newFromObject( $messageCache );
264 $cache = $messageCache->loadFromDB( 'de' );
266 $this->assertArrayHasKey( $key, $cache );
268 // Text in the cache has an extra space in front!
269 $this->assertSame( ' ' . 'Test drei', $cache[$key] );