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