Merge "Remove references to Popularpages alias"
[lhc/web/wiklou.git] / tests / phpunit / includes / registration / ExtensionProcessorTest.php
1 <?php
2
3 class ExtensionProcessorTest extends MediaWikiTestCase {
4
5 private $dir;
6
7 public function setUp() {
8 parent::setUp();
9 $this->dir = __DIR__ . '/FooBar/extension.json';
10 }
11
12 /**
13 * 'name' is absolutely required
14 *
15 * @var array
16 */
17 public static $default = array(
18 'name' => 'FooBar',
19 );
20
21 /**
22 * @covers ExtensionProcessor::extractInfo
23 */
24 public function testExtractInfo() {
25 // Test that attributes that begin with @ are ignored
26 $processor = new ExtensionProcessor();
27 $processor->extractInfo( $this->dir, self::$default + array(
28 '@metadata' => array( 'foobarbaz' ),
29 'AnAttribute' => array( 'omg' ),
30 'AutoloadClasses' => array( 'FooBar' => 'includes/FooBar.php' ),
31 ), 1 );
32
33 $extracted = $processor->getExtractedInfo();
34 $attributes = $extracted['attributes'];
35 $this->assertArrayHasKey( 'AnAttribute', $attributes );
36 $this->assertArrayNotHasKey( '@metadata', $attributes );
37 $this->assertArrayNotHasKey( 'AutoloadClasses', $attributes );
38 }
39
40 public static function provideRegisterHooks() {
41 $merge = array( ExtensionRegistry::MERGE_STRATEGY => 'array_merge_recursive' );
42 // Format:
43 // Current $wgHooks
44 // Content in extension.json
45 // Expected value of $wgHooks
46 return array(
47 // No hooks
48 array(
49 array(),
50 self::$default,
51 $merge,
52 ),
53 // No current hooks, adding one for "FooBaz"
54 array(
55 array(),
56 array( 'Hooks' => array( 'FooBaz' => 'FooBazCallback' ) ) + self::$default,
57 array( 'FooBaz' => array( 'FooBazCallback' ) ) + $merge,
58 ),
59 // Hook for "FooBaz", adding another one
60 array(
61 array( 'FooBaz' => array( 'PriorCallback' ) ),
62 array( 'Hooks' => array( 'FooBaz' => 'FooBazCallback' ) ) + self::$default,
63 array( 'FooBaz' => array( 'PriorCallback', 'FooBazCallback' ) ) + $merge,
64 ),
65 // Hook for "BarBaz", adding one for "FooBaz"
66 array(
67 array( 'BarBaz' => array( 'BarBazCallback' ) ),
68 array( 'Hooks' => array( 'FooBaz' => 'FooBazCallback' ) ) + self::$default,
69 array(
70 'BarBaz' => array( 'BarBazCallback' ),
71 'FooBaz' => array( 'FooBazCallback' ),
72 ) + $merge,
73 ),
74 // Callbacks for FooBaz wrapped in an array
75 array(
76 array(),
77 array( 'Hooks' => array( 'FooBaz' => array( 'Callback1' ) ) ) + self::$default,
78 array(
79 'FooBaz' => array( 'Callback1' ),
80 ) + $merge,
81 ),
82 // Multiple callbacks for FooBaz hook
83 array(
84 array(),
85 array( 'Hooks' => array( 'FooBaz' => array( 'Callback1', 'Callback2' ) ) ) + self::$default,
86 array(
87 'FooBaz' => array( 'Callback1', 'Callback2' ),
88 ) + $merge,
89 ),
90 );
91 }
92
93 /**
94 * @covers ExtensionProcessor::extractHooks
95 * @dataProvider provideRegisterHooks
96 */
97 public function testRegisterHooks( $pre, $info, $expected ) {
98 $processor = new MockExtensionProcessor( array( 'wgHooks' => $pre ) );
99 $processor->extractInfo( $this->dir, $info, 1 );
100 $extracted = $processor->getExtractedInfo();
101 $this->assertEquals( $expected, $extracted['globals']['wgHooks'] );
102 }
103
104 /**
105 * @covers ExtensionProcessor::extractConfig
106 */
107 public function testExtractConfig() {
108 $processor = new ExtensionProcessor;
109 $info = array(
110 'config' => array(
111 'Bar' => 'somevalue',
112 'Foo' => 10,
113 '@IGNORED' => 'yes',
114 ),
115 ) + self::$default;
116 $processor->extractInfo( $this->dir, $info, 1 );
117 $extracted = $processor->getExtractedInfo();
118 $this->assertEquals( 'somevalue', $extracted['globals']['wgBar'] );
119 $this->assertEquals( 10, $extracted['globals']['wgFoo'] );
120 $this->assertArrayNotHasKey( 'wg@IGNORED', $extracted['globals'] );
121 }
122
123 public static function provideExtracttExtensionMessagesFiles() {
124 $dir = __DIR__ . '/FooBar/';
125 return array(
126 array(
127 array( 'ExtensionMessagesFiles' => array( 'FooBarAlias' => 'FooBar.alias.php' ) ),
128 array( 'wgExtensionMessagesFiles' => array( 'FooBarAlias' => $dir . 'FooBar.alias.php' ) )
129 ),
130 array(
131 array(
132 'ExtensionMessagesFiles' => array(
133 'FooBarAlias' => 'FooBar.alias.php',
134 'FooBarMagic' => 'FooBar.magic.i18n.php',
135 ),
136 ),
137 array(
138 'wgExtensionMessagesFiles' => array(
139 'FooBarAlias' => $dir . 'FooBar.alias.php',
140 'FooBarMagic' => $dir . 'FooBar.magic.i18n.php',
141 ),
142 ),
143 ),
144 );
145 }
146
147 /**
148 * @covers ExtensionProcessor::extracttExtensionMessagesFiles
149 * @dataProvider provideExtracttExtensionMessagesFiles
150 */
151 public function testExtracttExtensionMessagesFiles( $input, $expected ) {
152 $processor = new ExtensionProcessor();
153 $processor->extractInfo( $this->dir, $input + self::$default, 1 );
154 $out = $processor->getExtractedInfo();
155 foreach ( $expected as $key => $value ) {
156 $this->assertEquals( $value, $out['globals'][$key] );
157 }
158 }
159
160
161 public static function provideExtractMessagesDirs() {
162 $dir = __DIR__ . '/FooBar/';
163 return array(
164 array(
165 array( 'MessagesDirs' => array( 'VisualEditor' => 'i18n' ) ),
166 array( 'wgMessagesDirs' => array( 'VisualEditor' => array( $dir . 'i18n' ) ) )
167 ),
168 array(
169 array( 'MessagesDirs' => array( 'VisualEditor' => array( 'i18n', 'foobar' ) ) ),
170 array( 'wgMessagesDirs' => array( 'VisualEditor' => array( $dir . 'i18n', $dir . 'foobar' ) ) )
171 ),
172 );
173 }
174
175 /**
176 * @covers ExtensionProcessor::extractMessagesDirs
177 * @dataProvider provideExtractMessagesDirs
178 */
179 public function testExtractMessagesDirs( $input, $expected ) {
180 $processor = new ExtensionProcessor();
181 $processor->extractInfo( $this->dir, $input + self::$default, 1 );
182 $out = $processor->getExtractedInfo();
183 foreach ( $expected as $key => $value ) {
184 $this->assertEquals( $value, $out['globals'][$key] );
185 }
186 }
187
188 /**
189 * @covers ExtensionProcessor::extractResourceLoaderModules
190 * @dataProvider provideExtractResourceLoaderModules
191 */
192 public function testExtractResourceLoaderModules( $input, $expected ) {
193 $processor = new ExtensionProcessor();
194 $processor->extractInfo( $this->dir, $input + self::$default, 1 );
195 $out = $processor->getExtractedInfo();
196 foreach ( $expected as $key => $value ) {
197 $this->assertEquals( $value, $out['globals'][$key] );
198 }
199 }
200
201 public static function provideExtractResourceLoaderModules() {
202 $dir = __DIR__ . '/FooBar/';
203 return array(
204 // Generic module with localBasePath/remoteExtPath specified
205 array(
206 // Input
207 array(
208 'ResourceModules' => array(
209 'test.foo' => array(
210 'styles' => 'foobar.js',
211 'localBasePath' => '',
212 'remoteExtPath' => 'FooBar',
213 ),
214 ),
215 ),
216 // Expected
217 array(
218 'wgResourceModules' => array(
219 'test.foo' => array(
220 'styles' => 'foobar.js',
221 'localBasePath' => $dir,
222 'remoteExtPath' => 'FooBar',
223 ),
224 ),
225 ),
226 ),
227 // ResourceFileModulePaths specified:
228 array(
229 // Input
230 array(
231 'ResourceFileModulePaths' => array(
232 'localBasePath' => '',
233 'remoteExtPath' => 'FooBar',
234 ),
235 'ResourceModules' => array(
236 // No paths
237 'test.foo' => array(
238 'styles' => 'foo.js',
239 ),
240 // Different paths set
241 'test.bar' => array(
242 'styles' => 'bar.js',
243 'localBasePath' => 'subdir',
244 'remoteExtPath' => 'FooBar/subdir',
245 ),
246 // Custom class with no paths set
247 'test.class' => array(
248 'class' => 'FooBarModule',
249 'extra' => 'argument',
250 ),
251 // Custom class with a localBasePath
252 'test.class.with.path' => array(
253 'class' => 'FooBarPathModule',
254 'extra' => 'argument',
255 'localBasePath' => '',
256 )
257 ),
258 ),
259 // Expected
260 array(
261 'wgResourceModules' => array(
262 'test.foo' => array(
263 'styles' => 'foo.js',
264 'localBasePath' => $dir,
265 'remoteExtPath' => 'FooBar',
266 ),
267 'test.bar' => array(
268 'styles' => 'bar.js',
269 'localBasePath' => $dir . 'subdir',
270 'remoteExtPath' => 'FooBar/subdir',
271 ),
272 'test.class' => array(
273 'class' => 'FooBarModule',
274 'extra' => 'argument',
275 'localBasePath' => $dir,
276 'remoteExtPath' => 'FooBar',
277 ),
278 'test.class.with.path' => array(
279 'class' => 'FooBarPathModule',
280 'extra' => 'argument',
281 'localBasePath' => $dir,
282 'remoteExtPath' => 'FooBar',
283 )
284 ),
285 ),
286 ),
287 // ResourceModuleSkinStyles with file module paths
288 array(
289 // Input
290 array(
291 'ResourceFileModulePaths' => array(
292 'localBasePath' => '',
293 'remoteSkinPath' => 'FooBar',
294 ),
295 'ResourceModuleSkinStyles' => array(
296 'foobar' => array(
297 'test.foo' => 'foo.css',
298 )
299 ),
300 ),
301 // Expected
302 array(
303 'wgResourceModuleSkinStyles' => array(
304 'foobar' => array(
305 'test.foo' => 'foo.css',
306 'localBasePath' => $dir,
307 'remoteSkinPath' => 'FooBar',
308 ),
309 ),
310 ),
311 ),
312 // ResourceModuleSkinStyles with file module paths and an override
313 array(
314 // Input
315 array(
316 'ResourceFileModulePaths' => array(
317 'localBasePath' => '',
318 'remoteSkinPath' => 'FooBar',
319 ),
320 'ResourceModuleSkinStyles' => array(
321 'foobar' => array(
322 'test.foo' => 'foo.css',
323 'remoteSkinPath' => 'BarFoo'
324 ),
325 ),
326 ),
327 // Expected
328 array(
329 'wgResourceModuleSkinStyles' => array(
330 'foobar' => array(
331 'test.foo' => 'foo.css',
332 'localBasePath' => $dir,
333 'remoteSkinPath' => 'BarFoo',
334 ),
335 ),
336 ),
337 ),
338 );
339 }
340
341 public static function provideSetToGlobal() {
342 return array(
343 array(
344 array( 'wgAPIModules', 'wgAvailableRights' ),
345 array(),
346 array(
347 'APIModules' => array( 'foobar' => 'ApiFooBar' ),
348 'AvailableRights' => array( 'foobar', 'unfoobar' ),
349 ),
350 array(
351 'wgAPIModules' => array( 'foobar' => 'ApiFooBar' ),
352 'wgAvailableRights' => array( 'foobar', 'unfoobar' ),
353 ),
354 ),
355 array(
356 array( 'wgAPIModules', 'wgAvailableRights' ),
357 array(
358 'wgAPIModules' => array( 'barbaz' => 'ApiBarBaz' ),
359 'wgAvailableRights' => array( 'barbaz' )
360 ),
361 array(
362 'APIModules' => array( 'foobar' => 'ApiFooBar' ),
363 'AvailableRights' => array( 'foobar', 'unfoobar' ),
364 ),
365 array(
366 'wgAPIModules' => array( 'barbaz' => 'ApiBarBaz', 'foobar' => 'ApiFooBar' ),
367 'wgAvailableRights' => array( 'barbaz', 'foobar', 'unfoobar' ),
368 ),
369 ),
370 array(
371 array( 'wgGroupPermissions' ),
372 array(
373 'wgGroupPermissions' => array( 'sysop' => array( 'delete' ) ),
374 ),
375 array(
376 'GroupPermissions' => array( 'sysop' => array( 'undelete' ), 'user' => array( 'edit' ) ),
377 ),
378 array(
379 'wgGroupPermissions' => array( 'sysop' => array( 'delete', 'undelete' ), 'user' => array( 'edit' ) ),
380 )
381 )
382 );
383 }
384 }
385
386
387 /**
388 * Allow overriding the default value of $this->globals
389 * so we can test merging
390 */
391 class MockExtensionProcessor extends ExtensionProcessor {
392 public function __construct( $globals = array() ) {
393 $this->globals = $globals + $this->globals;
394 }
395 }