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