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