1 /*
2  * Copyright 2014 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 #ifndef image_expectations_DEFINED
9 #define image_expectations_DEFINED
10 
11 #include "SkBitmap.h"
12 #include "SkJSONCPP.h"
13 #include "SkOSFile.h"
14 
15 namespace sk_tools {
16 
17     /**
18      * The digest of an image (either an image we have generated locally, or an image expectation).
19      *
20      * Currently, this is always a uint64_t hash digest of an SkBitmap.
21      */
22     class ImageDigest {
23     public:
24         /**
25          * Create an ImageDigest of a bitmap.
26          *
27          * Computes the hash of the bitmap lazily, since that is an expensive operation.
28          *
29          * @param bitmap image to get the digest of
30          */
31         explicit ImageDigest(const SkBitmap &bitmap);
32 
33         /**
34          * Create an ImageDigest using a hashType/hashValue pair.
35          *
36          * @param hashType the algorithm used to generate the hash; for now, only
37          *     kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 is allowed.
38          * @param hashValue the value generated by the hash algorithm for a particular image.
39          */
40         explicit ImageDigest(const SkString &hashType, uint64_t hashValue);
41 
42         /**
43          * Returns true iff this and other ImageDigest represent identical images.
44          */
45         bool equals(ImageDigest &other);
46 
47         /**
48          * Returns the hash digest type as an SkString.
49          *
50          * For now, this always returns kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 .
51          */
52         SkString getHashType();
53 
54         /**
55          * Returns the hash digest value as a uint64_t.
56          *
57          * Since the hash is computed lazily, this may take some time, and it may modify
58          * some fields on this object.
59          */
60         uint64_t getHashValue();
61 
62     private:
63         const SkBitmap fBitmap;
64         uint64_t fHashValue;
65         bool fComputedHashValue;
66     };
67 
68     /**
69      * Container that holds a reference to an SkBitmap and its ImageDigest.
70      */
71     class BitmapAndDigest {
72     public:
73         explicit BitmapAndDigest(const SkBitmap &bitmap);
74 
75         const SkBitmap *getBitmapPtr() const;
76 
77         /**
78          * Returns a pointer to the ImageDigest.
79          *
80          * Since the hash is computed lazily within the ImageDigest object, we cannot mandate
81          * that it be held const.
82          */
83         ImageDigest *getImageDigestPtr();
84     private:
85         const SkBitmap fBitmap;
86         ImageDigest fImageDigest;
87     };
88 
89     /**
90      * Expected test result: expected image (if any), and whether we want to ignore failures on
91      * this test or not.
92      *
93      * This is just an ImageDigest (or lack thereof, if there is no expectation) and a boolean
94      * telling us whether to ignore failures.
95      */
96     class Expectation {
97     public:
98         /**
99          * No expectation at all.
100          */
101         explicit Expectation(bool ignoreFailure=kDefaultIgnoreFailure);
102 
103         /**
104          * Expect an image, passed as hashType/hashValue.
105          */
106         explicit Expectation(const SkString &hashType, uint64_t hashValue,
107                              bool ignoreFailure=kDefaultIgnoreFailure);
108 
109         /**
110          * Expect an image, passed as a bitmap.
111          */
112         explicit Expectation(const SkBitmap& bitmap,
113                              bool ignoreFailure=kDefaultIgnoreFailure);
114 
115         /**
116          * Returns true iff we want to ignore failed expectations.
117          */
118         bool ignoreFailure() const;
119 
120         /**
121          * Returns true iff there are no allowed results.
122          */
123         bool empty() const;
124 
125         /**
126          * Returns true iff we are expecting a particular image, and imageDigest matches it.
127          *
128          * If empty() returns true, this will return false.
129          *
130          * If this expectation DOES contain an image, and imageDigest doesn't match it,
131          * this method will return false regardless of what ignoreFailure() would return.
132          * (The caller can check that separately.)
133          */
134         bool matches(ImageDigest &imageDigest);
135 
136     private:
137         static const bool kDefaultIgnoreFailure = false;
138 
139         const bool fIsEmpty;
140         const bool fIgnoreFailure;
141         ImageDigest fImageDigest;  // cannot be const, because it computes its hash lazily
142     };
143 
144     /**
145      * Collects ImageDigests of actually rendered images, perhaps comparing to expectations.
146      */
147     class ImageResultsAndExpectations {
148     public:
149         /**
150          * Adds expectations from a JSON file, returning true if successful.
151          *
152          * If the file exists but is empty, it succeeds, and there will be no expectations.
153          * If the file does not exist, this will fail.
154          *
155          * Reasoning:
156          * Generating expectations the first time can be a tricky chicken-and-egg
157          * proposition.  "I need actual results to turn into expectations... but the only
158          * way to get actual results is to run the tool, and the tool won't run without
159          * expectations!"
160          * We could make the tool run even if there is no expectations file at all, but it's
161          * better for the tool to fail if the expectations file is not found--that will tell us
162          * quickly if files are not being copied around as they should be.
163          * Creating an empty file is an easy way to break the chicken-and-egg cycle and generate
164          * the first real expectations.
165          */
166         bool readExpectationsFile(const char *jsonPath);
167 
168         /**
169          * Adds this image to the summary of results.
170          *
171          * @param sourceName name of the source file that generated this result
172          * @param fileName relative path to the image output file on local disk
173          * @param digest description of the image's contents
174          * @param tileNumber if not NULL, pointer to tile number
175          */
176         void add(const char *sourceName, const char *fileName, ImageDigest &digest,
177                  const int *tileNumber=NULL);
178 
179         /**
180          * Adds a key/value pair to the descriptions dict within the summary of results.
181          *
182          * @param key key within the descriptions dict
183          * @param value value to associate with that key
184          */
185         void addDescription(const char *key, const char *value);
186 
187         /**
188          * Adds the image base Google Storage URL to the summary of results.
189          *
190          * @param imageBaseGSUrl the image base Google Storage URL
191          */
192         void setImageBaseGSUrl(const char *imageBaseGSUrl);
193 
194         /**
195          * Returns the Expectation for this test.
196          *
197          * @param sourceName name of the source file that generated this result
198          * @param tileNumber if not NULL, pointer to tile number
199          *
200          * TODO(stephana): To make this work for GMs, we will need to add parameters for
201          * config, and maybe renderMode/builder?
202          */
203         Expectation getExpectation(const char *sourceName, const int *tileNumber=NULL);
204 
205         /**
206          * Writes the summary (as constructed so far) to a file.
207          *
208          * @param filename path to write the summary to
209          */
210         void writeToFile(const char *filename) const;
211 
212     private:
213 
214         /**
215          * Read the file contents from filePtr and parse them into jsonRoot.
216          *
217          * It is up to the caller to close filePtr after this is done.
218          *
219          * Returns true if successful.
220          */
221         static bool Parse(SkFILE* filePtr, Json::Value *jsonRoot);
222 
223         Json::Value fActualResults;
224         Json::Value fDescriptions;
225         Json::Value fExpectedJsonRoot;
226         Json::Value fExpectedResults;
227         Json::Value fImageBaseGSUrl;
228     };
229 
230 } // namespace sk_tools
231 
232 #endif  // image_expectations_DEFINED
233