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
25namespace aapt {
26
27struct TableMergerTest : public ::testing::Test {
28    std::unique_ptr<IAaptContext> mContext;
29
30    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
45TEST_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
81TEST_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
102TEST_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
117TEST_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
144TEST_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
167TEST_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
184TEST_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
202TEST_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