• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  *
7  * TODO(epoger): Combine this with tools/image_expectations.cpp, or eliminate one of the two.
8  */
9 
10 #include "gm_expectations.h"
11 #include "SkBitmapHasher.h"
12 #include "SkData.h"
13 #include "SkImageDecoder.h"
14 
15 #define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message")
16 
17 // See gm_json.py for descriptions of each of these JSON keys.
18 // These constants must be kept in sync with the ones in that Python file!
19 const static char kJsonKey_ActualResults[]   = "actual-results";
20 const static char kJsonKey_ActualResults_Failed[]        = "failed";
21 const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
22 const static char kJsonKey_ActualResults_NoComparison[]  = "no-comparison";
23 const static char kJsonKey_ActualResults_Succeeded[]     = "succeeded";
24 const static char kJsonKey_ExpectedResults[] = "expected-results";
25 const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
26 const static char kJsonKey_ExpectedResults_IgnoreFailure[]  = "ignore-failure";
27 
28 // Types of result hashes we support in the JSON file.
29 const static char kJsonKey_Hashtype_Bitmap_64bitMD5[]  = "bitmap-64bitMD5";
30 
31 
32 namespace skiagm {
33 
CreateJsonTree(Json::Value expectedResults,Json::Value actualResultsFailed,Json::Value actualResultsFailureIgnored,Json::Value actualResultsNoComparison,Json::Value actualResultsSucceeded)34     Json::Value CreateJsonTree(Json::Value expectedResults,
35                                Json::Value actualResultsFailed,
36                                Json::Value actualResultsFailureIgnored,
37                                Json::Value actualResultsNoComparison,
38                                Json::Value actualResultsSucceeded) {
39         Json::Value actualResults;
40         actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
41         actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
42         actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
43         actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
44         Json::Value root;
45         root[kJsonKey_ActualResults] = actualResults;
46         root[kJsonKey_ExpectedResults] = expectedResults;
47         return root;
48     }
49 
50     // GmResultDigest class...
51 
GmResultDigest(const SkBitmap & bitmap)52     GmResultDigest::GmResultDigest(const SkBitmap &bitmap) {
53         fIsValid = SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest);
54     }
55 
GmResultDigest(const Json::Value & jsonTypeValuePair)56     GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) {
57         fIsValid = false;
58         if (!jsonTypeValuePair.isArray()) {
59             SkDebugf("found non-array json value when parsing GmResultDigest: %s\n",
60                      jsonTypeValuePair.toStyledString().c_str());
61             DEBUGFAIL_SEE_STDERR;
62         } else if (2 != jsonTypeValuePair.size()) {
63             SkDebugf("found json array with wrong size when parsing GmResultDigest: %s\n",
64                      jsonTypeValuePair.toStyledString().c_str());
65             DEBUGFAIL_SEE_STDERR;
66         } else {
67             // TODO(epoger): The current implementation assumes that the
68             // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
69             Json::Value jsonHashValue = jsonTypeValuePair[1];
70             if (!jsonHashValue.isIntegral()) {
71                 SkDebugf("found non-integer jsonHashValue when parsing GmResultDigest: %s\n",
72                          jsonTypeValuePair.toStyledString().c_str());
73                 DEBUGFAIL_SEE_STDERR;
74             } else {
75                 fHashDigest = jsonHashValue.asUInt64();
76                 fIsValid = true;
77             }
78         }
79     }
80 
isValid() const81     bool GmResultDigest::isValid() const {
82         return fIsValid;
83     }
84 
equals(const GmResultDigest & other) const85     bool GmResultDigest::equals(const GmResultDigest &other) const {
86         // TODO(epoger): The current implementation assumes that this
87         // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5
88         return (this->fIsValid && other.fIsValid && (this->fHashDigest == other.fHashDigest));
89     }
90 
asJsonTypeValuePair() const91     Json::Value GmResultDigest::asJsonTypeValuePair() const {
92         // TODO(epoger): The current implementation assumes that the
93         // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
94         Json::Value jsonTypeValuePair;
95         if (fIsValid) {
96             jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
97             jsonTypeValuePair.append(Json::UInt64(fHashDigest));
98         } else {
99             jsonTypeValuePair.append(Json::Value("INVALID"));
100         }
101         return jsonTypeValuePair;
102     }
103 
getHashType() const104     SkString GmResultDigest::getHashType() const {
105         // TODO(epoger): The current implementation assumes that the
106         // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
107         return SkString(kJsonKey_Hashtype_Bitmap_64bitMD5);
108     }
109 
getDigestValue() const110     SkString GmResultDigest::getDigestValue() const {
111         // TODO(epoger): The current implementation assumes that the
112         // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
113         SkString retval;
114         retval.appendU64(fHashDigest);
115         return retval;
116     }
117 
118 
119     // Expectations class...
120 
Expectations(bool ignoreFailure)121     Expectations::Expectations(bool ignoreFailure) {
122         fIgnoreFailure = ignoreFailure;
123     }
124 
Expectations(const SkBitmap & bitmap,bool ignoreFailure)125     Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
126         fBitmap = bitmap;
127         fIgnoreFailure = ignoreFailure;
128         fAllowedResultDigests.push_back(GmResultDigest(bitmap));
129     }
130 
Expectations(const BitmapAndDigest & bitmapAndDigest)131     Expectations::Expectations(const BitmapAndDigest& bitmapAndDigest) {
132         fBitmap = bitmapAndDigest.fBitmap;
133         fIgnoreFailure = false;
134         fAllowedResultDigests.push_back(bitmapAndDigest.fDigest);
135     }
136 
Expectations(Json::Value jsonElement)137     Expectations::Expectations(Json::Value jsonElement) {
138         if (jsonElement.empty()) {
139             fIgnoreFailure = kDefaultIgnoreFailure;
140         } else {
141             Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
142             if (ignoreFailure.isNull()) {
143                 fIgnoreFailure = kDefaultIgnoreFailure;
144             } else if (!ignoreFailure.isBool()) {
145                 SkDebugf("found non-boolean json value for key '%s' in element '%s'\n",
146                          kJsonKey_ExpectedResults_IgnoreFailure,
147                          jsonElement.toStyledString().c_str());
148                 DEBUGFAIL_SEE_STDERR;
149                 fIgnoreFailure = kDefaultIgnoreFailure;
150             } else {
151                 fIgnoreFailure = ignoreFailure.asBool();
152             }
153 
154             Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests];
155             if (allowedDigests.isNull()) {
156                 // ok, we'll just assume there aren't any AllowedDigests to compare against
157             } else if (!allowedDigests.isArray()) {
158                 SkDebugf("found non-array json value for key '%s' in element '%s'\n",
159                          kJsonKey_ExpectedResults_AllowedDigests,
160                          jsonElement.toStyledString().c_str());
161                 DEBUGFAIL_SEE_STDERR;
162             } else {
163                 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
164                     fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
165                 }
166             }
167         }
168     }
169 
match(GmResultDigest actualGmResultDigest) const170     bool Expectations::match(GmResultDigest actualGmResultDigest) const {
171         for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
172             GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i];
173             if (allowedResultDigest.equals(actualGmResultDigest)) {
174                 return true;
175             }
176         }
177         return false;
178     }
179 
asJsonValue() const180     Json::Value Expectations::asJsonValue() const {
181         Json::Value allowedDigestArray;
182         if (!this->fAllowedResultDigests.empty()) {
183             for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
184                 allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair());
185             }
186         }
187 
188         Json::Value jsonExpectations;
189         jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
190         jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure]  = this->ignoreFailure();
191         return jsonExpectations;
192     }
193 
194     // IndividualImageExpectationsSource class...
195 
get(const char * testName) const196     Expectations IndividualImageExpectationsSource::get(const char *testName) const {
197         SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName);
198         SkBitmap referenceBitmap;
199         bool decodedReferenceBitmap =
200             SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, kN32_SkColorType,
201                                        SkImageDecoder::kDecodePixels_Mode, NULL);
202         if (decodedReferenceBitmap) {
203             return Expectations(referenceBitmap);
204         } else {
205             return Expectations();
206         }
207     }
208 
209 
210     // JsonExpectationsSource class...
211 
JsonExpectationsSource(const char * jsonPath)212     JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
213         Parse(jsonPath, &fJsonRoot);
214         fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
215     }
216 
get(const char * testName) const217     Expectations JsonExpectationsSource::get(const char *testName) const {
218         return Expectations(fJsonExpectedResults[testName]);
219     }
220 
Parse(const char * jsonPath,Json::Value * jsonRoot)221     /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
222         SkAutoDataUnref dataRef(SkData::NewFromFileName(jsonPath));
223         if (NULL == dataRef.get()) {
224             SkDebugf("error reading JSON file %s\n", jsonPath);
225             DEBUGFAIL_SEE_STDERR;
226             return false;
227         }
228 
229         const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
230         size_t size = dataRef.get()->size();
231         Json::Reader reader;
232         if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
233             SkDebugf("error parsing JSON file %s\n", jsonPath);
234             DEBUGFAIL_SEE_STDERR;
235             return false;
236         }
237         return true;
238     }
239 }
240