OLD | NEW |
---|---|
1 | |
2 /* | 1 /* |
3 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
4 * | 3 * |
5 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 5 * found in the LICENSE file. |
7 */ | 6 */ |
8 #include "Test.h" | |
9 | 7 |
10 #include "SkChecksum.h" | 8 #include "SkChecksum.h" |
9 #include "SkRandom.h" | |
10 #include "Test.h" | |
11 #include "TestClassDef.h" | |
11 | 12 |
12 // Word size that is large enough to hold results of any checksum type. | 13 // Murmur3 has an optional third seed argument, so we wrap it to fit a uniform t ype. |
13 typedef uint64_t checksum_result; | 14 static uint32_t murmur_noseed(const uint32_t* d, size_t l) { return SkChecksum:: Murmur3(d, l); } |
14 | 15 |
15 namespace skiatest { | 16 #define ASSERT(x) REPORTER_ASSERT(r, x) |
16 class ChecksumTestClass : public Test { | |
17 public: | |
18 static Test* Factory(void*) {return SkNEW(ChecksumTestClass); } | |
19 protected: | |
20 virtual void onGetName(SkString* name) { name->set("Checksum"); } | |
21 virtual void onRun(Reporter* reporter) { | |
22 this->fReporter = reporter; | |
23 RunTest(); | |
24 } | |
25 private: | |
26 enum Algorithm { | |
27 kSkChecksum, | |
28 kMurmur3, | |
29 }; | |
30 | 17 |
31 // Call Compute(data, size) on the appropriate checksum algorithm, | 18 DEF_TEST(Checksum, r) { |
32 // depending on this->fWhichAlgorithm. | 19 // Algorithms to test. They're currently all uint32_t(const uint32_t*, size _t). |
33 checksum_result ComputeChecksum(const char *data, size_t size) { | 20 uint32_t(*kAlgorithms[])(const uint32_t*, size_t) = { |
tfarina
2014/01/08 18:32:40
array of pointers to functions, the beauty of C :)
mtklein
2014/01/08 18:41:55
Yeah, this always takes me forever to remember how
bungeman-skia
2014/01/09 16:08:33
I know that doing this inline is awesome and all,
mtklein
2014/01/09 16:21:46
Done.
| |
34 switch(fWhichAlgorithm) { | 21 &SkChecksum::Compute, |
35 case kSkChecksum: | 22 &murmur_noseed, |
36 REPORTER_ASSERT_MESSAGE(fReporter, | |
37 reinterpret_cast<uintptr_t>(data) % 4 == 0, | |
38 "test data pointer is not 32-bit aligned "); | |
39 REPORTER_ASSERT_MESSAGE(fReporter, SkIsAlign4(size), | |
40 "test data size is not 32-bit aligned"); | |
41 return SkChecksum::Compute(reinterpret_cast<const uint32_t *>(da ta), size); | |
42 case kMurmur3: | |
43 REPORTER_ASSERT_MESSAGE(fReporter, | |
44 reinterpret_cast<uintptr_t>(data) % 4 == 0, | |
45 "test data pointer is not 32-bit aligned "); | |
46 REPORTER_ASSERT_MESSAGE(fReporter, SkIsAlign4(size), | |
47 "test data size is not 32-bit aligned"); | |
48 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t *>(da ta), size); | |
49 default: | |
50 SkString message("fWhichAlgorithm has unknown value "); | |
51 message.appendf("%d", fWhichAlgorithm); | |
52 fReporter->reportFailed(message); | |
53 } | |
54 // we never get here | |
55 return 0; | |
56 } | |
57 | |
58 // Confirm that the checksum algorithm (specified by fWhichAlgorithm) | |
59 // generates the same results if called twice over the same data. | |
60 void TestChecksumSelfConsistency(size_t buf_size) { | |
61 SkAutoMalloc storage(buf_size); | |
62 char* ptr = reinterpret_cast<char *>(storage.get()); | |
63 | |
64 REPORTER_ASSERT(fReporter, | |
65 GetTestDataChecksum(8, 0) == | |
66 GetTestDataChecksum(8, 0)); | |
67 REPORTER_ASSERT(fReporter, | |
68 GetTestDataChecksum(8, 0) != | |
69 GetTestDataChecksum(8, 1)); | |
70 | |
71 sk_bzero(ptr, buf_size); | |
72 checksum_result prev = 0; | |
73 | |
74 // assert that as we change values (from 0 to non-zero) in | |
75 // our buffer, we get a different value | |
76 for (size_t i = 0; i < buf_size; ++i) { | |
77 ptr[i] = (i & 0x7f) + 1; // need some non-zero value here | |
78 | |
79 // Try checksums of different-sized chunks, but always | |
80 // 32-bit aligned and big enough to contain all the | |
81 // nonzero bytes. (Remaining bytes will still be zero | |
82 // from the initial sk_bzero() call.) | |
83 size_t checksum_size = (((i/4)+1)*4); | |
84 REPORTER_ASSERT(fReporter, checksum_size <= buf_size); | |
85 | |
86 checksum_result curr = ComputeChecksum(ptr, checksum_size); | |
87 REPORTER_ASSERT(fReporter, prev != curr); | |
88 checksum_result again = ComputeChecksum(ptr, checksum_size); | |
89 REPORTER_ASSERT(fReporter, again == curr); | |
90 prev = curr; | |
91 } | |
92 } | |
93 | |
94 // Return the checksum of a buffer of bytes 'len' long. | |
95 // The pattern of values within the buffer will be consistent | |
96 // for every call, based on 'seed'. | |
97 checksum_result GetTestDataChecksum(size_t len, char seed=0) { | |
98 SkAutoMalloc storage(len); | |
99 char* start = reinterpret_cast<char *>(storage.get()); | |
100 char* ptr = start; | |
101 for (size_t i = 0; i < len; ++i) { | |
102 *ptr++ = ((seed+i) & 0x7f); | |
103 } | |
104 checksum_result result = ComputeChecksum(start, len); | |
105 return result; | |
106 } | |
107 | |
108 void RunTest() { | |
109 const Algorithm algorithms[] = { kSkChecksum, kMurmur3 }; | |
110 for (size_t i = 0; i < SK_ARRAY_COUNT(algorithms); i++) { | |
111 fWhichAlgorithm = algorithms[i]; | |
112 | |
113 // Test self-consistency of checksum algorithms. | |
114 TestChecksumSelfConsistency(128); | |
115 | |
116 // Test checksum results that should be consistent across | |
117 // versions and platforms. | |
118 REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0); | |
119 | |
120 const bool colision1 = GetTestDataChecksum(128) == GetTestDataCh ecksum(256); | |
121 const bool colision2 = GetTestDataChecksum(132) == GetTestDataCh ecksum(260); | |
122 if (fWhichAlgorithm == kSkChecksum) { | |
123 // TODO: note the weakness exposed by these collisions... | |
124 // We need to improve the SkChecksum algorithm. | |
125 // We would prefer that these asserts FAIL! | |
126 // Filed as https://code.google.com/p/skia/issues/detail?id= 981 | |
127 // ('SkChecksum algorithm allows for way too many collisions ') | |
128 REPORTER_ASSERT(fReporter, colision1); | |
129 REPORTER_ASSERT(fReporter, colision2); | |
130 } else { | |
131 REPORTER_ASSERT(fReporter, !colision1); | |
132 REPORTER_ASSERT(fReporter, !colision2); | |
133 } | |
134 } | |
135 } | |
136 | |
137 Reporter* fReporter; | |
138 Algorithm fWhichAlgorithm; | |
139 }; | 23 }; |
140 | 24 |
141 static TestRegistry gReg(ChecksumTestClass::Factory); | 25 // Put 128 random bytes into two identical buffers. Any multiple of 4 will do. |
26 const size_t kBytes = 128; | |
27 SkRandom rand; | |
28 uint32_t data[kBytes/4], tweaked[kBytes/4]; | |
29 for (size_t i = 0; i < SK_ARRAY_COUNT(tweaked); i++) data[i] = tweaked[i] = rand.nextU(); | |
tfarina
2014/01/08 18:32:40
ok, you put random numbers in an array of 32 eleme
bungeman-skia
2014/01/09 16:08:33
The magic of putting this all on one line is almos
mtklein
2014/01/09 16:21:46
Done.
| |
30 | |
31 // Test each algorithm. | |
32 for (size_t i = 0; i < SK_ARRAY_COUNT(kAlgorithms); i++) { | |
tfarina
2014/01/08 18:32:40
then you iterate through the array of function poi
bungeman-skia
2014/01/09 16:08:33
Totally ocd, but the post increment makes me wince
mtklein
2014/01/09 16:21:46
Done.
| |
33 // Hash of NULL is always 0. | |
34 ASSERT(kAlgorithms[i](NULL, 0) == 0); | |
bungeman-skia
2014/01/09 16:08:33
There's a lot of kAlgorithms[i] going on here, can
mtklein
2014/01/09 16:21:46
Done.
| |
35 | |
36 const uint32_t hash = kAlgorithms[i](data, kBytes); | |
tfarina
2014/01/08 18:32:40
so you call the algorithm to hash the data and sto
| |
37 // Should be deterministic. | |
38 ASSERT(hash == kAlgorithms[i](data, kBytes)); | |
tfarina
2014/01/08 18:32:40
make sure it will always return the same hash for
| |
39 | |
40 // Changing any one element should change the hash. | |
tfarina
2014/01/08 18:32:40
"any one element", that is hard to translate to my
mtklein
2014/01/08 18:41:55
"any single element"? The idea is that we have [
| |
41 for (size_t j = 0; j < SK_ARRAY_COUNT(tweaked); j++) { | |
42 const uint32_t saved = tweaked[j]; | |
43 tweaked[j] = rand.nextU(); | |
tfarina
2014/01/08 18:32:40
ok, so here you are changing the data you have pre
| |
44 const uint32_t tweakedHash = kAlgorithms[i](tweaked, kBytes); | |
tfarina
2014/01/08 18:32:40
then you call the algorithm again, to obtain a new
| |
45 ASSERT(tweakedHash != hash); | |
tfarina
2014/01/08 18:32:40
and it should be different for the original hash,
| |
46 ASSERT(tweakedHash == kAlgorithms[i](tweaked, kBytes)); | |
tfarina
2014/01/08 18:32:40
then you hash again to make sure the algorithm is
| |
47 tweaked[j] = saved; | |
tfarina
2014/01/08 18:32:40
and restore the original value (that part I didn't
mtklein
2014/01/08 18:41:55
Not strictly necessary, but we want to make sure w
| |
48 } | |
49 } | |
142 } | 50 } |
OLD | NEW |