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