Remove various double empty newlines
[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 $info2 = array(
117 'config' => array(
118 '_prefix' => 'eg',
119 'Bar' => 'somevalue'
120 ),
121 ) + self::$default;
122 $processor->extractInfo( $this->dir, $info, 1 );
123 $processor->extractInfo( $this->dir, $info2, 1 );
124 $extracted = $processor->getExtractedInfo();
125 $this->assertEquals( 'somevalue', $extracted['globals']['wgBar'] );
126 $this->assertEquals( 10, $extracted['globals']['wgFoo'] );
127 $this->assertArrayNotHasKey( 'wg@IGNORED', $extracted['globals'] );
128 // Custom prefix:
129 $this->assertEquals( 'somevalue', $extracted['globals']['egBar'] );
130 }
131
132 public static function provideExtracttExtensionMessagesFiles() {
133 $dir = __DIR__ . '/FooBar/';
134 return array(
135 array(
136 array( 'ExtensionMessagesFiles' => array( 'FooBarAlias' => 'FooBar.alias.php' ) ),
137 array( 'wgExtensionMessagesFiles' => array( 'FooBarAlias' => $dir . 'FooBar.alias.php' ) )
138 ),
139 array(
140 array(
141 'ExtensionMessagesFiles' => array(
142 'FooBarAlias' => 'FooBar.alias.php',
143 'FooBarMagic' => 'FooBar.magic.i18n.php',
144 ),
145 ),
146 array(
147 'wgExtensionMessagesFiles' => array(
148 'FooBarAlias' => $dir . 'FooBar.alias.php',
149 'FooBarMagic' => $dir . 'FooBar.magic.i18n.php',
150 ),
151 ),
152 ),
153 );
154 }
155
156 /**
157 * @covers ExtensionProcessor::extracttExtensionMessagesFiles
158 * @dataProvider provideExtracttExtensionMessagesFiles
159 */
160 public function testExtracttExtensionMessagesFiles( $input, $expected ) {
161 $processor = new ExtensionProcessor();
162 $processor->extractInfo( $this->dir, $input + self::$default, 1 );
163 $out = $processor->getExtractedInfo();
164 foreach ( $expected as $key => $value ) {
165 $this->assertEquals( $value, $out['globals'][$key] );
166 }
167 }
168
169 public static function provideExtractMessagesDirs() {
170 $dir = __DIR__ . '/FooBar/';
171 return array(
172 array(
173 array( 'MessagesDirs' => array( 'VisualEditor' => 'i18n' ) ),
174 array( 'wgMessagesDirs' => array( 'VisualEditor' => array( $dir . 'i18n' ) ) )
175 ),
176 array(
177 array( 'MessagesDirs' => array( 'VisualEditor' => array( 'i18n', 'foobar' ) ) ),
178 array( 'wgMessagesDirs' => array( 'VisualEditor' => array( $dir . 'i18n', $dir . 'foobar' ) ) )
179 ),
180 );
181 }
182
183 /**
184 * @covers ExtensionProcessor::extractMessagesDirs
185 * @dataProvider provideExtractMessagesDirs
186 */
187 public function testExtractMessagesDirs( $input, $expected ) {
188 $processor = new ExtensionProcessor();
189 $processor->extractInfo( $this->dir, $input + self::$default, 1 );
190 $out = $processor->getExtractedInfo();
191 foreach ( $expected as $key => $value ) {
192 $this->assertEquals( $value, $out['globals'][$key] );
193 }
194 }
195
196 /**
197 * @covers ExtensionProcessor::extractResourceLoaderModules
198 * @dataProvider provideExtractResourceLoaderModules
199 */
200 public function testExtractResourceLoaderModules( $input, $expected ) {
201 $processor = new ExtensionProcessor();
202 $processor->extractInfo( $this->dir, $input + self::$default, 1 );
203 $out = $processor->getExtractedInfo();
204 foreach ( $expected as $key => $value ) {
205 $this->assertEquals( $value, $out['globals'][$key] );
206 }
207 }
208
209 public static function provideExtractResourceLoaderModules() {
210 $dir = __DIR__ . '/FooBar/';
211 return array(
212 // Generic module with localBasePath/remoteExtPath specified
213 array(
214 // Input
215 array(
216 'ResourceModules' => array(
217 'test.foo' => array(
218 'styles' => 'foobar.js',
219 'localBasePath' => '',
220 'remoteExtPath' => 'FooBar',
221 ),
222 ),
223 ),
224 // Expected
225 array(
226 'wgResourceModules' => array(
227 'test.foo' => array(
228 'styles' => 'foobar.js',
229 'localBasePath' => $dir,
230 'remoteExtPath' => 'FooBar',
231 ),
232 ),
233 ),
234 ),
235 // ResourceFileModulePaths specified:
236 array(
237 // Input
238 array(
239 'ResourceFileModulePaths' => array(
240 'localBasePath' => '',
241 'remoteExtPath' => 'FooBar',
242 ),
243 'ResourceModules' => array(
244 // No paths
245 'test.foo' => array(
246 'styles' => 'foo.js',
247 ),
248 // Different paths set
249 'test.bar' => array(
250 'styles' => 'bar.js',
251 'localBasePath' => 'subdir',
252 'remoteExtPath' => 'FooBar/subdir',
253 ),
254 // Custom class with no paths set
255 'test.class' => array(
256 'class' => 'FooBarModule',
257 'extra' => 'argument',
258 ),
259 // Custom class with a localBasePath
260 'test.class.with.path' => array(
261 'class' => 'FooBarPathModule',
262 'extra' => 'argument',
263 'localBasePath' => '',
264 )
265 ),
266 ),
267 // Expected
268 array(
269 'wgResourceModules' => array(
270 'test.foo' => array(
271 'styles' => 'foo.js',
272 'localBasePath' => $dir,
273 'remoteExtPath' => 'FooBar',
274 ),
275 'test.bar' => array(
276 'styles' => 'bar.js',
277 'localBasePath' => $dir . 'subdir',
278 'remoteExtPath' => 'FooBar/subdir',
279 ),
280 'test.class' => array(
281 'class' => 'FooBarModule',
282 'extra' => 'argument',
283 'localBasePath' => $dir,
284 'remoteExtPath' => 'FooBar',
285 ),
286 'test.class.with.path' => array(
287 'class' => 'FooBarPathModule',
288 'extra' => 'argument',
289 'localBasePath' => $dir,
290 'remoteExtPath' => 'FooBar',
291 )
292 ),
293 ),
294 ),
295 // ResourceModuleSkinStyles with file module paths
296 array(
297 // Input
298 array(
299 'ResourceFileModulePaths' => array(
300 'localBasePath' => '',
301 'remoteSkinPath' => 'FooBar',
302 ),
303 'ResourceModuleSkinStyles' => array(
304 'foobar' => array(
305 'test.foo' => 'foo.css',
306 )
307 ),
308 ),
309 // Expected
310 array(
311 'wgResourceModuleSkinStyles' => array(
312 'foobar' => array(
313 'test.foo' => 'foo.css',
314 'localBasePath' => $dir,
315 'remoteSkinPath' => 'FooBar',
316 ),
317 ),
318 ),
319 ),
320 // ResourceModuleSkinStyles with file module paths and an override
321 array(
322 // Input
323 array(
324 'ResourceFileModulePaths' => array(
325 'localBasePath' => '',
326 'remoteSkinPath' => 'FooBar',
327 ),
328 'ResourceModuleSkinStyles' => array(
329 'foobar' => array(
330 'test.foo' => 'foo.css',
331 'remoteSkinPath' => 'BarFoo'
332 ),
333 ),
334 ),
335 // Expected
336 array(
337 'wgResourceModuleSkinStyles' => array(
338 'foobar' => array(
339 'test.foo' => 'foo.css',
340 'localBasePath' => $dir,
341 'remoteSkinPath' => 'BarFoo',
342 ),
343 ),
344 ),
345 ),
346 );
347 }
348
349 public static function provideSetToGlobal() {
350 return array(
351 array(
352 array( 'wgAPIModules', 'wgAvailableRights' ),
353 array(),
354 array(
355 'APIModules' => array( 'foobar' => 'ApiFooBar' ),
356 'AvailableRights' => array( 'foobar', 'unfoobar' ),
357 ),
358 array(
359 'wgAPIModules' => array( 'foobar' => 'ApiFooBar' ),
360 'wgAvailableRights' => array( 'foobar', 'unfoobar' ),
361 ),
362 ),
363 array(
364 array( 'wgAPIModules', 'wgAvailableRights' ),
365 array(
366 'wgAPIModules' => array( 'barbaz' => 'ApiBarBaz' ),
367 'wgAvailableRights' => array( 'barbaz' )
368 ),
369 array(
370 'APIModules' => array( 'foobar' => 'ApiFooBar' ),
371 'AvailableRights' => array( 'foobar', 'unfoobar' ),
372 ),
373 array(
374 'wgAPIModules' => array( 'barbaz' => 'ApiBarBaz', 'foobar' => 'ApiFooBar' ),
375 'wgAvailableRights' => array( 'barbaz', 'foobar', 'unfoobar' ),
376 ),
377 ),
378 array(
379 array( 'wgGroupPermissions' ),
380 array(
381 'wgGroupPermissions' => array(
382 'sysop' => array( 'delete' )
383 ),
384 ),
385 array(
386 'GroupPermissions' => array(
387 'sysop' => array( 'undelete' ),
388 'user' => array( 'edit' )
389 ),
390 ),
391 array(
392 'wgGroupPermissions' => array(
393 'sysop' => array( 'delete', 'undelete' ),
394 'user' => array( 'edit' )
395 ),
396 )
397 )
398 );
399 }
400 }
401
402 /**
403 * Allow overriding the default value of $this->globals
404 * so we can test merging
405 */
406 class MockExtensionProcessor extends ExtensionProcessor {
407 public function __construct( $globals = array() ) {
408 $this->globals = $globals + $this->globals;
409 }
410 }