1/* 2 * Copyright (C) 2017 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/XmlCompatVersioner.h" 18 19#include "Linkers.h" 20#include "test/Test.h" 21 22namespace aapt { 23 24constexpr auto TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION; 25constexpr auto TYPE_STRING = android::ResTable_map::TYPE_STRING; 26 27struct R { 28 struct attr { 29 enum : uint32_t { 30 paddingLeft = 0x010100d6u, // (API 1) 31 paddingRight = 0x010100d8u, // (API 1) 32 progressBarPadding = 0x01010319u, // (API 11) 33 paddingStart = 0x010103b3u, // (API 17) 34 paddingHorizontal = 0x0101053du, // (API 26) 35 }; 36 }; 37}; 38 39class XmlCompatVersionerTest : public ::testing::Test { 40 public: 41 void SetUp() override { 42 context_ = 43 test::ContextBuilder() 44 .SetCompilationPackage("com.app") 45 .SetPackageId(0x7f) 46 .SetPackageType(PackageType::kApp) 47 .SetMinSdkVersion(SDK_GINGERBREAD) 48 .AddSymbolSource( 49 test::StaticSymbolSourceBuilder() 50 .AddPublicSymbol("android:attr/paddingLeft", R::attr::paddingLeft, 51 util::make_unique<Attribute>(false, TYPE_DIMENSION)) 52 .AddPublicSymbol("android:attr/paddingRight", R::attr::paddingRight, 53 util::make_unique<Attribute>(false, TYPE_DIMENSION)) 54 .AddPublicSymbol("android:attr/progressBarPadding", R::attr::progressBarPadding, 55 util::make_unique<Attribute>(false, TYPE_DIMENSION)) 56 .AddPublicSymbol("android:attr/paddingStart", R::attr::paddingStart, 57 util::make_unique<Attribute>(false, TYPE_DIMENSION)) 58 .AddPublicSymbol("android:attr/paddingHorizontal", R::attr::paddingHorizontal, 59 util::make_unique<Attribute>(false, TYPE_DIMENSION)) 60 .AddSymbol("com.app:attr/foo", ResourceId(0x7f010000), 61 util::make_unique<Attribute>(false, TYPE_STRING)) 62 .Build()) 63 .Build(); 64 } 65 66 protected: 67 std::unique_ptr<IAaptContext> context_; 68}; 69 70TEST_F(XmlCompatVersionerTest, NoRulesOnlyStripsAndCopies) { 71 auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF( 72 <View xmlns:android="http://schemas.android.com/apk/res/android" 73 xmlns:app="http://schemas.android.com/apk/res-auto" 74 android:paddingHorizontal="24dp" 75 app:foo="16dp" 76 foo="bar"/>)EOF"); 77 78 XmlReferenceLinker linker; 79 ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); 80 81 XmlCompatVersioner::Rules rules; 82 const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1}; 83 84 XmlCompatVersioner versioner(&rules); 85 std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs = 86 versioner.Process(context_.get(), doc.get(), api_range); 87 ASSERT_EQ(2u, versioned_docs.size()); 88 89 xml::Element* el; 90 91 // Source XML file's sdkVersion == 0, so the first one must also have the same sdkVersion. 92 EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion); 93 el = xml::FindRootElement(versioned_docs[0].get()); 94 ASSERT_NE(nullptr, el); 95 EXPECT_EQ(2u, el->attributes.size()); 96 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 97 EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo")); 98 EXPECT_NE(nullptr, el->FindAttribute({}, "foo")); 99 100 EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion); 101 el = xml::FindRootElement(versioned_docs[1].get()); 102 ASSERT_NE(nullptr, el); 103 EXPECT_EQ(3u, el->attributes.size()); 104 EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 105 EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo")); 106 EXPECT_NE(nullptr, el->FindAttribute({}, "foo")); 107} 108 109TEST_F(XmlCompatVersionerTest, SingleRule) { 110 auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF( 111 <View xmlns:android="http://schemas.android.com/apk/res/android" 112 xmlns:app="http://schemas.android.com/apk/res-auto" 113 android:paddingHorizontal="24dp" 114 app:foo="16dp" 115 foo="bar"/>)EOF"); 116 117 XmlReferenceLinker linker; 118 ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); 119 120 XmlCompatVersioner::Rules rules; 121 rules[R::attr::paddingHorizontal] = 122 util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>( 123 {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)}, 124 ReplacementAttr{"paddingRight", R::attr::paddingRight, 125 Attribute(false, TYPE_DIMENSION)}})); 126 127 const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1}; 128 129 XmlCompatVersioner versioner(&rules); 130 std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs = 131 versioner.Process(context_.get(), doc.get(), api_range); 132 ASSERT_EQ(2u, versioned_docs.size()); 133 134 xml::Element* el; 135 136 EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion); 137 el = xml::FindRootElement(versioned_docs[0].get()); 138 ASSERT_NE(nullptr, el); 139 EXPECT_EQ(4u, el->attributes.size()); 140 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 141 EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo")); 142 EXPECT_NE(nullptr, el->FindAttribute({}, "foo")); 143 144 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"); 145 ASSERT_NE(nullptr, attr); 146 ASSERT_NE(nullptr, attr->compiled_value); 147 ASSERT_TRUE(attr->compiled_attribute); 148 149 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight"); 150 ASSERT_NE(nullptr, attr); 151 ASSERT_NE(nullptr, attr->compiled_value); 152 ASSERT_TRUE(attr->compiled_attribute); 153 154 EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion); 155 el = xml::FindRootElement(versioned_docs[1].get()); 156 ASSERT_NE(nullptr, el); 157 EXPECT_EQ(5u, el->attributes.size()); 158 EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 159 EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo")); 160 EXPECT_NE(nullptr, el->FindAttribute({}, "foo")); 161 162 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"); 163 ASSERT_NE(nullptr, attr); 164 ASSERT_NE(nullptr, attr->compiled_value); 165 ASSERT_TRUE(attr->compiled_attribute); 166 167 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight"); 168 ASSERT_NE(nullptr, attr); 169 ASSERT_NE(nullptr, attr->compiled_value); 170 ASSERT_TRUE(attr->compiled_attribute); 171} 172 173TEST_F(XmlCompatVersionerTest, ChainedRule) { 174 auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF( 175 <View xmlns:android="http://schemas.android.com/apk/res/android" 176 android:paddingHorizontal="24dp" />)EOF"); 177 178 XmlReferenceLinker linker; 179 ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); 180 181 XmlCompatVersioner::Rules rules; 182 rules[R::attr::progressBarPadding] = 183 util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>( 184 {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)}, 185 ReplacementAttr{"paddingRight", R::attr::paddingRight, 186 Attribute(false, TYPE_DIMENSION)}})); 187 rules[R::attr::paddingHorizontal] = 188 util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>({ReplacementAttr{ 189 "progressBarPadding", R::attr::progressBarPadding, Attribute(false, TYPE_DIMENSION)}})); 190 191 const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1}; 192 193 XmlCompatVersioner versioner(&rules); 194 std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs = 195 versioner.Process(context_.get(), doc.get(), api_range); 196 ASSERT_EQ(3u, versioned_docs.size()); 197 198 xml::Element* el; 199 200 EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion); 201 el = xml::FindRootElement(versioned_docs[0].get()); 202 ASSERT_NE(nullptr, el); 203 EXPECT_EQ(2u, el->attributes.size()); 204 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 205 206 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"); 207 ASSERT_NE(nullptr, attr); 208 ASSERT_NE(nullptr, attr->compiled_value); 209 ASSERT_TRUE(attr->compiled_attribute); 210 211 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight"); 212 ASSERT_NE(nullptr, attr); 213 ASSERT_NE(nullptr, attr->compiled_value); 214 ASSERT_TRUE(attr->compiled_attribute); 215 216 EXPECT_EQ(static_cast<uint16_t>(SDK_HONEYCOMB), versioned_docs[1]->file.config.sdkVersion); 217 el = xml::FindRootElement(versioned_docs[1].get()); 218 ASSERT_NE(nullptr, el); 219 EXPECT_EQ(1u, el->attributes.size()); 220 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 221 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft")); 222 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight")); 223 224 attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding"); 225 ASSERT_NE(nullptr, attr); 226 ASSERT_NE(nullptr, attr->compiled_value); 227 ASSERT_TRUE(attr->compiled_attribute); 228 229 EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[2]->file.config.sdkVersion); 230 el = xml::FindRootElement(versioned_docs[2].get()); 231 ASSERT_NE(nullptr, el); 232 EXPECT_EQ(2u, el->attributes.size()); 233 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft")); 234 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight")); 235 236 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"); 237 ASSERT_NE(nullptr, attr); 238 ASSERT_NE(nullptr, attr->compiled_value); 239 ASSERT_TRUE(attr->compiled_attribute); 240 241 attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding"); 242 ASSERT_NE(nullptr, attr); 243 ASSERT_NE(nullptr, attr->compiled_value); 244 ASSERT_TRUE(attr->compiled_attribute); 245} 246 247TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) { 248 auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF( 249 <View xmlns:android="http://schemas.android.com/apk/res/android" 250 android:paddingHorizontal="24dp" 251 android:paddingLeft="16dp" 252 android:paddingRight="16dp"/>)EOF"); 253 254 XmlReferenceLinker linker; 255 ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); 256 257 Item* padding_horizontal_value = xml::FindRootElement(doc.get()) 258 ->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal") 259 ->compiled_value.get(); 260 ASSERT_NE(nullptr, padding_horizontal_value); 261 262 XmlCompatVersioner::Rules rules; 263 rules[R::attr::paddingHorizontal] = 264 util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>( 265 {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)}, 266 ReplacementAttr{"paddingRight", R::attr::paddingRight, 267 Attribute(false, TYPE_DIMENSION)}})); 268 269 const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1}; 270 271 XmlCompatVersioner versioner(&rules); 272 std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs = 273 versioner.Process(context_.get(), doc.get(), api_range); 274 ASSERT_EQ(2u, versioned_docs.size()); 275 276 xml::Element* el; 277 278 EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion); 279 el = xml::FindRootElement(versioned_docs[0].get()); 280 ASSERT_NE(nullptr, el); 281 EXPECT_EQ(2u, el->attributes.size()); 282 EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")); 283 284 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"); 285 ASSERT_NE(nullptr, attr); 286 ASSERT_NE(nullptr, attr->compiled_value); 287 ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get())); 288 ASSERT_TRUE(attr->compiled_attribute); 289 290 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight"); 291 ASSERT_NE(nullptr, attr); 292 ASSERT_NE(nullptr, attr->compiled_value); 293 ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get())); 294 ASSERT_TRUE(attr->compiled_attribute); 295 296 EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion); 297 el = xml::FindRootElement(versioned_docs[1].get()); 298 ASSERT_NE(nullptr, el); 299 EXPECT_EQ(3u, el->attributes.size()); 300 301 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"); 302 ASSERT_NE(nullptr, attr); 303 ASSERT_NE(nullptr, attr->compiled_value); 304 ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get())); 305 ASSERT_TRUE(attr->compiled_attribute); 306 307 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"); 308 ASSERT_NE(nullptr, attr); 309 ASSERT_NE(nullptr, attr->compiled_value); 310 ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get())); 311 ASSERT_TRUE(attr->compiled_attribute); 312 313 attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight"); 314 ASSERT_NE(nullptr, attr); 315 ASSERT_NE(nullptr, attr->compiled_value); 316 ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get())); 317 ASSERT_TRUE(attr->compiled_attribute); 318} 319 320} // namespace aapt 321