MatrixClipCollapseTest.cpp revision 105a4a584c4c2c84c24e102112326b15683673f5
1/* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "Test.h" 9#include "SkCanvas.h" 10#include "SkDebugCanvas.h" 11#include "SkPicture.h" 12#include "SkPictureFlat.h" 13#include "SkPictureRecord.h" 14 15// This test exercises the Matrix/Clip State collapsing system. It generates 16// example skps and the compares the actual stored operations to the expected 17// operations. The test works by emitting canvas operations at three levels: 18// overall structure, bodies that draw something and model/clip state changes. 19// 20// Structure methods only directly emit save and restores but call the 21// ModelClip and Body helper methods to fill in the structure. Since they only 22// emit saves and restores the operations emitted by the structure methods will 23// be completely removed by the matrix/clip collapse. Note: every save in 24// a structure method is followed by a call to a ModelClip helper. 25// 26// Body methods only directly emit draw ops and saveLayer/restore pairs but call 27// the ModelClip helper methods. Since the body methods emit the ops that cannot 28// be collapsed (i.e., draw ops, saveLayer/restore) they also generate the 29// expected result information. Note: every saveLayer in a body method is 30// followed by a call to a ModelClip helper. 31// 32// The ModelClip methods output matrix and clip ops in various orders and 33// combinations. They contribute to the expected result by outputting the 34// expected matrix & clip ops. Note that, currently, the entire clip stack 35// is output for each MC state so the clip operations accumulate down the 36// save/restore stack. 37 38// TODOs: 39// check on clip offsets 40// - not sure if this is possible. The desire is to verify that the clip 41// operations' offsets point to the correct follow-on operations. This 42// could be difficult since there is no good way to communicate the 43// offset stored in the SkPicture to the debugger's clip objects 44// add comparison of rendered before & after images? 45// - not sure if this would be useful since it somewhat duplicates the 46// correctness test of running render_pictures in record mode and 47// rendering before and after images. Additionally the matrix/clip collapse 48// is sure to cause some small differences so an automated test might 49// yield too many false positives. 50// run the matrix/clip collapse system on the 10K skp set 51// - this should give us warm fuzzies that the matrix clip collapse 52// system is ready for prime time 53// bench the recording times with/without matrix/clip collapsing 54 55#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE 56 57// Enable/disable debugging helper code 58//#define TEST_COLLAPSE_MATRIX_CLIP_STATE 1 59 60// Extract the command ops from the input SkPicture 61static void gets_ops(SkPicture& input, SkTDArray<DrawType>* ops) { 62 SkDebugCanvas debugCanvas(input.width(), input.height()); 63 debugCanvas.setBounds(input.width(), input.height()); 64 input.draw(&debugCanvas); 65 66 ops->setCount(debugCanvas.getSize()); 67 for (int i = 0; i < debugCanvas.getSize(); ++i) { 68 (*ops)[i] = debugCanvas.getDrawCommandAt(i)->getType(); 69 } 70} 71 72enum ClipType { 73 kNone_ClipType, 74 kRect_ClipType, 75 kRRect_ClipType, 76 kPath_ClipType, 77 kRegion_ClipType, 78 79 kLast_ClipType = kRRect_ClipType 80}; 81 82static const int kClipTypeCount = kLast_ClipType + 1; 83 84enum MatType { 85 kNone_MatType, 86 kTranslate_MatType, 87 kScale_MatType, 88 kSkew_MatType, 89 kRotate_MatType, 90 kConcat_MatType, 91 kSetMatrix_MatType, 92 93 kLast_MatType = kScale_MatType 94}; 95 96static const int kMatTypeCount = kLast_MatType + 1; 97 98// TODO: implement the rest of the draw ops 99enum DrawOpType { 100 kNone_DrawOpType, 101#if 0 102 kBitmap_DrawOpType, 103 kBitmapMatrix_DrawOpType, 104 kBitmapNone_DrawOpType, 105 kBitmapRectToRect_DrawOpType, 106#endif 107 kClear_DrawOpType, 108#if 0 109 kData_DrawOpType, 110#endif 111 kOval_DrawOpType, 112#if 0 113 kPaint_DrawOpType, 114 kPath_DrawOpType, 115 kPicture_DrawOpType, 116 kPoints_DrawOpType, 117 kPosText_DrawOpType, 118 kPosTextTopBottom_DrawOpType, 119 kPosTextH_DrawOpType, 120 kPosTextHTopBottom_DrawOpType, 121#endif 122 kRect_DrawOpType, 123 kRRect_DrawOpType, 124#if 0 125 kSprite_DrawOpType, 126 kText_DrawOpType, 127 kTextOnPath_DrawOpType, 128 kTextTopBottom_DrawOpType, 129 kDrawVertices_DrawOpType, 130#endif 131 132 kLast_DrawOpType = kRect_DrawOpType 133}; 134 135static const int kDrawOpTypeCount = kLast_DrawOpType + 1; 136 137typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip, 138 DrawOpType draw, SkTDArray<DrawType>* expected, 139 int accumulatedClips); 140typedef void (*PFEmitBody)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 141 ClipType clip, DrawOpType draw, 142 SkTDArray<DrawType>* expected, int accumulatedClips); 143typedef void (*PFEmitStruct)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 144 ClipType clip, PFEmitBody emitBody, DrawOpType draw, 145 SkTDArray<DrawType>* expected); 146 147////////////////////////////////////////////////////////////////////////////// 148 149// TODO: expand the testing to include the different ops & AA types! 150static void emit_clip(SkCanvas* canvas, ClipType clip) { 151 switch (clip) { 152 case kNone_ClipType: 153 break; 154 case kRect_ClipType: { 155 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 156 canvas->clipRect(r, SkRegion::kIntersect_Op, true); 157 break; 158 } 159 case kRRect_ClipType: { 160 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 161 SkRRect rr; 162 rr.setRectXY(r, 10, 10); 163 canvas->clipRRect(rr, SkRegion::kIntersect_Op, true); 164 break; 165 } 166 case kPath_ClipType: { 167 SkPath p; 168 p.moveTo(5.0f, 5.0f); 169 p.lineTo(50.0f, 50.0f); 170 p.lineTo(100.0f, 5.0f); 171 p.close(); 172 canvas->clipPath(p, SkRegion::kIntersect_Op, true); 173 break; 174 } 175 case kRegion_ClipType: { 176 SkIRect rects[2] = { 177 { 1, 1, 55, 55 }, 178 { 45, 45, 99, 99 }, 179 }; 180 SkRegion r; 181 r.setRects(rects, 2); 182 canvas->clipRegion(r, SkRegion::kIntersect_Op); 183 break; 184 } 185 default: 186 SkASSERT(0); 187 } 188} 189 190static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) { 191 if (NULL == expected) { 192 // expected is NULL if this clip will be fused into later clips 193 return; 194 } 195 196 switch (clip) { 197 case kNone_ClipType: 198 break; 199 case kRect_ClipType: 200 *expected->append() = CONCAT; 201 *expected->append() = CLIP_RECT; 202 break; 203 case kRRect_ClipType: 204 *expected->append() = CONCAT; 205 *expected->append() = CLIP_RRECT; 206 break; 207 case kPath_ClipType: 208 *expected->append() = CONCAT; 209 *expected->append() = CLIP_PATH; 210 break; 211 case kRegion_ClipType: 212 *expected->append() = CONCAT; 213 *expected->append() = CLIP_REGION; 214 break; 215 default: 216 SkASSERT(0); 217 } 218} 219 220static void emit_mat(SkCanvas* canvas, MatType mat) { 221 switch (mat) { 222 case kNone_MatType: 223 break; 224 case kTranslate_MatType: 225 canvas->translate(5.0f, 5.0f); 226 break; 227 case kScale_MatType: 228 canvas->scale(1.1f, 1.1f); 229 break; 230 case kSkew_MatType: 231 canvas->skew(1.1f, 1.1f); 232 break; 233 case kRotate_MatType: 234 canvas->rotate(1.0f); 235 break; 236 case kConcat_MatType: { 237 SkMatrix m; 238 m.setTranslate(1.0f, 1.0f); 239 canvas->concat(m); 240 break; 241 } 242 case kSetMatrix_MatType: { 243 SkMatrix m; 244 m.setTranslate(1.0f, 1.0f); 245 canvas->setMatrix(m); 246 break; 247 } 248 default: 249 SkASSERT(0); 250 } 251} 252 253static void add_mat(MatType mat, SkTDArray<DrawType>* expected) { 254 if (NULL == expected) { 255 // expected is NULL if this matrix call will be fused into later ones 256 return; 257 } 258 259 switch (mat) { 260 case kNone_MatType: 261 break; 262 case kTranslate_MatType: // fall thru 263 case kScale_MatType: // fall thru 264 case kSkew_MatType: // fall thru 265 case kRotate_MatType: // fall thru 266 case kConcat_MatType: // fall thru 267 case kSetMatrix_MatType: 268 // TODO: this system currently converts a setMatrix to concat. If we wanted to 269 // really preserve the setMatrix semantics we should keep it a setMatrix. I'm 270 // not sure if this is a good idea though since this would keep things like pinch 271 // zoom from working. 272 *expected->append() = CONCAT; 273 break; 274 default: 275 SkASSERT(0); 276 } 277} 278 279static void emit_draw(SkCanvas* canvas, DrawOpType draw, SkTDArray<DrawType>* expected) { 280 switch (draw) { 281 case kNone_DrawOpType: 282 break; 283 case kClear_DrawOpType: 284 canvas->clear(SK_ColorRED); 285 *expected->append() = DRAW_CLEAR; 286 break; 287 case kOval_DrawOpType: { 288 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 289 SkPaint p; 290 canvas->drawOval(r, p); 291 *expected->append() = DRAW_OVAL; 292 break; 293 } 294 case kRect_DrawOpType: { 295 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 296 SkPaint p; 297 canvas->drawRect(r, p); 298 *expected->append() = DRAW_RECT; 299 break; 300 } 301 case kRRect_DrawOpType: { 302 SkRect r = SkRect::MakeLTRB(10.0f, 10.0f, 90.0f, 90.0f); 303 SkRRect rr; 304 rr.setRectXY(r, 5.0f, 5.0f); 305 SkPaint p; 306 canvas->drawRRect(rr, p); 307 *expected->append() = DRAW_RRECT; 308 break; 309 } 310 default: 311 SkASSERT(0); 312 } 313} 314 315////////////////////////////////////////////////////////////////////////////// 316 317// Emit: 318// clip 319// matrix 320// Simple case - the clip isn't effect by the matrix 321static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip, 322 DrawOpType draw, SkTDArray<DrawType>* expected, 323 int accumulatedClips) { 324 if (kNone_DrawOpType == draw) { 325 return; 326 } 327 328 emit_clip(canvas, clip); 329 emit_mat(canvas, mat); 330 331 for (int i = 0; i < accumulatedClips; ++i) { 332 add_clip(clip, mat, expected); 333 } 334 add_mat(mat, expected); 335} 336 337// Emit: 338// matrix 339// clip 340// Emitting the matrix first is more challenging since the matrix has to be 341// pushed across (i.e., applied to) the clip. 342static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip, 343 DrawOpType draw, SkTDArray<DrawType>* expected, 344 int accumulatedClips) { 345 if (kNone_DrawOpType == draw) { 346 return; 347 } 348 349 emit_mat(canvas, mat); 350 emit_clip(canvas, clip); 351 352 // the matrix & clip order will be reversed once collapsed! 353 for (int i = 0; i < accumulatedClips; ++i) { 354 add_clip(clip, mat, expected); 355 } 356 add_mat(mat, expected); 357} 358 359// Emit: 360// matrix 361// clip 362// matrix 363// clip 364// This tests that the matrices and clips coalesce when collapsed 365static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip, 366 DrawOpType draw, SkTDArray<DrawType>* expected, 367 int accumulatedClips) { 368 if (kNone_DrawOpType == draw) { 369 return; 370 } 371 372 emit_mat(canvas, mat); 373 emit_clip(canvas, clip); 374 emit_mat(canvas, mat); 375 emit_clip(canvas, clip); 376 377 for (int i = 0; i < accumulatedClips; ++i) { 378 add_clip(clip, mat, expected); 379 add_clip(clip, mat, expected); 380 } 381 add_mat(mat, expected); 382} 383 384// Emit: 385// matrix 386// clip 387// clip 388// This tests accumulation of clips in same transform state. It also tests pushing 389// of the matrix across both the clips. 390static void emit_mat_clip_clip(SkCanvas* canvas, MatType mat, ClipType clip, 391 DrawOpType draw, SkTDArray<DrawType>* expected, 392 int accumulatedClips) { 393 if (kNone_DrawOpType == draw) { 394 return; 395 } 396 397 emit_mat(canvas, mat); 398 emit_clip(canvas, clip); 399 emit_clip(canvas, clip); 400 401 for (int i = 0; i < accumulatedClips; ++i) { 402 add_clip(clip, mat, expected); 403 add_clip(clip, mat, expected); 404 } 405 add_mat(mat, expected); 406} 407 408////////////////////////////////////////////////////////////////////////////// 409 410// Emit: 411// matrix & clip calls 412// draw op 413static void emit_body0(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 414 ClipType clip, DrawOpType draw, 415 SkTDArray<DrawType>* expected, int accumulatedClips) { 416 bool needsSaveRestore = kNone_DrawOpType != draw && 417 (kNone_MatType != mat || kNone_ClipType != clip); 418 419 if (needsSaveRestore) { 420 *expected->append() = SAVE; 421 } 422 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1); 423 emit_draw(canvas, draw, expected); 424 if (needsSaveRestore) { 425 *expected->append() = RESTORE; 426 } 427} 428 429// Emit: 430// matrix & clip calls 431// draw op 432// matrix & clip calls 433// draw op 434static void emit_body1(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 435 ClipType clip, DrawOpType draw, 436 SkTDArray<DrawType>* expected, int accumulatedClips) { 437 bool needsSaveRestore = kNone_DrawOpType != draw && 438 (kNone_MatType != mat || kNone_ClipType != clip); 439 440 if (needsSaveRestore) { 441 *expected->append() = SAVE; 442 } 443 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1); 444 emit_draw(canvas, draw, expected); 445 if (needsSaveRestore) { 446 *expected->append() = RESTORE; 447 *expected->append() = SAVE; 448 } 449 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2); 450 emit_draw(canvas, draw, expected); 451 if (needsSaveRestore) { 452 *expected->append() = RESTORE; 453 } 454} 455 456// Emit: 457// matrix & clip calls 458// SaveLayer 459// matrix & clip calls 460// draw op 461// Restore 462static void emit_body2(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 463 ClipType clip, DrawOpType draw, 464 SkTDArray<DrawType>* expected, int accumulatedClips) { 465 bool needsSaveRestore = kNone_DrawOpType != draw && 466 (kNone_MatType != mat || kNone_ClipType != clip); 467 468 if (needsSaveRestore) { 469 *expected->append() = SAVE_LAYER; 470 } 471 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 472 // TODO: widen testing to exercise saveLayer's parameters 473 canvas->saveLayer(NULL, NULL); 474 if (needsSaveRestore) { 475 *expected->append() = SAVE; 476 } 477 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2); 478 emit_draw(canvas, draw, expected); 479 if (needsSaveRestore) { 480 *expected->append() = RESTORE; 481 } 482 canvas->restore(); 483 if (needsSaveRestore) { 484 *expected->append() = RESTORE; 485 } 486} 487 488// Emit: 489// matrix & clip calls 490// SaveLayer 491// matrix & clip calls 492// SaveLayer 493// matrix & clip calls 494// draw op 495// Restore 496// matrix & clip calls (will be ignored) 497// Restore 498static void emit_body3(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 499 ClipType clip, DrawOpType draw, 500 SkTDArray<DrawType>* expected, int accumulatedClips) { 501 bool needsSaveRestore = kNone_DrawOpType != draw && 502 (kNone_MatType != mat || kNone_ClipType != clip); 503 504 // This saveLayer will always be forced b.c. we currently can't tell 505 // ahead of time if it will be empty (see comment in SkMatrixClipStateMgr::save) 506 *expected->append() = SAVE_LAYER; 507 508 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 509 // TODO: widen testing to exercise saveLayer's parameters 510 canvas->saveLayer(NULL, NULL); 511 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 512 if (needsSaveRestore) { 513 *expected->append() = SAVE_LAYER; 514 } 515 // TODO: widen testing to exercise saveLayer's parameters 516 canvas->saveLayer(NULL, NULL); 517 if (needsSaveRestore) { 518 *expected->append() = SAVE; 519 } 520 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+3); 521 emit_draw(canvas, draw, expected); 522 if (needsSaveRestore) { 523 *expected->append() = RESTORE; 524 } 525 canvas->restore(); 526 if (needsSaveRestore) { 527 *expected->append() = RESTORE; 528 } 529 canvas->restore(); 530 531 // required to match forced SAVE_LAYER 532 *expected->append() = RESTORE; 533} 534 535////////////////////////////////////////////////////////////////////////////// 536 537// Emit: 538// Save 539// some body 540// Restore 541// Note: the outer save/restore are provided by beginRecording/endRecording 542static void emit_struct0(SkCanvas* canvas, 543 PFEmitMC emitMC, MatType mat, ClipType clip, 544 PFEmitBody emitBody, DrawOpType draw, 545 SkTDArray<DrawType>* expected) { 546 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 0); 547} 548 549// Emit: 550// Save 551// matrix & clip calls 552// Save 553// some body 554// Restore 555// matrix & clip calls (will be ignored) 556// Restore 557// Note: the outer save/restore are provided by beginRecording/endRecording 558static void emit_struct1(SkCanvas* canvas, 559 PFEmitMC emitMC, MatType mat, ClipType clip, 560 PFEmitBody emitBody, DrawOpType draw, 561 SkTDArray<DrawType>* expected) { 562 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 563 canvas->save(); 564 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 565 canvas->restore(); 566 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed 567} 568 569// Emit: 570// Save 571// matrix & clip calls 572// Save 573// some body 574// Restore 575// Save 576// some body 577// Restore 578// matrix & clip calls (will be ignored) 579// Restore 580// Note: the outer save/restore are provided by beginRecording/endRecording 581static void emit_struct2(SkCanvas* canvas, 582 PFEmitMC emitMC, MatType mat, ClipType clip, 583 PFEmitBody emitBody, DrawOpType draw, 584 SkTDArray<DrawType>* expected) { 585 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops 586 canvas->save(); 587 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 588 canvas->restore(); 589 canvas->save(); 590 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 591 canvas->restore(); 592 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get removed 593} 594 595// Emit: 596// Save 597// matrix & clip calls 598// Save 599// some body 600// Restore 601// Save 602// matrix & clip calls 603// Save 604// some body 605// Restore 606// Restore 607// matrix & clip calls (will be ignored) 608// Restore 609// Note: the outer save/restore are provided by beginRecording/endRecording 610static void emit_struct3(SkCanvas* canvas, 611 PFEmitMC emitMC, MatType mat, ClipType clip, 612 PFEmitBody emitBody, DrawOpType draw, 613 SkTDArray<DrawType>* expected) { 614 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get fused into later ops 615 canvas->save(); 616 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 617 canvas->restore(); 618 canvas->save(); 619 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops 620 canvas->save(); 621 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2); 622 canvas->restore(); 623 canvas->restore(); 624 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed 625} 626 627////////////////////////////////////////////////////////////////////////////// 628 629#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE 630static void print(const SkTDArray<DrawType>& expected, const SkTDArray<DrawType>& actual) { 631 SkDebugf("\n\nexpected %d --- actual %d\n", expected.count(), actual.count()); 632 int max = SkMax32(expected.count(), actual.count()); 633 634 for (int i = 0; i < max; ++i) { 635 if (i < expected.count()) { 636 SkDebugf("%16s, ", SkDrawCommand::GetCommandString(expected[i])); 637 } else { 638 SkDebugf("%16s, ", " "); 639 } 640 641 if (i < actual.count()) { 642 SkDebugf("%s\n", SkDrawCommand::GetCommandString(actual[i])); 643 } else { 644 SkDebugf("\n"); 645 } 646 } 647 SkDebugf("\n\n"); 648 SkASSERT(0); 649} 650#endif 651 652static void test_collapse(skiatest::Reporter* reporter) { 653 PFEmitStruct gStructure[] = { emit_struct0, emit_struct1, emit_struct2, emit_struct3 }; 654 PFEmitBody gBody[] = { emit_body0, emit_body1, emit_body2, emit_body3 }; 655 PFEmitMC gMCs[] = { emit_clip_and_mat, emit_mat_and_clip, 656 emit_double_mat_and_clip, emit_mat_clip_clip }; 657 658 for (size_t i = 0; i < SK_ARRAY_COUNT(gStructure); ++i) { 659 for (size_t j = 0; j < SK_ARRAY_COUNT(gBody); ++j) { 660 for (size_t k = 0; k < SK_ARRAY_COUNT(gMCs); ++k) { 661 for (int l = 0; l < kMatTypeCount; ++l) { 662 for (int m = 0; m < kClipTypeCount; ++m) { 663 for (int n = 0; n < kDrawOpTypeCount; ++n) { 664#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE 665 static int testID = -1; 666 ++testID; 667 if (testID < -1) { 668 continue; 669 } 670#endif 671 672 SkTDArray<DrawType> expected, actual; 673 674 SkPicture picture; 675 676 // Note: beginRecording/endRecording add a save/restore pair 677 SkCanvas* canvas = picture.beginRecording(100, 100); 678 (*gStructure[i])(canvas, 679 gMCs[k], 680 (MatType) l, 681 (ClipType) m, 682 gBody[j], 683 (DrawOpType) n, 684 &expected); 685 picture.endRecording(); 686 687 gets_ops(picture, &actual); 688 689 REPORTER_ASSERT(reporter, expected.count() == actual.count()); 690 691 if (expected.count() != actual.count()) { 692#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE 693 print(expected, actual); 694#endif 695 continue; 696 } 697 698 for (int i = 0; i < expected.count(); ++i) { 699 REPORTER_ASSERT(reporter, expected[i] == actual[i]); 700#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE 701 if (expected[i] != actual[i]) { 702 print(expected, actual); 703 } 704#endif 705 break; 706 } 707 } 708 } 709 } 710 } 711 } 712 } 713} 714 715DEF_TEST(MatrixClipCollapse, reporter) { 716 test_collapse(reporter); 717} 718 719#endif 720