aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/image_expectations.h
blob: 422c64d4eda78b379c6fa6495fd941fdc084ac2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef image_expectations_DEFINED
#define image_expectations_DEFINED

#include "SkBitmap.h"
#include "SkJSONCPP.h"
#include "SkOSFile.h"

namespace sk_tools {

    /**
     * The digest of an image (either an image we have generated locally, or an image expectation).
     *
     * Currently, this is always a uint64_t hash digest of an SkBitmap.
     */
    class ImageDigest {
    public:
        /**
         * Create an ImageDigest of a bitmap.
         *
         * Computes the hash of the bitmap lazily, since that is an expensive operation.
         *
         * @param bitmap image to get the digest of
         */
        explicit ImageDigest(const SkBitmap &bitmap);

        /**
         * Create an ImageDigest using a hashType/hashValue pair.
         *
         * @param hashType the algorithm used to generate the hash; for now, only
         *     kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 is allowed.
         * @param hashValue the value generated by the hash algorithm for a particular image.
         */
        explicit ImageDigest(const SkString &hashType, uint64_t hashValue);

        /**
         * Returns true iff this and other ImageDigest represent identical images.
         */
        bool equals(ImageDigest &other);

        /**
         * Returns the hash digest type as an SkString.
         *
         * For now, this always returns kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 .
         */
        SkString getHashType();

        /**
         * Returns the hash digest value as a uint64_t.
         *
         * Since the hash is computed lazily, this may take some time, and it may modify
         * some fields on this object.
         */
        uint64_t getHashValue();

    private:
        const SkBitmap fBitmap;
        uint64_t fHashValue;
        bool fComputedHashValue;
    };

    /**
     * Container that holds a reference to an SkBitmap and its ImageDigest.
     */
    class BitmapAndDigest {
    public:
        explicit BitmapAndDigest(const SkBitmap &bitmap);

        const SkBitmap *getBitmapPtr() const;

        /**
         * Returns a pointer to the ImageDigest.
         *
         * Since the hash is computed lazily within the ImageDigest object, we cannot mandate
         * that it be held const.
         */
        ImageDigest *getImageDigestPtr();
    private:
        const SkBitmap fBitmap;
        ImageDigest fImageDigest;
    };

    /**
     * Expected test result: expected image (if any), and whether we want to ignore failures on
     * this test or not.
     *
     * This is just an ImageDigest (or lack thereof, if there is no expectation) and a boolean
     * telling us whether to ignore failures.
     */
    class Expectation {
    public:
        /**
         * No expectation at all.
         */
        explicit Expectation(bool ignoreFailure=kDefaultIgnoreFailure);

        /**
         * Expect an image, passed as hashType/hashValue.
         */
        explicit Expectation(const SkString &hashType, uint64_t hashValue,
                             bool ignoreFailure=kDefaultIgnoreFailure);

        /**
         * Expect an image, passed as a bitmap.
         */
        explicit Expectation(const SkBitmap& bitmap,
                             bool ignoreFailure=kDefaultIgnoreFailure);

        /**
         * Returns true iff we want to ignore failed expectations.
         */
        bool ignoreFailure() const;

        /**
         * Returns true iff there are no allowed results.
         */
        bool empty() const;

        /**
         * Returns true iff we are expecting a particular image, and imageDigest matches it.
         *
         * If empty() returns true, this will return false.
         *
         * If this expectation DOES contain an image, and imageDigest doesn't match it,
         * this method will return false regardless of what ignoreFailure() would return.
         * (The caller can check that separately.)
         */
        bool matches(ImageDigest &imageDigest);

    private:
        static const bool kDefaultIgnoreFailure = false;

        const bool fIsEmpty;
        const bool fIgnoreFailure;
        ImageDigest fImageDigest;  // cannot be const, because it computes its hash lazily
    };

    /**
     * Collects ImageDigests of actually rendered images, perhaps comparing to expectations.
     */
    class ImageResultsAndExpectations {
    public:
        /**
         * Adds expectations from a JSON file, returning true if successful.
         *
         * If the file exists but is empty, it succeeds, and there will be no expectations.
         * If the file does not exist, this will fail.
         *
         * Reasoning:
         * Generating expectations the first time can be a tricky chicken-and-egg
         * proposition.  "I need actual results to turn into expectations... but the only
         * way to get actual results is to run the tool, and the tool won't run without
         * expectations!"
         * We could make the tool run even if there is no expectations file at all, but it's
         * better for the tool to fail if the expectations file is not found--that will tell us
         * quickly if files are not being copied around as they should be.
         * Creating an empty file is an easy way to break the chicken-and-egg cycle and generate
         * the first real expectations.
         */
        bool readExpectationsFile(const char *jsonPath);

        /**
         * Adds this image to the summary of results.
         *
         * @param sourceName name of the source file that generated this result
         * @param fileName relative path to the image output file on local disk
         * @param digest description of the image's contents
         * @param tileNumber if not NULL, pointer to tile number
         */
        void add(const char *sourceName, const char *fileName, ImageDigest &digest,
                 const int *tileNumber=NULL);

        /**
         * Adds a key/value pair to the descriptions dict within the summary of results.
         *
         * @param key key within the descriptions dict
         * @param value value to associate with that key
         */
        void addDescription(const char *key, const char *value);

        /**
         * Adds the image base Google Storage URL to the summary of results.
         *
         * @param imageBaseGSUrl the image base Google Storage URL
         */
        void setImageBaseGSUrl(const char *imageBaseGSUrl);

        /**
         * Returns the Expectation for this test.
         *
         * @param sourceName name of the source file that generated this result
         * @param tileNumber if not NULL, pointer to tile number
         *
         * TODO(stephana): To make this work for GMs, we will need to add parameters for
         * config, and maybe renderMode/builder?
         */
        Expectation getExpectation(const char *sourceName, const int *tileNumber=NULL);

        /**
         * Writes the summary (as constructed so far) to a file.
         *
         * @param filename path to write the summary to
         */
        void writeToFile(const char *filename) const;

    private:

        /**
         * Read the file contents from filePtr and parse them into jsonRoot.
         *
         * It is up to the caller to close filePtr after this is done.
         *
         * Returns true if successful.
         */
        static bool Parse(SkFILE* filePtr, Json::Value *jsonRoot);

        Json::Value fActualResults;
        Json::Value fDescriptions;
        Json::Value fExpectedJsonRoot;
        Json::Value fExpectedResults;
        Json::Value fImageBaseGSUrl;
    };

} // namespace sk_tools

#endif  // image_expectations_DEFINED