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 namespace aapt {
23
24 constexpr auto TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
25 constexpr auto TYPE_STRING = android::ResTable_map::TYPE_STRING;
26
27 struct 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
39 class XmlCompatVersionerTest : public ::testing::Test {
40 public:
SetUp()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
TEST_F(XmlCompatVersionerTest,NoRulesOnlyStripsAndCopies)70 TEST_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
109 TEST_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
173 TEST_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
247 TEST_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