| Index: gm/gm_expectations.cpp
|
| diff --git a/gm/gm_expectations.cpp b/gm/gm_expectations.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..db6abdf404b6fd0c3827947d3b8c9bb0eea490e4
|
| --- /dev/null
|
| +++ b/gm/gm_expectations.cpp
|
| @@ -0,0 +1,239 @@
|
| +/*
|
| + * Copyright 2013 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + *
|
| + * TODO(epoger): Combine this with tools/image_expectations.cpp, or eliminate one of the two.
|
| + */
|
| +
|
| +#include "gm_expectations.h"
|
| +#include "SkBitmapHasher.h"
|
| +#include "SkData.h"
|
| +#include "SkImageDecoder.h"
|
| +
|
| +#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message")
|
| +
|
| +// See gm_json.py for descriptions of each of these JSON keys.
|
| +// These constants must be kept in sync with the ones in that Python file!
|
| +const static char kJsonKey_ActualResults[] = "actual-results";
|
| +const static char kJsonKey_ActualResults_Failed[] = "failed";
|
| +const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
|
| +const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
|
| +const static char kJsonKey_ActualResults_Succeeded[] = "succeeded";
|
| +const static char kJsonKey_ExpectedResults[] = "expected-results";
|
| +const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
|
| +const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
|
| +
|
| +// Types of result hashes we support in the JSON file.
|
| +const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
|
| +
|
| +
|
| +namespace skiagm {
|
| +
|
| + Json::Value CreateJsonTree(Json::Value expectedResults,
|
| + Json::Value actualResultsFailed,
|
| + Json::Value actualResultsFailureIgnored,
|
| + Json::Value actualResultsNoComparison,
|
| + Json::Value actualResultsSucceeded) {
|
| + Json::Value actualResults;
|
| + actualResults[kJsonKey_ActualResults_Failed] = actualResultsFailed;
|
| + actualResults[kJsonKey_ActualResults_FailureIgnored] = actualResultsFailureIgnored;
|
| + actualResults[kJsonKey_ActualResults_NoComparison] = actualResultsNoComparison;
|
| + actualResults[kJsonKey_ActualResults_Succeeded] = actualResultsSucceeded;
|
| + Json::Value root;
|
| + root[kJsonKey_ActualResults] = actualResults;
|
| + root[kJsonKey_ExpectedResults] = expectedResults;
|
| + return root;
|
| + }
|
| +
|
| + // GmResultDigest class...
|
| +
|
| + GmResultDigest::GmResultDigest(const SkBitmap &bitmap) {
|
| + fIsValid = SkBitmapHasher::ComputeDigest(bitmap, &fHashDigest);
|
| + }
|
| +
|
| + GmResultDigest::GmResultDigest(const Json::Value &jsonTypeValuePair) {
|
| + fIsValid = false;
|
| + if (!jsonTypeValuePair.isArray()) {
|
| + SkDebugf("found non-array json value when parsing GmResultDigest: %s\n",
|
| + jsonTypeValuePair.toStyledString().c_str());
|
| + DEBUGFAIL_SEE_STDERR;
|
| + } else if (2 != jsonTypeValuePair.size()) {
|
| + SkDebugf("found json array with wrong size when parsing GmResultDigest: %s\n",
|
| + jsonTypeValuePair.toStyledString().c_str());
|
| + DEBUGFAIL_SEE_STDERR;
|
| + } else {
|
| + // TODO(epoger): The current implementation assumes that the
|
| + // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
|
| + Json::Value jsonHashValue = jsonTypeValuePair[1];
|
| + if (!jsonHashValue.isIntegral()) {
|
| + SkDebugf("found non-integer jsonHashValue when parsing GmResultDigest: %s\n",
|
| + jsonTypeValuePair.toStyledString().c_str());
|
| + DEBUGFAIL_SEE_STDERR;
|
| + } else {
|
| + fHashDigest = jsonHashValue.asUInt64();
|
| + fIsValid = true;
|
| + }
|
| + }
|
| + }
|
| +
|
| + bool GmResultDigest::isValid() const {
|
| + return fIsValid;
|
| + }
|
| +
|
| + bool GmResultDigest::equals(const GmResultDigest &other) const {
|
| + // TODO(epoger): The current implementation assumes that this
|
| + // and other are always of type kJsonKey_Hashtype_Bitmap_64bitMD5
|
| + return (this->fIsValid && other.fIsValid && (this->fHashDigest == other.fHashDigest));
|
| + }
|
| +
|
| + Json::Value GmResultDigest::asJsonTypeValuePair() const {
|
| + // TODO(epoger): The current implementation assumes that the
|
| + // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
|
| + Json::Value jsonTypeValuePair;
|
| + if (fIsValid) {
|
| + jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
|
| + jsonTypeValuePair.append(Json::UInt64(fHashDigest));
|
| + } else {
|
| + jsonTypeValuePair.append(Json::Value("INVALID"));
|
| + }
|
| + return jsonTypeValuePair;
|
| + }
|
| +
|
| + SkString GmResultDigest::getHashType() const {
|
| + // TODO(epoger): The current implementation assumes that the
|
| + // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
|
| + return SkString(kJsonKey_Hashtype_Bitmap_64bitMD5);
|
| + }
|
| +
|
| + SkString GmResultDigest::getDigestValue() const {
|
| + // TODO(epoger): The current implementation assumes that the
|
| + // result digest is always of type kJsonKey_Hashtype_Bitmap_64bitMD5
|
| + SkString retval;
|
| + retval.appendU64(fHashDigest);
|
| + return retval;
|
| + }
|
| +
|
| +
|
| + // Expectations class...
|
| +
|
| + Expectations::Expectations(bool ignoreFailure) {
|
| + fIgnoreFailure = ignoreFailure;
|
| + }
|
| +
|
| + Expectations::Expectations(const SkBitmap& bitmap, bool ignoreFailure) {
|
| + fBitmap = bitmap;
|
| + fIgnoreFailure = ignoreFailure;
|
| + fAllowedResultDigests.push_back(GmResultDigest(bitmap));
|
| + }
|
| +
|
| + Expectations::Expectations(const BitmapAndDigest& bitmapAndDigest) {
|
| + fBitmap = bitmapAndDigest.fBitmap;
|
| + fIgnoreFailure = false;
|
| + fAllowedResultDigests.push_back(bitmapAndDigest.fDigest);
|
| + }
|
| +
|
| + Expectations::Expectations(Json::Value jsonElement) {
|
| + if (jsonElement.empty()) {
|
| + fIgnoreFailure = kDefaultIgnoreFailure;
|
| + } else {
|
| + Json::Value ignoreFailure = jsonElement[kJsonKey_ExpectedResults_IgnoreFailure];
|
| + if (ignoreFailure.isNull()) {
|
| + fIgnoreFailure = kDefaultIgnoreFailure;
|
| + } else if (!ignoreFailure.isBool()) {
|
| + SkDebugf("found non-boolean json value for key '%s' in element '%s'\n",
|
| + kJsonKey_ExpectedResults_IgnoreFailure,
|
| + jsonElement.toStyledString().c_str());
|
| + DEBUGFAIL_SEE_STDERR;
|
| + fIgnoreFailure = kDefaultIgnoreFailure;
|
| + } else {
|
| + fIgnoreFailure = ignoreFailure.asBool();
|
| + }
|
| +
|
| + Json::Value allowedDigests = jsonElement[kJsonKey_ExpectedResults_AllowedDigests];
|
| + if (allowedDigests.isNull()) {
|
| + // ok, we'll just assume there aren't any AllowedDigests to compare against
|
| + } else if (!allowedDigests.isArray()) {
|
| + SkDebugf("found non-array json value for key '%s' in element '%s'\n",
|
| + kJsonKey_ExpectedResults_AllowedDigests,
|
| + jsonElement.toStyledString().c_str());
|
| + DEBUGFAIL_SEE_STDERR;
|
| + } else {
|
| + for (Json::ArrayIndex i=0; i<allowedDigests.size(); i++) {
|
| + fAllowedResultDigests.push_back(GmResultDigest(allowedDigests[i]));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + bool Expectations::match(GmResultDigest actualGmResultDigest) const {
|
| + for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
|
| + GmResultDigest allowedResultDigest = this->fAllowedResultDigests[i];
|
| + if (allowedResultDigest.equals(actualGmResultDigest)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + Json::Value Expectations::asJsonValue() const {
|
| + Json::Value allowedDigestArray;
|
| + if (!this->fAllowedResultDigests.empty()) {
|
| + for (int i=0; i < this->fAllowedResultDigests.count(); i++) {
|
| + allowedDigestArray.append(this->fAllowedResultDigests[i].asJsonTypeValuePair());
|
| + }
|
| + }
|
| +
|
| + Json::Value jsonExpectations;
|
| + jsonExpectations[kJsonKey_ExpectedResults_AllowedDigests] = allowedDigestArray;
|
| + jsonExpectations[kJsonKey_ExpectedResults_IgnoreFailure] = this->ignoreFailure();
|
| + return jsonExpectations;
|
| + }
|
| +
|
| + // IndividualImageExpectationsSource class...
|
| +
|
| + Expectations IndividualImageExpectationsSource::get(const char *testName) const {
|
| + SkString path = SkOSPath::Join(fRootDir.c_str(), testName);
|
| + SkBitmap referenceBitmap;
|
| + bool decodedReferenceBitmap =
|
| + SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, kN32_SkColorType,
|
| + SkImageDecoder::kDecodePixels_Mode, NULL);
|
| + if (decodedReferenceBitmap) {
|
| + return Expectations(referenceBitmap);
|
| + } else {
|
| + return Expectations();
|
| + }
|
| + }
|
| +
|
| +
|
| + // JsonExpectationsSource class...
|
| +
|
| + JsonExpectationsSource::JsonExpectationsSource(const char *jsonPath) {
|
| + Parse(jsonPath, &fJsonRoot);
|
| + fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
|
| + }
|
| +
|
| + Expectations JsonExpectationsSource::get(const char *testName) const {
|
| + return Expectations(fJsonExpectedResults[testName]);
|
| + }
|
| +
|
| + /*static*/ bool JsonExpectationsSource::Parse(const char *jsonPath, Json::Value *jsonRoot) {
|
| + SkAutoDataUnref dataRef(SkData::NewFromFileName(jsonPath));
|
| + if (NULL == dataRef.get()) {
|
| + SkDebugf("error reading JSON file %s\n", jsonPath);
|
| + DEBUGFAIL_SEE_STDERR;
|
| + return false;
|
| + }
|
| +
|
| + const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
|
| + size_t size = dataRef.get()->size();
|
| + Json::Reader reader;
|
| + if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
|
| + SkDebugf("error parsing JSON file %s\n", jsonPath);
|
| + DEBUGFAIL_SEE_STDERR;
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +}
|
|
|