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