Home | History | Annotate | Download | only in gm
      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!
     19 const static char kJsonKey_ActualResults[]   = "actual-results";
     20 const static char kJsonKey_ActualResults_Failed[]        = "failed";
     21 const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
     22 const static char kJsonKey_ActualResults_NoComparison[]  = "no-comparison";
     23 const static char kJsonKey_ActualResults_Succeeded[]     = "succeeded";
     24 const static char kJsonKey_ExpectedResults[] = "expected-results";
     25 const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
     26 const static char kJsonKey_ExpectedResults_IgnoreFailure[]  = "ignore-failure";
     27 
     28 // Types of result hashes we support in the JSON file.
     29 const static char kJsonKey_Hashtype_Bitmap_64bitMD5[]  = "bitmap-64bitMD5";
     30 
     31 
     32 namespace 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