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(&param, 0, sizeof(param));
186  param.density = 320;
187  table.setParameters(&param);
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(&param, 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(&param);
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(&param, 0, sizeof(param));
331  param.language[0] = 'v';
332  param.language[1] = 's';
333  sharedTable.setParameters(&param);
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(&param, 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(&param);
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