blob: 8e27f5a738f7eb17d9619a5094fa6b87230bdfc9 [file] [log] [blame]
Cody Northrop6cca6c22023-02-08 20:23:13 -07001/*
2 ** Copyright 2023, 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
17#include "MultifileBlobCache.h"
18
19#include <android-base/test_utils.h>
20#include <fcntl.h>
21#include <gtest/gtest.h>
22#include <stdio.h>
23
24#include <memory>
25
26namespace android {
27
28template <typename T>
29using sp = std::shared_ptr<T>;
30
Cody Northrop5dbcfa72023-03-24 15:34:09 -060031constexpr size_t kMaxKeySize = 2 * 1024;
32constexpr size_t kMaxValueSize = 6 * 1024;
Cody Northrop6cca6c22023-02-08 20:23:13 -070033constexpr size_t kMaxTotalSize = 32 * 1024;
Cody Northropb5267032023-10-24 10:11:21 -060034constexpr size_t kMaxTotalEntries = 64;
Cody Northrop6cca6c22023-02-08 20:23:13 -070035
36class MultifileBlobCacheTest : public ::testing::Test {
37protected:
38 virtual void SetUp() {
39 mTempFile.reset(new TemporaryFile());
Cody Northrop5dbcfa72023-03-24 15:34:09 -060040 mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize,
Cody Northropb5267032023-10-24 10:11:21 -060041 kMaxTotalEntries, &mTempFile->path[0]));
Cody Northrop6cca6c22023-02-08 20:23:13 -070042 }
43
44 virtual void TearDown() { mMBC.reset(); }
45
Cody Northrop5f8117a2023-09-26 20:48:59 -060046 int getFileDescriptorCount();
47
Cody Northrop6cca6c22023-02-08 20:23:13 -070048 std::unique_ptr<TemporaryFile> mTempFile;
49 std::unique_ptr<MultifileBlobCache> mMBC;
50};
51
52TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) {
53 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
54 mMBC->set("abcd", 4, "efgh", 4);
55 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 4));
56 ASSERT_EQ('e', buf[0]);
57 ASSERT_EQ('f', buf[1]);
58 ASSERT_EQ('g', buf[2]);
59 ASSERT_EQ('h', buf[3]);
60}
61
62TEST_F(MultifileBlobCacheTest, CacheTwoValuesSucceeds) {
63 unsigned char buf[2] = {0xee, 0xee};
64 mMBC->set("ab", 2, "cd", 2);
65 mMBC->set("ef", 2, "gh", 2);
66 ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
67 ASSERT_EQ('c', buf[0]);
68 ASSERT_EQ('d', buf[1]);
69 ASSERT_EQ(size_t(2), mMBC->get("ef", 2, buf, 2));
70 ASSERT_EQ('g', buf[0]);
71 ASSERT_EQ('h', buf[1]);
72}
73
74TEST_F(MultifileBlobCacheTest, GetSetTwiceSucceeds) {
75 unsigned char buf[2] = {0xee, 0xee};
76 mMBC->set("ab", 2, "cd", 2);
77 ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
78 ASSERT_EQ('c', buf[0]);
79 ASSERT_EQ('d', buf[1]);
80 // Use the same key, but different value
81 mMBC->set("ab", 2, "ef", 2);
82 ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
83 ASSERT_EQ('e', buf[0]);
84 ASSERT_EQ('f', buf[1]);
85}
86
87TEST_F(MultifileBlobCacheTest, GetOnlyWritesInsideBounds) {
88 unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
89 mMBC->set("abcd", 4, "efgh", 4);
90 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf + 1, 4));
91 ASSERT_EQ(0xee, buf[0]);
92 ASSERT_EQ('e', buf[1]);
93 ASSERT_EQ('f', buf[2]);
94 ASSERT_EQ('g', buf[3]);
95 ASSERT_EQ('h', buf[4]);
96 ASSERT_EQ(0xee, buf[5]);
97}
98
99TEST_F(MultifileBlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
100 unsigned char buf[3] = {0xee, 0xee, 0xee};
101 mMBC->set("abcd", 4, "efgh", 4);
102 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 3));
103 ASSERT_EQ(0xee, buf[0]);
104 ASSERT_EQ(0xee, buf[1]);
105 ASSERT_EQ(0xee, buf[2]);
106}
107
108TEST_F(MultifileBlobCacheTest, GetDoesntAccessNullBuffer) {
109 mMBC->set("abcd", 4, "efgh", 4);
110 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, nullptr, 0));
111}
112
113TEST_F(MultifileBlobCacheTest, MultipleSetsCacheLatestValue) {
114 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
115 mMBC->set("abcd", 4, "efgh", 4);
116 mMBC->set("abcd", 4, "ijkl", 4);
117 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 4));
118 ASSERT_EQ('i', buf[0]);
119 ASSERT_EQ('j', buf[1]);
120 ASSERT_EQ('k', buf[2]);
121 ASSERT_EQ('l', buf[3]);
122}
123
124TEST_F(MultifileBlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
125 unsigned char buf[kMaxValueSize + 1] = {0xee, 0xee, 0xee, 0xee};
126 mMBC->set("abcd", 4, "efgh", 4);
127 mMBC->set("abcd", 4, buf, kMaxValueSize + 1);
128 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 4));
129 ASSERT_EQ('e', buf[0]);
130 ASSERT_EQ('f', buf[1]);
131 ASSERT_EQ('g', buf[2]);
132 ASSERT_EQ('h', buf[3]);
133}
134
135TEST_F(MultifileBlobCacheTest, DoesntCacheIfKeyIsTooBig) {
136 char key[kMaxKeySize + 1];
137 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
138 for (int i = 0; i < kMaxKeySize + 1; i++) {
139 key[i] = 'a';
140 }
141 mMBC->set(key, kMaxKeySize + 1, "bbbb", 4);
142 ASSERT_EQ(size_t(0), mMBC->get(key, kMaxKeySize + 1, buf, 4));
143 ASSERT_EQ(0xee, buf[0]);
144 ASSERT_EQ(0xee, buf[1]);
145 ASSERT_EQ(0xee, buf[2]);
146 ASSERT_EQ(0xee, buf[3]);
147}
148
149TEST_F(MultifileBlobCacheTest, DoesntCacheIfValueIsTooBig) {
150 char buf[kMaxValueSize + 1];
151 for (int i = 0; i < kMaxValueSize + 1; i++) {
152 buf[i] = 'b';
153 }
154 mMBC->set("abcd", 4, buf, kMaxValueSize + 1);
155 for (int i = 0; i < kMaxValueSize + 1; i++) {
156 buf[i] = 0xee;
157 }
158 ASSERT_EQ(size_t(0), mMBC->get("abcd", 4, buf, kMaxValueSize + 1));
159 for (int i = 0; i < kMaxValueSize + 1; i++) {
160 SCOPED_TRACE(i);
161 ASSERT_EQ(0xee, buf[i]);
162 }
163}
164
165TEST_F(MultifileBlobCacheTest, CacheMaxKeySizeSucceeds) {
166 char key[kMaxKeySize];
167 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
168 for (int i = 0; i < kMaxKeySize; i++) {
169 key[i] = 'a';
170 }
171 mMBC->set(key, kMaxKeySize, "wxyz", 4);
172 ASSERT_EQ(size_t(4), mMBC->get(key, kMaxKeySize, buf, 4));
173 ASSERT_EQ('w', buf[0]);
174 ASSERT_EQ('x', buf[1]);
175 ASSERT_EQ('y', buf[2]);
176 ASSERT_EQ('z', buf[3]);
177}
178
179TEST_F(MultifileBlobCacheTest, CacheMaxValueSizeSucceeds) {
180 char buf[kMaxValueSize];
181 for (int i = 0; i < kMaxValueSize; i++) {
182 buf[i] = 'b';
183 }
184 mMBC->set("abcd", 4, buf, kMaxValueSize);
185 for (int i = 0; i < kMaxValueSize; i++) {
186 buf[i] = 0xee;
187 }
188 mMBC->get("abcd", 4, buf, kMaxValueSize);
189 for (int i = 0; i < kMaxValueSize; i++) {
190 SCOPED_TRACE(i);
191 ASSERT_EQ('b', buf[i]);
192 }
193}
194
Cody Northropbe163732023-03-22 10:14:26 -0600195TEST_F(MultifileBlobCacheTest, CacheMaxKeyAndValueSizeSucceeds) {
196 char key[kMaxKeySize];
197 for (int i = 0; i < kMaxKeySize; i++) {
198 key[i] = 'a';
199 }
200 char buf[kMaxValueSize];
201 for (int i = 0; i < kMaxValueSize; i++) {
202 buf[i] = 'b';
203 }
204 mMBC->set(key, kMaxKeySize, buf, kMaxValueSize);
205 for (int i = 0; i < kMaxValueSize; i++) {
206 buf[i] = 0xee;
207 }
208 mMBC->get(key, kMaxKeySize, buf, kMaxValueSize);
209 for (int i = 0; i < kMaxValueSize; i++) {
210 SCOPED_TRACE(i);
211 ASSERT_EQ('b', buf[i]);
212 }
213}
214
Cody Northropb5267032023-10-24 10:11:21 -0600215TEST_F(MultifileBlobCacheTest, CacheMaxEntrySucceeds) {
216 // Fill the cache with max entries
217 int i = 0;
218 for (i = 0; i < kMaxTotalEntries; i++) {
219 mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
220 }
221
222 // Ensure it is full
223 ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries);
224
225 // Add another entry
226 mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
227
228 // Ensure total entries is cut in half + 1
229 ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries / 2 + 1);
230}
231
Cody Northrop6cca6c22023-02-08 20:23:13 -0700232TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
233 unsigned char buf[1] = {0xee};
234 mMBC->set("x", 1, "y", 1);
235 ASSERT_EQ(size_t(1), mMBC->get("x", 1, buf, 1));
236 ASSERT_EQ('y', buf[0]);
237}
238
Cody Northrop5f8117a2023-09-26 20:48:59 -0600239int MultifileBlobCacheTest::getFileDescriptorCount() {
240 DIR* directory = opendir("/proc/self/fd");
241
242 int fileCount = 0;
243 struct dirent* entry;
244 while ((entry = readdir(directory)) != NULL) {
245 fileCount++;
246 // printf("File: %s\n", entry->d_name);
247 }
248
249 closedir(directory);
250 return fileCount;
251}
252
253TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
254 // Populate the cache with a bunch of entries
Cody Northropb5267032023-10-24 10:11:21 -0600255 for (int i = 0; i < kMaxTotalEntries; i++) {
Cody Northrop5f8117a2023-09-26 20:48:59 -0600256 // printf("Caching: %i", i);
257
258 // Use the index as the key and value
259 mMBC->set(&i, sizeof(i), &i, sizeof(i));
260
261 int result = 0;
262 ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
263 ASSERT_EQ(i, result);
264 }
265
266 // Ensure we don't have a bunch of open fds
Cody Northropb5267032023-10-24 10:11:21 -0600267 ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
Cody Northrop5f8117a2023-09-26 20:48:59 -0600268
269 // Close the cache so everything writes out
270 mMBC->finish();
271 mMBC.reset();
272
273 // Now open it again and ensure we still don't have a bunch of open fds
Cody Northropb5267032023-10-24 10:11:21 -0600274 mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
275 &mTempFile->path[0]));
Cody Northrop5f8117a2023-09-26 20:48:59 -0600276
277 // Check after initialization
Cody Northropb5267032023-10-24 10:11:21 -0600278 ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
Cody Northrop5f8117a2023-09-26 20:48:59 -0600279
Cody Northropb5267032023-10-24 10:11:21 -0600280 for (int i = 0; i < kMaxTotalEntries; i++) {
Cody Northrop5f8117a2023-09-26 20:48:59 -0600281 int result = 0;
282 ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
283 ASSERT_EQ(i, result);
284 }
285
286 // And again after we've actually used it
Cody Northropb5267032023-10-24 10:11:21 -0600287 ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
Cody Northrop5f8117a2023-09-26 20:48:59 -0600288}
289
Cody Northrop6cca6c22023-02-08 20:23:13 -0700290} // namespace android