Merge "Fix 'Tags' padding to keep it farther from the edge and document the source...
[lhc/web/wiklou.git] / tests / phpunit / includes / resourceloader / MessageBlobStoreTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * @group Cache
7 * @covers MessageBlobStore
8 */
9 class MessageBlobStoreTest extends PHPUnit\Framework\TestCase {
10
11 use MediaWikiCoversValidator;
12
13 protected function setUp() {
14 parent::setUp();
15 // MediaWiki tests defaults $wgMainWANCache to CACHE_NONE.
16 // Use hash instead so that caching is observed
17 $this->wanCache = $this->getMockBuilder( WANObjectCache::class )
18 ->setConstructorArgs( [ [
19 'cache' => new HashBagOStuff(),
20 'pool' => 'test',
21 'relayer' => new EventRelayerNull( [] )
22 ] ] )
23 ->setMethods( [ 'makePurgeValue' ] )
24 ->getMock();
25
26 $this->wanCache->expects( $this->any() )
27 ->method( 'makePurgeValue' )
28 ->will( $this->returnCallback( function ( $timestamp, $holdoff ) {
29 // Disable holdoff as it messes with testing. Aside from a 0-second holdoff,
30 // make sure that "time" passes between getMulti() check init and the set()
31 // in recacheMessageBlob(). This especially matters for Windows clocks.
32 $ts = (float)$timestamp - 0.0001;
33
34 return WANObjectCache::PURGE_VAL_PREFIX . $ts . ':0';
35 } ) );
36 }
37
38 protected function makeBlobStore( $methods = null, $rl = null ) {
39 $blobStore = $this->getMockBuilder( MessageBlobStore::class )
40 ->setConstructorArgs( [ $rl ] )
41 ->setMethods( $methods )
42 ->getMock();
43
44 $access = TestingAccessWrapper::newFromObject( $blobStore );
45 $access->wanCache = $this->wanCache;
46 return $blobStore;
47 }
48
49 protected function makeModule( array $messages ) {
50 $module = new ResourceLoaderTestModule( [ 'messages' => $messages ] );
51 $module->setName( 'test.blobstore' );
52 return $module;
53 }
54
55 /** @covers MessageBlobStore::setLogger */
56 public function testSetLogger() {
57 $blobStore = $this->makeBlobStore();
58 $this->assertSame( null, $blobStore->setLogger( new Psr\Log\NullLogger() ) );
59 }
60
61 /** @covers MessageBlobStore::getResourceLoader */
62 public function testGetResourceLoader() {
63 // Call protected method
64 $blobStore = TestingAccessWrapper::newFromObject( $this->makeBlobStore() );
65 $this->assertInstanceOf(
66 ResourceLoader::class,
67 $blobStore->getResourceLoader()
68 );
69 }
70
71 /** @covers MessageBlobStore::fetchMessage */
72 public function testFetchMessage() {
73 $module = $this->makeModule( [ 'mainpage' ] );
74 $rl = new ResourceLoader();
75 $rl->register( $module->getName(), $module );
76
77 $blobStore = $this->makeBlobStore( null, $rl );
78 $blob = $blobStore->getBlob( $module, 'en' );
79
80 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
81 }
82
83 /** @covers MessageBlobStore::fetchMessage */
84 public function testFetchMessageFail() {
85 $module = $this->makeModule( [ 'i-dont-exist' ] );
86 $rl = new ResourceLoader();
87 $rl->register( $module->getName(), $module );
88
89 $blobStore = $this->makeBlobStore( null, $rl );
90 $blob = $blobStore->getBlob( $module, 'en' );
91
92 $this->assertEquals( '{"i-dont-exist":"\u29fci-dont-exist\u29fd"}', $blob, 'Generated blob' );
93 }
94
95 public function testGetBlob() {
96 $module = $this->makeModule( [ 'foo' ] );
97 $rl = new ResourceLoader();
98 $rl->register( $module->getName(), $module );
99
100 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
101 $blobStore->expects( $this->once() )
102 ->method( 'fetchMessage' )
103 ->will( $this->returnValue( 'Example' ) );
104
105 $blob = $blobStore->getBlob( $module, 'en' );
106
107 $this->assertEquals( '{"foo":"Example"}', $blob, 'Generated blob' );
108 }
109
110 public function testGetBlobCached() {
111 $module = $this->makeModule( [ 'example' ] );
112 $rl = new ResourceLoader();
113 $rl->register( $module->getName(), $module );
114
115 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
116 $blobStore->expects( $this->once() )
117 ->method( 'fetchMessage' )
118 ->will( $this->returnValue( 'First' ) );
119
120 $module = $this->makeModule( [ 'example' ] );
121 $blob = $blobStore->getBlob( $module, 'en' );
122 $this->assertEquals( '{"example":"First"}', $blob, 'Generated blob' );
123
124 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
125 $blobStore->expects( $this->never() )
126 ->method( 'fetchMessage' )
127 ->will( $this->returnValue( 'Second' ) );
128
129 $module = $this->makeModule( [ 'example' ] );
130 $blob = $blobStore->getBlob( $module, 'en' );
131 $this->assertEquals( '{"example":"First"}', $blob, 'Cache hit' );
132 }
133
134 public function testUpdateMessage() {
135 $module = $this->makeModule( [ 'example' ] );
136 $rl = new ResourceLoader();
137 $rl->register( $module->getName(), $module );
138 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
139 $blobStore->expects( $this->once() )
140 ->method( 'fetchMessage' )
141 ->will( $this->returnValue( 'First' ) );
142
143 $blob = $blobStore->getBlob( $module, 'en' );
144 $this->assertEquals( '{"example":"First"}', $blob, 'Generated blob' );
145
146 $blobStore->updateMessage( 'example' );
147
148 $module = $this->makeModule( [ 'example' ] );
149 $rl = new ResourceLoader();
150 $rl->register( $module->getName(), $module );
151 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
152 $blobStore->expects( $this->once() )
153 ->method( 'fetchMessage' )
154 ->will( $this->returnValue( 'Second' ) );
155
156 $blob = $blobStore->getBlob( $module, 'en' );
157 $this->assertEquals( '{"example":"Second"}', $blob, 'Updated blob' );
158 }
159
160 public function testValidation() {
161 $module = $this->makeModule( [ 'foo' ] );
162 $rl = new ResourceLoader();
163 $rl->register( $module->getName(), $module );
164
165 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
166 $blobStore->expects( $this->once() )
167 ->method( 'fetchMessage' )
168 ->will( $this->returnValueMap( [
169 [ 'foo', 'en', 'Hello' ],
170 ] ) );
171
172 $blob = $blobStore->getBlob( $module, 'en' );
173 $this->assertEquals( '{"foo":"Hello"}', $blob, 'Generated blob' );
174
175 // Now, imagine a change to the module is deployed. The module now contains
176 // message 'foo' and 'bar'. While updateMessage() was not called (since no
177 // message values were changed) it should detect the change in list of
178 // message keys.
179 $module = $this->makeModule( [ 'foo', 'bar' ] );
180 $rl = new ResourceLoader();
181 $rl->register( $module->getName(), $module );
182
183 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
184 $blobStore->expects( $this->exactly( 2 ) )
185 ->method( 'fetchMessage' )
186 ->will( $this->returnValueMap( [
187 [ 'foo', 'en', 'Hello' ],
188 [ 'bar', 'en', 'World' ],
189 ] ) );
190
191 $blob = $blobStore->getBlob( $module, 'en' );
192 $this->assertEquals( '{"foo":"Hello","bar":"World"}', $blob, 'Updated blob' );
193 }
194
195 public function testClear() {
196 $module = $this->makeModule( [ 'example' ] );
197 $rl = new ResourceLoader();
198 $rl->register( $module->getName(), $module );
199 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
200 $blobStore->expects( $this->exactly( 2 ) )
201 ->method( 'fetchMessage' )
202 ->will( $this->onConsecutiveCalls( 'First', 'Second' ) );
203
204 $now = microtime( true );
205 $this->wanCache->setMockTime( $now );
206
207 $blob = $blobStore->getBlob( $module, 'en' );
208 $this->assertEquals( '{"example":"First"}', $blob, 'Generated blob' );
209
210 $blob = $blobStore->getBlob( $module, 'en' );
211 $this->assertEquals( '{"example":"First"}', $blob, 'Cache-hit' );
212
213 $now += 1;
214 $blobStore->clear();
215
216 $blob = $blobStore->getBlob( $module, 'en' );
217 $this->assertEquals( '{"example":"Second"}', $blob, 'Updated blob' );
218 }
219 }