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