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 #include "test/Test.h"
19 #include "util/Util.h"
20 
21 #include <sstream>
22 #include <string>
23 
24 namespace aapt {
25 
TEST(JavaClassGeneratorTest,FailWhenEntryIsJavaKeyword)26 TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
27     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
28             .setPackageId(u"android", 0x01)
29             .addSimple(u"@android:id/class", ResourceId(0x01020000))
30             .build();
31 
32     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
33             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
34             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
35             .build();
36     JavaClassGenerator generator(context.get(), table.get(), {});
37 
38     std::stringstream out;
39     EXPECT_FALSE(generator.generate(u"android", &out));
40 }
41 
TEST(JavaClassGeneratorTest,TransformInvalidJavaIdentifierCharacter)42 TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
43     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
44             .setPackageId(u"android", 0x01)
45             .addSimple(u"@android:id/hey-man", ResourceId(0x01020000))
46             .addValue(u"@android:attr/cool.attr", ResourceId(0x01010000),
47                       test::AttributeBuilder(false).build())
48             .addValue(u"@android:styleable/hey.dude", ResourceId(0x01030000),
49                       test::StyleableBuilder()
50                               .addItem(u"@android:attr/cool.attr", ResourceId(0x01010000))
51                               .build())
52             .build();
53 
54     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
55             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
56             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
57             .build();
58     JavaClassGenerator generator(context.get(), table.get(), {});
59 
60     std::stringstream out;
61     EXPECT_TRUE(generator.generate(u"android", &out));
62 
63     std::string output = out.str();
64 
65     EXPECT_NE(std::string::npos,
66               output.find("public static final int hey_man=0x01020000;"));
67 
68     EXPECT_NE(std::string::npos,
69               output.find("public static final int[] hey_dude={"));
70 
71     EXPECT_NE(std::string::npos,
72               output.find("public static final int hey_dude_cool_attr=0;"));
73 }
74 
TEST(JavaClassGeneratorTest,CorrectPackageNameIsUsed)75 TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
76     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
77             .setPackageId(u"android", 0x01)
78             .addSimple(u"@android:id/one", ResourceId(0x01020000))
79             .addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001))
80             .build();
81 
82     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
83             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
84             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
85             .build();
86     JavaClassGenerator generator(context.get(), table.get(), {});
87     std::stringstream out;
88     ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out));
89 
90     std::string output = out.str();
91     EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
92     EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
93     EXPECT_EQ(std::string::npos, output.find("two"));
94     EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
95 }
96 
TEST(JavaClassGeneratorTest,AttrPrivateIsWrittenAsAttr)97 TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
98     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
99             .setPackageId(u"android", 0x01)
100             .addSimple(u"@android:attr/two", ResourceId(0x01010001))
101             .addSimple(u"@android:^attr-private/one", ResourceId(0x01010000))
102             .build();
103 
104     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
105             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
106             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
107             .build();
108     JavaClassGenerator generator(context.get(), table.get(), {});
109     std::stringstream out;
110     ASSERT_TRUE(generator.generate(u"android", &out));
111 
112     std::string output = out.str();
113     EXPECT_NE(std::string::npos, output.find("public static final class attr"));
114     EXPECT_EQ(std::string::npos, output.find("public static final class ^attr-private"));
115 }
116 
TEST(JavaClassGeneratorTest,OnlyWritePublicResources)117 TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
118     StdErrDiagnostics diag;
119     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
120             .setPackageId(u"android", 0x01)
121             .addSimple(u"@android:id/one", ResourceId(0x01020000))
122             .addSimple(u"@android:id/two", ResourceId(0x01020001))
123             .addSimple(u"@android:id/three", ResourceId(0x01020002))
124             .setSymbolState(u"@android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
125             .setSymbolState(u"@android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
126             .build();
127 
128     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
129             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
130             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
131             .build();
132 
133     JavaClassGeneratorOptions options;
134     options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
135     {
136         JavaClassGenerator generator(context.get(), table.get(), options);
137         std::stringstream out;
138         ASSERT_TRUE(generator.generate(u"android", &out));
139         std::string output = out.str();
140         EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
141         EXPECT_EQ(std::string::npos, output.find("two"));
142         EXPECT_EQ(std::string::npos, output.find("three"));
143     }
144 
145     options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
146     {
147         JavaClassGenerator generator(context.get(), table.get(), options);
148         std::stringstream out;
149         ASSERT_TRUE(generator.generate(u"android", &out));
150         std::string output = out.str();
151         EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
152         EXPECT_NE(std::string::npos, output.find("public static final int two=0x01020001;"));
153         EXPECT_EQ(std::string::npos, output.find("three"));
154     }
155 
156     options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
157     {
158         JavaClassGenerator generator(context.get(), table.get(), options);
159         std::stringstream out;
160         ASSERT_TRUE(generator.generate(u"android", &out));
161         std::string output = out.str();
162         EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
163         EXPECT_NE(std::string::npos, output.find("public static final int two=0x01020001;"));
164         EXPECT_NE(std::string::npos, output.find("public static final int three=0x01020002;"));
165     }
166 }
167 
168 /*
169  * TODO(adamlesinski): Re-enable this once we get merging working again.
170  * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
171     ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
172                             ResourceId{ 0x01, 0x02, 0x0000 }));
173     ResourceTable table;
174     table.setPackage(u"com.lib");
175     ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test" }, {},
176                                   Source{ "lib.xml", 33 }, util::make_unique<Id>()));
177     ASSERT_TRUE(mTable->merge(std::move(table)));
178 
179     Linker linker(mTable,
180                   std::make_shared<MockResolver>(mTable, std::map<ResourceName, ResourceId>()),
181                   {});
182     ASSERT_TRUE(linker.linkAndValidate());
183 
184     JavaClassGenerator generator(mTable, {});
185 
186     std::stringstream out;
187     EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
188     std::string output = out.str();
189     EXPECT_NE(std::string::npos, output.find("int foo ="));
190     EXPECT_EQ(std::string::npos, output.find("int test ="));
191 
192     out.str("");
193     EXPECT_TRUE(generator.generate(u"com.lib", out));
194     output = out.str();
195     EXPECT_NE(std::string::npos, output.find("int test ="));
196     EXPECT_EQ(std::string::npos, output.find("int foo ="));
197 }*/
198 
TEST(JavaClassGeneratorTest,EmitOtherPackagesAttributesInStyleable)199 TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
200     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
201                 .setPackageId(u"android", 0x01)
202                 .setPackageId(u"com.lib", 0x02)
203                 .addValue(u"@android:attr/bar", ResourceId(0x01010000),
204                           test::AttributeBuilder(false).build())
205                 .addValue(u"@com.lib:attr/bar", ResourceId(0x02010000),
206                            test::AttributeBuilder(false).build())
207                 .addValue(u"@android:styleable/foo", ResourceId(0x01030000),
208                           test::StyleableBuilder()
209                                   .addItem(u"@android:attr/bar", ResourceId(0x01010000))
210                                   .addItem(u"@com.lib:attr/bar", ResourceId(0x02010000))
211                                   .build())
212                 .build();
213 
214     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
215             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
216             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
217             .build();
218     JavaClassGenerator generator(context.get(), table.get(), {});
219 
220     std::stringstream out;
221     EXPECT_TRUE(generator.generate(u"android", &out));
222 
223     std::string output = out.str();
224     EXPECT_NE(std::string::npos, output.find("int foo_bar="));
225     EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
226 }
227 
TEST(JavaClassGeneratorTest,CommentsForSimpleResourcesArePresent)228 TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
229     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
230             .setPackageId(u"android", 0x01)
231             .addSimple(u"@android:id/foo", ResourceId(0x01010000))
232             .build();
233     test::getValue<Id>(table.get(), u"@android:id/foo")
234             ->setComment(std::u16string(u"This is a comment\n@deprecated"));
235 
236     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
237             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
238             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
239             .build();
240     JavaClassGenerator generator(context.get(), table.get(), {});
241     std::stringstream out;
242     ASSERT_TRUE(generator.generate(u"android", &out));
243     std::string actual = out.str();
244 
245     const char* expectedText =
246 R"EOF(/**
247      * This is a comment
248      * @deprecated
249      */
250     @Deprecated
251     public static final int foo=0x01010000;)EOF";
252 
253     EXPECT_NE(std::string::npos, actual.find(expectedText));
254 }
255 
TEST(JavaClassGeneratorTest,CommentsForEnumAndFlagAttributesArePresent)256 TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {
257 
258 }
259 
TEST(JavaClassGeneratorTest,CommentsForStyleablesAndNestedAttributesArePresent)260 TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
261     Attribute attr(false);
262     attr.setComment(StringPiece16(u"This is an attribute"));
263 
264     Styleable styleable;
265     styleable.entries.push_back(Reference(test::parseNameOrDie(u"@android:attr/one")));
266     styleable.setComment(StringPiece16(u"This is a styleable"));
267 
268     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
269             .setPackageId(u"android", 0x01)
270             .addValue(u"@android:attr/one", util::make_unique<Attribute>(attr))
271             .addValue(u"@android:styleable/Container",
272                       std::unique_ptr<Styleable>(styleable.clone(nullptr)))
273             .build();
274 
275     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
276             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
277             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
278             .build();
279     JavaClassGeneratorOptions options;
280     options.useFinal = false;
281     JavaClassGenerator generator(context.get(), table.get(), options);
282     std::stringstream out;
283     ASSERT_TRUE(generator.generate(u"android", &out));
284     std::string actual = out.str();
285 
286     EXPECT_NE(std::string::npos, actual.find("@attr name android:one"));
287     EXPECT_NE(std::string::npos, actual.find("@attr description"));
288     EXPECT_NE(std::string::npos, actual.find(util::utf16ToUtf8(attr.getComment())));
289     EXPECT_NE(std::string::npos, actual.find(util::utf16ToUtf8(styleable.getComment())));
290 }
291 
TEST(JavaClassGeneratorTest,CommentsForRemovedAttributesAreNotPresentInClass)292 TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
293     Attribute attr(false);
294     attr.setComment(StringPiece16(u"@removed"));
295 
296 
297     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
298             .setPackageId(u"android", 0x01)
299             .addValue(u"@android:attr/one", util::make_unique<Attribute>(attr))
300             .build();
301 
302     std::unique_ptr<IAaptContext> context = test::ContextBuilder()
303             .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
304             .setNameManglerPolicy(NameManglerPolicy{ u"android" })
305             .build();
306     JavaClassGeneratorOptions options;
307     options.useFinal = false;
308     JavaClassGenerator generator(context.get(), table.get(), options);
309     std::stringstream out;
310     ASSERT_TRUE(generator.generate(u"android", &out));
311     std::string actual = out.str();
312 
313     std::cout << actual << std::endl;
314 
315     EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
316     EXPECT_EQ(std::string::npos, actual.find("@attr description"));
317 
318     // We should find @removed only in the attribute javadoc and not anywhere else (i.e. the class
319     // javadoc).
320     const size_t pos = actual.find("@removed");
321     EXPECT_NE(std::string::npos, pos);
322     EXPECT_EQ(std::string::npos, actual.find("@removed", pos + 1));
323 }
324 
325 } // namespace aapt
326