Merge "Drop unused FormatMetadata::flattenArray method"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiResultTest.php
1 <?php
2
3 /**
4 * @covers ApiResult
5 * @group API
6 */
7 class ApiResultTest extends MediaWikiTestCase {
8
9 /**
10 * @covers ApiResult
11 */
12 public function testStaticDataMethods() {
13 $arr = array();
14
15 ApiResult::setValue( $arr, 'setValue', '1' );
16
17 ApiResult::setValue( $arr, null, 'unnamed 1' );
18 ApiResult::setValue( $arr, null, 'unnamed 2' );
19
20 ApiResult::setValue( $arr, 'deleteValue', '2' );
21 ApiResult::unsetValue( $arr, 'deleteValue' );
22
23 ApiResult::setContentValue( $arr, 'setContentValue', '3' );
24
25 $this->assertSame( array(
26 'setValue' => '1',
27 'unnamed 1',
28 'unnamed 2',
29 ApiResult::META_CONTENT => 'setContentValue',
30 'setContentValue' => '3',
31 ), $arr );
32
33 try {
34 ApiResult::setValue( $arr, 'setValue', '99' );
35 $this->fail( 'Expected exception not thrown' );
36 } catch ( RuntimeException $ex ) {
37 $this->assertSame(
38 'Attempting to add element setValue=99, existing value is 1',
39 $ex->getMessage(),
40 'Expected exception'
41 );
42 }
43
44 try {
45 ApiResult::setContentValue( $arr, 'setContentValue2', '99' );
46 $this->fail( 'Expected exception not thrown' );
47 } catch ( RuntimeException $ex ) {
48 $this->assertSame(
49 'Attempting to set content element as setContentValue2 when setContentValue ' .
50 'is already set as the content element',
51 $ex->getMessage(),
52 'Expected exception'
53 );
54 }
55
56 ApiResult::setValue( $arr, 'setValue', '99', ApiResult::OVERRIDE );
57 $this->assertSame( '99', $arr['setValue'] );
58
59 ApiResult::setContentValue( $arr, 'setContentValue2', '99', ApiResult::OVERRIDE );
60 $this->assertSame( 'setContentValue2', $arr[ApiResult::META_CONTENT] );
61
62 $arr = array( 'foo' => 1, 'bar' => 1 );
63 ApiResult::setValue( $arr, 'top', '2', ApiResult::ADD_ON_TOP );
64 ApiResult::setValue( $arr, null, '2', ApiResult::ADD_ON_TOP );
65 ApiResult::setValue( $arr, 'bottom', '2' );
66 ApiResult::setValue( $arr, 'foo', '2', ApiResult::OVERRIDE );
67 ApiResult::setValue( $arr, 'bar', '2', ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP );
68 $this->assertSame( array( 0, 'top', 'foo', 'bar', 'bottom' ), array_keys( $arr ) );
69
70 $arr = array();
71 ApiResult::setValue( $arr, 'sub', array( 'foo' => 1 ) );
72 ApiResult::setValue( $arr, 'sub', array( 'bar' => 1 ) );
73 $this->assertSame( array( 'sub' => array( 'foo' => 1, 'bar' => 1 ) ), $arr );
74
75 try {
76 ApiResult::setValue( $arr, 'sub', array( 'foo' => 2, 'baz' => 2 ) );
77 $this->fail( 'Expected exception not thrown' );
78 } catch ( RuntimeException $ex ) {
79 $this->assertSame(
80 'Conflicting keys (foo) when attempting to merge element sub',
81 $ex->getMessage(),
82 'Expected exception'
83 );
84 }
85
86 $arr = array();
87 $title = Title::newFromText( "MediaWiki:Foobar" );
88 $obj = new stdClass;
89 $obj->foo = 1;
90 $obj->bar = 2;
91 ApiResult::setValue( $arr, 'title', $title );
92 ApiResult::setValue( $arr, 'obj', $obj );
93 $this->assertSame( array(
94 'title' => (string)$title,
95 'obj' => array( 'foo' => 1, 'bar' => 2, ApiResult::META_TYPE => 'assoc' ),
96 ), $arr );
97
98 $fh = tmpfile();
99 try {
100 ApiResult::setValue( $arr, 'file', $fh );
101 $this->fail( 'Expected exception not thrown' );
102 } catch ( InvalidArgumentException $ex ) {
103 $this->assertSame(
104 'Cannot add resource(stream) to ApiResult',
105 $ex->getMessage(),
106 'Expected exception'
107 );
108 }
109 try {
110 $obj->file = $fh;
111 ApiResult::setValue( $arr, 'sub', $obj );
112 $this->fail( 'Expected exception not thrown' );
113 } catch ( InvalidArgumentException $ex ) {
114 $this->assertSame(
115 'Cannot add resource(stream) to ApiResult',
116 $ex->getMessage(),
117 'Expected exception'
118 );
119 }
120 fclose( $fh );
121
122 try {
123 ApiResult::setValue( $arr, 'inf', INF );
124 $this->fail( 'Expected exception not thrown' );
125 } catch ( InvalidArgumentException $ex ) {
126 $this->assertSame(
127 'Cannot add non-finite floats to ApiResult',
128 $ex->getMessage(),
129 'Expected exception'
130 );
131 }
132 try {
133 ApiResult::setValue( $arr, 'nan', NAN );
134 $this->fail( 'Expected exception not thrown' );
135 } catch ( InvalidArgumentException $ex ) {
136 $this->assertSame(
137 'Cannot add non-finite floats to ApiResult',
138 $ex->getMessage(),
139 'Expected exception'
140 );
141 }
142
143 $arr = array();
144 $result2 = new ApiResult( 8388608 );
145 $result2->addValue( null, 'foo', 'bar' );
146 ApiResult::setValue( $arr, 'baz', $result2 );
147 $this->assertSame( array(
148 'baz' => array(
149 ApiResult::META_TYPE => 'assoc',
150 'foo' => 'bar',
151 )
152 ), $arr );
153
154 $arr = array();
155 ApiResult::setValue( $arr, 'foo', "foo\x80bar" );
156 ApiResult::setValue( $arr, 'bar', "a\xcc\x81" );
157 ApiResult::setValue( $arr, 'baz', 74 );
158 $this->assertSame( array(
159 'foo' => "foo\xef\xbf\xbdbar",
160 'bar' => "\xc3\xa1",
161 'baz' => 74,
162 ), $arr );
163 }
164
165 /**
166 * @covers ApiResult
167 */
168 public function testInstanceDataMethods() {
169 $result = new ApiResult( 8388608 );
170
171 $result->addValue( null, 'setValue', '1' );
172
173 $result->addValue( null, null, 'unnamed 1' );
174 $result->addValue( null, null, 'unnamed 2' );
175
176 $result->addValue( null, 'deleteValue', '2' );
177 $result->removeValue( null, 'deleteValue' );
178
179 $result->addValue( array( 'a', 'b' ), 'deleteValue', '3' );
180 $result->removeValue( array( 'a', 'b', 'deleteValue' ), null, '3' );
181
182 $result->addContentValue( null, 'setContentValue', '3' );
183
184 $this->assertSame( array(
185 'setValue' => '1',
186 'unnamed 1',
187 'unnamed 2',
188 'a' => array( 'b' => array() ),
189 'setContentValue' => '3',
190 ApiResult::META_TYPE => 'assoc',
191 ApiResult::META_CONTENT => 'setContentValue',
192 ), $result->getResultData() );
193 $this->assertSame( 20, $result->getSize() );
194
195 try {
196 $result->addValue( null, 'setValue', '99' );
197 $this->fail( 'Expected exception not thrown' );
198 } catch ( RuntimeException $ex ) {
199 $this->assertSame(
200 'Attempting to add element setValue=99, existing value is 1',
201 $ex->getMessage(),
202 'Expected exception'
203 );
204 }
205
206 try {
207 $result->addContentValue( null, 'setContentValue2', '99' );
208 $this->fail( 'Expected exception not thrown' );
209 } catch ( RuntimeException $ex ) {
210 $this->assertSame(
211 'Attempting to set content element as setContentValue2 when setContentValue ' .
212 'is already set as the content element',
213 $ex->getMessage(),
214 'Expected exception'
215 );
216 }
217
218 $result->addValue( null, 'setValue', '99', ApiResult::OVERRIDE );
219 $this->assertSame( '99', $result->getResultData( array( 'setValue' ) ) );
220
221 $result->addContentValue( null, 'setContentValue2', '99', ApiResult::OVERRIDE );
222 $this->assertSame( 'setContentValue2',
223 $result->getResultData( array( ApiResult::META_CONTENT ) ) );
224
225 $result->reset();
226 $this->assertSame( array(
227 ApiResult::META_TYPE => 'assoc',
228 ), $result->getResultData() );
229 $this->assertSame( 0, $result->getSize() );
230
231 $result->addValue( null, 'foo', 1 );
232 $result->addValue( null, 'bar', 1 );
233 $result->addValue( null, 'top', '2', ApiResult::ADD_ON_TOP );
234 $result->addValue( null, null, '2', ApiResult::ADD_ON_TOP );
235 $result->addValue( null, 'bottom', '2' );
236 $result->addValue( null, 'foo', '2', ApiResult::OVERRIDE );
237 $result->addValue( null, 'bar', '2', ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP );
238 $this->assertSame( array( 0, 'top', 'foo', 'bar', 'bottom', ApiResult::META_TYPE ),
239 array_keys( $result->getResultData() ) );
240
241 $result->reset();
242 $result->addValue( null, 'foo', array( 'bar' => 1 ) );
243 $result->addValue( array( 'foo', 'top' ), 'x', 2, ApiResult::ADD_ON_TOP );
244 $result->addValue( array( 'foo', 'bottom' ), 'x', 2 );
245 $this->assertSame( array( 'top', 'bar', 'bottom' ),
246 array_keys( $result->getResultData( array( 'foo' ) ) ) );
247
248 $result->reset();
249 $result->addValue( null, 'sub', array( 'foo' => 1 ) );
250 $result->addValue( null, 'sub', array( 'bar' => 1 ) );
251 $this->assertSame( array(
252 'sub' => array( 'foo' => 1, 'bar' => 1 ),
253 ApiResult::META_TYPE => 'assoc',
254 ), $result->getResultData() );
255
256 try {
257 $result->addValue( null, 'sub', array( 'foo' => 2, 'baz' => 2 ) );
258 $this->fail( 'Expected exception not thrown' );
259 } catch ( RuntimeException $ex ) {
260 $this->assertSame(
261 'Conflicting keys (foo) when attempting to merge element sub',
262 $ex->getMessage(),
263 'Expected exception'
264 );
265 }
266
267 $result->reset();
268 $title = Title::newFromText( "MediaWiki:Foobar" );
269 $obj = new stdClass;
270 $obj->foo = 1;
271 $obj->bar = 2;
272 $result->addValue( null, 'title', $title );
273 $result->addValue( null, 'obj', $obj );
274 $this->assertSame( array(
275 'title' => (string)$title,
276 'obj' => array( 'foo' => 1, 'bar' => 2, ApiResult::META_TYPE => 'assoc' ),
277 ApiResult::META_TYPE => 'assoc',
278 ), $result->getResultData() );
279
280 $fh = tmpfile();
281 try {
282 $result->addValue( null, 'file', $fh );
283 $this->fail( 'Expected exception not thrown' );
284 } catch ( InvalidArgumentException $ex ) {
285 $this->assertSame(
286 'Cannot add resource(stream) to ApiResult',
287 $ex->getMessage(),
288 'Expected exception'
289 );
290 }
291 try {
292 $obj->file = $fh;
293 $result->addValue( null, 'sub', $obj );
294 $this->fail( 'Expected exception not thrown' );
295 } catch ( InvalidArgumentException $ex ) {
296 $this->assertSame(
297 'Cannot add resource(stream) to ApiResult',
298 $ex->getMessage(),
299 'Expected exception'
300 );
301 }
302 fclose( $fh );
303
304 try {
305 $result->addValue( null, 'inf', INF );
306 $this->fail( 'Expected exception not thrown' );
307 } catch ( InvalidArgumentException $ex ) {
308 $this->assertSame(
309 'Cannot add non-finite floats to ApiResult',
310 $ex->getMessage(),
311 'Expected exception'
312 );
313 }
314 try {
315 $result->addValue( null, 'nan', NAN );
316 $this->fail( 'Expected exception not thrown' );
317 } catch ( InvalidArgumentException $ex ) {
318 $this->assertSame(
319 'Cannot add non-finite floats to ApiResult',
320 $ex->getMessage(),
321 'Expected exception'
322 );
323 }
324
325 $result->reset();
326 $result->addParsedLimit( 'foo', 12 );
327 $this->assertSame( array(
328 'limits' => array( 'foo' => 12 ),
329 ApiResult::META_TYPE => 'assoc',
330 ), $result->getResultData() );
331 $result->addParsedLimit( 'foo', 13 );
332 $this->assertSame( array(
333 'limits' => array( 'foo' => 13 ),
334 ApiResult::META_TYPE => 'assoc',
335 ), $result->getResultData() );
336 $this->assertSame( null, $result->getResultData( array( 'foo', 'bar', 'baz' ) ) );
337 $this->assertSame( 13, $result->getResultData( array( 'limits', 'foo' ) ) );
338 try {
339 $result->getResultData( array( 'limits', 'foo', 'bar' ) );
340 $this->fail( 'Expected exception not thrown' );
341 } catch ( InvalidArgumentException $ex ) {
342 $this->assertSame(
343 'Path limits.foo is not an array',
344 $ex->getMessage(),
345 'Expected exception'
346 );
347 }
348
349 $result = new ApiResult( 10 );
350 $formatter = new ApiErrorFormatter( $result, Language::factory( 'en' ), 'none', false );
351 $result->setErrorFormatter( $formatter );
352 $this->assertFalse( $result->addValue( null, 'foo', '12345678901' ) );
353 $this->assertTrue( $result->addValue( null, 'foo', '12345678901', ApiResult::NO_SIZE_CHECK ) );
354 $this->assertSame( 0, $result->getSize() );
355 $result->reset();
356 $this->assertTrue( $result->addValue( null, 'foo', '1234567890' ) );
357 $this->assertFalse( $result->addValue( null, 'foo', '1' ) );
358 $result->removeValue( null, 'foo' );
359 $this->assertTrue( $result->addValue( null, 'foo', '1' ) );
360
361 $result = new ApiResult( 8388608 );
362 $result2 = new ApiResult( 8388608 );
363 $result2->addValue( null, 'foo', 'bar' );
364 $result->addValue( null, 'baz', $result2 );
365 $this->assertSame( array(
366 'baz' => array(
367 'foo' => 'bar',
368 ApiResult::META_TYPE => 'assoc',
369 ),
370 ApiResult::META_TYPE => 'assoc',
371 ), $result->getResultData() );
372
373 $result = new ApiResult( 8388608 );
374 $result->addValue( null, 'foo', "foo\x80bar" );
375 $result->addValue( null, 'bar', "a\xcc\x81" );
376 $result->addValue( null, 'baz', 74 );
377 $this->assertSame( array(
378 'foo' => "foo\xef\xbf\xbdbar",
379 'bar' => "\xc3\xa1",
380 'baz' => 74,
381 ApiResult::META_TYPE => 'assoc',
382 ), $result->getResultData() );
383 }
384
385 /**
386 * @covers ApiResult
387 */
388 public function testMetadata() {
389 $arr = array( 'foo' => array( 'bar' => array() ) );
390 $result = new ApiResult( 8388608 );
391 $result->addValue( null, 'foo', array( 'bar' => array() ) );
392
393 $expect = array(
394 'foo' => array(
395 'bar' => array(
396 ApiResult::META_INDEXED_TAG_NAME => 'ritn',
397 ApiResult::META_TYPE => 'default',
398 ),
399 ApiResult::META_INDEXED_TAG_NAME => 'ritn',
400 ApiResult::META_TYPE => 'default',
401 ),
402 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
403 ApiResult::META_INDEXED_TAG_NAME => 'itn',
404 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar' ),
405 ApiResult::META_TYPE => 'array',
406 );
407
408 ApiResult::setSubelementsList( $arr, 'foo' );
409 ApiResult::setSubelementsList( $arr, array( 'bar', 'baz' ) );
410 ApiResult::unsetSubelementsList( $arr, 'baz' );
411 ApiResult::setIndexedTagNameRecursive( $arr, 'ritn' );
412 ApiResult::setIndexedTagName( $arr, 'itn' );
413 ApiResult::setPreserveKeysList( $arr, 'foo' );
414 ApiResult::setPreserveKeysList( $arr, array( 'bar', 'baz' ) );
415 ApiResult::unsetPreserveKeysList( $arr, 'baz' );
416 ApiResult::setArrayTypeRecursive( $arr, 'default' );
417 ApiResult::setArrayType( $arr, 'array' );
418 $this->assertSame( $expect, $arr );
419
420 $result->addSubelementsList( null, 'foo' );
421 $result->addSubelementsList( null, array( 'bar', 'baz' ) );
422 $result->removeSubelementsList( null, 'baz' );
423 $result->addIndexedTagNameRecursive( null, 'ritn' );
424 $result->addIndexedTagName( null, 'itn' );
425 $result->addPreserveKeysList( null, 'foo' );
426 $result->addPreserveKeysList( null, array( 'bar', 'baz' ) );
427 $result->removePreserveKeysList( null, 'baz' );
428 $result->addArrayTypeRecursive( null, 'default' );
429 $result->addArrayType( null, 'array' );
430 $this->assertEquals( $expect, $result->getResultData() );
431
432 $arr = array( 'foo' => array( 'bar' => array() ) );
433 $expect = array(
434 'foo' => array(
435 'bar' => array(
436 ApiResult::META_TYPE => 'kvp',
437 ApiResult::META_KVP_KEY_NAME => 'key',
438 ),
439 ApiResult::META_TYPE => 'kvp',
440 ApiResult::META_KVP_KEY_NAME => 'key',
441 ),
442 ApiResult::META_TYPE => 'BCkvp',
443 ApiResult::META_KVP_KEY_NAME => 'bc',
444 );
445 ApiResult::setArrayTypeRecursive( $arr, 'kvp', 'key' );
446 ApiResult::setArrayType( $arr, 'BCkvp', 'bc' );
447 $this->assertSame( $expect, $arr );
448 }
449
450 /**
451 * @covers ApiResult
452 */
453 public function testUtilityFunctions() {
454 $arr = array(
455 'foo' => array(
456 'bar' => array( '_dummy' => 'foobaz' ),
457 'bar2' => (object)array( '_dummy' => 'foobaz' ),
458 'x' => 'ok',
459 '_dummy' => 'foobaz',
460 ),
461 'foo2' => (object)array(
462 'bar' => array( '_dummy' => 'foobaz' ),
463 'bar2' => (object)array( '_dummy' => 'foobaz' ),
464 'x' => 'ok',
465 '_dummy' => 'foobaz',
466 ),
467 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
468 ApiResult::META_INDEXED_TAG_NAME => 'itn',
469 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ),
470 ApiResult::META_TYPE => 'array',
471 '_dummy' => 'foobaz',
472 '_dummy2' => 'foobaz!',
473 );
474 $this->assertEquals( array(
475 'foo' => array(
476 'bar' => array(),
477 'bar2' => (object)array(),
478 'x' => 'ok',
479 ),
480 'foo2' => (object)array(
481 'bar' => array(),
482 'bar2' => (object)array(),
483 'x' => 'ok',
484 ),
485 '_dummy2' => 'foobaz!',
486 ), ApiResult::stripMetadata( $arr ), 'ApiResult::stripMetadata' );
487
488 $metadata = array();
489 $data = ApiResult::stripMetadataNonRecursive( $arr, $metadata );
490 $this->assertEquals( array(
491 'foo' => array(
492 'bar' => array( '_dummy' => 'foobaz' ),
493 'bar2' => (object)array( '_dummy' => 'foobaz' ),
494 'x' => 'ok',
495 '_dummy' => 'foobaz',
496 ),
497 'foo2' => (object)array(
498 'bar' => array( '_dummy' => 'foobaz' ),
499 'bar2' => (object)array( '_dummy' => 'foobaz' ),
500 'x' => 'ok',
501 '_dummy' => 'foobaz',
502 ),
503 '_dummy2' => 'foobaz!',
504 ), $data, 'ApiResult::stripMetadataNonRecursive ($data)' );
505 $this->assertEquals( array(
506 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
507 ApiResult::META_INDEXED_TAG_NAME => 'itn',
508 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ),
509 ApiResult::META_TYPE => 'array',
510 '_dummy' => 'foobaz',
511 ), $metadata, 'ApiResult::stripMetadataNonRecursive ($metadata)' );
512
513 $metadata = null;
514 $data = ApiResult::stripMetadataNonRecursive( (object)$arr, $metadata );
515 $this->assertEquals( (object)array(
516 'foo' => array(
517 'bar' => array( '_dummy' => 'foobaz' ),
518 'bar2' => (object)array( '_dummy' => 'foobaz' ),
519 'x' => 'ok',
520 '_dummy' => 'foobaz',
521 ),
522 'foo2' => (object)array(
523 'bar' => array( '_dummy' => 'foobaz' ),
524 'bar2' => (object)array( '_dummy' => 'foobaz' ),
525 'x' => 'ok',
526 '_dummy' => 'foobaz',
527 ),
528 '_dummy2' => 'foobaz!',
529 ), $data, 'ApiResult::stripMetadataNonRecursive on object ($data)' );
530 $this->assertEquals( array(
531 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
532 ApiResult::META_INDEXED_TAG_NAME => 'itn',
533 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ),
534 ApiResult::META_TYPE => 'array',
535 '_dummy' => 'foobaz',
536 ), $metadata, 'ApiResult::stripMetadataNonRecursive on object ($metadata)' );
537 }
538
539 /**
540 * @covers ApiResult
541 * @dataProvider provideTransformations
542 * @param string $label
543 * @param array $input
544 * @param array $transforms
545 * @param array|Exception $expect
546 */
547 public function testTransformations( $label, $input, $transforms, $expect ) {
548 $result = new ApiResult( false );
549 $result->addValue( null, 'test', $input );
550
551 if ( $expect instanceof Exception ) {
552 try {
553 $output = $result->getResultData( 'test', $transforms );
554 $this->fail( 'Expected exception not thrown', $label );
555 } catch ( Exception $ex ) {
556 $this->assertEquals( $ex, $expect, $label );
557 }
558 } else {
559 $output = $result->getResultData( 'test', $transforms );
560 $this->assertEquals( $expect, $output, $label );
561 }
562 }
563
564 public function provideTransformations() {
565 $kvp = function ( $keyKey, $key, $valKey, $value ) {
566 return array(
567 $keyKey => $key,
568 $valKey => $value,
569 ApiResult::META_PRESERVE_KEYS => array( $keyKey ),
570 ApiResult::META_CONTENT => $valKey,
571 ApiResult::META_TYPE => 'assoc',
572 );
573 };
574 $typeArr = array(
575 'defaultArray' => array( 2 => 'a', 0 => 'b', 1 => 'c' ),
576 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c' ),
577 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c' ),
578 'array' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'array' ),
579 'BCarray' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'BCarray' ),
580 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'BCassoc' ),
581 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ),
582 'kvp' => array( 'x' => 'a', 'y' => 'b', 'z' => array( 'c' ), ApiResult::META_TYPE => 'kvp' ),
583 'BCkvp' => array( 'x' => 'a', 'y' => 'b',
584 ApiResult::META_TYPE => 'BCkvp',
585 ApiResult::META_KVP_KEY_NAME => 'key',
586 ),
587 'emptyDefault' => array( '_dummy' => 1 ),
588 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ),
589 '_dummy' => 1,
590 ApiResult::META_PRESERVE_KEYS => array( '_dummy' ),
591 );
592 $stripArr = array(
593 'foo' => array(
594 'bar' => array( '_dummy' => 'foobaz' ),
595 'baz' => array(
596 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
597 ApiResult::META_INDEXED_TAG_NAME => 'itn',
598 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ),
599 ApiResult::META_TYPE => 'array',
600 ),
601 'x' => 'ok',
602 '_dummy' => 'foobaz',
603 ),
604 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
605 ApiResult::META_INDEXED_TAG_NAME => 'itn',
606 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ),
607 ApiResult::META_TYPE => 'array',
608 '_dummy' => 'foobaz',
609 '_dummy2' => 'foobaz!',
610 );
611
612 return array(
613 array(
614 'BC: META_BC_BOOLS',
615 array(
616 'BCtrue' => true,
617 'BCfalse' => false,
618 'true' => true,
619 'false' => false,
620 ApiResult::META_BC_BOOLS => array( 0, 'true', 'false' ),
621 ),
622 array( 'BC' => array() ),
623 array(
624 'BCtrue' => '',
625 'true' => true,
626 'false' => false,
627 ApiResult::META_BC_BOOLS => array( 0, 'true', 'false' ),
628 )
629 ),
630 array(
631 'BC: META_BC_SUBELEMENTS',
632 array(
633 'bc' => 'foo',
634 'nobc' => 'bar',
635 ApiResult::META_BC_SUBELEMENTS => array( 'bc' ),
636 ),
637 array( 'BC' => array() ),
638 array(
639 'bc' => array(
640 '*' => 'foo',
641 ApiResult::META_CONTENT => '*',
642 ApiResult::META_TYPE => 'assoc',
643 ),
644 'nobc' => 'bar',
645 ApiResult::META_BC_SUBELEMENTS => array( 'bc' ),
646 ),
647 ),
648 array(
649 'BC: META_CONTENT',
650 array(
651 'content' => '!!!',
652 ApiResult::META_CONTENT => 'content',
653 ),
654 array( 'BC' => array() ),
655 array(
656 '*' => '!!!',
657 ApiResult::META_CONTENT => '*',
658 ),
659 ),
660 array(
661 'BC: BCkvp type',
662 array(
663 'foo' => 'foo value',
664 'bar' => 'bar value',
665 '_baz' => 'baz value',
666 ApiResult::META_TYPE => 'BCkvp',
667 ApiResult::META_KVP_KEY_NAME => 'key',
668 ApiResult::META_PRESERVE_KEYS => array( '_baz' ),
669 ),
670 array( 'BC' => array() ),
671 array(
672 $kvp( 'key', 'foo', '*', 'foo value' ),
673 $kvp( 'key', 'bar', '*', 'bar value' ),
674 $kvp( 'key', '_baz', '*', 'baz value' ),
675 ApiResult::META_TYPE => 'array',
676 ApiResult::META_KVP_KEY_NAME => 'key',
677 ApiResult::META_PRESERVE_KEYS => array( '_baz' ),
678 ),
679 ),
680 array(
681 'BC: BCarray type',
682 array(
683 ApiResult::META_TYPE => 'BCarray',
684 ),
685 array( 'BC' => array() ),
686 array(
687 ApiResult::META_TYPE => 'default',
688 ),
689 ),
690 array(
691 'BC: BCassoc type',
692 array(
693 ApiResult::META_TYPE => 'BCassoc',
694 ),
695 array( 'BC' => array() ),
696 array(
697 ApiResult::META_TYPE => 'default',
698 ),
699 ),
700 array(
701 'BC: BCkvp exception',
702 array(
703 ApiResult::META_TYPE => 'BCkvp',
704 ),
705 array( 'BC' => array() ),
706 new UnexpectedValueException(
707 'Type "BCkvp" used without setting ApiResult::META_KVP_KEY_NAME metadata item'
708 ),
709 ),
710 array(
711 'BC: nobool, no*, nosub',
712 array(
713 'true' => true,
714 'false' => false,
715 'content' => 'content',
716 ApiResult::META_CONTENT => 'content',
717 'bc' => 'foo',
718 ApiResult::META_BC_SUBELEMENTS => array( 'bc' ),
719 'BCarray' => array( ApiResult::META_TYPE => 'BCarray' ),
720 'BCassoc' => array( ApiResult::META_TYPE => 'BCassoc' ),
721 'BCkvp' => array(
722 'foo' => 'foo value',
723 'bar' => 'bar value',
724 '_baz' => 'baz value',
725 ApiResult::META_TYPE => 'BCkvp',
726 ApiResult::META_KVP_KEY_NAME => 'key',
727 ApiResult::META_PRESERVE_KEYS => array( '_baz' ),
728 ),
729 ),
730 array( 'BC' => array( 'nobool', 'no*', 'nosub' ) ),
731 array(
732 'true' => true,
733 'false' => false,
734 'content' => 'content',
735 'bc' => 'foo',
736 'BCarray' => array( ApiResult::META_TYPE => 'default' ),
737 'BCassoc' => array( ApiResult::META_TYPE => 'default' ),
738 'BCkvp' => array(
739 $kvp( 'key', 'foo', '*', 'foo value' ),
740 $kvp( 'key', 'bar', '*', 'bar value' ),
741 $kvp( 'key', '_baz', '*', 'baz value' ),
742 ApiResult::META_TYPE => 'array',
743 ApiResult::META_KVP_KEY_NAME => 'key',
744 ApiResult::META_PRESERVE_KEYS => array( '_baz' ),
745 ),
746 ApiResult::META_CONTENT => 'content',
747 ApiResult::META_BC_SUBELEMENTS => array( 'bc' ),
748 ),
749 ),
750
751 array(
752 'Types: Normal transform',
753 $typeArr,
754 array( 'Types' => array() ),
755 array(
756 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ),
757 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
758 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
759 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
760 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
761 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ),
762 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ),
763 'kvp' => array( 'x' => 'a', 'y' => 'b',
764 'z' => array( 'c', ApiResult::META_TYPE => 'array' ),
765 ApiResult::META_TYPE => 'assoc'
766 ),
767 'BCkvp' => array( 'x' => 'a', 'y' => 'b',
768 ApiResult::META_TYPE => 'assoc',
769 ApiResult::META_KVP_KEY_NAME => 'key',
770 ),
771 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ),
772 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ),
773 '_dummy' => 1,
774 ApiResult::META_PRESERVE_KEYS => array( '_dummy' ),
775 ApiResult::META_TYPE => 'assoc',
776 ),
777 ),
778 array(
779 'Types: AssocAsObject',
780 $typeArr,
781 array( 'Types' => array( 'AssocAsObject' => true ) ),
782 (object)array(
783 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ),
784 'defaultAssoc' => (object)array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
785 'defaultAssoc2' => (object)array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
786 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
787 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
788 'BCassoc' => (object)array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ),
789 'assoc' => (object)array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ),
790 'kvp' => (object)array( 'x' => 'a', 'y' => 'b',
791 'z' => array( 'c', ApiResult::META_TYPE => 'array' ),
792 ApiResult::META_TYPE => 'assoc'
793 ),
794 'BCkvp' => (object)array( 'x' => 'a', 'y' => 'b',
795 ApiResult::META_TYPE => 'assoc',
796 ApiResult::META_KVP_KEY_NAME => 'key',
797 ),
798 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ),
799 'emptyAssoc' => (object)array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ),
800 '_dummy' => 1,
801 ApiResult::META_PRESERVE_KEYS => array( '_dummy' ),
802 ApiResult::META_TYPE => 'assoc',
803 ),
804 ),
805 array(
806 'Types: ArmorKVP',
807 $typeArr,
808 array( 'Types' => array( 'ArmorKVP' => 'name' ) ),
809 array(
810 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ),
811 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
812 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
813 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
814 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
815 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ),
816 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ),
817 'kvp' => array(
818 $kvp( 'name', 'x', 'value', 'a' ),
819 $kvp( 'name', 'y', 'value', 'b' ),
820 $kvp( 'name', 'z', 'value', array( 'c', ApiResult::META_TYPE => 'array' ) ),
821 ApiResult::META_TYPE => 'array'
822 ),
823 'BCkvp' => array(
824 $kvp( 'key', 'x', 'value', 'a' ),
825 $kvp( 'key', 'y', 'value', 'b' ),
826 ApiResult::META_TYPE => 'array',
827 ApiResult::META_KVP_KEY_NAME => 'key',
828 ),
829 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ),
830 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ),
831 '_dummy' => 1,
832 ApiResult::META_PRESERVE_KEYS => array( '_dummy' ),
833 ApiResult::META_TYPE => 'assoc',
834 ),
835 ),
836 array(
837 'Types: ArmorKVP + BC',
838 $typeArr,
839 array( 'BC' => array(), 'Types' => array( 'ArmorKVP' => 'name' ) ),
840 array(
841 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ),
842 'defaultAssoc' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
843 'defaultAssoc2' => array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
844 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
845 'BCarray' => array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
846 'BCassoc' => array( 'a', 'b', 'c', ApiResult::META_TYPE => 'array' ),
847 'assoc' => array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ),
848 'kvp' => array(
849 $kvp( 'name', 'x', '*', 'a' ),
850 $kvp( 'name', 'y', '*', 'b' ),
851 $kvp( 'name', 'z', '*', array( 'c', ApiResult::META_TYPE => 'array' ) ),
852 ApiResult::META_TYPE => 'array'
853 ),
854 'BCkvp' => array(
855 $kvp( 'key', 'x', '*', 'a' ),
856 $kvp( 'key', 'y', '*', 'b' ),
857 ApiResult::META_TYPE => 'array',
858 ApiResult::META_KVP_KEY_NAME => 'key',
859 ),
860 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ),
861 'emptyAssoc' => array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ),
862 '_dummy' => 1,
863 ApiResult::META_PRESERVE_KEYS => array( '_dummy' ),
864 ApiResult::META_TYPE => 'assoc',
865 ),
866 ),
867 array(
868 'Types: ArmorKVP + AssocAsObject',
869 $typeArr,
870 array( 'Types' => array( 'ArmorKVP' => 'name', 'AssocAsObject' => true ) ),
871 (object)array(
872 'defaultArray' => array( 'b', 'c', 'a', ApiResult::META_TYPE => 'array' ),
873 'defaultAssoc' => (object)array( 'x' => 'a', 1 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
874 'defaultAssoc2' => (object)array( 2 => 'a', 3 => 'b', 0 => 'c', ApiResult::META_TYPE => 'assoc' ),
875 'array' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
876 'BCarray' => array( 'a', 'c', 'b', ApiResult::META_TYPE => 'array' ),
877 'BCassoc' => (object)array( 'a', 'b', 'c', ApiResult::META_TYPE => 'assoc' ),
878 'assoc' => (object)array( 2 => 'a', 0 => 'b', 1 => 'c', ApiResult::META_TYPE => 'assoc' ),
879 'kvp' => array(
880 (object)$kvp( 'name', 'x', 'value', 'a' ),
881 (object)$kvp( 'name', 'y', 'value', 'b' ),
882 (object)$kvp( 'name', 'z', 'value', array( 'c', ApiResult::META_TYPE => 'array' ) ),
883 ApiResult::META_TYPE => 'array'
884 ),
885 'BCkvp' => array(
886 (object)$kvp( 'key', 'x', 'value', 'a' ),
887 (object)$kvp( 'key', 'y', 'value', 'b' ),
888 ApiResult::META_TYPE => 'array',
889 ApiResult::META_KVP_KEY_NAME => 'key',
890 ),
891 'emptyDefault' => array( '_dummy' => 1, ApiResult::META_TYPE => 'array' ),
892 'emptyAssoc' => (object)array( '_dummy' => 1, ApiResult::META_TYPE => 'assoc' ),
893 '_dummy' => 1,
894 ApiResult::META_PRESERVE_KEYS => array( '_dummy' ),
895 ApiResult::META_TYPE => 'assoc',
896 ),
897 ),
898 array(
899 'Types: BCkvp exception',
900 array(
901 ApiResult::META_TYPE => 'BCkvp',
902 ),
903 array( 'Types' => array() ),
904 new UnexpectedValueException(
905 'Type "BCkvp" used without setting ApiResult::META_KVP_KEY_NAME metadata item'
906 ),
907 ),
908
909 array(
910 'Strip: With ArmorKVP + AssocAsObject transforms',
911 $typeArr,
912 array( 'Types' => array( 'ArmorKVP' => 'name', 'AssocAsObject' => true ), 'Strip' => 'all' ),
913 (object)array(
914 'defaultArray' => array( 'b', 'c', 'a' ),
915 'defaultAssoc' => (object)array( 'x' => 'a', 1 => 'b', 0 => 'c' ),
916 'defaultAssoc2' => (object)array( 2 => 'a', 3 => 'b', 0 => 'c' ),
917 'array' => array( 'a', 'c', 'b' ),
918 'BCarray' => array( 'a', 'c', 'b' ),
919 'BCassoc' => (object)array( 'a', 'b', 'c' ),
920 'assoc' => (object)array( 2 => 'a', 0 => 'b', 1 => 'c' ),
921 'kvp' => array(
922 (object)array( 'name' => 'x', 'value' => 'a' ),
923 (object)array( 'name' => 'y', 'value' => 'b' ),
924 (object)array( 'name' => 'z', 'value' => array( 'c' ) ),
925 ),
926 'BCkvp' => array(
927 (object)array( 'key' => 'x', 'value' => 'a' ),
928 (object)array( 'key' => 'y', 'value' => 'b' ),
929 ),
930 'emptyDefault' => array(),
931 'emptyAssoc' => (object)array(),
932 '_dummy' => 1,
933 ),
934 ),
935
936 array(
937 'Strip: all',
938 $stripArr,
939 array( 'Strip' => 'all' ),
940 array(
941 'foo' => array(
942 'bar' => array(),
943 'baz' => array(),
944 'x' => 'ok',
945 ),
946 '_dummy2' => 'foobaz!',
947 ),
948 ),
949 array(
950 'Strip: base',
951 $stripArr,
952 array( 'Strip' => 'base' ),
953 array(
954 'foo' => array(
955 'bar' => array( '_dummy' => 'foobaz' ),
956 'baz' => array(
957 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
958 ApiResult::META_INDEXED_TAG_NAME => 'itn',
959 ApiResult::META_PRESERVE_KEYS => array( 'foo', 'bar', '_dummy2', 0 ),
960 ApiResult::META_TYPE => 'array',
961 ),
962 'x' => 'ok',
963 '_dummy' => 'foobaz',
964 ),
965 '_dummy2' => 'foobaz!',
966 ),
967 ),
968 array(
969 'Strip: bc',
970 $stripArr,
971 array( 'Strip' => 'bc' ),
972 array(
973 'foo' => array(
974 'bar' => array(),
975 'baz' => array(
976 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
977 ApiResult::META_INDEXED_TAG_NAME => 'itn',
978 ),
979 'x' => 'ok',
980 ),
981 '_dummy2' => 'foobaz!',
982 ApiResult::META_SUBELEMENTS => array( 'foo', 'bar' ),
983 ApiResult::META_INDEXED_TAG_NAME => 'itn',
984 ),
985 ),
986
987 array(
988 'Custom transform',
989 array(
990 'foo' => '?',
991 'bar' => '?',
992 '_dummy' => '?',
993 '_dummy2' => '?',
994 '_dummy3' => '?',
995 ApiResult::META_CONTENT => 'foo',
996 ApiResult::META_PRESERVE_KEYS => array( '_dummy2', '_dummy3' ),
997 ),
998 array(
999 'Custom' => array( $this, 'customTransform' ),
1000 'BC' => array(),
1001 'Types' => array(),
1002 'Strip' => 'all'
1003 ),
1004 array(
1005 '*' => 'FOO',
1006 'bar' => 'BAR',
1007 'baz' => array( 'a', 'b' ),
1008 '_dummy2' => '_DUMMY2',
1009 '_dummy3' => '_DUMMY3',
1010 ApiResult::META_CONTENT => 'bar',
1011 ),
1012 ),
1013 );
1014
1015 }
1016
1017 /**
1018 * Custom transformer for testTransformations
1019 * @param array &$data
1020 * @param array &$metadata
1021 */
1022 public function customTransform( &$data, &$metadata ) {
1023 // Prevent recursion
1024 if ( isset( $metadata['_added'] ) ) {
1025 $metadata[ApiResult::META_TYPE] = 'array';
1026 return;
1027 }
1028
1029 foreach ( $data as $k => $v ) {
1030 $data[$k] = strtoupper( $k );
1031 }
1032 $data['baz'] = array( '_added' => 1, 'z' => 'b', 'y' => 'a' );
1033 $metadata[ApiResult::META_PRESERVE_KEYS][0] = '_dummy';
1034 $data[ApiResult::META_CONTENT] = 'bar';
1035 }
1036
1037 /**
1038 * @covers ApiResult
1039 */
1040 public function testDeprecatedFunctions() {
1041 // Ignore ApiResult deprecation warnings during this test
1042 set_error_handler( function ( $errno, $errstr ) use ( &$warnings ) {
1043 if ( preg_match( '/Use of ApiResult::\S+ was deprecated in MediaWiki \d+.\d+\./', $errstr ) ) {
1044 return true;
1045 }
1046 if ( preg_match( '/Use of ApiMain to ApiResult::__construct was deprecated in MediaWiki \d+.\d+\./', $errstr ) ) {
1047 return true;
1048 }
1049 return false;
1050 } );
1051 $reset = new ScopedCallback( 'restore_error_handler' );
1052
1053 $context = new DerivativeContext( RequestContext::getMain() );
1054 $context->setConfig( new HashConfig( array(
1055 'APIModules' => array(),
1056 'APIFormatModules' => array(),
1057 'APIMaxResultSize' => 42,
1058 ) ) );
1059 $main = new ApiMain( $context );
1060 $result = TestingAccessWrapper::newFromObject( new ApiResult( $main ) );
1061 $this->assertSame( 42, $result->maxSize );
1062 $this->assertSame( $main->getErrorFormatter(), $result->errorFormatter );
1063 $this->assertSame( $main, $result->mainForContinuation );
1064
1065 $result = new ApiResult( 8388608 );
1066
1067 $result->addContentValue( null, 'test', 'content' );
1068 $result->addContentValue( array( 'foo', 'bar' ), 'test', 'content' );
1069 $result->addIndexedTagName( null, 'itn' );
1070 $result->addSubelementsList( null, array( 'sub' ) );
1071 $this->assertSame( array(
1072 'foo' => array(
1073 'bar' => array(
1074 '*' => 'content',
1075 ),
1076 ),
1077 '*' => 'content',
1078 ), $result->getData() );
1079 $result->setRawMode();
1080 $this->assertSame( array(
1081 'foo' => array(
1082 'bar' => array(
1083 '*' => 'content',
1084 ),
1085 ),
1086 '*' => 'content',
1087 '_element' => 'itn',
1088 '_subelements' => array( 'sub' ),
1089 ), $result->getData() );
1090
1091 $arr = array();
1092 ApiResult::setContent( $arr, 'value' );
1093 ApiResult::setContent( $arr, 'value2', 'foobar' );
1094 $this->assertSame( array(
1095 ApiResult::META_CONTENT => 'content',
1096 'content' => 'value',
1097 'foobar' => array(
1098 ApiResult::META_CONTENT => 'content',
1099 'content' => 'value2',
1100 ),
1101 ), $arr );
1102
1103 $result = new ApiResult( 3 );
1104 $formatter = new ApiErrorFormatter_BackCompat( $result );
1105 $result->setErrorFormatter( $formatter );
1106 $result->disableSizeCheck();
1107 $this->assertTrue( $result->addValue( null, 'foo', '1234567890' ) );
1108 $result->enableSizeCheck();
1109 $this->assertSame( 0, $result->getSize() );
1110 $this->assertFalse( $result->addValue( null, 'foo', '1234567890' ) );
1111
1112 $arr = array( 'foo' => array( 'bar' => 1 ) );
1113 $result->setIndexedTagName_recursive( $arr, 'itn' );
1114 $this->assertSame( array(
1115 'foo' => array(
1116 'bar' => 1,
1117 ApiResult::META_INDEXED_TAG_NAME => 'itn'
1118 ),
1119 ), $arr );
1120
1121 $status = Status::newGood();
1122 $status->fatal( 'parentheses', '1' );
1123 $status->fatal( 'parentheses', '2' );
1124 $status->warning( 'parentheses', '3' );
1125 $status->warning( 'parentheses', '4' );
1126 $this->assertSame( array(
1127 array(
1128 'type' => 'error',
1129 'message' => 'parentheses',
1130 'params' => array(
1131 0 => '1',
1132 ApiResult::META_INDEXED_TAG_NAME => 'param',
1133 ),
1134 ),
1135 array(
1136 'type' => 'error',
1137 'message' => 'parentheses',
1138 'params' => array(
1139 0 => '2',
1140 ApiResult::META_INDEXED_TAG_NAME => 'param',
1141 ),
1142 ),
1143 ApiResult::META_INDEXED_TAG_NAME => 'error',
1144 ), $result->convertStatusToArray( $status, 'error' ) );
1145 $this->assertSame( array(
1146 array(
1147 'type' => 'warning',
1148 'message' => 'parentheses',
1149 'params' => array(
1150 0 => '3',
1151 ApiResult::META_INDEXED_TAG_NAME => 'param',
1152 ),
1153 ),
1154 array(
1155 'type' => 'warning',
1156 'message' => 'parentheses',
1157 'params' => array(
1158 0 => '4',
1159 ApiResult::META_INDEXED_TAG_NAME => 'param',
1160 ),
1161 ),
1162 ApiResult::META_INDEXED_TAG_NAME => 'warning',
1163 ), $result->convertStatusToArray( $status, 'warning' ) );
1164 }
1165
1166 /**
1167 * @covers ApiResult
1168 */
1169 public function testDeprecatedContinuation() {
1170 // Ignore ApiResult deprecation warnings during this test
1171 set_error_handler( function ( $errno, $errstr ) use ( &$warnings ) {
1172 if ( preg_match( '/Use of ApiResult::\S+ was deprecated in MediaWiki \d+.\d+\./', $errstr ) ) {
1173 return true;
1174 }
1175 return false;
1176 } );
1177
1178 $reset = new ScopedCallback( 'restore_error_handler' );
1179 $allModules = array(
1180 new MockApiQueryBase( 'mock1' ),
1181 new MockApiQueryBase( 'mock2' ),
1182 new MockApiQueryBase( 'mocklist' ),
1183 );
1184 $generator = new MockApiQueryBase( 'generator' );
1185
1186 $main = new ApiMain( RequestContext::getMain() );
1187 $result = new ApiResult( 8388608 );
1188 $result->setMainForContinuation( $main );
1189 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1190 $this->assertSame( array( false, $allModules ), $ret );
1191 $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) );
1192 $result->setContinueParam( $allModules[2], 'mlcontinue', 2 );
1193 $result->setGeneratorContinueParam( $generator, 'gcontinue', 3 );
1194 $result->endContinuation( 'raw' );
1195 $result->endContinuation( 'standard' );
1196 $this->assertSame( array(
1197 'mlcontinue' => 2,
1198 'm1continue' => '1|2',
1199 'continue' => '||mock2',
1200 ), $result->getResultData( 'continue' ) );
1201 $this->assertSame( null, $result->getResultData( 'batchcomplete' ) );
1202 $this->assertSame( array(
1203 'mock1' => array( 'm1continue' => '1|2' ),
1204 'mocklist' => array( 'mlcontinue' => 2 ),
1205 'generator' => array( 'gcontinue' => 3 ),
1206 ), $result->getResultData( 'query-continue' ) );
1207 $main->setContinuationManager( null );
1208
1209 $result = new ApiResult( 8388608 );
1210 $result->setMainForContinuation( $main );
1211 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1212 $this->assertSame( array( false, $allModules ), $ret );
1213 $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) );
1214 $result->setGeneratorContinueParam( $generator, 'gcontinue', array( 3, 4 ) );
1215 $result->endContinuation( 'raw' );
1216 $result->endContinuation( 'standard' );
1217 $this->assertSame( array(
1218 'm1continue' => '1|2',
1219 'continue' => '||mock2|mocklist',
1220 ), $result->getResultData( 'continue' ) );
1221 $this->assertSame( null, $result->getResultData( 'batchcomplete' ) );
1222 $this->assertSame( array(
1223 'mock1' => array( 'm1continue' => '1|2' ),
1224 'generator' => array( 'gcontinue' => '3|4' ),
1225 ), $result->getResultData( 'query-continue' ) );
1226 $main->setContinuationManager( null );
1227
1228 $result = new ApiResult( 8388608 );
1229 $result->setMainForContinuation( $main );
1230 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1231 $this->assertSame( array( false, $allModules ), $ret );
1232 $result->setContinueParam( $allModules[2], 'mlcontinue', 2 );
1233 $result->setGeneratorContinueParam( $generator, 'gcontinue', 3 );
1234 $result->endContinuation( 'raw' );
1235 $result->endContinuation( 'standard' );
1236 $this->assertSame( array(
1237 'mlcontinue' => 2,
1238 'gcontinue' => 3,
1239 'continue' => 'gcontinue||',
1240 ), $result->getResultData( 'continue' ) );
1241 $this->assertSame( true, $result->getResultData( 'batchcomplete' ) );
1242 $this->assertSame( array(
1243 'mocklist' => array( 'mlcontinue' => 2 ),
1244 'generator' => array( 'gcontinue' => 3 ),
1245 ), $result->getResultData( 'query-continue' ) );
1246 $main->setContinuationManager( null );
1247
1248 $result = new ApiResult( 8388608 );
1249 $result->setMainForContinuation( $main );
1250 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1251 $this->assertSame( array( false, $allModules ), $ret );
1252 $result->setGeneratorContinueParam( $generator, 'gcontinue', 3 );
1253 $result->endContinuation( 'raw' );
1254 $result->endContinuation( 'standard' );
1255 $this->assertSame( array(
1256 'gcontinue' => 3,
1257 'continue' => 'gcontinue||mocklist',
1258 ), $result->getResultData( 'continue' ) );
1259 $this->assertSame( true, $result->getResultData( 'batchcomplete' ) );
1260 $this->assertSame( array(
1261 'generator' => array( 'gcontinue' => 3 ),
1262 ), $result->getResultData( 'query-continue' ) );
1263 $main->setContinuationManager( null );
1264
1265 $result = new ApiResult( 8388608 );
1266 $result->setMainForContinuation( $main );
1267 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1268 $this->assertSame( array( false, $allModules ), $ret );
1269 $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) );
1270 $result->setContinueParam( $allModules[2], 'mlcontinue', 2 );
1271 $result->endContinuation( 'raw' );
1272 $result->endContinuation( 'standard' );
1273 $this->assertSame( array(
1274 'mlcontinue' => 2,
1275 'm1continue' => '1|2',
1276 'continue' => '||mock2',
1277 ), $result->getResultData( 'continue' ) );
1278 $this->assertSame( null, $result->getResultData( 'batchcomplete' ) );
1279 $this->assertSame( array(
1280 'mock1' => array( 'm1continue' => '1|2' ),
1281 'mocklist' => array( 'mlcontinue' => 2 ),
1282 ), $result->getResultData( 'query-continue' ) );
1283 $main->setContinuationManager( null );
1284
1285 $result = new ApiResult( 8388608 );
1286 $result->setMainForContinuation( $main );
1287 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1288 $this->assertSame( array( false, $allModules ), $ret );
1289 $result->setContinueParam( $allModules[0], 'm1continue', array( 1, 2 ) );
1290 $result->endContinuation( 'raw' );
1291 $result->endContinuation( 'standard' );
1292 $this->assertSame( array(
1293 'm1continue' => '1|2',
1294 'continue' => '||mock2|mocklist',
1295 ), $result->getResultData( 'continue' ) );
1296 $this->assertSame( null, $result->getResultData( 'batchcomplete' ) );
1297 $this->assertSame( array(
1298 'mock1' => array( 'm1continue' => '1|2' ),
1299 ), $result->getResultData( 'query-continue' ) );
1300 $main->setContinuationManager( null );
1301
1302 $result = new ApiResult( 8388608 );
1303 $result->setMainForContinuation( $main );
1304 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1305 $this->assertSame( array( false, $allModules ), $ret );
1306 $result->setContinueParam( $allModules[2], 'mlcontinue', 2 );
1307 $result->endContinuation( 'raw' );
1308 $result->endContinuation( 'standard' );
1309 $this->assertSame( array(
1310 'mlcontinue' => 2,
1311 'continue' => '-||mock1|mock2',
1312 ), $result->getResultData( 'continue' ) );
1313 $this->assertSame( true, $result->getResultData( 'batchcomplete' ) );
1314 $this->assertSame( array(
1315 'mocklist' => array( 'mlcontinue' => 2 ),
1316 ), $result->getResultData( 'query-continue' ) );
1317 $main->setContinuationManager( null );
1318
1319 $result = new ApiResult( 8388608 );
1320 $result->setMainForContinuation( $main );
1321 $ret = $result->beginContinuation( null, $allModules, array( 'mock1', 'mock2' ) );
1322 $this->assertSame( array( false, $allModules ), $ret );
1323 $result->endContinuation( 'raw' );
1324 $result->endContinuation( 'standard' );
1325 $this->assertSame( null, $result->getResultData( 'continue' ) );
1326 $this->assertSame( true, $result->getResultData( 'batchcomplete' ) );
1327 $this->assertSame( null, $result->getResultData( 'query-continue' ) );
1328 $main->setContinuationManager( null );
1329
1330 $result = new ApiResult( 8388608 );
1331 $result->setMainForContinuation( $main );
1332 $ret = $result->beginContinuation( '||mock2', $allModules, array( 'mock1', 'mock2' ) );
1333 $this->assertSame(
1334 array( false, array_values( array_diff_key( $allModules, array( 1 => 1 ) ) ) ),
1335 $ret
1336 );
1337 $main->setContinuationManager( null );
1338
1339 $result = new ApiResult( 8388608 );
1340 $result->setMainForContinuation( $main );
1341 $ret = $result->beginContinuation( '-||', $allModules, array( 'mock1', 'mock2' ) );
1342 $this->assertSame(
1343 array( true, array_values( array_diff_key( $allModules, array( 0 => 0, 1 => 1 ) ) ) ),
1344 $ret
1345 );
1346 $main->setContinuationManager( null );
1347
1348 $result = new ApiResult( 8388608 );
1349 $result->setMainForContinuation( $main );
1350 try {
1351 $result->beginContinuation( 'foo', $allModules, array( 'mock1', 'mock2' ) );
1352 $this->fail( 'Expected exception not thrown' );
1353 } catch ( UsageException $ex ) {
1354 $this->assertSame(
1355 'Invalid continue param. You should pass the original value returned by the previous query',
1356 $ex->getMessage(),
1357 'Expected exception'
1358 );
1359 }
1360 $main->setContinuationManager( null );
1361
1362 $result = new ApiResult( 8388608 );
1363 $result->setMainForContinuation( $main );
1364 $result->beginContinuation( '||mock2', array_slice( $allModules, 0, 2 ), array( 'mock1', 'mock2' ) );
1365 try {
1366 $result->setContinueParam( $allModules[1], 'm2continue', 1 );
1367 $this->fail( 'Expected exception not thrown' );
1368 } catch ( UnexpectedValueException $ex ) {
1369 $this->assertSame(
1370 'Module \'mock2\' was not supposed to have been executed, but it was executed anyway',
1371 $ex->getMessage(),
1372 'Expected exception'
1373 );
1374 }
1375 try {
1376 $result->setContinueParam( $allModules[2], 'mlcontinue', 1 );
1377 $this->fail( 'Expected exception not thrown' );
1378 } catch ( UnexpectedValueException $ex ) {
1379 $this->assertSame(
1380 'Module \'mocklist\' called ApiContinuationManager::addContinueParam but was not passed to ApiContinuationManager::__construct',
1381 $ex->getMessage(),
1382 'Expected exception'
1383 );
1384 }
1385 $main->setContinuationManager( null );
1386
1387 }
1388
1389 public function testObjectSerialization() {
1390 $arr = array();
1391 ApiResult::setValue( $arr, 'foo', (object)array( 'a' => 1, 'b' => 2 ) );
1392 $this->assertSame( array(
1393 'a' => 1,
1394 'b' => 2,
1395 ApiResult::META_TYPE => 'assoc',
1396 ), $arr['foo'] );
1397
1398 $arr = array();
1399 ApiResult::setValue( $arr, 'foo', new ApiResultTestStringifiableObject() );
1400 $this->assertSame( 'Ok', $arr['foo'] );
1401
1402 $arr = array();
1403 ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject( 'Ok' ) );
1404 $this->assertSame( 'Ok', $arr['foo'] );
1405
1406 try {
1407 $arr = array();
1408 ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject(
1409 new ApiResultTestStringifiableObject()
1410 ) );
1411 $this->fail( 'Expected exception not thrown' );
1412 } catch ( UnexpectedValueException $ex ) {
1413 $this->assertSame(
1414 'ApiResultTestSerializableObject::serializeForApiResult() returned an object of class ApiResultTestStringifiableObject',
1415 $ex->getMessage(),
1416 'Expected exception'
1417 );
1418 }
1419
1420 try {
1421 $arr = array();
1422 ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject( NAN ) );
1423 $this->fail( 'Expected exception not thrown' );
1424 } catch ( UnexpectedValueException $ex ) {
1425 $this->assertSame(
1426 'ApiResultTestSerializableObject::serializeForApiResult() returned an invalid value: Cannot add non-finite floats to ApiResult',
1427 $ex->getMessage(),
1428 'Expected exception'
1429 );
1430 }
1431
1432 $arr = array();
1433 ApiResult::setValue( $arr, 'foo', new ApiResultTestSerializableObject(
1434 array(
1435 'one' => new ApiResultTestStringifiableObject( '1' ),
1436 'two' => new ApiResultTestSerializableObject( 2 ),
1437 )
1438 ) );
1439 $this->assertSame( array(
1440 'one' => '1',
1441 'two' => 2,
1442 ), $arr['foo'] );
1443 }
1444
1445 }
1446
1447 class ApiResultTestStringifiableObject {
1448 private $ret;
1449
1450 public function __construct( $ret = 'Ok' ) {
1451 $this->ret = $ret;
1452 }
1453
1454 public function __toString() {
1455 return $this->ret;
1456 }
1457 }
1458
1459 class ApiResultTestSerializableObject {
1460 private $ret;
1461
1462 public function __construct( $ret ) {
1463 $this->ret = $ret;
1464 }
1465
1466 public function __toString() {
1467 return "Fail";
1468 }
1469
1470 public function serializeForApiResult() {
1471 return $this->ret;
1472 }
1473 }