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