diff options
author | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
---|---|---|
committer | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
commit | fc7369835258467bf97eb64f184b93691f9a9fd5 (patch) | |
tree | daabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/tests/phpunit/includes/MediaWikiServicesTest.php |
first commit
Diffstat (limited to 'www/wiki/tests/phpunit/includes/MediaWikiServicesTest.php')
-rw-r--r-- | www/wiki/tests/phpunit/includes/MediaWikiServicesTest.php | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/www/wiki/tests/phpunit/includes/MediaWikiServicesTest.php b/www/wiki/tests/phpunit/includes/MediaWikiServicesTest.php new file mode 100644 index 00000000..03588aec --- /dev/null +++ b/www/wiki/tests/phpunit/includes/MediaWikiServicesTest.php @@ -0,0 +1,379 @@ +<?php + +use Mediawiki\Http\HttpRequestFactory; +use MediaWiki\Interwiki\InterwikiLookup; +use MediaWiki\Linker\LinkRenderer; +use MediaWiki\Linker\LinkRendererFactory; +use MediaWiki\MediaWikiServices; +use MediaWiki\Services\DestructibleService; +use MediaWiki\Services\SalvageableService; +use MediaWiki\Services\ServiceDisabledException; +use MediaWiki\Shell\CommandFactory; +use MediaWiki\Storage\BlobStore; +use MediaWiki\Storage\BlobStoreFactory; +use MediaWiki\Storage\RevisionLookup; +use MediaWiki\Storage\RevisionStore; +use MediaWiki\Storage\SqlBlobStore; + +/** + * @covers MediaWiki\MediaWikiServices + * + * @group MediaWiki + */ +class MediaWikiServicesTest extends MediaWikiTestCase { + + /** + * @return Config + */ + private function newTestConfig() { + $globalConfig = new GlobalVarConfig(); + + $testConfig = new HashConfig(); + $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) ); + $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) ); + + return $testConfig; + } + + /** + * @return MediaWikiServices + */ + private function newMediaWikiServices( Config $config = null ) { + if ( $config === null ) { + $config = $this->newTestConfig(); + } + + $instance = new MediaWikiServices( $config ); + + // Load the default wiring from the specified files. + $wiringFiles = $config->get( 'ServiceWiringFiles' ); + $instance->loadWiringFiles( $wiringFiles ); + + return $instance; + } + + public function testGetInstance() { + $services = MediaWikiServices::getInstance(); + $this->assertInstanceOf( MediaWikiServices::class, $services ); + } + + public function testForceGlobalInstance() { + $newServices = $this->newMediaWikiServices(); + $oldServices = MediaWikiServices::forceGlobalInstance( $newServices ); + + $this->assertInstanceOf( MediaWikiServices::class, $oldServices ); + $this->assertNotSame( $oldServices, $newServices ); + + $theServices = MediaWikiServices::getInstance(); + $this->assertSame( $theServices, $newServices ); + + MediaWikiServices::forceGlobalInstance( $oldServices ); + + $theServices = MediaWikiServices::getInstance(); + $this->assertSame( $theServices, $oldServices ); + } + + public function testResetGlobalInstance() { + $newServices = $this->newMediaWikiServices(); + $oldServices = MediaWikiServices::forceGlobalInstance( $newServices ); + + $service1 = $this->createMock( SalvageableService::class ); + $service1->expects( $this->never() ) + ->method( 'salvage' ); + + $newServices->defineService( + 'Test', + function () use ( $service1 ) { + return $service1; + } + ); + + // force instantiation + $newServices->getService( 'Test' ); + + MediaWikiServices::resetGlobalInstance( $this->newTestConfig() ); + $theServices = MediaWikiServices::getInstance(); + + $this->assertSame( + $service1, + $theServices->getService( 'Test' ), + 'service definition should survive reset' + ); + + $this->assertNotSame( $theServices, $newServices ); + $this->assertNotSame( $theServices, $oldServices ); + + MediaWikiServices::forceGlobalInstance( $oldServices ); + } + + public function testResetGlobalInstance_quick() { + $newServices = $this->newMediaWikiServices(); + $oldServices = MediaWikiServices::forceGlobalInstance( $newServices ); + + $service1 = $this->createMock( SalvageableService::class ); + $service1->expects( $this->never() ) + ->method( 'salvage' ); + + $service2 = $this->createMock( SalvageableService::class ); + $service2->expects( $this->once() ) + ->method( 'salvage' ) + ->with( $service1 ); + + // sequence of values the instantiator will return + $instantiatorReturnValues = [ + $service1, + $service2, + ]; + + $newServices->defineService( + 'Test', + function () use ( &$instantiatorReturnValues ) { + return array_shift( $instantiatorReturnValues ); + } + ); + + // force instantiation + $newServices->getService( 'Test' ); + + MediaWikiServices::resetGlobalInstance( $this->newTestConfig(), 'quick' ); + $theServices = MediaWikiServices::getInstance(); + + $this->assertSame( $service2, $theServices->getService( 'Test' ) ); + + $this->assertNotSame( $theServices, $newServices ); + $this->assertNotSame( $theServices, $oldServices ); + + MediaWikiServices::forceGlobalInstance( $oldServices ); + } + + public function testDisableStorageBackend() { + $newServices = $this->newMediaWikiServices(); + $oldServices = MediaWikiServices::forceGlobalInstance( $newServices ); + + $lbFactory = $this->getMockBuilder( \Wikimedia\Rdbms\LBFactorySimple::class ) + ->disableOriginalConstructor() + ->getMock(); + + $newServices->redefineService( + 'DBLoadBalancerFactory', + function () use ( $lbFactory ) { + return $lbFactory; + } + ); + + // force the service to become active, so we can check that it does get destroyed + $newServices->getService( 'DBLoadBalancerFactory' ); + + MediaWikiServices::disableStorageBackend(); // should destroy DBLoadBalancerFactory + + try { + MediaWikiServices::getInstance()->getService( 'DBLoadBalancerFactory' ); + $this->fail( 'DBLoadBalancerFactory should have been disabled' ); + } + catch ( ServiceDisabledException $ex ) { + // ok, as expected + } catch ( Throwable $ex ) { + $this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) ); + } + + MediaWikiServices::forceGlobalInstance( $oldServices ); + $newServices->destroy(); + + // No exception was thrown, avoid being risky + $this->assertTrue( true ); + } + + public function testResetChildProcessServices() { + $newServices = $this->newMediaWikiServices(); + $oldServices = MediaWikiServices::forceGlobalInstance( $newServices ); + + $service1 = $this->createMock( DestructibleService::class ); + $service1->expects( $this->once() ) + ->method( 'destroy' ); + + $service2 = $this->createMock( DestructibleService::class ); + $service2->expects( $this->never() ) + ->method( 'destroy' ); + + // sequence of values the instantiator will return + $instantiatorReturnValues = [ + $service1, + $service2, + ]; + + $newServices->defineService( + 'Test', + function () use ( &$instantiatorReturnValues ) { + return array_shift( $instantiatorReturnValues ); + } + ); + + // force the service to become active, so we can check that it does get destroyed + $oldTestService = $newServices->getService( 'Test' ); + + MediaWikiServices::resetChildProcessServices(); + $finalServices = MediaWikiServices::getInstance(); + + $newTestService = $finalServices->getService( 'Test' ); + $this->assertNotSame( $oldTestService, $newTestService ); + + MediaWikiServices::forceGlobalInstance( $oldServices ); + } + + public function testResetServiceForTesting() { + $services = $this->newMediaWikiServices(); + $serviceCounter = 0; + + $services->defineService( + 'Test', + function () use ( &$serviceCounter ) { + $serviceCounter++; + $service = $this->createMock( MediaWiki\Services\DestructibleService::class ); + $service->expects( $this->once() )->method( 'destroy' ); + return $service; + } + ); + + // This should do nothing. In particular, it should not create a service instance. + $services->resetServiceForTesting( 'Test' ); + $this->assertEquals( 0, $serviceCounter, 'No service instance should be created yet.' ); + + $oldInstance = $services->getService( 'Test' ); + $this->assertEquals( 1, $serviceCounter, 'A service instance should exit now.' ); + + // The old instance should be detached, and destroy() called. + $services->resetServiceForTesting( 'Test' ); + $newInstance = $services->getService( 'Test' ); + + $this->assertNotSame( $oldInstance, $newInstance ); + + // Satisfy the expectation that destroy() is called also for the second service instance. + $newInstance->destroy(); + } + + public function testResetServiceForTesting_noDestroy() { + $services = $this->newMediaWikiServices(); + + $services->defineService( + 'Test', + function () { + $service = $this->createMock( MediaWiki\Services\DestructibleService::class ); + $service->expects( $this->never() )->method( 'destroy' ); + return $service; + } + ); + + $oldInstance = $services->getService( 'Test' ); + + // The old instance should be detached, but destroy() not called. + $services->resetServiceForTesting( 'Test', false ); + $newInstance = $services->getService( 'Test' ); + + $this->assertNotSame( $oldInstance, $newInstance ); + } + + public function provideGetters() { + $getServiceCases = $this->provideGetService(); + $getterCases = []; + + // All getters should be named just like the service, with "get" added. + foreach ( $getServiceCases as $name => $case ) { + if ( $name[0] === '_' ) { + // Internal service, no getter + continue; + } + list( $service, $class ) = $case; + $getterCases[$name] = [ + 'get' . $service, + $class, + ]; + } + + return $getterCases; + } + + /** + * @dataProvider provideGetters + */ + public function testGetters( $getter, $type ) { + // Test against the default instance, since the dummy will not know the default services. + $services = MediaWikiServices::getInstance(); + $service = $services->$getter(); + $this->assertInstanceOf( $type, $service ); + } + + public function provideGetService() { + // NOTE: This should list all service getters defined in ServiceWiring.php. + // NOTE: For every test case defined here there should be a corresponding + // test case defined in provideGetters(). + return [ + 'BootstrapConfig' => [ 'BootstrapConfig', Config::class ], + 'ConfigFactory' => [ 'ConfigFactory', ConfigFactory::class ], + 'MainConfig' => [ 'MainConfig', Config::class ], + 'SiteStore' => [ 'SiteStore', SiteStore::class ], + 'SiteLookup' => [ 'SiteLookup', SiteLookup::class ], + 'StatsdDataFactory' => [ 'StatsdDataFactory', IBufferingStatsdDataFactory::class ], + 'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup::class ], + 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ], + 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ], + 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig::class ], + 'SkinFactory' => [ 'SkinFactory', SkinFactory::class ], + 'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', Wikimedia\Rdbms\LBFactory::class ], + 'DBLoadBalancer' => [ 'DBLoadBalancer', Wikimedia\Rdbms\LoadBalancer::class ], + 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ], + 'WatchedItemQueryService' => [ 'WatchedItemQueryService', WatchedItemQueryService::class ], + 'CryptRand' => [ 'CryptRand', CryptRand::class ], + 'CryptHKDF' => [ 'CryptHKDF', CryptHKDF::class ], + 'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory::class ], + 'Parser' => [ 'Parser', Parser::class ], + 'ParserCache' => [ 'ParserCache', ParserCache::class ], + 'GenderCache' => [ 'GenderCache', GenderCache::class ], + 'LinkCache' => [ 'LinkCache', LinkCache::class ], + 'LinkRenderer' => [ 'LinkRenderer', LinkRenderer::class ], + 'LinkRendererFactory' => [ 'LinkRendererFactory', LinkRendererFactory::class ], + '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ], + 'MimeAnalyzer' => [ 'MimeAnalyzer', MimeAnalyzer::class ], + 'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ], + 'TitleParser' => [ 'TitleParser', TitleParser::class ], + 'ProxyLookup' => [ 'ProxyLookup', ProxyLookup::class ], + 'MainObjectStash' => [ 'MainObjectStash', BagOStuff::class ], + 'MainWANObjectCache' => [ 'MainWANObjectCache', WANObjectCache::class ], + 'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ], + 'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ], + 'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ], + 'BlobStoreFactory' => [ 'BlobStoreFactory', BlobStoreFactory::class ], + 'BlobStore' => [ 'BlobStore', BlobStore::class ], + '_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ], + 'RevisionStore' => [ 'RevisionStore', RevisionStore::class ], + 'RevisionLookup' => [ 'RevisionLookup', RevisionLookup::class ], + 'HttpRequestFactory' => [ 'HttpRequestFactory', HttpRequestFactory::class ], + 'CommentStore' => [ 'CommentStore', CommentStore::class ], + ]; + } + + /** + * @dataProvider provideGetService + */ + public function testGetService( $name, $type ) { + // Test against the default instance, since the dummy will not know the default services. + $services = MediaWikiServices::getInstance(); + + $service = $services->getService( $name ); + $this->assertInstanceOf( $type, $service ); + } + + public function testDefaultServiceInstantiation() { + // Check all services in the default instance, not a dummy instance! + // Note that we instantiate all services here, including any that + // were registered by extensions. + $services = MediaWikiServices::getInstance(); + $names = $services->getServiceNames(); + + foreach ( $names as $name ) { + $this->assertTrue( $services->hasService( $name ) ); + $service = $services->getService( $name ); + $this->assertInternalType( 'object', $service ); + } + } + +} |