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' );
27 $this->resetServices();
30 function addDBDataOnce() {
31 $this->configureLanguages();
33 // Set up messages and fallbacks ab -> ru -> de
34 $this->makePage( 'FallbackLanguageTest-Full', 'ab' );
35 $this->makePage( 'FallbackLanguageTest-Full', 'ru' );
36 $this->makePage( 'FallbackLanguageTest-Full', 'de' );
38 // Fallbacks where ab does not exist
39 $this->makePage( 'FallbackLanguageTest-Partial', 'ru' );
40 $this->makePage( 'FallbackLanguageTest-Partial', 'de' );
42 // Fallback to the content language
43 $this->makePage( 'FallbackLanguageTest-ContLang', 'de' );
45 // Add customizations for an existing message.
46 $this->makePage( 'sunday', 'ru' );
48 // Full key tests -- always want russian
49 $this->makePage( 'MessageCacheTest-FullKeyTest', 'ab' );
50 $this->makePage( 'MessageCacheTest-FullKeyTest', 'ru' );
52 // In content language -- get base if no derivative
53 $this->makePage( 'FallbackLanguageTest-NoDervContLang', 'de', 'de/none' );
57 * Helper function for addDBData -- adds a simple page to the database
59 * @param string $title Title of page to be created
60 * @param string $lang Language and content of the created page
61 * @param string|null $content Content of the created page, or null for a generic string
65 protected function makePage( $title, $lang, $content = null ) {
66 if ( $content === null ) {
69 if ( $lang !== MediaWikiServices
::getInstance()->getContentLanguage()->getCode() ) {
70 $title = "$title/$lang";
73 $title = Title
::newFromText( $title, NS_MEDIAWIKI
);
74 $wikiPage = new WikiPage( $title );
75 $contentHandler = ContentHandler
::makeContent( $content, $title );
76 $status = $wikiPage->doEditContent( $contentHandler, "$lang translation test case" );
79 $this->assertTrue( $status->isOK(), 'Create page ' . $title->getPrefixedDBkey() );
80 return $status->value
['revision'];
84 * Test message fallbacks, T3495
86 * @dataProvider provideMessagesForFallback
88 public function testMessageFallbacks( $message, $lang, $expectedContent ) {
89 $result = MessageCache
::singleton()->get( $message, true, $lang );
90 $this->assertEquals( $expectedContent, $result, "Message fallback failed." );
93 function provideMessagesForFallback() {
95 [ 'FallbackLanguageTest-Full', 'ab', 'ab' ],
96 [ 'FallbackLanguageTest-Partial', 'ab', 'ru' ],
97 [ 'FallbackLanguageTest-ContLang', 'ab', 'de' ],
98 [ 'FallbackLanguageTest-None', 'ab', false ],
100 // Existing message with customizations on the fallbacks
101 [ 'sunday', 'ab', 'амҽыш' ],
104 [ 'FallbackLanguageTest-NoDervContLang', 'de', 'de/none' ],
105 // UI language different from content language should only use de/none as last option
106 [ 'FallbackLanguageTest-NoDervContLang', 'fit', 'de/none' ],
110 public function testReplaceMsg() {
111 $messageCache = MessageCache
::singleton();
113 $uckey = MediaWikiServices
::getInstance()->getContentLanguage()->ucfirst( $message );
114 $oldText = $messageCache->get( $message ); // "Ausführen"
116 $dbw = wfGetDB( DB_MASTER
);
117 $dbw->startAtomic( __METHOD__
); // simulate request and block deferred updates
118 $messageCache->replace( $uckey, 'Allez!' );
119 $this->assertEquals( 'Allez!',
120 $messageCache->getMsgFromNamespace( $uckey, 'de' ),
121 'Updates are reflected in-process immediately' );
122 $this->assertEquals( 'Allez!',
123 $messageCache->get( $message ),
124 'Updates are reflected in-process immediately' );
125 $this->makePage( 'Go', 'de', 'Race!' );
126 $dbw->endAtomic( __METHOD__
);
128 $this->assertEquals( 0,
129 DeferredUpdates
::pendingUpdatesCount(),
130 'Post-commit deferred update triggers a run of all updates' );
132 $this->assertEquals( 'Race!', $messageCache->get( $message ), 'Correct final contents' );
134 $this->makePage( 'Go', 'de', $oldText );
135 $messageCache->replace( $uckey, $oldText ); // deferred update runs immediately
136 $this->assertEquals( $oldText, $messageCache->get( $message ), 'Content restored' );
139 public function testReplaceCache() {
140 global $wgWANObjectCaches;
142 // We need a WAN cache for this.
143 $this->setMwGlobals( [
144 'wgMainWANCache' => 'hash',
145 'wgWANObjectCaches' => $wgWANObjectCaches +
[
147 'class' => WANObjectCache
::class,
153 $this->overrideMwServices();
155 $messageCache = MessageCache
::singleton();
156 $messageCache->enable();
159 $this->makePage( 'Key1', 'de', 'Value1' );
160 $this->assertEquals( 0,
161 DeferredUpdates
::pendingUpdatesCount(),
162 'Post-commit deferred update triggers a run of all updates' );
163 $this->assertEquals( 'Value1', $messageCache->get( 'Key1' ), 'Key1 was successfully edited' );
165 // Screw up the database so MessageCache::loadFromDB() will
166 // produce the wrong result for reloading Key1
168 'page', [ 'page_namespace' => NS_MEDIAWIKI
, 'page_title' => 'Key1' ], __METHOD__
171 // Populate the second key
172 $this->makePage( 'Key2', 'de', 'Value2' );
173 $this->assertEquals( 0,
174 DeferredUpdates
::pendingUpdatesCount(),
175 'Post-commit deferred update triggers a run of all updates' );
176 $this->assertEquals( 'Value2', $messageCache->get( 'Key2' ), 'Key2 was successfully edited' );
178 // Now test that the second edit didn't reload Key1
179 $this->assertEquals( 'Value1', $messageCache->get( 'Key1' ),
180 'Key1 wasn\'t reloaded by edit of Key2' );
184 * @dataProvider provideNormalizeKey
186 public function testNormalizeKey( $key, $expected ) {
187 $actual = MessageCache
::normalizeKey( $key );
188 $this->assertEquals( $expected, $actual );
191 public function provideNormalizeKey() {
197 [ 'Foo bar', 'foo_bar' ],
199 [ 'Ćab_e 3', 'ćab_e_3' ],
206 public function testNoDBAccessContentLanguage() {
207 global $wgContLanguageCode;
209 $dbr = wfGetDB( DB_REPLICA
);
211 MessageCache
::singleton()->getMsgFromNamespace( 'allpages', $wgContLanguageCode );
213 $this->assertEquals( 0, $dbr->trxLevel() );
214 $dbr->setFlag( DBO_TRX
, $dbr::REMEMBER_PRIOR
); // make queries trigger TRX
216 MessageCache
::singleton()->getMsgFromNamespace( 'go', $wgContLanguageCode );
218 $dbr->restoreFlags();
220 $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries (content language)" );
223 public function testNoDBAccessNonContentLanguage() {
224 $dbr = wfGetDB( DB_REPLICA
);
226 MessageCache
::singleton()->getMsgFromNamespace( 'allpages/nl', 'nl' );
228 $this->assertEquals( 0, $dbr->trxLevel() );
229 $dbr->setFlag( DBO_TRX
, $dbr::REMEMBER_PRIOR
); // make queries trigger TRX
231 MessageCache
::singleton()->getMsgFromNamespace( 'go/nl', 'nl' );
233 $dbr->restoreFlags();
235 $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries (non-content language)" );
239 * Regression test for T218918
241 public function testLoadFromDB_fetchLatestRevision() {
242 // Create three revisions of the same message page.
243 // Must be an existing message key.
245 $this->makePage( $key, 'de', 'Test eins' );
246 $this->makePage( $key, 'de', 'Test zwei' );
247 $r3 = $this->makePage( $key, 'de', 'Test drei' );
249 // Create an out-of-sequence revision by importing a
250 // revision with an old timestamp. Hacky.
251 $importRevision = new WikiRevision( new HashConfig() );
252 $importRevision->setTitle( $r3->getTitle() );
253 $importRevision->setComment( 'Imported edit' );
254 $importRevision->setTimestamp( '19991122001122' );
255 $importRevision->setText( 'IMPORTED OLD TEST' );
256 $importRevision->setUsername( 'ext>Alan Smithee' );
258 $importer = MediaWikiServices
::getInstance()->getWikiRevisionOldRevisionImporterNoUpdates();
259 $importer->import( $importRevision );
261 // Now, load the message from the wiki page
262 $messageCache = MessageCache
::singleton();
263 $messageCache->enable();
264 $messageCache = TestingAccessWrapper
::newFromObject( $messageCache );
266 $cache = $messageCache->loadFromDB( 'de' );
268 $this->assertArrayHasKey( $key, $cache );
270 // Text in the cache has an extra space in front!
271 $this->assertSame( ' ' . 'Test drei', $cache[$key] );