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