123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- <?php
- use MediaWiki\MediaWikiServices;
- use MediaWiki\Services\DestructibleService;
- use MediaWiki\Services\SalvageableService;
- use MediaWiki\Services\ServiceDisabledException;
- /**
- * @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() {
- global $IP;
- $serviceList = require "$IP/includes/ServiceWiring.php";
- $ret = [];
- foreach ( $serviceList as $name => $callback ) {
- $fun = new ReflectionFunction( $callback );
- if ( !$fun->hasReturnType() ) {
- throw new MWException( 'All service callbacks must have a return type defined, ' .
- "none found for $name" );
- }
- $ret[$name] = [ $name, $fun->getReturnType()->__toString() ];
- }
- return $ret;
- }
- /**
- * @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 );
- }
- }
- public function testDefaultServiceWiringServicesHaveTests() {
- global $IP;
- $testedServices = array_keys( $this->provideGetService() );
- $allServices = array_keys( require "$IP/includes/ServiceWiring.php" );
- $this->assertEquals(
- [],
- array_diff( $allServices, $testedServices ),
- 'The following services have not been added to MediaWikiServicesTest::provideGetService'
- );
- }
- public function testGettersAreSorted() {
- $methods = ( new ReflectionClass( MediaWikiServices::class ) )
- ->getMethods( ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC );
- $names = array_map( function ( $method ) {
- return $method->getName();
- }, $methods );
- $serviceNames = array_map( function ( $name ) {
- return "get$name";
- }, array_keys( $this->provideGetService() ) );
- $names = array_values( array_filter( $names, function ( $name ) use ( $serviceNames ) {
- return in_array( $name, $serviceNames );
- } ) );
- $sortedNames = $names;
- sort( $sortedNames );
- $this->assertSame( $sortedNames, $names,
- 'Please keep service getters sorted alphabetically' );
- }
- }
|