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 "optimize/MultiApkGenerator.h" 18 19#include <string> 20 21#include "gmock/gmock.h" 22#include "gtest/gtest.h" 23 24#include "LoadedApk.h" 25#include "ResourceTable.h" 26#include "configuration/ConfigurationParser.h" 27#include "filter/Filter.h" 28#include "format/Archive.h" 29#include "format/binary/TableFlattener.h" 30#include "process/IResourceTableConsumer.h" 31#include "test/Context.h" 32#include "test/Test.h" 33 34namespace aapt { 35namespace { 36 37using ::aapt::configuration::Abi; 38using ::aapt::configuration::AndroidSdk; 39using ::aapt::configuration::OutputArtifact; 40using ::aapt::test::GetValue; 41using ::aapt::test::GetValueForConfig; 42using ::aapt::test::ParseConfigOrDie; 43using ::testing::Eq; 44using ::testing::IsNull; 45using ::testing::Not; 46using ::testing::NotNull; 47using ::testing::PrintToString; 48using ::testing::Return; 49using ::testing::Test; 50 51/** 52 * Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to 53 * directly test table filter. 54 */ 55class MultiApkGeneratorWrapper : public MultiApkGenerator { 56 public: 57 MultiApkGeneratorWrapper(LoadedApk* apk, IAaptContext* context) 58 : MultiApkGenerator(apk, context) { 59 } 60 61 std::unique_ptr<ResourceTable> FilterTable(IAaptContext* context, 62 const configuration::OutputArtifact& artifact, 63 const ResourceTable& old_table, 64 FilterChain* filter_chain) override { 65 return MultiApkGenerator::FilterTable(context, artifact, old_table, filter_chain); 66 } 67}; 68 69/** MultiApkGenerator test fixture. */ 70class MultiApkGeneratorTest : public ::testing::Test { 71 public: 72 std::unique_ptr<ResourceTable> BuildTable() { 73 return test::ResourceTableBuilder() 74 .AddFileReference(kResourceName, "res/drawable-mdpi/icon.png", mdpi_) 75 .AddFileReference(kResourceName, "res/drawable-hdpi/icon.png", hdpi_) 76 .AddFileReference(kResourceName, "res/drawable-xhdpi/icon.png", xhdpi_) 77 .AddFileReference(kResourceName, "res/drawable-xxhdpi/icon.png", xxhdpi_) 78 .AddFileReference(kResourceName, "res/drawable-v19/icon.xml", v19_) 79 .AddFileReference(kResourceName, "res/drawable-v21/icon.xml", v21_) 80 .AddSimple("android:string/one") 81 .Build(); 82 } 83 84 inline FileReference* ValueForConfig(ResourceTable* table, const ConfigDescription& config) { 85 return GetValueForConfig<FileReference>(table, kResourceName, config); 86 }; 87 88 void SetUp() override { 89 } 90 91 protected: 92 static constexpr const char* kResourceName = "android:drawable/icon"; 93 94 ConfigDescription default_ = ParseConfigOrDie("").CopyWithoutSdkVersion(); 95 ConfigDescription mdpi_ = ParseConfigOrDie("mdpi").CopyWithoutSdkVersion(); 96 ConfigDescription hdpi_ = ParseConfigOrDie("hdpi").CopyWithoutSdkVersion(); 97 ConfigDescription xhdpi_ = ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(); 98 ConfigDescription xxhdpi_ = ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(); 99 ConfigDescription xxxhdpi_ = ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion(); 100 ConfigDescription v19_ = ParseConfigOrDie("v19"); 101 ConfigDescription v21_ = ParseConfigOrDie("v21"); 102}; 103 104TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) { 105 std::unique_ptr<ResourceTable> table = BuildTable(); 106 107 LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}, kBinary}; 108 std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build(); 109 FilterChain chain; 110 111 OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).SetAndroidSdk(23).Build(); 112 113 MultiApkGeneratorWrapper generator{&apk, ctx.get()}; 114 std::unique_ptr<ResourceTable> split = 115 generator.FilterTable(ctx.get(), artifact, *apk.GetResourceTable(), &chain); 116 117 ResourceTable* new_table = split.get(); 118 EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull()); 119 EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull()); 120 EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull()); 121 EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull()); 122 EXPECT_THAT(ValueForConfig(new_table, v19_), IsNull()); 123 124 // xhdpi directly matches one of the required dimensions. 125 EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull()); 126 // drawable-v21 was converted to drawable. 127 EXPECT_THAT(ValueForConfig(new_table, default_), NotNull()); 128 EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull()); 129} 130 131TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) { 132 std::unique_ptr<ResourceTable> table = BuildTable(); 133 134 LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}, kBinary}; 135 std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build(); 136 FilterChain chain; 137 138 OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).SetAndroidSdk(4).Build(); 139 140 MultiApkGeneratorWrapper generator{&apk, ctx.get()};; 141 std::unique_ptr<ResourceTable> split = 142 generator.FilterTable(ctx.get(), artifact, *apk.GetResourceTable(), &chain); 143 144 ResourceTable* new_table = split.get(); 145 EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull()); 146 EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull()); 147 EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull()); 148 EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull()); 149 150 EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull()); 151 EXPECT_THAT(ValueForConfig(new_table, v19_), NotNull()); 152 EXPECT_THAT(ValueForConfig(new_table, v21_), NotNull()); 153 EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull()); 154} 155 156TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) { 157 std::unique_ptr<ResourceTable> table = BuildTable(); 158 159 LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}, kBinary}; 160 std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build(); 161 FilterChain chain; 162 163 OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).Build(); 164 165 MultiApkGeneratorWrapper generator{&apk, ctx.get()}; 166 std::unique_ptr<ResourceTable> split = 167 generator.FilterTable(ctx.get(), artifact, *apk.GetResourceTable(), &chain); 168 169 ResourceTable* new_table = split.get(); 170 EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull()); 171 EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull()); 172 EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull()); 173 EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull()); 174 175 EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull()); 176 EXPECT_THAT(ValueForConfig(new_table, v19_), NotNull()); 177 EXPECT_THAT(ValueForConfig(new_table, v21_), NotNull()); 178 EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull()); 179} 180 181} // namespace 182} // namespace aapt 183