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