| epoger@google.com | 3726960 | 2013-01-19 04:21:27 +0000 | [diff] [blame^] | 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 | #ifndef gm_expectations_DEFINED |
| 8 | #define gm_expectations_DEFINED |
| 9 | |
| 10 | #include "gm.h" |
| 11 | #include "SkBitmapChecksummer.h" |
| 12 | #include "SkImageDecoder.h" |
| 13 | #include "SkOSFile.h" |
| 14 | #include "SkRefCnt.h" |
| 15 | #include "SkTArray.h" |
| 16 | |
| 17 | #ifdef SK_BUILD_FOR_WIN |
| 18 | // json includes xlocale which generates warning 4530 because we're compiling without |
| 19 | // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067 |
| 20 | #pragma warning(push) |
| 21 | #pragma warning(disable : 4530) |
| 22 | #endif |
| 23 | #include "json/value.h" |
| 24 | #ifdef SK_BUILD_FOR_WIN |
| 25 | #pragma warning(pop) |
| 26 | #endif |
| 27 | |
| 28 | namespace skiagm { |
| 29 | |
| 30 | // The actual type we use to represent a checksum is hidden in here. |
| 31 | typedef Json::UInt64 Checksum; |
| 32 | static inline Json::Value asJsonValue(Checksum checksum) { |
| 33 | return checksum; |
| 34 | } |
| 35 | |
| 36 | static SkString make_filename(const char path[], |
| 37 | const char renderModeDescriptor[], |
| 38 | const char *name, |
| 39 | const char suffix[]) { |
| 40 | SkString filename(path); |
| 41 | if (filename.endsWith(SkPATH_SEPARATOR)) { |
| 42 | filename.remove(filename.size() - 1, 1); |
| 43 | } |
| 44 | filename.appendf("%c%s%s.%s", SkPATH_SEPARATOR, |
| 45 | name, renderModeDescriptor, suffix); |
| 46 | return filename; |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Test expectations (allowed image checksums, etc.) |
| 51 | */ |
| 52 | class Expectations { |
| 53 | public: |
| 54 | /** |
| 55 | * No expectations at all. |
| 56 | * |
| 57 | * We set ignoreFailure to false by default, but it doesn't really |
| 58 | * matter... the result will always be "no-comparison" anyway. |
| 59 | */ |
| 60 | Expectations(bool ignoreFailure=false) { |
| 61 | fIgnoreFailure = ignoreFailure; |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * Allow exactly one checksum (appropriate for the case when we |
| 66 | * are comparing against a single PNG file). |
| 67 | * |
| 68 | * By default, DO NOT ignore failures. |
| 69 | */ |
| 70 | Expectations(Checksum singleChecksum, bool ignoreFailure=false) { |
| 71 | fIgnoreFailure = ignoreFailure; |
| 72 | fAllowedChecksums.push_back() = singleChecksum; |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Returns true iff we want to ignore failed expectations. |
| 77 | */ |
| 78 | bool ignoreFailure() const { return this->fIgnoreFailure; } |
| 79 | |
| 80 | /** |
| 81 | * Returns true iff there are no allowed checksums. |
| 82 | */ |
| 83 | bool empty() const { return this->fAllowedChecksums.empty(); } |
| 84 | |
| 85 | /** |
| 86 | * Returns true iff actualChecksum matches any allowedChecksum, |
| 87 | * regardless of fIgnoreFailure. (The caller can check |
| 88 | * that separately.) |
| 89 | */ |
| 90 | bool match(Checksum actualChecksum) const { |
| 91 | for (int i=0; i < this->fAllowedChecksums.count(); i++) { |
| 92 | Checksum allowedChecksum = this->fAllowedChecksums[i]; |
| 93 | if (allowedChecksum == actualChecksum) { |
| 94 | return true; |
| 95 | } |
| 96 | } |
| 97 | return false; |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Return a JSON representation of the allowed checksums. |
| 102 | * This does NOT include any information about whether to |
| 103 | * ignore failures. |
| 104 | */ |
| 105 | Json::Value allowedChecksumsAsJson() const { |
| 106 | Json::Value allowedChecksumArray; |
| 107 | if (!this->fAllowedChecksums.empty()) { |
| 108 | for (int i=0; i < this->fAllowedChecksums.count(); i++) { |
| 109 | Checksum allowedChecksum = this->fAllowedChecksums[i]; |
| 110 | allowedChecksumArray.append(asJsonValue(allowedChecksum)); |
| 111 | } |
| 112 | } |
| 113 | return allowedChecksumArray; |
| 114 | } |
| 115 | |
| 116 | private: |
| 117 | SkTArray<Checksum> fAllowedChecksums; |
| 118 | bool fIgnoreFailure; |
| 119 | }; |
| 120 | |
| 121 | /** |
| 122 | * Abstract source of Expectations objects for individual tests. |
| 123 | */ |
| 124 | class ExpectationsSource : public SkRefCnt { |
| 125 | public: |
| 126 | virtual Expectations get(const char *testName) = 0; |
| 127 | }; |
| 128 | |
| 129 | /** |
| 130 | * Return Expectations based on individual image files on disk. |
| 131 | */ |
| 132 | class IndividualImageExpectationsSource : public ExpectationsSource { |
| 133 | public: |
| 134 | /** |
| 135 | * Create an ExpectationsSource that will return Expectations based on |
| 136 | * image files found within rootDir. |
| 137 | * |
| 138 | * rootDir: directory under which to look for image files |
| 139 | * (this string will be copied to storage within this object) |
| 140 | * notifyOfMissingFiles: whether to log a message to stderr if an image |
| 141 | * file cannot be found |
| 142 | */ |
| 143 | IndividualImageExpectationsSource(const char *rootDir, |
| 144 | bool notifyOfMissingFiles) : |
| 145 | fRootDir(rootDir), fNotifyOfMissingFiles(notifyOfMissingFiles) {} |
| 146 | |
| 147 | Expectations get(const char *testName) SK_OVERRIDE { |
| 148 | SkString path = make_filename(fRootDir.c_str(), "", testName, |
| 149 | "png"); |
| 150 | SkBitmap referenceBitmap; |
| 151 | bool decodedReferenceBitmap = |
| 152 | SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap, |
| 153 | SkBitmap::kARGB_8888_Config, |
| 154 | SkImageDecoder::kDecodePixels_Mode, |
| 155 | NULL); |
| 156 | if (decodedReferenceBitmap) { |
| 157 | Checksum checksum = SkBitmapChecksummer::Compute64( |
| 158 | referenceBitmap); |
| 159 | return Expectations(checksum); |
| 160 | } else { |
| 161 | if (fNotifyOfMissingFiles) { |
| 162 | fprintf(stderr, "FAILED to read %s\n", path.c_str()); |
| 163 | } |
| 164 | return Expectations(); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | private: |
| 169 | const SkString fRootDir; |
| 170 | const bool fNotifyOfMissingFiles; |
| 171 | }; |
| 172 | |
| 173 | } |
| 174 | #endif |