Merge "selenium: Remove "RunJobs" wait from specialrecentchanges test"
[lhc/web/wiklou.git] / tests / phpunit / includes / Revision / RevisionQueryInfoTest.php
1 <?php
2
3 namespace MediaWiki\Tests\Revision;
4
5 use MediaWiki\MediaWikiServices;
6 use MediaWiki\Revision\SlotRecord;
7 use MediaWikiTestCase;
8 use Revision;
9
10 /**
11 * Tests RevisionStore against the post-migration MCR DB schema.
12 *
13 * @group RevisionStore
14 * @group Storage
15 * @group Database
16 */
17 class RevisionQueryInfoTest extends MediaWikiTestCase {
18
19 protected function getRevisionQueryFields( $returnTextIdField = true ) {
20 $fields = [
21 'rev_id',
22 'rev_page',
23 'rev_timestamp',
24 'rev_minor_edit',
25 'rev_deleted',
26 'rev_len',
27 'rev_parent_id',
28 'rev_sha1',
29 ];
30 if ( $returnTextIdField ) {
31 $fields[] = 'rev_text_id';
32 }
33 return $fields;
34 }
35
36 protected function getArchiveQueryFields( $returnTextFields = true ) {
37 $fields = [
38 'ar_id',
39 'ar_page_id',
40 'ar_namespace',
41 'ar_title',
42 'ar_rev_id',
43 'ar_timestamp',
44 'ar_minor_edit',
45 'ar_deleted',
46 'ar_len',
47 'ar_parent_id',
48 'ar_sha1',
49 ];
50 if ( $returnTextFields ) {
51 $fields[] = 'ar_text_id';
52 }
53 return $fields;
54 }
55
56 protected function getNewCommentQueryFields( $prefix ) {
57 return [
58 "{$prefix}_comment_text" => "comment_{$prefix}_comment.comment_text",
59 "{$prefix}_comment_data" => "comment_{$prefix}_comment.comment_data",
60 "{$prefix}_comment_cid" => "comment_{$prefix}_comment.comment_id",
61 ];
62 }
63
64 protected function getNewActorQueryFields( $prefix, $tmp = false ) {
65 return [
66 "{$prefix}_user" => "actor_{$prefix}_user.actor_user",
67 "{$prefix}_user_text" => "actor_{$prefix}_user.actor_name",
68 "{$prefix}_actor" => $tmp ? "temp_{$prefix}_user.{$prefix}actor_actor" : "{$prefix}_actor",
69 ];
70 }
71
72 protected function getTextQueryFields() {
73 return [
74 'old_text',
75 'old_flags',
76 ];
77 }
78
79 protected function getPageQueryFields() {
80 return [
81 'page_namespace',
82 'page_title',
83 'page_id',
84 'page_latest',
85 'page_is_redirect',
86 'page_len',
87 ];
88 }
89
90 protected function getUserQueryFields() {
91 return [
92 'user_name',
93 ];
94 }
95
96 protected function getContentHandlerQueryFields( $prefix ) {
97 return [
98 "{$prefix}_content_format",
99 "{$prefix}_content_model",
100 ];
101 }
102
103 public function provideArchiveQueryInfo() {
104 yield 'MCR, comment, actor' => [
105 [
106 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
107 ],
108 [
109 'tables' => [
110 'archive',
111 'actor_ar_user' => 'actor',
112 'comment_ar_comment' => 'comment',
113 ],
114 'fields' => array_merge(
115 $this->getArchiveQueryFields( false ),
116 $this->getNewActorQueryFields( 'ar' ),
117 $this->getNewCommentQueryFields( 'ar' )
118 ),
119 'joins' => [
120 'comment_ar_comment'
121 => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
122 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
123 ],
124 ]
125 ];
126 yield 'read-new MCR, comment, actor' => [
127 [
128 'wgContentHandlerUseDB' => true,
129 'wgMultiContentRevisionSchemaMigrationStage'
130 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
131 ],
132 [
133 'tables' => [
134 'archive',
135 'actor_ar_user' => 'actor',
136 'comment_ar_comment' => 'comment',
137 ],
138 'fields' => array_merge(
139 $this->getArchiveQueryFields( false ),
140 $this->getNewActorQueryFields( 'ar' ),
141 $this->getNewCommentQueryFields( 'ar' )
142 ),
143 'joins' => [
144 'comment_ar_comment'
145 => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
146 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
147 ],
148 ]
149 ];
150 yield 'MCR write-both/read-old' => [
151 [
152 'wgContentHandlerUseDB' => true,
153 'wgMultiContentRevisionSchemaMigrationStage'
154 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
155 ],
156 [
157 'tables' => [
158 'archive',
159 'actor_ar_user' => 'actor',
160 'comment_ar_comment' => 'comment',
161 ],
162 'fields' => array_merge(
163 $this->getArchiveQueryFields( true ),
164 $this->getContentHandlerQueryFields( 'ar' ),
165 $this->getNewActorQueryFields( 'ar' ),
166 $this->getNewCommentQueryFields( 'ar' )
167 ),
168 'joins' => [
169 'comment_ar_comment'
170 => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
171 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
172 ],
173 ]
174 ];
175 }
176
177 public function provideQueryInfo() {
178 // TODO: more option variations
179 yield 'MCR, page, user, comment, actor' => [
180 [
181 'wgContentHandlerUseDB' => true,
182 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
183 ],
184 [ 'page', 'user' ],
185 [
186 'tables' => [
187 'revision',
188 'page',
189 'user',
190 'temp_rev_user' => 'revision_actor_temp',
191 'temp_rev_comment' => 'revision_comment_temp',
192 'actor_rev_user' => 'actor',
193 'comment_rev_comment' => 'comment',
194 ],
195 'fields' => array_merge(
196 $this->getRevisionQueryFields( false ),
197 $this->getPageQueryFields(),
198 $this->getUserQueryFields(),
199 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
200 $this->getNewCommentQueryFields( 'rev' )
201 ),
202 'joins' => [
203 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
204 'user' => [
205 'LEFT JOIN',
206 [ 'actor_rev_user.actor_user != 0', 'user_id = actor_rev_user.actor_user' ],
207 ],
208 'comment_rev_comment' => [
209 'JOIN',
210 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
211 ],
212 'actor_rev_user' => [
213 'JOIN',
214 'actor_rev_user.actor_id = temp_rev_user.revactor_actor',
215 ],
216 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
217 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
218 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
219 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
220 ],
221 ]
222 ];
223 yield 'MCR read-new, page, user, comment, actor' => [
224 [
225 'wgContentHandlerUseDB' => true,
226 'wgMultiContentRevisionSchemaMigrationStage'
227 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
228 ],
229 [ 'page', 'user' ],
230 [
231 'tables' => [
232 'revision',
233 'page',
234 'user',
235 'temp_rev_user' => 'revision_actor_temp',
236 'temp_rev_comment' => 'revision_comment_temp',
237 'actor_rev_user' => 'actor',
238 'comment_rev_comment' => 'comment',
239 ],
240 'fields' => array_merge(
241 $this->getRevisionQueryFields( false ),
242 $this->getPageQueryFields(),
243 $this->getUserQueryFields(),
244 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
245 $this->getNewCommentQueryFields( 'rev' )
246 ),
247 'joins' => [
248 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
249 'user' => [
250 'LEFT JOIN',
251 [
252 'actor_rev_user.actor_user != 0',
253 'user_id = actor_rev_user.actor_user',
254 ]
255 ],
256 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
257 'comment_rev_comment'
258 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
259 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
260 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
261 ],
262 ]
263 ];
264 yield 'MCR read-new' => [
265 [
266 'wgContentHandlerUseDB' => true,
267 'wgMultiContentRevisionSchemaMigrationStage'
268 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
269 ],
270 [ 'page', 'user' ],
271 [
272 'tables' => [
273 'revision',
274 'page',
275 'user',
276 'temp_rev_user' => 'revision_actor_temp',
277 'temp_rev_comment' => 'revision_comment_temp',
278 'actor_rev_user' => 'actor',
279 'comment_rev_comment' => 'comment',
280 ],
281 'fields' => array_merge(
282 $this->getRevisionQueryFields( false ),
283 $this->getPageQueryFields(),
284 $this->getUserQueryFields(),
285 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
286 $this->getNewCommentQueryFields( 'rev' )
287 ),
288 'joins' => [
289 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
290 'user' => [
291 'LEFT JOIN',
292 [
293 'actor_rev_user.actor_user != 0',
294 'user_id = actor_rev_user.actor_user'
295 ]
296 ],
297 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
298 'comment_rev_comment'
299 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
300 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
301 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
302 ],
303 ]
304 ];
305 yield 'MCR write-both/read-old' => [
306 [
307 'wgContentHandlerUseDB' => true,
308 'wgMultiContentRevisionSchemaMigrationStage'
309 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
310 ],
311 [],
312 [
313 'tables' => [
314 'revision',
315 'temp_rev_comment' => 'revision_comment_temp',
316 'comment_rev_comment' => 'comment',
317 'temp_rev_user' => 'revision_actor_temp',
318 'actor_rev_user' => 'actor',
319 ],
320 'fields' => array_merge(
321 $this->getRevisionQueryFields( true ),
322 $this->getContentHandlerQueryFields( 'rev' ),
323 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
324 $this->getNewCommentQueryFields( 'rev' )
325 ),
326 'joins' => [
327 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
328 'comment_rev_comment'
329 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
330 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
331 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
332 ],
333 ]
334 ];
335 yield 'MCR write-both/read-old, page, user' => [
336 [
337 'wgContentHandlerUseDB' => true,
338 'wgMultiContentRevisionSchemaMigrationStage'
339 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
340 ],
341 [ 'page', 'user' ],
342 [
343 'tables' => [
344 'revision',
345 'page',
346 'user',
347 'temp_rev_comment' => 'revision_comment_temp',
348 'comment_rev_comment' => 'comment',
349 'temp_rev_user' => 'revision_actor_temp',
350 'actor_rev_user' => 'actor',
351 ],
352 'fields' => array_merge(
353 $this->getRevisionQueryFields( true ),
354 $this->getContentHandlerQueryFields( 'rev' ),
355 $this->getUserQueryFields(),
356 $this->getPageQueryFields(),
357 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
358 $this->getNewCommentQueryFields( 'rev' )
359 ),
360 'joins' => [
361 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
362 'user' => [
363 'LEFT JOIN',
364 [
365 'actor_rev_user.actor_user != 0',
366 'user_id = actor_rev_user.actor_user',
367 ]
368 ],
369 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
370 'comment_rev_comment'
371 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
372 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
373 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
374 ],
375 ]
376 ];
377 }
378
379 public function provideSlotsQueryInfo() {
380 yield 'MCR, no options' => [
381 [
382 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
383 ],
384 [],
385 [
386 'tables' => [
387 'slots'
388 ],
389 'fields' => [
390 'slot_revision_id',
391 'slot_content_id',
392 'slot_origin',
393 'slot_role_id',
394 ],
395 'joins' => [],
396 'keys' => [
397 'rev_id' => 'slot_revision_id',
398 'role_id' => 'slot_role_id'
399 ],
400 ]
401 ];
402 yield 'MCR, role option' => [
403 [
404 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
405 ],
406 [ 'role' ],
407 [
408 'tables' => [
409 'slots',
410 'slot_roles',
411 ],
412 'fields' => [
413 'slot_revision_id',
414 'slot_content_id',
415 'slot_origin',
416 'slot_role_id',
417 'role_name',
418 ],
419 'joins' => [
420 'slot_roles' => [ 'LEFT JOIN', [ 'slot_role_id = role_id' ] ],
421 ],
422 'keys' => [
423 'rev_id' => 'slot_revision_id',
424 'role_id' => 'slot_role_id'
425 ],
426 ]
427 ];
428 yield 'MCR read-new, content option' => [
429 [
430 'wgMultiContentRevisionSchemaMigrationStage'
431 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
432 ],
433 [ 'content' ],
434 [
435 'tables' => [
436 'slots',
437 'content',
438 ],
439 'fields' => [
440 'slot_revision_id',
441 'slot_content_id',
442 'slot_origin',
443 'slot_role_id',
444 'content_size',
445 'content_sha1',
446 'content_address',
447 'content_model',
448 ],
449 'joins' => [
450 'content' => [ 'JOIN', [ 'slot_content_id = content_id' ] ],
451 ],
452 'keys' => [
453 'rev_id' => 'slot_revision_id',
454 'role_id' => 'slot_role_id',
455 'model_id' => 'content_model',
456 ],
457 ]
458 ];
459 yield 'MCR read-new, content and model options' => [
460 [
461 'wgMultiContentRevisionSchemaMigrationStage'
462 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
463 ],
464 [ 'content', 'model' ],
465 [
466 'tables' => [
467 'slots',
468 'content',
469 'content_models',
470 ],
471 'fields' => [
472 'slot_revision_id',
473 'slot_content_id',
474 'slot_origin',
475 'slot_role_id',
476 'content_size',
477 'content_sha1',
478 'content_address',
479 'content_model',
480 'model_name',
481 ],
482 'joins' => [
483 'content' => [ 'JOIN', [ 'slot_content_id = content_id' ] ],
484 'content_models' => [ 'LEFT JOIN', [ 'content_model = model_id' ] ],
485 ],
486 'keys' => [
487 'rev_id' => 'slot_revision_id',
488 'role_id' => 'slot_role_id',
489 'model_id' => 'content_model',
490 ],
491 ]
492 ];
493
494 $db = wfGetDB( DB_REPLICA );
495
496 yield 'MCR write-both/read-old' => [
497 [
498 'wgMultiContentRevisionSchemaMigrationStage'
499 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
500 ],
501 [],
502 [
503 'tables' => [
504 'revision',
505 ],
506 'fields' => array_merge(
507 [
508 'slot_revision_id' => 'rev_id',
509 'slot_content_id' => 'NULL',
510 'slot_origin' => 'rev_id',
511 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
512 ]
513 ),
514 'joins' => [],
515 'keys' => [
516 'rev_id' => 'rev_id'
517 ],
518 ]
519 ];
520 yield 'MCR write-both/read-old, content' => [
521 [
522 'wgMultiContentRevisionSchemaMigrationStage'
523 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
524 ],
525 [ 'content' ],
526 [
527 'tables' => [
528 'revision',
529 ],
530 'fields' => array_merge(
531 [
532 'slot_revision_id' => 'rev_id',
533 'slot_content_id' => 'NULL',
534 'slot_origin' => 'rev_id',
535 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
536 'content_size' => 'rev_len',
537 'content_sha1' => 'rev_sha1',
538 'content_address' => $db->buildConcat( [
539 $db->addQuotes( 'tt:' ), 'rev_text_id' ] ),
540 'rev_text_id' => 'rev_text_id',
541 'model_name' => 'rev_content_model',
542 ]
543 ),
544 'joins' => [],
545 'keys' => [
546 'rev_id' => 'rev_id'
547 ],
548 ]
549 ];
550 yield 'MCR write-both/read-old, content, model, role' => [
551 [
552 'wgMultiContentRevisionSchemaMigrationStage'
553 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
554 ],
555 [ 'content', 'model', 'role' ],
556 [
557 'tables' => [
558 'revision',
559 ],
560 'fields' => array_merge(
561 [
562 'slot_revision_id' => 'rev_id',
563 'slot_content_id' => 'NULL',
564 'slot_origin' => 'rev_id',
565 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
566 'content_size' => 'rev_len',
567 'content_sha1' => 'rev_sha1',
568 'content_address' => $db->buildConcat( [
569 $db->addQuotes( 'tt:' ), 'rev_text_id' ] ),
570 'rev_text_id' => 'rev_text_id',
571 'model_name' => 'rev_content_model',
572 ]
573 ),
574 'joins' => [],
575 'keys' => [
576 'rev_id' => 'rev_id'
577 ],
578 ]
579 ];
580 }
581
582 /**
583 * @covers Revision::getArchiveQueryInfo
584 * @dataProvider provideArchiveQueryInfo
585 */
586 public function testRevisionGetArchiveQueryInfo( $migrationStageSettings, $expected ) {
587 $this->setMwGlobals( $migrationStageSettings );
588
589 $queryInfo = Revision::getArchiveQueryInfo();
590 $this->assertQueryInfoEquals( $expected, $queryInfo );
591 }
592
593 /**
594 * @covers Revision::getQueryInfo
595 * @dataProvider provideQueryInfo
596 */
597 public function testRevisionGetQueryInfo( $migrationStageSettings, $options, $expected ) {
598 $this->setMwGlobals( $migrationStageSettings );
599
600 $queryInfo = Revision::getQueryInfo( $options );
601 $this->assertQueryInfoEquals( $expected, $queryInfo );
602 }
603
604 /**
605 * @dataProvider provideQueryInfo
606 * @covers \MediaWiki\Revision\RevisionStore::getQueryInfo
607 */
608 public function testRevisionStoreGetQueryInfo( $migrationStageSettings, $options, $expected ) {
609 $this->setMwGlobals( $migrationStageSettings );
610
611 $store = MediaWikiServices::getInstance()->getRevisionStore();
612
613 $queryInfo = $store->getQueryInfo( $options );
614 $this->assertQueryInfoEquals( $expected, $queryInfo );
615 }
616
617 /**
618 * @dataProvider provideSlotsQueryInfo
619 * @covers \MediaWiki\Revision\RevisionStore::getSlotsQueryInfo
620 */
621 public function testRevisionStoreGetSlotsQueryInfo(
622 $migrationStageSettings,
623 $options,
624 $expected
625 ) {
626 $this->setMwGlobals( $migrationStageSettings );
627
628 $store = MediaWikiServices::getInstance()->getRevisionStore();
629
630 $queryInfo = $store->getSlotsQueryInfo( $options );
631 $this->assertQueryInfoEquals( $expected, $queryInfo );
632 }
633
634 /**
635 * @dataProvider provideArchiveQueryInfo
636 * @covers \MediaWiki\Revision\RevisionStore::getArchiveQueryInfo
637 */
638 public function testRevisionStoreGetArchiveQueryInfo( $migrationStageSettings, $expected ) {
639 $this->setMwGlobals( $migrationStageSettings );
640
641 $store = MediaWikiServices::getInstance()->getRevisionStore();
642
643 $queryInfo = $store->getArchiveQueryInfo();
644 $this->assertQueryInfoEquals( $expected, $queryInfo );
645 }
646
647 private function assertQueryInfoEquals( $expected, $queryInfo ) {
648 $this->assertArrayEqualsIgnoringIntKeyOrder(
649 $expected['tables'],
650 $queryInfo['tables'],
651 'tables'
652 );
653 $this->assertArrayEqualsIgnoringIntKeyOrder(
654 $expected['fields'],
655 $queryInfo['fields'],
656 'fields'
657 );
658 $this->assertArrayEqualsIgnoringIntKeyOrder(
659 $expected['joins'],
660 $queryInfo['joins'],
661 'joins'
662 );
663 if ( isset( $expected['keys'] ) ) {
664 $this->assertArrayEqualsIgnoringIntKeyOrder(
665 $expected['keys'],
666 $queryInfo['keys'],
667 'keys'
668 );
669 }
670 }
671
672 /**
673 * Assert that the two arrays passed are equal, ignoring the order of the values that integer
674 * keys.
675 *
676 * Note: Failures of this assertion can be slightly confusing as the arrays are actually
677 * split into a string key array and an int key array before assertions occur.
678 *
679 * @param array $expected
680 * @param array $actual
681 */
682 private function assertArrayEqualsIgnoringIntKeyOrder(
683 array $expected,
684 array $actual,
685 $message = null
686 ) {
687 $this->objectAssociativeSort( $expected );
688 $this->objectAssociativeSort( $actual );
689
690 // Separate the int key values from the string key values so that assertion failures are
691 // easier to understand.
692 $expectedIntKeyValues = [];
693 $actualIntKeyValues = [];
694
695 // Remove all int keys and re add them at the end after sorting by value
696 // This will result in all int keys being in the same order with same ints at the end of
697 // the array
698 foreach ( $expected as $key => $value ) {
699 if ( is_int( $key ) ) {
700 unset( $expected[$key] );
701 $expectedIntKeyValues[] = $value;
702 }
703 }
704 foreach ( $actual as $key => $value ) {
705 if ( is_int( $key ) ) {
706 unset( $actual[$key] );
707 $actualIntKeyValues[] = $value;
708 }
709 }
710
711 $this->objectAssociativeSort( $expected );
712 $this->objectAssociativeSort( $actual );
713
714 $this->objectAssociativeSort( $expectedIntKeyValues );
715 $this->objectAssociativeSort( $actualIntKeyValues );
716
717 $this->assertEquals( $expected, $actual, $message );
718 $this->assertEquals( $expectedIntKeyValues, $actualIntKeyValues, $message );
719 }
720
721 }