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 "link/TableMerger.h" 18 19#include "filter/ConfigFilter.h" 20#include "io/FileSystem.h" 21#include "test/Test.h" 22 23using ::aapt::test::ValueEq; 24using ::testing::Contains; 25using ::testing::NotNull; 26using ::testing::UnorderedElementsAreArray; 27using ::testing::Pointee; 28using ::testing::Field; 29using ::testing::Eq; 30 31namespace aapt { 32 33struct TableMergerTest : public ::testing::Test { 34 std::unique_ptr<IAaptContext> context_; 35 36 void SetUp() override { 37 context_ = 38 test::ContextBuilder() 39 // We are compiling this package. 40 .SetCompilationPackage("com.app.a") 41 42 // Merge all packages that have this package ID. 43 .SetPackageId(0x7f) 44 45 // Mangle all packages that do not have this package name. 46 .SetNameManglerPolicy(NameManglerPolicy{"com.app.a", {"com.app.b"}}) 47 48 .Build(); 49 } 50}; 51 52TEST_F(TableMergerTest, SimpleMerge) { 53 std::unique_ptr<ResourceTable> table_a = 54 test::ResourceTableBuilder() 55 .SetPackageId("com.app.a", 0x7f) 56 .AddReference("com.app.a:id/foo", "com.app.a:id/bar") 57 .AddReference("com.app.a:id/bar", "com.app.b:id/foo") 58 .AddValue( 59 "com.app.a:styleable/view", 60 test::StyleableBuilder().AddItem("com.app.b:id/foo").Build()) 61 .Build(); 62 63 std::unique_ptr<ResourceTable> table_b = test::ResourceTableBuilder() 64 .SetPackageId("com.app.b", 0x7f) 65 .AddSimple("com.app.b:id/foo") 66 .Build(); 67 68 ResourceTable final_table; 69 TableMerger merger(context_.get(), &final_table, TableMergerOptions{}); 70 io::FileCollection collection; 71 72 ASSERT_TRUE(merger.Merge({}, table_a.get())); 73 ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection)); 74 75 EXPECT_TRUE(merger.merged_packages().count("com.app.b") != 0); 76 77 // Entries from com.app.a should not be mangled. 78 EXPECT_TRUE(final_table.FindResource(test::ParseNameOrDie("com.app.a:id/foo"))); 79 EXPECT_TRUE(final_table.FindResource(test::ParseNameOrDie("com.app.a:id/bar"))); 80 EXPECT_TRUE(final_table.FindResource(test::ParseNameOrDie("com.app.a:styleable/view"))); 81 82 // The unmangled name should not be present. 83 EXPECT_FALSE(final_table.FindResource(test::ParseNameOrDie("com.app.b:id/foo"))); 84 85 // Look for the mangled name. 86 EXPECT_TRUE(final_table.FindResource(test::ParseNameOrDie("com.app.a:id/com.app.b$foo"))); 87} 88 89TEST_F(TableMergerTest, MergeFile) { 90 ResourceTable final_table; 91 TableMergerOptions options; 92 options.auto_add_overlay = false; 93 TableMerger merger(context_.get(), &final_table, options); 94 95 ResourceFile file_desc; 96 file_desc.config = test::ParseConfigOrDie("hdpi-v4"); 97 file_desc.name = test::ParseNameOrDie("layout/main"); 98 file_desc.source = Source("res/layout-hdpi/main.xml"); 99 test::TestFile test_file("path/to/res/layout-hdpi/main.xml.flat"); 100 101 ASSERT_TRUE(merger.MergeFile(file_desc, &test_file)); 102 103 FileReference* file = test::GetValueForConfig<FileReference>( 104 &final_table, "com.app.a:layout/main", test::ParseConfigOrDie("hdpi-v4")); 105 ASSERT_THAT(file, NotNull()); 106 EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), *file->path); 107} 108 109TEST_F(TableMergerTest, MergeFileOverlay) { 110 ResourceTable final_table; 111 TableMergerOptions options; 112 options.auto_add_overlay = false; 113 TableMerger merger(context_.get(), &final_table, options); 114 115 ResourceFile file_desc; 116 file_desc.name = test::ParseNameOrDie("xml/foo"); 117 test::TestFile file_a("path/to/fileA.xml.flat"); 118 test::TestFile file_b("path/to/fileB.xml.flat"); 119 120 ASSERT_TRUE(merger.MergeFile(file_desc, &file_a)); 121 ASSERT_TRUE(merger.MergeFileOverlay(file_desc, &file_b)); 122} 123 124TEST_F(TableMergerTest, MergeFileReferences) { 125 std::unique_ptr<ResourceTable> table_a = 126 test::ResourceTableBuilder() 127 .SetPackageId("com.app.a", 0x7f) 128 .AddFileReference("com.app.a:xml/file", "res/xml/file.xml") 129 .Build(); 130 std::unique_ptr<ResourceTable> table_b = 131 test::ResourceTableBuilder() 132 .SetPackageId("com.app.b", 0x7f) 133 .AddFileReference("com.app.b:xml/file", "res/xml/file.xml") 134 .Build(); 135 136 ResourceTable final_table; 137 TableMerger merger(context_.get(), &final_table, TableMergerOptions{}); 138 io::FileCollection collection; 139 collection.InsertFile("res/xml/file.xml"); 140 141 ASSERT_TRUE(merger.Merge({}, table_a.get())); 142 ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection)); 143 144 FileReference* f = test::GetValue<FileReference>(&final_table, "com.app.a:xml/file"); 145 ASSERT_THAT(f, NotNull()); 146 EXPECT_EQ(std::string("res/xml/file.xml"), *f->path); 147 148 f = test::GetValue<FileReference>(&final_table, "com.app.a:xml/com.app.b$file"); 149 ASSERT_THAT(f, NotNull()); 150 EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path); 151} 152 153TEST_F(TableMergerTest, OverrideResourceWithOverlay) { 154 std::unique_ptr<ResourceTable> base = 155 test::ResourceTableBuilder() 156 .SetPackageId("", 0x00) 157 .AddValue("bool/foo", ResourceUtils::TryParseBool("true")) 158 .Build(); 159 std::unique_ptr<ResourceTable> overlay = 160 test::ResourceTableBuilder() 161 .SetPackageId("", 0x00) 162 .AddValue("bool/foo", ResourceUtils::TryParseBool("false")) 163 .Build(); 164 165 ResourceTable final_table; 166 TableMergerOptions options; 167 options.auto_add_overlay = false; 168 TableMerger merger(context_.get(), &final_table, options); 169 170 ASSERT_TRUE(merger.Merge({}, base.get())); 171 ASSERT_TRUE(merger.MergeOverlay({}, overlay.get())); 172 173 BinaryPrimitive* foo = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/foo"); 174 ASSERT_THAT(foo, 175 Pointee(Field(&BinaryPrimitive::value, Field(&android::Res_value::data, Eq(0u))))); 176} 177 178TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) { 179 std::unique_ptr<ResourceTable> base = 180 test::ResourceTableBuilder() 181 .SetPackageId("", 0x7f) 182 .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), 183 SymbolState::kPublic) 184 .Build(); 185 std::unique_ptr<ResourceTable> overlay = 186 test::ResourceTableBuilder() 187 .SetPackageId("", 0x7f) 188 .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), 189 SymbolState::kPublic) 190 .Build(); 191 192 ResourceTable final_table; 193 TableMergerOptions options; 194 options.auto_add_overlay = false; 195 TableMerger merger(context_.get(), &final_table, options); 196 197 ASSERT_TRUE(merger.Merge({}, base.get())); 198 ASSERT_TRUE(merger.MergeOverlay({}, overlay.get())); 199} 200 201TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) { 202 std::unique_ptr<ResourceTable> base = 203 test::ResourceTableBuilder() 204 .SetPackageId("", 0x7f) 205 .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), 206 SymbolState::kPublic) 207 .Build(); 208 std::unique_ptr<ResourceTable> overlay = 209 test::ResourceTableBuilder() 210 .SetPackageId("", 0x7f) 211 .SetSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), 212 SymbolState::kPublic) 213 .Build(); 214 215 ResourceTable final_table; 216 TableMergerOptions options; 217 options.auto_add_overlay = false; 218 TableMerger merger(context_.get(), &final_table, options); 219 220 ASSERT_TRUE(merger.Merge({}, base.get())); 221 ASSERT_FALSE(merger.MergeOverlay({}, overlay.get())); 222} 223 224TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) { 225 std::unique_ptr<ResourceTable> base = 226 test::ResourceTableBuilder() 227 .SetPackageId("", 0x7f) 228 .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), 229 SymbolState::kPublic) 230 .Build(); 231 std::unique_ptr<ResourceTable> overlay = 232 test::ResourceTableBuilder() 233 .SetPackageId("", 0x7f) 234 .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), 235 SymbolState::kPublic) 236 .Build(); 237 238 ResourceTable final_table; 239 TableMergerOptions options; 240 options.auto_add_overlay = false; 241 TableMerger merger(context_.get(), &final_table, options); 242 243 ASSERT_TRUE(merger.Merge({}, base.get())); 244 ASSERT_FALSE(merger.MergeOverlay({}, overlay.get())); 245} 246 247TEST_F(TableMergerTest, MergeAddResourceFromOverlay) { 248 std::unique_ptr<ResourceTable> table_a = 249 test::ResourceTableBuilder().SetPackageId("", 0x7f).Build(); 250 std::unique_ptr<ResourceTable> table_b = 251 test::ResourceTableBuilder() 252 .SetPackageId("", 0x7f) 253 .SetSymbolState("bool/foo", {}, SymbolState::kUndefined, true /*allow new overlay*/) 254 .AddValue("bool/foo", ResourceUtils::TryParseBool("true")) 255 .Build(); 256 257 ResourceTable final_table; 258 TableMergerOptions options; 259 options.auto_add_overlay = false; 260 TableMerger merger(context_.get(), &final_table, options); 261 262 ASSERT_TRUE(merger.Merge({}, table_a.get())); 263 ASSERT_TRUE(merger.MergeOverlay({}, table_b.get())); 264} 265 266TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) { 267 std::unique_ptr<ResourceTable> table_a = 268 test::ResourceTableBuilder().SetPackageId("", 0x7f).Build(); 269 std::unique_ptr<ResourceTable> table_b = 270 test::ResourceTableBuilder() 271 .SetPackageId("", 0x7f) 272 .AddValue("bool/foo", ResourceUtils::TryParseBool("true")) 273 .Build(); 274 275 ResourceTable final_table; 276 TableMergerOptions options; 277 options.auto_add_overlay = true; 278 TableMerger merger(context_.get(), &final_table, options); 279 280 ASSERT_TRUE(merger.Merge({}, table_a.get())); 281 ASSERT_TRUE(merger.MergeOverlay({}, table_b.get())); 282} 283 284TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) { 285 std::unique_ptr<ResourceTable> table_a = 286 test::ResourceTableBuilder().SetPackageId("", 0x7f).Build(); 287 std::unique_ptr<ResourceTable> table_b = 288 test::ResourceTableBuilder() 289 .SetPackageId("", 0x7f) 290 .AddValue("bool/foo", ResourceUtils::TryParseBool("true")) 291 .Build(); 292 293 ResourceTable final_table; 294 TableMergerOptions options; 295 options.auto_add_overlay = false; 296 TableMerger merger(context_.get(), &final_table, options); 297 298 ASSERT_TRUE(merger.Merge({}, table_a.get())); 299 ASSERT_FALSE(merger.MergeOverlay({}, table_b.get())); 300} 301 302TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) { 303 std::unique_ptr<ResourceTable> table_a = 304 test::ResourceTableBuilder() 305 .SetPackageId("com.app.a", 0x7f) 306 .AddValue("com.app.a:styleable/Foo", 307 test::StyleableBuilder() 308 .AddItem("com.app.a:attr/bar") 309 .AddItem("com.app.a:attr/foo", ResourceId(0x01010000)) 310 .Build()) 311 .AddValue("com.app.a:style/Theme", 312 test::StyleBuilder() 313 .SetParent("com.app.a:style/Parent") 314 .AddItem("com.app.a:attr/bar", util::make_unique<Id>()) 315 .AddItem("com.app.a:attr/foo", ResourceUtils::MakeBool(false)) 316 .Build()) 317 .Build(); 318 319 std::unique_ptr<ResourceTable> table_b = 320 test::ResourceTableBuilder() 321 .SetPackageId("com.app.a", 0x7f) 322 .AddValue("com.app.a:styleable/Foo", test::StyleableBuilder() 323 .AddItem("com.app.a:attr/bat") 324 .AddItem("com.app.a:attr/foo") 325 .Build()) 326 .AddValue("com.app.a:style/Theme", 327 test::StyleBuilder() 328 .SetParent("com.app.a:style/OverlayParent") 329 .AddItem("com.app.a:attr/bat", util::make_unique<Id>()) 330 .AddItem("com.app.a:attr/foo", ResourceId(0x01010000), 331 ResourceUtils::MakeBool(true)) 332 .Build()) 333 .Build(); 334 335 ResourceTable final_table; 336 TableMergerOptions options; 337 options.auto_add_overlay = true; 338 TableMerger merger(context_.get(), &final_table, options); 339 340 ASSERT_TRUE(merger.Merge({}, table_a.get())); 341 ASSERT_TRUE(merger.MergeOverlay({}, table_b.get())); 342 343 Styleable* styleable = test::GetValue<Styleable>(&final_table, "com.app.a:styleable/Foo"); 344 ASSERT_THAT(styleable, NotNull()); 345 346 std::vector<Reference> expected_refs = { 347 Reference(test::ParseNameOrDie("com.app.a:attr/bar")), 348 Reference(test::ParseNameOrDie("com.app.a:attr/bat")), 349 Reference(test::ParseNameOrDie("com.app.a:attr/foo"), ResourceId(0x01010000)), 350 }; 351 EXPECT_THAT(styleable->entries, UnorderedElementsAreArray(expected_refs)); 352 353 Style* style = test::GetValue<Style>(&final_table, "com.app.a:style/Theme"); 354 ASSERT_THAT(style, NotNull()); 355 356 std::vector<Reference> extracted_refs; 357 for (const auto& entry : style->entries) { 358 extracted_refs.push_back(entry.key); 359 } 360 EXPECT_THAT(extracted_refs, UnorderedElementsAreArray(expected_refs)); 361 362 const auto expected = ResourceUtils::MakeBool(true); 363 EXPECT_THAT(style->entries, Contains(Field(&Style::Entry::value, Pointee(ValueEq(*expected))))); 364 EXPECT_THAT(style->parent, 365 Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent"))))); 366} 367 368} // namespace aapt 369