add --descriptions flag to render_pictures tool

Needed because right now, when you look at the full set of SKP results in
rebaseline_server, you can't tell which renderMode (or builder) generated each
one.

BUG=skia:2833
R=borenet@google.com

Author: epoger@google.com

Review URL: https://codereview.chromium.org/466153006
diff --git a/gm/gm_json.py b/gm/gm_json.py
index 62d5755..6a91cc4 100644
--- a/gm/gm_json.py
+++ b/gm/gm_json.py
@@ -48,6 +48,10 @@
 JSONKEY_ACTUALRESULTS_SUCCEEDED = 'succeeded'
 
 
+# Descriptions of the result set as a whole.
+JSONKEY_DESCRIPTIONS = 'descriptions'
+
+
 JSONKEY_EXPECTEDRESULTS = 'expected-results'
 
 # One or more [HashType/DigestValue] pairs representing valid results for this
diff --git a/tools/image_expectations.cpp b/tools/image_expectations.cpp
index ac232e9..dfc2638 100644
--- a/tools/image_expectations.cpp
+++ b/tools/image_expectations.cpp
@@ -28,6 +28,7 @@
  * output module.
  */
 const static char kJsonKey_ActualResults[] = "actual-results";
+const static char kJsonKey_Descriptions[] = "descriptions";
 const static char kJsonKey_ExpectedResults[] = "expected-results";
 const static char kJsonKey_Header[] = "header";
 const static char kJsonKey_Header_Type[] = "type";
@@ -177,6 +178,10 @@
         }
     }
 
+    void ImageResultsAndExpectations::addDescription(const char *key, const char *value) {
+        fDescriptions[key] = value;
+    }
+
     bool ImageResultsAndExpectations::matchesExpectation(const char *sourceName,
                                                          const ImageDigest &digest,
                                                          const int *tileNumber) {
@@ -205,8 +210,9 @@
         header[kJsonKey_Header_Type] = kJsonValue_Header_Type;
         header[kJsonKey_Header_Revision] = kJsonValue_Header_Revision;
         Json::Value root;
-        root[kJsonKey_Header] = header;
         root[kJsonKey_ActualResults] = fActualResults;
+        root[kJsonKey_Descriptions] = fDescriptions;
+        root[kJsonKey_Header] = header;
         std::string jsonStdString = root.toStyledString();
         SkFILEWStream stream(filename);
         stream.write(jsonStdString.c_str(), jsonStdString.length());
diff --git a/tools/image_expectations.h b/tools/image_expectations.h
index a24334e..ae1df19 100644
--- a/tools/image_expectations.h
+++ b/tools/image_expectations.h
@@ -111,6 +111,14 @@
                  const int *tileNumber=NULL);
 
         /**
+         * Adds a key/value pair to the descriptions dict within the summary of results.
+         *
+         * @param key key within the descriptions dict
+         * @param value value to associate with that key
+         */
+        void addDescription(const char *key, const char *value);
+
+        /**
          * Returns true if this test result matches its expectations.
          * If there are no expectations for this test result, this will return false.
          *
@@ -140,6 +148,7 @@
         static bool Parse(SkFILE* filePtr, Json::Value *jsonRoot);
 
         Json::Value fActualResults;
+        Json::Value fDescriptions;
         Json::Value fExpectedJsonRoot;
         Json::Value fExpectedResults;
     };
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
index 23d973f..13cef09 100644
--- a/tools/render_pictures_main.cpp
+++ b/tools/render_pictures_main.cpp
@@ -26,12 +26,16 @@
 #include "picture_utils.h"
 
 // Flags used by this file, alphabetically:
+DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
 DECLARE_bool(deferImageDecoding);
+DEFINE_string(descriptions, "", "one or more key=value pairs to add to the descriptions section "
+              "of the JSON summary.");
 DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ "
              "by more than this amount are considered errors, though all diffs are reported. "
              "Requires --validate.");
 DEFINE_string(mismatchPath, "", "Write images for tests that failed due to "
               "pixel mismatches into this directory.");
+DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before rendering.");
 DEFINE_string(readJsonSummaryPath, "", "JSON file to read image expectations from.");
 DECLARE_string(readPath);
 DEFINE_bool(writeChecksumBasedFilenames, false,
@@ -47,10 +51,6 @@
             "the picture rendered in simple mode. When used in conjunction with --bbh, results "
             "are validated against the picture rendered in the same mode, but without the bbh.");
 
-DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
-
-DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before rendering.");
-
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -486,6 +486,13 @@
 #endif
 #endif
     if (FLAGS_writeJsonSummaryPath.count() == 1) {
+        // If there were any descriptions on the command line, insert them now.
+        for (int i=0; i<FLAGS_descriptions.count(); i++) {
+            SkTArray<SkString> tokens;
+            SkStrSplit(FLAGS_descriptions[i], "=", &tokens);
+            SkASSERT(tokens.count() == 2);
+            jsonSummary.addDescription(tokens[0].c_str(), tokens[1].c_str());
+        }
         jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]);
     }
     return 0;
diff --git a/tools/tests/render_pictures_test.py b/tools/tests/render_pictures_test.py
index a7636dc..1fdeb10 100755
--- a/tools/tests/render_pictures_test.py
+++ b/tools/tests/render_pictures_test.py
@@ -197,6 +197,7 @@
         '--writeWholeImage'])
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 "tiled-images": RED_TILES,
@@ -232,6 +233,7 @@
     modified_red_tiles[5]['comparisonResult'] = 'no-comparison'
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 "tiled-images": modified_red_tiles,
@@ -276,6 +278,7 @@
     if expected_summary_dict == None:
       expected_summary_dict = {
           "header" : EXPECTED_HEADER_CONTENTS,
+          "descriptions" : None,
           "actual-results" : {
               "red.skp": {
                   "whole-image": RED_WHOLEIMAGE,
@@ -300,6 +303,7 @@
       pass
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 "whole-image": modified_dict(
@@ -320,12 +324,19 @@
     write_path_dir = self.create_empty_dir(
         path=os.path.join(self._output_dir, 'writePath'))
     self._generate_skps()
-    self._run_render_pictures(['-r', self._input_skp_dir,
-                               '--writeChecksumBasedFilenames',
-                               '--writePath', write_path_dir,
-                               '--writeJsonSummaryPath', output_json_path])
+    self._run_render_pictures([
+        '-r', self._input_skp_dir,
+        '--descriptions', 'builder=builderName', 'renderMode=renderModeName',
+        '--writeChecksumBasedFilenames',
+        '--writePath', write_path_dir,
+        '--writeJsonSummaryPath', output_json_path,
+    ])
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : {
+            "builder": "builderName",
+            "renderMode": "renderModeName",
+        },
         "actual-results" : {
             "red.skp": {
                 # Manually verified: 640x400 red rectangle with black border
@@ -373,6 +384,7 @@
         '--writeJsonSummaryPath', output_json_path])
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 "whole-image": RED_WHOLEIMAGE,
@@ -400,6 +412,7 @@
         '--writeJsonSummaryPath', output_json_path])
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 "tiled-images": RED_TILES,
@@ -434,6 +447,7 @@
         '--writeJsonSummaryPath', output_json_path])
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 "tiled-images": RED_TILES,
@@ -464,6 +478,7 @@
                                '--writeJsonSummaryPath', output_json_path])
     expected_summary_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "actual-results" : {
             "red.skp": {
                 # Manually verified these 6 images, all 256x256 tiles,
@@ -589,6 +604,7 @@
     """
     expectations_dict = {
         "header" : EXPECTED_HEADER_CONTENTS,
+        "descriptions" : None,
         "expected-results" : {
             # red.skp: these should fail the comparison
             "red.skp": {