e57764306e347c44657ab82ac54799e4d2771576
[lhc/web/wiklou.git] / tests / phpunit / includes / resourceloader / MessageBlobStoreTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * @group ResourceLoader
7 * @covers MessageBlobStore
8 */
9 class MessageBlobStoreTest extends PHPUnit\Framework\TestCase {
10
11 use MediaWikiCoversValidator;
12 use PHPUnit4And6Compat;
13
14 protected function setUp() {
15 parent::setUp();
16 // MediaWiki's test wrapper sets $wgMainWANCache to CACHE_NONE.
17 // Use HashBagOStuff here so that we can observe caching.
18 $this->wanCache = new WANObjectCache( [
19 'cache' => new HashBagOStuff()
20 ] );
21
22 $this->clock = 1301655600.000;
23 $this->wanCache->setMockTime( $this->clock );
24 }
25
26 public function testBlobCreation() {
27 $module = $this->makeModule( [ 'mainpage' ] );
28 $rl = new ResourceLoader();
29 $rl->register( $module->getName(), $module );
30
31 $blobStore = $this->makeBlobStore( null, $rl );
32 $blob = $blobStore->getBlob( $module, 'en' );
33
34 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
35 }
36
37 public function testBlobCreation_unknownMessage() {
38 $module = $this->makeModule( [ 'i-dont-exist' ] );
39 $rl = new ResourceLoader();
40 $rl->register( $module->getName(), $module );
41 $blobStore = $this->makeBlobStore( null, $rl );
42
43 // Generating a blob should succeed without errors,
44 // even if a message is unknown.
45 $blob = $blobStore->getBlob( $module, 'en' );
46 $this->assertEquals( '{"i-dont-exist":"\u29fci-dont-exist\u29fd"}', $blob, 'Generated blob' );
47 }
48
49 public function testMessageCachingAndPurging() {
50 $module = $this->makeModule( [ 'example' ] );
51 $rl = new ResourceLoader();
52 $rl->register( $module->getName(), $module );
53 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
54
55 // Advance this new WANObjectCache instance to a normal state,
56 // by doing one "get" and letting its hold off period expire.
57 // Without this, the first real "get" would lazy-initialise the
58 // checkKey and thus reject the first "set".
59 $blobStore->getBlob( $module, 'en' );
60 $this->clock += 20;
61
62 // Arrange version 1 of a message
63 $blobStore->expects( $this->once() )
64 ->method( 'fetchMessage' )
65 ->will( $this->returnValue( 'First version' ) );
66
67 // Assert
68 $blob = $blobStore->getBlob( $module, 'en' );
69 $this->assertEquals( '{"example":"First version"}', $blob, 'Blob for v1' );
70
71 // Arrange version 2
72 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
73 $blobStore->expects( $this->once() )
74 ->method( 'fetchMessage' )
75 ->will( $this->returnValue( 'Second version' ) );
76 $this->clock += 20;
77
78 // Assert
79 // We do not validate whether a cached message is up-to-date.
80 // Instead, changes to messages will send us a purge.
81 // When cache is not purged or expired, it must be used.
82 $blob = $blobStore->getBlob( $module, 'en' );
83 $this->assertEquals( '{"example":"First version"}', $blob, 'Reuse cached v1 blob' );
84
85 // Purge cache
86 $blobStore->updateMessage( 'example' );
87 $this->clock += 20;
88
89 // Assert
90 $blob = $blobStore->getBlob( $module, 'en' );
91 $this->assertEquals( '{"example":"Second version"}', $blob, 'Updated blob for v2' );
92 }
93
94 public function testPurgeEverything() {
95 $module = $this->makeModule( [ 'example' ] );
96 $rl = new ResourceLoader();
97 $rl->register( $module->getName(), $module );
98 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
99 // Advance this new WANObjectCache instance to a normal state.
100 $blobStore->getBlob( $module, 'en' );
101 $this->clock += 20;
102
103 // Arrange version 1 and 2
104 $blobStore->expects( $this->exactly( 2 ) )
105 ->method( 'fetchMessage' )
106 ->will( $this->onConsecutiveCalls( 'First', 'Second' ) );
107
108 // Assert
109 $blob = $blobStore->getBlob( $module, 'en' );
110 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1' );
111
112 $this->clock += 20;
113
114 // Assert
115 $blob = $blobStore->getBlob( $module, 'en' );
116 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1 again' );
117
118 // Purge everything
119 $blobStore->clear();
120 $this->clock += 20;
121
122 // Assert
123 $blob = $blobStore->getBlob( $module, 'en' );
124 $this->assertEquals( '{"example":"Second"}', $blob, 'Blob for v2' );
125 }
126
127 public function testValidateAgainstModuleRegistry() {
128 // Arrange version 1 of a module
129 $module = $this->makeModule( [ 'foo' ] );
130 $rl = new ResourceLoader();
131 $rl->register( $module->getName(), $module );
132 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
133 $blobStore->expects( $this->once() )
134 ->method( 'fetchMessage' )
135 ->will( $this->returnValueMap( [
136 // message key, language code, message value
137 [ 'foo', 'en', 'Hello' ],
138 ] ) );
139
140 // Assert
141 $blob = $blobStore->getBlob( $module, 'en' );
142 $this->assertEquals( '{"foo":"Hello"}', $blob, 'Blob for v1' );
143
144 // Arrange version 2 of module
145 // While message values may be out of date, the set of messages returned
146 // must always match the set of message keys required by the module.
147 // We do not receive purges for this because no messages were changed.
148 $module = $this->makeModule( [ 'foo', 'bar' ] );
149 $rl = new ResourceLoader();
150 $rl->register( $module->getName(), $module );
151 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
152 $blobStore->expects( $this->exactly( 2 ) )
153 ->method( 'fetchMessage' )
154 ->will( $this->returnValueMap( [
155 // message key, language code, message value
156 [ 'foo', 'en', 'Hello' ],
157 [ 'bar', 'en', 'World' ],
158 ] ) );
159
160 // Assert
161 $blob = $blobStore->getBlob( $module, 'en' );
162 $this->assertEquals( '{"foo":"Hello","bar":"World"}', $blob, 'Blob for v2' );
163 }
164
165 public function testSetLoggedIsVoid() {
166 $blobStore = $this->makeBlobStore();
167 $this->assertSame( null, $blobStore->setLogger( new Psr\Log\NullLogger() ) );
168 }
169
170 private function makeBlobStore( $methods = null, $rl = null ) {
171 $blobStore = $this->getMockBuilder( MessageBlobStore::class )
172 ->setConstructorArgs( [ $rl ?? $this->createMock( ResourceLoader::class ) ] )
173 ->setMethods( $methods )
174 ->getMock();
175
176 $access = TestingAccessWrapper::newFromObject( $blobStore );
177 $access->wanCache = $this->wanCache;
178 return $blobStore;
179 }
180
181 private function makeModule( array $messages ) {
182 $module = new ResourceLoaderTestModule( [ 'messages' => $messages ] );
183 $module->setName( 'test.blobstore' );
184 return $module;
185 }
186 }