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