blob: d9ed7798bbc222daf0b357e0992c49ff29d53029 [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
35 SkString make_filename(const char path[],
36 const char renderModeDescriptor[],
37 const char *name,
38 const char suffix[]) {
39 SkString filename(path);
40 if (filename.endsWith(SkPATH_SEPARATOR)) {
41 filename.remove(filename.size() - 1, 1);
42 }
43 filename.appendf("%c%s%s.%s", SkPATH_SEPARATOR,
44 name, renderModeDescriptor, suffix);
45 return filename;
46 }
47
epoger@google.com76c913d2013-04-26 15:06:44 +000048 // TODO(epoger): This currently assumes that the result SkHashDigest was
epoger@google.com366a7702013-05-07 15:51:54 +000049 // generated as an SkHashDigest of an SkBitmap. We'll need to allow for other
50 // hash types to cover non-bitmaps.
epoger@google.com76c913d2013-04-26 15:06:44 +000051 Json::Value ActualResultAsJsonValue(const SkHashDigest& result) {
52 Json::Value jsonValue;
epoger@google.com366a7702013-05-07 15:51:54 +000053 jsonValue[kJsonKey_ActualResults_AnyStatus_BitmapHash] = asJsonValue(result);
epoger@google.com76c913d2013-04-26 15:06:44 +000054 return jsonValue;
55 }
56
57 Json::Value CreateJsonTree(Json::Value expectedResults,
58 Json::Value actualResultsFailed,
59 Json::Value actualResultsFailureIgnored,
60 Json::Value actualResultsNoComparison,
61 Json::Value actualResultsSucceeded) {
62 Json::Value actualResults;
63 actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
64 actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
65 actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
66 actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
67 Json::Value root;
68 root[kJsonKey_ActualResults] = actualResults;
69 root[kJsonKey_ExpectedResults] = expectedResults;
70 return root;
71 }
72
73
74 // Expectations class...
75
76 Expectations::Expectations(bool ignoreFailure) {
77 fIgnoreFailure = ignoreFailure;
78 }
79
80 Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
81 fBitmap = bitmap;
82 fIgnoreFailure = ignoreFailure;
83 SkHashDigest digest;
84 // TODO(epoger): Better handling for error returned by ComputeDigest()?
85 // For now, we just report a digest of 0 in error cases, like before.
86 if (!SkBitmapHasher::ComputeDigest(bitmap, &digest)) {
87 digest = 0;
88 }
epoger@google.com366a7702013-05-07 15:51:54 +000089 fAllowedBitmapChecksums.push_back() = digest;
epoger@google.com76c913d2013-04-26 15:06:44 +000090 }
91
92 Expectations::Expectations(Json::Value jsonElement) {
93 if (jsonElement.empty()) {
94 fIgnoreFailure = kDefaultIgnoreFailure;
95 } else {
96 Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
97 if (ignoreFailure.isNull()) {
98 fIgnoreFailure = kDefaultIgnoreFailure;
99 } else if (!ignoreFailure.isBool()) {
100 gm_fprintf(stderr, "found non-boolean json value"
101 " for key '%s' in element '%s'\n",
102 kJsonKey_ExpectedResults_IgnoreFailure,
103 jsonElement.toStyledString().c_str());
104 DEBUGFAIL_SEE_STDERR;
105 fIgnoreFailure = kDefaultIgnoreFailure;
106 } else {
107 fIgnoreFailure = ignoreFailure.asBool();
108 }
109
110 Json::Value allowedChecksums =
epoger@google.com366a7702013-05-07 15:51:54 +0000111 jsonElement[kJsonKey_ExpectedResults_AllowedBitmapHashes];
epoger@google.com76c913d2013-04-26 15:06:44 +0000112 if (allowedChecksums.isNull()) {
113 // ok, we'll just assume there aren't any expected checksums to compare against
114 } else if (!allowedChecksums.isArray()) {
115 gm_fprintf(stderr, "found non-array json value"
116 " for key '%s' in element '%s'\n",
epoger@google.com366a7702013-05-07 15:51:54 +0000117 kJsonKey_ExpectedResults_AllowedBitmapHashes,
epoger@google.com76c913d2013-04-26 15:06:44 +0000118 jsonElement.toStyledString().c_str());
119 DEBUGFAIL_SEE_STDERR;
120 } else {
121 for (Json::ArrayIndex i=0; i<allowedChecksums.size(); i++) {
122 Json::Value checksumElement = allowedChecksums[i];
123 if (!checksumElement.isIntegral()) {
124 gm_fprintf(stderr, "found non-integer checksum"
125 " in json element '%s'\n",
126 jsonElement.toStyledString().c_str());
127 DEBUGFAIL_SEE_STDERR;
128 } else {
epoger@google.com366a7702013-05-07 15:51:54 +0000129 fAllowedBitmapChecksums.push_back() = asChecksum(checksumElement);
epoger@google.com76c913d2013-04-26 15:06:44 +0000130 }
131 }
132 }
133 }
134 }
135
136 bool Expectations::match(Checksum actualChecksum) const {
epoger@google.com366a7702013-05-07 15:51:54 +0000137 for (int i=0; i < this->fAllowedBitmapChecksums.count(); i++) {
138 Checksum allowedChecksum = this->fAllowedBitmapChecksums[i];
epoger@google.com76c913d2013-04-26 15:06:44 +0000139 if (allowedChecksum == actualChecksum) {
140 return true;
141 }
142 }
143 return false;
144 }
145
146 Json::Value Expectations::asJsonValue() const {
147 Json::Value allowedChecksumArray;
epoger@google.com366a7702013-05-07 15:51:54 +0000148 if (!this->fAllowedBitmapChecksums.empty()) {
149 for (int i=0; i < this->fAllowedBitmapChecksums.count(); i++) {
150 Checksum allowedChecksum = this->fAllowedBitmapChecksums[i];
epoger@google.com76c913d2013-04-26 15:06:44 +0000151 allowedChecksumArray.append(Json::UInt64(allowedChecksum));
152 }
153 }
154
155 Json::Value jsonValue;
epoger@google.com366a7702013-05-07 15:51:54 +0000156 jsonValue[kJsonKey_ExpectedResults_AllowedBitmapHashes] = allowedChecksumArray;
epoger@google.com76c913d2013-04-26 15:06:44 +0000157 jsonValue[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
158 return jsonValue;
159 }
160
161
162 // IndividualImageExpectationsSource class...
163
164 Expectations IndividualImageExpectationsSource::get(const char *testName) {
165 SkString path = make_filename(fRootDir.c_str(), "", testName,
166 "png");
167 SkBitmap referenceBitmap;
168 bool decodedReferenceBitmap =
169 SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
170 SkBitmap::kARGB_8888_Config,
171 SkImageDecoder::kDecodePixels_Mode,
172 NULL);
173 if (decodedReferenceBitmap) {
174 return Expectations(referenceBitmap);
175 } else {
176 return Expectations();
177 }
178 }
179
180
181 // JsonExpectationsSource class...
182
183 JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000184 Parse(jsonPath, &fJsonRoot);
epoger@google.com76c913d2013-04-26 15:06:44 +0000185 fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
186 }
187
188 Expectations JsonExpectationsSource::get(const char *testName) {
189 return Expectations(fJsonExpectedResults[testName]);
190 }
191
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000192 /*static*/ SkData* JsonExpectationsSource::ReadIntoSkData(SkStream &stream, size_t maxBytes) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000193 if (0 == maxBytes) {
194 return SkData::NewEmpty();
195 }
196 char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes));
197 char* bufPtr = bufStart;
198 size_t bytesRemaining = maxBytes;
199 while (bytesRemaining > 0) {
200 size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining);
201 if (0 == bytesReadThisTime) {
202 break;
203 }
204 bytesRemaining -= bytesReadThisTime;
205 bufPtr += bytesReadThisTime;
206 }
207 return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining);
208 }
209
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000210 /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
epoger@google.com76c913d2013-04-26 15:06:44 +0000211 SkFILEStream inFile(jsonPath);
212 if (!inFile.isValid()) {
213 gm_fprintf(stderr, "unable to read JSON file %s\n", jsonPath);
214 DEBUGFAIL_SEE_STDERR;
215 return false;
216 }
217
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000218 SkAutoDataUnref dataRef(ReadFileIntoSkData(inFile));
epoger@google.com76c913d2013-04-26 15:06:44 +0000219 if (NULL == dataRef.get()) {
220 gm_fprintf(stderr, "error reading JSON file %s\n", jsonPath);
221 DEBUGFAIL_SEE_STDERR;
222 return false;
223 }
224
225 const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
226 size_t size = dataRef.get()->size();
227 Json::Reader reader;
228 if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
229 gm_fprintf(stderr, "error parsing JSON file %s\n", jsonPath);
230 DEBUGFAIL_SEE_STDERR;
231 return false;
232 }
233 return true;
234 }
235
236}