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