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
23using ::aapt::io::StringOutputStream;
24using ::testing::HasSubstr;
25using ::testing::Not;
26
27namespace aapt {
28
29std::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
37TEST(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
52TEST(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
66TEST(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
83TEST(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
99TEST(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
140TEST(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
161TEST(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
179TEST(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
194TEST(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