1 /*
2  * Copyright (C) 2015 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/JavaClassGenerator.h"
18 
19 #include <sstream>
20 #include <string>
21 
22 #include "test/Test.h"
23 #include "util/Util.h"
24 
25 using android::StringPiece;
26 
27 namespace aapt {
28 
TEST(JavaClassGeneratorTest,FailWhenEntryIsJavaKeyword)29 TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
30   std::unique_ptr<ResourceTable> table =
31       test::ResourceTableBuilder()
32           .SetPackageId("android", 0x01)
33           .AddSimple("android:id/class", ResourceId(0x01020000))
34           .Build();
35 
36   std::unique_ptr<IAaptContext> context =
37       test::ContextBuilder()
38           .AddSymbolSource(
39               util::make_unique<ResourceTableSymbolSource>(table.get()))
40           .SetNameManglerPolicy(NameManglerPolicy{"android"})
41           .Build();
42   JavaClassGenerator generator(context.get(), table.get(), {});
43 
44   std::stringstream out;
45   EXPECT_FALSE(generator.Generate("android", &out));
46 }
47 
TEST(JavaClassGeneratorTest,TransformInvalidJavaIdentifierCharacter)48 TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
49   std::unique_ptr<ResourceTable> table =
50       test::ResourceTableBuilder()
51           .SetPackageId("android", 0x01)
52           .AddSimple("android:id/hey-man", ResourceId(0x01020000))
53           .AddValue("android:attr/cool.attr", ResourceId(0x01010000),
54                     test::AttributeBuilder(false).Build())
55           .AddValue(
56               "android:styleable/hey.dude", ResourceId(0x01030000),
57               test::StyleableBuilder()
58                   .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
59                   .Build())
60           .Build();
61 
62   std::unique_ptr<IAaptContext> context =
63       test::ContextBuilder()
64           .AddSymbolSource(
65               util::make_unique<ResourceTableSymbolSource>(table.get()))
66           .SetNameManglerPolicy(NameManglerPolicy{"android"})
67           .Build();
68   JavaClassGenerator generator(context.get(), table.get(), {});
69 
70   std::stringstream out;
71   EXPECT_TRUE(generator.Generate("android", &out));
72 
73   std::string output = out.str();
74 
75   EXPECT_NE(std::string::npos,
76             output.find("public static final int hey_man=0x01020000;"));
77 
78   EXPECT_NE(std::string::npos,
79             output.find("public static final int[] hey_dude={"));
80 
81   EXPECT_NE(std::string::npos,
82             output.find("public static final int hey_dude_cool_attr=0;"));
83 }
84 
TEST(JavaClassGeneratorTest,CorrectPackageNameIsUsed)85 TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
86   std::unique_ptr<ResourceTable> table =
87       test::ResourceTableBuilder()
88           .SetPackageId("android", 0x01)
89           .AddSimple("android:id/one", ResourceId(0x01020000))
90           .AddSimple("android:id/com.foo$two", ResourceId(0x01020001))
91           .Build();
92 
93   std::unique_ptr<IAaptContext> context =
94       test::ContextBuilder()
95           .AddSymbolSource(
96               util::make_unique<ResourceTableSymbolSource>(table.get()))
97           .SetNameManglerPolicy(NameManglerPolicy{"android"})
98           .Build();
99   JavaClassGenerator generator(context.get(), table.get(), {});
100   std::stringstream out;
101   ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
102 
103   std::string output = out.str();
104   EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
105   EXPECT_NE(std::string::npos,
106             output.find("public static final int one=0x01020000;"));
107   EXPECT_EQ(std::string::npos, output.find("two"));
108   EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
109 }
110 
TEST(JavaClassGeneratorTest,AttrPrivateIsWrittenAsAttr)111 TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
112   std::unique_ptr<ResourceTable> table =
113       test::ResourceTableBuilder()
114           .SetPackageId("android", 0x01)
115           .AddSimple("android:attr/two", ResourceId(0x01010001))
116           .AddSimple("android:^attr-private/one", ResourceId(0x01010000))
117           .Build();
118 
119   std::unique_ptr<IAaptContext> context =
120       test::ContextBuilder()
121           .AddSymbolSource(
122               util::make_unique<ResourceTableSymbolSource>(table.get()))
123           .SetNameManglerPolicy(NameManglerPolicy{"android"})
124           .Build();
125   JavaClassGenerator generator(context.get(), table.get(), {});
126   std::stringstream out;
127   ASSERT_TRUE(generator.Generate("android", &out));
128 
129   std::string output = out.str();
130   EXPECT_NE(std::string::npos, output.find("public static final class attr"));
131   EXPECT_EQ(std::string::npos,
132             output.find("public static final class ^attr-private"));
133 }
134 
TEST(JavaClassGeneratorTest,OnlyWritePublicResources)135 TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
136   StdErrDiagnostics diag;
137   std::unique_ptr<ResourceTable> table =
138       test::ResourceTableBuilder()
139           .SetPackageId("android", 0x01)
140           .AddSimple("android:id/one", ResourceId(0x01020000))
141           .AddSimple("android:id/two", ResourceId(0x01020001))
142           .AddSimple("android:id/three", ResourceId(0x01020002))
143           .SetSymbolState("android:id/one", ResourceId(0x01020000),
144                           SymbolState::kPublic)
145           .SetSymbolState("android:id/two", ResourceId(0x01020001),
146                           SymbolState::kPrivate)
147           .Build();
148 
149   std::unique_ptr<IAaptContext> context =
150       test::ContextBuilder()
151           .AddSymbolSource(
152               util::make_unique<ResourceTableSymbolSource>(table.get()))
153           .SetNameManglerPolicy(NameManglerPolicy{"android"})
154           .Build();
155 
156   JavaClassGeneratorOptions options;
157   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
158   {
159     JavaClassGenerator generator(context.get(), table.get(), options);
160     std::stringstream out;
161     ASSERT_TRUE(generator.Generate("android", &out));
162     std::string output = out.str();
163     EXPECT_NE(std::string::npos,
164               output.find("public static final int one=0x01020000;"));
165     EXPECT_EQ(std::string::npos, output.find("two"));
166     EXPECT_EQ(std::string::npos, output.find("three"));
167   }
168 
169   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
170   {
171     JavaClassGenerator generator(context.get(), table.get(), options);
172     std::stringstream out;
173     ASSERT_TRUE(generator.Generate("android", &out));
174     std::string output = out.str();
175     EXPECT_NE(std::string::npos,
176               output.find("public static final int one=0x01020000;"));
177     EXPECT_NE(std::string::npos,
178               output.find("public static final int two=0x01020001;"));
179     EXPECT_EQ(std::string::npos, output.find("three"));
180   }
181 
182   options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
183   {
184     JavaClassGenerator generator(context.get(), table.get(), options);
185     std::stringstream out;
186     ASSERT_TRUE(generator.Generate("android", &out));
187     std::string output = out.str();
188     EXPECT_NE(std::string::npos,
189               output.find("public static final int one=0x01020000;"));
190     EXPECT_NE(std::string::npos,
191               output.find("public static final int two=0x01020001;"));
192     EXPECT_NE(std::string::npos,
193               output.find("public static final int three=0x01020002;"));
194   }
195 }
196 
197 /*
198  * TODO(adamlesinski): Re-enable this once we get merging working again.
199  * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
200     ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
201                             ResourceId{ 0x01, 0x02, 0x0000 }));
202     ResourceTable table;
203     table.setPackage(u"com.lib");
204     ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test"
205 }, {},
206                                   Source{ "lib.xml", 33 },
207 util::make_unique<Id>()));
208     ASSERT_TRUE(mTable->merge(std::move(table)));
209 
210     Linker linker(mTable,
211                   std::make_shared<MockResolver>(mTable, std::map<ResourceName,
212 ResourceId>()),
213                   {});
214     ASSERT_TRUE(linker.linkAndValidate());
215 
216     JavaClassGenerator generator(mTable, {});
217 
218     std::stringstream out;
219     EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
220     std::string output = out.str();
221     EXPECT_NE(std::string::npos, output.find("int foo ="));
222     EXPECT_EQ(std::string::npos, output.find("int test ="));
223 
224     out.str("");
225     EXPECT_TRUE(generator.generate(u"com.lib", out));
226     output = out.str();
227     EXPECT_NE(std::string::npos, output.find("int test ="));
228     EXPECT_EQ(std::string::npos, output.find("int foo ="));
229 }*/
230 
TEST(JavaClassGeneratorTest,EmitOtherPackagesAttributesInStyleable)231 TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
232   std::unique_ptr<ResourceTable> table =
233       test::ResourceTableBuilder()
234           .SetPackageId("android", 0x01)
235           .SetPackageId("com.lib", 0x02)
236           .AddValue("android:attr/bar", ResourceId(0x01010000),
237                     test::AttributeBuilder(false).Build())
238           .AddValue("com.lib:attr/bar", ResourceId(0x02010000),
239                     test::AttributeBuilder(false).Build())
240           .AddValue("android:styleable/foo", ResourceId(0x01030000),
241                     test::StyleableBuilder()
242                         .AddItem("android:attr/bar", ResourceId(0x01010000))
243                         .AddItem("com.lib:attr/bar", ResourceId(0x02010000))
244                         .Build())
245           .Build();
246 
247   std::unique_ptr<IAaptContext> context =
248       test::ContextBuilder()
249           .AddSymbolSource(
250               util::make_unique<ResourceTableSymbolSource>(table.get()))
251           .SetNameManglerPolicy(NameManglerPolicy{"android"})
252           .Build();
253   JavaClassGenerator generator(context.get(), table.get(), {});
254 
255   std::stringstream out;
256   EXPECT_TRUE(generator.Generate("android", &out));
257 
258   std::string output = out.str();
259   EXPECT_NE(std::string::npos, output.find("int foo_bar="));
260   EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
261 }
262 
TEST(JavaClassGeneratorTest,CommentsForSimpleResourcesArePresent)263 TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
264   std::unique_ptr<ResourceTable> table =
265       test::ResourceTableBuilder()
266           .SetPackageId("android", 0x01)
267           .AddSimple("android:id/foo", ResourceId(0x01010000))
268           .Build();
269   test::GetValue<Id>(table.get(), "android:id/foo")
270       ->SetComment(std::string("This is a comment\n@deprecated"));
271 
272   std::unique_ptr<IAaptContext> context =
273       test::ContextBuilder()
274           .AddSymbolSource(
275               util::make_unique<ResourceTableSymbolSource>(table.get()))
276           .SetNameManglerPolicy(NameManglerPolicy{"android"})
277           .Build();
278   JavaClassGenerator generator(context.get(), table.get(), {});
279   std::stringstream out;
280   ASSERT_TRUE(generator.Generate("android", &out));
281   std::string actual = out.str();
282 
283   const char* expectedText =
284       R"EOF(/**
285      * This is a comment
286      * @deprecated
287      */
288     @Deprecated
289     public static final int foo=0x01010000;)EOF";
290 
291   EXPECT_NE(std::string::npos, actual.find(expectedText));
292 }
293 
TEST(JavaClassGeneratorTest,CommentsForEnumAndFlagAttributesArePresent)294 TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
295 
TEST(JavaClassGeneratorTest,CommentsForStyleablesAndNestedAttributesArePresent)296 TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
297   Attribute attr(false);
298   attr.SetComment(StringPiece("This is an attribute"));
299 
300   Styleable styleable;
301   styleable.entries.push_back(
302       Reference(test::ParseNameOrDie("android:attr/one")));
303   styleable.SetComment(StringPiece("This is a styleable"));
304 
305   std::unique_ptr<ResourceTable> table =
306       test::ResourceTableBuilder()
307           .SetPackageId("android", 0x01)
308           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
309           .AddValue("android:styleable/Container",
310                     std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
311           .Build();
312 
313   std::unique_ptr<IAaptContext> context =
314       test::ContextBuilder()
315           .AddSymbolSource(
316               util::make_unique<ResourceTableSymbolSource>(table.get()))
317           .SetNameManglerPolicy(NameManglerPolicy{"android"})
318           .Build();
319   JavaClassGeneratorOptions options;
320   options.use_final = false;
321   JavaClassGenerator generator(context.get(), table.get(), options);
322   std::stringstream out;
323   ASSERT_TRUE(generator.Generate("android", &out));
324   std::string actual = out.str();
325 
326   EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
327   EXPECT_NE(std::string::npos, actual.find("attr description"));
328   EXPECT_NE(std::string::npos, actual.find(attr.GetComment().data()));
329   EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data()));
330 }
331 
TEST(JavaClassGeneratorTest,CommentsForRemovedAttributesAreNotPresentInClass)332 TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
333   Attribute attr(false);
334   attr.SetComment(StringPiece("removed"));
335 
336   std::unique_ptr<ResourceTable> table =
337       test::ResourceTableBuilder()
338           .SetPackageId("android", 0x01)
339           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
340           .Build();
341 
342   std::unique_ptr<IAaptContext> context =
343       test::ContextBuilder()
344           .AddSymbolSource(
345               util::make_unique<ResourceTableSymbolSource>(table.get()))
346           .SetNameManglerPolicy(NameManglerPolicy{"android"})
347           .Build();
348   JavaClassGeneratorOptions options;
349   options.use_final = false;
350   JavaClassGenerator generator(context.get(), table.get(), options);
351   std::stringstream out;
352   ASSERT_TRUE(generator.Generate("android", &out));
353   std::string actual = out.str();
354 
355   EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
356   EXPECT_EQ(std::string::npos, actual.find("@attr description"));
357 
358   // We should find @removed only in the attribute javadoc and not anywhere else
359   // (i.e. the class
360   // javadoc).
361   const size_t pos = actual.find("removed");
362   EXPECT_NE(std::string::npos, pos);
363   EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
364 }
365 
TEST(JavaClassGeneratorTest,GenerateOnResourcesLoadedCallbackForSharedLibrary)366 TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
367   std::unique_ptr<ResourceTable> table =
368       test::ResourceTableBuilder()
369           .SetPackageId("android", 0x00)
370           .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>(false))
371           .AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
372           .AddValue(
373               "android:style/foo", ResourceId(0x00030000),
374               test::StyleBuilder()
375                   .AddItem("android:attr/foo", ResourceId(0x00010000), util::make_unique<Id>())
376                   .Build())
377           .Build();
378 
379   std::unique_ptr<IAaptContext> context =
380       test::ContextBuilder().SetPackageId(0x00).SetCompilationPackage("android").Build();
381 
382   JavaClassGeneratorOptions options;
383   options.use_final = false;
384   options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
385       {"com.foo", "com.boo"},
386   };
387   JavaClassGenerator generator(context.get(), table.get(), options);
388 
389   std::stringstream out;
390   ASSERT_TRUE(generator.Generate("android", &out));
391 
392   std::string actual = out.str();
393 
394   EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
395   EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
396   EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
397 }
398 
399 }  // namespace aapt
400