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,DoNotOverrideResourceComment)181 TEST_F(TableMergerTest, DoNotOverrideResourceComment) {
182   std::unique_ptr<Value> foo_original = ResourceUtils::TryParseBool("true");
183   foo_original->SetComment(android::StringPiece("Original foo comment"));
184   std::unique_ptr<Value> bar_original = ResourceUtils::TryParseBool("true");
185 
186   std::unique_ptr<Value> foo_overlay =  ResourceUtils::TryParseBool("false");
187   foo_overlay->SetComment(android::StringPiece("Overlay foo comment"));
188   std::unique_ptr<Value> bar_overlay =  ResourceUtils::TryParseBool("false");
189   bar_overlay->SetComment(android::StringPiece("Overlay bar comment"));
190   std::unique_ptr<Value> baz_overlay =  ResourceUtils::TryParseBool("false");
191   baz_overlay->SetComment(android::StringPiece("Overlay baz comment"));
192 
193   std::unique_ptr<ResourceTable> base =
194       test::ResourceTableBuilder()
195           .SetPackageId("", 0x00)
196           .AddValue("bool/foo", std::move(foo_original))
197           .AddValue("bool/bar", std::move(bar_original))
198           .Build();
199 
200   std::unique_ptr<ResourceTable> overlay =
201       test::ResourceTableBuilder()
202           .SetPackageId("", 0x00)
203           .AddValue("bool/foo", std::move(foo_overlay))
204           .AddValue("bool/bar", std::move(bar_overlay))
205           .AddValue("bool/baz", std::move(baz_overlay))
206           .Build();
207 
208   ResourceTable final_table;
209   TableMergerOptions options;
210   options.auto_add_overlay = true;
211   TableMerger merger(context_.get(), &final_table, options);
212 
213   ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
214   ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
215 
216   BinaryPrimitive* foo = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/foo");
217   EXPECT_THAT(foo, Pointee(Property(&BinaryPrimitive::GetComment, StrEq("Original foo comment"))));
218   BinaryPrimitive* bar = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/bar");
219   EXPECT_THAT(bar, Pointee(Property(&BinaryPrimitive::GetComment, StrEq(""))));
220   BinaryPrimitive* baz = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/baz");
221   EXPECT_THAT(baz, Pointee(Property(&BinaryPrimitive::GetComment, StrEq("Overlay baz comment"))));
222 }
223 
TEST_F(TableMergerTest,OverrideSameResourceIdsWithOverlay)224 TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) {
225   std::unique_ptr<ResourceTable> base =
226       test::ResourceTableBuilder()
227           .SetPackageId("", 0x7f)
228           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
229           .Build();
230   std::unique_ptr<ResourceTable> overlay =
231       test::ResourceTableBuilder()
232           .SetPackageId("", 0x7f)
233           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
234           .Build();
235 
236   ResourceTable final_table;
237   TableMergerOptions options;
238   options.auto_add_overlay = false;
239   TableMerger merger(context_.get(), &final_table, options);
240 
241   ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
242   ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
243 }
244 
TEST_F(TableMergerTest,FailToOverrideConflictingTypeIdsWithOverlay)245 TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) {
246   std::unique_ptr<ResourceTable> base =
247       test::ResourceTableBuilder()
248           .SetPackageId("", 0x7f)
249           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
250           .Build();
251   std::unique_ptr<ResourceTable> overlay =
252       test::ResourceTableBuilder()
253           .SetPackageId("", 0x7f)
254           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), Visibility::Level::kPublic)
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({}, base.get(), false /*overlay*/));
263   ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
264 }
265 
TEST_F(TableMergerTest,FailToOverrideConflictingEntryIdsWithOverlay)266 TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) {
267   std::unique_ptr<ResourceTable> base =
268       test::ResourceTableBuilder()
269           .SetPackageId("", 0x7f)
270           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
271           .Build();
272   std::unique_ptr<ResourceTable> overlay =
273       test::ResourceTableBuilder()
274           .SetPackageId("", 0x7f)
275           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), Visibility::Level::kPublic)
276           .Build();
277 
278   ResourceTable final_table;
279   TableMergerOptions options;
280   options.auto_add_overlay = false;
281   TableMerger merger(context_.get(), &final_table, options);
282 
283   ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
284   ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
285 }
286 
TEST_F(TableMergerTest,FailConflictingVisibility)287 TEST_F(TableMergerTest, FailConflictingVisibility) {
288   std::unique_ptr<ResourceTable> base =
289       test::ResourceTableBuilder()
290           .SetPackageId("", 0x7f)
291           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
292           .Build();
293   std::unique_ptr<ResourceTable> overlay =
294       test::ResourceTableBuilder()
295           .SetPackageId("", 0x7f)
296           .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPrivate)
297           .Build();
298 
299   // It should fail if the "--strict-visibility" flag is set.
300   ResourceTable final_table;
301   TableMergerOptions options;
302   options.auto_add_overlay = false;
303   options.strict_visibility = true;
304   TableMerger merger(context_.get(), &final_table, options);
305 
306   ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
307   ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
308 
309   // But it should still pass if the flag is not set.
310   ResourceTable final_table2;
311   options.strict_visibility = false;
312   TableMerger merger2(context_.get(), &final_table2, options);
313 
314   ASSERT_TRUE(merger2.Merge({}, base.get(), false /*overlay*/));
315   ASSERT_TRUE(merger2.Merge({}, overlay.get(), true /*overlay*/));
316 }
317 
TEST_F(TableMergerTest,MergeAddResourceFromOverlay)318 TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
319   std::unique_ptr<ResourceTable> table_a =
320       test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
321   std::unique_ptr<ResourceTable> table_b =
322       test::ResourceTableBuilder()
323           .SetPackageId("", 0x7f)
324           .SetSymbolState("bool/foo", {}, Visibility::Level::kUndefined, true /*allow new overlay*/)
325           .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
326           .Build();
327 
328   ResourceTable final_table;
329   TableMergerOptions options;
330   options.auto_add_overlay = false;
331   TableMerger merger(context_.get(), &final_table, options);
332 
333   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
334   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
335 }
336 
TEST_F(TableMergerTest,MergeAddResourceFromOverlayWithAutoAddOverlay)337 TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
338   std::unique_ptr<ResourceTable> table_a =
339       test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
340   std::unique_ptr<ResourceTable> table_b =
341       test::ResourceTableBuilder()
342           .SetPackageId("", 0x7f)
343           .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
344           .Build();
345 
346   ResourceTable final_table;
347   TableMergerOptions options;
348   options.auto_add_overlay = true;
349   TableMerger merger(context_.get(), &final_table, options);
350 
351   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
352   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
353 }
354 
TEST_F(TableMergerTest,OverrideAttributeSameFormatsWithOverlay)355 TEST_F(TableMergerTest, OverrideAttributeSameFormatsWithOverlay) {
356   std::unique_ptr<ResourceTable> base =
357       test::ResourceTableBuilder()
358           .SetPackageId("", 0x7f)
359           .AddValue("attr/foo", test::AttributeBuilder()
360               .SetTypeMask(android::ResTable_map::TYPE_STRING)
361               .SetWeak(false)
362               .Build())
363           .Build();
364 
365   std::unique_ptr<ResourceTable> overlay =
366       test::ResourceTableBuilder()
367           .SetPackageId("", 0x7f)
368           .AddValue("attr/foo", test::AttributeBuilder()
369               .SetTypeMask(android::ResTable_map::TYPE_STRING)
370               .SetWeak(false)
371               .Build())
372           .Build();
373 
374   ResourceTable final_table;
375   TableMergerOptions options;
376   options.auto_add_overlay = false;
377   TableMerger merger(context_.get(), &final_table, options);
378 
379   ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
380   ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
381 }
382 
TEST_F(TableMergerTest,FailToOverrideConflictingAttributeFormatsWithOverlay)383 TEST_F(TableMergerTest, FailToOverrideConflictingAttributeFormatsWithOverlay) {
384   std::unique_ptr<ResourceTable> base =
385       test::ResourceTableBuilder()
386           .SetPackageId("", 0x7f)
387           .AddValue("attr/foo", test::AttributeBuilder()
388               .SetTypeMask(android::ResTable_map::TYPE_ANY)
389               .SetWeak(false)
390               .Build())
391           .Build();
392 
393   std::unique_ptr<ResourceTable> overlay =
394       test::ResourceTableBuilder()
395           .SetPackageId("", 0x7f)
396           .AddValue("attr/foo", test::AttributeBuilder()
397               .SetTypeMask(android::ResTable_map::TYPE_STRING)
398               .SetWeak(false)
399               .Build())
400           .Build();
401 
402   ResourceTable final_table;
403   TableMergerOptions options;
404   options.auto_add_overlay = false;
405   TableMerger merger(context_.get(), &final_table, options);
406 
407   ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
408   ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
409 }
410 
TEST_F(TableMergerTest,FailToMergeNewResourceWithoutAutoAddOverlay)411 TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
412   std::unique_ptr<ResourceTable> table_a =
413       test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
414   std::unique_ptr<ResourceTable> table_b =
415       test::ResourceTableBuilder()
416           .SetPackageId("", 0x7f)
417           .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
418           .Build();
419 
420   ResourceTable final_table;
421   TableMergerOptions options;
422   options.auto_add_overlay = false;
423   TableMerger merger(context_.get(), &final_table, options);
424 
425   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
426   ASSERT_FALSE(merger.Merge({}, table_b.get(), true /*overlay*/));
427 }
428 
TEST_F(TableMergerTest,OverlaidStyleablesAndStylesShouldBeMerged)429 TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
430   std::unique_ptr<ResourceTable> table_a =
431       test::ResourceTableBuilder()
432           .SetPackageId("com.app.a", 0x7f)
433           .AddValue("com.app.a:styleable/Foo",
434                     test::StyleableBuilder()
435                         .AddItem("com.app.a:attr/bar")
436                         .AddItem("com.app.a:attr/foo", ResourceId(0x01010000))
437                         .Build())
438           .AddValue("com.app.a:style/Theme",
439                     test::StyleBuilder()
440                         .SetParent("com.app.a:style/Parent")
441                         .AddItem("com.app.a:attr/bar", util::make_unique<Id>())
442                         .AddItem("com.app.a:attr/foo", ResourceUtils::MakeBool(false))
443                         .Build())
444           .Build();
445 
446   std::unique_ptr<ResourceTable> table_b =
447       test::ResourceTableBuilder()
448           .SetPackageId("com.app.a", 0x7f)
449           .AddValue("com.app.a:styleable/Foo", test::StyleableBuilder()
450                                                    .AddItem("com.app.a:attr/bat")
451                                                    .AddItem("com.app.a:attr/foo")
452                                                    .Build())
453           .AddValue("com.app.a:style/Theme",
454                     test::StyleBuilder()
455                         .SetParent("com.app.a:style/OverlayParent")
456                         .AddItem("com.app.a:attr/bat", util::make_unique<Id>())
457                         .AddItem("com.app.a:attr/foo", ResourceId(0x01010000),
458                                  ResourceUtils::MakeBool(true))
459                         .Build())
460           .Build();
461 
462   ResourceTable final_table;
463   TableMergerOptions options;
464   options.auto_add_overlay = true;
465   TableMerger merger(context_.get(), &final_table, options);
466 
467   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
468   ASSERT_TRUE(merger.Merge({}, table_b.get(), true /*overlay*/));
469 
470   Styleable* styleable = test::GetValue<Styleable>(&final_table, "com.app.a:styleable/Foo");
471   ASSERT_THAT(styleable, NotNull());
472 
473   std::vector<Reference> expected_refs = {
474       Reference(test::ParseNameOrDie("com.app.a:attr/bar")),
475       Reference(test::ParseNameOrDie("com.app.a:attr/bat")),
476       Reference(test::ParseNameOrDie("com.app.a:attr/foo"), ResourceId(0x01010000)),
477   };
478   EXPECT_THAT(styleable->entries, UnorderedElementsAreArray(expected_refs));
479 
480   Style* style = test::GetValue<Style>(&final_table, "com.app.a:style/Theme");
481   ASSERT_THAT(style, NotNull());
482 
483   std::vector<Reference> extracted_refs;
484   for (const auto& entry : style->entries) {
485     extracted_refs.push_back(entry.key);
486   }
487   EXPECT_THAT(extracted_refs, UnorderedElementsAreArray(expected_refs));
488 
489   const auto expected = ResourceUtils::MakeBool(true);
490   EXPECT_THAT(style->entries, Contains(Field(&Style::Entry::value, Pointee(ValueEq(*expected)))));
491   EXPECT_THAT(style->parent,
492               Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
493 }
494 
TEST_F(TableMergerTest,SetOverlayable)495 TEST_F(TableMergerTest, SetOverlayable) {
496   auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
497                                                   "overlay://customization");
498   OverlayableItem overlayable_item(overlayable);
499   overlayable_item.policies |= OverlayableItem::Policy::kProduct;
500   overlayable_item.policies |= OverlayableItem::Policy::kVendor;
501 
502   std::unique_ptr<ResourceTable> table_a =
503       test::ResourceTableBuilder()
504           .SetPackageId("com.app.a", 0x7f)
505           .SetOverlayable("bool/foo", overlayable_item)
506           .Build();
507 
508   std::unique_ptr<ResourceTable> table_b =
509       test::ResourceTableBuilder()
510           .SetPackageId("com.app.a", 0x7f)
511           .AddSimple("bool/foo")
512           .Build();
513 
514   ResourceTable final_table;
515   TableMergerOptions options;
516   options.auto_add_overlay = true;
517   TableMerger merger(context_.get(), &final_table, options);
518   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
519   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
520 
521   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
522   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
523   ASSERT_TRUE(search_result);
524   ASSERT_TRUE(search_result.value().entry->overlayable_item);
525   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
526   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
527   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
528   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
529                                                    | OverlayableItem::Policy::kVendor));
530 }
531 
TEST_F(TableMergerTest,SetOverlayableLater)532 TEST_F(TableMergerTest, SetOverlayableLater) {
533   auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
534                                                   "overlay://customization");
535   std::unique_ptr<ResourceTable> table_a =
536       test::ResourceTableBuilder()
537           .SetPackageId("com.app.a", 0x7f)
538           .AddSimple("bool/foo")
539           .Build();
540 
541   OverlayableItem overlayable_item(overlayable);
542   overlayable_item.policies |= OverlayableItem::Policy::kPublic;
543   overlayable_item.policies |= OverlayableItem::Policy::kSystem;
544   std::unique_ptr<ResourceTable> table_b =
545       test::ResourceTableBuilder()
546           .SetPackageId("com.app.a", 0x7f)
547           .SetOverlayable("bool/foo", overlayable_item)
548           .Build();
549 
550   ResourceTable final_table;
551   TableMergerOptions options;
552   options.auto_add_overlay = true;
553   TableMerger merger(context_.get(), &final_table, options);
554   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
555   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
556 
557   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
558   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
559   ASSERT_TRUE(search_result);
560   ASSERT_TRUE(search_result.value().entry->overlayable_item);
561   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
562   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
563   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
564   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
565                                                    | OverlayableItem::Policy::kSystem));
566 }
567 
TEST_F(TableMergerTest,SameResourceDifferentNameFail)568 TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
569   auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
570                                                          "overlay://customization");
571   OverlayableItem overlayable_item_first(overlayable_first);
572   overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
573   std::unique_ptr<ResourceTable> table_a =
574       test::ResourceTableBuilder()
575           .SetPackageId("com.app.a", 0x7f)
576           .SetOverlayable("bool/foo", overlayable_item_first)
577           .Build();
578 
579   auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
580                                                           "overlay://customization");
581   OverlayableItem overlayable_item_second(overlayable_second);
582   overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
583   std::unique_ptr<ResourceTable> table_b =
584       test::ResourceTableBuilder()
585           .SetPackageId("com.app.a", 0x7f)
586           .SetOverlayable("bool/foo", overlayable_item_second)
587           .Build();
588 
589   ResourceTable final_table;
590   TableMergerOptions options;
591   options.auto_add_overlay = true;
592   TableMerger merger(context_.get(), &final_table, options);
593   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
594   ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
595 }
596 
TEST_F(TableMergerTest,SameResourceDifferentActorFail)597 TEST_F(TableMergerTest, SameResourceDifferentActorFail) {
598   auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
599                                                          "overlay://customization");
600   OverlayableItem overlayable_item_first(overlayable_first);
601   overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
602   std::unique_ptr<ResourceTable> table_a =
603       test::ResourceTableBuilder()
604           .SetPackageId("com.app.a", 0x7f)
605           .SetOverlayable("bool/foo", overlayable_item_first)
606           .Build();
607 
608   auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
609                                                           "overlay://theme");
610   OverlayableItem overlayable_item_second(overlayable_second);
611   overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
612   std::unique_ptr<ResourceTable> table_b =
613       test::ResourceTableBuilder()
614           .SetPackageId("com.app.a", 0x7f)
615           .SetOverlayable("bool/foo", overlayable_item_second)
616           .Build();
617 
618   ResourceTable final_table;
619   TableMergerOptions options;
620   options.auto_add_overlay = true;
621   TableMerger merger(context_.get(), &final_table, options);
622   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
623   ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
624 }
625 
TEST_F(TableMergerTest,SameResourceDifferentPoliciesFail)626 TEST_F(TableMergerTest, SameResourceDifferentPoliciesFail) {
627   auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
628                                                          "overlay://customization");
629   OverlayableItem overlayable_item_first(overlayable_first);
630   overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
631   std::unique_ptr<ResourceTable> table_a =
632       test::ResourceTableBuilder()
633           .SetPackageId("com.app.a", 0x7f)
634           .SetOverlayable("bool/foo", overlayable_item_first)
635           .Build();
636 
637   auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
638                                                           "overlay://customization");
639   OverlayableItem overlayable_item_second(overlayable_second);
640   overlayable_item_second.policies |= OverlayableItem::Policy::kSignature;
641   std::unique_ptr<ResourceTable> table_b =
642       test::ResourceTableBuilder()
643           .SetPackageId("com.app.a", 0x7f)
644           .SetOverlayable("bool/foo", overlayable_item_second)
645           .Build();
646 
647   ResourceTable final_table;
648   TableMergerOptions options;
649   options.auto_add_overlay = true;
650   TableMerger merger(context_.get(), &final_table, options);
651   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
652   ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
653 }
654 
TEST_F(TableMergerTest,SameResourceSameOverlayable)655 TEST_F(TableMergerTest, SameResourceSameOverlayable) {
656   auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
657                                                   "overlay://customization");
658 
659   OverlayableItem overlayable_item_first(overlayable);
660   overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
661   std::unique_ptr<ResourceTable> table_a =
662       test::ResourceTableBuilder()
663           .SetPackageId("com.app.a", 0x7f)
664           .SetOverlayable("bool/foo", overlayable_item_first)
665           .Build();
666 
667   OverlayableItem overlayable_item_second(overlayable);
668   overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
669   std::unique_ptr<ResourceTable> table_b =
670       test::ResourceTableBuilder()
671           .SetPackageId("com.app.a", 0x7f)
672           .SetOverlayable("bool/foo", overlayable_item_second)
673           .Build();
674 
675   ResourceTable final_table;
676   TableMergerOptions options;
677   options.auto_add_overlay = true;
678   TableMerger merger(context_.get(), &final_table, options);
679   ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
680   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
681 }
682 
683 }  // namespace aapt
684