Update DM to allow Src's to have optional options.

Review URL: https://codereview.chromium.org/1059513002
diff --git a/dm/DM.cpp b/dm/DM.cpp
index ffb6cb1..db9eaca 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -25,10 +25,10 @@
 #include "Test.h"
 #include "Timer.h"
 
-DEFINE_string(src, "tests gm skp image subset codec scanline", "Source types to test.");
+DEFINE_string(src, "tests gm skp image", "Source types to test.");
 DEFINE_bool(nameByHash, false,
             "If true, write to FLAGS_writePath[0]/<hash>.png instead of "
-            "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png");
+            "to FLAGS_writePath[0]/<config>/<sourceType>/<sourceOptions>/<name>.png");
 DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
 DEFINE_string(matrix, "1 0 0 1",
               "2x2 scale+skew matrix to apply or upright when using "
@@ -36,9 +36,9 @@
 DEFINE_bool(gpu_threading, false, "Allow GPU work to run on multiple threads?");
 
 DEFINE_string(blacklist, "",
-        "Space-separated config/src/name triples to blacklist.  '_' matches anything.  E.g. \n"
-        "'--blacklist gpu skp _' will blacklist all SKPs drawn into the gpu config.\n"
-        "'--blacklist gpu skp _ 8888 gm aarects' will also blacklist the aarects GM on 8888.");
+        "Space-separated config/src/srcOptions/name quadruples to blacklist.  '_' matches anything.  E.g. \n"
+        "'--blacklist gpu skp _ _' will blacklist all SKPs drawn into the gpu config.\n"
+        "'--blacklist gpu skp _ _ 8888 gm _ aarects' will also blacklist the aarects GM on 8888.");
 
 DEFINE_string2(readPath, r, "", "If set check for equality with golden results in this directory.");
 
@@ -62,9 +62,10 @@
 static SkTArray<SkString> gRunning;
 
 static void done(double ms,
-                 ImplicitString config, ImplicitString src, ImplicitString name,
-                 ImplicitString note, ImplicitString log) {
-    SkString id = SkStringPrintf("%s %s %s", config.c_str(), src.c_str(), name.c_str());
+                 ImplicitString config, ImplicitString src, ImplicitString srcOptions,
+                 ImplicitString name, ImplicitString note, ImplicitString log) {
+    SkString id = SkStringPrintf("%s %s %s %s", config.c_str(), src.c_str(),
+                                                srcOptions.c_str(), name.c_str());
     {
         SkAutoMutexAcquire lock(gRunningMutex);
         for (int i = 0; i < gRunning.count(); i++) {
@@ -95,8 +96,10 @@
     }
 }
 
-static void start(ImplicitString config, ImplicitString src, ImplicitString name) {
-    SkString id = SkStringPrintf("%s %s %s", config.c_str(), src.c_str(), name.c_str());
+static void start(ImplicitString config, ImplicitString src,
+                  ImplicitString srcOptions, ImplicitString name) {
+    SkString id = SkStringPrintf("%s %s %s %s", config.c_str(), src.c_str(),
+                                                srcOptions.c_str(), name.c_str());
     SkAutoMutexAcquire lock(gRunningMutex);
     gRunning.push_back(id);
 }
@@ -105,10 +108,12 @@
 
 struct Gold : public SkString {
     Gold() : SkString("") {}
-    Gold(ImplicitString sink, ImplicitString src, ImplicitString name, ImplicitString md5)
+    Gold(ImplicitString sink, ImplicitString src, ImplicitString srcOptions,
+         ImplicitString name, ImplicitString md5)
         : SkString("") {
         this->append(sink);
         this->append(src);
+        this->append(srcOptions);
         this->append(name);
         this->append(md5);
     }
@@ -117,7 +122,7 @@
 static SkTHashSet<Gold, Gold::Hash> gGold;
 
 static void add_gold(JsonWriter::BitmapResult r) {
-    gGold.add(Gold(r.config, r.sourceType, r.name, r.md5));
+    gGold.add(Gold(r.config, r.sourceType, r.sourceOptions, r.name, r.md5));
 }
 
 static void gather_gold() {
@@ -133,20 +138,24 @@
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
 template <typename T>
-struct Tagged : public SkAutoTDelete<T> { const char* tag; };
+struct Tagged : public SkAutoTDelete<T> {
+  const char* tag;
+  const char* options;
+};
 
 static const bool kMemcpyOK = true;
 
 static SkTArray<Tagged<Src>,  kMemcpyOK>  gSrcs;
 static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks;
 
-static void push_src(const char* tag, Src* s) {
+static void push_src(const char* tag, const char* options, Src* s) {
     SkAutoTDelete<Src> src(s);
     if (FLAGS_src.contains(tag) &&
         !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) {
         Tagged<Src>& s = gSrcs.push_back();
         s.reset(src.detach());
         s.tag = tag;
+        s.options = options;
     }
 }
 
@@ -168,17 +177,17 @@
 
 static void gather_srcs() {
     for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) {
-        push_src("gm", new GMSrc(r->factory()));
+        push_src("gm", "", new GMSrc(r->factory()));
     }
     for (int i = 0; i < FLAGS_skps.count(); i++) {
         const char* path = FLAGS_skps[i];
         if (sk_isdir(path)) {
             SkOSFile::Iter it(path, "skp");
             for (SkString file; it.next(&file); ) {
-                push_src("skp", new SKPSrc(SkOSPath::Join(path, file.c_str())));
+                push_src("skp", "", new SKPSrc(SkOSPath::Join(path, file.c_str())));
             }
         } else {
-            push_src("skp", new SKPSrc(path));
+            push_src("skp", "", new SKPSrc(path));
         }
     }
     static const char* const exts[] = {
@@ -192,20 +201,20 @@
                 SkOSFile::Iter it(flag, exts[j]);
                 for (SkString file; it.next(&file); ) {
                     SkString path = SkOSPath::Join(flag, file.c_str());
-                    push_src("image", new ImageSrc(path));     // Decode entire image.
-                    push_src("subset", new ImageSrc(path, 2)); // Decode into 2 x 2 subsets
+                    push_src("image", "decode", new ImageSrc(path)); // Decode entire image
+                    push_src("image", "subset", new ImageSrc(path, 2)); // Decode into 2x2 subsets
                     if (codec_supported(exts[j])) {
-                        push_src("codec", new CodecSrc(path, CodecSrc::kNormal_Mode));
-                        push_src("scanline", new CodecSrc(path, CodecSrc::kScanline_Mode));
+                        push_src("image", "codec", new CodecSrc(path, CodecSrc::kNormal_Mode));
+                        push_src("image", "scanline", new CodecSrc(path, CodecSrc::kScanline_Mode));
                     }
                 }
             }
         } else if (sk_exists(flag)) {
             // assume that FLAGS_images[i] is a valid image if it is a file.
-            push_src("image", new ImageSrc(flag));     // Decode entire image.
-            push_src("subset", new ImageSrc(flag, 2)); // Decode into 2 x 2 subsets
-            push_src("codec", new CodecSrc(flag, CodecSrc::kNormal_Mode));
-            push_src("scanline", new CodecSrc(flag, CodecSrc::kScanline_Mode));
+            push_src("image", "decode", new ImageSrc(flag)); // Decode entire image.
+            push_src("image", "subset", new ImageSrc(flag, 2)); // Decode into 2 x 2 subsets
+            push_src("image", "codec", new CodecSrc(flag, CodecSrc::kNormal_Mode));
+            push_src("image", "scanline", new CodecSrc(flag, CodecSrc::kScanline_Mode));
         }
     }
 }
@@ -342,13 +351,16 @@
     return 0 == strcmp("_", needle) || NULL != strstr(haystack, needle);
 }
 
-static ImplicitString is_blacklisted(const char* sink, const char* src, const char* name) {
-    for (int i = 0; i < FLAGS_blacklist.count() - 2; i += 3) {
+static ImplicitString is_blacklisted(const char* sink, const char* src,
+                                     const char* srcOptions, const char* name) {
+    for (int i = 0; i < FLAGS_blacklist.count() - 2; i += 4) {
         if (match(FLAGS_blacklist[i+0], sink) &&
-            match(FLAGS_blacklist[i+1],  src) &&
-            match(FLAGS_blacklist[i+2], name)) {
-            return SkStringPrintf("%s %s %s",
-                                  FLAGS_blacklist[i+0], FLAGS_blacklist[i+1], FLAGS_blacklist[i+2]);
+            match(FLAGS_blacklist[i+1], src) &&
+            match(FLAGS_blacklist[i+2], srcOptions) &&
+            match(FLAGS_blacklist[i+3], name)) {
+            return SkStringPrintf("%s %s %s %s",
+                                  FLAGS_blacklist[i+0], FLAGS_blacklist[i+1],
+                                  FLAGS_blacklist[i+2], FLAGS_blacklist[i+3]);
         }
     }
     return "";
@@ -364,7 +376,8 @@
     static void Run(Task* task) {
         SkString name = task->src->name();
         SkString note;
-        SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag, name.c_str());
+        SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag,
+                                                 task->src.options, name.c_str());
         if (!whyBlacklisted.isEmpty()) {
             note.appendf(" (--blacklist %s)", whyBlacklisted.c_str());
         }
@@ -374,20 +387,22 @@
         if (!FLAGS_dryRun && whyBlacklisted.isEmpty()) {
             SkBitmap bitmap;
             SkDynamicMemoryWStream stream;
-            start(task->sink.tag, task->src.tag, name.c_str());
+            start(task->sink.tag, task->src.tag, task->src.options, name.c_str());
             Error err = task->sink->draw(*task->src, &bitmap, &stream, &log);
             if (!err.isEmpty()) {
                 timer.end();
                 if (err.isFatal()) {
-                    fail(SkStringPrintf("%s %s %s: %s",
+                    fail(SkStringPrintf("%s %s %s %s: %s",
                                         task->sink.tag,
                                         task->src.tag,
+                                        task->src.options,
                                         name.c_str(),
                                         err.c_str()));
                 } else {
                     note.appendf(" (skipped: %s)", err.c_str());
                 }
-                done(timer.fWall, task->sink.tag, task->src.tag, name, note, log);
+                done(timer.fWall, task->sink.tag, task->src.tag, task->src.options,
+                     name, note, log);
                 return;
             }
             SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream());
@@ -409,11 +424,13 @@
             }
 
             if (!FLAGS_readPath.isEmpty() &&
-                !gGold.contains(Gold(task->sink.tag, task->src.tag, name, md5))) {
-                fail(SkStringPrintf("%s not found for %s %s %s in %s",
+                !gGold.contains(Gold(task->sink.tag, task->src.tag,
+                                     task->src.options, name, md5))) {
+                fail(SkStringPrintf("%s not found for %s %s %s %s in %s",
                                     md5.c_str(),
                                     task->sink.tag,
                                     task->src.tag,
+                                    task->src.options,
                                     name.c_str(),
                                     FLAGS_readPath[0]));
             }
@@ -429,7 +446,7 @@
             }
         }
         timer.end();
-        done(timer.fWall, task->sink.tag, task->src.tag, name, note, log);
+        done(timer.fWall, task->sink.tag, task->src.tag, task->src.options, name, note, log);
     }
 
     static void WriteToDisk(const Task& task,
@@ -438,11 +455,12 @@
                             SkStream* data, size_t len,
                             const SkBitmap* bitmap) {
         JsonWriter::BitmapResult result;
-        result.name       = task.src->name();
-        result.config     = task.sink.tag;
-        result.sourceType = task.src.tag;
-        result.ext        = ext;
-        result.md5        = md5;
+        result.name          = task.src->name();
+        result.config        = task.sink.tag;
+        result.sourceType    = task.src.tag;
+        result.sourceOptions = task.src.options;
+        result.ext           = ext;
+        result.md5           = md5;
         JsonWriter::AddBitmapResult(result);
 
         const char* dir = FLAGS_writePath[0];
@@ -464,6 +482,10 @@
             sk_mkdir(path.c_str());
             path = SkOSPath::Join(path.c_str(), task.src.tag);
             sk_mkdir(path.c_str());
+            if (strcmp(task.src.options, "") != 0) {
+              path = SkOSPath::Join(path.c_str(), task.src.options);
+              sk_mkdir(path.c_str());
+            }
             path = SkOSPath::Join(path.c_str(), task.src->name().c_str());
             path.append(".");
             path.append(ext);
@@ -546,12 +568,12 @@
     WallTimer timer;
     timer.start();
     if (!FLAGS_dryRun) {
-        start("unit", "test", test->name);
+        start("unit", "test", "", test->name);
         GrContextFactory factory;
         test->proc(&reporter, &factory);
     }
     timer.end();
-    done(timer.fWall, "unit", "test", test->name, "", "");
+    done(timer.fWall, "unit", "test", "", test->name, "", "");
 }
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/