PathOpsSkpClipTest.cpp revision 297aaf97a32ac16a2bc3f4bbc231d5de859ac02d
1#include "CrashHandler.h" 2// #include "OverwriteLine.h" 3#include "Resources.h" 4#include "SkBitmap.h" 5#include "SkCanvas.h" 6#include "SkColor.h" 7#include "SkColorPriv.h" 8#include "SkCommandLineFlags.h" 9#include "SkDevice.h" 10#include "SkForceLinking.h" 11#include "SkGraphics.h" 12#include "SkImageDecoder.h" 13#include "SkImageEncoder.h" 14#include "SkOSFile.h" 15#include "SkPathOpsDebug.h" 16#include "SkPicture.h" 17#include "SkRTConf.h" 18#include "SkRunnable.h" 19#include "SkTSort.h" 20#include "SkStream.h" 21#include "SkString.h" 22#include "SkTArray.h" 23#include "SkTDArray.h" 24#include "SkTaskGroup.h" 25#include "SkTemplates.h" 26#include "SkTime.h" 27 28__SK_FORCE_IMAGE_DECODER_LINKING; 29 30/* add local exceptions here */ 31/* TODO : add command flag interface */ 32const struct SkipOverTest { 33 int directory; 34 const char* filename; 35 bool blamePathOps; 36} skipOver[] = { 37 { 2, "http___www_groupon_sg_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); 38 { 6, "http___www_googleventures_com_.skp", true}, // addTCoincident SkASSERT(test->fT < 1); 39 { 7, "http___www_foxsports_nl_.skp", true}, // (no repro on mac) addT SkASSERT(this != other || fVerb == SkPath::kCubic_Verb) 40 {13, "http___www_modernqigong_com_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); 41 {14, "http___www_devbridge_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple); 42 {16, "http___www_1023world_net_.skp", false}, // bitmap decode assert (corrupt skp?) 43 {19, "http___www_alamdi_com_.skp", true}, // cubic/quad intersection 44 {26, "http___www_liveencounters_net_.skp", true}, // (no repro on mac) checkSmall addT:549 (line, expects cubic) 45 {28, "http___www_encros_fr_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); 46 {37, "http___www_familysurvivalprotocol_wordpress_com_.skp", true}, // bumpSpan SkASSERT(span->fOppValue >= 0); 47 {39, "http___sufeinet_com_.skp", false}, // bitmap decode assert (corrupt skp?) 48 {41, "http___www_rano360_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple); 49 {44, "http___www_firstunitedbank_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0); 50 {46, "http___www_shinydemos_com_.skp", true}, // addSimpleAngle SkASSERT(index == count() - 2); 51 {48, "http___www_familysurvivalprotocol_com_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" 52 {57, "http___www_lptemp_com_.skp", true}, // addTCoincident oPeek = &other->fTs[++oPeekIndex]; 53 {71, "http___www_1milyonkahraman_org_.skp", true}, // addTCoincident SkASSERT(test->fT < 1); 54 {88, "http___www_apuntesdelechuza_wordpress_com_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" 55 {89, "http___www_mobilizedconsulting_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0); 56 {93, "http___www_simple_living_in_suffolk_co_uk_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" 57}; 58 59size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]); 60 61 62/* customize file in/out here */ 63/* TODO : add command flag interface */ 64#define CHROME_VERSION "1e5dfa4-4a995df" 65#define SUMMARY_RUN 1 66 67#ifdef SK_BUILD_FOR_WIN 68 #define DRIVE_SPEC "D:" 69 #define PATH_SLASH "\\" 70#else 71 #define DRIVE_SPEC "" 72 #define PATH_SLASH "/" 73#endif 74 75#define IN_DIR_PRE DRIVE_SPEC PATH_SLASH "skps" PATH_SLASH "slave" 76#define OUT_DIR_PRE DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "slave" 77#define OUT_DIR_SUM DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "summary" 78#define DIR_POST PATH_SLASH "All" PATH_SLASH CHROME_VERSION 79 80static const char outOpDir[] = "opClip"; 81static const char outOldDir[] = "oldClip"; 82static const char outStatusDir[] = "statusTest"; 83 84static SkString get_in_path(int dirNo, const char* filename) { 85 SkString path; 86 SkASSERT(dirNo); 87 path.appendf("%s%d%s", IN_DIR_PRE, dirNo, DIR_POST); 88 if (!sk_exists(path.c_str())) { 89 SkDebugf("could not read %s\n", path.c_str()); 90 return SkString(); 91 } 92 if (filename) { 93 path.appendf("%s%s", PATH_SLASH, filename); 94 if (!sk_exists(path.c_str())) { 95 SkDebugf("could not read %s\n", path.c_str()); 96 return SkString(); 97 } 98 } 99 return path; 100} 101 102static void make_recursive_dir(const SkString& path) { 103 if (sk_exists(path.c_str())) { 104 return; 105 } 106 const char* pathStr = path.c_str(); 107 int last = (int) path.size(); 108 do { 109 while (last > 0 && pathStr[--last] != PATH_SLASH[0]) 110 ; 111 SkASSERT(last > 0); 112 SkString shorter(pathStr, last); 113 if (sk_mkdir(shorter.c_str())) { 114 break; 115 } 116 } while (true); 117 do { 118 while (last < (int) path.size() && pathStr[++last] != PATH_SLASH[0]) 119 ; 120 SkString shorter(pathStr, last); 121 SkAssertResult(sk_mkdir(shorter.c_str())); 122 } while (last < (int) path.size()); 123} 124 125static SkString get_out_path(int dirNo, const char* dirName) { 126 SkString path; 127 SkASSERT(dirNo); 128 SkASSERT(dirName); 129 path.appendf("%s%d%s%s%s", OUT_DIR_PRE, dirNo, DIR_POST, PATH_SLASH, dirName); 130 make_recursive_dir(path); 131 return path; 132} 133 134static SkString get_sum_path(const char* dirName) { 135 SkString path; 136 SkASSERT(dirName); 137 path.appendf("%s%d%s%s", OUT_DIR_SUM, SUMMARY_RUN, PATH_SLASH, dirName); 138 SkDebugf("%s\n", path.c_str()); 139 make_recursive_dir(path); 140 return path; 141} 142 143static SkString make_png_name(const char* filename) { 144 SkString pngName = SkString(filename); 145 pngName.remove(pngName.size() - 3, 3); 146 pngName.append("png"); 147 return pngName; 148} 149 150//////////////////////////////////////////////////////// 151 152enum TestStep { 153 kCompareBits, 154 kEncodeFiles, 155}; 156 157enum { 158 kMaxLength = 256, 159 kMaxFiles = 128, 160 kSmallLimit = 1000, 161}; 162 163struct TestResult { 164 void init(int dirNo) { 165 fDirNo = dirNo; 166 sk_bzero(fFilename, sizeof(fFilename)); 167 fTestStep = kCompareBits; 168 fScale = 1; 169 } 170 171 void init(int dirNo, const SkString& filename) { 172 fDirNo = dirNo; 173 strcpy(fFilename, filename.c_str()); 174 fTestStep = kCompareBits; 175 fScale = 1; 176 } 177 178 SkString status() { 179 SkString outStr; 180 outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime); 181 return outStr; 182 } 183 184 SkString progress() { 185 SkString outStr; 186 outStr.printf("dir=%d %s ", fDirNo, fFilename); 187 if (fPixelError) { 188 outStr.appendf(" err=%d", fPixelError); 189 } 190 if (fTime) { 191 outStr.appendf(" time=%d", fTime); 192 } 193 if (fScale != 1) { 194 outStr.appendf(" scale=%d", fScale); 195 } 196 outStr.appendf("\n"); 197 return outStr; 198 199 } 200 201 void test(int dirNo, const SkString& filename) { 202 init(dirNo); 203 strcpy(fFilename, filename.c_str()); 204 testOne(); 205 } 206 207 void testOne(); 208 209 char fFilename[kMaxLength]; 210 TestStep fTestStep; 211 int fDirNo; 212 int fPixelError; 213 int fTime; 214 int fScale; 215}; 216 217class SortByPixel : public TestResult { 218public: 219 bool operator<(const SortByPixel& rh) const { 220 return fPixelError < rh.fPixelError; 221 } 222}; 223 224class SortByTime : public TestResult { 225public: 226 bool operator<(const SortByTime& rh) const { 227 return fTime < rh.fTime; 228 } 229}; 230 231class SortByName : public TestResult { 232public: 233 bool operator<(const SortByName& rh) const { 234 return strcmp(fFilename, rh.fFilename) < 0; 235 } 236}; 237 238struct TestState { 239 void init(int dirNo) { 240 fResult.init(dirNo); 241 } 242 243 SkTDArray<SortByPixel> fPixelWorst; 244 SkTDArray<SortByTime> fSlowest; 245 TestResult fResult; 246}; 247 248struct TestRunner { 249 ~TestRunner(); 250 void render(); 251 SkTDArray<class TestRunnable*> fRunnables; 252}; 253 254class TestRunnable : public SkRunnable { 255public: 256 void run() SK_OVERRIDE { 257 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); 258 (*fTestFun)(&fState); 259 } 260 261 TestState fState; 262 void (*fTestFun)(TestState*); 263}; 264 265 266class TestRunnableDir : public TestRunnable { 267public: 268 TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) { 269 fState.init(dirNo); 270 fTestFun = testFun; 271 } 272 273}; 274 275class TestRunnableFile : public TestRunnable { 276public: 277 TestRunnableFile(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) { 278 fState.init(dirNo); 279 strcpy(fState.fResult.fFilename, name); 280 fTestFun = testFun; 281 } 282}; 283 284class TestRunnableEncode : public TestRunnableFile { 285public: 286 TestRunnableEncode(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) 287 : TestRunnableFile(testFun, dirNo, name, runner) { 288 fState.fResult.fTestStep = kEncodeFiles; 289 } 290}; 291 292TestRunner::~TestRunner() { 293 for (int index = 0; index < fRunnables.count(); index++) { 294 SkDELETE(fRunnables[index]); 295 } 296} 297 298void TestRunner::render() { 299 SkTaskGroup tg; 300 for (int index = 0; index < fRunnables.count(); ++ index) { 301 tg.add(fRunnables[index]); 302 } 303} 304 305//////////////////////////////////////////////// 306 307 308static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { 309 const int kRowCount = 3; 310 const int kThreshold = 3; 311 int width = SkTMin(gr.width(), sk.width()); 312 if (width < kRowCount) { 313 return true; 314 } 315 int height = SkTMin(gr.height(), sk.height()); 316 if (height < kRowCount) { 317 return true; 318 } 319 int errorTotal = 0; 320 SkTArray<int, true> errorRows; 321 errorRows.push_back_n(width * kRowCount); 322 SkAutoLockPixels autoGr(gr); 323 SkAutoLockPixels autoSk(sk); 324 for (int y = 0; y < height; ++y) { 325 SkPMColor* grRow = gr.getAddr32(0, y); 326 SkPMColor* skRow = sk.getAddr32(0, y); 327 int* base = &errorRows[0]; 328 int* cOut = &errorRows[y % kRowCount]; 329 for (int x = 0; x < width; ++x) { 330 SkPMColor grColor = grRow[x]; 331 SkPMColor skColor = skRow[x]; 332 int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); 333 int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); 334 int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); 335 int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); 336 if (error < kThreshold || x < 2) { 337 continue; 338 } 339 if (base[x - 2] < kThreshold 340 || base[width + x - 2] < kThreshold 341 || base[width * 2 + x - 2] < kThreshold 342 || base[x - 1] < kThreshold 343 || base[width + x - 1] < kThreshold 344 || base[width * 2 + x - 1] < kThreshold 345 || base[x] < kThreshold 346 || base[width + x] < kThreshold 347 || base[width * 2 + x] < kThreshold) { 348 continue; 349 } 350 errorTotal += error; 351 } 352 } 353 return errorTotal; 354} 355 356static bool addError(TestState* data, const TestResult& testResult) { 357 if (testResult.fPixelError <= 0 && testResult.fTime <= 0) { 358 return false; 359 } 360 int worstCount = data->fPixelWorst.count(); 361 int pixelError = testResult.fPixelError; 362 if (pixelError > 0) { 363 for (int index = 0; index < worstCount; ++index) { 364 if (pixelError > data->fPixelWorst[index].fPixelError) { 365 data->fPixelWorst[index] = *(SortByPixel*) &testResult; 366 return true; 367 } 368 } 369 } 370 int slowCount = data->fSlowest.count(); 371 int time = testResult.fTime; 372 if (time > 0) { 373 for (int index = 0; index < slowCount; ++index) { 374 if (time > data->fSlowest[index].fTime) { 375 data->fSlowest[index] = *(SortByTime*) &testResult; 376 return true; 377 } 378 } 379 } 380 if (pixelError > 0 && worstCount < kMaxFiles) { 381 *data->fPixelWorst.append() = *(SortByPixel*) &testResult; 382 return true; 383 } 384 if (time > 0 && slowCount < kMaxFiles) { 385 *data->fSlowest.append() = *(SortByTime*) &testResult; 386 return true; 387 } 388 return false; 389} 390 391static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) { 392 canvas->save(); 393 SkScalar pWidth = pic->cullRect().width(); 394 SkScalar pHeight = pic->cullRect().height(); 395 const SkScalar maxDimension = 1000.0f; 396 const int slices = 3; 397 SkScalar xInterval = SkTMax(pWidth - maxDimension, 0.0f) / (slices - 1); 398 SkScalar yInterval = SkTMax(pHeight - maxDimension, 0.0f) / (slices - 1); 399 SkRect rect = {0, 0, SkTMin(maxDimension, pWidth), SkTMin(maxDimension, pHeight) }; 400 canvas->clipRect(rect); 401 SkMSec start = SkTime::GetMSecs(); 402 for (int x = 0; x < slices; ++x) { 403 for (int y = 0; y < slices; ++y) { 404 pic->playback(canvas); 405 canvas->translate(0, yInterval); 406 } 407 canvas->translate(xInterval, -yInterval * slices); 408 } 409 SkMSec end = SkTime::GetMSecs(); 410 canvas->restore(); 411 return end - start; 412} 413 414static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { 415 canvas->clear(SK_ColorWHITE); 416 if (scale != 1) { 417 canvas->save(); 418 canvas->scale(1.0f / scale, 1.0f / scale); 419 } 420 pic->playback(canvas); 421 if (scale != 1) { 422 canvas->restore(); 423 } 424} 425 426static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { 427 SkString outFile = get_sum_path(outDir); 428 outFile.appendf("%s%s", PATH_SLASH, pngName); 429 if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100)) { 430 SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName, 431 bitmap.width(), bitmap.height()); 432 } 433} 434 435void TestResult::testOne() { 436 SkPicture* pic = NULL; 437 { 438 #if DEBUG_SHOW_TEST_NAME 439 if (fTestStep == kCompareBits) { 440 SkString testName(fFilename); 441 const char http[] = "http"; 442 if (testName.startsWith(http)) { 443 testName.remove(0, sizeof(http) - 1); 444 } 445 while (testName.startsWith("_")) { 446 testName.remove(0, 1); 447 } 448 const char dotSkp[] = ".skp"; 449 if (testName.endsWith(dotSkp)) { 450 size_t len = testName.size(); 451 testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1); 452 } 453 testName.prepend("skp"); 454 testName.append("1"); 455 strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH); 456 } else if (fTestStep == kEncodeFiles) { 457 strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); 458 } 459 #endif 460 SkString path = get_in_path(fDirNo, fFilename); 461 SkFILEStream stream(path.c_str()); 462 if (!stream.isValid()) { 463 SkDebugf("invalid stream %s\n", path.c_str()); 464 goto finish; 465 } 466 pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); 467 if (!pic) { 468 SkDebugf("unable to decode %s\n", fFilename); 469 goto finish; 470 } 471 SkScalar width = pic->cullRect().width(); 472 SkScalar height = pic->cullRect().height(); 473 SkBitmap oldBitmap, opBitmap; 474 fScale = 1; 475 while (width / fScale > 32767 || height / fScale > 32767) { 476 ++fScale; 477 } 478 do { 479 int dimX = SkScalarCeilToInt(width / fScale); 480 int dimY = SkScalarCeilToInt(height / fScale); 481 if (oldBitmap.tryAllocN32Pixels(dimX, dimY) && opBitmap.tryAllocN32Pixels(dimX, dimY)) { 482 break; 483 } 484 SkDebugf("-%d-", fScale); 485 } while (++fScale < 256); 486 if (fScale >= 256) { 487 SkDebugf("unable to allocate bitmap for %s (w=%f h=%f)\n", fFilename, 488 width, height); 489 goto finish; 490 } 491 oldBitmap.eraseColor(SK_ColorWHITE); 492 SkCanvas oldCanvas(oldBitmap); 493 oldCanvas.setAllowSimplifyClip(false); 494 opBitmap.eraseColor(SK_ColorWHITE); 495 SkCanvas opCanvas(opBitmap); 496 opCanvas.setAllowSimplifyClip(true); 497 drawPict(pic, &oldCanvas, fScale); 498 drawPict(pic, &opCanvas, fScale); 499 if (fTestStep == kCompareBits) { 500 fPixelError = similarBits(oldBitmap, opBitmap); 501 int oldTime = timePict(pic, &oldCanvas); 502 int opTime = timePict(pic, &opCanvas); 503 fTime = SkTMax(0, oldTime - opTime); 504 } else if (fTestStep == kEncodeFiles) { 505 SkString pngStr = make_png_name(fFilename); 506 const char* pngName = pngStr.c_str(); 507 writePict(oldBitmap, outOldDir, pngName); 508 writePict(opBitmap, outOpDir, pngName); 509 } 510 } 511finish: 512 if (pic) { 513 pic->unref(); 514 } 515} 516 517DEFINE_string2(match, m, "PathOpsSkpClipThreaded", 518 "[~][^]substring[$] [...] of test name to run.\n" 519 "Multiple matches may be separated by spaces.\n" 520 "~ causes a matching test to always be skipped\n" 521 "^ requires the start of the test to match\n" 522 "$ requires the end of the test to match\n" 523 "^ and $ requires an exact match\n" 524 "If a test does not match any list entry,\n" 525 "it is skipped unless some list entry starts with ~"); 526DEFINE_string2(dir, d, NULL, "range of directories (e.g., 1-100)"); 527DEFINE_string2(skp, s, NULL, "skp to test"); 528DEFINE_bool2(single, z, false, "run tests on a single thread internally."); 529DEFINE_int32(testIndex, 0, "override local test index (PathOpsSkpClipOneOff only)."); 530DEFINE_bool2(verbose, v, false, "enable verbose output."); 531 532static bool verbose() { 533 return FLAGS_verbose; 534} 535 536class Dirs { 537public: 538 Dirs() { 539 reset(); 540 sk_bzero(fRun, sizeof(fRun)); 541 fSet = false; 542 } 543 544 int first() const { 545 int index = 0; 546 while (++index < kMaxDir) { 547 if (fRun[index]) { 548 return index; 549 } 550 } 551 SkASSERT(0); 552 return -1; 553 } 554 555 int last() const { 556 int index = kMaxDir; 557 while (--index > 0 && !fRun[index]) 558 ; 559 return index; 560 } 561 562 int next() { 563 while (++fIndex < kMaxDir) { 564 if (fRun[fIndex]) { 565 return fIndex; 566 } 567 } 568 return -1; 569 } 570 571 void reset() { 572 fIndex = -1; 573 } 574 575 void set(int start, int end) { 576 while (start < end) { 577 fRun[start++] = 1; 578 } 579 fSet = true; 580 } 581 582 void setDefault() { 583 if (!fSet) { 584 set(1, 100); 585 } 586 } 587 588private: 589 enum { 590 kMaxDir = 101 591 }; 592 char fRun[kMaxDir]; 593 int fIndex; 594 bool fSet; 595} gDirs; 596 597class Filenames { 598public: 599 Filenames() 600 : fIndex(-1) { 601 } 602 603 const char* next() { 604 while (fNames && ++fIndex < fNames->count()) { 605 return (*fNames)[fIndex]; 606 } 607 return NULL; 608 } 609 610 void set(const SkCommandLineFlags::StringArray& names) { 611 fNames = &names; 612 } 613 614private: 615 int fIndex; 616 const SkCommandLineFlags::StringArray* fNames; 617} gNames; 618 619static bool buildTestDir(int dirNo, int firstDirNo, 620 SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) { 621 SkString dirName = get_out_path(dirNo, outStatusDir); 622 if (!dirName.size()) { 623 return false; 624 } 625 SkOSFile::Iter iter(dirName.c_str(), "skp"); 626 SkString filename; 627 while (iter.next(&filename)) { 628 TestResult test; 629 test.init(dirNo); 630 SkString spaceFile(filename); 631 char* spaces = spaceFile.writable_str(); 632 int spaceSize = (int) spaceFile.size(); 633 for (int index = 0; index < spaceSize; ++index) { 634 if (spaces[index] == '.') { 635 spaces[index] = ' '; 636 } 637 } 638 int success = sscanf(spaces, "%s %d %d skp", test.fFilename, 639 &test.fPixelError, &test.fTime); 640 if (success < 3) { 641 SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success); 642 return false; 643 } 644 *tests[dirNo - firstDirNo].append() = test; 645 } 646 if (!sorted) { 647 return true; 648 } 649 SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo]; 650 int count = testSet.count(); 651 for (int index = 0; index < count; ++index) { 652 *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index]; 653 } 654 if (sorted[dirNo - firstDirNo].count()) { 655 SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(), 656 sorted[dirNo - firstDirNo].end() - 1); 657 if (verbose()) { 658 SkDebugf("+"); 659 } 660 } 661 return true; 662} 663 664static void testSkpClip(TestState* data) { 665 data->fResult.testOne(); 666 SkString statName(data->fResult.fFilename); 667 SkASSERT(statName.endsWith(".skp")); 668 statName.remove(statName.size() - 4, 4); 669 statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime); 670 SkString statusFile = get_out_path(data->fResult.fDirNo, outStatusDir); 671 if (!statusFile.size()) { 672 SkDebugf("failed to create %s", statusFile.c_str()); 673 return; 674 } 675 statusFile.appendf("%s%s", PATH_SLASH, statName.c_str()); 676 SkFILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag); 677 if (!file) { 678 SkDebugf("failed to create %s", statusFile.c_str()); 679 return; 680 } 681 sk_fclose(file); 682 if (verbose()) { 683 if (data->fResult.fPixelError || data->fResult.fTime) { 684 SkDebugf("%s", data->fResult.progress().c_str()); 685 } else { 686 SkDebugf("."); 687 } 688 } 689} 690 691bool Less(const SortByName& a, const SortByName& b); 692bool Less(const SortByName& a, const SortByName& b) { 693 return a < b; 694} 695 696static bool doOneDir(TestState* state, bool threaded) { 697 int dirNo = state->fResult.fDirNo; 698 SkString dirName = get_in_path(dirNo, NULL); 699 if (!dirName.size()) { 700 return false; 701 } 702 SkTDArray<TestResult> tests[1]; 703 SkTDArray<SortByName*> sorted[1]; 704 if (!buildTestDir(dirNo, dirNo, tests, sorted)) { 705 return false; 706 } 707 SkOSFile::Iter iter(dirName.c_str(), "skp"); 708 SkString filename; 709 while (iter.next(&filename)) { 710 for (size_t index = 0; index < skipOverCount; ++index) { 711 if (skipOver[index].directory == dirNo 712 && strcmp(filename.c_str(), skipOver[index].filename) == 0) { 713 goto checkEarlyExit; 714 } 715 } 716 { 717 SortByName name; 718 name.init(dirNo); 719 strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp 720 int count = sorted[0].count(); 721 int idx = SkTSearch<SortByName, Less>(sorted[0].begin(), count, &name, sizeof(&name)); 722 if (idx >= 0) { 723 SortByName* found = sorted[0][idx]; 724 (void) addError(state, *found); 725 continue; 726 } 727 TestResult test; 728 test.init(dirNo, filename); 729 state->fResult = test; 730 testSkpClip(state); 731#if 0 // artificially limit to a few while debugging code 732 static int debugLimit = 0; 733 if (++debugLimit == 5) { 734 return true; 735 } 736#endif 737 } 738checkEarlyExit: 739 ; 740 } 741 return true; 742} 743 744static void initTest() { 745#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC 746 SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); 747 SK_CONF_SET("images.png.suppressDecoderWarnings", true); 748#endif 749} 750 751static void testSkpClipEncode(TestState* data) { 752 data->fResult.testOne(); 753 if (verbose()) { 754 SkDebugf("+"); 755 } 756} 757 758static void encodeFound(TestState& state) { 759 if (verbose()) { 760 if (state.fPixelWorst.count()) { 761 SkTDArray<SortByPixel*> worst; 762 for (int index = 0; index < state.fPixelWorst.count(); ++index) { 763 *worst.append() = &state.fPixelWorst[index]; 764 } 765 SkTQSort<SortByPixel>(worst.begin(), worst.end() - 1); 766 for (int index = 0; index < state.fPixelWorst.count(); ++index) { 767 const TestResult& result = *worst[index]; 768 SkDebugf("%d %s pixelError=%d\n", result.fDirNo, result.fFilename, result.fPixelError); 769 } 770 } 771 if (state.fSlowest.count()) { 772 SkTDArray<SortByTime*> slowest; 773 for (int index = 0; index < state.fSlowest.count(); ++index) { 774 *slowest.append() = &state.fSlowest[index]; 775 } 776 if (slowest.count() > 0) { 777 SkTQSort<SortByTime>(slowest.begin(), slowest.end() - 1); 778 for (int index = 0; index < slowest.count(); ++index) { 779 const TestResult& result = *slowest[index]; 780 SkDebugf("%d %s time=%d\n", result.fDirNo, result.fFilename, result.fTime); 781 } 782 } 783 } 784 } 785 TestRunner testRunner; 786 for (int index = 0; index < state.fPixelWorst.count(); ++index) { 787 const TestResult& result = state.fPixelWorst[index]; 788 SkString filename(result.fFilename); 789 if (!filename.endsWith(".skp")) { 790 filename.append(".skp"); 791 } 792 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableEncode, 793 (&testSkpClipEncode, result.fDirNo, filename.c_str(), &testRunner)); 794 } 795 testRunner.render(); 796} 797 798class Test { 799public: 800 Test() {} 801 virtual ~Test() {} 802 803 const char* getName() { onGetName(&fName); return fName.c_str(); } 804 void run() { onRun(); } 805 806protected: 807 virtual void onGetName(SkString*) = 0; 808 virtual void onRun() = 0; 809 810private: 811 SkString fName; 812}; 813 814typedef SkTRegistry<Test*(*)(void*)> TestRegistry; 815 816#define DEF_TEST(name) \ 817 static void test_##name(); \ 818 class name##Class : public Test { \ 819 public: \ 820 static Test* Factory(void*) { return SkNEW(name##Class); } \ 821 protected: \ 822 void onGetName(SkString* name) SK_OVERRIDE { \ 823 name->set(#name); \ 824 } \ 825 void onRun() SK_OVERRIDE { test_##name(); } \ 826 }; \ 827 static TestRegistry gReg_##name##Class(name##Class::Factory); \ 828 static void test_##name() 829 830DEF_TEST(PathOpsSkpClip) { 831 gDirs.setDefault(); 832 initTest(); 833 SkTArray<TestResult, true> errors; 834 TestState state; 835 state.init(0); 836 int dirNo; 837 gDirs.reset(); 838 while ((dirNo = gDirs.next()) > 0) { 839 if (verbose()) { 840 SkDebugf("dirNo=%d\n", dirNo); 841 } 842 state.fResult.fDirNo = dirNo; 843 if (!doOneDir(&state, false)) { 844 break; 845 } 846 } 847 encodeFound(state); 848} 849 850static void testSkpClipMain(TestState* data) { 851 (void) doOneDir(data, true); 852} 853 854DEF_TEST(PathOpsSkpClipThreaded) { 855 gDirs.setDefault(); 856 initTest(); 857 TestRunner testRunner; 858 int dirNo; 859 gDirs.reset(); 860 while ((dirNo = gDirs.next()) > 0) { 861 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableDir, 862 (&testSkpClipMain, dirNo, &testRunner)); 863 } 864 testRunner.render(); 865 TestState state; 866 state.init(0); 867 gDirs.reset(); 868 while ((dirNo = gDirs.next()) > 0) { 869 TestState& testState = testRunner.fRunnables[dirNo - 1]->fState; 870 SkASSERT(testState.fResult.fDirNo == dirNo); 871 for (int inner = 0; inner < testState.fPixelWorst.count(); ++inner) { 872 addError(&state, testState.fPixelWorst[inner]); 873 } 874 for (int inner = 0; inner < testState.fSlowest.count(); ++inner) { 875 addError(&state, testState.fSlowest[inner]); 876 } 877 } 878 encodeFound(state); 879} 880 881static bool buildTests(SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) { 882 int firstDirNo = gDirs.first(); 883 int dirNo; 884 while ((dirNo = gDirs.next()) > 0) { 885 if (!buildTestDir(dirNo, firstDirNo, tests, sorted)) { 886 return false; 887 } 888 } 889 return true; 890} 891 892DEF_TEST(PathOpsSkpClipUberThreaded) { 893 gDirs.setDefault(); 894 const int firstDirNo = gDirs.next(); 895 const int lastDirNo = gDirs.last(); 896 initTest(); 897 int dirCount = lastDirNo - firstDirNo + 1; 898 SkAutoTDeleteArray<SkTDArray<TestResult> > tests(new SkTDArray<TestResult>[dirCount]); 899 SkAutoTDeleteArray<SkTDArray<SortByName*> > sorted(new SkTDArray<SortByName*>[dirCount]); 900 if (!buildTests(tests.get(), sorted.get())) { 901 return; 902 } 903 TestRunner testRunner; 904 int dirNo; 905 gDirs.reset(); 906 while ((dirNo = gDirs.next()) > 0) { 907 SkString dirName = get_in_path(dirNo, NULL); 908 if (!dirName.size()) { 909 continue; 910 } 911 SkOSFile::Iter iter(dirName.c_str(), "skp"); 912 SkString filename; 913 while (iter.next(&filename)) { 914 for (size_t index = 0; index < skipOverCount; ++index) { 915 if (skipOver[index].directory == dirNo 916 && strcmp(filename.c_str(), skipOver[index].filename) == 0) { 917 goto checkEarlyExit; 918 } 919 } 920 { 921 SortByName name; 922 name.init(dirNo); 923 strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp 924 int count = sorted.get()[dirNo - firstDirNo].count(); 925 if (SkTSearch<SortByName, Less>(sorted.get()[dirNo - firstDirNo].begin(), 926 count, &name, sizeof(&name)) < 0) { 927 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableFile, 928 (&testSkpClip, dirNo, filename.c_str(), &testRunner)); 929 } 930 } 931 checkEarlyExit: 932 ; 933 } 934 935 } 936 testRunner.render(); 937 SkAutoTDeleteArray<SkTDArray<TestResult> > results(new SkTDArray<TestResult>[dirCount]); 938 if (!buildTests(results.get(), NULL)) { 939 return; 940 } 941 SkTDArray<TestResult> allResults; 942 for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) { 943 SkTDArray<TestResult>& array = results.get()[dirNo - firstDirNo]; 944 allResults.append(array.count(), array.begin()); 945 } 946 int allCount = allResults.count(); 947 SkTDArray<SortByPixel*> pixels; 948 SkTDArray<SortByTime*> times; 949 for (int index = 0; index < allCount; ++index) { 950 *pixels.append() = (SortByPixel*) &allResults[index]; 951 *times.append() = (SortByTime*) &allResults[index]; 952 } 953 TestState state; 954 if (pixels.count()) { 955 SkTQSort<SortByPixel>(pixels.begin(), pixels.end() - 1); 956 for (int inner = 0; inner < kMaxFiles; ++inner) { 957 *state.fPixelWorst.append() = *pixels[allCount - inner - 1]; 958 } 959 } 960 if (times.count()) { 961 SkTQSort<SortByTime>(times.begin(), times.end() - 1); 962 for (int inner = 0; inner < kMaxFiles; ++inner) { 963 *state.fSlowest.append() = *times[allCount - inner - 1]; 964 } 965 } 966 encodeFound(state); 967} 968 969DEF_TEST(PathOpsSkpClipOneOff) { 970 const int testIndex = FLAGS_testIndex; 971 int dirNo = gDirs.next(); 972 if (dirNo < 0) { 973 dirNo = skipOver[testIndex].directory; 974 } 975 const char* skp = gNames.next(); 976 if (!skp) { 977 skp = skipOver[testIndex].filename; 978 } 979 initTest(); 980 SkAssertResult(get_in_path(dirNo, skp).size()); 981 SkString filename(skp); 982 TestResult state; 983 state.test(dirNo, filename); 984 if (verbose()) { 985 SkDebugf("%s", state.status().c_str()); 986 } 987 state.fTestStep = kEncodeFiles; 988 state.testOne(); 989} 990 991DEF_TEST(PathOpsTestSkipped) { 992 for (size_t index = 0; index < skipOverCount; ++index) { 993 const SkipOverTest& skip = skipOver[index]; 994 if (!skip.blamePathOps) { 995 continue; 996 } 997 int dirNo = skip.directory; 998 const char* skp = skip.filename; 999 initTest(); 1000 SkAssertResult(get_in_path(dirNo, skp).size()); 1001 SkString filename(skp); 1002 TestResult state; 1003 state.test(dirNo, filename); 1004 if (verbose()) { 1005 SkDebugf("%s", state.status().c_str()); 1006 } 1007 state.fTestStep = kEncodeFiles; 1008 state.testOne(); 1009 } 1010} 1011 1012DEF_TEST(PathOpsCopyFails) { 1013 FLAGS_verbose = true; 1014 for (size_t index = 0; index < skipOverCount; ++index) { 1015 int dirNo = skipOver[index].directory; 1016 SkDebugf("mkdir -p " IN_DIR_PRE "%d" DIR_POST "\n", dirNo); 1017 } 1018 for (size_t index = 0; index < skipOverCount; ++index) { 1019 int dirNo = skipOver[index].directory; 1020 const char* filename = skipOver[index].filename; 1021 SkDebugf("rsync -av cary-linux.cnc:/tera" PATH_SLASH "skps" PATH_SLASH "slave" 1022 "%d" DIR_POST "/%s " IN_DIR_PRE "%d" DIR_POST "\n", dirNo, filename, dirNo); 1023 } 1024} 1025 1026template TestRegistry* TestRegistry::gHead; 1027 1028class Iter { 1029public: 1030 Iter() { this->reset(); } 1031 void reset() { fReg = TestRegistry::Head(); } 1032 1033 Test* next() { 1034 if (fReg) { 1035 TestRegistry::Factory fact = fReg->factory(); 1036 fReg = fReg->next(); 1037 Test* test = fact(NULL); 1038 return test; 1039 } 1040 return NULL; 1041 } 1042 1043private: 1044 const TestRegistry* fReg; 1045}; 1046 1047int tool_main(int argc, char** argv); 1048int tool_main(int argc, char** argv) { 1049 SetupCrashHandler(); 1050 SkCommandLineFlags::SetUsage(""); 1051 SkCommandLineFlags::Parse(argc, argv); 1052 SkGraphics::Init(); 1053 SkString header("PathOps SkpClip:"); 1054 if (!FLAGS_match.isEmpty()) { 1055 header.appendf(" --match"); 1056 for (int index = 0; index < FLAGS_match.count(); ++index) { 1057 header.appendf(" %s", FLAGS_match[index]); 1058 } 1059 } 1060 if (!FLAGS_dir.isEmpty()) { 1061 int count = FLAGS_dir.count(); 1062 for (int i = 0; i < count; ++i) { 1063 const char* range = FLAGS_dir[i]; 1064 const char* dash = strchr(range, '-'); 1065 if (!dash) { 1066 dash = strchr(range, ','); 1067 } 1068 int first = atoi(range); 1069 int last = dash ? atoi(dash + 1) : first; 1070 if (!first || !last) { 1071 SkDebugf("couldn't parse --dir %s\n", range); 1072 return 1; 1073 } 1074 gDirs.set(first, last); 1075 } 1076 } 1077 if (!FLAGS_skp.isEmpty()) { 1078 gNames.set(FLAGS_skp); 1079 } 1080#ifdef SK_DEBUG 1081 header.append(" SK_DEBUG"); 1082#else 1083 header.append(" SK_RELEASE"); 1084#endif 1085 header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8); 1086 if (FLAGS_verbose) { 1087 header.appendf("\n"); 1088 } 1089 SkDebugf("%s", header.c_str()); 1090 Iter iter; 1091 Test* test; 1092 while ((test = iter.next()) != NULL) { 1093 SkAutoTDelete<Test> owned(test); 1094 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) { 1095 test->run(); 1096 } 1097 } 1098 SkGraphics::Term(); 1099 return 0; 1100} 1101 1102#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 1103int main(int argc, char * const argv[]) { 1104 return tool_main(argc, (char**) argv); 1105} 1106#endif 1107