Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "gm_expectations.h" | 8 #include "gm_expectations.h" |
| 9 #include "SkBitmapHasher.h" | 9 #include "SkBitmapHasher.h" |
| 10 #include "SkImageDecoder.h" | 10 #include "SkImageDecoder.h" |
| 11 | 11 |
| 12 #define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message") | 12 #define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message") |
| 13 | 13 |
| 14 // These constants must be kept in sync with the JSONKEY_ constants in | 14 // These constants must be kept in sync with the JSONKEY_ constants in |
| 15 // display_json_results.py ! | 15 // display_json_results.py ! |
| 16 const static char kJsonKey_ActualResults[] = "actual-results"; | 16 const static char kJsonKey_ActualResults[] = "actual-results"; |
| 17 const static char kJsonKey_ActualResults_Failed[] = "failed"; | 17 const static char kJsonKey_ActualResults_Failed[] = "failed"; |
| 18 const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored"; | 18 const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored"; |
| 19 const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison"; | 19 const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison"; |
| 20 const static char kJsonKey_ActualResults_Succeeded[] = "succeeded"; | 20 const static char kJsonKey_ActualResults_Succeeded[] = "succeeded"; |
| 21 const static char kJsonKey_ActualResults_AnyStatus_BitmapHash[] = "bitmap-64bit MD5"; | |
| 22 | 21 |
| 23 const static char kJsonKey_ExpectedResults[] = "expected-results"; | 22 const static char kJsonKey_ExpectedResults[] = "expected-results"; |
| 24 const static char kJsonKey_ExpectedResults_AllowedBitmapHashes[] = "allowed-bitm ap-64bitMD5s"; | 23 const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests"; |
|
epoger
2013/05/23 19:18:31
As the GmResultDigest class learns about additiona
| |
| 25 const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-f ailure"; | 24 const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure"; |
| 25 | |
| 26 // Types of result hashes we support in the JSON file. | |
| 27 const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5"; | |
| 28 | |
| 26 | 29 |
| 27 namespace skiagm { | 30 namespace skiagm { |
| 28 | 31 |
| 29 void gm_fprintf(FILE *stream, const char format[], ...) { | 32 void gm_fprintf(FILE *stream, const char format[], ...) { |
| 30 va_list args; | 33 va_list args; |
| 31 va_start(args, format); | 34 va_start(args, format); |
| 32 fprintf(stream, "GM: "); | 35 fprintf(stream, "GM: "); |
| 33 vfprintf(stream, format, args); | 36 vfprintf(stream, format, args); |
| 34 va_end(args); | 37 va_end(args); |
| 35 } | 38 } |
| 36 | 39 |
| 37 SkString SkPathJoin(const char *rootPath, const char *relativePath) { | 40 SkString SkPathJoin(const char *rootPath, const char *relativePath) { |
| 38 SkString result(rootPath); | 41 SkString result(rootPath); |
| 39 if (!result.endsWith(SkPATH_SEPARATOR)) { | 42 if (!result.endsWith(SkPATH_SEPARATOR)) { |
| 40 result.appendUnichar(SkPATH_SEPARATOR); | 43 result.appendUnichar(SkPATH_SEPARATOR); |
| 41 } | 44 } |
| 42 result.append(relativePath); | 45 result.append(relativePath); |
| 43 return result; | 46 return result; |
| 44 } | 47 } |
| 45 | 48 |
| 46 // TODO(epoger): This currently assumes that the result SkHashDigest was | |
| 47 // generated as an SkHashDigest of an SkBitmap. We'll need to allow for oth er | |
| 48 // hash types to cover non-bitmaps. | |
| 49 Json::Value ActualResultAsJsonValue(const SkHashDigest& result) { | |
| 50 Json::Value jsonValue; | |
| 51 jsonValue[kJsonKey_ActualResults_AnyStatus_BitmapHash] = asJsonValue(res ult); | |
| 52 return jsonValue; | |
| 53 } | |
| 54 | |
| 55 Json::Value CreateJsonTree(Json::Value expectedResults, | 49 Json::Value CreateJsonTree(Json::Value expectedResults, |
| 56 Json::Value actualResultsFailed, | 50 Json::Value actualResultsFailed, |
| 57 Json::Value actualResultsFailureIgnored, | 51 Json::Value actualResultsFailureIgnored, |
| 58 Json::Value actualResultsNoComparison, | 52 Json::Value actualResultsNoComparison, |
| 59 Json::Value actualResultsSucceeded) { | 53 Json::Value actualResultsSucceeded) { |
| 60 Json::Value actualResults; | 54 Json::Value actualResults; |
| 61 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed; | 55 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed; |
| 62 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFail ureIgnored; | 56 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFail ureIgnored; |
| 63 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComp arison; | 57 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComp arison; |
| 64 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded ; | 58 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded ; |
| 65 Json::Value root; | 59 Json::Value root; |
| 66 root[kJsonKey_ActualResults] = actualResults; | 60 root[kJsonKey_ActualResults] = actualResults; |
| 67 root[kJsonKey_ExpectedResults] = expectedResults; | 61 root[kJsonKey_ExpectedResults] = expectedResults; |
| 68 return root; | 62 return root; |
| 69 } | 63 } |
| 70 | 64 |
| 71 | 65 |
| 66 // GmResultDigest class... | |
| 67 | |
| 68 GmResultDigest::GmResultDigest(const SkBitmap &bitmap) { | |
| 69 // TODO(epoger): Better handling for error returned by ComputeDigest()? | |
| 70 // For now, we just report a digest of 0 in error cases, like before. | |
| 71 if (!SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest)) { | |
| 72 fHashDigest = 0; | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) { | |
| 77 // TODO(epoger): We check for various error cases below, but in release | |
| 78 // mode we keep on trucking even in these error cases. | |
| 79 // | |
| 80 // Maybe we should set some sort of fIsBogus boolean flag on the | |
| 81 // object created? Or, instead of a constructor, use a factory method | |
| 82 // which can signal success/failure? | |
| 83 | |
| 84 fHashDigest = 0; // default value in case of error | |
| 85 if (!jsonTypeValuePair.isArray()) { | |
| 86 gm_fprintf(stderr, "found non-array json value when parsing GmResult Digest: %s\n", | |
| 87 jsonTypeValuePair.toStyledString().c_str()); | |
| 88 DEBUGFAIL_SEE_STDERR; | |
| 89 } else if (2 != jsonTypeValuePair.size()) { | |
| 90 gm_fprintf(stderr, "found json array with wrong size when parsing Gm ResultDigest: %s\n", | |
| 91 jsonTypeValuePair.toStyledString().c_str()); | |
| 92 DEBUGFAIL_SEE_STDERR; | |
| 93 } else { | |
| 94 // TODO(epoger): The current implementation assumes that the | |
| 95 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5 | |
| 96 Json::Value jsonHashValue = jsonTypeValuePair[1]; | |
| 97 if (!jsonHashValue.isIntegral()) { | |
| 98 gm_fprintf(stderr, | |
| 99 "found non-integer jsonHashValue when parsing GmResul tDigest: %s\n", | |
| 100 jsonTypeValuePair.toStyledString().c_str()); | |
| 101 DEBUGFAIL_SEE_STDERR; | |
| 102 } else { | |
| 103 fHashDigest = jsonHashValue.asUInt64(); | |
| 104 } | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 bool GmResultDigest::equals(const GmResultDigest &other) const { | |
| 109 // TODO(epoger): The current implementation assumes that this | |
| 110 // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5 | |
| 111 return (this->fHashDigest == other.fHashDigest); | |
|
scroggo
2013/05/23 20:42:58
If we special cased 0, two failed GmResultDigests
epoger
2013/05/23 21:06:18
Good idea! I took this and ran with it, adding an
| |
| 112 } | |
| 113 | |
| 114 Json::Value GmResultDigest::asJsonTypeValuePair() const { | |
| 115 // TODO(epoger): The current implementation assumes that the | |
| 116 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5 | |
| 117 Json::Value jsonTypeValuePair; | |
| 118 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5)) ; | |
| 119 jsonTypeValuePair.append(Json::UInt64(fHashDigest)); | |
| 120 return jsonTypeValuePair; | |
| 121 } | |
| 122 | |
| 123 | |
| 72 // Expectations class... | 124 // Expectations class... |
| 73 | 125 |
| 74 Expectations::Expectations(bool ignoreFailure) { | 126 Expectations::Expectations(bool ignoreFailure) { |
| 75 fIgnoreFailure = ignoreFailure; | 127 fIgnoreFailure = ignoreFailure; |
| 76 } | 128 } |
| 77 | 129 |
| 78 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) { | 130 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) { |
| 79 fBitmap = bitmap; | 131 fBitmap = bitmap; |
| 80 fIgnoreFailure = ignoreFailure; | 132 fIgnoreFailure = ignoreFailure; |
| 81 SkHashDigest digest; | 133 fAllowedResultDigests.push_back(GmResultDigest(bitmap)); |
| 82 // TODO(epoger): Better handling for error returned by ComputeDigest()? | |
| 83 // For now, we just report a digest of 0 in error cases, like before. | |
| 84 if (!SkBitmapHasher::ComputeDigest(bitmap, &digest)) { | |
| 85 digest = 0; | |
| 86 } | |
| 87 fAllowedBitmapChecksums.push_back() = digest; | |
| 88 } | 134 } |
| 89 | 135 |
| 90 Expectations::Expectations(Json::Value jsonElement) { | 136 Expectations::Expectations(Json::Value jsonElement) { |
| 91 if (jsonElement.empty()) { | 137 if (jsonElement.empty()) { |
| 92 fIgnoreFailure = kDefaultIgnoreFailure; | 138 fIgnoreFailure = kDefaultIgnoreFailure; |
| 93 } else { | 139 } else { |
| 94 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_Ign oreFailure]; | 140 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_Ign oreFailure]; |
| 95 if (ignoreFailure.isNull()) { | 141 if (ignoreFailure.isNull()) { |
| 96 fIgnoreFailure = kDefaultIgnoreFailure; | 142 fIgnoreFailure = kDefaultIgnoreFailure; |
| 97 } else if (!ignoreFailure.isBool()) { | 143 } else if (!ignoreFailure.isBool()) { |
| 98 gm_fprintf(stderr, "found non-boolean json value" | 144 gm_fprintf(stderr, "found non-boolean json value" |
| 99 " for key '%s' in element '%s'\n", | 145 " for key '%s' in element '%s'\n", |
| 100 kJsonKey_ExpectedResults_IgnoreFailure, | 146 kJsonKey_ExpectedResults_IgnoreFailure, |
| 101 jsonElement.toStyledString().c_str()); | 147 jsonElement.toStyledString().c_str()); |
| 102 DEBUGFAIL_SEE_STDERR; | 148 DEBUGFAIL_SEE_STDERR; |
| 103 fIgnoreFailure = kDefaultIgnoreFailure; | 149 fIgnoreFailure = kDefaultIgnoreFailure; |
| 104 } else { | 150 } else { |
| 105 fIgnoreFailure = ignoreFailure.asBool(); | 151 fIgnoreFailure = ignoreFailure.asBool(); |
| 106 } | 152 } |
| 107 | 153 |
| 108 Json::Value allowedChecksums = | 154 Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_Al lowedDigests]; |
| 109 jsonElement[kJsonKey_ExpectedResults_AllowedBitmapHashes]; | 155 if (allowedDigests.isNull()) { |
| 110 if (allowedChecksums.isNull()) { | 156 // ok, we'll just assume there aren't any AllowedDigests to comp are against |
| 111 // ok, we'll just assume there aren't any expected checksums to compare against | 157 } else if (!allowedDigests.isArray()) { |
| 112 } else if (!allowedChecksums.isArray()) { | |
| 113 gm_fprintf(stderr, "found non-array json value" | 158 gm_fprintf(stderr, "found non-array json value" |
| 114 " for key '%s' in element '%s'\n", | 159 " for key '%s' in element '%s'\n", |
| 115 kJsonKey_ExpectedResults_AllowedBitmapHashes, | 160 kJsonKey_ExpectedResults_AllowedDigests, |
| 116 jsonElement.toStyledString().c_str()); | 161 jsonElement.toStyledString().c_str()); |
| 117 DEBUGFAIL_SEE_STDERR; | 162 DEBUGFAIL_SEE_STDERR; |
| 118 } else { | 163 } else { |
| 119 for (Json::ArrayIndex i=0; i<allowedChecksums.size(); i++) { | 164 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) { |
| 120 Json::Value checksumElement = allowedChecksums[i]; | 165 fAllowedResultDigests.push_back(GmResultDigest(allowedDigest s[i])); |
| 121 if (!checksumElement.isIntegral()) { | |
| 122 gm_fprintf(stderr, "found non-integer checksum" | |
| 123 " in json element '%s'\n", | |
| 124 jsonElement.toStyledString().c_str()); | |
| 125 DEBUGFAIL_SEE_STDERR; | |
| 126 } else { | |
| 127 fAllowedBitmapChecksums.push_back() = asChecksum(checksu mElement); | |
| 128 } | |
| 129 } | 166 } |
| 130 } | 167 } |
| 131 } | 168 } |
| 132 } | 169 } |
| 133 | 170 |
| 134 bool Expectations::match(Checksum actualChecksum) const { | 171 bool Expectations::match(GmResultDigest actualGmResultDigest) const { |
| 135 for (int i=0; i < this->fAllowedBitmapChecksums.count(); i++) { | 172 for (int i=0; i < this->fAllowedResultDigests.count(); i++) { |
| 136 Checksum allowedChecksum = this->fAllowedBitmapChecksums[i]; | 173 GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i]; |
| 137 if (allowedChecksum == actualChecksum) { | 174 if (allowedResultDigest.equals(actualGmResultDigest)) { |
| 138 return true; | 175 return true; |
| 139 } | 176 } |
| 140 } | 177 } |
| 141 return false; | 178 return false; |
| 142 } | 179 } |
| 143 | 180 |
| 144 Json::Value Expectations::asJsonValue() const { | 181 Json::Value Expectations::asJsonValue() const { |
| 145 Json::Value allowedChecksumArray; | 182 Json::Value allowedDigestArray; |
| 146 if (!this->fAllowedBitmapChecksums.empty()) { | 183 if (!this->fAllowedResultDigests.empty()) { |
| 147 for (int i=0; i < this->fAllowedBitmapChecksums.count(); i++) { | 184 for (int i=0; i < this->fAllowedResultDigests.count(); i++) { |
| 148 Checksum allowedChecksum = this->fAllowedBitmapChecksums[i]; | 185 allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonT ypeValuePair()); |
| 149 allowedChecksumArray.append(Json::UInt64(allowedChecksum)); | |
| 150 } | 186 } |
| 151 } | 187 } |
| 152 | 188 |
| 153 Json::Value jsonValue; | 189 Json::Value jsonExpectations; |
| 154 jsonValue[kJsonKey_ExpectedResults_AllowedBitmapHashes] = allowedChecksu mArray; | 190 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDiges tArray; |
| 155 jsonValue[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure( ); | 191 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignore Failure(); |
| 156 return jsonValue; | 192 return jsonExpectations; |
| 157 } | 193 } |
| 158 | 194 |
| 159 | 195 |
| 160 // IndividualImageExpectationsSource class... | 196 // IndividualImageExpectationsSource class... |
| 161 | 197 |
| 162 Expectations IndividualImageExpectationsSource::get(const char *testName) { | 198 Expectations IndividualImageExpectationsSource::get(const char *testName) { |
| 163 SkString path = SkPathJoin(fRootDir.c_str(), testName); | 199 SkString path = SkPathJoin(fRootDir.c_str(), testName); |
| 164 SkBitmap referenceBitmap; | 200 SkBitmap referenceBitmap; |
| 165 bool decodedReferenceBitmap = | 201 bool decodedReferenceBitmap = |
| 166 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, | 202 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 Json::Reader reader; | 260 Json::Reader reader; |
| 225 if (!reader.parse(bytes, bytes+size, *jsonRoot)) { | 261 if (!reader.parse(bytes, bytes+size, *jsonRoot)) { |
| 226 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath); | 262 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath); |
| 227 DEBUGFAIL_SEE_STDERR; | 263 DEBUGFAIL_SEE_STDERR; |
| 228 return false; | 264 return false; |
| 229 } | 265 } |
| 230 return true; | 266 return true; |
| 231 } | 267 } |
| 232 | 268 |
| 233 } | 269 } |
| OLD | NEW |