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