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