78c43d7bee
Sort output artifacts so that the updated versionCode manifest entry will allow correct handling of updates from Play Store. The most important dimension is Android SDK version. It is important that a split based on min SDK version will allow a user to get a new APK if they upgrade the OS on their device to support a new split. ABI splits need to also be taken into consideration as it is possible for a device to run in ARM emulation mode and installing an ARM APK over a x86 APK could cause performance regressions. The XML file format was updated to give each of the configuration groups have their own section of the XML file. This allows the sort order to be determined by a groups ordering. Artifacts can now be added to the configuration file in an arbitrary order. Since this will be the common case for developers, it will help reduce errors from inserting a new artifact in the wrong spot. The implementation follows the rules outlined at: https://developer.android.com/google/play/publishing/multiple-apks.html Test: Unit tests Test: Manual process XML configuration Change-Id: I0face862c6d6b9d3cd2d99088afe5b9491be0120
911 lines
30 KiB
C++
911 lines
30 KiB
C++
/*
|
|
* 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 <string>
|
|
|
|
#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"
|
|
|
|
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<std::string>& 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"(<?xml version="1.0" encoding="utf-8" ?>
|
|
<post-process xmlns="http://schemas.android.com/tools/aapt">
|
|
<abi-groups>
|
|
<abi-group label="arm">
|
|
<abi>armeabi-v7a</abi>
|
|
<abi>arm64-v8a</abi>
|
|
</abi-group>
|
|
<abi-group label="other">
|
|
<abi>x86</abi>
|
|
<abi>mips</abi>
|
|
</abi-group>
|
|
</abi-groups>
|
|
<screen-density-groups>
|
|
<screen-density-group label="large">
|
|
<screen-density>xhdpi</screen-density>
|
|
<screen-density>xxhdpi</screen-density>
|
|
<screen-density>xxxhdpi</screen-density>
|
|
</screen-density-group>
|
|
<screen-density-group label="alldpi">
|
|
<screen-density>ldpi</screen-density>
|
|
<screen-density>mdpi</screen-density>
|
|
<screen-density>hdpi</screen-density>
|
|
<screen-density>xhdpi</screen-density>
|
|
<screen-density>xxhdpi</screen-density>
|
|
<screen-density>xxxhdpi</screen-density>
|
|
</screen-density-group>
|
|
</screen-density-groups>
|
|
<locale-groups>
|
|
<locale-group label="europe">
|
|
<locale>en</locale>
|
|
<locale>es</locale>
|
|
<locale>fr</locale>
|
|
<locale>de</locale>
|
|
</locale-group>
|
|
<locale-group label="north-america">
|
|
<locale>en</locale>
|
|
<locale>es-rMX</locale>
|
|
<locale>fr-rCA</locale>
|
|
</locale-group>
|
|
</locale-groups>
|
|
<android-sdks>
|
|
<android-sdk
|
|
label="v19"
|
|
minSdkVersion="19"
|
|
targetSdkVersion="24"
|
|
maxSdkVersion="25">
|
|
<manifest>
|
|
<!--- manifest additions here XSLT? TODO -->
|
|
</manifest>
|
|
</android-sdk>
|
|
</android-sdks>
|
|
<gl-texture-groups>
|
|
<gl-texture-group label="dxt1">
|
|
<gl-texture name="GL_EXT_texture_compression_dxt1">
|
|
<texture-path>assets/dxt1/*</texture-path>
|
|
</gl-texture>
|
|
</gl-texture-group>
|
|
</gl-texture-groups>
|
|
<device-feature-groups>
|
|
<device-feature-group label="low-latency">
|
|
<supports-feature>android.hardware.audio.low_latency</supports-feature>
|
|
</device-feature-group>
|
|
</device-feature-groups>
|
|
<artifacts>
|
|
<artifact-format>
|
|
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
|
|
</artifact-format>
|
|
<artifact
|
|
name="art1"
|
|
abi-group="arm"
|
|
screen-density-group="large"
|
|
locale-group="europe"
|
|
android-sdk="v19"
|
|
gl-texture-group="dxt1"
|
|
device-feature-group="low-latency"/>
|
|
<artifact
|
|
name="art2"
|
|
abi-group="other"
|
|
screen-density-group="alldpi"
|
|
locale-group="north-america"
|
|
android-sdk="v19"
|
|
gl-texture-group="dxt1"
|
|
device-feature-group="low-latency"/>
|
|
</artifacts>
|
|
</post-process>
|
|
)";
|
|
|
|
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<PostProcessingConfiguration> maybe_config =
|
|
ExtractConfiguration(kValidConfig, "dummy.xml", &diag_);
|
|
|
|
PostProcessingConfiguration config = maybe_config.value();
|
|
|
|
auto& arm = config.abi_groups["arm"];
|
|
auto& other = config.abi_groups["other"];
|
|
EXPECT_EQ(arm.order, 1ul);
|
|
EXPECT_EQ(other.order, 2ul);
|
|
|
|
auto& large = config.screen_density_groups["large"];
|
|
auto& alldpi = config.screen_density_groups["alldpi"];
|
|
EXPECT_EQ(large.order, 1ul);
|
|
EXPECT_EQ(alldpi.order, 2ul);
|
|
|
|
auto& north_america = config.locale_groups["north-america"];
|
|
auto& europe = config.locale_groups["europe"];
|
|
// Checked in reverse to make sure access order does not matter.
|
|
EXPECT_EQ(north_america.order, 2ul);
|
|
EXPECT_EQ(europe.order, 1ul);
|
|
}
|
|
|
|
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<OutputArtifact>& 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"(<?xml version="1.0" encoding="utf-8" ?>
|
|
<post-process xmlns="http://schemas.android.com/tools/another-unknown-tool" />)";
|
|
|
|
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(
|
|
<artifact
|
|
abi-group="arm"
|
|
screen-density-group="large"
|
|
locale-group="europe"
|
|
android-sdk="v19"
|
|
gl-texture-group="dxt1"
|
|
device-feature-group="low-latency"/>)xml");
|
|
|
|
ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast<Element>(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(
|
|
<artifact-format>
|
|
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
|
|
</artifact-format>)xml");
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = ArtifactFormatTagHandler(&config, NodeCast<Element>(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<std::string>(config.artifact_format.value())
|
|
);
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, AbiGroupAction) {
|
|
static constexpr const char* xml = R"xml(
|
|
<abi-group label="arm">
|
|
<!-- First comment. -->
|
|
<abi>
|
|
armeabi-v7a
|
|
</abi>
|
|
<!-- Another comment. -->
|
|
<abi>arm64-v8a</abi>
|
|
</abi-group>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(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(<abi-group label="arm64-v8a"/>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(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"].entry;
|
|
ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a));
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
|
|
static constexpr const char* xml = R"xml(<abi-group label="arm"/>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
|
|
ASSERT_FALSE(ok);
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
|
|
static constexpr const char* xml = R"xml(
|
|
<screen-density-group label="large">
|
|
<screen-density>xhdpi</screen-density>
|
|
<screen-density>
|
|
xxhdpi
|
|
</screen-density>
|
|
<screen-density>xxxhdpi</screen-density>
|
|
</screen-density-group>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(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(<screen-density-group label="xhdpi"/>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(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"].entry;
|
|
ASSERT_THAT(out, ElementsAre(xhdpi));
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
|
|
static constexpr const char* xml = R"xml(<screen-density-group label="really-big-screen"/>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
|
|
ASSERT_FALSE(ok);
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, LocaleGroupAction) {
|
|
static constexpr const char* xml = R"xml(
|
|
<locale-group label="europe">
|
|
<locale>en</locale>
|
|
<locale>es</locale>
|
|
<locale>fr</locale>
|
|
<locale>de</locale>
|
|
</locale-group>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(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(<locale-group label="en"/>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(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"].entry;
|
|
|
|
ConfigDescription en = test::ParseConfigOrDie("en");
|
|
|
|
ASSERT_THAT(out, ElementsAre(en));
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) {
|
|
static constexpr const char* xml = R"xml(<locale-group label="arm64"/>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
|
|
ASSERT_FALSE(ok);
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
|
|
static constexpr const char* xml = R"xml(
|
|
<android-sdk label="v19"
|
|
minSdkVersion="19"
|
|
targetSdkVersion="24"
|
|
maxSdkVersion="25">
|
|
<manifest>
|
|
<!--- manifest additions here XSLT? TODO -->
|
|
</manifest>
|
|
</android-sdk>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(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 = "<android-sdk label='v19' minSdkVersion='19'></android-sdk>";
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(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 =
|
|
"<android-sdk label='v19' minSdkVersion='19' maxSdkVersion='19'></android-sdk>";
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(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 =
|
|
"<android-sdk label='v19' minSdkVersion='19' targetSdkVersion='19'></android-sdk>";
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(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(
|
|
<android-sdk
|
|
label="v19"
|
|
minSdkVersion="v19"
|
|
targetSdkVersion="v24"
|
|
maxSdkVersion="v25">
|
|
<manifest>
|
|
<!--- manifest additions here XSLT? TODO -->
|
|
</manifest>
|
|
</android-sdk>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
|
|
ASSERT_FALSE(ok);
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
|
|
static constexpr const char* xml = R"xml(
|
|
<android-sdk
|
|
label="P"
|
|
minSdkVersion="25"
|
|
targetSdkVersion="%s"
|
|
maxSdkVersion="%s">
|
|
</android-sdk>)xml";
|
|
|
|
const auto& dev_sdk = GetDevelopmentSdkCodeNameAndVersion();
|
|
const char* codename = dev_sdk.first.data();
|
|
const ApiVersion& version = dev_sdk.second;
|
|
|
|
auto doc = test::BuildXmlDom(StringPrintf(xml, codename, codename));
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
|
|
ASSERT_TRUE(ok);
|
|
|
|
ASSERT_EQ(1ul, config.android_sdks.size());
|
|
ASSERT_EQ(1u, config.android_sdks.count("P"));
|
|
|
|
auto& out = config.android_sdks["P"];
|
|
|
|
AndroidSdk sdk;
|
|
sdk.min_sdk_version = 25;
|
|
sdk.target_sdk_version = version;
|
|
sdk.max_sdk_version = version;
|
|
|
|
ASSERT_EQ(sdk, out);
|
|
}
|
|
|
|
TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
|
|
static constexpr const char* xml = R"xml(
|
|
<gl-texture-group label="dxt1">
|
|
<gl-texture name="GL_EXT_texture_compression_dxt1">
|
|
<texture-path>assets/dxt1/main/*</texture-path>
|
|
<texture-path>
|
|
assets/dxt1/test/*
|
|
</texture-path>
|
|
</gl-texture>
|
|
</gl-texture-group>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = GlTextureGroupTagHandler(&config, NodeCast<Element>(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(
|
|
<device-feature-group label="low-latency">
|
|
<supports-feature>android.hardware.audio.low_latency</supports-feature>
|
|
<supports-feature>
|
|
android.hardware.audio.pro
|
|
</supports-feature>
|
|
</device-feature-group>)xml";
|
|
|
|
auto doc = test::BuildXmlDom(xml);
|
|
|
|
PostProcessingConfiguration config;
|
|
bool ok = DeviceFeatureGroupTagHandler(&config, NodeCast<Element>(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));
|
|
}
|
|
|
|
// 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<std::string>& 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
|