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