1 /*
2 * Copyright (C) 2015 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 "ResourceTable.h"
18 #include "Diagnostics.h"
19 #include "ResourceValues.h"
20 #include "test/Test.h"
21 #include "util/Util.h"
22
23 #include <algorithm>
24 #include <ostream>
25 #include <string>
26
27 using ::android::ConfigDescription;
28 using ::android::StringPiece;
29 using ::testing::Eq;
30 using ::testing::NotNull;
31 using ::testing::StrEq;
32
33 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
34
35 namespace aapt {
36
TEST(ResourceTableTest,FailToAddResourceWithBadName)37 TEST(ResourceTableTest, FailToAddResourceWithBadName) {
38 ResourceTable table;
39
40 EXPECT_FALSE(table.AddResource(
41 test::ParseNameOrDie("android:id/hey,there"), ConfigDescription{}, "",
42 test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
43 test::GetDiagnostics()));
44
45 EXPECT_FALSE(table.AddResource(
46 test::ParseNameOrDie("android:id/hey:there"), ConfigDescription{}, "",
47 test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
48 test::GetDiagnostics()));
49 }
50
TEST(ResourceTableTest,AddResourceWithWeirdNameWhenAddingMangledResources)51 TEST(ResourceTableTest, AddResourceWithWeirdNameWhenAddingMangledResources) {
52 ResourceTable table;
53
54 EXPECT_TRUE(table.AddResourceMangled(
55 test::ParseNameOrDie("android:id/heythere "), ConfigDescription{}, "",
56 test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), test::GetDiagnostics()));
57 }
58
TEST(ResourceTableTest,AddOneResource)59 TEST(ResourceTableTest, AddOneResource) {
60 ResourceTable table;
61
62 EXPECT_TRUE(table.AddResource(
63 test::ParseNameOrDie("android:attr/id"), ConfigDescription{}, "",
64 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build(),
65 test::GetDiagnostics()));
66
67 EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull());
68 }
69
TEST(ResourceTableTest,AddMultipleResources)70 TEST(ResourceTableTest, AddMultipleResources) {
71 ResourceTable table;
72
73 ConfigDescription config;
74 ConfigDescription language_config;
75 memcpy(language_config.language, "pl", sizeof(language_config.language));
76
77 EXPECT_TRUE(table.AddResource(
78 test::ParseNameOrDie("android:attr/layout_width"), config, "",
79 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 10u).Build(),
80 test::GetDiagnostics()));
81
82 EXPECT_TRUE(table.AddResource(
83 test::ParseNameOrDie("android:attr/id"), config, "",
84 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 12u).Build(),
85 test::GetDiagnostics()));
86
87 EXPECT_TRUE(table.AddResource(
88 test::ParseNameOrDie("android:string/ok"), config, "",
89 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 14u).Build(),
90 test::GetDiagnostics()));
91
92 EXPECT_TRUE(table.AddResource(
93 test::ParseNameOrDie("android:string/ok"), language_config, "",
94 test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
95 .SetSource("test/path/file.xml", 20u)
96 .Build(),
97 test::GetDiagnostics()));
98
99 EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/layout_width"), NotNull());
100 EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull());
101 EXPECT_THAT(test::GetValue<Id>(&table, "android:string/ok"), NotNull());
102 EXPECT_THAT(test::GetValueForConfig<BinaryPrimitive>(&table, "android:string/ok", language_config), NotNull());
103 }
104
TEST(ResourceTableTest,OverrideWeakResourceValue)105 TEST(ResourceTableTest, OverrideWeakResourceValue) {
106 ResourceTable table;
107
108 ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
109 test::AttributeBuilder().SetWeak(true).Build(),
110 test::GetDiagnostics()));
111
112 Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo");
113 ASSERT_THAT(attr, NotNull());
114 EXPECT_TRUE(attr->IsWeak());
115
116 ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
117 util::make_unique<Attribute>(), test::GetDiagnostics()));
118
119 attr = test::GetValue<Attribute>(&table, "android:attr/foo");
120 ASSERT_THAT(attr, NotNull());
121 EXPECT_FALSE(attr->IsWeak());
122 }
123
TEST(ResourceTableTest,AllowCompatibleDuplicateAttributes)124 TEST(ResourceTableTest, AllowCompatibleDuplicateAttributes) {
125 ResourceTable table;
126
127 const ResourceName name = test::ParseNameOrDie("android:attr/foo");
128 Attribute attr_one(android::ResTable_map::TYPE_STRING);
129 attr_one.SetWeak(true);
130 Attribute attr_two(android::ResTable_map::TYPE_STRING | android::ResTable_map::TYPE_REFERENCE);
131 attr_two.SetWeak(true);
132
133 ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "",
134 util::make_unique<Attribute>(attr_one), test::GetDiagnostics()));
135 ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "",
136 util::make_unique<Attribute>(attr_two), test::GetDiagnostics()));
137 }
138
TEST(ResourceTableTest,ProductVaryingValues)139 TEST(ResourceTableTest, ProductVaryingValues) {
140 ResourceTable table;
141
142 EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
143 test::ParseConfigOrDie("land"), "tablet",
144 util::make_unique<Id>(),
145 test::GetDiagnostics()));
146 EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
147 test::ParseConfigOrDie("land"), "phone",
148 util::make_unique<Id>(),
149 test::GetDiagnostics()));
150
151 EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull());
152 EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull());
153
154 Maybe<ResourceTable::SearchResult> sr =
155 table.FindResource(test::ParseNameOrDie("android:string/foo"));
156 ASSERT_TRUE(sr);
157 std::vector<ResourceConfigValue*> values =
158 sr.value().entry->FindAllValues(test::ParseConfigOrDie("land"));
159 ASSERT_EQ(2u, values.size());
160 EXPECT_EQ(std::string("phone"), values[0]->product);
161 EXPECT_EQ(std::string("tablet"), values[1]->product);
162 }
163
LevelToString(Visibility::Level level)164 static StringPiece LevelToString(Visibility::Level level) {
165 switch (level) {
166 case Visibility::Level::kPrivate:
167 return "private";
168 case Visibility::Level::kPublic:
169 return "private";
170 default:
171 return "undefined";
172 }
173 }
174
VisibilityOfResource(const ResourceTable & table,const ResourceNameRef & name,Visibility::Level level,const StringPiece & comment)175 static ::testing::AssertionResult VisibilityOfResource(const ResourceTable& table,
176 const ResourceNameRef& name,
177 Visibility::Level level,
178 const StringPiece& comment) {
179 Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
180 if (!result) {
181 return ::testing::AssertionFailure() << "no resource '" << name << "' found in table";
182 }
183
184 const Visibility& visibility = result.value().entry->visibility;
185 if (visibility.level != level) {
186 return ::testing::AssertionFailure() << "expected visibility " << LevelToString(level)
187 << " but got " << LevelToString(visibility.level);
188 }
189
190 if (visibility.comment != comment) {
191 return ::testing::AssertionFailure() << "expected visibility comment '" << comment
192 << "' but got '" << visibility.comment << "'";
193 }
194 return ::testing::AssertionSuccess();
195 }
196
TEST(ResourceTableTest,SetVisibility)197 TEST(ResourceTableTest, SetVisibility) {
198 using Level = Visibility::Level;
199
200 ResourceTable table;
201 const ResourceName name = test::ParseNameOrDie("android:string/foo");
202
203 Visibility visibility;
204 visibility.level = Visibility::Level::kPrivate;
205 visibility.comment = "private";
206 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
207 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private"));
208
209 visibility.level = Visibility::Level::kUndefined;
210 visibility.comment = "undefined";
211 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
212 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private"));
213
214 visibility.level = Visibility::Level::kPublic;
215 visibility.comment = "public";
216 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
217 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public"));
218
219 visibility.level = Visibility::Level::kPrivate;
220 visibility.comment = "private";
221 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
222 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public"));
223 }
224
TEST(ResourceTableTest,SetAllowNew)225 TEST(ResourceTableTest, SetAllowNew) {
226 ResourceTable table;
227 const ResourceName name = test::ParseNameOrDie("android:string/foo");
228
229 AllowNew allow_new;
230 Maybe<ResourceTable::SearchResult> result;
231
232 allow_new.comment = "first";
233 ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics()));
234 result = table.FindResource(name);
235 ASSERT_TRUE(result);
236 ASSERT_TRUE(result.value().entry->allow_new);
237 ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("first"));
238
239 allow_new.comment = "second";
240 ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics()));
241 result = table.FindResource(name);
242 ASSERT_TRUE(result);
243 ASSERT_TRUE(result.value().entry->allow_new);
244 ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second"));
245 }
246
TEST(ResourceTableTest,SetOverlayable)247 TEST(ResourceTableTest, SetOverlayable) {
248 ResourceTable table;
249 auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
250 Source("res/values/overlayable.xml", 40));
251 OverlayableItem overlayable_item(overlayable);
252 overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
253 overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
254 overlayable_item.comment = "comment";
255 overlayable_item.source = Source("res/values/overlayable.xml", 42);
256
257 const ResourceName name = test::ParseNameOrDie("android:string/foo");
258 ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
259 Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
260
261 ASSERT_TRUE(search_result);
262 ASSERT_TRUE(search_result.value().entry->overlayable_item);
263
264 OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
265 EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
266 EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
267 EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
268 EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
269 EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
270 | PolicyFlags::VENDOR_PARTITION));
271 ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
272 EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
273 EXPECT_THAT(result_overlayable_item.source.line, 42);
274 }
275
TEST(ResourceTableTest,SetMultipleOverlayableResources)276 TEST(ResourceTableTest, SetMultipleOverlayableResources) {
277 ResourceTable table;
278
279 const ResourceName foo = test::ParseNameOrDie("android:string/foo");
280 auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
281 OverlayableItem overlayable(group);
282 overlayable.policies = PolicyFlags::PRODUCT_PARTITION;
283 ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
284
285 const ResourceName bar = test::ParseNameOrDie("android:string/bar");
286 OverlayableItem overlayable2(group);
287 overlayable2.policies = PolicyFlags::PRODUCT_PARTITION;
288 ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
289
290 const ResourceName baz = test::ParseNameOrDie("android:string/baz");
291 OverlayableItem overlayable3(group);
292 overlayable3.policies = PolicyFlags::VENDOR_PARTITION;
293 ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
294 }
295
TEST(ResourceTableTest,SetOverlayableDifferentResourcesDifferentName)296 TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
297 ResourceTable table;
298
299 const ResourceName foo = test::ParseNameOrDie("android:string/foo");
300 OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
301 overlayable_item.policies = PolicyFlags::PRODUCT_PARTITION;
302 ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
303
304 const ResourceName bar = test::ParseNameOrDie("android:string/bar");
305 OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
306 overlayable_item2.policies = PolicyFlags::PRODUCT_PARTITION;
307 ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
308 }
309
TEST(ResourceTableTest,SetOverlayableSameResourcesFail)310 TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
311 ResourceTable table;
312 const ResourceName name = test::ParseNameOrDie("android:string/foo");
313
314 auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
315 OverlayableItem overlayable_item(overlayable);
316 ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
317
318 OverlayableItem overlayable_item2(overlayable);
319 ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
320 }
321
TEST(ResourceTableTest,SetOverlayableSameResourcesDifferentNameFail)322 TEST(ResourceTableTest, SetOverlayableSameResourcesDifferentNameFail) {
323 ResourceTable table;
324 const ResourceName name = test::ParseNameOrDie("android:string/foo");
325
326 auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
327 OverlayableItem overlayable_item(overlayable);
328 ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
329
330 auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
331 OverlayableItem overlayable_item2(overlayable2);
332 ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
333 }
334
TEST(ResourceTableTest,AllowDuplictaeResourcesNames)335 TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
336 ResourceTable table(/* validate_resources */ false);
337
338 const ResourceName foo_name = test::ParseNameOrDie("android:bool/foo");
339 ASSERT_TRUE(table.AddResourceWithId(foo_name, ResourceId(0x7f0100ff), ConfigDescription{} , "",
340 test::BuildPrimitive(android::Res_value::TYPE_INT_BOOLEAN, 0),
341 test::GetDiagnostics()));
342 ASSERT_TRUE(table.AddResourceWithId(foo_name, ResourceId(0x7f010100), ConfigDescription{} , "",
343 test::BuildPrimitive(android::Res_value::TYPE_INT_BOOLEAN, 1),
344 test::GetDiagnostics()));
345
346 ASSERT_TRUE(table.SetVisibilityWithId(foo_name, Visibility{Visibility::Level::kPublic},
347 ResourceId(0x7f0100ff), test::GetDiagnostics()));
348 ASSERT_TRUE(table.SetVisibilityWithId(foo_name, Visibility{Visibility::Level::kPrivate},
349 ResourceId(0x7f010100), test::GetDiagnostics()));
350
351 auto package = table.FindPackageById(0x7f);
352 ASSERT_THAT(package, NotNull());
353 auto type = package->FindType(ResourceType::kBool);
354 ASSERT_THAT(type, NotNull());
355
356 auto entry1 = type->FindEntry("foo", 0x00ff);
357 ASSERT_THAT(entry1, NotNull());
358 ASSERT_THAT(entry1->id, Eq(0x00ff));
359 ASSERT_THAT(entry1->values[0], NotNull());
360 ASSERT_THAT(entry1->values[0]->value, NotNull());
361 ASSERT_THAT(ValueCast<BinaryPrimitive>(entry1->values[0]->value.get()), NotNull());
362 ASSERT_THAT(ValueCast<BinaryPrimitive>(entry1->values[0]->value.get())->value.data, Eq(0u));
363 ASSERT_THAT(entry1->visibility.level, Visibility::Level::kPublic);
364
365 auto entry2 = type->FindEntry("foo", 0x0100);
366 ASSERT_THAT(entry2, NotNull());
367 ASSERT_THAT(entry2->id, Eq(0x0100));
368 ASSERT_THAT(entry2->values[0], NotNull());
369 ASSERT_THAT(entry1->values[0]->value, NotNull());
370 ASSERT_THAT(ValueCast<BinaryPrimitive>(entry2->values[0]->value.get()), NotNull());
371 ASSERT_THAT(ValueCast<BinaryPrimitive>(entry2->values[0]->value.get())->value.data, Eq(1u));
372 ASSERT_THAT(entry2->visibility.level, Visibility::Level::kPrivate);
373 }
374
375 } // namespace aapt
376