blob: 8efb98671387724061ebcee1ee78e62808268c23 [file] [log] [blame]
epoger@google.com37269602013-01-19 04:21:27 +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#ifndef gm_expectations_DEFINED
8#define gm_expectations_DEFINED
9
10#include "gm.h"
epoger@google.com84a18022013-02-01 20:39:15 +000011#include "SkBitmap.h"
epoger@google.com908f5832013-04-12 02:23:55 +000012#include "SkBitmapHasher.h"
epoger@google.comd271d242013-02-13 18:14:48 +000013#include "SkData.h"
epoger@google.com37269602013-01-19 04:21:27 +000014#include "SkOSFile.h"
15#include "SkRefCnt.h"
epoger@google.comd271d242013-02-13 18:14:48 +000016#include "SkStream.h"
epoger@google.com37269602013-01-19 04:21:27 +000017#include "SkTArray.h"
18
19#ifdef SK_BUILD_FOR_WIN
20 // json includes xlocale which generates warning 4530 because we're compiling without
21 // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067
22 #pragma warning(push)
23 #pragma warning(disable : 4530)
24#endif
epoger@google.comd271d242013-02-13 18:14:48 +000025#include "json/reader.h"
epoger@google.com37269602013-01-19 04:21:27 +000026#include "json/value.h"
27#ifdef SK_BUILD_FOR_WIN
28 #pragma warning(pop)
29#endif
30
31namespace skiagm {
32
scroggo@google.com6843bdb2013-05-08 19:14:23 +000033 void gm_fprintf(FILE *stream, const char format[], ...);
epoger@google.com5efdd0c2013-03-13 14:18:40 +000034
epoger@google.comce057fe2013-05-14 15:17:46 +000035 /**
36 * Assembles rootPath and relativePath into a single path, like this:
37 * rootPath/relativePath
38 *
39 * Uses SkPATH_SEPARATOR, to work on all platforms.
40 *
41 * TODO(epoger): This should probably move into SkOSFile.h
42 */
43 SkString SkPathJoin(const char *rootPath, const char *relativePath);
44
epoger@google.com76c913d2013-04-26 15:06:44 +000045 Json::Value CreateJsonTree(Json::Value expectedResults,
46 Json::Value actualResultsFailed,
47 Json::Value actualResultsFailureIgnored,
48 Json::Value actualResultsNoComparison,
49 Json::Value actualResultsSucceeded);
50
epoger@google.com37269602013-01-19 04:21:27 +000051 /**
epoger@google.comd4993ff2013-05-24 14:33:28 +000052 * The digest of a GM test result.
53 *
54 * Currently, this is always a uint64_t hash digest of an SkBitmap...
55 * but we will add other flavors soon.
56 */
57 class GmResultDigest {
58 public:
59 /**
60 * Create a ResultDigest representing an actual image result.
61 */
62 GmResultDigest(const SkBitmap &bitmap);
63
64 /**
65 * Create a ResultDigest representing an allowed result
66 * checksum within JSON expectations file, in the form
67 * ["bitmap-64bitMD5", 12345].
68 */
69 GmResultDigest(const Json::Value &jsonTypeValuePair);
70
71 /**
72 * Returns true if this GmResultDigest was fully and successfully
73 * created.
74 */
75 bool isValid() const;
76
77 /**
78 * Returns true if this and other GmResultDigest could
79 * represent identical results.
80 */
81 bool equals(const GmResultDigest &other) const;
82
83 /**
84 * Returns a JSON type/value pair representing this result,
85 * such as ["bitmap-64bitMD5", 12345].
86 */
87 Json::Value asJsonTypeValuePair() const;
88
89 private:
90 bool fIsValid; // always check this first--if it's false, other fields are meaningless
91 uint64_t fHashDigest;
92 };
93
94 /**
95 * Test expectations (allowed image results, etc.)
epoger@google.com37269602013-01-19 04:21:27 +000096 */
97 class Expectations {
98 public:
99 /**
100 * No expectations at all.
epoger@google.com37269602013-01-19 04:21:27 +0000101 */
epoger@google.com76c913d2013-04-26 15:06:44 +0000102 Expectations(bool ignoreFailure=kDefaultIgnoreFailure);
epoger@google.com37269602013-01-19 04:21:27 +0000103
104 /**
epoger@google.com84a18022013-02-01 20:39:15 +0000105 * Expect exactly one image (appropriate for the case when we
epoger@google.com37269602013-01-19 04:21:27 +0000106 * are comparing against a single PNG file).
epoger@google.com37269602013-01-19 04:21:27 +0000107 */
epoger@google.com76c913d2013-04-26 15:06:44 +0000108 Expectations(const SkBitmap& bitmap, bool ignoreFailure=kDefaultIgnoreFailure);
epoger@google.com37269602013-01-19 04:21:27 +0000109
110 /**
epoger@google.comd271d242013-02-13 18:14:48 +0000111 * Create Expectations from a JSON element as found within the
112 * kJsonKey_ExpectedResults section.
113 *
114 * It's fine if the jsonElement is null or empty; in that case, we just
115 * don't have any expectations.
116 */
epoger@google.com76c913d2013-04-26 15:06:44 +0000117 Expectations(Json::Value jsonElement);
epoger@google.comd271d242013-02-13 18:14:48 +0000118
119 /**
epoger@google.com37269602013-01-19 04:21:27 +0000120 * Returns true iff we want to ignore failed expectations.
121 */
122 bool ignoreFailure() const { return this->fIgnoreFailure; }
123
124 /**
epoger@google.comd4993ff2013-05-24 14:33:28 +0000125 * Returns true iff there are no allowed results.
epoger@google.com37269602013-01-19 04:21:27 +0000126 */
epoger@google.comd4993ff2013-05-24 14:33:28 +0000127 bool empty() const { return this->fAllowedResultDigests.empty(); }
epoger@google.com37269602013-01-19 04:21:27 +0000128
129 /**
epoger@google.comd4993ff2013-05-24 14:33:28 +0000130 * Returns true iff resultDigest matches any allowed result,
epoger@google.com37269602013-01-19 04:21:27 +0000131 * regardless of fIgnoreFailure. (The caller can check
132 * that separately.)
133 */
epoger@google.comd4993ff2013-05-24 14:33:28 +0000134 bool match(GmResultDigest resultDigest) const;
epoger@google.com37269602013-01-19 04:21:27 +0000135
136 /**
epoger@google.com84a18022013-02-01 20:39:15 +0000137 * If this Expectation is based on a single SkBitmap, return a
138 * pointer to that SkBitmap. Otherwise (if the Expectation is
139 * empty, or if it was based on a list of checksums rather
140 * than a single bitmap), returns NULL.
141 */
142 const SkBitmap *asBitmap() const {
143 return (SkBitmap::kNo_Config == fBitmap.config()) ? NULL : &fBitmap;
144 }
145
146 /**
epoger@google.com76c913d2013-04-26 15:06:44 +0000147 * Return a JSON representation of the expectations.
epoger@google.com37269602013-01-19 04:21:27 +0000148 */
epoger@google.com76c913d2013-04-26 15:06:44 +0000149 Json::Value asJsonValue() const;
epoger@google.com37269602013-01-19 04:21:27 +0000150
151 private:
epoger@google.comd271d242013-02-13 18:14:48 +0000152 const static bool kDefaultIgnoreFailure = false;
153
epoger@google.comd4993ff2013-05-24 14:33:28 +0000154 SkTArray<GmResultDigest> fAllowedResultDigests;
epoger@google.com37269602013-01-19 04:21:27 +0000155 bool fIgnoreFailure;
epoger@google.com84a18022013-02-01 20:39:15 +0000156 SkBitmap fBitmap;
epoger@google.com37269602013-01-19 04:21:27 +0000157 };
158
159 /**
160 * Abstract source of Expectations objects for individual tests.
161 */
162 class ExpectationsSource : public SkRefCnt {
163 public:
164 virtual Expectations get(const char *testName) = 0;
165 };
166
167 /**
168 * Return Expectations based on individual image files on disk.
169 */
170 class IndividualImageExpectationsSource : public ExpectationsSource {
171 public:
172 /**
173 * Create an ExpectationsSource that will return Expectations based on
174 * image files found within rootDir.
175 *
176 * rootDir: directory under which to look for image files
177 * (this string will be copied to storage within this object)
epoger@google.com37269602013-01-19 04:21:27 +0000178 */
epoger@google.comb0f8b432013-04-10 18:46:25 +0000179 IndividualImageExpectationsSource(const char *rootDir) : fRootDir(rootDir) {}
epoger@google.com37269602013-01-19 04:21:27 +0000180
epoger@google.com76c913d2013-04-26 15:06:44 +0000181 Expectations get(const char *testName) SK_OVERRIDE ;
epoger@google.com37269602013-01-19 04:21:27 +0000182
183 private:
184 const SkString fRootDir;
epoger@google.com37269602013-01-19 04:21:27 +0000185 };
186
epoger@google.comd271d242013-02-13 18:14:48 +0000187 /**
188 * Return Expectations based on JSON summary file.
189 */
190 class JsonExpectationsSource : public ExpectationsSource {
191 public:
192 /**
193 * Create an ExpectationsSource that will return Expectations based on
194 * a JSON file.
195 *
196 * jsonPath: path to JSON file to read
197 */
epoger@google.com76c913d2013-04-26 15:06:44 +0000198 JsonExpectationsSource(const char *jsonPath);
epoger@google.comd271d242013-02-13 18:14:48 +0000199
epoger@google.com76c913d2013-04-26 15:06:44 +0000200 Expectations get(const char *testName) SK_OVERRIDE;
epoger@google.comd271d242013-02-13 18:14:48 +0000201
202 private:
203
204 /**
205 * Read as many bytes as possible (up to maxBytes) from the stream into
206 * an SkData object.
207 *
208 * If the returned SkData contains fewer than maxBytes, then EOF has been
209 * reached and no more data would be available from subsequent calls.
210 * (If EOF has already been reached, then this call will return an empty
211 * SkData object immediately.)
212 *
213 * If there are fewer than maxBytes bytes available to read from the
214 * stream, but the stream has not been closed yet, this call will block
215 * until there are enough bytes to read or the stream has been closed.
216 *
217 * It is up to the caller to call unref() on the returned SkData object
218 * once the data is no longer needed, so that the underlying buffer will
219 * be freed. For example:
220 *
221 * {
222 * size_t maxBytes = 256;
223 * SkAutoDataUnref dataRef(readIntoSkData(stream, maxBytes));
224 * if (NULL != dataRef.get()) {
225 * size_t bytesActuallyRead = dataRef.get()->size();
226 * // use the data...
227 * }
228 * }
229 * // underlying buffer has been freed, thanks to auto unref
230 *
231 */
232 // TODO(epoger): Move this, into SkStream.[cpp|h] as attempted in
233 // https://codereview.appspot.com/7300071 ?
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000234 // And maybe ReadFileIntoSkData() also?
235 static SkData* ReadIntoSkData(SkStream &stream, size_t maxBytes);
epoger@google.comd271d242013-02-13 18:14:48 +0000236
237 /**
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000238 * Wrapper around ReadIntoSkData for files: reads the entire file into
epoger@google.comd271d242013-02-13 18:14:48 +0000239 * an SkData object.
240 */
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000241 static SkData* ReadFileIntoSkData(SkFILEStream &stream) {
242 return ReadIntoSkData(stream, stream.getLength());
epoger@google.comd271d242013-02-13 18:14:48 +0000243 }
244
245 /**
246 * Read the file contents from jsonPath and parse them into jsonRoot.
247 *
248 * Returns true if successful.
249 */
scroggo@google.com6843bdb2013-05-08 19:14:23 +0000250 static bool Parse(const char *jsonPath, Json::Value *jsonRoot);
epoger@google.comd271d242013-02-13 18:14:48 +0000251
252 Json::Value fJsonRoot;
253 Json::Value fJsonExpectedResults;
254 };
255
epoger@google.com37269602013-01-19 04:21:27 +0000256}
257#endif