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