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