blob: 7d6ccf64642817f173cef29cc7ec381c412fcea1 [file] [log] [blame]
epoger@google.com76c913d2013-04-26 15:06:44 +00001/*
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
epoger@google.coma783f2b2013-07-08 17:51:58 +000014// 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!
epoger@google.com76c913d2013-04-26 15:06:44 +000016const 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";
epoger@google.com76c913d2013-04-26 15:06:44 +000021const static char kJsonKey_ExpectedResults[] = "expected-results";
epoger@google.comd4993ff2013-05-24 14:33:28 +000022const 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
epoger@google.com76c913d2013-04-26 15:06:44 +000028
29namespace skiagm {
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +000030 SK_DEFINE_INST_COUNT(ExpectationsSource)
epoger@google.com76c913d2013-04-26 15:06:44 +000031
scroggo@google.com6843bdb2013-05-08 19:14:23 +000032 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
epoger@google.com76c913d2013-04-26 15:06:44 +000040 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
epoger@google.comd4993ff2013-05-24 14:33:28 +000057 // 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
epoger@google.com6f7f14d2013-06-19 18:28:31 +0000112 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
epoger@google.comd4993ff2013-05-24 14:33:28 +0000126
epoger@google.com76c913d2013-04-26 15:06:44 +0000127 // 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;
epoger@google.comd4993ff2013-05-24 14:33:28 +0000136 fAllowedResultDigests.push_back(GmResultDigest(bitmap));
epoger@google.com76c913d2013-04-26 15:06:44 +0000137 }
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
epoger@google.comd4993ff2013-05-24 14:33:28 +0000157 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()) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000161 gm_fprintf(stderr, "found non-array json value"
162 " for key '%s' in element '%s'\n",
epoger@google.comd4993ff2013-05-24 14:33:28 +0000163 kJsonKey_ExpectedResults_AllowedDigests,
epoger@google.com76c913d2013-04-26 15:06:44 +0000164 jsonElement.toStyledString().c_str());
165 DEBUGFAIL_SEE_STDERR;
166 } else {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000167 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
168 fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
epoger@google.com76c913d2013-04-26 15:06:44 +0000169 }
170 }
171 }
172 }
173
epoger@google.comd4993ff2013-05-24 14:33:28 +0000174 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)) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000178 return true;
179 }
180 }
181 return false;
182 }
183
184 Json::Value Expectations::asJsonValue() const {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000185 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());
epoger@google.com76c913d2013-04-26 15:06:44 +0000189 }
190 }
191
epoger@google.comd4993ff2013-05-24 14:33:28 +0000192 Json::Value jsonExpectations;
193 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
194 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
195 return jsonExpectations;
epoger@google.com76c913d2013-04-26 15:06:44 +0000196 }
197
198
199 // IndividualImageExpectationsSource class...
200
201 Expectations IndividualImageExpectationsSource::get(const char *testName) {
scroggo@google.comccd7afb2013-05-28 16:45:07 +0000202 SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName);
epoger@google.com76c913d2013-04-26 15:06:44 +0000203 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) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000220 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000221 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
222 }
223
224 Expectations JsonExpectationsSource::get(const char *testName) {
225 return Expectations(fJsonExpectedResults[testName]);
226 }
227
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000228 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000229 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
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000246 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000247 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
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000254 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000255 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}