blob: 828e9f8b97f84a7e9dc656aba7e4f1bb54ddd2f8 [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);
commit-bot@chromium.orgaadb4d92013-10-09 15:09:42 +000037#ifdef SK_BUILD_FOR_WIN
38 if (stderr == stream || stdout == stream) {
39 fflush(stream);
40 }
41#endif
scroggo@google.com6843bdb2013-05-08 19:14:23 +000042 va_end(args);
43 }
44
epoger@google.com76c913d2013-04-26 15:06:44 +000045 Json::Value CreateJsonTree(Json::Value expectedResults,
46 Json::Value actualResultsFailed,
47 Json::Value actualResultsFailureIgnored,
48 Json::Value actualResultsNoComparison,
49 Json::Value actualResultsSucceeded) {
50 Json::Value actualResults;
51 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
52 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
53 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
54 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
55 Json::Value root;
56 root[kJsonKey_ActualResults] = actualResults;
57 root[kJsonKey_ExpectedResults] = expectedResults;
58 return root;
59 }
60
61
epoger@google.comd4993ff2013-05-24 14:33:28 +000062 // GmResultDigest class...
63
64 GmResultDigest::GmResultDigest(const SkBitmap &bitmap) {
65 fIsValid = SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest);
66 }
67
68 GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) {
69 fIsValid = false;
70 if (!jsonTypeValuePair.isArray()) {
71 gm_fprintf(stderr, "found non-array json value when parsing GmResultDigest: %s\n",
72 jsonTypeValuePair.toStyledString().c_str());
73 DEBUGFAIL_SEE_STDERR;
74 } else if (2 != jsonTypeValuePair.size()) {
75 gm_fprintf(stderr, "found json array with wrong size when parsing GmResultDigest: %s\n",
76 jsonTypeValuePair.toStyledString().c_str());
77 DEBUGFAIL_SEE_STDERR;
78 } else {
79 // TODO(epoger): The current implementation assumes that the
80 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
81 Json::Value jsonHashValue = jsonTypeValuePair[1];
82 if (!jsonHashValue.isIntegral()) {
83 gm_fprintf(stderr,
84 "found non-integer jsonHashValue when parsing GmResultDigest: %s\n",
85 jsonTypeValuePair.toStyledString().c_str());
86 DEBUGFAIL_SEE_STDERR;
87 } else {
88 fHashDigest = jsonHashValue.asUInt64();
89 fIsValid = true;
90 }
91 }
92 }
93
94 bool GmResultDigest::isValid() const {
95 return fIsValid;
96 }
97
98 bool GmResultDigest::equals(const GmResultDigest &other) const {
99 // TODO(epoger): The current implementation assumes that this
100 // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5
101 return (this->fIsValid && other.fIsValid && (this->fHashDigest == other.fHashDigest));
102 }
103
104 Json::Value GmResultDigest::asJsonTypeValuePair() const {
105 // TODO(epoger): The current implementation assumes that the
106 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
107 Json::Value jsonTypeValuePair;
108 if (fIsValid) {
109 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
110 jsonTypeValuePair.append(Json::UInt64(fHashDigest));
111 } else {
112 jsonTypeValuePair.append(Json::Value("INVALID"));
113 }
114 return jsonTypeValuePair;
115 }
116
epoger@google.com6f7f14d2013-06-19 18:28:31 +0000117 SkString GmResultDigest::getHashType() const {
118 // TODO(epoger): The current implementation assumes that the
119 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
120 return SkString(kJsonKey_Hashtype_Bitmap_64bitMD5);
121 }
122
123 SkString GmResultDigest::getDigestValue() const {
124 // TODO(epoger): The current implementation assumes that the
125 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
126 SkString retval;
127 retval.appendU64(fHashDigest);
128 return retval;
129 }
130
epoger@google.comd4993ff2013-05-24 14:33:28 +0000131
epoger@google.com76c913d2013-04-26 15:06:44 +0000132 // Expectations class...
133
134 Expectations::Expectations(bool ignoreFailure) {
135 fIgnoreFailure = ignoreFailure;
136 }
137
138 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
139 fBitmap = bitmap;
140 fIgnoreFailure = ignoreFailure;
epoger@google.comd4993ff2013-05-24 14:33:28 +0000141 fAllowedResultDigests.push_back(GmResultDigest(bitmap));
epoger@google.com76c913d2013-04-26 15:06:44 +0000142 }
143
144 Expectations::Expectations(Json::Value jsonElement) {
145 if (jsonElement.empty()) {
146 fIgnoreFailure = kDefaultIgnoreFailure;
147 } else {
148 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
149 if (ignoreFailure.isNull()) {
150 fIgnoreFailure = kDefaultIgnoreFailure;
151 } else if (!ignoreFailure.isBool()) {
152 gm_fprintf(stderr, "found non-boolean json value"
153 " for key '%s' in element '%s'\n",
154 kJsonKey_ExpectedResults_IgnoreFailure,
155 jsonElement.toStyledString().c_str());
156 DEBUGFAIL_SEE_STDERR;
157 fIgnoreFailure = kDefaultIgnoreFailure;
158 } else {
159 fIgnoreFailure = ignoreFailure.asBool();
160 }
161
epoger@google.comd4993ff2013-05-24 14:33:28 +0000162 Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests];
163 if (allowedDigests.isNull()) {
164 // ok, we'll just assume there aren't any AllowedDigests to compare against
165 } else if (!allowedDigests.isArray()) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000166 gm_fprintf(stderr, "found non-array json value"
167 " for key '%s' in element '%s'\n",
epoger@google.comd4993ff2013-05-24 14:33:28 +0000168 kJsonKey_ExpectedResults_AllowedDigests,
epoger@google.com76c913d2013-04-26 15:06:44 +0000169 jsonElement.toStyledString().c_str());
170 DEBUGFAIL_SEE_STDERR;
171 } else {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000172 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
173 fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
epoger@google.com76c913d2013-04-26 15:06:44 +0000174 }
175 }
176 }
177 }
178
epoger@google.comd4993ff2013-05-24 14:33:28 +0000179 bool Expectations::match(GmResultDigest actualGmResultDigest) const {
180 for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
181 GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i];
182 if (allowedResultDigest.equals(actualGmResultDigest)) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000183 return true;
184 }
185 }
186 return false;
187 }
188
189 Json::Value Expectations::asJsonValue() const {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000190 Json::Value allowedDigestArray;
191 if (!this->fAllowedResultDigests.empty()) {
192 for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
193 allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair());
epoger@google.com76c913d2013-04-26 15:06:44 +0000194 }
195 }
196
epoger@google.comd4993ff2013-05-24 14:33:28 +0000197 Json::Value jsonExpectations;
198 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
199 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
200 return jsonExpectations;
epoger@google.com76c913d2013-04-26 15:06:44 +0000201 }
202
203
204 // IndividualImageExpectationsSource class...
205
commit-bot@chromium.org1d5bbb22013-10-14 14:15:28 +0000206 Expectations IndividualImageExpectationsSource::get(const char *testName) const {
scroggo@google.comccd7afb2013-05-28 16:45:07 +0000207 SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName);
epoger@google.com76c913d2013-04-26 15:06:44 +0000208 SkBitmap referenceBitmap;
209 bool decodedReferenceBitmap =
210 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
211 SkBitmap::kARGB_8888_Config,
212 SkImageDecoder::kDecodePixels_Mode,
213 NULL);
214 if (decodedReferenceBitmap) {
215 return Expectations(referenceBitmap);
216 } else {
217 return Expectations();
218 }
219 }
220
221
222 // JsonExpectationsSource class...
223
224 JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000225 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000226 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
227 }
228
commit-bot@chromium.org1d5bbb22013-10-14 14:15:28 +0000229 Expectations JsonExpectationsSource::get(const char *testName) const {
epoger@google.com76c913d2013-04-26 15:06:44 +0000230 return Expectations(fJsonExpectedResults[testName]);
231 }
232
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000233 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000234 if (0 == maxBytes) {
235 return SkData::NewEmpty();
236 }
237 char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes));
238 char* bufPtr = bufStart;
239 size_t bytesRemaining = maxBytes;
240 while (bytesRemaining > 0) {
241 size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining);
242 if (0 == bytesReadThisTime) {
243 break;
244 }
245 bytesRemaining -= bytesReadThisTime;
246 bufPtr += bytesReadThisTime;
247 }
248 return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining);
249 }
250
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000251 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000252 SkFILEStream inFile(jsonPath);
253 if (!inFile.isValid()) {
254 gm_fprintf(stderr, "unable to read JSON file %s\n", jsonPath);
255 DEBUGFAIL_SEE_STDERR;
256 return false;
257 }
258
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000259 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000260 if (NULL == dataRef.get()) {
261 gm_fprintf(stderr, "error reading JSON file %s\n", jsonPath);
262 DEBUGFAIL_SEE_STDERR;
263 return false;
264 }
265
266 const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
267 size_t size = dataRef.get()->size();
268 Json::Reader reader;
269 if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
270 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath);
271 DEBUGFAIL_SEE_STDERR;
272 return false;
273 }
274 return true;
275 }
276
277}