Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5979385
MediaWikiServicesTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
MediaWikiServicesTest.php
View Options
<?php
use
MediaWiki\Config\Config
;
use
MediaWiki\Config\GlobalVarConfig
;
use
MediaWiki\Config\HashConfig
;
use
MediaWiki\Hook\MediaWikiServicesHook
;
use
MediaWiki\HookContainer\HookContainer
;
use
MediaWiki\HookContainer\StaticHookRegistry
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\MediaWikiServices
;
use
Wikimedia\Services\DestructibleService
;
use
Wikimedia\Services\SalvageableService
;
/**
* @covers \MediaWiki\MediaWikiServices
* @group Database
* This test doesn't really make queries, but needs to be in the Database test to make sure
* that storage isn't disabled on the original instance.
*/
class
MediaWikiServicesTest
extends
MediaWikiIntegrationTestCase
{
private
const
DEPRECATED_SERVICES
=
[
'BlockErrorFormatter'
,
'ConfigRepository'
,
'ConfiguredReadOnlyMode'
,
];
/** @var array */
public
static
$mockServiceWiring
=
[];
/**
* @return Config
*/
private
function
newTestConfig
()
{
$globalConfig
=
new
GlobalVarConfig
();
$testConfig
=
new
HashConfig
();
$testConfig
->
set
(
MainConfigNames
::
ServiceWiringFiles
,
$globalConfig
->
get
(
MainConfigNames
::
ServiceWiringFiles
)
);
$testConfig
->
set
(
MainConfigNames
::
ConfigRegistry
,
$globalConfig
->
get
(
MainConfigNames
::
ConfigRegistry
)
);
$testConfig
->
set
(
MainConfigNames
::
Hooks
,
[]
);
return
$testConfig
;
}
/**
* @return MediaWikiServices
*/
private
function
newMediaWikiServices
()
{
$config
=
$this
->
newTestConfig
();
$instance
=
new
MediaWikiServices
(
$config
);
// Load the default wiring from the specified files.
$wiringFiles
=
$config
->
get
(
MainConfigNames
::
ServiceWiringFiles
);
$instance
->
loadWiringFiles
(
$wiringFiles
);
return
$instance
;
}
private
function
newConfigWithMockWiring
()
{
$config
=
new
HashConfig
;
$config
->
set
(
MainConfigNames
::
ServiceWiringFiles
,
[
__DIR__
.
'/MockServiceWiring.php'
]
);
return
$config
;
}
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'
,
static
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'
,
static
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
testResetGlobalInstance_T263925
()
{
$newServices
=
$this
->
newMediaWikiServices
();
$oldServices
=
MediaWikiServices
::
forceGlobalInstance
(
$newServices
);
self
::
$mockServiceWiring
=
[
'HookContainer'
=>
function
(
MediaWikiServices
$services
)
{
return
new
HookContainer
(
new
StaticHookRegistry
(
[],
[
'MediaWikiServices'
=>
[
[
'handler'
=>
[
'name'
=>
'test'
,
'factory'
=>
static
function
()
{
return
new
class
implements
MediaWikiServicesHook
{
public
function
onMediaWikiServices
(
$services
)
{
}
};
}
],
'deprecated'
=>
false
,
'extensionPath'
=>
'path'
],
]
],
[]
),
$this
->
createSimpleObjectFactory
()
);
}
];
$newServices
->
redefineService
(
'HookContainer'
,
self
::
$mockServiceWiring
[
'HookContainer'
]
);
$newServices
->
getHookContainer
()->
run
(
'MediaWikiServices'
,
[
$newServices
]
);
MediaWikiServices
::
resetGlobalInstance
(
$this
->
newConfigWithMockWiring
(),
'quick'
);
$this
->
assertTrue
(
true
,
'expected no exception from above'
);
self
::
$mockServiceWiring
=
[];
MediaWikiServices
::
forceGlobalInstance
(
$oldServices
);
}
public
function
testDisableStorage
()
{
$newServices
=
$this
->
newMediaWikiServices
();
$oldServices
=
MediaWikiServices
::
forceGlobalInstance
(
$newServices
);
$lbFactory
=
$this
->
createMock
(
\Wikimedia\Rdbms\LBFactorySimple
::
class
);
$newServices
->
redefineService
(
'DBLoadBalancerFactory'
,
static
function
()
use
(
$lbFactory
)
{
return
$lbFactory
;
}
);
$this
->
assertFalse
(
$newServices
->
isStorageDisabled
()
);
$newServices
->
disableStorage
();
// should destroy DBLoadBalancerFactory
$this
->
assertTrue
(
$newServices
->
isStorageDisabled
()
);
try
{
$newServices
->
getDBLoadBalancer
()->
getConnection
(
DB_REPLICA
);
}
catch
(
RuntimeException
)
{
// ok, as expected
}
MediaWikiServices
::
forceGlobalInstance
(
$oldServices
);
$newServices
->
destroy
();
// This should work now.
MediaWikiServices
::
getInstance
()->
getDBLoadBalancer
()->
getConnection
(
DB_REPLICA
);
// 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'
,
static
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
(
Wikimedia\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
->
assertSame
(
0
,
$serviceCounter
,
'No service instance should be created yet.'
);
$oldInstance
=
$services
->
getService
(
'Test'
);
$this
->
assertSame
(
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
(
Wikimedia\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
static
function
provideGetters
()
{
$getServiceCases
=
self
::
provideGetService
();
$getterCases
=
[
// These are "mis-named" getters that don't follow the standard pattern, so are listed explicitly
'getWikiRevisionOldRevisionImporter'
=>
[
'getWikiRevisionOldRevisionImporter'
,
OldRevisionImporter
::
class
],
'newSearchEngine'
=>
[
'newSearchEngine'
,
SearchEngine
::
class
]
];
// 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
;
}
[
$service
,
$class
]
=
$case
;
$getterCases
[
$name
]
=
[
'get'
.
$service
,
$class
,
in_array
(
$service
,
self
::
DEPRECATED_SERVICES
)
];
}
return
$getterCases
;
}
/**
* @dataProvider provideGetters
*/
public
function
testGetters
(
$getter
,
$type
,
$isDeprecated
=
false
)
{
if
(
$isDeprecated
)
{
$this
->
hideDeprecated
(
MediaWikiServices
::
class
.
"::$getter"
);
}
// 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
static
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
LogicException
(
'All service callbacks must have a return type defined, '
.
"none found for $name"
);
}
$returnType
=
$fun
->
getReturnType
();
$ret
[
$name
]
=
[
$name
,
$returnType
->
getName
()
];
}
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
)
);
// Check that the service can be instantiated without errors.
// Make no assumption about the value returned by the instantiator
// as extensions may be putting all manners of values in the container.
$services
->
getService
(
$name
);
}
}
public
function
testDefaultServiceWiringServicesHaveTests
()
{
global
$IP
;
$testedServices
=
array_keys
(
self
::
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
);
$getters
=
self
::
provideGetService
();
$names
=
array_map
(
static
fn
(
$method
)
=>
$method
->
getName
(),
$methods
);
$names
=
array_filter
(
$names
,
static
fn
(
$name
)
=>
array_key_exists
(
"get$name"
,
$getters
)
);
$sortedNames
=
$names
;
natcasesort
(
$sortedNames
);
$this
->
assertSame
(
$sortedNames
,
$names
,
'Please keep service getters sorted alphabetically'
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Oct 3, 20:53 (4 d, 23 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
0d/4e/14e5b895ad69cf9f5ed8664e3006
Default Alt Text
MediaWikiServicesTest.php (13 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment