ResTable_test.cpp revision 4c67a475a334e4f65238d439a3339195e03c03be
1/* 2 * Copyright (C) 2014 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 "androidfw/ResourceTypes.h" 18 19#include <codecvt> 20#include <locale> 21#include <string> 22 23#include "utils/String16.h" 24#include "utils/String8.h" 25 26#include "TestHelpers.h" 27#include "data/basic/R.h" 28#include "data/lib/R.h" 29 30namespace basic = com::android::basic; 31namespace lib = com::android::lib; 32 33namespace android { 34 35TEST(ResTableTest, ShouldLoadSuccessfully) { 36 std::string contents; 37 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 38 "resources.arsc", &contents)); 39 40 ResTable table; 41 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 42} 43 44TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) { 45 std::string contents; 46 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 47 "resources.arsc", &contents)); 48 49 ResTable table; 50 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 51 52 EXPECT_TRUE(IsStringEqual(table, basic::R::string::test1, "test1")); 53} 54 55TEST(ResTableTest, ResourceNameIsResolved) { 56 std::string contents; 57 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 58 "resources.arsc", &contents)); 59 60 ResTable table; 61 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 62 63 String16 defPackage("com.android.basic"); 64 String16 testName("@string/test1"); 65 uint32_t resID = 66 table.identifierForName(testName.string(), testName.size(), 0, 0, 67 defPackage.string(), defPackage.size()); 68 ASSERT_NE(uint32_t(0x00000000), resID); 69 ASSERT_EQ(basic::R::string::test1, resID); 70} 71 72TEST(ResTableTest, NoParentThemeIsAppliedCorrectly) { 73 std::string contents; 74 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 75 "resources.arsc", &contents)); 76 77 ResTable table; 78 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 79 80 ResTable::Theme theme(table); 81 ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme1)); 82 83 Res_value val; 84 uint32_t specFlags = 0; 85 ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags); 86 ASSERT_GE(index, 0); 87 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 88 ASSERT_EQ(uint32_t(100), val.data); 89 90 index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags); 91 ASSERT_GE(index, 0); 92 ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 93 ASSERT_EQ(basic::R::integer::number1, val.data); 94} 95 96TEST(ResTableTest, ParentThemeIsAppliedCorrectly) { 97 std::string contents; 98 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 99 "resources.arsc", &contents)); 100 101 ResTable table; 102 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 103 104 ResTable::Theme theme(table); 105 ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme2)); 106 107 Res_value val; 108 uint32_t specFlags = 0; 109 ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags); 110 ASSERT_GE(index, 0); 111 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 112 ASSERT_EQ(uint32_t(300), val.data); 113 114 index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags); 115 ASSERT_GE(index, 0); 116 ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 117 ASSERT_EQ(basic::R::integer::number1, val.data); 118} 119 120TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) { 121 std::string contents; 122 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib/lib.apk", 123 "resources.arsc", &contents)); 124 125 ResTable table; 126 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 127 128 ResTable::Theme theme(table); 129 ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme)); 130 131 Res_value val; 132 uint32_t specFlags = 0; 133 ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags); 134 ASSERT_GE(index, 0); 135 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 136 ASSERT_EQ(uint32_t(700), val.data); 137 138 index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags); 139 ASSERT_GE(index, 0); 140 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 141 ASSERT_EQ(uint32_t(700), val.data); 142} 143 144TEST(ResTableTest, ReferenceToBagIsNotResolved) { 145 std::string contents; 146 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 147 "resources.arsc", &contents)); 148 149 ResTable table; 150 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 151 152 Res_value val; 153 ssize_t block = 154 table.getResource(basic::R::integer::number2, &val, MAY_NOT_BE_BAG); 155 ASSERT_GE(block, 0); 156 ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 157 ASSERT_EQ(basic::R::array::integerArray1, val.data); 158 159 ssize_t newBlock = table.resolveReference(&val, block); 160 EXPECT_EQ(block, newBlock); 161 EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 162 EXPECT_EQ(basic::R::array::integerArray1, val.data); 163} 164 165TEST(ResTableTest, ResourcesStillAccessibleAfterParameterChange) { 166 std::string contents; 167 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 168 "resources.arsc", &contents)); 169 170 ResTable table; 171 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 172 173 Res_value val; 174 ssize_t block = 175 table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 176 ASSERT_GE(block, 0); 177 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 178 179 const ResTable::bag_entry* entry; 180 ssize_t count = table.lockBag(basic::R::array::integerArray1, &entry); 181 ASSERT_GE(count, 0); 182 table.unlockBag(entry); 183 184 ResTable_config param; 185 memset(¶m, 0, sizeof(param)); 186 param.density = 320; 187 table.setParameters(¶m); 188 189 block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 190 ASSERT_GE(block, 0); 191 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 192 193 count = table.lockBag(basic::R::array::integerArray1, &entry); 194 ASSERT_GE(count, 0); 195 table.unlockBag(entry); 196} 197 198TEST(ResTableTest, ResourceIsOverridenWithBetterConfig) { 199 std::string contents; 200 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 201 "resources.arsc", &contents)); 202 203 ResTable table; 204 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 205 206 Res_value val; 207 ssize_t block = 208 table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 209 ASSERT_GE(block, 0); 210 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 211 ASSERT_EQ(uint32_t(200), val.data); 212 213 ResTable_config param; 214 memset(¶m, 0, sizeof(param)); 215 param.language[0] = 's'; 216 param.language[1] = 'v'; 217 param.country[0] = 'S'; 218 param.country[1] = 'E'; 219 table.setParameters(¶m); 220 221 block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 222 ASSERT_GE(block, 0); 223 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 224 ASSERT_EQ(uint32_t(400), val.data); 225} 226 227TEST(ResTableTest, emptyTableHasSensibleDefaults) { 228 const int32_t assetCookie = 1; 229 230 ResTable table; 231 ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie)); 232 233 // Adding an empty table gives us one table! 234 ASSERT_EQ(uint32_t(1), table.getTableCount()); 235 236 // Adding an empty table doesn't mean we get packages. 237 ASSERT_EQ(uint32_t(0), table.getBasePackageCount()); 238 239 Res_value val; 240 ASSERT_LT(table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG), 241 0); 242} 243 244void testU16StringToInt(const char16_t* str, uint32_t expectedValue, 245 bool expectSuccess, bool expectHex) { 246 size_t len = std::char_traits<char16_t>::length(str); 247 248 // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :( 249 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; 250 std::string s = convert.to_bytes(std::u16string(str, len)); 251 252 Res_value out = {}; 253 ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) << "Failed with " 254 << s; 255 256 if (!expectSuccess) { 257 ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s; 258 return; 259 } 260 261 if (expectHex) { 262 ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s; 263 } else { 264 ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s; 265 } 266 267 ASSERT_EQ(expectedValue, out.data) << "Failed with " << s; 268} 269 270TEST(ResTableTest, U16StringToInt) { 271 testU16StringToInt(u"", 0U, false, false); 272 testU16StringToInt(u" ", 0U, false, false); 273 testU16StringToInt(u"\t\n", 0U, false, false); 274 275 testU16StringToInt(u"abcd", 0U, false, false); 276 testU16StringToInt(u"10abcd", 0U, false, false); 277 testU16StringToInt(u"42 42", 0U, false, false); 278 testU16StringToInt(u"- 42", 0U, false, false); 279 testU16StringToInt(u"-", 0U, false, false); 280 281 testU16StringToInt(u"0x", 0U, false, true); 282 testU16StringToInt(u"0xnope", 0U, false, true); 283 testU16StringToInt(u"0X42", 0U, false, true); 284 testU16StringToInt(u"0x42 0x42", 0U, false, true); 285 testU16StringToInt(u"-0x0", 0U, false, true); 286 testU16StringToInt(u"-0x42", 0U, false, true); 287 testU16StringToInt(u"- 0x42", 0U, false, true); 288 289 // Note that u" 42" would pass. This preserves the old behavior, but it may 290 // not be desired. 291 testU16StringToInt(u"42 ", 0U, false, false); 292 testU16StringToInt(u"0x42 ", 0U, false, true); 293 294 // Decimal cases. 295 testU16StringToInt(u"0", 0U, true, false); 296 testU16StringToInt(u"-0", 0U, true, false); 297 testU16StringToInt(u"42", 42U, true, false); 298 testU16StringToInt(u" 42", 42U, true, false); 299 testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false); 300 testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false); 301 testU16StringToInt(u"042", 42U, true, false); 302 testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false); 303 304 // Hex cases. 305 testU16StringToInt(u"0x0", 0x0, true, true); 306 testU16StringToInt(u"0x42", 0x42, true, true); 307 testU16StringToInt(u" 0x42", 0x42, true, true); 308 309 // Just before overflow cases: 310 testU16StringToInt(u"2147483647", INT_MAX, true, false); 311 testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true, 312 false); 313 testU16StringToInt(u"0xffffffff", UINT_MAX, true, true); 314 315 // Overflow cases: 316 testU16StringToInt(u"2147483648", 0U, false, false); 317 testU16StringToInt(u"-2147483649", 0U, false, false); 318 testU16StringToInt(u"0x1ffffffff", 0U, false, true); 319} 320 321TEST(ResTableTest, ShareButDontModifyResTable) { 322 std::string contents; 323 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 324 "resources.arsc", &contents)); 325 326 ResTable sharedTable; 327 ASSERT_EQ(NO_ERROR, sharedTable.add(contents.data(), contents.size())); 328 329 ResTable_config param; 330 memset(¶m, 0, sizeof(param)); 331 param.language[0] = 'v'; 332 param.language[1] = 's'; 333 sharedTable.setParameters(¶m); 334 335 // Check that we get the default value for @integer:number1 336 Res_value val; 337 ssize_t block = 338 sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 339 ASSERT_GE(block, 0); 340 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 341 ASSERT_EQ(uint32_t(600), val.data); 342 343 // Create a new table that shares the entries of the shared table. 344 ResTable table; 345 ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false)); 346 347 // Set a new configuration on the new table. 348 memset(¶m, 0, sizeof(param)); 349 param.language[0] = 's'; 350 param.language[1] = 'v'; 351 param.country[0] = 'S'; 352 param.country[1] = 'E'; 353 table.setParameters(¶m); 354 355 // Check that we get a new value in the new table. 356 block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 357 ASSERT_GE(block, 0); 358 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 359 ASSERT_EQ(uint32_t(400), val.data); 360 361 // Check that we still get the old value in the shared table. 362 block = 363 sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 364 ASSERT_GE(block, 0); 365 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 366 ASSERT_EQ(uint32_t(600), val.data); 367} 368 369TEST(ResTableTest, GetConfigurationsReturnsUniqueList) { 370 std::string contents; 371 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 372 "resources.arsc", &contents)); 373 374 std::string system_contents; 375 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/system/system.apk", 376 "resources.arsc", &system_contents)); 377 378 ResTable table; 379 ASSERT_EQ(NO_ERROR, 380 table.add(system_contents.data(), system_contents.size())); 381 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 382 383 ResTable_config configSv; 384 memset(&configSv, 0, sizeof(configSv)); 385 configSv.language[0] = 's'; 386 configSv.language[1] = 'v'; 387 388 Vector<ResTable_config> configs; 389 table.getConfigurations(&configs); 390 391 EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv)); 392 393 Vector<String8> locales; 394 table.getLocales(&locales); 395 396 EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv"))); 397} 398 399} // namespace android 400