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 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 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 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 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 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 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 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 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 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 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 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 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 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