blob: 8138af78907e3fd34286d69f9f524b0d0fa1cf16 [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.com26515ba2013-05-14 18:58:12 +000014// These constants must be kept in sync with the JSONKEY_ constants in
epoger@google.coma55e48d2013-05-21 16:06:40 +000015// display_json_results.py !
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 +000021
22const static char kJsonKey_ExpectedResults[] = "expected-results";
epoger@google.comd4993ff2013-05-24 14:33:28 +000023const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
24const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
25
26// Types of result hashes we support in the JSON file.
27const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
28
epoger@google.com76c913d2013-04-26 15:06:44 +000029
30namespace skiagm {
31
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
scroggo@google.comdbbcaa82013-05-24 18:28:57 +000040 SkString SkPathJoin(const char *rootPath, const char *relativePath) {
41 SkString result(rootPath);
42 if (!result.endsWith(SkPATH_SEPARATOR)) {
43 result.appendUnichar(SkPATH_SEPARATOR);
44 }
45 result.append(relativePath);
46 return result;
47 }
48
epoger@google.com76c913d2013-04-26 15:06:44 +000049 Json::Value CreateJsonTree(Json::Value expectedResults,
50 Json::Value actualResultsFailed,
51 Json::Value actualResultsFailureIgnored,
52 Json::Value actualResultsNoComparison,
53 Json::Value actualResultsSucceeded) {
54 Json::Value actualResults;
55 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
56 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
57 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
58 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
59 Json::Value root;
60 root[kJsonKey_ActualResults] = actualResults;
61 root[kJsonKey_ExpectedResults] = expectedResults;
62 return root;
63 }
64
65
epoger@google.comd4993ff2013-05-24 14:33:28 +000066 // GmResultDigest class...
67
68 GmResultDigest::GmResultDigest(const SkBitmap &bitmap) {
69 fIsValid = SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest);
70 }
71
72 GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) {
73 fIsValid = false;
74 if (!jsonTypeValuePair.isArray()) {
75 gm_fprintf(stderr, "found non-array json value when parsing GmResultDigest: %s\n",
76 jsonTypeValuePair.toStyledString().c_str());
77 DEBUGFAIL_SEE_STDERR;
78 } else if (2 != jsonTypeValuePair.size()) {
79 gm_fprintf(stderr, "found json array with wrong size when parsing GmResultDigest: %s\n",
80 jsonTypeValuePair.toStyledString().c_str());
81 DEBUGFAIL_SEE_STDERR;
82 } else {
83 // TODO(epoger): The current implementation assumes that the
84 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
85 Json::Value jsonHashValue = jsonTypeValuePair[1];
86 if (!jsonHashValue.isIntegral()) {
87 gm_fprintf(stderr,
88 "found non-integer jsonHashValue when parsing GmResultDigest: %s\n",
89 jsonTypeValuePair.toStyledString().c_str());
90 DEBUGFAIL_SEE_STDERR;
91 } else {
92 fHashDigest = jsonHashValue.asUInt64();
93 fIsValid = true;
94 }
95 }
96 }
97
98 bool GmResultDigest::isValid() const {
99 return fIsValid;
100 }
101
102 bool GmResultDigest::equals(const GmResultDigest &other) const {
103 // TODO(epoger): The current implementation assumes that this
104 // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5
105 return (this->fIsValid && other.fIsValid && (this->fHashDigest == other.fHashDigest));
106 }
107
108 Json::Value GmResultDigest::asJsonTypeValuePair() const {
109 // TODO(epoger): The current implementation assumes that the
110 // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
111 Json::Value jsonTypeValuePair;
112 if (fIsValid) {
113 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
114 jsonTypeValuePair.append(Json::UInt64(fHashDigest));
115 } else {
116 jsonTypeValuePair.append(Json::Value("INVALID"));
117 }
118 return jsonTypeValuePair;
119 }
120
121
epoger@google.com76c913d2013-04-26 15:06:44 +0000122 // Expectations class...
123
124 Expectations::Expectations(bool ignoreFailure) {
125 fIgnoreFailure = ignoreFailure;
126 }
127
128 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
129 fBitmap = bitmap;
130 fIgnoreFailure = ignoreFailure;
epoger@google.comd4993ff2013-05-24 14:33:28 +0000131 fAllowedResultDigests.push_back(GmResultDigest(bitmap));
epoger@google.com76c913d2013-04-26 15:06:44 +0000132 }
133
134 Expectations::Expectations(Json::Value jsonElement) {
135 if (jsonElement.empty()) {
136 fIgnoreFailure = kDefaultIgnoreFailure;
137 } else {
138 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
139 if (ignoreFailure.isNull()) {
140 fIgnoreFailure = kDefaultIgnoreFailure;
141 } else if (!ignoreFailure.isBool()) {
142 gm_fprintf(stderr, "found non-boolean json value"
143 " for key '%s' in element '%s'\n",
144 kJsonKey_ExpectedResults_IgnoreFailure,
145 jsonElement.toStyledString().c_str());
146 DEBUGFAIL_SEE_STDERR;
147 fIgnoreFailure = kDefaultIgnoreFailure;
148 } else {
149 fIgnoreFailure = ignoreFailure.asBool();
150 }
151
epoger@google.comd4993ff2013-05-24 14:33:28 +0000152 Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests];
153 if (allowedDigests.isNull()) {
154 // ok, we'll just assume there aren't any AllowedDigests to compare against
155 } else if (!allowedDigests.isArray()) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000156 gm_fprintf(stderr, "found non-array json value"
157 " for key '%s' in element '%s'\n",
epoger@google.comd4993ff2013-05-24 14:33:28 +0000158 kJsonKey_ExpectedResults_AllowedDigests,
epoger@google.com76c913d2013-04-26 15:06:44 +0000159 jsonElement.toStyledString().c_str());
160 DEBUGFAIL_SEE_STDERR;
161 } else {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000162 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
163 fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
epoger@google.com76c913d2013-04-26 15:06:44 +0000164 }
165 }
166 }
167 }
168
epoger@google.comd4993ff2013-05-24 14:33:28 +0000169 bool Expectations::match(GmResultDigest actualGmResultDigest) const {
170 for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
171 GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i];
172 if (allowedResultDigest.equals(actualGmResultDigest)) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000173 return true;
174 }
175 }
176 return false;
177 }
178
179 Json::Value Expectations::asJsonValue() const {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000180 Json::Value allowedDigestArray;
181 if (!this->fAllowedResultDigests.empty()) {
182 for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
183 allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair());
epoger@google.com76c913d2013-04-26 15:06:44 +0000184 }
185 }
186
epoger@google.comd4993ff2013-05-24 14:33:28 +0000187 Json::Value jsonExpectations;
188 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
189 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
190 return jsonExpectations;
epoger@google.com76c913d2013-04-26 15:06:44 +0000191 }
192
193
194 // IndividualImageExpectationsSource class...
195
196 Expectations IndividualImageExpectationsSource::get(const char *testName) {
scroggo@google.comdbbcaa82013-05-24 18:28:57 +0000197 SkString path = SkPathJoin(fRootDir.c_str(), testName);
epoger@google.com76c913d2013-04-26 15:06:44 +0000198 SkBitmap referenceBitmap;
199 bool decodedReferenceBitmap =
200 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
201 SkBitmap::kARGB_8888_Config,
202 SkImageDecoder::kDecodePixels_Mode,
203 NULL);
204 if (decodedReferenceBitmap) {
205 return Expectations(referenceBitmap);
206 } else {
207 return Expectations();
208 }
209 }
210
211
212 // JsonExpectationsSource class...
213
214 JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000215 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000216 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
217 }
218
219 Expectations JsonExpectationsSource::get(const char *testName) {
220 return Expectations(fJsonExpectedResults[testName]);
221 }
222
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000223 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000224 if (0 == maxBytes) {
225 return SkData::NewEmpty();
226 }
227 char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes));
228 char* bufPtr = bufStart;
229 size_t bytesRemaining = maxBytes;
230 while (bytesRemaining > 0) {
231 size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining);
232 if (0 == bytesReadThisTime) {
233 break;
234 }
235 bytesRemaining -= bytesReadThisTime;
236 bufPtr += bytesReadThisTime;
237 }
238 return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining);
239 }
240
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000241 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000242 SkFILEStream inFile(jsonPath);
243 if (!inFile.isValid()) {
244 gm_fprintf(stderr, "unable to read JSON file %s\n", jsonPath);
245 DEBUGFAIL_SEE_STDERR;
246 return false;
247 }
248
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000249 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000250 if (NULL == dataRef.get()) {
251 gm_fprintf(stderr, "error reading JSON file %s\n", jsonPath);
252 DEBUGFAIL_SEE_STDERR;
253 return false;
254 }
255
256 const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
257 size_t size = dataRef.get()->size();
258 Json::Reader reader;
259 if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
260 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath);
261 DEBUGFAIL_SEE_STDERR;
262 return false;
263 }
264 return true;
265 }
266
267}