Merge "StringUtils: Add a utility for checking if a string is a valid regex"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiModuleManagerTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use Wikimedia\ObjectFactory;
5
6 /**
7 * @covers ApiModuleManager
8 *
9 * @group API
10 * @group Database
11 * @group medium
12 */
13 class ApiModuleManagerTest extends MediaWikiTestCase {
14
15 private function getModuleManager() {
16 $request = new FauxRequest();
17 $main = new ApiMain( $request );
18
19 return new ApiModuleManager( $main, MediaWikiServices::getInstance()->getObjectFactory() );
20 }
21
22 public function newApiLogin( $main, $action ) {
23 return new ApiLogin( $main, $action );
24 }
25
26 public function addModuleProvider() {
27 return [
28 'plain class' => [
29 'login',
30 'action',
31 ApiLogin::class,
32 null,
33 ],
34
35 'with class and factory' => [
36 'login',
37 'action',
38 ApiLogin::class,
39 [ $this, 'newApiLogin' ],
40 ],
41
42 'with spec (class only)' => [
43 'login',
44 'action',
45 [
46 'class' => ApiLogin::class
47 ],
48 null,
49 ],
50
51 'with spec' => [
52 'login',
53 'action',
54 [
55 'class' => ApiLogin::class,
56 'factory' => [ $this, 'newApiLogin' ],
57 ],
58 null,
59 ],
60
61 'with spec (using services)' => [
62 'logout',
63 'action',
64 [
65 'class' => ApiLogout::class,
66 'factory' => function ( ApiMain $main, $action, ObjectFactory $objectFactory ) {
67 return new ApiLogout( $main, $action );
68 },
69 'services' => [
70 'ObjectFactory'
71 ],
72 ],
73 null,
74 ]
75 ];
76 }
77
78 /**
79 * @dataProvider addModuleProvider
80 */
81 public function testAddModule( $name, $group, $spec, $factory ) {
82 if ( $factory ) {
83 $this->hideDeprecated(
84 ApiModuleManager::class . '::addModule with $class and $factory'
85 );
86 }
87
88 $moduleManager = $this->getModuleManager();
89 $moduleManager->addModule( $name, $group, $spec, $factory );
90
91 $this->assertTrue( $moduleManager->isDefined( $name, $group ), 'isDefined' );
92 $this->assertNotNull( $moduleManager->getModule( $name, $group, true ), 'getModule' );
93 }
94
95 public function addModulesProvider() {
96 return [
97 'empty' => [
98 [],
99 'action',
100 ],
101
102 'simple' => [
103 [
104 'login' => ApiLogin::class,
105 'logout' => ApiLogout::class,
106 ],
107 'action',
108 ],
109
110 'with factories' => [
111 [
112 'login' => [
113 'class' => ApiLogin::class,
114 'factory' => [ $this, 'newApiLogin' ],
115 ],
116 'logout' => [
117 'class' => ApiLogout::class,
118 'factory' => function ( ApiMain $main, $action ) {
119 return new ApiLogout( $main, $action );
120 },
121 ],
122 ],
123 'action',
124 ],
125 ];
126 }
127
128 /**
129 * @dataProvider addModulesProvider
130 */
131 public function testAddModules( array $modules, $group ) {
132 $moduleManager = $this->getModuleManager();
133 $moduleManager->addModules( $modules, $group );
134
135 foreach ( array_keys( $modules ) as $name ) {
136 $this->assertTrue( $moduleManager->isDefined( $name, $group ), 'isDefined' );
137 $this->assertNotNull( $moduleManager->getModule( $name, $group, true ), 'getModule' );
138 }
139
140 $this->assertTrue( true ); // Don't mark the test as risky if $modules is empty
141 }
142
143 public function getModuleProvider() {
144 $modules = [
145 'feedrecentchanges' => ApiFeedRecentChanges::class,
146 'feedcontributions' => [ 'class' => ApiFeedContributions::class ],
147 'login' => [
148 'class' => ApiLogin::class,
149 'factory' => [ $this, 'newApiLogin' ],
150 ],
151 'logout' => [
152 'class' => ApiLogout::class,
153 'factory' => function ( ApiMain $main, $action ) {
154 return new ApiLogout( $main, $action );
155 },
156 ],
157 ];
158
159 return [
160 'legacy entry' => [
161 $modules,
162 'feedrecentchanges',
163 ApiFeedRecentChanges::class,
164 ],
165
166 'just a class' => [
167 $modules,
168 'feedcontributions',
169 ApiFeedContributions::class,
170 ],
171
172 'with factory' => [
173 $modules,
174 'login',
175 ApiLogin::class,
176 ],
177
178 'with closure' => [
179 $modules,
180 'logout',
181 ApiLogout::class,
182 ],
183 ];
184 }
185
186 /**
187 * @covers ApiModuleManager::getModule
188 * @dataProvider getModuleProvider
189 */
190 public function testGetModule( $modules, $name, $expectedClass ) {
191 $moduleManager = $this->getModuleManager();
192 $moduleManager->addModules( $modules, 'test' );
193
194 // should return the right module
195 $module1 = $moduleManager->getModule( $name, null, false );
196 $this->assertInstanceOf( $expectedClass, $module1 );
197
198 // should pass group check (with caching disabled)
199 $module2 = $moduleManager->getModule( $name, 'test', true );
200 $this->assertNotNull( $module2 );
201
202 // should use cached instance
203 $module3 = $moduleManager->getModule( $name, null, false );
204 $this->assertSame( $module1, $module3 );
205
206 // should not use cached instance if caching is disabled
207 $module4 = $moduleManager->getModule( $name, null, true );
208 $this->assertNotSame( $module1, $module4 );
209 }
210
211 /**
212 * @covers ApiModuleManager::getModule
213 */
214 public function testGetModule_null() {
215 $modules = [
216 'login' => ApiLogin::class,
217 'logout' => ApiLogout::class,
218 ];
219
220 $moduleManager = $this->getModuleManager();
221 $moduleManager->addModules( $modules, 'test' );
222
223 $this->assertNull( $moduleManager->getModule( 'quux' ), 'unknown name' );
224 $this->assertNull( $moduleManager->getModule( 'login', 'bla' ), 'wrong group' );
225 }
226
227 /**
228 * @covers ApiModuleManager::getNames
229 */
230 public function testGetNames() {
231 $fooModules = [
232 'login' => ApiLogin::class,
233 'logout' => ApiLogout::class,
234 ];
235
236 $barModules = [
237 'feedcontributions' => [ 'class' => ApiFeedContributions::class ],
238 'feedrecentchanges' => [ 'class' => ApiFeedRecentChanges::class ],
239 ];
240
241 $moduleManager = $this->getModuleManager();
242 $moduleManager->addModules( $fooModules, 'foo' );
243 $moduleManager->addModules( $barModules, 'bar' );
244
245 $fooNames = $moduleManager->getNames( 'foo' );
246 $this->assertArrayEquals( array_keys( $fooModules ), $fooNames );
247
248 $allNames = $moduleManager->getNames();
249 $allModules = array_merge( $fooModules, $barModules );
250 $this->assertArrayEquals( array_keys( $allModules ), $allNames );
251 }
252
253 /**
254 * @covers ApiModuleManager::getNamesWithClasses
255 */
256 public function testGetNamesWithClasses() {
257 $fooModules = [
258 'login' => ApiLogin::class,
259 'logout' => ApiLogout::class,
260 ];
261
262 $barModules = [
263 'feedcontributions' => [ 'class' => ApiFeedContributions::class ],
264 'feedrecentchanges' => [ 'class' => ApiFeedRecentChanges::class ],
265 ];
266
267 $moduleManager = $this->getModuleManager();
268 $moduleManager->addModules( $fooModules, 'foo' );
269 $moduleManager->addModules( $barModules, 'bar' );
270
271 $fooNamesWithClasses = $moduleManager->getNamesWithClasses( 'foo' );
272 $this->assertArrayEquals( $fooModules, $fooNamesWithClasses );
273
274 $allNamesWithClasses = $moduleManager->getNamesWithClasses();
275 $allModules = array_merge( $fooModules, [
276 'feedcontributions' => ApiFeedContributions::class,
277 'feedrecentchanges' => ApiFeedRecentChanges::class,
278 ] );
279 $this->assertArrayEquals( $allModules, $allNamesWithClasses );
280 }
281
282 /**
283 * @covers ApiModuleManager::getModuleGroup
284 */
285 public function testGetModuleGroup() {
286 $fooModules = [
287 'login' => ApiLogin::class,
288 'logout' => ApiLogout::class,
289 ];
290
291 $barModules = [
292 'feedcontributions' => [ 'class' => ApiFeedContributions::class ],
293 'feedrecentchanges' => [ 'class' => ApiFeedRecentChanges::class ],
294 ];
295
296 $moduleManager = $this->getModuleManager();
297 $moduleManager->addModules( $fooModules, 'foo' );
298 $moduleManager->addModules( $barModules, 'bar' );
299
300 $this->assertEquals( 'foo', $moduleManager->getModuleGroup( 'login' ) );
301 $this->assertEquals( 'bar', $moduleManager->getModuleGroup( 'feedrecentchanges' ) );
302 $this->assertNull( $moduleManager->getModuleGroup( 'quux' ) );
303 }
304
305 /**
306 * @covers ApiModuleManager::getGroups
307 */
308 public function testGetGroups() {
309 $fooModules = [
310 'login' => ApiLogin::class,
311 'logout' => ApiLogout::class,
312 ];
313
314 $barModules = [
315 'feedcontributions' => [ 'class' => ApiFeedContributions::class ],
316 'feedrecentchanges' => [ 'class' => ApiFeedRecentChanges::class ],
317 ];
318
319 $moduleManager = $this->getModuleManager();
320 $moduleManager->addModules( $fooModules, 'foo' );
321 $moduleManager->addModules( $barModules, 'bar' );
322
323 $groups = $moduleManager->getGroups();
324 $this->assertArrayEquals( [ 'foo', 'bar' ], $groups );
325 }
326
327 /**
328 * @covers ApiModuleManager::getClassName
329 */
330 public function testGetClassName() {
331 $fooModules = [
332 'login' => ApiLogin::class,
333 'logout' => ApiLogout::class,
334 ];
335
336 $barModules = [
337 'feedcontributions' => [ 'class' => ApiFeedContributions::class ],
338 'feedrecentchanges' => [ 'class' => ApiFeedRecentChanges::class ],
339 ];
340
341 $moduleManager = $this->getModuleManager();
342 $moduleManager->addModules( $fooModules, 'foo' );
343 $moduleManager->addModules( $barModules, 'bar' );
344
345 $this->assertEquals(
346 ApiLogin::class,
347 $moduleManager->getClassName( 'login' )
348 );
349 $this->assertEquals(
350 ApiLogout::class,
351 $moduleManager->getClassName( 'logout' )
352 );
353 $this->assertEquals(
354 ApiFeedContributions::class,
355 $moduleManager->getClassName( 'feedcontributions' )
356 );
357 $this->assertEquals(
358 ApiFeedRecentChanges::class,
359 $moduleManager->getClassName( 'feedrecentchanges' )
360 );
361 $this->assertFalse(
362 $moduleManager->getClassName( 'nonexistentmodule' )
363 );
364 }
365
366 /**
367 * @expectedException \InvalidArgumentException
368 * @expectedExceptionMessage $spec must define a class name
369 */
370 public function testAddModuleWithIncompleteSpec() {
371 $moduleManager = $this->getModuleManager();
372
373 $moduleManager->addModule(
374 'logout',
375 'action',
376 [
377 'factory' => function ( ApiMain $main, $action ) {
378 return new ApiLogout( $main, $action );
379 },
380 ]
381 );
382 }
383 }