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:
SetUp()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
TEST_F(XmlCompatVersionerTest,NoRulesOnlyStripsAndCopies)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(nullptr);
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(nullptr);
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(nullptr);
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(nullptr);
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