1 /* 2 * Copyright 2012 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 "PathOpsExtendedTest.h" 9 #include "PathOpsThreadedCommon.h" 10 #include "SkBitmap.h" 11 #include "SkCanvas.h" 12 #include "SkMatrix.h" 13 #include "SkMutex.h" 14 #include "SkPaint.h" 15 #include "SkRegion.h" 16 #include "SkStream.h" 17 18 #include <stdlib.h> 19 20 #ifdef SK_BUILD_FOR_MAC 21 #include <sys/sysctl.h> 22 #endif 23 24 // std::to_string isn't implemented on android 25 #include <sstream> 26 27 template <typename T> 28 std::string std_to_string(T value) 29 { 30 std::ostringstream os ; 31 os << value ; 32 return os.str() ; 33 } 34 35 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result 36 SkDEBUGPARAMS(bool skipAssert) 37 SkDEBUGPARAMS(const char* testName)); 38 39 bool SimplifyDebug(const SkPath& one, SkPath* result 40 SkDEBUGPARAMS(bool skipAssert) 41 SkDEBUGPARAMS(const char* testName)); 42 43 static const char marker[] = 44 "</div>\n" 45 "\n" 46 "<script type=\"text/javascript\">\n" 47 "\n" 48 "var testDivs = [\n"; 49 50 static const char* opStrs[] = { 51 "kDifference_SkPathOp", 52 "kIntersect_SkPathOp", 53 "kUnion_SkPathOp", 54 "kXOR_PathOp", 55 "kReverseDifference_SkPathOp", 56 }; 57 58 static const char* opSuffixes[] = { 59 "d", 60 "i", 61 "u", 62 "o", 63 "r", 64 }; 65 66 enum class ExpectSuccess { 67 kNo, 68 kYes, 69 kFlaky 70 }; 71 72 enum class SkipAssert { 73 kNo, 74 kYes 75 }; 76 77 enum class ExpectMatch { 78 kNo, 79 kYes, 80 kFlaky 81 }; 82 83 #if DEBUG_SHOW_TEST_NAME 84 static void showPathData(const SkPath& path) { 85 SkPath::RawIter iter(path); 86 uint8_t verb; 87 SkPoint pts[4]; 88 SkPoint firstPt = {0, 0}, lastPt = {0, 0}; 89 bool firstPtSet = false; 90 bool lastPtSet = true; 91 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 92 switch (verb) { 93 case SkPath::kMove_Verb: 94 if (firstPtSet && lastPtSet && firstPt != lastPt) { 95 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY, 96 firstPt.fX, firstPt.fY); 97 lastPtSet = false; 98 } 99 firstPt = pts[0]; 100 firstPtSet = true; 101 continue; 102 case SkPath::kLine_Verb: 103 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY, 104 pts[1].fX, pts[1].fY); 105 lastPt = pts[1]; 106 lastPtSet = true; 107 break; 108 case SkPath::kQuad_Verb: 109 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", 110 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); 111 lastPt = pts[2]; 112 lastPtSet = true; 113 break; 114 case SkPath::kConic_Verb: 115 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}, //weight=%1.9g\n", 116 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, 117 iter.conicWeight()); 118 lastPt = pts[2]; 119 lastPtSet = true; 120 break; 121 case SkPath::kCubic_Verb: 122 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", 123 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, 124 pts[3].fX, pts[3].fY); 125 lastPt = pts[3]; 126 lastPtSet = true; 127 break; 128 case SkPath::kClose_Verb: 129 if (firstPtSet && lastPtSet && firstPt != lastPt) { 130 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY, 131 firstPt.fX, firstPt.fY); 132 } 133 firstPtSet = lastPtSet = false; 134 break; 135 default: 136 SkDEBUGFAIL("bad verb"); 137 return; 138 } 139 } 140 if (firstPtSet && lastPtSet && firstPt != lastPt) { 141 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY, 142 firstPt.fX, firstPt.fY); 143 } 144 } 145 #endif 146 147 void showOp(const SkPathOp op) { 148 switch (op) { 149 case kDifference_SkPathOp: 150 SkDebugf("op difference\n"); 151 break; 152 case kIntersect_SkPathOp: 153 SkDebugf("op intersect\n"); 154 break; 155 case kUnion_SkPathOp: 156 SkDebugf("op union\n"); 157 break; 158 case kXOR_SkPathOp: 159 SkDebugf("op xor\n"); 160 break; 161 case kReverseDifference_SkPathOp: 162 SkDebugf("op reverse difference\n"); 163 break; 164 default: 165 SkASSERT(0); 166 } 167 } 168 169 #if DEBUG_SHOW_TEST_NAME 170 static char hexorator(int x) { 171 if (x < 10) { 172 return x + '0'; 173 } 174 x -= 10; 175 SkASSERT(x < 26); 176 return x + 'A'; 177 } 178 #endif 179 180 void ShowTestName(PathOpsThreadState* state, int a, int b, int c, int d) { 181 #if DEBUG_SHOW_TEST_NAME 182 state->fSerialNo[0] = hexorator(state->fA); 183 state->fSerialNo[1] = hexorator(state->fB); 184 state->fSerialNo[2] = hexorator(state->fC); 185 state->fSerialNo[3] = hexorator(state->fD); 186 state->fSerialNo[4] = hexorator(a); 187 state->fSerialNo[5] = hexorator(b); 188 state->fSerialNo[6] = hexorator(c); 189 state->fSerialNo[7] = hexorator(d); 190 state->fSerialNo[8] = '\0'; 191 SkDebugf("%s\n", state->fSerialNo); 192 if (strcmp(state->fSerialNo, state->fKey) == 0) { 193 SkDebugf("%s\n", state->fPathStr.c_str()); 194 } 195 #endif 196 } 197 198 const int bitWidth = 64; 199 const int bitHeight = 64; 200 201 static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { 202 SkRect larger = one.getBounds(); 203 larger.join(two.getBounds()); 204 SkScalar largerWidth = larger.width(); 205 if (largerWidth < 4) { 206 largerWidth = 4; 207 } 208 SkScalar largerHeight = larger.height(); 209 if (largerHeight < 4) { 210 largerHeight = 4; 211 } 212 SkScalar hScale = (bitWidth - 2) / largerWidth; 213 SkScalar vScale = (bitHeight - 2) / largerHeight; 214 scale.reset(); 215 scale.preScale(hScale, vScale); 216 larger.fLeft *= hScale; 217 larger.fRight *= hScale; 218 larger.fTop *= vScale; 219 larger.fBottom *= vScale; 220 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft 221 : 16000 < larger.fRight ? 16000 - larger.fRight : 0; 222 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop 223 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; 224 scale.postTranslate(dx, dy); 225 } 226 227 static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo, 228 int& error2x2) { 229 if (bits.width() == 0) { 230 bits.allocN32Pixels(bitWidth * 2, bitHeight); 231 } 232 SkCanvas canvas(bits); 233 canvas.drawColor(SK_ColorWHITE); 234 SkPaint paint; 235 canvas.save(); 236 const SkRect& bounds1 = scaledOne.getBounds(); 237 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); 238 canvas.drawPath(scaledOne, paint); 239 canvas.restore(); 240 canvas.save(); 241 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); 242 canvas.drawPath(scaledTwo, paint); 243 canvas.restore(); 244 int errors2 = 0; 245 int errors = 0; 246 for (int y = 0; y < bitHeight - 1; ++y) { 247 uint32_t* addr1 = bits.getAddr32(0, y); 248 uint32_t* addr2 = bits.getAddr32(0, y + 1); 249 uint32_t* addr3 = bits.getAddr32(bitWidth, y); 250 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1); 251 for (int x = 0; x < bitWidth - 1; ++x) { 252 // count 2x2 blocks 253 bool err = addr1[x] != addr3[x]; 254 if (err) { 255 errors2 += addr1[x + 1] != addr3[x + 1] 256 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1]; 257 errors++; 258 } 259 } 260 } 261 error2x2 = errors2; 262 return errors; 263 } 264 265 static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne, 266 SkPath& scaledTwo, int& error2x2) { 267 SkMatrix scale; 268 scaleMatrix(one, two, scale); 269 one.transform(scale, &scaledOne); 270 two.transform(scale, &scaledTwo); 271 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2); 272 } 273 274 bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) { 275 if (!drawPaths) { 276 return true; 277 } 278 const SkRect& bounds1 = one.getBounds(); 279 const SkRect& bounds2 = two.getBounds(); 280 SkRect larger = bounds1; 281 larger.join(bounds2); 282 SkBitmap bits; 283 char out[256]; 284 int bitWidth = SkScalarCeilToInt(larger.width()) + 2; 285 if (bitWidth * 2 + 1 >= (int) sizeof(out)) { 286 return false; 287 } 288 int bitHeight = SkScalarCeilToInt(larger.height()) + 2; 289 if (bitHeight >= (int) sizeof(out)) { 290 return false; 291 } 292 bits.allocN32Pixels(bitWidth * 2, bitHeight); 293 SkCanvas canvas(bits); 294 canvas.drawColor(SK_ColorWHITE); 295 SkPaint paint; 296 canvas.save(); 297 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); 298 canvas.drawPath(one, paint); 299 canvas.restore(); 300 canvas.save(); 301 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); 302 canvas.drawPath(two, paint); 303 canvas.restore(); 304 for (int y = 0; y < bitHeight; ++y) { 305 uint32_t* addr1 = bits.getAddr32(0, y); 306 int x; 307 char* outPtr = out; 308 for (x = 0; x < bitWidth; ++x) { 309 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; 310 } 311 *outPtr++ = '|'; 312 for (x = bitWidth; x < bitWidth * 2; ++x) { 313 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; 314 } 315 *outPtr++ = '\0'; 316 SkDebugf("%s\n", out); 317 } 318 return true; 319 } 320 321 int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one, 322 const SkPath& two, SkBitmap& bitmap) { 323 int errors2x2; 324 SkPath scaledOne, scaledTwo; 325 (void) pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2); 326 if (errors2x2 == 0) { 327 return 0; 328 } 329 const int MAX_ERRORS = 9; 330 return errors2x2 > MAX_ERRORS ? errors2x2 : 0; 331 } 332 333 static SkTDArray<SkPathOp> gTestOp; 334 335 static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two, 336 const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo, 337 const SkPathOp shapeOp, const SkMatrix& scale) { 338 SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs)); 339 if (!testName) { 340 testName = "xOp"; 341 } 342 SkDebugf("static void %s_%s(skiatest::Reporter* reporter, const char* filename) {\n", 343 testName, opSuffixes[shapeOp]); 344 *gTestOp.append() = shapeOp; 345 SkDebugf(" SkPath path, pathB;\n"); 346 SkPathOpsDebug::ShowOnePath(a, "path", false); 347 SkPathOpsDebug::ShowOnePath(b, "pathB", false); 348 SkDebugf(" testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]); 349 SkDebugf("}\n"); 350 drawAsciiPaths(scaledOne, scaledTwo, true); 351 } 352 353 SK_DECLARE_STATIC_MUTEX(compareDebugOut3); 354 355 static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one, 356 const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap, 357 const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale, 358 ExpectMatch expectMatch) { 359 int errors2x2; 360 const int MAX_ERRORS = 8; 361 (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2); 362 if (ExpectMatch::kNo == expectMatch) { 363 if (errors2x2 < MAX_ERRORS) { 364 REPORTER_ASSERT(reporter, 0); 365 } 366 return 0; 367 } 368 if (errors2x2 == 0) { 369 return 0; 370 } 371 if (ExpectMatch::kYes == expectMatch && errors2x2 >= MAX_ERRORS) { 372 SkAutoMutexAcquire autoM(compareDebugOut3); 373 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale); 374 SkDebugf("\n/*"); 375 REPORTER_ASSERT(reporter, 0); 376 SkDebugf(" */\n"); 377 } 378 return errors2x2 >= MAX_ERRORS ? errors2x2 : 0; 379 } 380 381 // Default values for when reporter->verbose() is false. 382 static int testNumber = 55; 383 static const char* testName = "pathOpTest"; 384 385 static void appendTestName(const char* nameSuffix, std::string& out) { 386 out += testName; 387 out += std_to_string(testNumber); 388 ++testNumber; 389 if (nameSuffix) { 390 out.append(nameSuffix); 391 } 392 } 393 394 static void appendTest(const char* pathStr, const char* pathPrefix, const char* nameSuffix, 395 const char* testFunction, bool twoPaths, std::string& out) { 396 #if 0 397 out.append("\n<div id=\""); 398 appendTestName(nameSuffix, out); 399 out.append("\">\n"); 400 if (pathPrefix) { 401 out.append(pathPrefix); 402 } 403 out.append(pathStr); 404 out.append("</div>\n\n"); 405 406 out.append(marker); 407 out.append(" "); 408 appendTestName(nameSuffix, out); 409 out.append(",\n\n\n"); 410 #endif 411 out.append("static void "); 412 appendTestName(nameSuffix, out); 413 out.append("(skiatest::Reporter* reporter) {\n SkPath path"); 414 if (twoPaths) { 415 out.append(", pathB"); 416 } 417 out.append(";\n"); 418 if (pathPrefix) { 419 out.append(pathPrefix); 420 } 421 out += pathStr; 422 out += " "; 423 out += testFunction; 424 #if 0 425 out.append("static void (*firstTest)() = "); 426 appendTestName(nameSuffix, out); 427 out.append(";\n\n"); 428 429 out.append("static struct {\n"); 430 out.append(" void (*fun)();\n"); 431 out.append(" const char* str;\n"); 432 out.append("} tests[] = {\n"); 433 out.append(" TEST("); 434 appendTestName(nameSuffix, out); 435 out.append("),\n"); 436 #endif 437 } 438 439 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut); 440 441 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state, 442 const char* pathStr) { 443 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType; 444 path.setFillType(fillType); 445 state.fReporter->bumpTestCount(); 446 if (!Simplify(path, &out)) { 447 SkDebugf("%s did not expect failure\n", __FUNCTION__); 448 REPORTER_ASSERT(state.fReporter, 0); 449 return false; 450 } 451 if (!state.fReporter->verbose()) { 452 return true; 453 } 454 int result = comparePaths(state.fReporter, nullptr, path, out, *state.fBitmap); 455 if (result) { 456 SkAutoMutexAcquire autoM(simplifyDebugOut); 457 std::string str; 458 const char* pathPrefix = nullptr; 459 const char* nameSuffix = nullptr; 460 if (fillType == SkPath::kEvenOdd_FillType) { 461 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n"; 462 nameSuffix = "x"; 463 } 464 const char testFunction[] = "testSimplify(reporter, path);"; 465 appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, str); 466 SkDebugf("%s", str.c_str()); 467 REPORTER_ASSERT(state.fReporter, 0); 468 } 469 state.fReporter->bumpTestCount(); 470 return result == 0; 471 } 472 473 static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename, 474 ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) { 475 #if 0 && DEBUG_SHOW_TEST_NAME 476 showPathData(path); 477 #endif 478 SkPath out; 479 if (!SimplifyDebug(path, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) 480 SkDEBUGPARAMS(testName))) { 481 if (ExpectSuccess::kYes == expectSuccess) { 482 SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename); 483 REPORTER_ASSERT(reporter, 0); 484 } 485 return false; 486 } else { 487 if (ExpectSuccess::kNo == expectSuccess) { 488 SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename); 489 REPORTER_ASSERT(reporter, 0); 490 } 491 } 492 SkBitmap bitmap; 493 int errors = comparePaths(reporter, filename, path, out, bitmap); 494 if (ExpectMatch::kNo == expectMatch) { 495 if (!errors) { 496 SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename); 497 REPORTER_ASSERT(reporter, 0); 498 return false; 499 } 500 } else if (ExpectMatch::kYes == expectMatch && errors) { 501 REPORTER_ASSERT(reporter, 0); 502 } 503 reporter->bumpTestCount(); 504 return errors == 0; 505 } 506 507 bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { 508 return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo, 509 ExpectMatch::kYes); 510 } 511 512 bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { 513 return inner_simplify(reporter, path, filename, ExpectSuccess::kFlaky, SkipAssert::kYes, 514 ExpectMatch::kFlaky); 515 } 516 517 bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename, 518 bool checkFail) { 519 return inner_simplify(reporter, path, filename, checkFail ? 520 ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo); 521 } 522 523 #if DEBUG_SHOW_TEST_NAME 524 static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) { 525 SkDebugf("\n"); 526 showPathData(a); 527 showOp(shapeOp); 528 showPathData(b); 529 } 530 #endif 531 532 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 533 const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess, 534 SkipAssert skipAssert, ExpectMatch expectMatch) { 535 #if 0 && DEBUG_SHOW_TEST_NAME 536 showName(a, b, shapeOp); 537 #endif 538 SkPath out; 539 if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) 540 SkDEBUGPARAMS(testName))) { 541 if (ExpectSuccess::kYes == expectSuccess) { 542 SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName); 543 REPORTER_ASSERT(reporter, 0); 544 } 545 return false; 546 } else { 547 if (ExpectSuccess::kNo == expectSuccess) { 548 SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName); 549 REPORTER_ASSERT(reporter, 0); 550 } 551 } 552 if (!reporter->verbose()) { 553 return true; 554 } 555 SkPath pathOut, scaledPathOut; 556 SkRegion rgnA, rgnB, openClip, rgnOut; 557 openClip.setRect(-16000, -16000, 16000, 16000); 558 rgnA.setPath(a, openClip); 559 rgnB.setPath(b, openClip); 560 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp); 561 rgnOut.getBoundaryPath(&pathOut); 562 563 SkMatrix scale; 564 scaleMatrix(a, b, scale); 565 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut; 566 SkPath scaledA, scaledB; 567 scaledA.addPath(a, scale); 568 scaledA.setFillType(a.getFillType()); 569 scaledB.addPath(b, scale); 570 scaledB.setFillType(b.getFillType()); 571 scaledRgnA.setPath(scaledA, openClip); 572 scaledRgnB.setPath(scaledB, openClip); 573 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp); 574 scaledRgnOut.getBoundaryPath(&scaledPathOut); 575 SkBitmap bitmap; 576 SkPath scaledOut; 577 scaledOut.addPath(out, scale); 578 scaledOut.setFillType(out.getFillType()); 579 int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap, 580 a, b, shapeOp, scale, expectMatch); 581 reporter->bumpTestCount(); 582 return result == 0; 583 } 584 585 bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 586 const SkPathOp shapeOp, const char* testName) { 587 return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo, 588 ExpectMatch::kYes); 589 } 590 591 bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 592 const SkPathOp shapeOp, const char* testName, bool checkFail) { 593 return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ? 594 ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo); 595 } 596 597 bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 598 const SkPathOp shapeOp, const char* testName) { 599 return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kFlaky, SkipAssert::kYes, 600 ExpectMatch::kFlaky); 601 } 602 603 bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 604 const SkPathOp shapeOp, const char* testName) { 605 #if DEBUG_SHOW_TEST_NAME 606 showName(a, b, shapeOp); 607 #endif 608 SkPath orig; 609 orig.lineTo(54, 43); 610 SkPath out = orig; 611 if (Op(a, b, shapeOp, &out) ) { 612 SkDebugf("%s test is expected to fail\n", __FUNCTION__); 613 REPORTER_ASSERT(reporter, 0); 614 return false; 615 } 616 SkASSERT(out == orig); 617 return true; 618 } 619 620 SK_DECLARE_STATIC_MUTEX(gMutex); 621 622 void initializeTests(skiatest::Reporter* reporter, const char* test) { 623 if (reporter->verbose()) { 624 SkAutoMutexAcquire lock(gMutex); 625 testName = test; 626 size_t testNameSize = strlen(test); 627 SkFILEStream inFile("../../experimental/Intersection/op.htm"); 628 if (inFile.isValid()) { 629 SkTDArray<char> inData; 630 inData.setCount((int) inFile.getLength()); 631 size_t inLen = inData.count(); 632 inFile.read(inData.begin(), inLen); 633 inFile.close(); 634 char* insert = strstr(inData.begin(), marker); 635 if (insert) { 636 insert += sizeof(marker) - 1; 637 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1; 638 testNumber = atoi(numLoc) + 1; 639 } 640 } 641 } 642 } 643 644 void PathOpsThreadState::outputProgress(const char* pathStr, SkPath::FillType pathFillType) { 645 const char testFunction[] = "testSimplify(path);"; 646 const char* pathPrefix = nullptr; 647 const char* nameSuffix = nullptr; 648 if (pathFillType == SkPath::kEvenOdd_FillType) { 649 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n"; 650 nameSuffix = "x"; 651 } 652 appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, fPathStr); 653 } 654 655 void PathOpsThreadState::outputProgress(const char* pathStr, SkPathOp op) { 656 const char testFunction[] = "testOp(path);"; 657 SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes)); 658 const char* nameSuffix = opSuffixes[op]; 659 appendTest(pathStr, nullptr, nameSuffix, testFunction, true, fPathStr); 660 } 661 662 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count, 663 void (*firstTest)(skiatest::Reporter* , const char* filename), 664 void (*skipTest)(skiatest::Reporter* , const char* filename), 665 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) { 666 size_t index; 667 if (firstTest) { 668 index = count - 1; 669 while (index > 0 && tests[index].fun != firstTest) { 670 --index; 671 } 672 #if DEBUG_SHOW_TEST_NAME 673 SkDebugf("\n<div id=\"%s\">\n", tests[index].str); 674 #endif 675 (*tests[index].fun)(reporter, tests[index].str); 676 if (tests[index].fun == stopTest) { 677 return; 678 } 679 } 680 index = reverse ? count - 1 : 0; 681 size_t last = reverse ? 0 : count - 1; 682 bool foundSkip = !skipTest; 683 do { 684 if (tests[index].fun == skipTest) { 685 foundSkip = true; 686 } 687 if (foundSkip && tests[index].fun != firstTest) { 688 #if DEBUG_SHOW_TEST_NAME 689 SkDebugf("\n<div id=\"%s\">\n", tests[index].str); 690 #endif 691 (*tests[index].fun)(reporter, tests[index].str); 692 } 693 if (tests[index].fun == stopTest || index == last) { 694 break; 695 } 696 index += reverse ? -1 : 1; 697 } while (true); 698 #if DEBUG_SHOW_TEST_NAME 699 SkDebugf( 700 "\n" 701 "</div>\n" 702 "\n" 703 "<script type=\"text/javascript\">\n" 704 "\n" 705 "var testDivs = [\n" 706 ); 707 index = reverse ? count - 1 : 0; 708 last = reverse ? 0 : count - 1; 709 foundSkip = !skipTest; 710 do { 711 if (tests[index].fun == skipTest) { 712 foundSkip = true; 713 } 714 if (foundSkip && tests[index].fun != firstTest) { 715 SkDebugf(" %s,\n", tests[index].str); 716 } 717 if (tests[index].fun == stopTest || index == last) { 718 break; 719 } 720 index += reverse ? -1 : 1; 721 } while (true); 722 #endif 723 } 724