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