When parsing is complete, we now have a list of output artifacts that have their referential integrity validated. This means that once the configuration file is parsed, the only errors that can occur are related to APK processing, and not the configuration itself. This reduces the number of errors that could cause a partial output of APK artifacts. It simplifies the public API and reduces the complexity of the code to generate multiple APKs. Test: Ran unit tests Test: manually ran the optimize command to ensure it still works Change-Id: I3f2d885b207a84c958f5348a4baa6718598184a4
183 lines
7.1 KiB
C++
183 lines
7.1 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 "format/Archive.h"
|
|
#include "format/binary/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::OutputArtifact;
|
|
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;
|
|
|
|
/**
|
|
* 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(IAaptContext* context,
|
|
const configuration::OutputArtifact& artifact,
|
|
const ResourceTable& old_table,
|
|
FilterChain* filter_chain) override {
|
|
return MultiApkGenerator::FilterTable(context, artifact, old_table, filter_chain);
|
|
}
|
|
};
|
|
|
|
/** 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, VersionFilterNewerVersion) {
|
|
std::unique_ptr<ResourceTable> table = BuildTable();
|
|
|
|
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
|
|
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
|
|
FilterChain chain;
|
|
|
|
OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).SetAndroidSdk(23).Build();
|
|
|
|
MultiApkGeneratorWrapper generator{&apk, ctx.get()};
|
|
std::unique_ptr<ResourceTable> split =
|
|
generator.FilterTable(ctx.get(), artifact, *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();
|
|
|
|
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
|
|
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
|
|
FilterChain chain;
|
|
|
|
OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).SetAndroidSdk(4).Build();
|
|
|
|
MultiApkGeneratorWrapper generator{&apk, ctx.get()};;
|
|
std::unique_ptr<ResourceTable> split =
|
|
generator.FilterTable(ctx.get(), artifact, *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();
|
|
|
|
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
|
|
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
|
|
FilterChain chain;
|
|
|
|
OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).Build();
|
|
|
|
MultiApkGeneratorWrapper generator{&apk, ctx.get()};
|
|
std::unique_ptr<ResourceTable> split =
|
|
generator.FilterTable(ctx.get(), artifact, *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
|