aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm/gm_expectations.h
diff options
context:
space:
mode:
authorGravatar epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-02-13 18:14:48 +0000
committerGravatar epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-02-13 18:14:48 +0000
commitd271d245e97da5fef642c0f106abe3fd948c2270 (patch)
treed5efb4dc98a15245fa743c0eba999aab6b8948b3 /gm/gm_expectations.h
parent73a9694b4ceb67547e5863db5315488e7d5294f7 (diff)
gm: Add ability to compare against checksums (as opposed to image files)
Review URL: https://codereview.appspot.com/7306071 git-svn-id: http://skia.googlecode.com/svn/trunk@7724 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'gm/gm_expectations.h')
-rw-r--r--gm/gm_expectations.h191
1 files changed, 184 insertions, 7 deletions
diff --git a/gm/gm_expectations.h b/gm/gm_expectations.h
index 07f0e60dbd..acff9d478b 100644
--- a/gm/gm_expectations.h
+++ b/gm/gm_expectations.h
@@ -10,9 +10,11 @@
#include "gm.h"
#include "SkBitmap.h"
#include "SkBitmapChecksummer.h"
+#include "SkData.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkRefCnt.h"
+#include "SkStream.h"
#include "SkTArray.h"
#ifdef SK_BUILD_FOR_WIN
@@ -21,11 +23,25 @@
#pragma warning(push)
#pragma warning(disable : 4530)
#endif
+#include "json/reader.h"
#include "json/value.h"
#ifdef SK_BUILD_FOR_WIN
#pragma warning(pop)
#endif
+#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message")
+
+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_ActualResults_AnyStatus_Checksum[] = "checksum";
+
+const static char kJsonKey_ExpectedResults[] = "expected-results";
+const static char kJsonKey_ExpectedResults_Checksums[] = "checksums";
+const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
+
namespace skiagm {
// The actual type we use to represent a checksum is hidden in here.
@@ -33,6 +49,9 @@ namespace skiagm {
static inline Json::Value asJsonValue(Checksum checksum) {
return checksum;
}
+ static inline Checksum asChecksum(Json::Value jsonValue) {
+ return jsonValue.asUInt64();
+ }
static SkString make_filename(const char path[],
const char renderModeDescriptor[],
@@ -54,27 +73,69 @@ namespace skiagm {
public:
/**
* No expectations at all.
- *
- * We set ignoreFailure to false by default, but it doesn't really
- * matter... the result will always be "no-comparison" anyway.
*/
- Expectations(bool ignoreFailure=false) {
+ Expectations(bool ignoreFailure=kDefaultIgnoreFailure) {
fIgnoreFailure = ignoreFailure;
}
/**
* Expect exactly one image (appropriate for the case when we
* are comparing against a single PNG file).
- *
- * By default, DO NOT ignore failures.
*/
- Expectations(const SkBitmap& bitmap, bool ignoreFailure=false) {
+ Expectations(const SkBitmap& bitmap, bool ignoreFailure=kDefaultIgnoreFailure) {
fBitmap = bitmap;
fIgnoreFailure = ignoreFailure;
fAllowedChecksums.push_back() = SkBitmapChecksummer::Compute64(bitmap);
}
/**
+ * Create Expectations from a JSON element as found within the
+ * kJsonKey_ExpectedResults section.
+ *
+ * It's fine if the jsonElement is null or empty; in that case, we just
+ * don't have any 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()) {
+ fprintf(stderr, "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 allowedChecksums = jsonElement[kJsonKey_ExpectedResults_Checksums];
+ if (allowedChecksums.isNull()) {
+ // ok, we'll just assume there aren't any expected checksums to compare against
+ } else if (!allowedChecksums.isArray()) {
+ fprintf(stderr, "found non-array json value for key '%s' in element '%s'\n",
+ kJsonKey_ExpectedResults_Checksums,
+ jsonElement.toStyledString().c_str());
+ DEBUGFAIL_SEE_STDERR;
+ } else {
+ for (Json::ArrayIndex i=0; i<allowedChecksums.size(); i++) {
+ Json::Value checksumElement = allowedChecksums[i];
+ if (!checksumElement.isIntegral()) {
+ fprintf(stderr, "found non-integer checksum in json element '%s'\n",
+ jsonElement.toStyledString().c_str());
+ DEBUGFAIL_SEE_STDERR;
+ } else {
+ fAllowedChecksums.push_back() = asChecksum(checksumElement);
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Returns true iff we want to ignore failed expectations.
*/
bool ignoreFailure() const { return this->fIgnoreFailure; }
@@ -126,6 +187,8 @@ namespace skiagm {
}
private:
+ const static bool kDefaultIgnoreFailure = false;
+
SkTArray<Checksum> fAllowedChecksums;
bool fIgnoreFailure;
SkBitmap fBitmap;
@@ -181,5 +244,119 @@ namespace skiagm {
const bool fNotifyOfMissingFiles;
};
+ /**
+ * Return Expectations based on JSON summary file.
+ */
+ class JsonExpectationsSource : public ExpectationsSource {
+ public:
+ /**
+ * Create an ExpectationsSource that will return Expectations based on
+ * a JSON file.
+ *
+ * jsonPath: path to JSON file to read
+ */
+ JsonExpectationsSource(const char *jsonPath) {
+ parse(jsonPath, &fJsonRoot);
+ fJsonExpectedResults = fJsonRoot[kJsonKey_ExpectedResults];
+ }
+
+ Expectations get(const char *testName) SK_OVERRIDE {
+ return Expectations(fJsonExpectedResults[testName]);
+ }
+
+ private:
+
+ /**
+ * Read as many bytes as possible (up to maxBytes) from the stream into
+ * an SkData object.
+ *
+ * If the returned SkData contains fewer than maxBytes, then EOF has been
+ * reached and no more data would be available from subsequent calls.
+ * (If EOF has already been reached, then this call will return an empty
+ * SkData object immediately.)
+ *
+ * If there are fewer than maxBytes bytes available to read from the
+ * stream, but the stream has not been closed yet, this call will block
+ * until there are enough bytes to read or the stream has been closed.
+ *
+ * It is up to the caller to call unref() on the returned SkData object
+ * once the data is no longer needed, so that the underlying buffer will
+ * be freed. For example:
+ *
+ * {
+ * size_t maxBytes = 256;
+ * SkAutoDataUnref dataRef(readIntoSkData(stream, maxBytes));
+ * if (NULL != dataRef.get()) {
+ * size_t bytesActuallyRead = dataRef.get()->size();
+ * // use the data...
+ * }
+ * }
+ * // underlying buffer has been freed, thanks to auto unref
+ *
+ */
+ // TODO(epoger): Move this, into SkStream.[cpp|h] as attempted in
+ // https://codereview.appspot.com/7300071 ?
+ // And maybe readFileIntoSkData() also?
+ static SkData* readIntoSkData(SkStream &stream, size_t maxBytes) {
+ if (0 == maxBytes) {
+ return SkData::NewEmpty();
+ }
+ char* bufStart = reinterpret_cast<char *>(sk_malloc_throw(maxBytes));
+ char* bufPtr = bufStart;
+ size_t bytesRemaining = maxBytes;
+ while (bytesRemaining > 0) {
+ size_t bytesReadThisTime = stream.read(bufPtr, bytesRemaining);
+ if (0 == bytesReadThisTime) {
+ break;
+ }
+ bytesRemaining -= bytesReadThisTime;
+ bufPtr += bytesReadThisTime;
+ }
+ return SkData::NewFromMalloc(bufStart, maxBytes - bytesRemaining);
+ }
+
+ /**
+ * Wrapper around readIntoSkData for files: reads the entire file into
+ * an SkData object.
+ */
+ static SkData* readFileIntoSkData(SkFILEStream &stream) {
+ return readIntoSkData(stream, stream.getLength());
+ }
+
+ /**
+ * Read the file contents from jsonPath and parse them into jsonRoot.
+ *
+ * Returns true if successful.
+ */
+ static bool parse(const char *jsonPath, Json::Value *jsonRoot) {
+ SkFILEStream inFile(jsonPath);
+ if (!inFile.isValid()) {
+ fprintf(stderr, "unable to read JSON file %s\n", jsonPath);
+ DEBUGFAIL_SEE_STDERR;
+ return false;
+ }
+
+ SkAutoDataUnref dataRef(readFileIntoSkData(inFile));
+ if (NULL == dataRef.get()) {
+ fprintf(stderr, "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)) {
+ fprintf(stderr, "error parsing JSON file %s\n", jsonPath);
+ DEBUGFAIL_SEE_STDERR;
+ return false;
+ }
+ return true;
+ }
+
+ Json::Value fJsonRoot;
+ Json::Value fJsonExpectedResults;
+ };
+
}
#endif