1/*
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 * TODO(epoger): Combine this with tools/image_expectations.cpp, or eliminate one of the two.
8 */
9
10#include "gm_expectations.h"
11#include "SkBitmapHasher.h"
12#include "SkData.h"
13#include "SkImageDecoder.h"
14
15#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message")
16
17// See gm_json.py for descriptions of each of these JSON keys.
18// These constants must be kept in sync with the ones in that Python file!
19const static char kJsonKey_ActualResults[]   = "actual-results";
20const static char kJsonKey_ActualResults_Failed[]        = "failed";
21const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
22const static char kJsonKey_ActualResults_NoComparison[]  = "no-comparison";
23const static char kJsonKey_ActualResults_Succeeded[]     = "succeeded";
24const static char kJsonKey_ExpectedResults[] = "expected-results";
25const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
26const static char kJsonKey_ExpectedResults_IgnoreFailure[]  = "ignore-failure";
27
28// Types of result hashes we support in the JSON file.
29const static char kJsonKey_Hashtype_Bitmap_64bitMD5[]  = "bitmap-64bitMD5";
30
31
32namespace skiagm {
33
34    Json::Value CreateJsonTree(Json::Value expectedResults,
35                               Json::Value actualResultsFailed,
36                               Json::Value actualResultsFailureIgnored,
37                               Json::Value actualResultsNoComparison,
38                               Json::Value actualResultsSucceeded) {
39        Json::Value actualResults;
40        actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
41        actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
42        actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
43        actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
44        Json::Value root;
45        root[kJsonKey_ActualResults] = actualResults;
46        root[kJsonKey_ExpectedResults] = expectedResults;
47        return root;
48    }
49
50    // GmResultDigest class...
51
52    GmResultDigest::GmResultDigest(const SkBitmap &bitmap) {
53        fIsValid = SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest);
54    }
55
56    GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) {
57        fIsValid = false;
58        if (!jsonTypeValuePair.isArray()) {
59            SkDebugf("found non-array json value when parsing GmResultDigest: %s\n",
60                     jsonTypeValuePair.toStyledString().c_str());
61            DEBUGFAIL_SEE_STDERR;
62        } else if (2 != jsonTypeValuePair.size()) {
63            SkDebugf("found json array with wrong size when parsing GmResultDigest: %s\n",
64                     jsonTypeValuePair.toStyledString().c_str());
65            DEBUGFAIL_SEE_STDERR;
66        } else {
67            // TODO(epoger): The current implementation assumes that the
68            // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
69            Json::Value jsonHashValue = jsonTypeValuePair[1];
70            if (!jsonHashValue.isIntegral()) {
71                SkDebugf("found non-integer jsonHashValue when parsing GmResultDigest: %s\n",
72                         jsonTypeValuePair.toStyledString().c_str());
73                DEBUGFAIL_SEE_STDERR;
74            } else {
75                fHashDigest = jsonHashValue.asUInt64();
76                fIsValid = true;
77            }
78        }
79    }
80
81    bool GmResultDigest::isValid() const {
82        return fIsValid;
83    }
84
85    bool GmResultDigest::equals(const GmResultDigest &other) const {
86        // TODO(epoger): The current implementation assumes that this
87        // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5
88        return (this->fIsValid && other.fIsValid && (this->fHashDigest == other.fHashDigest));
89    }
90
91    Json::Value GmResultDigest::asJsonTypeValuePair() const {
92        // TODO(epoger): The current implementation assumes that the
93        // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
94        Json::Value jsonTypeValuePair;
95        if (fIsValid) {
96            jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
97            jsonTypeValuePair.append(Json::UInt64(fHashDigest));
98        } else {
99            jsonTypeValuePair.append(Json::Value("INVALID"));
100        }
101        return jsonTypeValuePair;
102    }
103
104    SkString GmResultDigest::getHashType() const {
105        // TODO(epoger): The current implementation assumes that the
106        // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
107        return SkString(kJsonKey_Hashtype_Bitmap_64bitMD5);
108    }
109
110    SkString GmResultDigest::getDigestValue() const {
111        // TODO(epoger): The current implementation assumes that the
112        // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
113        SkString retval;
114        retval.appendU64(fHashDigest);
115        return retval;
116    }
117
118
119    // Expectations class...
120
121    Expectations::Expectations(bool ignoreFailure) {
122        fIgnoreFailure = ignoreFailure;
123    }
124
125    Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
126        fBitmap = bitmap;
127        fIgnoreFailure = ignoreFailure;
128        fAllowedResultDigests.push_back(GmResultDigest(bitmap));
129    }
130
131    Expectations::Expectations(const BitmapAndDigest& bitmapAndDigest) {
132        fBitmap = bitmapAndDigest.fBitmap;
133        fIgnoreFailure = false;
134        fAllowedResultDigests.push_back(bitmapAndDigest.fDigest);
135    }
136
137    Expectations::Expectations(Json::Value jsonElement) {
138        if (jsonElement.empty()) {
139            fIgnoreFailure = kDefaultIgnoreFailure;
140        } else {
141            Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
142            if (ignoreFailure.isNull()) {
143                fIgnoreFailure = kDefaultIgnoreFailure;
144            } else if (!ignoreFailure.isBool()) {
145                SkDebugf("found non-boolean json value for key '%s' in element '%s'\n",
146                         kJsonKey_ExpectedResults_IgnoreFailure,
147                         jsonElement.toStyledString().c_str());
148                DEBUGFAIL_SEE_STDERR;
149                fIgnoreFailure = kDefaultIgnoreFailure;
150            } else {
151                fIgnoreFailure = ignoreFailure.asBool();
152            }
153
154            Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests];
155            if (allowedDigests.isNull()) {
156                // ok, we'll just assume there aren't any AllowedDigests to compare against
157            } else if (!allowedDigests.isArray()) {
158                SkDebugf("found non-array json value for key '%s' in element '%s'\n",
159                         kJsonKey_ExpectedResults_AllowedDigests,
160                         jsonElement.toStyledString().c_str());
161                DEBUGFAIL_SEE_STDERR;
162            } else {
163                for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
164                    fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
165                }
166            }
167        }
168    }
169
170    bool Expectations::match(GmResultDigest actualGmResultDigest) const {
171        for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
172            GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i];
173            if (allowedResultDigest.equals(actualGmResultDigest)) {
174                return true;
175            }
176        }
177        return false;
178    }
179
180    Json::Value Expectations::asJsonValue() const {
181        Json::Value allowedDigestArray;
182        if (!this->fAllowedResultDigests.empty()) {
183            for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
184                allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair());
185            }
186        }
187
188        Json::Value jsonExpectations;
189        jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
190        jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure]  = this->ignoreFailure();
191        return jsonExpectations;
192    }
193
194    // IndividualImageExpectationsSource class...
195
196    Expectations IndividualImageExpectationsSource::get(const char *testName) const {
197        SkString path = SkOSPath::SkPathJoin(fRootDir.c_str(), testName);
198        SkBitmap referenceBitmap;
199        bool decodedReferenceBitmap =
200            SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, kN32_SkColorType,
201                                       SkImageDecoder::kDecodePixels_Mode, NULL);
202        if (decodedReferenceBitmap) {
203            return Expectations(referenceBitmap);
204        } else {
205            return Expectations();
206        }
207    }
208
209
210    // JsonExpectationsSource class...
211
212    JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
213        Parse(jsonPath, &fJsonRoot);
214        fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
215    }
216
217    Expectations JsonExpectationsSource::get(const char *testName) const {
218        return Expectations(fJsonExpectedResults[testName]);
219    }
220
221    /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
222        SkAutoDataUnref dataRef(SkData::NewFromFileName(jsonPath));
223        if (NULL == dataRef.get()) {
224            SkDebugf("error reading JSON file %s\n", jsonPath);
225            DEBUGFAIL_SEE_STDERR;
226            return false;
227        }
228
229        const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
230        size_t size = dataRef.get()->size();
231        Json::Reader reader;
232        if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
233            SkDebugf("error parsing JSON file %s\n", jsonPath);
234            DEBUGFAIL_SEE_STDERR;
235            return false;
236        }
237        return true;
238    }
239}
240