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