Files
frameworks_base/tools/aapt2/optimize/MultiApkGenerator_test.cpp
Shane Farmer efe45392c3 AAPT2: Multi APK generator by version
- Added an additional axis for generating a multi-apk split by minimum
Android SDK version. This removes any resources that will not be used
for the desired minimum SDK version. If there are multiple resources
that would be valid for any version newer than the requested minimum,
then all would be kept so that the best match can be found.

- Added a context wrapper to set the appropriate Android SDK version for
each generated artifact.

- Split out the FilterTable method to allow it to be directly tested
without the need to mock the APK writing steps.

Test: Unit tests
Test: manually run optimize command

Change-Id: I7e6018df081af9ed5d9e8aaf40ed216c1275f138
2017-08-31 16:30:38 -07:00

275 lines
10 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 "optimize/MultiApkGenerator.h"
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "LoadedApk.h"
#include "ResourceTable.h"
#include "configuration/ConfigurationParser.h"
#include "filter/Filter.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
#include "process/IResourceTableConsumer.h"
#include "test/Context.h"
#include "test/Test.h"
namespace aapt {
namespace {
using ::aapt::configuration::Abi;
using ::aapt::configuration::AndroidSdk;
using ::aapt::configuration::Artifact;
using ::aapt::configuration::PostProcessingConfiguration;
using ::aapt::test::GetValue;
using ::aapt::test::GetValueForConfig;
using ::aapt::test::ParseConfigOrDie;
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::Not;
using ::testing::NotNull;
using ::testing::PrintToString;
using ::testing::Return;
using ::testing::Test;
using ::testing::_;
/** Subclass the LoadedApk class so that we can mock the WriteToArchive method. */
class MockApk : public LoadedApk {
public:
MockApk(std::unique_ptr<ResourceTable> table) : LoadedApk({"test.apk"}, {}, std::move(table)){};
MOCK_METHOD5(WriteToArchive, bool(IAaptContext*, ResourceTable*, const TableFlattenerOptions&,
FilterChain*, IArchiveWriter*));
};
/**
* Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to
* directly test table filter.
*/
class MultiApkGeneratorWrapper : public MultiApkGenerator {
public:
MultiApkGeneratorWrapper(LoadedApk* apk, IAaptContext* context)
: MultiApkGenerator(apk, context) {
}
std::unique_ptr<ResourceTable> FilterTable(
const configuration::Artifact& artifact,
const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
FilterChain* pChain) override {
return MultiApkGenerator::FilterTable(artifact, config, old_table, pChain);
}
};
/** MultiApkGenerator test fixture. */
class MultiApkGeneratorTest : public ::testing::Test {
public:
std::unique_ptr<ResourceTable> BuildTable() {
return test::ResourceTableBuilder()
.AddFileReference(kResourceName, "res/drawable-mdpi/icon.png", mdpi_)
.AddFileReference(kResourceName, "res/drawable-hdpi/icon.png", hdpi_)
.AddFileReference(kResourceName, "res/drawable-xhdpi/icon.png", xhdpi_)
.AddFileReference(kResourceName, "res/drawable-xxhdpi/icon.png", xxhdpi_)
.AddFileReference(kResourceName, "res/drawable-v19/icon.xml", v19_)
.AddFileReference(kResourceName, "res/drawable-v21/icon.xml", v21_)
.AddSimple("android:string/one")
.Build();
}
inline FileReference* ValueForConfig(ResourceTable* table, const ConfigDescription& config) {
return GetValueForConfig<FileReference>(table, kResourceName, config);
};
void SetUp() override {
}
protected:
static constexpr const char* kResourceName = "android:drawable/icon";
ConfigDescription default_ = ParseConfigOrDie("").CopyWithoutSdkVersion();
ConfigDescription mdpi_ = ParseConfigOrDie("mdpi").CopyWithoutSdkVersion();
ConfigDescription hdpi_ = ParseConfigOrDie("hdpi").CopyWithoutSdkVersion();
ConfigDescription xhdpi_ = ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion();
ConfigDescription xxhdpi_ = ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion();
ConfigDescription xxxhdpi_ = ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion();
ConfigDescription v19_ = ParseConfigOrDie("v19");
ConfigDescription v21_ = ParseConfigOrDie("v21");
};
TEST_F(MultiApkGeneratorTest, FromBaseApk) {
std::unique_ptr<ResourceTable> table = BuildTable();
MockApk apk{std::move(table)};
EXPECT_CALL(apk, WriteToArchive(_, _, _, _, _)).Times(0);
test::Context ctx;
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
MultiApkGenerator generator{&apk, &ctx};
EXPECT_TRUE(generator.FromBaseApk({"out", empty_config, table_flattener_options}));
Artifact x64 = test::ArtifactBuilder()
.SetName("${basename}.x64.apk")
.SetAbiGroup("x64")
.SetLocaleGroup("en")
.SetDensityGroup("xhdpi")
.Build();
Artifact intel = test::ArtifactBuilder()
.SetName("${basename}.intel.apk")
.SetAbiGroup("intel")
.SetLocaleGroup("europe")
.SetDensityGroup("large")
.Build();
auto config = test::PostProcessingConfigurationBuilder()
.SetLocaleGroup("en", {"en"})
.SetLocaleGroup("europe", {"en", "fr", "de", "es"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetAbiGroup("intel", {Abi::kX86_64, Abi::kX86})
.SetDensityGroup("xhdpi", {"xhdpi"})
.SetDensityGroup("large", {"xhdpi", "xxhdpi", "xxxhdpi"})
.AddArtifact(x64)
.AddArtifact(intel)
.Build();
// Called once for each artifact.
EXPECT_CALL(apk, WriteToArchive(Eq(&ctx), _, _, _, _)).Times(2).WillRepeatedly(Return(true));
EXPECT_TRUE(generator.FromBaseApk({"out", config, table_flattener_options}));
}
TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
MockApk apk{std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
FilterChain chain;
Artifact x64 = test::ArtifactBuilder()
.SetName("${basename}.${sdk}.apk")
.SetDensityGroup("xhdpi")
.SetAndroidSdk("v23")
.Build();
auto config = test::PostProcessingConfigurationBuilder()
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
.SetAndroidSdk("v23", AndroidSdk::ForMinSdk("v23"))
.AddArtifact(x64)
.Build();
MultiApkGeneratorWrapper generator{&apk, ctx.get()};
std::unique_ptr<ResourceTable> split =
generator.FilterTable(x64, config, *apk.GetResourceTable(), &chain);
ResourceTable* new_table = split.get();
EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, v19_), IsNull());
// xhdpi directly matches one of the required dimensions.
EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull());
// drawable-v21 was converted to drawable.
EXPECT_THAT(ValueForConfig(new_table, default_), NotNull());
EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull());
}
TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
MockApk apk{std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
FilterChain chain;
Artifact x64 = test::ArtifactBuilder()
.SetName("${basename}.${sdk}.apk")
.SetDensityGroup("xhdpi")
.SetAndroidSdk("v4")
.Build();
auto config = test::PostProcessingConfigurationBuilder()
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
.SetAndroidSdk("v4", AndroidSdk::ForMinSdk("v4"))
.AddArtifact(x64)
.Build();
MultiApkGeneratorWrapper generator{&apk, ctx.get()};;
std::unique_ptr<ResourceTable> split =
generator.FilterTable(x64, config, *apk.GetResourceTable(), &chain);
ResourceTable* new_table = split.get();
EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull());
EXPECT_THAT(ValueForConfig(new_table, v19_), NotNull());
EXPECT_THAT(ValueForConfig(new_table, v21_), NotNull());
EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull());
}
TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
MockApk apk{std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
FilterChain chain;
Artifact x64 =
test::ArtifactBuilder().SetName("${basename}.${sdk}.apk").SetDensityGroup("xhdpi").Build();
auto config = test::PostProcessingConfigurationBuilder()
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
.AddArtifact(x64)
.Build();
MultiApkGeneratorWrapper generator{&apk, ctx.get()};
std::unique_ptr<ResourceTable> split =
generator.FilterTable(x64, config, *apk.GetResourceTable(), &chain);
ResourceTable* new_table = split.get();
EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull());
EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull());
EXPECT_THAT(ValueForConfig(new_table, v19_), NotNull());
EXPECT_THAT(ValueForConfig(new_table, v21_), NotNull());
EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull());
}
} // namespace
} // namespace aapt