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 "filter/ConfigFilter.h"
18 #include "io/FileSystem.h"
19 #include "link/TableMerger.h"
20 #include "test/Builders.h"
21 #include "test/Context.h"
22 
23 #include <gtest/gtest.h>
24 
25 namespace aapt {
26 
27 struct TableMergerTest : public ::testing::Test {
28     std::unique_ptr<IAaptContext> mContext;
29 
SetUpaapt::TableMergerTest30     void SetUp() override {
31         mContext = test::ContextBuilder()
32                 // We are compiling this package.
33                 .setCompilationPackage(u"com.app.a")
34 
35                 // Merge all packages that have this package ID.
36                 .setPackageId(0x7f)
37 
38                 // Mangle all packages that do not have this package name.
39                 .setNameManglerPolicy(NameManglerPolicy{ u"com.app.a", { u"com.app.b" } })
40 
41                 .build();
42     }
43 };
44 
TEST_F(TableMergerTest,SimpleMerge)45 TEST_F(TableMergerTest, SimpleMerge) {
46     std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
47             .setPackageId(u"com.app.a", 0x7f)
48             .addReference(u"@com.app.a:id/foo", u"@com.app.a:id/bar")
49             .addReference(u"@com.app.a:id/bar", u"@com.app.b:id/foo")
50             .addValue(u"@com.app.a:styleable/view", test::StyleableBuilder()
51                     .addItem(u"@com.app.b:id/foo")
52                     .build())
53             .build();
54 
55     std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
56             .setPackageId(u"com.app.b", 0x7f)
57             .addSimple(u"@com.app.b:id/foo")
58             .build();
59 
60     ResourceTable finalTable;
61     TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
62     io::FileCollection collection;
63 
64     ASSERT_TRUE(merger.merge({}, tableA.get()));
65     ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
66 
67     EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0);
68 
69     // Entries from com.app.a should not be mangled.
70     AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/foo")));
71     AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/bar")));
72     AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:styleable/view")));
73 
74     // The unmangled name should not be present.
75     AAPT_EXPECT_FALSE(finalTable.findResource(test::parseNameOrDie(u"@com.app.b:id/foo")));
76 
77     // Look for the mangled name.
78     AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/com.app.b$foo")));
79 }
80 
TEST_F(TableMergerTest,MergeFile)81 TEST_F(TableMergerTest, MergeFile) {
82     ResourceTable finalTable;
83     TableMergerOptions options;
84     options.autoAddOverlay = false;
85     TableMerger merger(mContext.get(), &finalTable, options);
86 
87     ResourceFile fileDesc;
88     fileDesc.config = test::parseConfigOrDie("hdpi-v4");
89     fileDesc.name = test::parseNameOrDie(u"@layout/main");
90     fileDesc.source = Source("res/layout-hdpi/main.xml");
91     test::TestFile testFile("path/to/res/layout-hdpi/main.xml.flat");
92 
93     ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
94 
95     FileReference* file = test::getValueForConfig<FileReference>(&finalTable,
96                                                                  u"@com.app.a:layout/main",
97                                                                  test::parseConfigOrDie("hdpi-v4"));
98     ASSERT_NE(nullptr, file);
99     EXPECT_EQ(std::u16string(u"res/layout-hdpi-v4/main.xml"), *file->path);
100 }
101 
TEST_F(TableMergerTest,MergeFileOverlay)102 TEST_F(TableMergerTest, MergeFileOverlay) {
103     ResourceTable finalTable;
104     TableMergerOptions tableMergerOptions;
105     tableMergerOptions.autoAddOverlay = false;
106     TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
107 
108     ResourceFile fileDesc;
109     fileDesc.name = test::parseNameOrDie(u"@xml/foo");
110     test::TestFile fileA("path/to/fileA.xml.flat");
111     test::TestFile fileB("path/to/fileB.xml.flat");
112 
113     ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
114     ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
115 }
116 
TEST_F(TableMergerTest,MergeFileReferences)117 TEST_F(TableMergerTest, MergeFileReferences) {
118     std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
119             .setPackageId(u"com.app.a", 0x7f)
120             .addFileReference(u"@com.app.a:xml/file", u"res/xml/file.xml")
121             .build();
122     std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
123             .setPackageId(u"com.app.b", 0x7f)
124             .addFileReference(u"@com.app.b:xml/file", u"res/xml/file.xml")
125             .build();
126 
127     ResourceTable finalTable;
128     TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
129     io::FileCollection collection;
130     collection.insertFile("res/xml/file.xml");
131 
132     ASSERT_TRUE(merger.merge({}, tableA.get()));
133     ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
134 
135     FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file");
136     ASSERT_NE(f, nullptr);
137     EXPECT_EQ(std::u16string(u"res/xml/file.xml"), *f->path);
138 
139     f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/com.app.b$file");
140     ASSERT_NE(f, nullptr);
141     EXPECT_EQ(std::u16string(u"res/xml/com.app.b$file.xml"), *f->path);
142 }
143 
TEST_F(TableMergerTest,OverrideResourceWithOverlay)144 TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
145     std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
146             .setPackageId(u"", 0x00)
147             .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
148             .build();
149     std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
150             .setPackageId(u"", 0x00)
151             .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"false"))
152             .build();
153 
154     ResourceTable finalTable;
155     TableMergerOptions tableMergerOptions;
156     tableMergerOptions.autoAddOverlay = false;
157     TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
158 
159     ASSERT_TRUE(merger.merge({}, base.get()));
160     ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
161 
162     BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, u"@com.app.a:bool/foo");
163     ASSERT_NE(nullptr, foo);
164     EXPECT_EQ(0x0u, foo->value.data);
165 }
166 
TEST_F(TableMergerTest,MergeAddResourceFromOverlay)167 TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
168     std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
169             .setPackageId(u"", 0x7f)
170             .setSymbolState(u"@bool/foo", {}, SymbolState::kUndefined)
171             .build();
172     std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
173             .setPackageId(u"", 0x7f)
174             .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
175             .build();
176 
177     ResourceTable finalTable;
178     TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
179 
180     ASSERT_TRUE(merger.merge({}, tableA.get()));
181     ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
182 }
183 
TEST_F(TableMergerTest,MergeAddResourceFromOverlayWithAutoAddOverlay)184 TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
185     std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
186             .setPackageId(u"", 0x7f)
187             .build();
188     std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
189             .setPackageId(u"", 0x7f)
190             .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
191             .build();
192 
193     ResourceTable finalTable;
194     TableMergerOptions options;
195     options.autoAddOverlay = true;
196     TableMerger merger(mContext.get(), &finalTable, options);
197 
198     ASSERT_TRUE(merger.merge({}, tableA.get()));
199     ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
200 }
201 
TEST_F(TableMergerTest,FailToMergeNewResourceWithoutAutoAddOverlay)202 TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
203     std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
204             .setPackageId(u"", 0x7f)
205             .build();
206     std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
207             .setPackageId(u"", 0x7f)
208             .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
209             .build();
210 
211     ResourceTable finalTable;
212     TableMergerOptions options;
213     options.autoAddOverlay = false;
214     TableMerger merger(mContext.get(), &finalTable, options);
215 
216     ASSERT_TRUE(merger.merge({}, tableA.get()));
217     ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
218 }
219 
220 } // namespace aapt
221