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