diff options
Diffstat (limited to 'www/wiki/tests/phpunit/includes/objectcache')
4 files changed, 422 insertions, 0 deletions
diff --git a/www/wiki/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php b/www/wiki/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php new file mode 100644 index 00000000..432754b6 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php @@ -0,0 +1,107 @@ +<?php +/** + * @group BagOStuff + */ +class MemcachedBagOStuffTest extends MediaWikiTestCase { + /** @var MemcachedBagOStuff */ + private $cache; + + protected function setUp() { + parent::setUp(); + $this->cache = new MemcachedBagOStuff( [ 'keyspace' => 'test' ] ); + } + + /** + * @covers MemcachedBagOStuff::makeKey + */ + public function testKeyNormalization() { + $this->assertEquals( + 'test:vanilla', + $this->cache->makeKey( 'vanilla' ) + ); + + $this->assertEquals( + 'test:punctuation_marks_are_ok:!@$^&*()', + $this->cache->makeKey( 'punctuation_marks_are_ok', '!@$^&*()' ) + ); + + $this->assertEquals( + 'test:but_spaces:hashes%23:and%0Anewlines:are_not', + $this->cache->makeKey( 'but spaces', 'hashes#', "and\nnewlines", 'are_not' ) + ); + + $this->assertEquals( + 'test:this:key:contains:%F0%9D%95%9E%F0%9D%95%A6%F0%9D%95%9D%F0%9D%95%A5%F0%9' . + 'D%95%9A%F0%9D%95%93%F0%9D%95%AA%F0%9D%95%A5%F0%9D%95%96:characters', + $this->cache->makeKey( 'this', 'key', 'contains', '𝕞𝕦𝕝𝕥𝕚𝕓𝕪𝕥𝕖', 'characters' ) + ); + + $this->assertEquals( + 'test:this:key:contains:#c118f92685a635cb843039de50014c9c', + $this->cache->makeKey( 'this', 'key', 'contains', '𝕥𝕠𝕠 𝕞𝕒𝕟𝕪 𝕞𝕦𝕝𝕥𝕚𝕓𝕪𝕥𝕖 𝕔𝕙𝕒𝕣𝕒𝕔𝕥𝕖𝕣𝕤' ) + ); + + $this->assertEquals( + 'test:BagOStuff-long-key:##dc89dcb43b28614da27660240af478b5', + $this->cache->makeKey( '𝕖𝕧𝕖𝕟', '𝕚𝕗', '𝕨𝕖', '𝕄𝔻𝟝', '𝕖𝕒𝕔𝕙', + '𝕒𝕣𝕘𝕦𝕞𝕖𝕟𝕥', '𝕥𝕙𝕚𝕤', '𝕜𝕖𝕪', '𝕨𝕠𝕦𝕝𝕕', '𝕤𝕥𝕚𝕝𝕝', '𝕓𝕖', '𝕥𝕠𝕠', '𝕝𝕠𝕟𝕘' ) + ); + + $this->assertEquals( + 'test:%23%235820ad1d105aa4dc698585c39df73e19', + $this->cache->makeKey( '##5820ad1d105aa4dc698585c39df73e19' ) + ); + + $this->assertEquals( + 'test:percent_is_escaped:!@$%25^&*()', + $this->cache->makeKey( 'percent_is_escaped', '!@$%^&*()' ) + ); + + $this->assertEquals( + 'test:colon_is_escaped:!@$%3A^&*()', + $this->cache->makeKey( 'colon_is_escaped', '!@$:^&*()' ) + ); + + $this->assertEquals( + 'test:long_key_part_hashed:#0244f7b1811d982dd932dd7de01465ac', + $this->cache->makeKey( 'long_key_part_hashed', str_repeat( 'y', 500 ) ) + ); + } + + /** + * @dataProvider validKeyProvider + * @covers MemcachedBagOStuff::validateKeyEncoding + */ + public function testValidateKeyEncoding( $key ) { + $this->assertSame( $key, $this->cache->validateKeyEncoding( $key ) ); + } + + public function validKeyProvider() { + return [ + 'empty' => [ '' ], + 'digits' => [ '09' ], + 'letters' => [ 'AZaz' ], + 'ASCII special characters' => [ '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' ], + ]; + } + + /** + * @dataProvider invalidKeyProvider + * @covers MemcachedBagOStuff::validateKeyEncoding + */ + public function testValidateKeyEncodingThrowsException( $key ) { + $this->setExpectedException( Exception::class ); + $this->cache->validateKeyEncoding( $key ); + } + + public function invalidKeyProvider() { + return [ + [ "\x00" ], + [ ' ' ], + [ "\x1F" ], + [ "\x7F" ], + [ "\x80" ], + [ "\xFF" ], + ]; + } +} diff --git a/www/wiki/tests/phpunit/includes/objectcache/ObjectCacheTest.php b/www/wiki/tests/phpunit/includes/objectcache/ObjectCacheTest.php new file mode 100644 index 00000000..43188528 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/objectcache/ObjectCacheTest.php @@ -0,0 +1,115 @@ +<?php + +class ObjectCacheTest extends MediaWikiTestCase { + + protected function setUp() { + // Parent calls ObjectCache::clear() among other things + parent::setUp(); + + $this->setCacheConfig(); + $this->setMwGlobals( [ + 'wgMainCacheType' => CACHE_NONE, + 'wgMessageCacheType' => CACHE_NONE, + 'wgParserCacheType' => CACHE_NONE, + ] ); + } + + private function setCacheConfig( $arr = [] ) { + $defaults = [ + CACHE_NONE => [ 'class' => EmptyBagOStuff::class ], + CACHE_DB => [ 'class' => SqlBagOStuff::class ], + CACHE_ANYTHING => [ 'factory' => 'ObjectCache::newAnything' ], + // Mock ACCEL with 'hash' as being installed. + // This makes tests deterministic regardless of APC. + CACHE_ACCEL => [ 'class' => HashBagOStuff::class ], + 'hash' => [ 'class' => HashBagOStuff::class ], + ]; + $this->setMwGlobals( 'wgObjectCaches', $arr + $defaults ); + } + + /** @covers ObjectCache::newAnything */ + public function testNewAnythingNothing() { + $this->assertInstanceOf( + SqlBagOStuff::class, + ObjectCache::newAnything( [] ), + 'No available types. Fallback to DB' + ); + } + + /** @covers ObjectCache::newAnything */ + public function testNewAnythingHash() { + $this->setMwGlobals( [ + 'wgMainCacheType' => 'hash' + ] ); + + $this->assertInstanceOf( + HashBagOStuff::class, + ObjectCache::newAnything( [] ), + 'Use an available type (hash)' + ); + } + + /** @covers ObjectCache::newAnything */ + public function testNewAnythingAccel() { + $this->setMwGlobals( [ + 'wgMainCacheType' => CACHE_ACCEL + ] ); + + $this->assertInstanceOf( + HashBagOStuff::class, + ObjectCache::newAnything( [] ), + 'Use an available type (CACHE_ACCEL)' + ); + } + + /** @covers ObjectCache::newAnything */ + public function testNewAnythingNoAccel() { + $this->setMwGlobals( [ + 'wgMainCacheType' => CACHE_ACCEL + ] ); + + $this->setCacheConfig( [ + // Mock APC not being installed (T160519, T147161) + CACHE_ACCEL => [ 'class' => EmptyBagOStuff::class ] + ] ); + + $this->assertInstanceOf( + SqlBagOStuff::class, + ObjectCache::newAnything( [] ), + 'Fallback to DB if available types fall back to Empty' + ); + } + + /** @covers ObjectCache::newAnything */ + public function testNewAnythingNoAccelNoDb() { + $this->overrideMwServices(); // Ensures restore on tear down + MediaWiki\MediaWikiServices::disableStorageBackend(); + + $this->setMwGlobals( [ + 'wgMainCacheType' => CACHE_ACCEL + ] ); + + $this->setCacheConfig( [ + // Mock APC not being installed (T160519, T147161) + CACHE_ACCEL => [ 'class' => EmptyBagOStuff::class ] + ] ); + + $this->assertInstanceOf( + EmptyBagOStuff::class, + ObjectCache::newAnything( [] ), + 'Fallback to none if available types and DB are unavailable' + ); + } + + /** @covers ObjectCache::newAnything */ + public function testNewAnythingNothingNoDb() { + $this->overrideMwServices(); + MediaWiki\MediaWikiServices::disableStorageBackend(); + + $this->assertInstanceOf( + EmptyBagOStuff::class, + ObjectCache::newAnything( [] ), + 'No available types or DB. Fallback to none.' + ); + } +} diff --git a/www/wiki/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php b/www/wiki/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php new file mode 100644 index 00000000..66754fc9 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php @@ -0,0 +1,90 @@ +<?php +/** + * @group BagOStuff + * + * @covers RESTBagOStuff + */ +class RESTBagOStuffTest extends MediaWikiTestCase { + + /** + * @var MultiHttpClient + */ + private $client; + /** + * @var RESTBagOStuff + */ + private $bag; + + public function setUp() { + parent::setUp(); + $this->client = + $this->getMockBuilder( MultiHttpClient::class ) + ->setConstructorArgs( [ [] ] ) + ->setMethods( [ 'run' ] ) + ->getMock(); + $this->bag = new RESTBagOStuff( [ 'client' => $this->client, 'url' => 'http://test/rest/' ] ); + } + + public function testGet() { + $this->client->expects( $this->once() )->method( 'run' )->with( [ + 'method' => 'GET', + 'url' => 'http://test/rest/42xyz42' + // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) + ] )->willReturn( [ 200, 'OK', [], 's:8:"somedata";', 0 ] ); + $result = $this->bag->get( '42xyz42' ); + $this->assertEquals( 'somedata', $result ); + } + + public function testGetNotExist() { + $this->client->expects( $this->once() )->method( 'run' )->with( [ + 'method' => 'GET', + 'url' => 'http://test/rest/42xyz42' + // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) + ] )->willReturn( [ 404, 'Not found', [], 'Nothing to see here', 0 ] ); + $result = $this->bag->get( '42xyz42' ); + $this->assertFalse( $result ); + } + + public function testGetBadClient() { + $this->client->expects( $this->once() )->method( 'run' )->with( [ + 'method' => 'GET', + 'url' => 'http://test/rest/42xyz42' + // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) + ] )->willReturn( [ 0, '', [], '', 'cURL has failed you today' ] ); + $result = $this->bag->get( '42xyz42' ); + $this->assertFalse( $result ); + $this->assertEquals( BagOStuff::ERR_UNREACHABLE, $this->bag->getLastError() ); + } + + public function testGetBadServer() { + $this->client->expects( $this->once() )->method( 'run' )->with( [ + 'method' => 'GET', + 'url' => 'http://test/rest/42xyz42' + // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) + ] )->willReturn( [ 500, 'Too busy', [], 'Server is too busy', '' ] ); + $result = $this->bag->get( '42xyz42' ); + $this->assertFalse( $result ); + $this->assertEquals( BagOStuff::ERR_UNEXPECTED, $this->bag->getLastError() ); + } + + public function testPut() { + $this->client->expects( $this->once() )->method( 'run' )->with( [ + 'method' => 'PUT', + 'url' => 'http://test/rest/42xyz42', + 'body' => 's:8:"postdata";' + // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) + ] )->willReturn( [ 200, 'OK', [], 'Done', 0 ] ); + $result = $this->bag->set( '42xyz42', 'postdata' ); + $this->assertTrue( $result ); + } + + public function testDelete() { + $this->client->expects( $this->once() )->method( 'run' )->with( [ + 'method' => 'DELETE', + 'url' => 'http://test/rest/42xyz42', + // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) + ] )->willReturn( [ 200, 'OK', [], 'Done', 0 ] ); + $result = $this->bag->delete( '42xyz42' ); + $this->assertTrue( $result ); + } +} diff --git a/www/wiki/tests/phpunit/includes/objectcache/RedisBagOStuffTest.php b/www/wiki/tests/phpunit/includes/objectcache/RedisBagOStuffTest.php new file mode 100644 index 00000000..df5614d8 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/objectcache/RedisBagOStuffTest.php @@ -0,0 +1,110 @@ +<?php + +use Wikimedia\TestingAccessWrapper; + +/** + * @group BagOStuff + */ +class RedisBagOStuffTest extends PHPUnit\Framework\TestCase { + + use MediaWikiCoversValidator; + + /** @var RedisBagOStuff */ + private $cache; + + protected function setUp() { + parent::setUp(); + $cache = $this->getMockBuilder( RedisBagOStuff::class ) + ->disableOriginalConstructor() + ->getMock(); + $this->cache = TestingAccessWrapper::newFromObject( $cache ); + } + + /** + * @covers RedisBagOStuff::unserialize + * @dataProvider unserializeProvider + */ + public function testUnserialize( $expected, $input, $message ) { + $actual = $this->cache->unserialize( $input ); + $this->assertSame( $expected, $actual, $message ); + } + + public function unserializeProvider() { + return [ + [ + -1, + '-1', + 'String representation of \'-1\'', + ], + [ + 0, + '0', + 'String representation of \'0\'', + ], + [ + 1, + '1', + 'String representation of \'1\'', + ], + [ + -1.0, + 'd:-1;', + 'Serialized negative double', + ], + [ + 'foo', + 's:3:"foo";', + 'Serialized string', + ] + ]; + } + + /** + * @covers RedisBagOStuff::serialize + * @dataProvider serializeProvider + */ + public function testSerialize( $expected, $input, $message ) { + $actual = $this->cache->serialize( $input ); + $this->assertSame( $expected, $actual, $message ); + } + + public function serializeProvider() { + return [ + [ + -1, + -1, + '-1 as integer', + ], + [ + 0, + 0, + '0 as integer', + ], + [ + 1, + 1, + '1 as integer', + ], + [ + 'd:-1;', + -1.0, + 'Negative double', + ], + [ + 's:3:"2.1";', + '2.1', + 'Decimal string', + ], + [ + 's:1:"1";', + '1', + 'String representation of 1', + ], + [ + 's:3:"foo";', + 'foo', + 'String', + ], + ]; + } +} |