blob: 2c6dede25aba40925b1ef2949f7f9ee6bd8a6346 [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
14const static char kJsonKey_ActualResults[] = "actual-results";
15const static char kJsonKey_ActualResults_Failed[] = "failed";
16const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
17const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
18const static char kJsonKey_ActualResults_Succeeded[] = "succeeded";
epoger@google.com366a7702013-05-07 15:51:54 +000019const static char kJsonKey_ActualResults_AnyStatus_BitmapHash[] = "bitmap-64bitMD5";
epoger@google.com76c913d2013-04-26 15:06:44 +000020
21const static char kJsonKey_ExpectedResults[] = "expected-results";
epoger@google.com366a7702013-05-07 15:51:54 +000022const static char kJsonKey_ExpectedResults_AllowedBitmapHashes[] = "allowed-bitmap-64bitMD5s";
epoger@google.com76c913d2013-04-26 15:06:44 +000023const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
24
25namespace skiagm {
26
scroggo@google.com6843bdb2013-05-08 19:14:23 +000027 void gm_fprintf(FILE *stream, const char format[], ...) {
28 va_list args;
29 va_start(args, format);
30 fprintf(stream, "GM: ");
31 vfprintf(stream, format, args);
32 va_end(args);
33 }
34
epoger@google.comce057fe2013-05-14 15:17:46 +000035 SkString SkPathJoin(const char *rootPath, const char *relativePath) {
36 SkString result(rootPath);
37 if (!result.endsWith(SkPATH_SEPARATOR)) {
38 result.appendUnichar(SkPATH_SEPARATOR);
39 }
40 result.append(relativePath);
41 return result;
42 }
43
scroggo@google.com6843bdb2013-05-08 19:14:23 +000044 SkString make_filename(const char path[],
45 const char renderModeDescriptor[],
46 const char *name,
47 const char suffix[]) {
epoger@google.comce057fe2013-05-14 15:17:46 +000048 SkString filename(name);
49 filename.append(renderModeDescriptor);
50 filename.appendUnichar('.');
51 filename.append(suffix);
52 return SkPathJoin(path, filename.c_str());
scroggo@google.com6843bdb2013-05-08 19:14:23 +000053 }
54
epoger@google.com76c913d2013-04-26 15:06:44 +000055 // TODO(epoger): This currently assumes that the result SkHashDigest was
epoger@google.com366a7702013-05-07 15:51:54 +000056 // generated as an SkHashDigest of an SkBitmap. We'll need to allow for other
57 // hash types to cover non-bitmaps.
epoger@google.com76c913d2013-04-26 15:06:44 +000058 Json::Value ActualResultAsJsonValue(const SkHashDigest& result) {
59 Json::Value jsonValue;
epoger@google.com366a7702013-05-07 15:51:54 +000060 jsonValue[kJsonKey_ActualResults_AnyStatus_BitmapHash] = asJsonValue(result);
epoger@google.com76c913d2013-04-26 15:06:44 +000061 return jsonValue;
62 }
63
64 Json::Value CreateJsonTree(Json::Value expectedResults,
65 Json::Value actualResultsFailed,
66 Json::Value actualResultsFailureIgnored,
67 Json::Value actualResultsNoComparison,
68 Json::Value actualResultsSucceeded) {
69 Json::Value actualResults;
70 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
71 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
72 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
73 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
74 Json::Value root;
75 root[kJsonKey_ActualResults] = actualResults;
76 root[kJsonKey_ExpectedResults] = expectedResults;
77 return root;
78 }
79
80
81 // Expectations class...
82
83 Expectations::Expectations(bool ignoreFailure) {
84 fIgnoreFailure = ignoreFailure;
85 }
86
87 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
88 fBitmap = bitmap;
89 fIgnoreFailure = ignoreFailure;
90 SkHashDigest digest;
91 // TODO(epoger): Better handling for error returned by ComputeDigest()?
92 // For now, we just report a digest of 0 in error cases, like before.
93 if (!SkBitmapHasher::ComputeDigest(bitmap, &digest)) {
94 digest = 0;
95 }
epoger@google.com366a7702013-05-07 15:51:54 +000096 fAllowedBitmapChecksums.push_back() = digest;
epoger@google.com76c913d2013-04-26 15:06:44 +000097 }
98
99 Expectations::Expectations(Json::Value jsonElement) {
100 if (jsonElement.empty()) {
101 fIgnoreFailure = kDefaultIgnoreFailure;
102 } else {
103 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
104 if (ignoreFailure.isNull()) {
105 fIgnoreFailure = kDefaultIgnoreFailure;
106 } else if (!ignoreFailure.isBool()) {
107 gm_fprintf(stderr, "found non-boolean json value"
108 " for key '%s' in element '%s'\n",
109 kJsonKey_ExpectedResults_IgnoreFailure,
110 jsonElement.toStyledString().c_str());
111 DEBUGFAIL_SEE_STDERR;
112 fIgnoreFailure = kDefaultIgnoreFailure;
113 } else {
114 fIgnoreFailure = ignoreFailure.asBool();
115 }
116
117 Json::Value allowedChecksums =
epoger@google.com366a7702013-05-07 15:51:54 +0000118 jsonElement[kJsonKey_ExpectedResults_AllowedBitmapHashes];
epoger@google.com76c913d2013-04-26 15:06:44 +0000119 if (allowedChecksums.isNull()) {
120 // ok, we'll just assume there aren't any expected checksums to compare against
121 } else if (!allowedChecksums.isArray()) {
122 gm_fprintf(stderr, "found non-array json value"
123 " for key '%s' in element '%s'\n",
epoger@google.com366a7702013-05-07 15:51:54 +0000124 kJsonKey_ExpectedResults_AllowedBitmapHashes,
epoger@google.com76c913d2013-04-26 15:06:44 +0000125 jsonElement.toStyledString().c_str());
126 DEBUGFAIL_SEE_STDERR;
127 } else {
128 for (Json::ArrayIndex i=0; i<allowedChecksums.size(); i++) {
129 Json::Value checksumElement = allowedChecksums[i];
130 if (!checksumElement.isIntegral()) {
131 gm_fprintf(stderr, "found non-integer checksum"
132 " in json element '%s'\n",
133 jsonElement.toStyledString().c_str());
134 DEBUGFAIL_SEE_STDERR;
135 } else {
epoger@google.com366a7702013-05-07 15:51:54 +0000136 fAllowedBitmapChecksums.push_back() = asChecksum(checksumElement);
epoger@google.com76c913d2013-04-26 15:06:44 +0000137 }
138 }
139 }
140 }
141 }
142
143 bool Expectations::match(Checksum actualChecksum) const {
epoger@google.com366a7702013-05-07 15:51:54 +0000144 for (int i=0; i < this->fAllowedBitmapChecksums.count(); i++) {
145 Checksum allowedChecksum = this->fAllowedBitmapChecksums[i];
epoger@google.com76c913d2013-04-26 15:06:44 +0000146 if (allowedChecksum == actualChecksum) {
147 return true;
148 }
149 }
150 return false;
151 }
152
153 Json::Value Expectations::asJsonValue() const {
154 Json::Value allowedChecksumArray;
epoger@google.com366a7702013-05-07 15:51:54 +0000155 if (!this->fAllowedBitmapChecksums.empty()) {
156 for (int i=0; i < this->fAllowedBitmapChecksums.count(); i++) {
157 Checksum allowedChecksum = this->fAllowedBitmapChecksums[i];
epoger@google.com76c913d2013-04-26 15:06:44 +0000158 allowedChecksumArray.append(Json::UInt64(allowedChecksum));
159 }
160 }
161
162 Json::Value jsonValue;
epoger@google.com366a7702013-05-07 15:51:54 +0000163 jsonValue[kJsonKey_ExpectedResults_AllowedBitmapHashes] = allowedChecksumArray;
epoger@google.com76c913d2013-04-26 15:06:44 +0000164 jsonValue[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
165 return jsonValue;
166 }
167
168
169 // IndividualImageExpectationsSource class...
170
171 Expectations IndividualImageExpectationsSource::get(const char *testName) {
epoger@google.comce057fe2013-05-14 15:17:46 +0000172 SkString path = SkPathJoin(fRootDir.c_str(), testName);
epoger@google.com76c913d2013-04-26 15:06:44 +0000173 SkBitmap referenceBitmap;
174 bool decodedReferenceBitmap =
175 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
176 SkBitmap::kARGB_8888_Config,
177 SkImageDecoder::kDecodePixels_Mode,
178 NULL);
179 if (decodedReferenceBitmap) {
180 return Expectations(referenceBitmap);
181 } else {
182 return Expectations();
183 }
184 }
185
186
187 // JsonExpectationsSource class...
188
189 JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000190 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000191 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
192 }
193
194 Expectations JsonExpectationsSource::get(const char *testName) {
195 return Expectations(fJsonExpectedResults[testName]);
196 }
197
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000198 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000199 if (0 == maxBytes) {
200 return SkData::NewEmpty();
201 }
202 char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes));
203 char* bufPtr = bufStart;
204 size_t bytesRemaining = maxBytes;
205 while (bytesRemaining > 0) {
206 size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining);
207 if (0 == bytesReadThisTime) {
208 break;
209 }
210 bytesRemaining -= bytesReadThisTime;
211 bufPtr += bytesReadThisTime;
212 }
213 return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining);
214 }
215
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000216 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000217 SkFILEStream inFile(jsonPath);
218 if (!inFile.isValid()) {
219 gm_fprintf(stderr, "unable to read JSON file %s\n", jsonPath);
220 DEBUGFAIL_SEE_STDERR;
221 return false;
222 }
223
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000224 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000225 if (NULL == dataRef.get()) {
226 gm_fprintf(stderr, "error reading JSON file %s\n", jsonPath);
227 DEBUGFAIL_SEE_STDERR;
228 return false;
229 }
230
231 const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
232 size_t size = dataRef.get()->size();
233 Json::Reader reader;
234 if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
235 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath);
236 DEBUGFAIL_SEE_STDERR;
237 return false;
238 }
239 return true;
240 }
241
242}