blob: 2f921201fa6e0f8ffd59af5472eafba9f70ea409 [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.com9d331542013-05-28 15:25:38 +000015// gm_json.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
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
112
epoger@google.com76c913d2013-04-26 15:06:44 +0000113 // Expectations class...
114
115 Expectations::Expectations(bool ignoreFailure) {
116 fIgnoreFailure = ignoreFailure;
117 }
118
119 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
120 fBitmap = bitmap;
121 fIgnoreFailure = ignoreFailure;
epoger@google.comd4993ff2013-05-24 14:33:28 +0000122 fAllowedResultDigests.push_back(GmResultDigest(bitmap));
epoger@google.com76c913d2013-04-26 15:06:44 +0000123 }
124
125 Expectations::Expectations(Json::Value jsonElement) {
126 if (jsonElement.empty()) {
127 fIgnoreFailure = kDefaultIgnoreFailure;
128 } else {
129 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
130 if (ignoreFailure.isNull()) {
131 fIgnoreFailure = kDefaultIgnoreFailure;
132 } else if (!ignoreFailure.isBool()) {
133 gm_fprintf(stderr, "found non-boolean json value"
134 " for key '%s' in element '%s'\n",
135 kJsonKey_ExpectedResults_IgnoreFailure,
136 jsonElement.toStyledString().c_str());
137 DEBUGFAIL_SEE_STDERR;
138 fIgnoreFailure = kDefaultIgnoreFailure;
139 } else {
140 fIgnoreFailure = ignoreFailure.asBool();
141 }
142
epoger@google.comd4993ff2013-05-24 14:33:28 +0000143 Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests];
144 if (allowedDigests.isNull()) {
145 // ok, we'll just assume there aren't any AllowedDigests to compare against
146 } else if (!allowedDigests.isArray()) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000147 gm_fprintf(stderr, "found non-array json value"
148 " for key '%s' in element '%s'\n",
epoger@google.comd4993ff2013-05-24 14:33:28 +0000149 kJsonKey_ExpectedResults_AllowedDigests,
epoger@google.com76c913d2013-04-26 15:06:44 +0000150 jsonElement.toStyledString().c_str());
151 DEBUGFAIL_SEE_STDERR;
152 } else {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000153 for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
154 fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
epoger@google.com76c913d2013-04-26 15:06:44 +0000155 }
156 }
157 }
158 }
159
epoger@google.comd4993ff2013-05-24 14:33:28 +0000160 bool Expectations::match(GmResultDigest actualGmResultDigest) const {
161 for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
162 GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i];
163 if (allowedResultDigest.equals(actualGmResultDigest)) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000164 return true;
165 }
166 }
167 return false;
168 }
169
170 Json::Value Expectations::asJsonValue() const {
epoger@google.comd4993ff2013-05-24 14:33:28 +0000171 Json::Value allowedDigestArray;
172 if (!this->fAllowedResultDigests.empty()) {
173 for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
174 allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair());
epoger@google.com76c913d2013-04-26 15:06:44 +0000175 }
176 }
177
epoger@google.comd4993ff2013-05-24 14:33:28 +0000178 Json::Value jsonExpectations;
179 jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
180 jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
181 return jsonExpectations;
epoger@google.com76c913d2013-04-26 15:06:44 +0000182 }
183
184
185 // IndividualImageExpectationsSource class...
186
187 Expectations IndividualImageExpectationsSource::get(const char *testName) {
scroggo@google.comccd7afb2013-05-28 16:45:07 +0000188 SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName);
epoger@google.com76c913d2013-04-26 15:06:44 +0000189 SkBitmap referenceBitmap;
190 bool decodedReferenceBitmap =
191 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
192 SkBitmap::kARGB_8888_Config,
193 SkImageDecoder::kDecodePixels_Mode,
194 NULL);
195 if (decodedReferenceBitmap) {
196 return Expectations(referenceBitmap);
197 } else {
198 return Expectations();
199 }
200 }
201
202
203 // JsonExpectationsSource class...
204
205 JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000206 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000207 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
208 }
209
210 Expectations JsonExpectationsSource::get(const char *testName) {
211 return Expectations(fJsonExpectedResults[testName]);
212 }
213
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000214 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000215 if (0 == maxBytes) {
216 return SkData::NewEmpty();
217 }
218 char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes));
219 char* bufPtr = bufStart;
220 size_t bytesRemaining = maxBytes;
221 while (bytesRemaining > 0) {
222 size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining);
223 if (0 == bytesReadThisTime) {
224 break;
225 }
226 bytesRemaining -= bytesReadThisTime;
227 bufPtr += bytesReadThisTime;
228 }
229 return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining);
230 }
231
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000232 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000233 SkFILEStream inFile(jsonPath);
234 if (!inFile.isValid()) {
235 gm_fprintf(stderr, "unable to read JSON file %s\n", jsonPath);
236 DEBUGFAIL_SEE_STDERR;
237 return false;
238 }
239
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000240 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000241 if (NULL == dataRef.get()) {
242 gm_fprintf(stderr, "error reading JSON file %s\n", jsonPath);
243 DEBUGFAIL_SEE_STDERR;
244 return false;
245 }
246
247 const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
248 size_t size = dataRef.get()->size();
249 Json::Reader reader;
250 if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
251 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath);
252 DEBUGFAIL_SEE_STDERR;
253 return false;
254 }
255 return true;
256 }
257
258}