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