1 /*
2 * Copyright (C) 2020 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 <memory>
18 #include <utility>
19
20 #include "androidfw/CursorWindow.h"
21
22 #include "TestHelpers.h"
23
24 // Verify that the memory in use is a multiple of 4 bytes
25 #define ASSERT_ALIGNED(w) \
26 ASSERT_EQ(((w)->sizeInUse() & 3), 0); \
27 ASSERT_EQ(((w)->freeSpace() & 3), 0); \
28 ASSERT_EQ(((w)->sizeOfSlots() & 3), 0)
29
30 #define CREATE_WINDOW_1K \
31 CursorWindow* w; \
32 CursorWindow::create(String8("test"), 1 << 10, &w); \
33 ASSERT_ALIGNED(w);
34
35 #define CREATE_WINDOW_1K_3X3 \
36 CursorWindow* w; \
37 CursorWindow::create(String8("test"), 1 << 10, &w); \
38 ASSERT_EQ(w->setNumColumns(3), OK); \
39 ASSERT_EQ(w->allocRow(), OK); \
40 ASSERT_EQ(w->allocRow(), OK); \
41 ASSERT_EQ(w->allocRow(), OK); \
42 ASSERT_ALIGNED(w);
43
44 #define CREATE_WINDOW_2M \
45 CursorWindow* w; \
46 CursorWindow::create(String8("test"), 1 << 21, &w); \
47 ASSERT_ALIGNED(w);
48
49 static constexpr const size_t kHalfInlineSize = 8192;
50 static constexpr const size_t kGiantSize = 1048576;
51
52 namespace android {
53
TEST(CursorWindowTest,Empty)54 TEST(CursorWindowTest, Empty) {
55 CREATE_WINDOW_1K;
56
57 ASSERT_EQ(w->getNumRows(), 0);
58 ASSERT_EQ(w->getNumColumns(), 0);
59 ASSERT_EQ(w->size(), 1 << 10);
60 ASSERT_EQ(w->freeSpace(), 1 << 10);
61 ASSERT_ALIGNED(w);
62 }
63
TEST(CursorWindowTest,SetNumColumns)64 TEST(CursorWindowTest, SetNumColumns) {
65 CREATE_WINDOW_1K;
66
67 // Once we've locked in columns, we can't adjust
68 ASSERT_EQ(w->getNumColumns(), 0);
69 ASSERT_EQ(w->setNumColumns(4), OK);
70 ASSERT_NE(w->setNumColumns(5), OK);
71 ASSERT_NE(w->setNumColumns(3), OK);
72 ASSERT_EQ(w->getNumColumns(), 4);
73 ASSERT_ALIGNED(w);
74 }
75
TEST(CursorWindowTest,SetNumColumnsAfterRow)76 TEST(CursorWindowTest, SetNumColumnsAfterRow) {
77 CREATE_WINDOW_1K;
78
79 // Once we've locked in a row, we can't adjust columns
80 ASSERT_EQ(w->getNumColumns(), 0);
81 ASSERT_EQ(w->allocRow(), OK);
82 ASSERT_NE(w->setNumColumns(4), OK);
83 ASSERT_EQ(w->getNumColumns(), 0);
84 ASSERT_ALIGNED(w);
85 }
86
TEST(CursorWindowTest,AllocRow)87 TEST(CursorWindowTest, AllocRow) {
88 CREATE_WINDOW_1K;
89
90 ASSERT_EQ(w->setNumColumns(4), OK);
91
92 // Rolling forward means we have less free space
93 ASSERT_EQ(w->getNumRows(), 0);
94 auto before = w->freeSpace();
95 ASSERT_EQ(w->allocRow(), OK);
96 ASSERT_LT(w->freeSpace(), before);
97 ASSERT_EQ(w->getNumRows(), 1);
98 ASSERT_ALIGNED(w);
99
100 // Verify we can unwind
101 ASSERT_EQ(w->freeLastRow(), OK);
102 ASSERT_EQ(w->freeSpace(), before);
103 ASSERT_EQ(w->getNumRows(), 0);
104 ASSERT_ALIGNED(w);
105
106 // Can't unwind when no rows left
107 ASSERT_NE(w->freeLastRow(), OK);
108 ASSERT_ALIGNED(w);
109 }
110
TEST(CursorWindowTest,AllocRowBounds)111 TEST(CursorWindowTest, AllocRowBounds) {
112 CREATE_WINDOW_1K;
113
114 // 60 columns is 960 bytes, which means only a single row can fit
115 ASSERT_EQ(w->setNumColumns(60), OK);
116 ASSERT_EQ(w->allocRow(), OK);
117 ASSERT_NE(w->allocRow(), OK);
118 ASSERT_ALIGNED(w);
119 }
120
TEST(CursorWindowTest,StoreNull)121 TEST(CursorWindowTest, StoreNull) {
122 CREATE_WINDOW_1K_3X3;
123
124 ASSERT_EQ(w->putNull(1, 1), OK);
125 ASSERT_EQ(w->putNull(0, 0), OK);
126
127 {
128 auto field = w->getFieldSlot(1, 1);
129 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL);
130 }
131 {
132 auto field = w->getFieldSlot(0, 0);
133 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL);
134 }
135 ASSERT_ALIGNED(w);
136 }
137
TEST(CursorWindowTest,StoreLong)138 TEST(CursorWindowTest, StoreLong) {
139 CREATE_WINDOW_1K_3X3;
140
141 ASSERT_EQ(w->putLong(1, 1, 0xf00d), OK);
142 ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
143
144 {
145 auto field = w->getFieldSlot(1, 1);
146 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
147 ASSERT_EQ(w->getFieldSlotValueLong(field), 0xf00d);
148 }
149 {
150 auto field = w->getFieldSlot(0, 0);
151 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
152 ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
153 }
154 ASSERT_ALIGNED(w);
155 }
156
TEST(CursorWindowTest,StoreString)157 TEST(CursorWindowTest, StoreString) {
158 CREATE_WINDOW_1K_3X3;
159
160 ASSERT_EQ(w->putString(1, 1, "food", 5), OK);
161 ASSERT_EQ(w->putString(0, 0, "cafe", 5), OK);
162
163 size_t size;
164 {
165 auto field = w->getFieldSlot(1, 1);
166 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING);
167 auto actual = w->getFieldSlotValueString(field, &size);
168 ASSERT_EQ(std::string(actual), "food");
169 }
170 {
171 auto field = w->getFieldSlot(0, 0);
172 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING);
173 auto actual = w->getFieldSlotValueString(field, &size);
174 ASSERT_EQ(std::string(actual), "cafe");
175 }
176 ASSERT_ALIGNED(w);
177 }
178
TEST(CursorWindowTest,StoreBounds)179 TEST(CursorWindowTest, StoreBounds) {
180 CREATE_WINDOW_1K_3X3;
181
182 // Can't work with values beyond bounds
183 ASSERT_NE(w->putLong(0, 3, 0xcafe), OK);
184 ASSERT_NE(w->putLong(3, 0, 0xcafe), OK);
185 ASSERT_NE(w->putLong(3, 3, 0xcafe), OK);
186 ASSERT_EQ(w->getFieldSlot(0, 3), nullptr);
187 ASSERT_EQ(w->getFieldSlot(3, 0), nullptr);
188 ASSERT_EQ(w->getFieldSlot(3, 3), nullptr);
189
190 // Can't work with invalid indexes
191 ASSERT_NE(w->putLong(-1, 0, 0xcafe), OK);
192 ASSERT_NE(w->putLong(0, -1, 0xcafe), OK);
193 ASSERT_NE(w->putLong(-1, -1, 0xcafe), OK);
194 ASSERT_EQ(w->getFieldSlot(-1, 0), nullptr);
195 ASSERT_EQ(w->getFieldSlot(0, -1), nullptr);
196 ASSERT_EQ(w->getFieldSlot(-1, -1), nullptr);
197 ASSERT_ALIGNED(w);
198 }
199
TEST(CursorWindowTest,Inflate)200 TEST(CursorWindowTest, Inflate) {
201 CREATE_WINDOW_2M;
202
203 auto before = w->size();
204 ASSERT_EQ(w->setNumColumns(4), OK);
205 ASSERT_EQ(w->allocRow(), OK);
206
207 // Scratch buffer that will fit before inflation
208 char buf[kHalfInlineSize];
209
210 // Store simple value
211 ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
212
213 // Store first object that fits inside
214 memset(buf, 42, kHalfInlineSize);
215 ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK);
216 ASSERT_EQ(w->size(), before);
217
218 // Store second simple value
219 ASSERT_EQ(w->putLong(0, 2, 0xface), OK);
220
221 // Store second object that requires inflation
222 memset(buf, 84, kHalfInlineSize);
223 ASSERT_EQ(w->putBlob(0, 3, buf, kHalfInlineSize), OK);
224 ASSERT_GT(w->size(), before);
225
226 // Verify data is intact
227 {
228 auto field = w->getFieldSlot(0, 0);
229 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
230 ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
231 }
232 {
233 auto field = w->getFieldSlot(0, 1);
234 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
235 size_t actualSize;
236 auto actual = w->getFieldSlotValueBlob(field, &actualSize);
237 ASSERT_EQ(actualSize, kHalfInlineSize);
238 memset(buf, 42, kHalfInlineSize);
239 ASSERT_NE(actual, buf);
240 ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
241 }
242 {
243 auto field = w->getFieldSlot(0, 2);
244 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
245 ASSERT_EQ(w->getFieldSlotValueLong(field), 0xface);
246 }
247 {
248 auto field = w->getFieldSlot(0, 3);
249 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
250 size_t actualSize;
251 auto actual = w->getFieldSlotValueBlob(field, &actualSize);
252 ASSERT_EQ(actualSize, kHalfInlineSize);
253 memset(buf, 84, kHalfInlineSize);
254 ASSERT_NE(actual, buf);
255 ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
256 }
257 ASSERT_ALIGNED(w);
258 }
259
TEST(CursorWindowTest,ParcelEmpty)260 TEST(CursorWindowTest, ParcelEmpty) {
261 CREATE_WINDOW_2M;
262
263 Parcel p;
264 w->writeToParcel(&p);
265 p.setDataPosition(0);
266 w = nullptr;
267
268 ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
269 ASSERT_EQ(w->getNumRows(), 0);
270 ASSERT_EQ(w->getNumColumns(), 0);
271 ASSERT_EQ(w->size(), 0);
272 ASSERT_EQ(w->freeSpace(), 0);
273 ASSERT_ALIGNED(w);
274
275 // We can't mutate the window after parceling
276 ASSERT_NE(w->setNumColumns(4), OK);
277 ASSERT_NE(w->allocRow(), OK);
278 ASSERT_ALIGNED(w);
279 }
280
TEST(CursorWindowTest,ParcelSmall)281 TEST(CursorWindowTest, ParcelSmall) {
282 CREATE_WINDOW_2M;
283
284 auto before = w->size();
285 ASSERT_EQ(w->setNumColumns(4), OK);
286 ASSERT_EQ(w->allocRow(), OK);
287
288 // Scratch buffer that will fit before inflation
289 char buf[kHalfInlineSize];
290
291 // Store simple value
292 ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
293
294 // Store first object that fits inside
295 memset(buf, 42, kHalfInlineSize);
296 ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK);
297 ASSERT_EQ(w->size(), before);
298
299 // Store second object with zero length
300 ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK);
301 ASSERT_EQ(w->size(), before);
302
303 // Force through a parcel
304 Parcel p;
305 w->writeToParcel(&p);
306 p.setDataPosition(0);
307 w = nullptr;
308
309 ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
310 ASSERT_EQ(w->getNumRows(), 1);
311 ASSERT_EQ(w->getNumColumns(), 4);
312
313 // Verify data is intact
314 {
315 auto field = w->getFieldSlot(0, 0);
316 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
317 ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
318 }
319 {
320 auto field = w->getFieldSlot(0, 1);
321 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
322 size_t actualSize;
323 auto actual = w->getFieldSlotValueBlob(field, &actualSize);
324 ASSERT_EQ(actualSize, kHalfInlineSize);
325 memset(buf, 42, kHalfInlineSize);
326 ASSERT_NE(actual, buf);
327 ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0);
328 }
329 {
330 auto field = w->getFieldSlot(0, 2);
331 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
332 size_t actualSize;
333 auto actual = w->getFieldSlotValueBlob(field, &actualSize);
334 ASSERT_EQ(actualSize, 0);
335 ASSERT_NE(actual, nullptr);
336 }
337 ASSERT_ALIGNED(w);
338 }
339
TEST(CursorWindowTest,ParcelLarge)340 TEST(CursorWindowTest, ParcelLarge) {
341 CREATE_WINDOW_2M;
342
343 ASSERT_EQ(w->setNumColumns(4), OK);
344 ASSERT_EQ(w->allocRow(), OK);
345
346 // Store simple value
347 ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK);
348
349 // Store object that forces inflation
350 std::unique_ptr<char> bufPtr(new char[kGiantSize]);
351 void* buf = bufPtr.get();
352 memset(buf, 42, kGiantSize);
353 ASSERT_EQ(w->putBlob(0, 1, buf, kGiantSize), OK);
354
355 // Store second object with zero length
356 ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK);
357
358 // Force through a parcel
359 Parcel p;
360 w->writeToParcel(&p);
361 p.setDataPosition(0);
362 w = nullptr;
363
364 ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK);
365 ASSERT_EQ(w->getNumRows(), 1);
366 ASSERT_EQ(w->getNumColumns(), 4);
367
368 // Verify data is intact
369 {
370 auto field = w->getFieldSlot(0, 0);
371 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER);
372 ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe);
373 }
374 {
375 auto field = w->getFieldSlot(0, 1);
376 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
377 size_t actualSize;
378 auto actual = w->getFieldSlotValueBlob(field, &actualSize);
379 ASSERT_EQ(actualSize, kGiantSize);
380 memset(buf, 42, kGiantSize);
381 ASSERT_EQ(memcmp(buf, actual, kGiantSize), 0);
382 }
383 {
384 auto field = w->getFieldSlot(0, 2);
385 ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB);
386 size_t actualSize;
387 auto actual = w->getFieldSlotValueBlob(field, &actualSize);
388 ASSERT_EQ(actualSize, 0);
389 ASSERT_NE(actual, nullptr);
390 }
391 ASSERT_ALIGNED(w);
392 }
393
394 } // android
395