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