| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "CrashHandler.h" |
| // #include "OverwriteLine.h" |
| #include "Resources.h" |
| #include "SkBitmap.h" |
| #include "SkCanvas.h" |
| #include "SkColor.h" |
| #include "SkColorPriv.h" |
| #include "SkCommandLineFlags.h" |
| #include "SkDevice.h" |
| #include "SkForceLinking.h" |
| #include "SkGraphics.h" |
| #include "SkImageDecoder.h" |
| #include "SkImageEncoder.h" |
| #include "SkOSFile.h" |
| #include "SkPathOpsDebug.h" |
| #include "SkPicture.h" |
| #include "SkRTConf.h" |
| #include "SkTSort.h" |
| #include "SkStream.h" |
| #include "SkString.h" |
| #include "SkTArray.h" |
| #include "SkTDArray.h" |
| #include "SkTaskGroup.h" |
| #include "SkTemplates.h" |
| #include "SkTime.h" |
| |
| #include <stdlib.h> |
| |
| __SK_FORCE_IMAGE_DECODER_LINKING; |
| |
| /* add local exceptions here */ |
| /* TODO : add command flag interface */ |
| const struct SkipOverTest { |
| int directory; |
| const char* filename; |
| bool blamePathOps; |
| } skipOver[] = { |
| { 2, "http___www_groupon_sg_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); |
| { 6, "http___www_googleventures_com_.skp", true}, // addTCoincident SkASSERT(test->fT < 1); |
| { 7, "http___www_foxsports_nl_.skp", true}, // (no repro on mac) addT SkASSERT(this != other || fVerb == SkPath::kCubic_Verb) |
| {13, "http___www_modernqigong_com_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); |
| {14, "http___www_devbridge_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple); |
| {16, "http___www_1023world_net_.skp", false}, // bitmap decode assert (corrupt skp?) |
| {19, "http___www_alamdi_com_.skp", true}, // cubic/quad intersection |
| {26, "http___www_liveencounters_net_.skp", true}, // (no repro on mac) checkSmall addT:549 (line, expects cubic) |
| {28, "http___www_encros_fr_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); |
| {37, "http___www_familysurvivalprotocol_wordpress_com_.skp", true}, // bumpSpan SkASSERT(span->fOppValue >= 0); |
| {39, "http___sufeinet_com_.skp", false}, // bitmap decode assert (corrupt skp?) |
| {41, "http___www_rano360_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple); |
| {44, "http___www_firstunitedbank_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0); |
| {46, "http___www_shinydemos_com_.skp", true}, // addSimpleAngle SkASSERT(index == count() - 2); |
| {48, "http___www_familysurvivalprotocol_com_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" |
| {57, "http___www_lptemp_com_.skp", true}, // addTCoincident oPeek = &other->fTs[++oPeekIndex]; |
| {71, "http___www_1milyonkahraman_org_.skp", true}, // addTCoincident SkASSERT(test->fT < 1); |
| {88, "http___www_apuntesdelechuza_wordpress_com_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" |
| {89, "http___www_mobilizedconsulting_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0); |
| {93, "http___www_simple_living_in_suffolk_co_uk_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" |
| }; |
| |
| size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]); |
| |
| |
| /* customize file in/out here */ |
| /* TODO : add command flag interface */ |
| #define CHROME_VERSION "1e5dfa4-4a995df" |
| #define SUMMARY_RUN 1 |
| |
| #ifdef SK_BUILD_FOR_WIN |
| #define DRIVE_SPEC "D:" |
| #define PATH_SLASH "\\" |
| #else |
| #define DRIVE_SPEC "" |
| #define PATH_SLASH "/" |
| #endif |
| |
| #define IN_DIR_PRE DRIVE_SPEC PATH_SLASH "skps" PATH_SLASH "slave" |
| #define OUT_DIR_PRE DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "slave" |
| #define OUT_DIR_SUM DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "summary" |
| #define DIR_POST PATH_SLASH "All" PATH_SLASH CHROME_VERSION |
| |
| static const char outOpDir[] = "opClip"; |
| static const char outOldDir[] = "oldClip"; |
| static const char outStatusDir[] = "statusTest"; |
| |
| static SkString get_in_path(int dirNo, const char* filename) { |
| SkString path; |
| SkASSERT(dirNo); |
| path.appendf("%s%d%s", IN_DIR_PRE, dirNo, DIR_POST); |
| if (!sk_exists(path.c_str())) { |
| SkDebugf("could not read %s\n", path.c_str()); |
| return SkString(); |
| } |
| if (filename) { |
| path.appendf("%s%s", PATH_SLASH, filename); |
| if (!sk_exists(path.c_str())) { |
| SkDebugf("could not read %s\n", path.c_str()); |
| return SkString(); |
| } |
| } |
| return path; |
| } |
| |
| static void make_recursive_dir(const SkString& path) { |
| if (sk_exists(path.c_str())) { |
| return; |
| } |
| const char* pathStr = path.c_str(); |
| int last = (int) path.size(); |
| do { |
| while (last > 0 && pathStr[--last] != PATH_SLASH[0]) |
| ; |
| SkASSERT(last > 0); |
| SkString shorter(pathStr, last); |
| if (sk_mkdir(shorter.c_str())) { |
| break; |
| } |
| } while (true); |
| do { |
| while (last < (int) path.size() && pathStr[++last] != PATH_SLASH[0]) |
| ; |
| SkString shorter(pathStr, last); |
| SkAssertResult(sk_mkdir(shorter.c_str())); |
| } while (last < (int) path.size()); |
| } |
| |
| static SkString get_out_path(int dirNo, const char* dirName) { |
| SkString path; |
| SkASSERT(dirNo); |
| SkASSERT(dirName); |
| path.appendf("%s%d%s%s%s", OUT_DIR_PRE, dirNo, DIR_POST, PATH_SLASH, dirName); |
| make_recursive_dir(path); |
| return path; |
| } |
| |
| static SkString get_sum_path(const char* dirName) { |
| SkString path; |
| SkASSERT(dirName); |
| path.appendf("%s%d%s%s", OUT_DIR_SUM, SUMMARY_RUN, PATH_SLASH, dirName); |
| SkDebugf("%s\n", path.c_str()); |
| make_recursive_dir(path); |
| return path; |
| } |
| |
| static SkString make_png_name(const char* filename) { |
| SkString pngName = SkString(filename); |
| pngName.remove(pngName.size() - 3, 3); |
| pngName.append("png"); |
| return pngName; |
| } |
| |
| //////////////////////////////////////////////////////// |
| |
| enum TestStep { |
| kCompareBits, |
| kEncodeFiles, |
| }; |
| |
| enum { |
| kMaxLength = 256, |
| kMaxFiles = 128, |
| kSmallLimit = 1000, |
| }; |
| |
| struct TestResult { |
| void init(int dirNo) { |
| fDirNo = dirNo; |
| sk_bzero(fFilename, sizeof(fFilename)); |
| fTestStep = kCompareBits; |
| fScale = 1; |
| } |
| |
| void init(int dirNo, const SkString& filename) { |
| fDirNo = dirNo; |
| strcpy(fFilename, filename.c_str()); |
| fTestStep = kCompareBits; |
| fScale = 1; |
| } |
| |
| SkString status() { |
| SkString outStr; |
| outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime); |
| return outStr; |
| } |
| |
| SkString progress() { |
| SkString outStr; |
| outStr.printf("dir=%d %s ", fDirNo, fFilename); |
| if (fPixelError) { |
| outStr.appendf(" err=%d", fPixelError); |
| } |
| if (fTime) { |
| outStr.appendf(" time=%d", fTime); |
| } |
| if (fScale != 1) { |
| outStr.appendf(" scale=%d", fScale); |
| } |
| outStr.appendf("\n"); |
| return outStr; |
| |
| } |
| |
| void test(int dirNo, const SkString& filename) { |
| init(dirNo); |
| strcpy(fFilename, filename.c_str()); |
| testOne(); |
| } |
| |
| void testOne(); |
| |
| char fFilename[kMaxLength]; |
| TestStep fTestStep; |
| int fDirNo; |
| int fPixelError; |
| int fTime; |
| int fScale; |
| }; |
| |
| class SortByPixel : public TestResult { |
| public: |
| bool operator<(const SortByPixel& rh) const { |
| return fPixelError < rh.fPixelError; |
| } |
| }; |
| |
| class SortByTime : public TestResult { |
| public: |
| bool operator<(const SortByTime& rh) const { |
| return fTime < rh.fTime; |
| } |
| }; |
| |
| class SortByName : public TestResult { |
| public: |
| bool operator<(const SortByName& rh) const { |
| return strcmp(fFilename, rh.fFilename) < 0; |
| } |
| }; |
| |
| struct TestState { |
| void init(int dirNo) { |
| fResult.init(dirNo); |
| } |
| |
| SkTDArray<SortByPixel> fPixelWorst; |
| SkTDArray<SortByTime> fSlowest; |
| TestResult fResult; |
| }; |
| |
| struct TestRunner { |
| ~TestRunner(); |
| void render(); |
| SkTDArray<class TestRunnable*> fRunnables; |
| }; |
| |
| class TestRunnable { |
| public: |
| void operator()() { |
| SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); |
| (*fTestFun)(&fState); |
| } |
| |
| TestState fState; |
| void (*fTestFun)(TestState*); |
| }; |
| |
| |
| class TestRunnableDir : public TestRunnable { |
| public: |
| TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) { |
| fState.init(dirNo); |
| fTestFun = testFun; |
| } |
| |
| }; |
| |
| class TestRunnableFile : public TestRunnable { |
| public: |
| TestRunnableFile(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) { |
| fState.init(dirNo); |
| strcpy(fState.fResult.fFilename, name); |
| fTestFun = testFun; |
| } |
| }; |
| |
| class TestRunnableEncode : public TestRunnableFile { |
| public: |
| TestRunnableEncode(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) |
| : TestRunnableFile(testFun, dirNo, name, runner) { |
| fState.fResult.fTestStep = kEncodeFiles; |
| } |
| }; |
| |
| TestRunner::~TestRunner() { |
| for (int index = 0; index < fRunnables.count(); index++) { |
| delete fRunnables[index]; |
| } |
| } |
| |
| void TestRunner::render() { |
| SkTaskGroup().batch(fRunnables.count(), [&](int i) { |
| (*fRunnables[i])(); |
| }); |
| } |
| |
| //////////////////////////////////////////////// |
| |
| |
| static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { |
| const int kRowCount = 3; |
| const int kThreshold = 3; |
| int width = SkTMin(gr.width(), sk.width()); |
| if (width < kRowCount) { |
| return true; |
| } |
| int height = SkTMin(gr.height(), sk.height()); |
| if (height < kRowCount) { |
| return true; |
| } |
| int errorTotal = 0; |
| SkTArray<int, true> errorRows; |
| errorRows.push_back_n(width * kRowCount); |
| SkAutoLockPixels autoGr(gr); |
| SkAutoLockPixels autoSk(sk); |
| for (int y = 0; y < height; ++y) { |
| SkPMColor* grRow = gr.getAddr32(0, y); |
| SkPMColor* skRow = sk.getAddr32(0, y); |
| int* base = &errorRows[0]; |
| int* cOut = &errorRows[y % kRowCount]; |
| for (int x = 0; x < width; ++x) { |
| SkPMColor grColor = grRow[x]; |
| SkPMColor skColor = skRow[x]; |
| int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); |
| int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); |
| int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); |
| int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); |
| if (error < kThreshold || x < 2) { |
| continue; |
| } |
| if (base[x - 2] < kThreshold |
| || base[width + x - 2] < kThreshold |
| || base[width * 2 + x - 2] < kThreshold |
| || base[x - 1] < kThreshold |
| || base[width + x - 1] < kThreshold |
| || base[width * 2 + x - 1] < kThreshold |
| || base[x] < kThreshold |
| || base[width + x] < kThreshold |
| || base[width * 2 + x] < kThreshold) { |
| continue; |
| } |
| errorTotal += error; |
| } |
| } |
| return errorTotal; |
| } |
| |
| static bool addError(TestState* data, const TestResult& testResult) { |
| if (testResult.fPixelError <= 0 && testResult.fTime <= 0) { |
| return false; |
| } |
| int worstCount = data->fPixelWorst.count(); |
| int pixelError = testResult.fPixelError; |
| if (pixelError > 0) { |
| for (int index = 0; index < worstCount; ++index) { |
| if (pixelError > data->fPixelWorst[index].fPixelError) { |
| data->fPixelWorst[index] = *(SortByPixel*) &testResult; |
| return true; |
| } |
| } |
| } |
| int slowCount = data->fSlowest.count(); |
| int time = testResult.fTime; |
| if (time > 0) { |
| for (int index = 0; index < slowCount; ++index) { |
| if (time > data->fSlowest[index].fTime) { |
| data->fSlowest[index] = *(SortByTime*) &testResult; |
| return true; |
| } |
| } |
| } |
| if (pixelError > 0 && worstCount < kMaxFiles) { |
| *data->fPixelWorst.append() = *(SortByPixel*) &testResult; |
| return true; |
| } |
| if (time > 0 && slowCount < kMaxFiles) { |
| *data->fSlowest.append() = *(SortByTime*) &testResult; |
| return true; |
| } |
| return false; |
| } |
| |
| static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) { |
| canvas->save(); |
| SkScalar pWidth = pic->cullRect().width(); |
| SkScalar pHeight = pic->cullRect().height(); |
| const SkScalar maxDimension = 1000.0f; |
| const int slices = 3; |
| SkScalar xInterval = SkTMax(pWidth - maxDimension, 0.0f) / (slices - 1); |
| SkScalar yInterval = SkTMax(pHeight - maxDimension, 0.0f) / (slices - 1); |
| SkRect rect = {0, 0, SkTMin(maxDimension, pWidth), SkTMin(maxDimension, pHeight) }; |
| canvas->clipRect(rect); |
| SkMSec start = SkTime::GetMSecs(); |
| for (int x = 0; x < slices; ++x) { |
| for (int y = 0; y < slices; ++y) { |
| pic->playback(canvas); |
| canvas->translate(0, yInterval); |
| } |
| canvas->translate(xInterval, -yInterval * slices); |
| } |
| SkMSec end = SkTime::GetMSecs(); |
| canvas->restore(); |
| return end - start; |
| } |
| |
| static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { |
| canvas->clear(SK_ColorWHITE); |
| if (scale != 1) { |
| canvas->save(); |
| canvas->scale(1.0f / scale, 1.0f / scale); |
| } |
| pic->playback(canvas); |
| if (scale != 1) { |
| canvas->restore(); |
| } |
| } |
| |
| static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { |
| SkString outFile = get_sum_path(outDir); |
| outFile.appendf("%s%s", PATH_SLASH, pngName); |
| if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100)) { |
| SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName, |
| bitmap.width(), bitmap.height()); |
| } |
| } |
| |
| void TestResult::testOne() { |
| SkPicture* pic = nullptr; |
| { |
| #if DEBUG_SHOW_TEST_NAME |
| if (fTestStep == kCompareBits) { |
| SkString testName(fFilename); |
| const char http[] = "http"; |
| if (testName.startsWith(http)) { |
| testName.remove(0, sizeof(http) - 1); |
| } |
| while (testName.startsWith("_")) { |
| testName.remove(0, 1); |
| } |
| const char dotSkp[] = ".skp"; |
| if (testName.endsWith(dotSkp)) { |
| size_t len = testName.size(); |
| testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1); |
| } |
| testName.prepend("skp"); |
| testName.append("1"); |
| strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH); |
| } else if (fTestStep == kEncodeFiles) { |
| strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); |
| } |
| #endif |
| SkString path = get_in_path(fDirNo, fFilename); |
| SkFILEStream stream(path.c_str()); |
| if (!stream.isValid()) { |
| SkDebugf("invalid stream %s\n", path.c_str()); |
| goto finish; |
| } |
| pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); |
| if (!pic) { |
| SkDebugf("unable to decode %s\n", fFilename); |
| goto finish; |
| } |
| SkScalar width = pic->cullRect().width(); |
| SkScalar height = pic->cullRect().height(); |
| SkBitmap oldBitmap, opBitmap; |
| fScale = 1; |
| while (width / fScale > 32767 || height / fScale > 32767) { |
| ++fScale; |
| } |
| do { |
| int dimX = SkScalarCeilToInt(width / fScale); |
| int dimY = SkScalarCeilToInt(height / fScale); |
| if (oldBitmap.tryAllocN32Pixels(dimX, dimY) && opBitmap.tryAllocN32Pixels(dimX, dimY)) { |
| break; |
| } |
| SkDebugf("-%d-", fScale); |
| } while (++fScale < 256); |
| if (fScale >= 256) { |
| SkDebugf("unable to allocate bitmap for %s (w=%f h=%f)\n", fFilename, |
| width, height); |
| goto finish; |
| } |
| oldBitmap.eraseColor(SK_ColorWHITE); |
| SkCanvas oldCanvas(oldBitmap); |
| oldCanvas.setAllowSimplifyClip(false); |
| opBitmap.eraseColor(SK_ColorWHITE); |
| SkCanvas opCanvas(opBitmap); |
| opCanvas.setAllowSimplifyClip(true); |
| drawPict(pic, &oldCanvas, fScale); |
| drawPict(pic, &opCanvas, fScale); |
| if (fTestStep == kCompareBits) { |
| fPixelError = similarBits(oldBitmap, opBitmap); |
| int oldTime = timePict(pic, &oldCanvas); |
| int opTime = timePict(pic, &opCanvas); |
| fTime = SkTMax(0, oldTime - opTime); |
| } else if (fTestStep == kEncodeFiles) { |
| SkString pngStr = make_png_name(fFilename); |
| const char* pngName = pngStr.c_str(); |
| writePict(oldBitmap, outOldDir, pngName); |
| writePict(opBitmap, outOpDir, pngName); |
| } |
| } |
| finish: |
| if (pic) { |
| pic->unref(); |
| } |
| } |
| |
| DEFINE_string2(match, m, "PathOpsSkpClipThreaded", |
| "[~][^]substring[$] [...] of test name to run.\n" |
| "Multiple matches may be separated by spaces.\n" |
| "~ causes a matching test to always be skipped\n" |
| "^ requires the start of the test to match\n" |
| "$ requires the end of the test to match\n" |
| "^ and $ requires an exact match\n" |
| "If a test does not match any list entry,\n" |
| "it is skipped unless some list entry starts with ~"); |
| DEFINE_string2(dir, d, nullptr, "range of directories (e.g., 1-100)"); |
| DEFINE_string2(skp, s, nullptr, "skp to test"); |
| DEFINE_bool2(single, z, false, "run tests on a single thread internally."); |
| DEFINE_int32(testIndex, 0, "override local test index (PathOpsSkpClipOneOff only)."); |
| DEFINE_bool2(verbose, v, false, "enable verbose output."); |
| |
| static bool verbose() { |
| return FLAGS_verbose; |
| } |
| |
| class Dirs { |
| public: |
| Dirs() { |
| reset(); |
| sk_bzero(fRun, sizeof(fRun)); |
| fSet = false; |
| } |
| |
| int first() const { |
| int index = 0; |
| while (++index < kMaxDir) { |
| if (fRun[index]) { |
| return index; |
| } |
| } |
| SkASSERT(0); |
| return -1; |
| } |
| |
| int last() const { |
| int index = kMaxDir; |
| while (--index > 0 && !fRun[index]) |
| ; |
| return index; |
| } |
| |
| int next() { |
| while (++fIndex < kMaxDir) { |
| if (fRun[fIndex]) { |
| return fIndex; |
| } |
| } |
| return -1; |
| } |
| |
| void reset() { |
| fIndex = -1; |
| } |
| |
| void set(int start, int end) { |
| while (start < end) { |
| fRun[start++] = 1; |
| } |
| fSet = true; |
| } |
| |
| void setDefault() { |
| if (!fSet) { |
| set(1, 100); |
| } |
| } |
| |
| private: |
| enum { |
| kMaxDir = 101 |
| }; |
| char fRun[kMaxDir]; |
| int fIndex; |
| bool fSet; |
| } gDirs; |
| |
| class Filenames { |
| public: |
| Filenames() |
| : fIndex(-1) { |
| } |
| |
| const char* next() { |
| while (fNames && ++fIndex < fNames->count()) { |
| return (*fNames)[fIndex]; |
| } |
| return nullptr; |
| } |
| |
| void set(const SkCommandLineFlags::StringArray& names) { |
| fNames = &names; |
| } |
| |
| private: |
| int fIndex; |
| const SkCommandLineFlags::StringArray* fNames; |
| } gNames; |
| |
| static bool buildTestDir(int dirNo, int firstDirNo, |
| SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) { |
| SkString dirName = get_out_path(dirNo, outStatusDir); |
| if (!dirName.size()) { |
| return false; |
| } |
| SkOSFile::Iter iter(dirName.c_str(), "skp"); |
| SkString filename; |
| while (iter.next(&filename)) { |
| TestResult test; |
| test.init(dirNo); |
| SkString spaceFile(filename); |
| char* spaces = spaceFile.writable_str(); |
| int spaceSize = (int) spaceFile.size(); |
| for (int index = 0; index < spaceSize; ++index) { |
| if (spaces[index] == '.') { |
| spaces[index] = ' '; |
| } |
| } |
| int success = sscanf(spaces, "%s %d %d skp", test.fFilename, |
| &test.fPixelError, &test.fTime); |
| if (success < 3) { |
| SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success); |
| return false; |
| } |
| *tests[dirNo - firstDirNo].append() = test; |
| } |
| if (!sorted) { |
| return true; |
| } |
| SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo]; |
| int count = testSet.count(); |
| for (int index = 0; index < count; ++index) { |
| *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index]; |
| } |
| if (sorted[dirNo - firstDirNo].count()) { |
| SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(), |
| sorted[dirNo - firstDirNo].end() - 1); |
| if (verbose()) { |
| SkDebugf("+"); |
| } |
| } |
| return true; |
| } |
| |
| static void testSkpClip(TestState* data) { |
| data->fResult.testOne(); |
| SkString statName(data->fResult.fFilename); |
| SkASSERT(statName.endsWith(".skp")); |
| statName.remove(statName.size() - 4, 4); |
| statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime); |
| SkString statusFile = get_out_path(data->fResult.fDirNo, outStatusDir); |
| if (!statusFile.size()) { |
| SkDebugf("failed to create %s", statusFile.c_str()); |
| return; |
| } |
| statusFile.appendf("%s%s", PATH_SLASH, statName.c_str()); |
| FILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag); |
| if (!file) { |
| SkDebugf("failed to create %s", statusFile.c_str()); |
| return; |
| } |
| sk_fclose(file); |
| if (verbose()) { |
| if (data->fResult.fPixelError || data->fResult.fTime) { |
| SkDebugf("%s", data->fResult.progress().c_str()); |
| } else { |
| SkDebugf("."); |
| } |
| } |
| } |
| |
| bool Less(const SortByName& a, const SortByName& b); |
| bool Less(const SortByName& a, const SortByName& b) { |
| return a < b; |
| } |
| |
| static bool doOneDir(TestState* state, bool threaded) { |
| int dirNo = state->fResult.fDirNo; |
| SkString dirName = get_in_path(dirNo, nullptr); |
| if (!dirName.size()) { |
| return false; |
| } |
| SkTDArray<TestResult> tests[1]; |
| SkTDArray<SortByName*> sorted[1]; |
| if (!buildTestDir(dirNo, dirNo, tests, sorted)) { |
| return false; |
| } |
| SkOSFile::Iter iter(dirName.c_str(), "skp"); |
| SkString filename; |
| while (iter.next(&filename)) { |
| for (size_t index = 0; index < skipOverCount; ++index) { |
| if (skipOver[index].directory == dirNo |
| && strcmp(filename.c_str(), skipOver[index].filename) == 0) { |
| goto checkEarlyExit; |
| } |
| } |
| { |
| SortByName name; |
| name.init(dirNo); |
| strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp |
| int count = sorted[0].count(); |
| int idx = SkTSearch<SortByName, Less>(sorted[0].begin(), count, &name, sizeof(&name)); |
| if (idx >= 0) { |
| SortByName* found = sorted[0][idx]; |
| (void) addError(state, *found); |
| continue; |
| } |
| TestResult test; |
| test.init(dirNo, filename); |
| state->fResult = test; |
| testSkpClip(state); |
| #if 0 // artificially limit to a few while debugging code |
| static int debugLimit = 0; |
| if (++debugLimit == 5) { |
| return true; |
| } |
| #endif |
| } |
| checkEarlyExit: |
| ; |
| } |
| return true; |
| } |
| |
| static void initTest() { |
| #if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC |
| SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); |
| SK_CONF_SET("images.png.suppressDecoderWarnings", true); |
| #endif |
| } |
| |
| static void testSkpClipEncode(TestState* data) { |
| data->fResult.testOne(); |
| if (verbose()) { |
| SkDebugf("+"); |
| } |
| } |
| |
| static void encodeFound(TestState& state) { |
| if (verbose()) { |
| if (state.fPixelWorst.count()) { |
| SkTDArray<SortByPixel*> worst; |
| for (int index = 0; index < state.fPixelWorst.count(); ++index) { |
| *worst.append() = &state.fPixelWorst[index]; |
| } |
| SkTQSort<SortByPixel>(worst.begin(), worst.end() - 1); |
| for (int index = 0; index < state.fPixelWorst.count(); ++index) { |
| const TestResult& result = *worst[index]; |
| SkDebugf("%d %s pixelError=%d\n", result.fDirNo, result.fFilename, result.fPixelError); |
| } |
| } |
| if (state.fSlowest.count()) { |
| SkTDArray<SortByTime*> slowest; |
| for (int index = 0; index < state.fSlowest.count(); ++index) { |
| *slowest.append() = &state.fSlowest[index]; |
| } |
| if (slowest.count() > 0) { |
| SkTQSort<SortByTime>(slowest.begin(), slowest.end() - 1); |
| for (int index = 0; index < slowest.count(); ++index) { |
| const TestResult& result = *slowest[index]; |
| SkDebugf("%d %s time=%d\n", result.fDirNo, result.fFilename, result.fTime); |
| } |
| } |
| } |
| } |
| TestRunner testRunner; |
| for (int index = 0; index < state.fPixelWorst.count(); ++index) { |
| const TestResult& result = state.fPixelWorst[index]; |
| SkString filename(result.fFilename); |
| if (!filename.endsWith(".skp")) { |
| filename.append(".skp"); |
| } |
| *testRunner.fRunnables.append() = new TestRunnableEncode(&testSkpClipEncode, result.fDirNo, |
| filename.c_str(), &testRunner); |
| } |
| testRunner.render(); |
| } |
| |
| class Test { |
| public: |
| Test() {} |
| virtual ~Test() {} |
| |
| const char* getName() { onGetName(&fName); return fName.c_str(); } |
| void run() { onRun(); } |
| |
| protected: |
| virtual void onGetName(SkString*) = 0; |
| virtual void onRun() = 0; |
| |
| private: |
| SkString fName; |
| }; |
| |
| typedef SkTRegistry<Test*(*)(void*)> TestRegistry; |
| |
| #define DEF_TEST(name) \ |
| static void test_##name(); \ |
| class name##Class : public Test { \ |
| public: \ |
| static Test* Factory(void*) { return new name##Class; } \ |
| \ |
| protected: \ |
| void onGetName(SkString* name) override { name->set(#name); } \ |
| void onRun() override { test_##name(); } \ |
| }; \ |
| static TestRegistry gReg_##name##Class(name##Class::Factory); \ |
| static void test_##name() |
| |
| DEF_TEST(PathOpsSkpClip) { |
| gDirs.setDefault(); |
| initTest(); |
| SkTArray<TestResult, true> errors; |
| TestState state; |
| state.init(0); |
| int dirNo; |
| gDirs.reset(); |
| while ((dirNo = gDirs.next()) > 0) { |
| if (verbose()) { |
| SkDebugf("dirNo=%d\n", dirNo); |
| } |
| state.fResult.fDirNo = dirNo; |
| if (!doOneDir(&state, false)) { |
| break; |
| } |
| } |
| encodeFound(state); |
| } |
| |
| static void testSkpClipMain(TestState* data) { |
| (void) doOneDir(data, true); |
| } |
| |
| DEF_TEST(PathOpsSkpClipThreaded) { |
| gDirs.setDefault(); |
| initTest(); |
| TestRunner testRunner; |
| int dirNo; |
| gDirs.reset(); |
| while ((dirNo = gDirs.next()) > 0) { |
| *testRunner.fRunnables.append() = new TestRunnableDir(&testSkpClipMain, dirNo, &testRunner); |
| } |
| testRunner.render(); |
| TestState state; |
| state.init(0); |
| gDirs.reset(); |
| while ((dirNo = gDirs.next()) > 0) { |
| TestState& testState = testRunner.fRunnables[dirNo - 1]->fState; |
| SkASSERT(testState.fResult.fDirNo == dirNo); |
| for (int inner = 0; inner < testState.fPixelWorst.count(); ++inner) { |
| addError(&state, testState.fPixelWorst[inner]); |
| } |
| for (int inner = 0; inner < testState.fSlowest.count(); ++inner) { |
| addError(&state, testState.fSlowest[inner]); |
| } |
| } |
| encodeFound(state); |
| } |
| |
| static bool buildTests(SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) { |
| int firstDirNo = gDirs.first(); |
| int dirNo; |
| while ((dirNo = gDirs.next()) > 0) { |
| if (!buildTestDir(dirNo, firstDirNo, tests, sorted)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| DEF_TEST(PathOpsSkpClipUberThreaded) { |
| gDirs.setDefault(); |
| const int firstDirNo = gDirs.next(); |
| const int lastDirNo = gDirs.last(); |
| initTest(); |
| int dirCount = lastDirNo - firstDirNo + 1; |
| SkAutoTDeleteArray<SkTDArray<TestResult> > tests(new SkTDArray<TestResult>[dirCount]); |
| SkAutoTDeleteArray<SkTDArray<SortByName*> > sorted(new SkTDArray<SortByName*>[dirCount]); |
| if (!buildTests(tests.get(), sorted.get())) { |
| return; |
| } |
| TestRunner testRunner; |
| int dirNo; |
| gDirs.reset(); |
| while ((dirNo = gDirs.next()) > 0) { |
| SkString dirName = get_in_path(dirNo, nullptr); |
| if (!dirName.size()) { |
| continue; |
| } |
| SkOSFile::Iter iter(dirName.c_str(), "skp"); |
| SkString filename; |
| while (iter.next(&filename)) { |
| for (size_t index = 0; index < skipOverCount; ++index) { |
| if (skipOver[index].directory == dirNo |
| && strcmp(filename.c_str(), skipOver[index].filename) == 0) { |
| goto checkEarlyExit; |
| } |
| } |
| { |
| SortByName name; |
| name.init(dirNo); |
| strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp |
| int count = sorted.get()[dirNo - firstDirNo].count(); |
| if (SkTSearch<SortByName, Less>(sorted.get()[dirNo - firstDirNo].begin(), |
| count, &name, sizeof(&name)) < 0) { |
| *testRunner.fRunnables.append() = new TestRunnableFile( |
| &testSkpClip, dirNo, filename.c_str(), &testRunner); |
| } |
| } |
| checkEarlyExit: |
| ; |
| } |
| |
| } |
| testRunner.render(); |
| SkAutoTDeleteArray<SkTDArray<TestResult> > results(new SkTDArray<TestResult>[dirCount]); |
| if (!buildTests(results.get(), nullptr)) { |
| return; |
| } |
| SkTDArray<TestResult> allResults; |
| for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) { |
| SkTDArray<TestResult>& array = results.get()[dirNo - firstDirNo]; |
| allResults.append(array.count(), array.begin()); |
| } |
| int allCount = allResults.count(); |
| SkTDArray<SortByPixel*> pixels; |
| SkTDArray<SortByTime*> times; |
| for (int index = 0; index < allCount; ++index) { |
| *pixels.append() = (SortByPixel*) &allResults[index]; |
| *times.append() = (SortByTime*) &allResults[index]; |
| } |
| TestState state; |
| if (pixels.count()) { |
| SkTQSort<SortByPixel>(pixels.begin(), pixels.end() - 1); |
| for (int inner = 0; inner < kMaxFiles; ++inner) { |
| *state.fPixelWorst.append() = *pixels[allCount - inner - 1]; |
| } |
| } |
| if (times.count()) { |
| SkTQSort<SortByTime>(times.begin(), times.end() - 1); |
| for (int inner = 0; inner < kMaxFiles; ++inner) { |
| *state.fSlowest.append() = *times[allCount - inner - 1]; |
| } |
| } |
| encodeFound(state); |
| } |
| |
| DEF_TEST(PathOpsSkpClipOneOff) { |
| const int testIndex = FLAGS_testIndex; |
| int dirNo = gDirs.next(); |
| if (dirNo < 0) { |
| dirNo = skipOver[testIndex].directory; |
| } |
| const char* skp = gNames.next(); |
| if (!skp) { |
| skp = skipOver[testIndex].filename; |
| } |
| initTest(); |
| SkAssertResult(get_in_path(dirNo, skp).size()); |
| SkString filename(skp); |
| TestResult state; |
| state.test(dirNo, filename); |
| if (verbose()) { |
| SkDebugf("%s", state.status().c_str()); |
| } |
| state.fTestStep = kEncodeFiles; |
| state.testOne(); |
| } |
| |
| DEF_TEST(PathOpsTestSkipped) { |
| for (size_t index = 0; index < skipOverCount; ++index) { |
| const SkipOverTest& skip = skipOver[index]; |
| if (!skip.blamePathOps) { |
| continue; |
| } |
| int dirNo = skip.directory; |
| const char* skp = skip.filename; |
| initTest(); |
| SkAssertResult(get_in_path(dirNo, skp).size()); |
| SkString filename(skp); |
| TestResult state; |
| state.test(dirNo, filename); |
| if (verbose()) { |
| SkDebugf("%s", state.status().c_str()); |
| } |
| state.fTestStep = kEncodeFiles; |
| state.testOne(); |
| } |
| } |
| |
| DEF_TEST(PathOpsCopyFails) { |
| FLAGS_verbose = true; |
| for (size_t index = 0; index < skipOverCount; ++index) { |
| int dirNo = skipOver[index].directory; |
| SkDebugf("mkdir -p " IN_DIR_PRE "%d" DIR_POST "\n", dirNo); |
| } |
| for (size_t index = 0; index < skipOverCount; ++index) { |
| int dirNo = skipOver[index].directory; |
| const char* filename = skipOver[index].filename; |
| SkDebugf("rsync -av cary-linux.cnc:/tera" PATH_SLASH "skps" PATH_SLASH "slave" |
| "%d" DIR_POST "/%s " IN_DIR_PRE "%d" DIR_POST "\n", dirNo, filename, dirNo); |
| } |
| } |
| |
| template TestRegistry* TestRegistry::gHead; |
| |
| class Iter { |
| public: |
| Iter() { this->reset(); } |
| void reset() { fReg = TestRegistry::Head(); } |
| |
| Test* next() { |
| if (fReg) { |
| TestRegistry::Factory fact = fReg->factory(); |
| fReg = fReg->next(); |
| Test* test = fact(nullptr); |
| return test; |
| } |
| return nullptr; |
| } |
| |
| private: |
| const TestRegistry* fReg; |
| }; |
| |
| int tool_main(int argc, char** argv); |
| int tool_main(int argc, char** argv) { |
| SetupCrashHandler(); |
| SkCommandLineFlags::SetUsage(""); |
| SkCommandLineFlags::Parse(argc, argv); |
| SkGraphics::Init(); |
| SkString header("PathOps SkpClip:"); |
| if (!FLAGS_match.isEmpty()) { |
| header.appendf(" --match"); |
| for (int index = 0; index < FLAGS_match.count(); ++index) { |
| header.appendf(" %s", FLAGS_match[index]); |
| } |
| } |
| if (!FLAGS_dir.isEmpty()) { |
| int count = FLAGS_dir.count(); |
| for (int i = 0; i < count; ++i) { |
| const char* range = FLAGS_dir[i]; |
| const char* dash = strchr(range, '-'); |
| if (!dash) { |
| dash = strchr(range, ','); |
| } |
| int first = atoi(range); |
| int last = dash ? atoi(dash + 1) : first; |
| if (!first || !last) { |
| SkDebugf("couldn't parse --dir %s\n", range); |
| return 1; |
| } |
| gDirs.set(first, last); |
| } |
| } |
| if (!FLAGS_skp.isEmpty()) { |
| gNames.set(FLAGS_skp); |
| } |
| #ifdef SK_DEBUG |
| header.append(" SK_DEBUG"); |
| #else |
| header.append(" SK_RELEASE"); |
| #endif |
| if (FLAGS_verbose) { |
| header.appendf("\n"); |
| } |
| SkDebugf("%s", header.c_str()); |
| Iter iter; |
| Test* test; |
| while ((test = iter.next()) != nullptr) { |
| SkAutoTDelete<Test> owned(test); |
| if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) { |
| test->run(); |
| } |
| } |
| return 0; |
| } |
| |
| #if !defined(SK_BUILD_FOR_IOS) |
| int main(int argc, char * const argv[]) { |
| return tool_main(argc, (char**) argv); |
| } |
| #endif |