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