• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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