/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef image_expectations_DEFINED
#define image_expectations_DEFINED
#include "SkBitmap.h"
#include "SkJSONCPP.h"
#include "SkOSFile.h"
namespace sk_tools {
/**
* The digest of an image (either an image we have generated locally, or an image expectation).
*
* Currently, this is always a uint64_t hash digest of an SkBitmap.
*/
class ImageDigest {
public:
/**
* Create an ImageDigest of a bitmap.
*
* Computes the hash of the bitmap lazily, since that is an expensive operation.
*
* @param bitmap image to get the digest of
*/
explicit ImageDigest(const SkBitmap &bitmap);
/**
* Create an ImageDigest using a hashType/hashValue pair.
*
* @param hashType the algorithm used to generate the hash; for now, only
* kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 is allowed.
* @param hashValue the value generated by the hash algorithm for a particular image.
*/
explicit ImageDigest(const SkString &hashType, uint64_t hashValue);
/**
* Returns true iff this and other ImageDigest represent identical images.
*/
bool equals(ImageDigest &other);
/**
* Returns the hash digest type as an SkString.
*
* For now, this always returns kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 .
*/
SkString getHashType();
/**
* Returns the hash digest value as a uint64_t.
*
* Since the hash is computed lazily, this may take some time, and it may modify
* some fields on this object.
*/
uint64_t getHashValue();
private:
const SkBitmap fBitmap;
uint64_t fHashValue;
bool fComputedHashValue;
};
/**
* Container that holds a reference to an SkBitmap and its ImageDigest.
*/
class BitmapAndDigest {
public:
explicit BitmapAndDigest(const SkBitmap &bitmap);
const SkBitmap *getBitmapPtr() const;
/**
* Returns a pointer to the ImageDigest.
*
* Since the hash is computed lazily within the ImageDigest object, we cannot mandate
* that it be held const.
*/
ImageDigest *getImageDigestPtr();
private:
const SkBitmap fBitmap;
ImageDigest fImageDigest;
};
/**
* Expected test result: expected image (if any), and whether we want to ignore failures on
* this test or not.
*
* This is just an ImageDigest (or lack thereof, if there is no expectation) and a boolean
* telling us whether to ignore failures.
*/
class Expectation {
public:
/**
* No expectation at all.
*/
explicit Expectation(bool ignoreFailure=kDefaultIgnoreFailure);
/**
* Expect an image, passed as hashType/hashValue.
*/
explicit Expectation(const SkString &hashType, uint64_t hashValue,
bool ignoreFailure=kDefaultIgnoreFailure);
/**
* Expect an image, passed as a bitmap.
*/
explicit Expectation(const SkBitmap& bitmap,
bool ignoreFailure=kDefaultIgnoreFailure);
/**
* Returns true iff we want to ignore failed expectations.
*/
bool ignoreFailure() const;
/**
* Returns true iff there are no allowed results.
*/
bool empty() const;
/**
* Returns true iff we are expecting a particular image, and imageDigest matches it.
*
* If empty() returns true, this will return false.
*
* If this expectation DOES contain an image, and imageDigest doesn't match it,
* this method will return false regardless of what ignoreFailure() would return.
* (The caller can check that separately.)
*/
bool matches(ImageDigest &imageDigest);
private:
static const bool kDefaultIgnoreFailure = false;
const bool fIsEmpty;
const bool fIgnoreFailure;
ImageDigest fImageDigest; // cannot be const, because it computes its hash lazily
};
/**
* Collects ImageDigests of actually rendered images, perhaps comparing to expectations.
*/
class ImageResultsAndExpectations {
public:
/**
* Adds expectations from a JSON file, returning true if successful.
*
* If the file exists but is empty, it succeeds, and there will be no expectations.
* If the file does not exist, this will fail.
*
* Reasoning:
* Generating expectations the first time can be a tricky chicken-and-egg
* proposition. "I need actual results to turn into expectations... but the only
* way to get actual results is to run the tool, and the tool won't run without
* expectations!"
* We could make the tool run even if there is no expectations file at all, but it's
* better for the tool to fail if the expectations file is not found--that will tell us
* quickly if files are not being copied around as they should be.
* Creating an empty file is an easy way to break the chicken-and-egg cycle and generate
* the first real expectations.
*/
bool readExpectationsFile(const char *jsonPath);
/**
* Adds this image to the summary of results.
*
* @param sourceName name of the source file that generated this result
* @param fileName relative path to the image output file on local disk
* @param digest description of the image's contents
* @param tileNumber if not NULL, pointer to tile number
*/
void add(const char *sourceName, const char *fileName, ImageDigest &digest,
const int *tileNumber=NULL);
/**
* Adds a key/value pair to the descriptions dict within the summary of results.
*
* @param key key within the descriptions dict
* @param value value to associate with that key
*/
void addDescription(const char *key, const char *value);
/**
* Adds the image base Google Storage URL to the summary of results.
*
* @param imageBaseGSUrl the image base Google Storage URL
*/
void setImageBaseGSUrl(const char *imageBaseGSUrl);
/**
* Returns the Expectation for this test.
*
* @param sourceName name of the source file that generated this result
* @param tileNumber if not NULL, pointer to tile number
*
* TODO(stephana): To make this work for GMs, we will need to add parameters for
* config, and maybe renderMode/builder?
*/
Expectation getExpectation(const char *sourceName, const int *tileNumber=NULL);
/**
* Writes the summary (as constructed so far) to a file.
*
* @param filename path to write the summary to
*/
void writeToFile(const char *filename) const;
private:
/**
* Read the file contents from filePtr and parse them into jsonRoot.
*
* It is up to the caller to close filePtr after this is done.
*
* Returns true if successful.
*/
static bool Parse(SkFILE* filePtr, Json::Value *jsonRoot);
Json::Value fActualResults;
Json::Value fDescriptions;
Json::Value fExpectedJsonRoot;
Json::Value fExpectedResults;
Json::Value fImageBaseGSUrl;
};
} // namespace sk_tools
#endif // image_expectations_DEFINED