/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "configuration/ConfigurationParser.h" #include #include "android-base/stringprintf.h" #include "androidfw/ResourceTypes.h" #include "SdkConstants.h" #include "configuration/ConfigurationParser.internal.h" #include "test/Test.h" #include "xml/XmlDom.h" using ::android::ConfigDescription; namespace aapt { namespace configuration { void PrintTo(const AndroidSdk& sdk, std::ostream* os) { *os << "SDK: min=" << sdk.min_sdk_version << ", target=" << sdk.target_sdk_version.value_or_default(-1) << ", max=" << sdk.max_sdk_version.value_or_default(-1); } bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) { return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group && lhs.screen_density_group == rhs.screen_density_group && lhs.locale_group == rhs.locale_group && lhs.android_sdk == rhs.android_sdk && lhs.device_feature_group == rhs.device_feature_group && lhs.gl_texture_group == rhs.gl_texture_group; } std::ostream& operator<<(std::ostream& out, const Maybe& value) { PrintTo(value, &out); return out; } void PrintTo(const ConfiguredArtifact& artifact, std::ostream* os) { *os << "\n{" << "\n name: " << artifact.name << "\n sdk: " << artifact.android_sdk << "\n abi: " << artifact.abi_group << "\n density: " << artifact.screen_density_group << "\n locale: " << artifact.locale_group << "\n features: " << artifact.device_feature_group << "\n textures: " << artifact.gl_texture_group << "\n}\n"; } namespace handler { namespace { using ::aapt::configuration::Abi; using ::aapt::configuration::AndroidManifest; using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::ConfiguredArtifact; using ::aapt::configuration::DeviceFeature; using ::aapt::configuration::ExtractConfiguration; using ::aapt::configuration::GlTexture; using ::aapt::configuration::Locale; using ::aapt::configuration::PostProcessingConfiguration; using ::aapt::xml::Element; using ::aapt::xml::NodeCast; using ::android::ResTable_config; using ::android::base::StringPrintf; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::SizeIs; using ::testing::StrEq; constexpr const char* kValidConfig = R"( x86 mips armeabi-v7a arm64-v8a xhdpi xxhdpi xxxhdpi ldpi mdpi hdpi xhdpi xxhdpi xxxhdpi en es fr de en es-rMX fr-rCA assets/dxt1/* android.hardware.audio.low_latency ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release )"; class ConfigurationParserTest : public ConfigurationParser, public ::testing::Test { public: ConfigurationParserTest() : ConfigurationParser("", "config.xml") { } protected: StdErrDiagnostics diag_; }; TEST_F(ConfigurationParserTest, ForPath_NoFile) { auto result = ConfigurationParser::ForPath("./does_not_exist.xml"); EXPECT_FALSE(result); } TEST_F(ConfigurationParserTest, ExtractConfiguration) { Maybe maybe_config = ExtractConfiguration(kValidConfig, "fake.xml", &diag_); PostProcessingConfiguration config = maybe_config.value(); auto& arm = config.abi_groups["arm"]; auto& other = config.abi_groups["other"]; EXPECT_EQ(arm.order, 1); EXPECT_EQ(other.order, 2); auto& large = config.screen_density_groups["large"]; auto& alldpi = config.screen_density_groups["alldpi"]; EXPECT_EQ(large.order, 2); EXPECT_EQ(alldpi.order, 1); auto& north_america = config.locale_groups["north-america"]; auto& europe = config.locale_groups["europe"]; auto& all = config.locale_groups["all"]; // Checked in reverse to make sure access order does not matter. EXPECT_EQ(north_america.order, 2); EXPECT_EQ(europe.order, 1); EXPECT_EQ(all.order, -1); EXPECT_EQ(3ul, config.locale_groups.size()); } TEST_F(ConfigurationParserTest, ValidateFile) { auto parser = ConfigurationParser::ForContents(kValidConfig, "conf.xml").WithDiagnostics(&diag_); auto result = parser.Parse("test.apk"); ASSERT_TRUE(result); const std::vector& value = result.value(); EXPECT_THAT(value, SizeIs(2ul)); const OutputArtifact& a1 = value[0]; EXPECT_EQ(a1.name, "art1.apk"); EXPECT_EQ(a1.version, 1); EXPECT_THAT(a1.abis, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a)); EXPECT_THAT(a1.screen_densities, ElementsAre(test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion())); EXPECT_THAT(a1.locales, ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es"), test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de"))); ASSERT_TRUE(a1.android_sdk); ASSERT_TRUE(a1.android_sdk.value().min_sdk_version); EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19L); EXPECT_THAT(a1.textures, SizeIs(1ul)); EXPECT_THAT(a1.features, SizeIs(1ul)); const OutputArtifact& a2 = value[1]; EXPECT_EQ(a2.name, "art2.apk"); EXPECT_EQ(a2.version, 2); EXPECT_THAT(a2.abis, ElementsAre(Abi::kX86, Abi::kMips)); EXPECT_THAT(a2.screen_densities, ElementsAre(test::ParseConfigOrDie("ldpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("mdpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("hdpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(), test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion())); EXPECT_THAT(a2.locales, ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es-rMX"), test::ParseConfigOrDie("fr-rCA"))); ASSERT_TRUE(a2.android_sdk); ASSERT_TRUE(a2.android_sdk.value().min_sdk_version); EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19L); EXPECT_THAT(a2.textures, SizeIs(1ul)); EXPECT_THAT(a2.features, SizeIs(1ul)); } TEST_F(ConfigurationParserTest, ConfiguredArtifactOrdering) { // Create a base builder with the configuration groups but no artifacts to allow it to be copied. test::PostProcessingConfigurationBuilder base_builder = test::PostProcessingConfigurationBuilder() .AddAbiGroup("arm") .AddAbiGroup("arm64") .AddAndroidSdk("v23", 23) .AddAndroidSdk("v19", 19); { // Test version ordering. ConfiguredArtifact v23; v23.android_sdk = {"v23"}; ConfiguredArtifact v19; v19.android_sdk = {"v19"}; test::PostProcessingConfigurationBuilder builder = base_builder; PostProcessingConfiguration config = builder.AddArtifact(v23).AddArtifact(v19).Build(); config.SortArtifacts(); ASSERT_THAT(config.artifacts, SizeIs(2)); EXPECT_THAT(config.artifacts[0], Eq(v19)); EXPECT_THAT(config.artifacts[1], Eq(v23)); } { // Test ABI ordering. ConfiguredArtifact arm; arm.android_sdk = {"v19"}; arm.abi_group = {"arm"}; ConfiguredArtifact arm64; arm64.android_sdk = {"v19"}; arm64.abi_group = {"arm64"}; test::PostProcessingConfigurationBuilder builder = base_builder; PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build(); config.SortArtifacts(); ASSERT_THAT(config.artifacts, SizeIs(2)); EXPECT_THAT(config.artifacts[0], Eq(arm)); EXPECT_THAT(config.artifacts[1], Eq(arm64)); } { // Test Android SDK has precedence over ABI. ConfiguredArtifact arm; arm.android_sdk = {"v23"}; arm.abi_group = {"arm"}; ConfiguredArtifact arm64; arm64.android_sdk = {"v19"}; arm64.abi_group = {"arm64"}; test::PostProcessingConfigurationBuilder builder = base_builder; PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build(); config.SortArtifacts(); ASSERT_THAT(config.artifacts, SizeIs(2)); EXPECT_THAT(config.artifacts[0], Eq(arm64)); EXPECT_THAT(config.artifacts[1], Eq(arm)); } { // Test version is better than ABI. ConfiguredArtifact arm; arm.abi_group = {"arm"}; ConfiguredArtifact v19; v19.android_sdk = {"v19"}; test::PostProcessingConfigurationBuilder builder = base_builder; PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build(); config.SortArtifacts(); ASSERT_THAT(config.artifacts, SizeIs(2)); EXPECT_THAT(config.artifacts[0], Eq(arm)); EXPECT_THAT(config.artifacts[1], Eq(v19)); } { // Test version is sorted higher than no version. ConfiguredArtifact arm; arm.abi_group = {"arm"}; ConfiguredArtifact v19; v19.abi_group = {"arm"}; v19.android_sdk = {"v19"}; test::PostProcessingConfigurationBuilder builder = base_builder; PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build(); config.SortArtifacts(); ASSERT_THAT(config.artifacts, SizeIs(2)); EXPECT_THAT(config.artifacts[0], Eq(arm)); EXPECT_THAT(config.artifacts[1], Eq(v19)); } } TEST_F(ConfigurationParserTest, InvalidNamespace) { constexpr const char* invalid_ns = R"( )"; auto result = ConfigurationParser::ForContents(invalid_ns, "config.xml").Parse("test.apk"); ASSERT_FALSE(result); } TEST_F(ConfigurationParserTest, ArtifactAction) { PostProcessingConfiguration config; const auto doc = test::BuildXmlDom(R"xml( )xml"); ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast(doc->root.get()), &diag_)); EXPECT_THAT(config.artifacts, SizeIs(1ul)); auto& artifact = config.artifacts.back(); EXPECT_FALSE(artifact.name); // TODO: make this fail. EXPECT_EQ("arm", artifact.abi_group.value()); EXPECT_EQ("large", artifact.screen_density_group.value()); EXPECT_EQ("europe", artifact.locale_group.value()); EXPECT_EQ("v19", artifact.android_sdk.value()); EXPECT_EQ("dxt1", artifact.gl_texture_group.value()); EXPECT_EQ("low-latency", artifact.device_feature_group.value()); } TEST_F(ConfigurationParserTest, ArtifactFormatAction) { const auto doc = test::BuildXmlDom(R"xml( ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release )xml"); PostProcessingConfiguration config; bool ok = ArtifactFormatTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_TRUE(config.artifact_format); EXPECT_EQ( "${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release", static_cast(config.artifact_format.value()) ); } TEST_F(ConfigurationParserTest, AbiGroupAction) { static constexpr const char* xml = R"xml( armeabi-v7a arm64-v8a )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AbiGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); EXPECT_THAT(config.abi_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.abi_groups.count("arm")); auto& out = config.abi_groups["arm"].entry; ASSERT_THAT(out, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a)); } TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AbiGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); EXPECT_THAT(config.abi_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a")); auto& out = config.abi_groups["arm64-v8a"]; ASSERT_THAT(out.entry, ElementsAre(Abi::kArm64V8a)); EXPECT_EQ(3, out.order); } TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup_NoOrder) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AbiGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AbiGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) { static constexpr const char* xml = R"xml( xhdpi xxhdpi xxxhdpi )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = ScreenDensityGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); EXPECT_THAT(config.screen_density_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.screen_density_groups.count("large")); ConfigDescription xhdpi; xhdpi.density = ResTable_config::DENSITY_XHIGH; ConfigDescription xxhdpi; xxhdpi.density = ResTable_config::DENSITY_XXHIGH; ConfigDescription xxxhdpi; xxxhdpi.density = ResTable_config::DENSITY_XXXHIGH; auto& out = config.screen_density_groups["large"].entry; ASSERT_THAT(out, ElementsAre(xhdpi, xxhdpi, xxxhdpi)); } TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = ScreenDensityGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); EXPECT_THAT(config.screen_density_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.screen_density_groups.count("xhdpi")); ConfigDescription xhdpi; xhdpi.density = ResTable_config::DENSITY_XHIGH; auto& out = config.screen_density_groups["xhdpi"]; EXPECT_THAT(out.entry, ElementsAre(xhdpi)); EXPECT_EQ(4, out.order); } TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup_NoVersion) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = ScreenDensityGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = ScreenDensityGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, LocaleGroupAction) { static constexpr const char* xml = R"xml( en es fr de )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = LocaleGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.locale_groups.size()); ASSERT_EQ(1u, config.locale_groups.count("europe")); const auto& out = config.locale_groups["europe"].entry; ConfigDescription en = test::ParseConfigOrDie("en"); ConfigDescription es = test::ParseConfigOrDie("es"); ConfigDescription fr = test::ParseConfigOrDie("fr"); ConfigDescription de = test::ParseConfigOrDie("de"); ASSERT_THAT(out, ElementsAre(en, es, fr, de)); } TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = LocaleGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.locale_groups.size()); ASSERT_EQ(1u, config.locale_groups.count("en")); const auto& out = config.locale_groups["en"]; ConfigDescription en = test::ParseConfigOrDie("en"); EXPECT_THAT(out.entry, ElementsAre(en)); EXPECT_EQ(6, out.order); } TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup_NoOrder) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = LocaleGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) { static constexpr const char* xml = R"xml()xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = LocaleGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) { static constexpr const char* xml = R"xml( )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.android_sdks.size()); ASSERT_EQ(1u, config.android_sdks.count("v19")); auto& out = config.android_sdks["v19"]; AndroidSdk sdk; sdk.min_sdk_version = 19; sdk.target_sdk_version = 24; sdk.max_sdk_version = 25; sdk.manifest = AndroidManifest(); ASSERT_EQ(sdk, out); } TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_SingleVersion) { { const char* xml = ""; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.android_sdks.size()); ASSERT_EQ(1u, config.android_sdks.count("v19")); auto& out = config.android_sdks["v19"]; EXPECT_EQ(19, out.min_sdk_version); EXPECT_FALSE(out.max_sdk_version); EXPECT_FALSE(out.target_sdk_version); } { const char* xml = ""; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.android_sdks.size()); ASSERT_EQ(1u, config.android_sdks.count("v19")); auto& out = config.android_sdks["v19"]; EXPECT_EQ(19, out.max_sdk_version.value()); EXPECT_EQ(19, out.min_sdk_version); EXPECT_FALSE(out.target_sdk_version); } { const char* xml = ""; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.android_sdks.size()); ASSERT_EQ(1u, config.android_sdks.count("v19")); auto& out = config.android_sdks["v19"]; EXPECT_EQ(19, out.target_sdk_version.value()); EXPECT_EQ(19, out.min_sdk_version); EXPECT_FALSE(out.max_sdk_version); } } TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_InvalidVersion) { static constexpr const char* xml = R"xml( )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) { auto doc = test::BuildXmlDom(R"xml( )xml"); PostProcessingConfiguration config; ASSERT_TRUE(AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_)); ASSERT_EQ(1ul, config.android_sdks.size()); ASSERT_EQ(1u, config.android_sdks.count("Q")); AndroidSdk sdk; sdk.min_sdk_version = 25; sdk.target_sdk_version = 10000; sdk.max_sdk_version = 10000; ASSERT_EQ(sdk, config.android_sdks["Q"]); } TEST_F(ConfigurationParserTest, GlTextureGroupAction) { static constexpr const char* xml = R"xml( assets/dxt1/main/* assets/dxt1/test/* )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = GlTextureGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); EXPECT_THAT(config.gl_texture_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.gl_texture_groups.count("dxt1")); auto& out = config.gl_texture_groups["dxt1"].entry; GlTexture texture{ std::string("GL_EXT_texture_compression_dxt1"), {"assets/dxt1/main/*", "assets/dxt1/test/*"} }; ASSERT_EQ(1ul, out.size()); ASSERT_EQ(texture, out[0]); } TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) { static constexpr const char* xml = R"xml( android.hardware.audio.low_latency android.hardware.audio.pro )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; bool ok = DeviceFeatureGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); EXPECT_THAT(config.device_feature_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.device_feature_groups.count("low-latency")); auto& out = config.device_feature_groups["low-latency"].entry; DeviceFeature low_latency = "android.hardware.audio.low_latency"; DeviceFeature pro = "android.hardware.audio.pro"; ASSERT_THAT(out, ElementsAre(low_latency, pro)); } TEST_F(ConfigurationParserTest, Group_Valid) { Group group; group["item1"].order = 1; group["item2"].order = 2; group["item3"].order = 3; group["item4"].order = 4; group["item5"].order = 5; group["item6"].order = 6; EXPECT_TRUE(IsGroupValid(group, "test", &diag_)); } TEST_F(ConfigurationParserTest, Group_OverlappingOrder) { Group group; group["item1"].order = 1; group["item2"].order = 2; group["item3"].order = 3; group["item4"].order = 2; group["item5"].order = 5; group["item6"].order = 1; EXPECT_FALSE(IsGroupValid(group, "test", &diag_)); } // Artifact name parser test cases. TEST(ArtifactTest, Simple) { StdErrDiagnostics diag; ConfiguredArtifact x86; x86.abi_group = {"x86"}; auto x86_result = x86.ToArtifactName("something.${abi}.apk", "", &diag); ASSERT_TRUE(x86_result); EXPECT_EQ(x86_result.value(), "something.x86.apk"); ConfiguredArtifact arm; arm.abi_group = {"armeabi-v7a"}; { auto arm_result = arm.ToArtifactName("app.${abi}.apk", "", &diag); ASSERT_TRUE(arm_result); EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk"); } { auto arm_result = arm.ToArtifactName("app.${abi}.apk", "different_name.apk", &diag); ASSERT_TRUE(arm_result); EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk"); } { auto arm_result = arm.ToArtifactName("${basename}.${abi}.apk", "app.apk", &diag); ASSERT_TRUE(arm_result); EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk"); } { auto arm_result = arm.ToArtifactName("app.${abi}.${ext}", "app.apk", &diag); ASSERT_TRUE(arm_result); EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk"); } } TEST(ArtifactTest, Complex) { StdErrDiagnostics diag; ConfiguredArtifact artifact; artifact.abi_group = {"mips64"}; artifact.screen_density_group = {"ldpi"}; artifact.device_feature_group = {"df1"}; artifact.gl_texture_group = {"glx1"}; artifact.locale_group = {"en-AU"}; artifact.android_sdk = {"v26"}; { auto result = artifact.ToArtifactName( "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk"); } { auto result = artifact.ToArtifactName( "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk"); } { auto result = artifact.ToArtifactName( "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk"); } { auto result = artifact.ToArtifactName( "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.${ext}", "app.apk", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk"); } { auto result = artifact.ToArtifactName( "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}", "app.apk", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk"); } } TEST(ArtifactTest, Missing) { StdErrDiagnostics diag; ConfiguredArtifact x86; x86.abi_group = {"x86"}; EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "", &diag)); EXPECT_FALSE(x86.ToArtifactName("something.apk", "", &diag)); EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "something.apk", &diag)); EXPECT_FALSE(x86.ToArtifactName("something.apk", "something.apk", &diag)); } TEST(ArtifactTest, Empty) { StdErrDiagnostics diag; ConfiguredArtifact artifact; EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "", &diag)); EXPECT_TRUE(artifact.ToArtifactName("something.apk", "", &diag)); EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag)); EXPECT_TRUE(artifact.ToArtifactName("something.apk", "something.apk", &diag)); } TEST(ArtifactTest, Repeated) { StdErrDiagnostics diag; ConfiguredArtifact artifact; artifact.screen_density_group = {"mdpi"}; ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "", &diag)); EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", "", &diag)); ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag)); } TEST(ArtifactTest, Nesting) { StdErrDiagnostics diag; ConfiguredArtifact x86; x86.abi_group = {"x86"}; EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag)); const Maybe& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag); ASSERT_TRUE(name); EXPECT_EQ(name.value(), "something.${abix86}.apk"); } TEST(ArtifactTest, Recursive) { StdErrDiagnostics diag; ConfiguredArtifact artifact; artifact.device_feature_group = {"${gl}"}; artifact.gl_texture_group = {"glx1"}; EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag)); artifact.device_feature_group = {"df1"}; artifact.gl_texture_group = {"${feature}"}; { const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.df1.${feature}.apk"); } // This is an invalid case, but should be the only possible case due to the ordering of // replacement. artifact.device_feature_group = {"${gl}"}; artifact.gl_texture_group = {"glx1"}; { const auto& result = artifact.ToArtifactName("app.${feature}.apk", "", &diag); ASSERT_TRUE(result); EXPECT_EQ(result.value(), "app.glx1.apk"); } } } // namespace } // namespace handler } // namespace configuration } // namespace aapt