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