Home | History | Annotate | Download | only in tests
      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