1 2 /* 3 * Copyright 2012 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "Test.h" 9 10 #include "SkBitmap.h" 11 #include "SkBitmapChecksummer.h" 12 #include "SkChecksum.h" 13 #include "SkCityHash.h" 14 #include "SkColor.h" 15 16 // Word size that is large enough to hold results of any checksum type. 17 typedef uint64_t checksum_result; 18 19 namespace skiatest { 20 class ChecksumTestClass : public Test { 21 public: Factory(void *)22 static Test* Factory(void*) {return SkNEW(ChecksumTestClass); } 23 protected: onGetName(SkString * name)24 virtual void onGetName(SkString* name) { name->set("Checksum"); } onRun(Reporter * reporter)25 virtual void onRun(Reporter* reporter) { 26 this->fReporter = reporter; 27 RunTest(); 28 } 29 private: 30 enum Algorithm { 31 kSkChecksum, 32 kSkCityHash32, 33 kSkCityHash64 34 }; 35 36 // Call Compute(data, size) on the appropriate checksum algorithm, 37 // depending on this->fWhichAlgorithm. ComputeChecksum(const char * data,size_t size)38 checksum_result ComputeChecksum(const char *data, size_t size) { 39 switch(fWhichAlgorithm) { 40 case kSkChecksum: 41 REPORTER_ASSERT_MESSAGE(fReporter, 42 reinterpret_cast<uintptr_t>(data) % 4 == 0, 43 "test data pointer is not 32-bit aligned"); 44 REPORTER_ASSERT_MESSAGE(fReporter, SkIsAlign4(size), 45 "test data size is not 32-bit aligned"); 46 return SkChecksum::Compute(reinterpret_cast<const uint32_t *>(data), size); 47 case kSkCityHash32: 48 return SkCityHash::Compute32(data, size); 49 case kSkCityHash64: 50 return SkCityHash::Compute64(data, size); 51 default: 52 SkString message("fWhichAlgorithm has unknown value "); 53 message.appendf("%d", fWhichAlgorithm); 54 fReporter->reportFailed(message); 55 } 56 // we never get here 57 return 0; 58 } 59 60 // Confirm that the checksum algorithm (specified by fWhichAlgorithm) 61 // generates the same results if called twice over the same data. TestChecksumSelfConsistency(size_t buf_size)62 void TestChecksumSelfConsistency(size_t buf_size) { 63 SkAutoMalloc storage(buf_size); 64 char* ptr = reinterpret_cast<char *>(storage.get()); 65 66 REPORTER_ASSERT(fReporter, 67 GetTestDataChecksum(8, 0) == 68 GetTestDataChecksum(8, 0)); 69 REPORTER_ASSERT(fReporter, 70 GetTestDataChecksum(8, 0) != 71 GetTestDataChecksum(8, 1)); 72 73 sk_bzero(ptr, buf_size); 74 checksum_result prev = 0; 75 76 // assert that as we change values (from 0 to non-zero) in 77 // our buffer, we get a different value 78 for (size_t i = 0; i < buf_size; ++i) { 79 ptr[i] = (i & 0x7f) + 1; // need some non-zero value here 80 81 // Try checksums of different-sized chunks, but always 82 // 32-bit aligned and big enough to contain all the 83 // nonzero bytes. (Remaining bytes will still be zero 84 // from the initial sk_bzero() call.) 85 size_t checksum_size = (((i/4)+1)*4); 86 REPORTER_ASSERT(fReporter, checksum_size <= buf_size); 87 88 checksum_result curr = ComputeChecksum(ptr, checksum_size); 89 REPORTER_ASSERT(fReporter, prev != curr); 90 checksum_result again = ComputeChecksum(ptr, checksum_size); 91 REPORTER_ASSERT(fReporter, again == curr); 92 prev = curr; 93 } 94 } 95 96 // Return the checksum of a buffer of bytes 'len' long. 97 // The pattern of values within the buffer will be consistent 98 // for every call, based on 'seed'. GetTestDataChecksum(size_t len,char seed=0)99 checksum_result GetTestDataChecksum(size_t len, char seed=0) { 100 SkAutoMalloc storage(len); 101 char* start = reinterpret_cast<char *>(storage.get()); 102 char* ptr = start; 103 for (size_t i = 0; i < len; ++i) { 104 *ptr++ = ((seed+i) & 0x7f); 105 } 106 checksum_result result = ComputeChecksum(start, len); 107 return result; 108 } 109 110 // Fill in bitmap with test data. CreateTestBitmap(SkBitmap & bitmap,SkBitmap::Config config,int width,int height,SkColor color)111 void CreateTestBitmap(SkBitmap &bitmap, SkBitmap::Config config, int width, int height, 112 SkColor color) { 113 bitmap.setConfig(config, width, height); 114 REPORTER_ASSERT(fReporter, bitmap.allocPixels()); 115 bitmap.setIsOpaque(true); 116 bitmap.eraseColor(color); 117 } 118 RunTest()119 void RunTest() { 120 // Test self-consistency of checksum algorithms. 121 fWhichAlgorithm = kSkChecksum; 122 TestChecksumSelfConsistency(128); 123 fWhichAlgorithm = kSkCityHash32; 124 TestChecksumSelfConsistency(128); 125 fWhichAlgorithm = kSkCityHash64; 126 TestChecksumSelfConsistency(128); 127 128 // Test checksum results that should be consistent across 129 // versions and platforms. 130 fWhichAlgorithm = kSkChecksum; 131 REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0); 132 fWhichAlgorithm = kSkCityHash32; 133 REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0xdc56d17a); 134 REPORTER_ASSERT(fReporter, GetTestDataChecksum(4) == 0x616e1132); 135 REPORTER_ASSERT(fReporter, GetTestDataChecksum(8) == 0xeb0fd2d6); 136 REPORTER_ASSERT(fReporter, GetTestDataChecksum(128) == 0x5321e430); 137 REPORTER_ASSERT(fReporter, GetTestDataChecksum(132) == 0x924a10e4); 138 REPORTER_ASSERT(fReporter, GetTestDataChecksum(256) == 0xd4de9dc9); 139 REPORTER_ASSERT(fReporter, GetTestDataChecksum(260) == 0xecf0325d); 140 fWhichAlgorithm = kSkCityHash64; 141 REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0x9ae16a3b2f90404fULL); 142 REPORTER_ASSERT(fReporter, GetTestDataChecksum(4) == 0x82bffd898958e540ULL); 143 REPORTER_ASSERT(fReporter, GetTestDataChecksum(8) == 0xad5a13e1e8e93b98ULL); 144 REPORTER_ASSERT(fReporter, GetTestDataChecksum(128) == 0x10b153630af1f395ULL); 145 REPORTER_ASSERT(fReporter, GetTestDataChecksum(132) == 0x7db71dc4adcc6647ULL); 146 REPORTER_ASSERT(fReporter, GetTestDataChecksum(256) == 0xeee763519b91b010ULL); 147 REPORTER_ASSERT(fReporter, GetTestDataChecksum(260) == 0x2fe19e0b2239bc23ULL); 148 149 // TODO: note the weakness exposed by these collisions... 150 // We need to improve the SkChecksum algorithm. 151 // We would prefer that these asserts FAIL! 152 // Filed as https://code.google.com/p/skia/issues/detail?id=981 153 // ('SkChecksum algorithm allows for way too many collisions') 154 fWhichAlgorithm = kSkChecksum; 155 REPORTER_ASSERT(fReporter, 156 GetTestDataChecksum(128) == GetTestDataChecksum(256)); 157 REPORTER_ASSERT(fReporter, 158 GetTestDataChecksum(132) == GetTestDataChecksum(260)); 159 160 // Test SkBitmapChecksummer 161 SkBitmap bitmap; 162 // initial test case 163 CreateTestBitmap(bitmap, SkBitmap::kARGB_8888_Config, 333, 555, SK_ColorBLUE); 164 REPORTER_ASSERT(fReporter, 165 SkBitmapChecksummer::Compute64(bitmap) == 0x18f9df68b1b02f38ULL); 166 // same pixel data but different dimensions should yield a different checksum 167 CreateTestBitmap(bitmap, SkBitmap::kARGB_8888_Config, 555, 333, SK_ColorBLUE); 168 REPORTER_ASSERT(fReporter, 169 SkBitmapChecksummer::Compute64(bitmap) == 0x6b0298183f786c8eULL); 170 // same dimensions but different color should yield a different checksum 171 CreateTestBitmap(bitmap, SkBitmap::kARGB_8888_Config, 555, 333, SK_ColorGREEN); 172 REPORTER_ASSERT(fReporter, 173 SkBitmapChecksummer::Compute64(bitmap) == 0xc6b4b3f6fadaaf37ULL); 174 // same pixel colors in a different config should yield the same checksum 175 CreateTestBitmap(bitmap, SkBitmap::kARGB_4444_Config, 555, 333, SK_ColorGREEN); 176 REPORTER_ASSERT(fReporter, 177 SkBitmapChecksummer::Compute64(bitmap) == 0xc6b4b3f6fadaaf37ULL); 178 } 179 180 Reporter* fReporter; 181 Algorithm fWhichAlgorithm; 182 }; 183 184 static TestRegistry gReg(ChecksumTestClass::Factory); 185 } 186