Separate MediaWiki unit and integration tests
[lhc/web/wiklou.git] / tests / phpunit / unit / includes / HooksTest.php
1 <?php
2
3 class HooksTest extends \MediaWikiUnitTestCase {
4
5 function setUp() {
6 global $wgHooks;
7 parent::setUp();
8 Hooks::clear( 'MediaWikiHooksTest001' );
9 unset( $wgHooks['MediaWikiHooksTest001'] );
10 }
11
12 public static function provideHooks() {
13 $i = new NothingClass();
14
15 return [
16 [
17 'Object and method',
18 [ $i, 'someNonStatic' ],
19 'changed-nonstatic',
20 'changed-nonstatic'
21 ],
22 [ 'Object and no method', [ $i ], 'changed-onevent', 'original' ],
23 [
24 'Object and method with data',
25 [ $i, 'someNonStaticWithData', 'data' ],
26 'data',
27 'original'
28 ],
29 [ 'Object and static method', [ $i, 'someStatic' ], 'changed-static', 'original' ],
30 [
31 'Class::method static call',
32 [ 'NothingClass::someStatic' ],
33 'changed-static',
34 'original'
35 ],
36 [
37 'Class::method static call as array',
38 [ [ 'NothingClass::someStatic' ] ],
39 'changed-static',
40 'original'
41 ],
42 [ 'Global function', [ 'NothingFunction' ], 'changed-func', 'original' ],
43 [ 'Global function with data', [ 'NothingFunctionData', 'data' ], 'data', 'original' ],
44 [ 'Closure', [ function ( &$foo, $bar ) {
45 $foo = 'changed-closure';
46
47 return true;
48 } ], 'changed-closure', 'original' ],
49 [ 'Closure with data', [ function ( $data, &$foo, $bar ) {
50 $foo = $data;
51
52 return true;
53 }, 'data' ], 'data', 'original' ]
54 ];
55 }
56
57 /**
58 * @dataProvider provideHooks
59 * @covers Hooks::register
60 * @covers Hooks::run
61 * @covers Hooks::callHook
62 */
63 public function testNewStyleHooks( $msg, $hook, $expectedFoo, $expectedBar ) {
64 $foo = $bar = 'original';
65
66 Hooks::register( 'MediaWikiHooksTest001', $hook );
67 Hooks::run( 'MediaWikiHooksTest001', [ &$foo, &$bar ] );
68
69 $this->assertSame( $expectedFoo, $foo, $msg );
70 $this->assertSame( $expectedBar, $bar, $msg );
71 }
72
73 /**
74 * @covers Hooks::getHandlers
75 */
76 public function testGetHandlers() {
77 global $wgHooks;
78
79 $this->assertSame(
80 [],
81 Hooks::getHandlers( 'MediaWikiHooksTest001' ),
82 'No hooks registered'
83 );
84
85 $a = new NothingClass();
86 $b = new NothingClass();
87
88 $wgHooks['MediaWikiHooksTest001'][] = $a;
89
90 $this->assertSame(
91 [ $a ],
92 Hooks::getHandlers( 'MediaWikiHooksTest001' ),
93 'Hook registered by $wgHooks'
94 );
95
96 Hooks::register( 'MediaWikiHooksTest001', $b );
97 $this->assertSame(
98 [ $b, $a ],
99 Hooks::getHandlers( 'MediaWikiHooksTest001' ),
100 'Hooks::getHandlers() should return hooks registered via wgHooks as well as Hooks::register'
101 );
102
103 Hooks::clear( 'MediaWikiHooksTest001' );
104 unset( $wgHooks['MediaWikiHooksTest001'] );
105
106 Hooks::register( 'MediaWikiHooksTest001', $b );
107 $this->assertSame(
108 [ $b ],
109 Hooks::getHandlers( 'MediaWikiHooksTest001' ),
110 'Hook registered by Hook::register'
111 );
112 }
113
114 /**
115 * @covers Hooks::isRegistered
116 * @covers Hooks::register
117 * @covers Hooks::run
118 * @covers Hooks::callHook
119 */
120 public function testNewStyleHookInteraction() {
121 global $wgHooks;
122
123 $a = new NothingClass();
124 $b = new NothingClass();
125
126 $wgHooks['MediaWikiHooksTest001'][] = $a;
127 $this->assertTrue(
128 Hooks::isRegistered( 'MediaWikiHooksTest001' ),
129 'Hook registered via $wgHooks should be noticed by Hooks::isRegistered'
130 );
131
132 Hooks::register( 'MediaWikiHooksTest001', $b );
133 $this->assertEquals(
134 2,
135 count( Hooks::getHandlers( 'MediaWikiHooksTest001' ) ),
136 'Hooks::getHandlers() should return hooks registered via wgHooks as well as Hooks::register'
137 );
138
139 $foo = 'quux';
140 $bar = 'qaax';
141
142 Hooks::run( 'MediaWikiHooksTest001', [ &$foo, &$bar ] );
143 $this->assertEquals(
144 1,
145 $a->calls,
146 'Hooks::run() should run hooks registered via wgHooks as well as Hooks::register'
147 );
148 $this->assertEquals(
149 1,
150 $b->calls,
151 'Hooks::run() should run hooks registered via wgHooks as well as Hooks::register'
152 );
153 }
154
155 /**
156 * @expectedException MWException
157 * @covers Hooks::run
158 * @covers Hooks::callHook
159 */
160 public function testUncallableFunction() {
161 Hooks::register( 'MediaWikiHooksTest001', 'ThisFunctionDoesntExist' );
162 Hooks::run( 'MediaWikiHooksTest001', [] );
163 }
164
165 /**
166 * @covers Hooks::run
167 * @covers Hooks::callHook
168 */
169 public function testFalseReturn() {
170 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
171 return false;
172 } );
173 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
174 $foo = 'test';
175
176 return true;
177 } );
178 $foo = 'original';
179 Hooks::run( 'MediaWikiHooksTest001', [ &$foo ] );
180 $this->assertSame( 'original', $foo, 'Hooks abort after a false return.' );
181 }
182
183 /**
184 * @covers Hooks::run
185 */
186 public function testNullReturn() {
187 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
188 return;
189 } );
190 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
191 $foo = 'test';
192
193 return true;
194 } );
195 $foo = 'original';
196 Hooks::run( 'MediaWikiHooksTest001', [ &$foo ] );
197 $this->assertSame( 'test', $foo, 'Hooks continue after a null return.' );
198 }
199
200 /**
201 * @covers Hooks::callHook
202 */
203 public function testCallHook_FalseHook() {
204 Hooks::register( 'MediaWikiHooksTest001', false );
205 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
206 $foo = 'test';
207
208 return true;
209 } );
210 $foo = 'original';
211 Hooks::run( 'MediaWikiHooksTest001', [ &$foo ] );
212 $this->assertSame( 'test', $foo, 'Hooks that are falsey are skipped.' );
213 }
214
215 /**
216 * @covers Hooks::callHook
217 * @expectedException MWException
218 */
219 public function testCallHook_UnknownDatatype() {
220 Hooks::register( 'MediaWikiHooksTest001', 12345 );
221 Hooks::run( 'MediaWikiHooksTest001' );
222 }
223
224 /**
225 * @covers Hooks::callHook
226 * @expectedException PHPUnit_Framework_Error_Deprecated
227 */
228 public function testCallHook_Deprecated() {
229 Hooks::register( 'MediaWikiHooksTest001', 'NothingClass::someStatic' );
230 Hooks::run( 'MediaWikiHooksTest001', [], '1.31' );
231 }
232
233 /**
234 * @covers Hooks::runWithoutAbort
235 * @covers Hooks::callHook
236 */
237 public function testRunWithoutAbort() {
238 $list = [];
239 Hooks::register( 'MediaWikiHooksTest001', function ( &$list ) {
240 $list[] = 1;
241 return true; // Explicit true
242 } );
243 Hooks::register( 'MediaWikiHooksTest001', function ( &$list ) {
244 $list[] = 2;
245 return; // Implicit null
246 } );
247 Hooks::register( 'MediaWikiHooksTest001', function ( &$list ) {
248 $list[] = 3;
249 // No return
250 } );
251
252 Hooks::runWithoutAbort( 'MediaWikiHooksTest001', [ &$list ] );
253 $this->assertSame( [ 1, 2, 3 ], $list, 'All hooks ran.' );
254 }
255
256 /**
257 * @covers Hooks::runWithoutAbort
258 * @covers Hooks::callHook
259 */
260 public function testRunWithoutAbortWarning() {
261 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
262 return false;
263 } );
264 Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
265 $foo = 'test';
266 return true;
267 } );
268 $foo = 'original';
269
270 $this->setExpectedException(
271 UnexpectedValueException::class,
272 'Invalid return from hook-MediaWikiHooksTest001-closure for ' .
273 'unabortable MediaWikiHooksTest001'
274 );
275 Hooks::runWithoutAbort( 'MediaWikiHooksTest001', [ &$foo ] );
276 }
277
278 /**
279 * @expectedException FatalError
280 * @covers Hooks::run
281 */
282 public function testFatalError() {
283 Hooks::register( 'MediaWikiHooksTest001', function () {
284 return 'test';
285 } );
286 Hooks::run( 'MediaWikiHooksTest001', [] );
287 }
288 }
289
290 function NothingFunction( &$foo, &$bar ) {
291 $foo = 'changed-func';
292
293 return true;
294 }
295
296 function NothingFunctionData( $data, &$foo, &$bar ) {
297 $foo = $data;
298
299 return true;
300 }
301
302 class NothingClass {
303 public $calls = 0;
304
305 public static function someStatic( &$foo, &$bar ) {
306 $foo = 'changed-static';
307
308 return true;
309 }
310
311 public function someNonStatic( &$foo, &$bar ) {
312 $this->calls++;
313 $foo = 'changed-nonstatic';
314 $bar = 'changed-nonstatic';
315
316 return true;
317 }
318
319 public function onMediaWikiHooksTest001( &$foo, &$bar ) {
320 $this->calls++;
321 $foo = 'changed-onevent';
322
323 return true;
324 }
325
326 public function someNonStaticWithData( $data, &$foo, &$bar ) {
327 $this->calls++;
328 $foo = $data;
329
330 return true;
331 }
332 }