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::StringPiece; 28 using ::testing::Eq; 29 using ::testing::NotNull; 30 using ::testing::StrEq; 31 32 namespace aapt { 33 34 TEST(ResourceTableTest, FailToAddResourceWithBadName) { 35 ResourceTable table; 36 37 EXPECT_FALSE(table.AddResource( 38 test::ParseNameOrDie("android:id/hey,there"), ConfigDescription{}, "", 39 test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), 40 test::GetDiagnostics())); 41 42 EXPECT_FALSE(table.AddResource( 43 test::ParseNameOrDie("android:id/hey:there"), ConfigDescription{}, "", 44 test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), 45 test::GetDiagnostics())); 46 } 47 48 TEST(ResourceTableTest, AddResourceWithWeirdNameWhenAddingMangledResources) { 49 ResourceTable table; 50 51 EXPECT_TRUE(table.AddResourceMangled( 52 test::ParseNameOrDie("android:id/heythere "), ConfigDescription{}, "", 53 test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), test::GetDiagnostics())); 54 } 55 56 TEST(ResourceTableTest, AddOneResource) { 57 ResourceTable table; 58 59 EXPECT_TRUE(table.AddResource( 60 test::ParseNameOrDie("android:attr/id"), ConfigDescription{}, "", 61 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build(), 62 test::GetDiagnostics())); 63 64 EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull()); 65 } 66 67 TEST(ResourceTableTest, AddMultipleResources) { 68 ResourceTable table; 69 70 ConfigDescription config; 71 ConfigDescription language_config; 72 memcpy(language_config.language, "pl", sizeof(language_config.language)); 73 74 EXPECT_TRUE(table.AddResource( 75 test::ParseNameOrDie("android:attr/layout_width"), config, "", 76 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 10u).Build(), 77 test::GetDiagnostics())); 78 79 EXPECT_TRUE(table.AddResource( 80 test::ParseNameOrDie("android:attr/id"), config, "", 81 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 12u).Build(), 82 test::GetDiagnostics())); 83 84 EXPECT_TRUE(table.AddResource( 85 test::ParseNameOrDie("android:string/ok"), config, "", 86 test::ValueBuilder<Id>().SetSource("test/path/file.xml", 14u).Build(), 87 test::GetDiagnostics())); 88 89 EXPECT_TRUE(table.AddResource( 90 test::ParseNameOrDie("android:string/ok"), language_config, "", 91 test::ValueBuilder<BinaryPrimitive>(android::Res_value{}) 92 .SetSource("test/path/file.xml", 20u) 93 .Build(), 94 test::GetDiagnostics())); 95 96 EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/layout_width"), NotNull()); 97 EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull()); 98 EXPECT_THAT(test::GetValue<Id>(&table, "android:string/ok"), NotNull()); 99 EXPECT_THAT(test::GetValueForConfig<BinaryPrimitive>(&table, "android:string/ok", language_config), NotNull()); 100 } 101 102 TEST(ResourceTableTest, OverrideWeakResourceValue) { 103 ResourceTable table; 104 105 ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "", 106 test::AttributeBuilder().SetWeak(true).Build(), 107 test::GetDiagnostics())); 108 109 Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo"); 110 ASSERT_THAT(attr, NotNull()); 111 EXPECT_TRUE(attr->IsWeak()); 112 113 ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "", 114 util::make_unique<Attribute>(), test::GetDiagnostics())); 115 116 attr = test::GetValue<Attribute>(&table, "android:attr/foo"); 117 ASSERT_THAT(attr, NotNull()); 118 EXPECT_FALSE(attr->IsWeak()); 119 } 120 121 TEST(ResourceTableTest, AllowCompatibleDuplicateAttributes) { 122 ResourceTable table; 123 124 const ResourceName name = test::ParseNameOrDie("android:attr/foo"); 125 Attribute attr_one(android::ResTable_map::TYPE_STRING); 126 attr_one.SetWeak(true); 127 Attribute attr_two(android::ResTable_map::TYPE_STRING | android::ResTable_map::TYPE_REFERENCE); 128 attr_two.SetWeak(true); 129 130 ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "", 131 util::make_unique<Attribute>(attr_one), test::GetDiagnostics())); 132 ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "", 133 util::make_unique<Attribute>(attr_two), test::GetDiagnostics())); 134 } 135 136 TEST(ResourceTableTest, ProductVaryingValues) { 137 ResourceTable table; 138 139 EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"), 140 test::ParseConfigOrDie("land"), "tablet", 141 util::make_unique<Id>(), 142 test::GetDiagnostics())); 143 EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"), 144 test::ParseConfigOrDie("land"), "phone", 145 util::make_unique<Id>(), 146 test::GetDiagnostics())); 147 148 EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull()); 149 EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull()); 150 151 Maybe<ResourceTable::SearchResult> sr = 152 table.FindResource(test::ParseNameOrDie("android:string/foo")); 153 ASSERT_TRUE(sr); 154 std::vector<ResourceConfigValue*> values = 155 sr.value().entry->FindAllValues(test::ParseConfigOrDie("land")); 156 ASSERT_EQ(2u, values.size()); 157 EXPECT_EQ(std::string("phone"), values[0]->product); 158 EXPECT_EQ(std::string("tablet"), values[1]->product); 159 } 160 161 static StringPiece LevelToString(Visibility::Level level) { 162 switch (level) { 163 case Visibility::Level::kPrivate: 164 return "private"; 165 case Visibility::Level::kPublic: 166 return "private"; 167 default: 168 return "undefined"; 169 } 170 } 171 172 static ::testing::AssertionResult VisibilityOfResource(const ResourceTable& table, 173 const ResourceNameRef& name, 174 Visibility::Level level, 175 const StringPiece& comment) { 176 Maybe<ResourceTable::SearchResult> result = table.FindResource(name); 177 if (!result) { 178 return ::testing::AssertionFailure() << "no resource '" << name << "' found in table"; 179 } 180 181 const Visibility& visibility = result.value().entry->visibility; 182 if (visibility.level != level) { 183 return ::testing::AssertionFailure() << "expected visibility " << LevelToString(level) 184 << " but got " << LevelToString(visibility.level); 185 } 186 187 if (visibility.comment != comment) { 188 return ::testing::AssertionFailure() << "expected visibility comment '" << comment 189 << "' but got '" << visibility.comment << "'"; 190 } 191 return ::testing::AssertionSuccess(); 192 } 193 194 TEST(ResourceTableTest, SetVisibility) { 195 using Level = Visibility::Level; 196 197 ResourceTable table; 198 const ResourceName name = test::ParseNameOrDie("android:string/foo"); 199 200 Visibility visibility; 201 visibility.level = Visibility::Level::kPrivate; 202 visibility.comment = "private"; 203 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics())); 204 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private")); 205 206 visibility.level = Visibility::Level::kUndefined; 207 visibility.comment = "undefined"; 208 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics())); 209 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private")); 210 211 visibility.level = Visibility::Level::kPublic; 212 visibility.comment = "public"; 213 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics())); 214 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public")); 215 216 visibility.level = Visibility::Level::kPrivate; 217 visibility.comment = "private"; 218 ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics())); 219 ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public")); 220 } 221 222 TEST(ResourceTableTest, SetAllowNew) { 223 ResourceTable table; 224 const ResourceName name = test::ParseNameOrDie("android:string/foo"); 225 226 AllowNew allow_new; 227 Maybe<ResourceTable::SearchResult> result; 228 229 allow_new.comment = "first"; 230 ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics())); 231 result = table.FindResource(name); 232 ASSERT_TRUE(result); 233 ASSERT_TRUE(result.value().entry->allow_new); 234 ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("first")); 235 236 allow_new.comment = "second"; 237 ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics())); 238 result = table.FindResource(name); 239 ASSERT_TRUE(result); 240 ASSERT_TRUE(result.value().entry->allow_new); 241 ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second")); 242 } 243 244 TEST(ResourceTableTest, SetOverlayable) { 245 ResourceTable table; 246 const ResourceName name = test::ParseNameOrDie("android:string/foo"); 247 248 Overlayable overlayable; 249 250 overlayable.comment = "first"; 251 ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics())); 252 Maybe<ResourceTable::SearchResult> result = table.FindResource(name); 253 ASSERT_TRUE(result); 254 ASSERT_TRUE(result.value().entry->overlayable); 255 ASSERT_THAT(result.value().entry->overlayable.value().comment, StrEq("first")); 256 257 overlayable.comment = "second"; 258 ASSERT_FALSE(table.SetOverlayable(name, overlayable, test::GetDiagnostics())); 259 } 260 261 } // namespace aapt 262