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 "java/ProguardRules.h"
18 #include "link/Linkers.h"
19
20 #include "io/StringStream.h"
21 #include "test/Test.h"
22
23 using ::aapt::io::StringOutputStream;
24 using ::testing::HasSubstr;
25 using ::testing::Not;
26
27 namespace aapt {
28
GetKeepSetString(const proguard::KeepSet & set)29 std::string GetKeepSetString(const proguard::KeepSet& set) {
30 std::string out;
31 StringOutputStream sout(&out);
32 proguard::WriteKeepSet(set, &sout);
33 sout.Flush();
34 return out;
35 }
36
TEST(ProguardRulesTest,FragmentNameRuleIsEmitted)37 TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
38 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
39 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
40 <fragment xmlns:android="http://schemas.android.com/apk/res/android"
41 android:name="com.foo.Bar"/>)");
42 layout->file.name = test::ParseNameOrDie("layout/foo");
43
44 proguard::KeepSet set;
45 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
46
47 std::string actual = GetKeepSetString(set);
48
49 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
50 }
51
52 TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
53 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
54 std::unique_ptr<xml::XmlResource> layout =
55 test::BuildXmlDom(R"(<fragment class="com.foo.Bar"/>)");
56 layout->file.name = test::ParseNameOrDie("layout/foo");
57
58 proguard::KeepSet set;
59 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
60
61 std::string actual = GetKeepSetString(set);
62
63 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
64 }
65
66 TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
67 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
68 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
69 <fragment xmlns:android="http://schemas.android.com/apk/res/android"
70 android:name="com.foo.Baz"
71 class="com.foo.Bar"/>)");
72 layout->file.name = test::ParseNameOrDie("layout/foo");
73
74 proguard::KeepSet set;
75 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
76
77 std::string actual = GetKeepSetString(set);
78
79 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
80 EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
81 }
82
83 TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
84 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
85 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
86 <View xmlns:android="http://schemas.android.com/apk/res/android">
87 <com.foo.Bar />
88 </View>)");
89 layout->file.name = test::ParseNameOrDie("layout/foo");
90
91 proguard::KeepSet set;
92 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
93
94 std::string actual = GetKeepSetString(set);
95
96 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
97 }
98
99 TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
100 std::unique_ptr<xml::XmlResource> bar_layout = test::BuildXmlDom(R"(
101 <View xmlns:android="http://schemas.android.com/apk/res/android">
102 <com.foo.Bar />
103 </View>)");
104 bar_layout->file.name = test::ParseNameOrDie("com.foo:layout/bar");
105
106 ResourceTable table;
107 StdErrDiagnostics errDiagnostics;
108 table.AddResource(bar_layout->file.name, ConfigDescription::DefaultConfig(), "",
109 util::make_unique<FileReference>(), &errDiagnostics);
110
111 std::unique_ptr<IAaptContext> context =
112 test::ContextBuilder()
113 .SetCompilationPackage("com.foo")
114 .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(&table))
115 .Build();
116
117 std::unique_ptr<xml::XmlResource> foo_layout = test::BuildXmlDom(R"(
118 <View xmlns:android="http://schemas.android.com/apk/res/android">
119 <include layout="@layout/bar" />
120 </View>)");
121 foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
122
123 XmlReferenceLinker xml_linker;
124 ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
125 ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
126
127 proguard::KeepSet set = proguard::KeepSet(true);
128 ASSERT_TRUE(proguard::CollectProguardRules(bar_layout.get(), &set));
129 ASSERT_TRUE(proguard::CollectProguardRules(foo_layout.get(), &set));
130
131 std::string actual = GetKeepSetString(set);
132
133 EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
134 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
135 EXPECT_THAT(actual, HasSubstr("int foo"));
136 EXPECT_THAT(actual, HasSubstr("int bar"));
137 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
138 }
139
140 TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) {
141 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
142 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
143 <View xmlns:android="http://schemas.android.com/apk/res/android">
144 <com.foo.Bar />
145 </View>)");
146 layout->file.name = test::ParseNameOrDie("layout/foo");
147
148 proguard::KeepSet set = proguard::KeepSet(true);
149 set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
150 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
151
152 std::string actual = GetKeepSetString(set);
153
154 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
155 EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
156 EXPECT_THAT(actual, HasSubstr("int foo"));
157 EXPECT_THAT(actual, HasSubstr("int bar"));
158 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
159 }
160
161 TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
162 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
163 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
164 <View xmlns:android="http://schemas.android.com/apk/res/android">
165 <com.foo.Bar />
166 </View>)");
167 layout->file.name = test::ParseNameOrDie("layout/foo");
168
169 proguard::KeepSet set = proguard::KeepSet(true);
170 set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
171 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
172
173 std::string actual = GetKeepSetString(set);
174
175 EXPECT_THAT(actual, Not(HasSubstr("-if")));
176 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
177 }
178
179 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
180 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
181 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
182 <View xmlns:android="http://schemas.android.com/apk/res/android"
183 android:onClick="bar_method" />)");
184 layout->file.name = test::ParseNameOrDie("layout/foo");
185
186 proguard::KeepSet set;
187 ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
188
189 std::string actual = GetKeepSetString(set);
190
191 EXPECT_THAT(actual, HasSubstr("bar_method"));
192 }
193
194 TEST(ProguardRulesTest, MenuRulesAreEmitted) {
195 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
196 std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
197 <menu xmlns:android="http://schemas.android.com/apk/res/android">
198 <item android:onClick="on_click"
199 android:actionViewClass="com.foo.Bar"
200 android:actionProviderClass="com.foo.Baz"
201 android:name="com.foo.Bat" />
202 </menu>)");
203 menu->file.name = test::ParseNameOrDie("menu/foo");
204
205 proguard::KeepSet set;
206 ASSERT_TRUE(proguard::CollectProguardRules(menu.get(), &set));
207
208 std::string actual = GetKeepSetString(set);
209
210 EXPECT_THAT(actual, HasSubstr("on_click"));
211 EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
212 EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
213 EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
214 }
215
216 } // namespace aapt
217