BlobCache_test.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. /*
  2. ** Copyright 2011, The Android Open Source Project
  3. **
  4. ** Licensed under the Apache License, Version 2.0 (the "License");
  5. ** you may not use this file except in compliance with the License.
  6. ** You may obtain a copy of the License at
  7. **
  8. ** http://www.apache.org/licenses/LICENSE-2.0
  9. **
  10. ** Unless required by applicable law or agreed to in writing, software
  11. ** distributed under the License is distributed on an "AS IS" BASIS,
  12. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ** See the License for the specific language governing permissions and
  14. ** limitations under the License.
  15. */
  16. #include <fcntl.h>
  17. #include <stdio.h>
  18. #include <memory>
  19. #include <gtest/gtest.h>
  20. #include "BlobCache.h"
  21. namespace android {
  22. template<typename T> using sp = std::shared_ptr<T>;
  23. class BlobCacheTest : public ::testing::Test {
  24. protected:
  25. enum {
  26. OK = 0,
  27. BAD_VALUE = -EINVAL
  28. };
  29. enum {
  30. MAX_KEY_SIZE = 6,
  31. MAX_VALUE_SIZE = 8,
  32. MAX_TOTAL_SIZE = 13,
  33. };
  34. virtual void SetUp() {
  35. mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
  36. }
  37. virtual void TearDown() {
  38. mBC.reset();
  39. }
  40. std::unique_ptr<BlobCache> mBC;
  41. };
  42. TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
  43. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  44. mBC->set("abcd", 4, "efgh", 4);
  45. ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
  46. ASSERT_EQ('e', buf[0]);
  47. ASSERT_EQ('f', buf[1]);
  48. ASSERT_EQ('g', buf[2]);
  49. ASSERT_EQ('h', buf[3]);
  50. }
  51. TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
  52. unsigned char buf[2] = { 0xee, 0xee };
  53. mBC->set("ab", 2, "cd", 2);
  54. mBC->set("ef", 2, "gh", 2);
  55. ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
  56. ASSERT_EQ('c', buf[0]);
  57. ASSERT_EQ('d', buf[1]);
  58. ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
  59. ASSERT_EQ('g', buf[0]);
  60. ASSERT_EQ('h', buf[1]);
  61. }
  62. TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
  63. unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
  64. mBC->set("abcd", 4, "efgh", 4);
  65. ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
  66. ASSERT_EQ(0xee, buf[0]);
  67. ASSERT_EQ('e', buf[1]);
  68. ASSERT_EQ('f', buf[2]);
  69. ASSERT_EQ('g', buf[3]);
  70. ASSERT_EQ('h', buf[4]);
  71. ASSERT_EQ(0xee, buf[5]);
  72. }
  73. TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
  74. unsigned char buf[3] = { 0xee, 0xee, 0xee };
  75. mBC->set("abcd", 4, "efgh", 4);
  76. ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
  77. ASSERT_EQ(0xee, buf[0]);
  78. ASSERT_EQ(0xee, buf[1]);
  79. ASSERT_EQ(0xee, buf[2]);
  80. }
  81. TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
  82. mBC->set("abcd", 4, "efgh", 4);
  83. ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0));
  84. }
  85. TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
  86. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  87. mBC->set("abcd", 4, "efgh", 4);
  88. mBC->set("abcd", 4, "ijkl", 4);
  89. ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
  90. ASSERT_EQ('i', buf[0]);
  91. ASSERT_EQ('j', buf[1]);
  92. ASSERT_EQ('k', buf[2]);
  93. ASSERT_EQ('l', buf[3]);
  94. }
  95. TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
  96. unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
  97. mBC->set("abcd", 4, "efgh", 4);
  98. mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
  99. ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
  100. ASSERT_EQ('e', buf[0]);
  101. ASSERT_EQ('f', buf[1]);
  102. ASSERT_EQ('g', buf[2]);
  103. ASSERT_EQ('h', buf[3]);
  104. }
  105. TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
  106. char key[MAX_KEY_SIZE+1];
  107. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  108. for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
  109. key[i] = 'a';
  110. }
  111. mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
  112. ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
  113. ASSERT_EQ(0xee, buf[0]);
  114. ASSERT_EQ(0xee, buf[1]);
  115. ASSERT_EQ(0xee, buf[2]);
  116. ASSERT_EQ(0xee, buf[3]);
  117. }
  118. TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
  119. char buf[MAX_VALUE_SIZE+1];
  120. for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
  121. buf[i] = 'b';
  122. }
  123. mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
  124. for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
  125. buf[i] = 0xee;
  126. }
  127. ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
  128. for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
  129. SCOPED_TRACE(i);
  130. ASSERT_EQ(0xee, buf[i]);
  131. }
  132. }
  133. TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
  134. // Check a testing assumptions
  135. ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
  136. ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
  137. enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
  138. char key[MAX_KEY_SIZE];
  139. char buf[bufSize];
  140. for (int i = 0; i < MAX_KEY_SIZE; i++) {
  141. key[i] = 'a';
  142. }
  143. for (int i = 0; i < bufSize; i++) {
  144. buf[i] = 'b';
  145. }
  146. mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
  147. ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
  148. }
  149. TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
  150. char key[MAX_KEY_SIZE];
  151. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  152. for (int i = 0; i < MAX_KEY_SIZE; i++) {
  153. key[i] = 'a';
  154. }
  155. mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
  156. ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
  157. ASSERT_EQ('w', buf[0]);
  158. ASSERT_EQ('x', buf[1]);
  159. ASSERT_EQ('y', buf[2]);
  160. ASSERT_EQ('z', buf[3]);
  161. }
  162. TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
  163. char buf[MAX_VALUE_SIZE];
  164. for (int i = 0; i < MAX_VALUE_SIZE; i++) {
  165. buf[i] = 'b';
  166. }
  167. mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
  168. for (int i = 0; i < MAX_VALUE_SIZE; i++) {
  169. buf[i] = 0xee;
  170. }
  171. ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
  172. MAX_VALUE_SIZE));
  173. for (int i = 0; i < MAX_VALUE_SIZE; i++) {
  174. SCOPED_TRACE(i);
  175. ASSERT_EQ('b', buf[i]);
  176. }
  177. }
  178. TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
  179. // Check a testing assumption
  180. ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
  181. enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
  182. char key[MAX_KEY_SIZE];
  183. char buf[bufSize];
  184. for (int i = 0; i < MAX_KEY_SIZE; i++) {
  185. key[i] = 'a';
  186. }
  187. for (int i = 0; i < bufSize; i++) {
  188. buf[i] = 'b';
  189. }
  190. mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
  191. ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
  192. }
  193. TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
  194. unsigned char buf[1] = { 0xee };
  195. mBC->set("x", 1, "y", 1);
  196. ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
  197. ASSERT_EQ('y', buf[0]);
  198. }
  199. TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
  200. for (int i = 0; i < 256; i++) {
  201. uint8_t k = i;
  202. mBC->set(&k, 1, "x", 1);
  203. }
  204. int numCached = 0;
  205. for (int i = 0; i < 256; i++) {
  206. uint8_t k = i;
  207. if (mBC->get(&k, 1, nullptr, 0) == 1) {
  208. numCached++;
  209. }
  210. }
  211. ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
  212. }
  213. TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
  214. // Fill up the entire cache with 1 char key/value pairs.
  215. const int maxEntries = MAX_TOTAL_SIZE / 2;
  216. for (int i = 0; i < maxEntries; i++) {
  217. uint8_t k = i;
  218. mBC->set(&k, 1, "x", 1);
  219. }
  220. // Insert one more entry, causing a cache overflow.
  221. {
  222. uint8_t k = maxEntries;
  223. mBC->set(&k, 1, "x", 1);
  224. }
  225. // Count the number of entries in the cache.
  226. int numCached = 0;
  227. for (int i = 0; i < maxEntries+1; i++) {
  228. uint8_t k = i;
  229. if (mBC->get(&k, 1, nullptr, 0) == 1) {
  230. numCached++;
  231. }
  232. }
  233. ASSERT_EQ(maxEntries/2 + 1, numCached);
  234. }
  235. class BlobCacheFlattenTest : public BlobCacheTest {
  236. protected:
  237. virtual void SetUp() {
  238. BlobCacheTest::SetUp();
  239. mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
  240. }
  241. virtual void TearDown() {
  242. mBC2.reset();
  243. BlobCacheTest::TearDown();
  244. }
  245. void roundTrip() {
  246. size_t size = mBC->getFlattenedSize();
  247. uint8_t* flat = new uint8_t[size];
  248. ASSERT_EQ(OK, mBC->flatten(flat, size));
  249. ASSERT_EQ(OK, mBC2->unflatten(flat, size));
  250. delete[] flat;
  251. }
  252. sp<BlobCache> mBC2;
  253. };
  254. TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
  255. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  256. mBC->set("abcd", 4, "efgh", 4);
  257. roundTrip();
  258. ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
  259. ASSERT_EQ('e', buf[0]);
  260. ASSERT_EQ('f', buf[1]);
  261. ASSERT_EQ('g', buf[2]);
  262. ASSERT_EQ('h', buf[3]);
  263. }
  264. TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
  265. // Fill up the entire cache with 1 char key/value pairs.
  266. const int maxEntries = MAX_TOTAL_SIZE / 2;
  267. for (int i = 0; i < maxEntries; i++) {
  268. uint8_t k = i;
  269. mBC->set(&k, 1, &k, 1);
  270. }
  271. roundTrip();
  272. // Verify the deserialized cache
  273. for (int i = 0; i < maxEntries; i++) {
  274. uint8_t k = i;
  275. uint8_t v = 0xee;
  276. ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
  277. ASSERT_EQ(k, v);
  278. }
  279. }
  280. TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
  281. // Fill up the entire cache with 1 char key/value pairs.
  282. const int maxEntries = MAX_TOTAL_SIZE / 2;
  283. for (int i = 0; i < maxEntries; i++) {
  284. uint8_t k = i;
  285. mBC->set(&k, 1, &k, 1);
  286. }
  287. size_t size = mBC->getFlattenedSize();
  288. uint8_t* flat = new uint8_t[size];
  289. ASSERT_EQ(OK, mBC->flatten(flat, size));
  290. delete[] flat;
  291. // Verify the cache that we just serialized
  292. for (int i = 0; i < maxEntries; i++) {
  293. uint8_t k = i;
  294. uint8_t v = 0xee;
  295. ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
  296. ASSERT_EQ(k, v);
  297. }
  298. }
  299. TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
  300. // Fill up the entire cache with 1 char key/value pairs.
  301. const int maxEntries = MAX_TOTAL_SIZE / 2;
  302. for (int i = 0; i < maxEntries; i++) {
  303. uint8_t k = i;
  304. mBC->set(&k, 1, &k, 1);
  305. }
  306. size_t size = mBC->getFlattenedSize() - 1;
  307. uint8_t* flat = new uint8_t[size];
  308. // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
  309. // TODO: The above fails. I expect this is so because getFlattenedSize()
  310. // overstimates the size by using PROPERTY_VALUE_MAX.
  311. delete[] flat;
  312. }
  313. TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
  314. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  315. mBC->set("abcd", 4, "efgh", 4);
  316. size_t size = mBC->getFlattenedSize();
  317. uint8_t* flat = new uint8_t[size];
  318. ASSERT_EQ(OK, mBC->flatten(flat, size));
  319. flat[1] = ~flat[1];
  320. // Bad magic should cause an error.
  321. ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
  322. delete[] flat;
  323. // The error should cause the unflatten to result in an empty cache
  324. ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
  325. }
  326. TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
  327. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  328. mBC->set("abcd", 4, "efgh", 4);
  329. size_t size = mBC->getFlattenedSize();
  330. uint8_t* flat = new uint8_t[size];
  331. ASSERT_EQ(OK, mBC->flatten(flat, size));
  332. flat[5] = ~flat[5];
  333. // Version mismatches shouldn't cause errors, but should not use the
  334. // serialized entries
  335. ASSERT_EQ(OK, mBC2->unflatten(flat, size));
  336. delete[] flat;
  337. // The version mismatch should cause the unflatten to result in an empty
  338. // cache
  339. ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
  340. }
  341. TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
  342. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  343. mBC->set("abcd", 4, "efgh", 4);
  344. size_t size = mBC->getFlattenedSize();
  345. uint8_t* flat = new uint8_t[size];
  346. ASSERT_EQ(OK, mBC->flatten(flat, size));
  347. flat[10] = ~flat[10];
  348. // Version mismatches shouldn't cause errors, but should not use the
  349. // serialized entries
  350. ASSERT_EQ(OK, mBC2->unflatten(flat, size));
  351. delete[] flat;
  352. // The version mismatch should cause the unflatten to result in an empty
  353. // cache
  354. ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
  355. }
  356. TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
  357. unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
  358. mBC->set("abcd", 4, "efgh", 4);
  359. size_t size = mBC->getFlattenedSize();
  360. uint8_t* flat = new uint8_t[size];
  361. ASSERT_EQ(OK, mBC->flatten(flat, size));
  362. // A buffer truncation shouldt cause an error
  363. // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
  364. // TODO: The above appears to fail because getFlattenedSize() is
  365. // conservative.
  366. delete[] flat;
  367. // The error should cause the unflatten to result in an empty cache
  368. ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
  369. }
  370. } // namespace android