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 "configuration/ConfigurationParser.h"
18 
19 #include <string>
20 
21 #include "android-base/stringprintf.h"
22 #include "androidfw/ResourceTypes.h"
23 
24 #include "SdkConstants.h"
25 #include "configuration/ConfigurationParser.internal.h"
26 #include "test/Test.h"
27 #include "xml/XmlDom.h"
28 
29 using ::android::ConfigDescription;
30 
31 namespace aapt {
32 
33 namespace configuration {
34 
operator ==(const ConfiguredArtifact & lhs,const ConfiguredArtifact & rhs)35 bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
36   return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group &&
37          lhs.screen_density_group == rhs.screen_density_group &&
38          lhs.locale_group == rhs.locale_group && lhs.android_sdk == rhs.android_sdk &&
39          lhs.device_feature_group == rhs.device_feature_group &&
40          lhs.gl_texture_group == rhs.gl_texture_group;
41 }
42 
43 namespace handler {
44 
45 namespace {
46 
47 using ::aapt::configuration::Abi;
48 using ::aapt::configuration::AndroidManifest;
49 using ::aapt::configuration::AndroidSdk;
50 using ::aapt::configuration::ConfiguredArtifact;
51 using ::aapt::configuration::DeviceFeature;
52 using ::aapt::configuration::ExtractConfiguration;
53 using ::aapt::configuration::GlTexture;
54 using ::aapt::configuration::Locale;
55 using ::aapt::configuration::PostProcessingConfiguration;
56 using ::aapt::xml::Element;
57 using ::aapt::xml::NodeCast;
58 using ::android::ResTable_config;
59 using ::android::base::StringPrintf;
60 using ::testing::ElementsAre;
61 using ::testing::Eq;
62 using ::testing::SizeIs;
63 using ::testing::StrEq;
64 
65 constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
66 <post-process xmlns="http://schemas.android.com/tools/aapt">
67   <abi-groups>
68     <abi-group label="other" version-code-order="2">
69       <abi>x86</abi>
70       <abi>mips</abi>
71     </abi-group>
72     <abi-group label="arm" version-code-order="1">
73       <abi>armeabi-v7a</abi>
74       <abi>arm64-v8a</abi>
75     </abi-group>
76   </abi-groups>
77   <screen-density-groups>
78     <screen-density-group label="large" version-code-order="2">
79       <screen-density>xhdpi</screen-density>
80       <screen-density>xxhdpi</screen-density>
81       <screen-density>xxxhdpi</screen-density>
82     </screen-density-group>
83     <screen-density-group label="alldpi" version-code-order="1">
84       <screen-density>ldpi</screen-density>
85       <screen-density>mdpi</screen-density>
86       <screen-density>hdpi</screen-density>
87       <screen-density>xhdpi</screen-density>
88       <screen-density>xxhdpi</screen-density>
89       <screen-density>xxxhdpi</screen-density>
90     </screen-density-group>
91   </screen-density-groups>
92   <locale-groups>
93     <locale-group label="europe" version-code-order="1">
94       <locale>en</locale>
95       <locale>es</locale>
96       <locale>fr</locale>
97       <locale>de</locale>
98     </locale-group>
99     <locale-group label="north-america" version-code-order="2">
100       <locale>en</locale>
101       <locale>es-rMX</locale>
102       <locale>fr-rCA</locale>
103     </locale-group>
104     <locale-group label="all" version-code-order="-1">
105       <locale />
106     </locale-group>
107   </locale-groups>
108   <android-sdks>
109     <android-sdk
110     	  label="v19"
111         minSdkVersion="19"
112         targetSdkVersion="24"
113         maxSdkVersion="25">
114       <manifest>
115         <!--- manifest additions here XSLT? TODO -->
116       </manifest>
117     </android-sdk>
118   </android-sdks>
119   <gl-texture-groups>
120     <gl-texture-group label="dxt1" version-code-order="2">
121       <gl-texture name="GL_EXT_texture_compression_dxt1">
122         <texture-path>assets/dxt1/*</texture-path>
123       </gl-texture>
124     </gl-texture-group>
125   </gl-texture-groups>
126   <device-feature-groups>
127     <device-feature-group label="low-latency" version-code-order="2">
128       <supports-feature>android.hardware.audio.low_latency</supports-feature>
129     </device-feature-group>
130   </device-feature-groups>
131   <artifacts>
132     <artifact-format>
133       ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
134     </artifact-format>
135     <artifact
136         name="art1"
137         abi-group="arm"
138         screen-density-group="large"
139         locale-group="europe"
140         android-sdk="v19"
141         gl-texture-group="dxt1"
142         device-feature-group="low-latency"/>
143     <artifact
144         name="art2"
145         abi-group="other"
146         screen-density-group="alldpi"
147         locale-group="north-america"
148         android-sdk="v19"
149         gl-texture-group="dxt1"
150         device-feature-group="low-latency"/>
151   </artifacts>
152 </post-process>
153 )";
154 
155 class ConfigurationParserTest : public ConfigurationParser, public ::testing::Test {
156  public:
ConfigurationParserTest()157   ConfigurationParserTest() : ConfigurationParser("", "config.xml") {
158   }
159 
160  protected:
161   StdErrDiagnostics diag_;
162 };
163 
164 TEST_F(ConfigurationParserTest, ForPath_NoFile) {
165   auto result = ConfigurationParser::ForPath("./does_not_exist.xml");
166   EXPECT_FALSE(result);
167 }
168 
169 TEST_F(ConfigurationParserTest, ExtractConfiguration) {
170   std::optional<PostProcessingConfiguration> maybe_config =
171       ExtractConfiguration(kValidConfig, "fake.xml", &diag_);
172 
173   PostProcessingConfiguration config = maybe_config.value();
174 
175   auto& arm = config.abi_groups["arm"];
176   auto& other = config.abi_groups["other"];
177   EXPECT_EQ(arm.order, 1);
178   EXPECT_EQ(other.order, 2);
179 
180   auto& large = config.screen_density_groups["large"];
181   auto& alldpi = config.screen_density_groups["alldpi"];
182   EXPECT_EQ(large.order, 2);
183   EXPECT_EQ(alldpi.order, 1);
184 
185   auto& north_america = config.locale_groups["north-america"];
186   auto& europe = config.locale_groups["europe"];
187   auto& all = config.locale_groups["all"];
188   // Checked in reverse to make sure access order does not matter.
189   EXPECT_EQ(north_america.order, 2);
190   EXPECT_EQ(europe.order, 1);
191   EXPECT_EQ(all.order, -1);
192   EXPECT_EQ(3ul, config.locale_groups.size());
193 }
194 
195 TEST_F(ConfigurationParserTest, ValidateFile) {
196   auto parser = ConfigurationParser::ForContents(kValidConfig, "conf.xml").WithDiagnostics(&diag_);
197   auto result = parser.Parse("test.apk");
198   ASSERT_TRUE(result);
199   const std::vector<OutputArtifact>& value = result.value();
200   EXPECT_THAT(value, SizeIs(2ul));
201 
202   const OutputArtifact& a1 = value[0];
203   EXPECT_EQ(a1.name, "art1.apk");
204   EXPECT_EQ(a1.version, 1);
205   EXPECT_THAT(a1.abis, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a));
206   EXPECT_THAT(a1.screen_densities,
207               ElementsAre(test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(),
208                           test::ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(),
209                           test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion()));
210   EXPECT_THAT(a1.locales, ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es"),
211                                       test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de")));
212   ASSERT_TRUE(a1.android_sdk);
213   ASSERT_TRUE(a1.android_sdk.value().min_sdk_version);
214   EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19L);
215   EXPECT_THAT(a1.textures, SizeIs(1ul));
216   EXPECT_THAT(a1.features, SizeIs(1ul));
217 
218   const OutputArtifact& a2 = value[1];
219   EXPECT_EQ(a2.name, "art2.apk");
220   EXPECT_EQ(a2.version, 2);
221   EXPECT_THAT(a2.abis, ElementsAre(Abi::kX86, Abi::kMips));
222   EXPECT_THAT(a2.screen_densities,
223               ElementsAre(test::ParseConfigOrDie("ldpi").CopyWithoutSdkVersion(),
224                           test::ParseConfigOrDie("mdpi").CopyWithoutSdkVersion(),
225                           test::ParseConfigOrDie("hdpi").CopyWithoutSdkVersion(),
226                           test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(),
227                           test::ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(),
228                           test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion()));
229   EXPECT_THAT(a2.locales,
230               ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es-rMX"),
231                           test::ParseConfigOrDie("fr-rCA")));
232   ASSERT_TRUE(a2.android_sdk);
233   ASSERT_TRUE(a2.android_sdk.value().min_sdk_version);
234   EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19L);
235   EXPECT_THAT(a2.textures, SizeIs(1ul));
236   EXPECT_THAT(a2.features, SizeIs(1ul));
237 }
238 
239 TEST_F(ConfigurationParserTest, ConfiguredArtifactOrdering) {
240   // Create a base builder with the configuration groups but no artifacts to allow it to be copied.
241   test::PostProcessingConfigurationBuilder base_builder = test::PostProcessingConfigurationBuilder()
242                                                               .AddAbiGroup("arm")
243                                                               .AddAbiGroup("arm64")
244                                                               .AddAndroidSdk("v23", 23)
245                                                               .AddAndroidSdk("v19", 19);
246 
247   {
248     // Test version ordering.
249     ConfiguredArtifact v23;
250     v23.android_sdk = {"v23"};
251     ConfiguredArtifact v19;
252     v19.android_sdk = {"v19"};
253 
254     test::PostProcessingConfigurationBuilder builder = base_builder;
255     PostProcessingConfiguration config = builder.AddArtifact(v23).AddArtifact(v19).Build();
256 
257     config.SortArtifacts();
258     ASSERT_THAT(config.artifacts, SizeIs(2));
259     EXPECT_THAT(config.artifacts[0], Eq(v19));
260     EXPECT_THAT(config.artifacts[1], Eq(v23));
261   }
262 
263   {
264     // Test ABI ordering.
265     ConfiguredArtifact arm;
266     arm.android_sdk = {"v19"};
267     arm.abi_group = {"arm"};
268     ConfiguredArtifact arm64;
269     arm64.android_sdk = {"v19"};
270     arm64.abi_group = {"arm64"};
271 
272     test::PostProcessingConfigurationBuilder builder = base_builder;
273     PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build();
274 
275     config.SortArtifacts();
276     ASSERT_THAT(config.artifacts, SizeIs(2));
277     EXPECT_THAT(config.artifacts[0], Eq(arm));
278     EXPECT_THAT(config.artifacts[1], Eq(arm64));
279   }
280 
281   {
282     // Test Android SDK has precedence over ABI.
283     ConfiguredArtifact arm;
284     arm.android_sdk = {"v23"};
285     arm.abi_group = {"arm"};
286     ConfiguredArtifact arm64;
287     arm64.android_sdk = {"v19"};
288     arm64.abi_group = {"arm64"};
289 
290     test::PostProcessingConfigurationBuilder builder = base_builder;
291     PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build();
292 
293     config.SortArtifacts();
294     ASSERT_THAT(config.artifacts, SizeIs(2));
295     EXPECT_THAT(config.artifacts[0], Eq(arm64));
296     EXPECT_THAT(config.artifacts[1], Eq(arm));
297   }
298 
299   {
300     // Test version is better than ABI.
301     ConfiguredArtifact arm;
302     arm.abi_group = {"arm"};
303     ConfiguredArtifact v19;
304     v19.android_sdk = {"v19"};
305 
306     test::PostProcessingConfigurationBuilder builder = base_builder;
307     PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build();
308 
309     config.SortArtifacts();
310     ASSERT_THAT(config.artifacts, SizeIs(2));
311     EXPECT_THAT(config.artifacts[0], Eq(arm));
312     EXPECT_THAT(config.artifacts[1], Eq(v19));
313   }
314 
315   {
316     // Test version is sorted higher than no version.
317     ConfiguredArtifact arm;
318     arm.abi_group = {"arm"};
319     ConfiguredArtifact v19;
320     v19.abi_group = {"arm"};
321     v19.android_sdk = {"v19"};
322 
323     test::PostProcessingConfigurationBuilder builder = base_builder;
324     PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build();
325 
326     config.SortArtifacts();
327     ASSERT_THAT(config.artifacts, SizeIs(2));
328     EXPECT_THAT(config.artifacts[0], Eq(arm));
329     EXPECT_THAT(config.artifacts[1], Eq(v19));
330   }
331 }
332 
333 TEST_F(ConfigurationParserTest, InvalidNamespace) {
334   constexpr const char* invalid_ns = R"(<?xml version="1.0" encoding="utf-8" ?>
335     <post-process xmlns="http://schemas.android.com/tools/another-unknown-tool" />)";
336 
337   auto result = ConfigurationParser::ForContents(invalid_ns, "config.xml").Parse("test.apk");
338   ASSERT_FALSE(result);
339 }
340 
341 TEST_F(ConfigurationParserTest, ArtifactAction) {
342   PostProcessingConfiguration config;
343   const auto doc = test::BuildXmlDom(R"xml(
344       <artifact
345           abi-group="arm"
346           screen-density-group="large"
347           locale-group="europe"
348           android-sdk="v19"
349           gl-texture-group="dxt1"
350           device-feature-group="low-latency"/>)xml");
351 
352   ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast<Element>(doc->root.get()), &diag_));
353 
354   EXPECT_THAT(config.artifacts, SizeIs(1ul));
355 
356   auto& artifact = config.artifacts.back();
357   EXPECT_FALSE(artifact.name);  // TODO: make this fail.
358   EXPECT_EQ("arm", artifact.abi_group.value());
359   EXPECT_EQ("large", artifact.screen_density_group.value());
360   EXPECT_EQ("europe", artifact.locale_group.value());
361   EXPECT_EQ("v19", artifact.android_sdk.value());
362   EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
363   EXPECT_EQ("low-latency", artifact.device_feature_group.value());
364 }
365 
366 TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
367   const auto doc = test::BuildXmlDom(R"xml(
368     <artifact-format>
369       ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
370     </artifact-format>)xml");
371 
372   PostProcessingConfiguration config;
373   bool ok = ArtifactFormatTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
374   ASSERT_TRUE(ok);
375   ASSERT_TRUE(config.artifact_format);
376   EXPECT_EQ(
377       "${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release",
378       static_cast<std::string>(config.artifact_format.value())
379   );
380 }
381 
382 TEST_F(ConfigurationParserTest, AbiGroupAction) {
383   static constexpr const char* xml = R"xml(
384     <abi-group label="arm"  version-code-order="2">
385       <!-- First comment. -->
386       <abi>
387         armeabi-v7a
388       </abi>
389       <!-- Another comment. -->
390       <abi>arm64-v8a</abi>
391     </abi-group>)xml";
392 
393   auto doc = test::BuildXmlDom(xml);
394 
395   PostProcessingConfiguration config;
396   bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
397   ASSERT_TRUE(ok);
398 
399   EXPECT_THAT(config.abi_groups, SizeIs(1ul));
400   ASSERT_EQ(1u, config.abi_groups.count("arm"));
401 
402   auto& out = config.abi_groups["arm"].entry;
403   ASSERT_THAT(out, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a));
404 }
405 
406 TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
407   static constexpr const char* xml =
408       R"xml(<abi-group label="arm64-v8a" version-code-order="3"/>)xml";
409 
410   auto doc = test::BuildXmlDom(xml);
411 
412   PostProcessingConfiguration config;
413   bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
414   ASSERT_TRUE(ok);
415 
416   EXPECT_THAT(config.abi_groups, SizeIs(1ul));
417   ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a"));
418 
419   auto& out = config.abi_groups["arm64-v8a"];
420   ASSERT_THAT(out.entry, ElementsAre(Abi::kArm64V8a));
421   EXPECT_EQ(3, out.order);
422 }
423 
424 TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup_NoOrder) {
425   static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
426 
427   auto doc = test::BuildXmlDom(xml);
428 
429   PostProcessingConfiguration config;
430   bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
431   ASSERT_FALSE(ok);
432 }
433 
434 TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
435   static constexpr const char* xml = R"xml(<abi-group label="arm" order="2"/>)xml";
436 
437   auto doc = test::BuildXmlDom(xml);
438 
439   PostProcessingConfiguration config;
440   bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
441   ASSERT_FALSE(ok);
442 }
443 
444 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
445   static constexpr const char* xml = R"xml(
446     <screen-density-group label="large" version-code-order="2">
447       <screen-density>xhdpi</screen-density>
448       <screen-density>
449         xxhdpi
450       </screen-density>
451       <screen-density>xxxhdpi</screen-density>
452     </screen-density-group>)xml";
453 
454   auto doc = test::BuildXmlDom(xml);
455 
456   PostProcessingConfiguration config;
457   bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
458   ASSERT_TRUE(ok);
459 
460   EXPECT_THAT(config.screen_density_groups, SizeIs(1ul));
461   ASSERT_EQ(1u, config.screen_density_groups.count("large"));
462 
463   ConfigDescription xhdpi;
464   xhdpi.density = ResTable_config::DENSITY_XHIGH;
465   ConfigDescription xxhdpi;
466   xxhdpi.density = ResTable_config::DENSITY_XXHIGH;
467   ConfigDescription xxxhdpi;
468   xxxhdpi.density = ResTable_config::DENSITY_XXXHIGH;
469 
470   auto& out = config.screen_density_groups["large"].entry;
471   ASSERT_THAT(out, ElementsAre(xhdpi, xxhdpi, xxxhdpi));
472 }
473 
474 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
475   static constexpr const char* xml =
476       R"xml(<screen-density-group label="xhdpi" version-code-order="4"/>)xml";
477 
478   auto doc = test::BuildXmlDom(xml);
479 
480   PostProcessingConfiguration config;
481   bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
482   ASSERT_TRUE(ok);
483 
484   EXPECT_THAT(config.screen_density_groups, SizeIs(1ul));
485   ASSERT_EQ(1u, config.screen_density_groups.count("xhdpi"));
486 
487   ConfigDescription xhdpi;
488   xhdpi.density = ResTable_config::DENSITY_XHIGH;
489 
490   auto& out = config.screen_density_groups["xhdpi"];
491   EXPECT_THAT(out.entry, ElementsAre(xhdpi));
492   EXPECT_EQ(4, out.order);
493 }
494 
495 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup_NoVersion) {
496   static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
497 
498   auto doc = test::BuildXmlDom(xml);
499 
500   PostProcessingConfiguration config;
501   bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
502   ASSERT_FALSE(ok);
503 }
504 
505 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
506   static constexpr const char* xml = R"xml(<screen-density-group label="really-big-screen"/>)xml";
507 
508   auto doc = test::BuildXmlDom(xml);
509 
510   PostProcessingConfiguration config;
511   bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
512   ASSERT_FALSE(ok);
513 }
514 
515 TEST_F(ConfigurationParserTest, LocaleGroupAction) {
516   static constexpr const char* xml = R"xml(
517     <locale-group label="europe" version-code-order="2">
518       <locale>en</locale>
519       <locale>es</locale>
520       <locale>fr</locale>
521       <locale>de</locale>
522     </locale-group>)xml";
523 
524   auto doc = test::BuildXmlDom(xml);
525 
526   PostProcessingConfiguration config;
527   bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
528   ASSERT_TRUE(ok);
529 
530   ASSERT_EQ(1ul, config.locale_groups.size());
531   ASSERT_EQ(1u, config.locale_groups.count("europe"));
532 
533   const auto& out = config.locale_groups["europe"].entry;
534 
535   ConfigDescription en = test::ParseConfigOrDie("en");
536   ConfigDescription es = test::ParseConfigOrDie("es");
537   ConfigDescription fr = test::ParseConfigOrDie("fr");
538   ConfigDescription de = test::ParseConfigOrDie("de");
539 
540   ASSERT_THAT(out, ElementsAre(en, es, fr, de));
541 }
542 
543 TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
544   static constexpr const char* xml = R"xml(<locale-group label="en" version-code-order="6"/>)xml";
545 
546   auto doc = test::BuildXmlDom(xml);
547 
548   PostProcessingConfiguration config;
549   bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
550   ASSERT_TRUE(ok);
551 
552   ASSERT_EQ(1ul, config.locale_groups.size());
553   ASSERT_EQ(1u, config.locale_groups.count("en"));
554 
555   const auto& out = config.locale_groups["en"];
556 
557   ConfigDescription en = test::ParseConfigOrDie("en");
558 
559   EXPECT_THAT(out.entry, ElementsAre(en));
560   EXPECT_EQ(6, out.order);
561 }
562 
563 TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup_NoOrder) {
564   static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
565 
566   auto doc = test::BuildXmlDom(xml);
567 
568   PostProcessingConfiguration config;
569   bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
570   ASSERT_FALSE(ok);
571 }
572 
573 TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) {
574   static constexpr const char* xml = R"xml(<locale-group label="arm64"/>)xml";
575 
576   auto doc = test::BuildXmlDom(xml);
577 
578   PostProcessingConfiguration config;
579   bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
580   ASSERT_FALSE(ok);
581 }
582 
583 TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
584   static constexpr const char* xml = R"xml(
585       <android-sdk label="v19"
586           minSdkVersion="19"
587           targetSdkVersion="24"
588           maxSdkVersion="25">
589         <manifest>
590           <!--- manifest additions here XSLT? TODO -->
591         </manifest>
592       </android-sdk>)xml";
593 
594   auto doc = test::BuildXmlDom(xml);
595 
596   PostProcessingConfiguration config;
597   bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
598   ASSERT_TRUE(ok);
599 
600   ASSERT_EQ(1ul, config.android_sdks.size());
601   ASSERT_EQ(1u, config.android_sdks.count("v19"));
602 
603   auto& out = config.android_sdks["v19"];
604 
605   AndroidSdk sdk;
606   sdk.min_sdk_version = 19;
607   sdk.target_sdk_version = 24;
608   sdk.max_sdk_version = 25;
609   sdk.manifest = AndroidManifest();
610 
611   ASSERT_EQ(sdk, out);
612 }
613 
614 TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_SingleVersion) {
615   {
616     const char* xml = "<android-sdk label='v19' minSdkVersion='19'></android-sdk>";
617     auto doc = test::BuildXmlDom(xml);
618 
619     PostProcessingConfiguration config;
620     bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
621     ASSERT_TRUE(ok);
622 
623     ASSERT_EQ(1ul, config.android_sdks.size());
624     ASSERT_EQ(1u, config.android_sdks.count("v19"));
625 
626     auto& out = config.android_sdks["v19"];
627     EXPECT_EQ(19, out.min_sdk_version);
628     EXPECT_FALSE(out.max_sdk_version);
629     EXPECT_FALSE(out.target_sdk_version);
630   }
631 
632   {
633     const char* xml =
634         "<android-sdk label='v19' minSdkVersion='19' maxSdkVersion='19'></android-sdk>";
635     auto doc = test::BuildXmlDom(xml);
636 
637     PostProcessingConfiguration config;
638     bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
639     ASSERT_TRUE(ok);
640 
641     ASSERT_EQ(1ul, config.android_sdks.size());
642     ASSERT_EQ(1u, config.android_sdks.count("v19"));
643 
644     auto& out = config.android_sdks["v19"];
645     EXPECT_EQ(19, out.max_sdk_version.value());
646     EXPECT_EQ(19, out.min_sdk_version);
647     EXPECT_FALSE(out.target_sdk_version);
648   }
649 
650   {
651     const char* xml =
652         "<android-sdk label='v19' minSdkVersion='19' targetSdkVersion='19'></android-sdk>";
653     auto doc = test::BuildXmlDom(xml);
654 
655     PostProcessingConfiguration config;
656     bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
657     ASSERT_TRUE(ok);
658 
659     ASSERT_EQ(1ul, config.android_sdks.size());
660     ASSERT_EQ(1u, config.android_sdks.count("v19"));
661 
662     auto& out = config.android_sdks["v19"];
663     EXPECT_EQ(19, out.target_sdk_version.value());
664     EXPECT_EQ(19, out.min_sdk_version);
665     EXPECT_FALSE(out.max_sdk_version);
666   }
667 }
668 
669 TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_InvalidVersion) {
670   static constexpr const char* xml = R"xml(
671     <android-sdk
672         label="v19"
673         minSdkVersion="v19"
674         targetSdkVersion="v24"
675         maxSdkVersion="v25">
676       <manifest>
677         <!--- manifest additions here XSLT? TODO -->
678       </manifest>
679     </android-sdk>)xml";
680 
681   auto doc = test::BuildXmlDom(xml);
682 
683   PostProcessingConfiguration config;
684   bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
685   ASSERT_FALSE(ok);
686 }
687 
688 TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
689   auto doc = test::BuildXmlDom(R"xml(
690       <android-sdk
691           label="Q"
692           minSdkVersion="25"
693           targetSdkVersion="Q"
694           maxSdkVersion="Q">
695       </android-sdk>)xml");
696 
697   PostProcessingConfiguration config;
698   ASSERT_TRUE(AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
699   ASSERT_EQ(1ul, config.android_sdks.size());
700   ASSERT_EQ(1u, config.android_sdks.count("Q"));
701 
702   AndroidSdk sdk;
703   sdk.min_sdk_version = 25;
704   sdk.target_sdk_version = 10000;
705   sdk.max_sdk_version = 10000;
706   ASSERT_EQ(sdk, config.android_sdks["Q"]);
707 }
708 
709 TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
710   static constexpr const char* xml = R"xml(
711     <gl-texture-group label="dxt1" version-code-order="2">
712       <gl-texture name="GL_EXT_texture_compression_dxt1">
713         <texture-path>assets/dxt1/main/*</texture-path>
714         <texture-path>
715           assets/dxt1/test/*
716         </texture-path>
717       </gl-texture>
718     </gl-texture-group>)xml";
719 
720   auto doc = test::BuildXmlDom(xml);
721 
722   PostProcessingConfiguration config;
723   bool ok = GlTextureGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
724   ASSERT_TRUE(ok);
725 
726   EXPECT_THAT(config.gl_texture_groups, SizeIs(1ul));
727   ASSERT_EQ(1u, config.gl_texture_groups.count("dxt1"));
728 
729   auto& out = config.gl_texture_groups["dxt1"].entry;
730 
731   GlTexture texture{
732       std::string("GL_EXT_texture_compression_dxt1"),
733       {"assets/dxt1/main/*", "assets/dxt1/test/*"}
734   };
735 
736   ASSERT_EQ(1ul, out.size());
737   ASSERT_EQ(texture, out[0]);
738 }
739 
740 TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
741   static constexpr const char* xml = R"xml(
742     <device-feature-group label="low-latency" version-code-order="2">
743       <supports-feature>android.hardware.audio.low_latency</supports-feature>
744       <supports-feature>
745         android.hardware.audio.pro
746       </supports-feature>
747     </device-feature-group>)xml";
748 
749   auto doc = test::BuildXmlDom(xml);
750 
751   PostProcessingConfiguration config;
752   bool ok = DeviceFeatureGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
753   ASSERT_TRUE(ok);
754 
755   EXPECT_THAT(config.device_feature_groups, SizeIs(1ul));
756   ASSERT_EQ(1u, config.device_feature_groups.count("low-latency"));
757 
758   auto& out = config.device_feature_groups["low-latency"].entry;
759 
760   DeviceFeature low_latency = "android.hardware.audio.low_latency";
761   DeviceFeature pro = "android.hardware.audio.pro";
762   ASSERT_THAT(out, ElementsAre(low_latency, pro));
763 }
764 
765 TEST_F(ConfigurationParserTest, Group_Valid) {
766   Group<int32_t> group;
767   group["item1"].order = 1;
768   group["item2"].order = 2;
769   group["item3"].order = 3;
770   group["item4"].order = 4;
771   group["item5"].order = 5;
772   group["item6"].order = 6;
773 
774   EXPECT_TRUE(IsGroupValid(group, "test", &diag_));
775 }
776 
777 TEST_F(ConfigurationParserTest, Group_OverlappingOrder) {
778   Group<int32_t> group;
779   group["item1"].order = 1;
780   group["item2"].order = 2;
781   group["item3"].order = 3;
782   group["item4"].order = 2;
783   group["item5"].order = 5;
784   group["item6"].order = 1;
785 
786   EXPECT_FALSE(IsGroupValid(group, "test", &diag_));
787 }
788 
789 // Artifact name parser test cases.
790 
791 TEST(ArtifactTest, Simple) {
792   StdErrDiagnostics diag;
793   ConfiguredArtifact x86;
794   x86.abi_group = {"x86"};
795 
796   auto x86_result = x86.ToArtifactName("something.${abi}.apk", "", &diag);
797   ASSERT_TRUE(x86_result);
798   EXPECT_EQ(x86_result.value(), "something.x86.apk");
799 
800   ConfiguredArtifact arm;
801   arm.abi_group = {"armeabi-v7a"};
802 
803   {
804     auto arm_result = arm.ToArtifactName("app.${abi}.apk", "", &diag);
805     ASSERT_TRUE(arm_result);
806     EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
807   }
808 
809   {
810     auto arm_result = arm.ToArtifactName("app.${abi}.apk", "different_name.apk", &diag);
811     ASSERT_TRUE(arm_result);
812     EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
813   }
814 
815   {
816     auto arm_result = arm.ToArtifactName("${basename}.${abi}.apk", "app.apk", &diag);
817     ASSERT_TRUE(arm_result);
818     EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
819   }
820 
821   {
822     auto arm_result = arm.ToArtifactName("app.${abi}.${ext}", "app.apk", &diag);
823     ASSERT_TRUE(arm_result);
824     EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
825   }
826 }
827 
828 TEST(ArtifactTest, Complex) {
829   StdErrDiagnostics diag;
830   ConfiguredArtifact artifact;
831   artifact.abi_group = {"mips64"};
832   artifact.screen_density_group = {"ldpi"};
833   artifact.device_feature_group = {"df1"};
834   artifact.gl_texture_group = {"glx1"};
835   artifact.locale_group = {"en-AU"};
836   artifact.android_sdk = {"v26"};
837 
838   {
839     auto result = artifact.ToArtifactName(
840         "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "", &diag);
841     ASSERT_TRUE(result);
842     EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
843   }
844 
845   {
846     auto result = artifact.ToArtifactName(
847         "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag);
848     ASSERT_TRUE(result);
849     EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
850   }
851 
852   {
853     auto result = artifact.ToArtifactName(
854         "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag);
855     ASSERT_TRUE(result);
856     EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
857   }
858 
859   {
860     auto result = artifact.ToArtifactName(
861         "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.${ext}", "app.apk", &diag);
862     ASSERT_TRUE(result);
863     EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
864   }
865 
866   {
867     auto result = artifact.ToArtifactName(
868         "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}", "app.apk", &diag);
869     ASSERT_TRUE(result);
870     EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
871   }
872 }
873 
874 TEST(ArtifactTest, Missing) {
875   StdErrDiagnostics diag;
876   ConfiguredArtifact x86;
877   x86.abi_group = {"x86"};
878 
879   EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "", &diag));
880   EXPECT_FALSE(x86.ToArtifactName("something.apk", "", &diag));
881   EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "something.apk", &diag));
882   EXPECT_FALSE(x86.ToArtifactName("something.apk", "something.apk", &diag));
883 }
884 
885 TEST(ArtifactTest, Empty) {
886   StdErrDiagnostics diag;
887   ConfiguredArtifact artifact;
888 
889   EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
890   EXPECT_TRUE(artifact.ToArtifactName("something.apk", "", &diag));
891   EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
892   EXPECT_TRUE(artifact.ToArtifactName("something.apk", "something.apk", &diag));
893 }
894 
895 TEST(ArtifactTest, Repeated) {
896   StdErrDiagnostics diag;
897   ConfiguredArtifact artifact;
898   artifact.screen_density_group = {"mdpi"};
899 
900   ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
901   EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", "", &diag));
902   ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
903 }
904 
905 TEST(ArtifactTest, Nesting) {
906   StdErrDiagnostics diag;
907   ConfiguredArtifact x86;
908   x86.abi_group = {"x86"};
909 
910   EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
911 
912   const std::optional<std::string>& name =
913       x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
914   ASSERT_TRUE(name);
915   EXPECT_EQ(name.value(), "something.${abix86}.apk");
916 }
917 
918 TEST(ArtifactTest, Recursive) {
919   StdErrDiagnostics diag;
920   ConfiguredArtifact artifact;
921   artifact.device_feature_group = {"${gl}"};
922   artifact.gl_texture_group = {"glx1"};
923 
924   EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag));
925 
926   artifact.device_feature_group = {"df1"};
927   artifact.gl_texture_group = {"${feature}"};
928   {
929     const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag);
930     ASSERT_TRUE(result);
931     EXPECT_EQ(result.value(), "app.df1.${feature}.apk");
932   }
933 
934   // This is an invalid case, but should be the only possible case due to the ordering of
935   // replacement.
936   artifact.device_feature_group = {"${gl}"};
937   artifact.gl_texture_group = {"glx1"};
938   {
939     const auto& result = artifact.ToArtifactName("app.${feature}.apk", "", &diag);
940     ASSERT_TRUE(result);
941     EXPECT_EQ(result.value(), "app.glx1.apk");
942   }
943 }
944 
945 }  // namespace
946 }  // namespace handler
947 }  // namespace configuration
948 }  // namespace aapt
949