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