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