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