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 <test/Context.h>
18#include "link/Linkers.h"
19#include "test/Test.h"
20
21namespace aapt {
22
23class XmlReferenceLinkerTest : public ::testing::Test {
24public:
25    void SetUp() override {
26        mContext = test::ContextBuilder()
27                .setCompilationPackage(u"com.app.test")
28                .setNameManglerPolicy(
29                        NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
30                .addSymbolSource(test::StaticSymbolSourceBuilder()
31                        .addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
32                                   test::AttributeBuilder()
33                                        .setTypeMask(android::ResTable_map::TYPE_ENUM |
34                                                     android::ResTable_map::TYPE_DIMENSION)
35                                        .addItem(u"match_parent", 0xffffffff)
36                                        .build())
37                        .addPublicSymbol(u"@android:attr/background", ResourceId(0x01010001),
38                                   test::AttributeBuilder()
39                                        .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
40                        .addPublicSymbol(u"@android:attr/attr", ResourceId(0x01010002),
41                                   test::AttributeBuilder().build())
42                        .addPublicSymbol(u"@android:attr/text", ResourceId(0x01010003),
43                                   test::AttributeBuilder()
44                                        .setTypeMask(android::ResTable_map::TYPE_STRING)
45                                        .build())
46
47                         // Add one real symbol that was introduces in v21
48                        .addPublicSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
49                                   test::AttributeBuilder().build())
50
51                        // Private symbol.
52                        .addSymbol(u"@android:color/hidden", ResourceId(0x01020001))
53
54                        .addPublicSymbol(u"@android:id/id", ResourceId(0x01030000))
55                        .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f030000))
56                        .addSymbol(u"@com.app.test:color/green", ResourceId(0x7f020000))
57                        .addSymbol(u"@com.app.test:color/red", ResourceId(0x7f020001))
58                        .addSymbol(u"@com.app.test:attr/colorAccent", ResourceId(0x7f010000),
59                                   test::AttributeBuilder()
60                                       .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
61                        .addPublicSymbol(u"@com.app.test:attr/com.android.support$colorAccent",
62                                   ResourceId(0x7f010001), test::AttributeBuilder()
63                                       .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
64                        .addPublicSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002),
65                                   test::AttributeBuilder().build())
66                        .build())
67                .build();
68    }
69
70protected:
71    std::unique_ptr<IAaptContext> mContext;
72};
73
74TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
75    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
76        <View xmlns:android="http://schemas.android.com/apk/res/android"
77              android:layout_width="match_parent"
78              android:background="@color/green"
79              android:text="hello"
80              class="hello" />)EOF");
81
82    XmlReferenceLinker linker;
83    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
84
85    xml::Element* viewEl = xml::findRootElement(doc.get());
86    ASSERT_NE(viewEl, nullptr);
87
88    xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android",
89                                                    u"layout_width");
90    ASSERT_NE(xmlAttr, nullptr);
91    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
92    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
93    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010000));
94    ASSERT_NE(xmlAttr->compiledValue, nullptr);
95    ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
96
97    xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"background");
98    ASSERT_NE(xmlAttr, nullptr);
99    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
100    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
101    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010001));
102    ASSERT_NE(xmlAttr->compiledValue, nullptr);
103    Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
104    ASSERT_NE(ref, nullptr);
105    AAPT_ASSERT_TRUE(ref->name);
106    EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@color/green")); // Make sure the name
107                                                                         // didn't change.
108    AAPT_ASSERT_TRUE(ref->id);
109    EXPECT_EQ(ref->id.value(), ResourceId(0x7f020000));
110
111    xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"text");
112    ASSERT_NE(xmlAttr, nullptr);
113    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
114    ASSERT_FALSE(xmlAttr->compiledValue);   // Strings don't get compiled for memory sake.
115
116    xmlAttr = viewEl->findAttribute(u"", u"class");
117    ASSERT_NE(xmlAttr, nullptr);
118    AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
119    ASSERT_EQ(xmlAttr->compiledValue, nullptr);
120}
121
122TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
123    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
124        <View xmlns:android="http://schemas.android.com/apk/res/android"
125              android:colorAccent="@android:color/hidden" />)EOF");
126
127    XmlReferenceLinker linker;
128    ASSERT_FALSE(linker.consume(mContext.get(), doc.get()));
129}
130
131TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
132    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
133    <View xmlns:android="http://schemas.android.com/apk/res/android"
134          android:colorAccent="@*android:color/hidden" />)EOF");
135
136    XmlReferenceLinker linker;
137    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
138}
139
140TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
141    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
142        <View xmlns:android="http://schemas.android.com/apk/res/android"
143              android:colorAccent="#ffffff" />)EOF");
144
145    XmlReferenceLinker linker;
146    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
147    EXPECT_TRUE(linker.getSdkLevels().count(21) == 1);
148}
149
150TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
151    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
152            <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
153                  support:colorAccent="#ff0000" />)EOF");
154
155    XmlReferenceLinker linker;
156    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
157
158    xml::Element* viewEl = xml::findRootElement(doc.get());
159    ASSERT_NE(viewEl, nullptr);
160
161    xml::Attribute* xmlAttr = viewEl->findAttribute(
162            u"http://schemas.android.com/apk/res/com.android.support", u"colorAccent");
163    ASSERT_NE(xmlAttr, nullptr);
164    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
165    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
166    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010001));
167    ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
168}
169
170TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
171    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
172            <View xmlns:app="http://schemas.android.com/apk/res-auto"
173                  app:colorAccent="@app:color/red" />)EOF");
174
175    XmlReferenceLinker linker;
176    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
177
178    xml::Element* viewEl = xml::findRootElement(doc.get());
179    ASSERT_NE(viewEl, nullptr);
180
181    xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res-auto",
182                                                    u"colorAccent");
183    ASSERT_NE(xmlAttr, nullptr);
184    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
185    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
186    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010000));
187    Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
188    ASSERT_NE(ref, nullptr);
189    AAPT_ASSERT_TRUE(ref->name);
190    AAPT_ASSERT_TRUE(ref->id);
191    EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
192}
193
194TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
195    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
196            <View xmlns:app="http://schemas.android.com/apk/res/android"
197                  app:attr="@app:id/id">
198              <View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
199                    app:attr="@app:id/id"/>
200            </View>)EOF");
201
202    XmlReferenceLinker linker;
203    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
204
205    xml::Element* viewEl = xml::findRootElement(doc.get());
206    ASSERT_NE(viewEl, nullptr);
207
208    // All attributes and references in this element should be referring to "android" (0x01).
209    xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android",
210                                                    u"attr");
211    ASSERT_NE(xmlAttr, nullptr);
212    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
213    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
214    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010002));
215    Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
216    ASSERT_NE(ref, nullptr);
217    AAPT_ASSERT_TRUE(ref->id);
218    EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
219
220    ASSERT_FALSE(viewEl->getChildElements().empty());
221    viewEl = viewEl->getChildElements().front();
222    ASSERT_NE(viewEl, nullptr);
223
224    // All attributes and references in this element should be referring to "com.app.test" (0x7f).
225    xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/com.app.test", u"attr");
226    ASSERT_NE(xmlAttr, nullptr);
227    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
228    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
229    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
230    ref = valueCast<Reference>(xmlAttr->compiledValue.get());
231    ASSERT_NE(ref, nullptr);
232    AAPT_ASSERT_TRUE(ref->id);
233    EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
234}
235
236TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
237    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
238            <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
239                  android:attr="@id/id"/>)EOF");
240
241    XmlReferenceLinker linker;
242    ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
243
244    xml::Element* viewEl = xml::findRootElement(doc.get());
245    ASSERT_NE(viewEl, nullptr);
246
247    // All attributes and references in this element should be referring to "com.app.test" (0x7f).
248    xml::Attribute* xmlAttr = viewEl->findAttribute(
249            u"http://schemas.android.com/apk/res/com.app.test", u"attr");
250    ASSERT_NE(xmlAttr, nullptr);
251    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
252    AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
253    EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
254    Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
255    ASSERT_NE(ref, nullptr);
256    AAPT_ASSERT_TRUE(ref->id);
257    EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
258}
259
260} // namespace aapt
261