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