PHPUnit: introduce setMainCache

The main object cache is disabled during testing. Some integration tests
need it though. This provides a clean way to enable it, to replace the hacks
that were used so far.

Note that we may want to enable the main cache during testing soon. When
that happens, this method is still useful to disable the cache in certain
tests, and to set a specific cache instance.

Change-Id: I04ae1bf1b6b2c8f6310acd2edf89459d01a9c870
This commit is contained in:
daniel 2022-06-21 14:56:19 +02:00 committed by Tim Starling
parent c3d4646763
commit bf092744c9
12 changed files with 91 additions and 30 deletions

View file

@ -87,6 +87,7 @@ define( 'CACHE_NONE', 0 ); // Do not cache
define( 'CACHE_DB', 1 ); // Store cache objects in the DB
define( 'CACHE_MEMCACHED', 'memcached-php' ); // Backwards-compatability alias for Memcached
define( 'CACHE_ACCEL', 3 ); // APC or WinCache
define( 'CACHE_HASH', 'hash' ); // A HashBagOStuff, mostly useful for testing. Not configurable
/** @} */
/** @{

View file

@ -94,7 +94,7 @@ class ObjectCache {
// Always recognize these ones
if ( $id === CACHE_NONE ) {
return new EmptyBagOStuff();
} elseif ( $id === 'hash' ) {
} elseif ( $id === CACHE_HASH ) {
return new HashBagOStuff();
}

View file

@ -344,6 +344,7 @@ abstract class MediaWikiIntegrationTestCase extends PHPUnit\Framework\TestCase {
'apc' => $hashCache,
'apcu' => $hashCache,
'wincache' => $hashCache,
'UTCache' => $hashCache,
] + $baseConfig->get( MainConfigNames::ObjectCaches );
// Use hash based caches
@ -837,6 +838,45 @@ abstract class MediaWikiIntegrationTestCase extends PHPUnit\Framework\TestCase {
$this->setMwGlobals( "wg$key", $value );
}
/**
* Set the main object cache that will be returned by ObjectCache::getLocalClusterInstance().
*
* Per default, the main object cache is disabled during testing (that is, the cache is an
* EmptyBagOStuff).
*
* The $cache parameter support the following kinds of values:
* - a string: refers to an entry in the ObjectCaches array, see MainConfigSchema::ObjectCaches.
* MainCacheType will be set to this value. Use CACHE_HASH to use a HashBagOStuff.
* - an int: refers to an entry in the ObjectCaches array, see MainConfigSchema::ObjectCaches.
* MainCacheType will be set to this value. Use CACHE_NONE to disable caching.
* - a BagOStuff: the object will be injected into the ObjectCache class under the name
* 'UTCache', and MainCacheType will be set to 'UTCache'.
*
* @note Most entries in the ObjectCaches config setting are overwritten during testing.
* To set the cache to anything other than CACHE_HASH, you will have to override
* the ObjectCaches setting first.
*
* @note This will cause any existing service instances to be reset.
*
* @param string|BagOStuff $cache
*
* @return string|int The new value of the MainCacheType setting.
*/
protected function setMainCache( $cache ) {
if ( $cache instanceof BagOStuff ) {
// ObjectCache::$instances is reset after each test by resetNonGlobalServices().
ObjectCache::$instances[ 'UTCache' ] = $cache;
$cache = 'UTCache';
}
if ( !is_string( $cache ) && !is_int( $cache ) ) {
throw new InvalidArgumentException( 'Bad type of $cache parameter: ' . get_debug_type( $cache ) );
}
$this->overrideConfigValue( MainConfigNames::MainCacheType, $cache );
return $cache;
}
/**
* Overrides a set of config settings for the duration of the current test case.
* The original values of the config settings will be restored after the test case finishes.

View file

@ -333,8 +333,8 @@ class ApiLoginTest extends ApiTestCase {
];
$this->setGroupPermissions( 'sysop', 'noratelimit', false );
$this->setMwGlobals( 'wgMainCacheType', 'hash' );
$this->setMwGlobals( 'wgPasswordAttemptThrottle', $throttle );
$this->setMainCache( CACHE_HASH );
$this->overrideConfigValue( 'PasswordAttemptThrottle', $throttle );
list( $name, $password ) = $this->setUpForBotPassword();

View file

@ -247,9 +247,9 @@ class ApiMoveTest extends ApiTestCase {
$name = ucfirst( __FUNCTION__ );
$this->setMwGlobals( 'wgMainCacheType', 'hash' );
$this->setMainCache( CACHE_HASH );
$this->mergeMwGlobalArrayValue( 'wgRateLimits',
$this->overrideConfigValue( 'RateLimits',
[ 'move' => [ '&can-bypass' => false, 'user' => [ 1, 60 ] ] ] );
$id = $this->createPage( $name );

View file

@ -32,8 +32,9 @@ class ApiStashEditTest extends ApiTestCase {
$this->getServiceContainer()->getHookContainer(),
PageEditStash::INITIATOR_USER
) );
// Clear rate-limiting cache between tests
$this->setMwGlobals( 'wgMainCacheType', 'hash' );
// Enable the main cache, so we have a place to store rate limit counters.
$this->setMainCache( CACHE_HASH );
}
/**

View file

@ -2580,9 +2580,7 @@ class AuthManagerTest extends \MediaWikiIntegrationTestCase {
$this->setGroupPermissions( '*', 'autocreateaccount', false );
$this->initializeManager( true );
$this->mergeMwGlobalArrayValue( 'wgObjectCaches',
[ __METHOD__ => [ 'class' => 'HashBagOStuff' ] ] );
$this->setMwGlobals( [ 'wgMainCacheType' => __METHOD__ ] );
$this->setMainCache( CACHE_HASH );
// Set up lots of mocks...
$mocks = [];

View file

@ -26,10 +26,12 @@ class SessionManagerTest extends MediaWikiIntegrationTestCase {
private $store;
protected function getManager() {
\ObjectCache::$instances['testSessionStore'] = new TestBagOStuff();
$this->store = new TestBagOStuff();
$cacheType = $this->setMainCache( $this->store );
$this->config = new \HashConfig( [
'LanguageCode' => 'en',
'SessionCacheType' => 'testSessionStore',
'SessionCacheType' => $cacheType,
'ObjectCacheSessionExpiry' => 100,
'SessionProviders' => [
[ 'class' => \DummySessionProvider::class ],
@ -43,7 +45,6 @@ class SessionManagerTest extends MediaWikiIntegrationTestCase {
|| preg_match( '/^(Persisting|Unpersisting) session (for|due to)/', $m )
) ? null : $m;
} );
$this->store = new TestBagOStuff();
return new SessionManager( [
'config' => $this->config,
@ -127,7 +128,7 @@ class SessionManagerTest extends MediaWikiIntegrationTestCase {
$manager = TestingAccessWrapper::newFromObject( new SessionManager( [
'config' => $this->config,
] ) );
$this->assertSame( \ObjectCache::$instances['testSessionStore'], $manager->store );
$this->assertSame( $this->store, $manager->store );
foreach ( [
'config' => '$options[\'config\'] must be an instance of Config',

View file

@ -12,7 +12,6 @@ use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityValue;
use MediaWikiIntegrationTestCase;
use MWTimestamp;
use ObjectCache;
use PHPUnit\Framework\MockObject\MockObject;
use Wikimedia\TestingAccessWrapper;
@ -135,8 +134,6 @@ class RateLimiterTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Permissions\RateLimiter::limit
*/
public function testPingLimiterWithStaleCache() {
global $wgMainCacheType;
$limits = [
'edit' => [
'user' => [ 1, 60 ],
@ -147,8 +144,7 @@ class RateLimiterTest extends MediaWikiIntegrationTestCase {
$appTime = 1600000000;
$bag = new HashBagOStuff();
// TODO: make the main object cache a service we can override, T243233
ObjectCache::$instances[$wgMainCacheType] = $bag;
$this->setMainCache( $bag );
$bag->setMockTime( $bagTime ); // this is a reference!
MWTimestamp::setFakeTime( static function () use ( &$appTime ) {
@ -175,8 +171,6 @@ class RateLimiterTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Permissions\RateLimiter::limit
*/
public function testPingLimiterRate() {
global $wgMainCacheType;
$limits = [
'edit' => [
'user' => [ 3, 60 ],
@ -184,18 +178,17 @@ class RateLimiterTest extends MediaWikiIntegrationTestCase {
];
$fakeTime = 1600000000;
$store = new HashBagOStuff();
$cache = new HashBagOStuff();
// TODO: make the main object cache a service we can override, T243233
ObjectCache::$instances[$wgMainCacheType] = $store;
$this->setMainCache( $cache );
$store->setMockTime( $fakeTime ); // this is a reference!
$cache->setMockTime( $fakeTime ); // this is a reference!
MWTimestamp::setFakeTime( static function () use ( &$fakeTime ) {
return (int)$fakeTime;
} );
$user = $this->newFakeUser( 'Frank', '1.2.3.4', 111 );
$limiter = $this->newRateLimiter( $limits, [], $store );
$limiter = $this->newRateLimiter( $limits, [], $cache );
// The limit is 3 per 60 second. Do 5 edits at an emulated 50 second interval.
// They should all pass. This tests that the counter doesn't just keeps increasing

View file

@ -387,7 +387,8 @@ class PageHTMLHandlerTest extends MediaWikiIntegrationTestCase {
public function testStashingWithRateLimitExceeded() {
// Set the rate limit to 1 request per minute
$this->mergeMwGlobalArrayValue( 'wgRateLimits',
$this->overrideConfigValue(
'RateLimits',
[
'stashbasehtml' => [
'&can-bypass' => false,
@ -395,7 +396,7 @@ class PageHTMLHandlerTest extends MediaWikiIntegrationTestCase {
'newbie' => [ 1, 60 ]
]
] );
$this->setMwGlobals( [ 'wgMainCacheType' => CACHE_ANYTHING ] );
$this->setMainCache( CACHE_HASH );
$page = $this->getExistingTestPage();

View file

@ -411,7 +411,8 @@ class RevisionHTMLHandlerTest extends MediaWikiIntegrationTestCase {
public function testStashingWithRateLimitExceeded() {
// Set the rate limit to 1 request per minute
$this->mergeMwGlobalArrayValue( 'wgRateLimits',
$this->overrideConfigValue(
'RateLimits',
[
'stashbasehtml' => [
'&can-bypass' => false,
@ -419,7 +420,7 @@ class RevisionHTMLHandlerTest extends MediaWikiIntegrationTestCase {
'newbie' => [ 1, 60 ]
]
] );
$this->setMwGlobals( [ 'wgMainCacheType' => CACHE_ANYTHING ] );
$this->setMainCache( CACHE_HASH );
$page = $this->getExistingTestPage();

View file

@ -168,6 +168,31 @@ class MediaWikiIntegrationTestCaseTest extends MediaWikiIntegrationTestCase {
$this->assertSame( 'YYY', $config->get( MainConfigNames::JobTypeConf ) );
}
public function testSetMainCache() {
// Cache should be disabled per default during testing.
$this->assertInstanceOf( EmptyBagOStuff::class, ObjectCache::getLocalClusterInstance() );
// Use HashBagOStuff.
$this->setMainCache( CACHE_HASH );
$cache = ObjectCache::getLocalClusterInstance();
$this->assertInstanceOf( HashBagOStuff::class, $cache );
// Install different HashBagOStuff
$cache = new HashBagOStuff();
$name = $this->setMainCache( $cache );
$this->assertSame( $cache, ObjectCache::getLocalClusterInstance() );
$this->assertSame( $cache, ObjectCache::getInstance( $name ) );
// Our custom cache object should not replace an existing entry.
$this->assertNotSame( $cache, ObjectCache::getInstance( CACHE_HASH ) );
$this->setMainCache( CACHE_HASH );
$this->assertNotSame( $cache, ObjectCache::getLocalClusterInstance() );
// We should be able to disable the cache.
$this->assertSame( CACHE_NONE, $this->setMainCache( CACHE_NONE ) );
$this->assertInstanceOf( EmptyBagOStuff::class, ObjectCache::getLocalClusterInstance() );
}
public function testOverrideMwServices() {
$initialServices = MediaWikiServices::getInstance();