• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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