3 namespace MediaWiki\Tests\Storage
;
5 use InvalidArgumentException
;
7 use MediaWiki\MediaWikiServices
;
8 use MediaWiki\Storage\BlobAccessException
;
9 use MediaWiki\Storage\SqlBlobStore
;
10 use MediaWikiTestCase
;
15 * @covers \MediaWiki\Storage\SqlBlobStore
18 class SqlBlobStoreTest
extends MediaWikiTestCase
{
21 * @return SqlBlobStore
23 public function getBlobStore( $legacyEncoding = false, $compressRevisions = false ) {
24 $services = MediaWikiServices
::getInstance();
26 $store = new SqlBlobStore(
27 $services->getDBLoadBalancer(),
28 $services->getExternalStoreAccess(),
29 $services->getMainWANObjectCache()
32 if ( $compressRevisions ) {
33 $store->setCompressBlobs( $compressRevisions );
35 if ( $legacyEncoding ) {
36 $store->setLegacyEncoding( $legacyEncoding, Language
::factory( 'en' ) );
43 * @covers \MediaWiki\Storage\SqlBlobStore::getCompressBlobs()
44 * @covers \MediaWiki\Storage\SqlBlobStore::setCompressBlobs()
46 public function testGetSetCompressRevisions() {
47 $store = $this->getBlobStore();
48 $this->assertFalse( $store->getCompressBlobs() );
49 $store->setCompressBlobs( true );
50 $this->assertTrue( $store->getCompressBlobs() );
54 * @covers \MediaWiki\Storage\SqlBlobStore::getLegacyEncoding()
55 * @covers \MediaWiki\Storage\SqlBlobStore::getLegacyEncodingConversionLang()
56 * @covers \MediaWiki\Storage\SqlBlobStore::setLegacyEncoding()
58 public function testGetSetLegacyEncoding() {
59 $store = $this->getBlobStore();
60 $this->assertFalse( $store->getLegacyEncoding() );
61 $this->assertNull( $store->getLegacyEncodingConversionLang() );
62 $en = Language
::factory( 'en' );
63 $store->setLegacyEncoding( 'foo', $en );
64 $this->assertSame( 'foo', $store->getLegacyEncoding() );
65 $this->assertSame( $en, $store->getLegacyEncodingConversionLang() );
69 * @covers \MediaWiki\Storage\SqlBlobStore::getCacheExpiry()
70 * @covers \MediaWiki\Storage\SqlBlobStore::setCacheExpiry()
72 public function testGetSetCacheExpiry() {
73 $store = $this->getBlobStore();
74 $this->assertSame( 604800, $store->getCacheExpiry() );
75 $store->setCacheExpiry( 12 );
76 $this->assertSame( 12, $store->getCacheExpiry() );
80 * @covers \MediaWiki\Storage\SqlBlobStore::getUseExternalStore()
81 * @covers \MediaWiki\Storage\SqlBlobStore::setUseExternalStore()
83 public function testGetSetUseExternalStore() {
84 $store = $this->getBlobStore();
85 $this->assertFalse( $store->getUseExternalStore() );
86 $store->setUseExternalStore( true );
87 $this->assertTrue( $store->getUseExternalStore() );
90 public function provideDecompress() {
91 yield
'(no legacy encoding), empty in empty out' => [ false, '', [], '' ];
92 yield
'(no legacy encoding), empty in empty out' => [ false, 'A', [], 'A' ];
93 yield
'(no legacy encoding), error flag -> false' => [ false, 'X', [ 'error' ], false ];
94 yield
'(no legacy encoding), string in with gzip flag returns string' => [
95 // gzip string below generated with gzdeflate( 'AAAABBAAA' )
96 false, "sttttr\002\022\000", [ 'gzip' ], 'AAAABBAAA',
98 yield
'(no legacy encoding), string in with object flag returns false' => [
99 // gzip string below generated with serialize( 'JOJO' )
100 false, "s:4:\"JOJO\";", [ 'object' ], false,
102 yield
'(no legacy encoding), serialized object in with object flag returns string' => [
104 // Using a TitleValue object as it has a getText method (which is needed)
105 serialize( new TitleValue( 0, 'HHJJDDFF' ) ),
109 yield
'(no legacy encoding), serialized object in with object & gzip flag returns string' => [
111 // Using a TitleValue object as it has a getText method (which is needed)
112 gzdeflate( serialize( new TitleValue( 0, '8219JJJ840' ) ) ),
113 [ 'object', 'gzip' ],
116 yield
'(ISO-8859-1 encoding), string in string out' => [
118 iconv( 'utf-8', 'ISO-8859-1', "1®Àþ1" ),
122 yield
'(ISO-8859-1 encoding), serialized object in with gzip flags returns string' => [
124 gzdeflate( iconv( 'utf-8', 'ISO-8859-1', "4®Àþ4" ) ),
128 yield
'(ISO-8859-1 encoding), serialized object in with object flags returns string' => [
130 serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "3®Àþ3" ) ) ),
134 yield
'(ISO-8859-1 encoding), serialized object in with object & gzip flags returns string' => [
136 gzdeflate( serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "2®Àþ2" ) ) ) ),
137 [ 'gzip', 'object' ],
140 yield
'T184749 (windows-1252 encoding), string in string out' => [
142 iconv( 'utf-8', 'windows-1252', "sammansättningar" ),
146 yield
'T184749 (windows-1252 encoding), string in string out with gzip' => [
148 gzdeflate( iconv( 'utf-8', 'windows-1252', "sammansättningar" ) ),
155 * @dataProvider provideDecompress
156 * @covers \MediaWiki\Storage\SqlBlobStore::decompressData
158 * @param string|bool $legacyEncoding
160 * @param array $flags
161 * @param mixed $expected
163 public function testDecompressData( $legacyEncoding, $data, $flags, $expected ) {
164 $store = $this->getBlobStore( $legacyEncoding );
167 $store->decompressData( $data, $flags )
172 * @covers \MediaWiki\Storage\SqlBlobStore::decompressData
174 public function testDecompressData_InvalidArgumentException() {
175 $store = $this->getBlobStore();
177 $this->setExpectedException( InvalidArgumentException
::class );
178 $store->decompressData( false, [] );
182 * @covers \MediaWiki\Storage\SqlBlobStore::compressData
184 public function testCompressRevisionTextUtf8() {
185 $store = $this->getBlobStore();
187 $row->old_text
= "Wiki est l'\xc3\xa9cole superieur !";
188 $row->old_flags
= $store->compressData( $row->old_text
);
189 $this->assertTrue( strpos( $row->old_flags
, 'utf-8' ) !== false,
190 "Flags should contain 'utf-8'" );
191 $this->assertFalse( strpos( $row->old_flags
, 'gzip' ) !== false,
192 "Flags should not contain 'gzip'" );
193 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
194 $row->old_text
, "Direct check" );
198 * @covers \MediaWiki\Storage\SqlBlobStore::compressData
200 public function testCompressRevisionTextUtf8Gzip() {
201 $store = $this->getBlobStore( false, true );
202 $this->checkPHPExtension( 'zlib' );
205 $row->old_text
= "Wiki est l'\xc3\xa9cole superieur !";
206 $row->old_flags
= $store->compressData( $row->old_text
);
207 $this->assertTrue( strpos( $row->old_flags
, 'utf-8' ) !== false,
208 "Flags should contain 'utf-8'" );
209 $this->assertTrue( strpos( $row->old_flags
, 'gzip' ) !== false,
210 "Flags should contain 'gzip'" );
211 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
212 gzinflate( $row->old_text
), "Direct check" );
215 public function provideBlobs() {
217 yield
[ 'someText' ];
218 yield
[ "sammansättningar" ];
222 * @param string $blob
223 * @dataProvider provideBlobs
224 * @covers \MediaWiki\Storage\SqlBlobStore::storeBlob
225 * @covers \MediaWiki\Storage\SqlBlobStore::getBlob
227 public function testSimpleStoreGetBlobSimpleRoundtrip( $blob ) {
228 $store = $this->getBlobStore();
229 $address = $store->storeBlob( $blob );
230 $this->assertSame( $blob, $store->getBlob( $address ) );
234 * @covers \MediaWiki\Storage\SqlBlobStore::storeBlob
235 * @covers \MediaWiki\Storage\SqlBlobStore::getBlobBatch
237 public function testSimpleStorageGetBlobBatchSimpleEmpty() {
238 $store = $this->getBlobStore();
239 $this->assertArrayEquals(
241 $store->getBlobBatch( [] )->getValue()
246 * @param string $blob
247 * @dataProvider provideBlobs
248 * @covers \MediaWiki\Storage\SqlBlobStore::storeBlob
249 * @covers \MediaWiki\Storage\SqlBlobStore::getBlobBatch
251 public function testSimpleStorageGetBlobBatchSimpleRoundtrip( $blob ) {
252 $store = $this->getBlobStore();
254 $store->storeBlob( $blob ),
255 $store->storeBlob( $blob . '1' )
257 $this->assertArrayEquals(
258 array_combine( $addresses, [ $blob, $blob . '1' ] ),
259 $store->getBlobBatch( $addresses )->getValue()
264 * @covers \MediaWiki\Storage\SqlBlobStore::getBlob
266 public function testSimpleStorageNonExistentBlob() {
267 $this->setExpectedException( BlobAccessException
::class );
268 $store = $this->getBlobStore();
269 $store->getBlob( 'tt:this_will_not_exist' );
273 * @covers \MediaWiki\Storage\SqlBlobStore::getBlobBatch
275 public function testSimpleStorageNonExistentBlobBatch() {
276 $store = $this->getBlobStore();
277 $result = $store->getBlobBatch( [ 'tt:this_will_not_exist', 'tt:1000', 'bla:1001' ] );
280 'tt:this_will_not_exist' => null,
289 'message' => 'internalerror',
291 'Bad blob address: tt:this_will_not_exist'
296 'message' => 'internalerror',
298 'Unknown blob address schema: bla'
303 'message' => 'internalerror',
305 'Unable to fetch blob at tt:1000'
308 ], $result->getErrors() );
312 * @covers \MediaWiki\Storage\SqlBlobStore::getBlobBatch
314 public function testSimpleStoragePartialNonExistentBlobBatch() {
315 $store = $this->getBlobStore();
316 $address = $store->storeBlob( 'test_data' );
317 $result = $store->getBlobBatch( [ $address, 'tt:this_will_not_exist_too' ] );
320 $address => 'test_data',
321 'tt:this_will_not_exist_too' => null
328 'message' => 'internalerror',
330 'Bad blob address: tt:this_will_not_exist_too'
333 ], $result->getErrors() );
337 * @dataProvider provideBlobs
338 * @covers \MediaWiki\Storage\SqlBlobStore::storeBlob
339 * @covers \MediaWiki\Storage\SqlBlobStore::getBlob
341 public function testSimpleStoreGetBlobSimpleRoundtripWindowsLegacyEncoding( $blob ) {
342 $store = $this->getBlobStore( 'windows-1252' );
343 $address = $store->storeBlob( $blob );
344 $this->assertSame( $blob, $store->getBlob( $address ) );
348 * @dataProvider provideBlobs
349 * @covers \MediaWiki\Storage\SqlBlobStore::storeBlob
350 * @covers \MediaWiki\Storage\SqlBlobStore::getBlob
352 public function testSimpleStoreGetBlobSimpleRoundtripWindowsLegacyEncodingGzip( $blob ) {
353 // FIXME: fails under postgres
354 $this->markTestSkippedIfDbType( 'postgres' );
355 $store = $this->getBlobStore( 'windows-1252', true );
356 $address = $store->storeBlob( $blob );
357 $this->assertSame( $blob, $store->getBlob( $address ) );
360 public function provideGetTextIdFromAddress() {
361 yield
[ 'tt:17', 17 ];
362 yield
[ 'xy:17', null ];
363 yield
[ 'xy:xyzzy', null ];
367 * @dataProvider provideGetTextIdFromAddress
369 public function testGetTextIdFromAddress( $address, $textId ) {
370 $store = $this->getBlobStore();
371 $this->assertSame( $textId, $store->getTextIdFromAddress( $address ) );
374 public function provideGetTextIdFromAddressInvalidArgumentException() {
384 * @dataProvider provideGetTextIdFromAddressInvalidArgumentException
386 public function testGetTextIdFromAddressInvalidArgumentException( $address ) {
387 $this->setExpectedException( InvalidArgumentException
::class );
388 $store = $this->getBlobStore();
389 $store->getTextIdFromAddress( $address );
392 public function testMakeAddressFromTextId() {
393 $this->assertSame( 'tt:17', SqlBlobStore
::makeAddressFromTextId( 17 ) );