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 8#include "gm_expectations.h" 9#include "SkBitmapHasher.h" 10#include "SkImageDecoder.h" 11 12#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message") 13 14// See gm_json.py for descriptions of each of these JSON keys. 15// These constants must be kept in sync with the ones in that Python file! 16const static char kJsonKey_ActualResults[] = "actual-results"; 17const static char kJsonKey_ActualResults_Failed[] = "failed"; 18const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored"; 19const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison"; 20const static char kJsonKey_ActualResults_Succeeded[] = "succeeded"; 21const static char kJsonKey_ExpectedResults[] = "expected-results"; 22const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests"; 23const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure"; 24 25// Types of result hashes we support in the JSON file. 26const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5"; 27 28 29namespace skiagm { 30 SK_DEFINE_INST_COUNT(ExpectationsSource) 31 32 void gm_fprintf(FILE *stream, const char format[], ...) { 33 va_list args; 34 va_start(args, format); 35 fprintf(stream, "GM: "); 36 vfprintf(stream, format, args); 37 va_end(args); 38 } 39 40 Json::Value CreateJsonTree(Json::Value expectedResults, 41 Json::Value actualResultsFailed, 42 Json::Value actualResultsFailureIgnored, 43 Json::Value actualResultsNoComparison, 44 Json::Value actualResultsSucceeded) { 45 Json::Value actualResults; 46 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed; 47 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored; 48 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison; 49 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded; 50 Json::Value root; 51 root[kJsonKey_ActualResults] = actualResults; 52 root[kJsonKey_ExpectedResults] = expectedResults; 53 return root; 54 } 55 56 57 // GmResultDigest class... 58 59 GmResultDigest::GmResultDigest(const SkBitmap &bitmap) { 60 fIsValid = SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest); 61 } 62 63 GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) { 64 fIsValid = false; 65 if (!jsonTypeValuePair.isArray()) { 66 gm_fprintf(stderr, "found non-array json value when parsing GmResultDigest: %s\n", 67 jsonTypeValuePair.toStyledString().c_str()); 68 DEBUGFAIL_SEE_STDERR; 69 } else if (2 != jsonTypeValuePair.size()) { 70 gm_fprintf(stderr, "found json array with wrong size when parsing GmResultDigest: %s\n", 71 jsonTypeValuePair.toStyledString().c_str()); 72 DEBUGFAIL_SEE_STDERR; 73 } else { 74 // TODO(epoger): The current implementation assumes that the 75 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5 76 Json::Value jsonHashValue = jsonTypeValuePair[1]; 77 if (!jsonHashValue.isIntegral()) { 78 gm_fprintf(stderr, 79 "found non-integer jsonHashValue when parsing GmResultDigest: %s\n", 80 jsonTypeValuePair.toStyledString().c_str()); 81 DEBUGFAIL_SEE_STDERR; 82 } else { 83 fHashDigest = jsonHashValue.asUInt64(); 84 fIsValid = true; 85 } 86 } 87 } 88 89 bool GmResultDigest::isValid() const { 90 return fIsValid; 91 } 92 93 bool GmResultDigest::equals(const GmResultDigest &other) const { 94 // TODO(epoger): The current implementation assumes that this 95 // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5 96 return (this->fIsValid && other.fIsValid && (this->fHashDigest == other.fHashDigest)); 97 } 98 99 Json::Value GmResultDigest::asJsonTypeValuePair() const { 100 // TODO(epoger): The current implementation assumes that the 101 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5 102 Json::Value jsonTypeValuePair; 103 if (fIsValid) { 104 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5)); 105 jsonTypeValuePair.append(Json::UInt64(fHashDigest)); 106 } else { 107 jsonTypeValuePair.append(Json::Value("INVALID")); 108 } 109 return jsonTypeValuePair; 110 } 111 112 SkString GmResultDigest::getHashType() const { 113 // TODO(epoger): The current implementation assumes that the 114 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5 115 return SkString(kJsonKey_Hashtype_Bitmap_64bitMD5); 116 } 117 118 SkString GmResultDigest::getDigestValue() const { 119 // TODO(epoger): The current implementation assumes that the 120 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5 121 SkString retval; 122 retval.appendU64(fHashDigest); 123 return retval; 124 } 125 126 127 // Expectations class... 128 129 Expectations::Expectations(bool ignoreFailure) { 130 fIgnoreFailure = ignoreFailure; 131 } 132 133 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) { 134 fBitmap = bitmap; 135 fIgnoreFailure = ignoreFailure; 136 fAllowedResultDigests.push_back(GmResultDigest(bitmap)); 137 } 138 139 Expectations::Expectations(Json::Value jsonElement) { 140 if (jsonElement.empty()) { 141 fIgnoreFailure = kDefaultIgnoreFailure; 142 } else { 143 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure]; 144 if (ignoreFailure.isNull()) { 145 fIgnoreFailure = kDefaultIgnoreFailure; 146 } else if (!ignoreFailure.isBool()) { 147 gm_fprintf(stderr, "found non-boolean json value" 148 " for key '%s' in element '%s'\n", 149 kJsonKey_ExpectedResults_IgnoreFailure, 150 jsonElement.toStyledString().c_str()); 151 DEBUGFAIL_SEE_STDERR; 152 fIgnoreFailure = kDefaultIgnoreFailure; 153 } else { 154 fIgnoreFailure = ignoreFailure.asBool(); 155 } 156 157 Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests]; 158 if (allowedDigests.isNull()) { 159 // ok, we'll just assume there aren't any AllowedDigests to compare against 160 } else if (!allowedDigests.isArray()) { 161 gm_fprintf(stderr, "found non-array json value" 162 " for key '%s' in element '%s'\n", 163 kJsonKey_ExpectedResults_AllowedDigests, 164 jsonElement.toStyledString().c_str()); 165 DEBUGFAIL_SEE_STDERR; 166 } else { 167 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) { 168 fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i])); 169 } 170 } 171 } 172 } 173 174 bool Expectations::match(GmResultDigest actualGmResultDigest) const { 175 for (int i=0; i < this->fAllowedResultDigests.count(); i++) { 176 GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i]; 177 if (allowedResultDigest.equals(actualGmResultDigest)) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 Json::Value Expectations::asJsonValue() const { 185 Json::Value allowedDigestArray; 186 if (!this->fAllowedResultDigests.empty()) { 187 for (int i=0; i < this->fAllowedResultDigests.count(); i++) { 188 allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair()); 189 } 190 } 191 192 Json::Value jsonExpectations; 193 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray; 194 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure(); 195 return jsonExpectations; 196 } 197 198 199 // IndividualImageExpectationsSource class... 200 201 Expectations IndividualImageExpectationsSource::get(const char *testName) { 202 SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName); 203 SkBitmap referenceBitmap; 204 bool decodedReferenceBitmap = 205 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, 206 SkBitmap::kARGB_8888_Config, 207 SkImageDecoder::kDecodePixels_Mode, 208 NULL); 209 if (decodedReferenceBitmap) { 210 return Expectations(referenceBitmap); 211 } else { 212 return Expectations(); 213 } 214 } 215 216 217 // JsonExpectationsSource class... 218 219 JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) { 220 Parse(jsonPath, &fJsonRoot); 221 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults]; 222 } 223 224 Expectations JsonExpectationsSource::get(const char *testName) { 225 return Expectations(fJsonExpectedResults[testName]); 226 } 227 228 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) { 229 if (0 == maxBytes) { 230 return SkData::NewEmpty(); 231 } 232 char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes)); 233 char* bufPtr = bufStart; 234 size_t bytesRemaining = maxBytes; 235 while (bytesRemaining > 0) { 236 size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining); 237 if (0 == bytesReadThisTime) { 238 break; 239 } 240 bytesRemaining -= bytesReadThisTime; 241 bufPtr += bytesReadThisTime; 242 } 243 return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining); 244 } 245 246 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) { 247 SkFILEStream inFile(jsonPath); 248 if (!inFile.isValid()) { 249 gm_fprintf(stderr, "unable to read JSON file %s\n", jsonPath); 250 DEBUGFAIL_SEE_STDERR; 251 return false; 252 } 253 254 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile)); 255 if (NULL == dataRef.get()) { 256 gm_fprintf(stderr, "error reading JSON file %s\n", jsonPath); 257 DEBUGFAIL_SEE_STDERR; 258 return false; 259 } 260 261 const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data()); 262 size_t size = dataRef.get()->size(); 263 Json::Reader reader; 264 if (!reader.parse(bytes, bytes+size, *jsonRoot)) { 265 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath); 266 DEBUGFAIL_SEE_STDERR; 267 return false; 268 } 269 return true; 270 } 271 272} 273