Home | History | Annotate | Download | only in link
      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