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