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