blob: c7bab920442605a2b8593b7405ed6863b421d6b8 [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 {
scroggo@google.com6843bdb2013-05-08 19:14:23 +000030 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);
commit-bot@chromium.orgaadb4d92013-10-09 15:09:42 +000035#ifdef SK_BUILD_FOR_WIN
36 if (stderr == stream || stdout == stream) {
37 fflush(stream);
38 }
39#endif
scroggo@google.com6843bdb2013-05-08 19:14:23 +000040 va_end(args);
41 }
42
epoger@google.com76c913d2013-04-26 15:06:44 +000043 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
epoger@google.comd4993ff2013-05-24 14:33:28 +000060 // 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
epoger@google.com6f7f14d2013-06-19 18:28:31 +0000115 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
epoger@google.comd4993ff2013-05-24 14:33:28 +0000129
epoger@google.com76c913d2013-04-26 15:06:44 +0000130 // 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;
epoger@google.comd4993ff2013-05-24 14:33:28 +0000139 fAllowedResultDigests.push_back(GmResultDigest(bitmap));
epoger@google.com76c913d2013-04-26 15:06:44 +0000140 }
141
scroggo@google.com5187c432013-10-22 00:42:46 +0000142 Expectations::Expectations(const BitmapAndDigest& bitmapAndDigest) {
143 fBitmap = bitmapAndDigest.fBitmap;
144 fIgnoreFailure = false;
145 fAllowedResultDigests.push_back(bitmapAndDigest.fDigest);
146 }
147
epoger@google.com76c913d2013-04-26 15:06:44 +0000148 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
epoger@google.comd4993ff2013-05-24 14:33:28 +0000166 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()) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000170 gm_fprintf(stderr, "found non-array json value"
171 " for key '%s' in element '%s'\n",
epoger@google.comd4993ff2013-05-24 14:33:28 +0000172 kJsonKey_ExpectedResults_AllowedDigests,
epoger@google.com76c913d2013-04-26 15:06:44 +0000173 jsonElement.toStyledString().c_str());
174 DEBUGFAIL_SEE_STDERR;
175 } else {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000176 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
177 fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
epoger@google.com76c913d2013-04-26 15:06:44 +0000178 }
179 }
180 }
181 }
182
epoger@google.comd4993ff2013-05-24 14:33:28 +0000183 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)) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000187 return true;
188 }
189 }
190 return false;
191 }
192
193 Json::Value Expectations::asJsonValue() const {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000194 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());
epoger@google.com76c913d2013-04-26 15:06:44 +0000198 }
199 }
200
epoger@google.comd4993ff2013-05-24 14:33:28 +0000201 Json::Value jsonExpectations;
202 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
203 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
204 return jsonExpectations;
epoger@google.com76c913d2013-04-26 15:06:44 +0000205 }
206
207
208 // IndividualImageExpectationsSource class...
209
commit-bot@chromium.org1d5bbb22013-10-14 14:15:28 +0000210 Expectations IndividualImageExpectationsSource::get(const char *testName) const {
scroggo@google.comccd7afb2013-05-28 16:45:07 +0000211 SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName);
epoger@google.com76c913d2013-04-26 15:06:44 +0000212 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) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000229 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000230 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
231 }
232
commit-bot@chromium.org1d5bbb22013-10-14 14:15:28 +0000233 Expectations JsonExpectationsSource::get(const char *testName) const {
epoger@google.com76c913d2013-04-26 15:06:44 +0000234 return Expectations(fJsonExpectedResults[testName]);
235 }
236
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000237 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000238 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
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000255 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000256 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
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000263 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000264 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}