blob: 1639be64806187c01eb749721bc0fa17b100df7c [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 Northrop6cca6c22023-02-08 20:23:13 -070034
35class MultifileBlobCacheTest : public ::testing::Test {
36protected:
37 virtual void SetUp() {
38 mTempFile.reset(new TemporaryFile());
Cody Northrop5dbcfa72023-03-24 15:34:09 -060039 mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize,
40 &mTempFile->path[0]));
Cody Northrop6cca6c22023-02-08 20:23:13 -070041 }
42
43 virtual void TearDown() { mMBC.reset(); }
44
Cody Northrop5f8117a2023-09-26 20:48:59 -060045 int getFileDescriptorCount();
46
Cody Northrop6cca6c22023-02-08 20:23:13 -070047 std::unique_ptr<TemporaryFile> mTempFile;
48 std::unique_ptr<MultifileBlobCache> mMBC;
49};
50
51TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) {
52 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
53 mMBC->set("abcd", 4, "efgh", 4);
54 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 4));
55 ASSERT_EQ('e', buf[0]);
56 ASSERT_EQ('f', buf[1]);
57 ASSERT_EQ('g', buf[2]);
58 ASSERT_EQ('h', buf[3]);
59}
60
61TEST_F(MultifileBlobCacheTest, CacheTwoValuesSucceeds) {
62 unsigned char buf[2] = {0xee, 0xee};
63 mMBC->set("ab", 2, "cd", 2);
64 mMBC->set("ef", 2, "gh", 2);
65 ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
66 ASSERT_EQ('c', buf[0]);
67 ASSERT_EQ('d', buf[1]);
68 ASSERT_EQ(size_t(2), mMBC->get("ef", 2, buf, 2));
69 ASSERT_EQ('g', buf[0]);
70 ASSERT_EQ('h', buf[1]);
71}
72
73TEST_F(MultifileBlobCacheTest, GetSetTwiceSucceeds) {
74 unsigned char buf[2] = {0xee, 0xee};
75 mMBC->set("ab", 2, "cd", 2);
76 ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
77 ASSERT_EQ('c', buf[0]);
78 ASSERT_EQ('d', buf[1]);
79 // Use the same key, but different value
80 mMBC->set("ab", 2, "ef", 2);
81 ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
82 ASSERT_EQ('e', buf[0]);
83 ASSERT_EQ('f', buf[1]);
84}
85
86TEST_F(MultifileBlobCacheTest, GetOnlyWritesInsideBounds) {
87 unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
88 mMBC->set("abcd", 4, "efgh", 4);
89 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf + 1, 4));
90 ASSERT_EQ(0xee, buf[0]);
91 ASSERT_EQ('e', buf[1]);
92 ASSERT_EQ('f', buf[2]);
93 ASSERT_EQ('g', buf[3]);
94 ASSERT_EQ('h', buf[4]);
95 ASSERT_EQ(0xee, buf[5]);
96}
97
98TEST_F(MultifileBlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
99 unsigned char buf[3] = {0xee, 0xee, 0xee};
100 mMBC->set("abcd", 4, "efgh", 4);
101 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 3));
102 ASSERT_EQ(0xee, buf[0]);
103 ASSERT_EQ(0xee, buf[1]);
104 ASSERT_EQ(0xee, buf[2]);
105}
106
107TEST_F(MultifileBlobCacheTest, GetDoesntAccessNullBuffer) {
108 mMBC->set("abcd", 4, "efgh", 4);
109 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, nullptr, 0));
110}
111
112TEST_F(MultifileBlobCacheTest, MultipleSetsCacheLatestValue) {
113 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
114 mMBC->set("abcd", 4, "efgh", 4);
115 mMBC->set("abcd", 4, "ijkl", 4);
116 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 4));
117 ASSERT_EQ('i', buf[0]);
118 ASSERT_EQ('j', buf[1]);
119 ASSERT_EQ('k', buf[2]);
120 ASSERT_EQ('l', buf[3]);
121}
122
123TEST_F(MultifileBlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
124 unsigned char buf[kMaxValueSize + 1] = {0xee, 0xee, 0xee, 0xee};
125 mMBC->set("abcd", 4, "efgh", 4);
126 mMBC->set("abcd", 4, buf, kMaxValueSize + 1);
127 ASSERT_EQ(size_t(4), mMBC->get("abcd", 4, buf, 4));
128 ASSERT_EQ('e', buf[0]);
129 ASSERT_EQ('f', buf[1]);
130 ASSERT_EQ('g', buf[2]);
131 ASSERT_EQ('h', buf[3]);
132}
133
134TEST_F(MultifileBlobCacheTest, DoesntCacheIfKeyIsTooBig) {
135 char key[kMaxKeySize + 1];
136 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
137 for (int i = 0; i < kMaxKeySize + 1; i++) {
138 key[i] = 'a';
139 }
140 mMBC->set(key, kMaxKeySize + 1, "bbbb", 4);
141 ASSERT_EQ(size_t(0), mMBC->get(key, kMaxKeySize + 1, buf, 4));
142 ASSERT_EQ(0xee, buf[0]);
143 ASSERT_EQ(0xee, buf[1]);
144 ASSERT_EQ(0xee, buf[2]);
145 ASSERT_EQ(0xee, buf[3]);
146}
147
148TEST_F(MultifileBlobCacheTest, DoesntCacheIfValueIsTooBig) {
149 char buf[kMaxValueSize + 1];
150 for (int i = 0; i < kMaxValueSize + 1; i++) {
151 buf[i] = 'b';
152 }
153 mMBC->set("abcd", 4, buf, kMaxValueSize + 1);
154 for (int i = 0; i < kMaxValueSize + 1; i++) {
155 buf[i] = 0xee;
156 }
157 ASSERT_EQ(size_t(0), mMBC->get("abcd", 4, buf, kMaxValueSize + 1));
158 for (int i = 0; i < kMaxValueSize + 1; i++) {
159 SCOPED_TRACE(i);
160 ASSERT_EQ(0xee, buf[i]);
161 }
162}
163
164TEST_F(MultifileBlobCacheTest, CacheMaxKeySizeSucceeds) {
165 char key[kMaxKeySize];
166 unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
167 for (int i = 0; i < kMaxKeySize; i++) {
168 key[i] = 'a';
169 }
170 mMBC->set(key, kMaxKeySize, "wxyz", 4);
171 ASSERT_EQ(size_t(4), mMBC->get(key, kMaxKeySize, buf, 4));
172 ASSERT_EQ('w', buf[0]);
173 ASSERT_EQ('x', buf[1]);
174 ASSERT_EQ('y', buf[2]);
175 ASSERT_EQ('z', buf[3]);
176}
177
178TEST_F(MultifileBlobCacheTest, CacheMaxValueSizeSucceeds) {
179 char buf[kMaxValueSize];
180 for (int i = 0; i < kMaxValueSize; i++) {
181 buf[i] = 'b';
182 }
183 mMBC->set("abcd", 4, buf, kMaxValueSize);
184 for (int i = 0; i < kMaxValueSize; i++) {
185 buf[i] = 0xee;
186 }
187 mMBC->get("abcd", 4, buf, kMaxValueSize);
188 for (int i = 0; i < kMaxValueSize; i++) {
189 SCOPED_TRACE(i);
190 ASSERT_EQ('b', buf[i]);
191 }
192}
193
Cody Northropbe163732023-03-22 10:14:26 -0600194TEST_F(MultifileBlobCacheTest, CacheMaxKeyAndValueSizeSucceeds) {
195 char key[kMaxKeySize];
196 for (int i = 0; i < kMaxKeySize; i++) {
197 key[i] = 'a';
198 }
199 char buf[kMaxValueSize];
200 for (int i = 0; i < kMaxValueSize; i++) {
201 buf[i] = 'b';
202 }
203 mMBC->set(key, kMaxKeySize, buf, kMaxValueSize);
204 for (int i = 0; i < kMaxValueSize; i++) {
205 buf[i] = 0xee;
206 }
207 mMBC->get(key, kMaxKeySize, buf, kMaxValueSize);
208 for (int i = 0; i < kMaxValueSize; i++) {
209 SCOPED_TRACE(i);
210 ASSERT_EQ('b', buf[i]);
211 }
212}
213
Cody Northrop6cca6c22023-02-08 20:23:13 -0700214TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
215 unsigned char buf[1] = {0xee};
216 mMBC->set("x", 1, "y", 1);
217 ASSERT_EQ(size_t(1), mMBC->get("x", 1, buf, 1));
218 ASSERT_EQ('y', buf[0]);
219}
220
Cody Northrop5f8117a2023-09-26 20:48:59 -0600221int MultifileBlobCacheTest::getFileDescriptorCount() {
222 DIR* directory = opendir("/proc/self/fd");
223
224 int fileCount = 0;
225 struct dirent* entry;
226 while ((entry = readdir(directory)) != NULL) {
227 fileCount++;
228 // printf("File: %s\n", entry->d_name);
229 }
230
231 closedir(directory);
232 return fileCount;
233}
234
235TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
236 // Populate the cache with a bunch of entries
237 size_t kLargeNumberOfEntries = 1024;
238 for (int i = 0; i < kLargeNumberOfEntries; i++) {
239 // printf("Caching: %i", i);
240
241 // Use the index as the key and value
242 mMBC->set(&i, sizeof(i), &i, sizeof(i));
243
244 int result = 0;
245 ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
246 ASSERT_EQ(i, result);
247 }
248
249 // Ensure we don't have a bunch of open fds
250 ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
251
252 // Close the cache so everything writes out
253 mMBC->finish();
254 mMBC.reset();
255
256 // Now open it again and ensure we still don't have a bunch of open fds
257 mMBC.reset(
258 new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
259
260 // Check after initialization
261 ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
262
263 for (int i = 0; i < kLargeNumberOfEntries; i++) {
264 int result = 0;
265 ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
266 ASSERT_EQ(i, result);
267 }
268
269 // And again after we've actually used it
270 ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
271}
272
Cody Northrop6cca6c22023-02-08 20:23:13 -0700273} // namespace android