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           .AddSimple("android:id/class", ResourceId(0x01020000))
38           .Build();
39 
40   std::unique_ptr<IAaptContext> context =
41       test::ContextBuilder()
42           .AddSymbolSource(
43               util::make_unique<ResourceTableSymbolSource>(table.get()))
44           .SetNameManglerPolicy(NameManglerPolicy{"android"})
45           .Build();
46   JavaClassGenerator generator(context.get(), table.get(), {});
47 
48   std::string result;
49   StringOutputStream out(&result);
50   EXPECT_FALSE(generator.Generate("android", &out));
51 }
52 
TEST(JavaClassGeneratorTest,TransformInvalidJavaIdentifierCharacter)53 TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
54   std::unique_ptr<ResourceTable> table =
55       test::ResourceTableBuilder()
56           .AddSimple("android:id/hey-man", ResourceId(0x01020000))
57           .AddValue("android:attr/cool.attr", ResourceId(0x01010000),
58                     test::AttributeBuilder().Build())
59           .AddValue("android:styleable/hey.dude", ResourceId(0x01030000),
60                     test::StyleableBuilder()
61                         .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
62                         .Build())
63           .Build();
64 
65   std::unique_ptr<IAaptContext> context =
66       test::ContextBuilder()
67           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
68           .SetNameManglerPolicy(NameManglerPolicy{"android"})
69           .Build();
70   JavaClassGenerator generator(context.get(), table.get(), {});
71 
72   std::string output;
73   StringOutputStream out(&output);
74   EXPECT_TRUE(generator.Generate("android", &out));
75   out.Flush();
76 
77   EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
78   EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
79   EXPECT_THAT(output, HasSubstr("public static final int hey_dude_cool_attr=0;"));
80 }
81 
TEST(JavaClassGeneratorTest,CorrectPackageNameIsUsed)82 TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
83   std::unique_ptr<ResourceTable> table =
84       test::ResourceTableBuilder()
85           .AddSimple("android:id/one", ResourceId(0x01020000))
86           .AddSimple("android:id/com.foo$two", ResourceId(0x01020001))
87           .Build();
88 
89   std::unique_ptr<IAaptContext> context =
90       test::ContextBuilder()
91           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
92           .SetNameManglerPolicy(NameManglerPolicy{"android"})
93           .Build();
94   JavaClassGenerator generator(context.get(), table.get(), {});
95 
96   std::string output;
97   StringOutputStream out(&output);
98   ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
99   out.Flush();
100 
101   EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
102   EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
103   EXPECT_THAT(output, Not(HasSubstr("two")));
104   EXPECT_THAT(output, Not(HasSubstr("com_foo$two")));
105 }
106 
TEST(JavaClassGeneratorTest,StyleableAttributesWithDifferentPackageName)107 TEST(JavaClassGeneratorTest, StyleableAttributesWithDifferentPackageName) {
108   std::unique_ptr<ResourceTable> table =
109       test::ResourceTableBuilder()
110           .AddValue("app:attr/foo", ResourceId(0x7f010000),
111                     test::AttributeBuilder().Build())
112           .AddValue("app:attr/bar", ResourceId(0x7f010001),
113                     test::AttributeBuilder().Build())
114           .AddValue("android:attr/baz", ResourceId(0x01010000),
115                     test::AttributeBuilder().Build())
116           .AddValue("app:styleable/MyStyleable", ResourceId(0x7f030000),
117                     test::StyleableBuilder()
118                         .AddItem("app:attr/foo", ResourceId(0x7f010000))
119                         .AddItem("attr/bar", ResourceId(0x7f010001))
120                         .AddItem("android:attr/baz", ResourceId(0x01010000))
121                         .Build())
122           .Build();
123 
124   std::unique_ptr<IAaptContext> context =
125       test::ContextBuilder()
126           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
127           .SetNameManglerPolicy(NameManglerPolicy{"custom"})
128           .SetCompilationPackage("custom")
129           .Build();
130   JavaClassGenerator generator(context.get(), table.get(), {});
131 
132   std::string output;
133   StringOutputStream out(&output);
134   EXPECT_TRUE(generator.Generate("app", &out));
135   out.Flush();
136 
137   EXPECT_THAT(output, Not(HasSubstr("public static final int baz=0x01010000;")));
138   EXPECT_THAT(output, HasSubstr("public static final int foo=0x7f010000;"));
139   EXPECT_THAT(output, HasSubstr("public static final int bar=0x7f010001;"));
140 
141   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_baz=0;"));
142   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_foo=1;"));
143   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_bar=2;"));
144 
145   EXPECT_THAT(output, HasSubstr("@link #MyStyleable_android_baz android:baz"));
146   EXPECT_THAT(output, HasSubstr("@link #MyStyleable_foo app:foo"));
147   EXPECT_THAT(output, HasSubstr("@link #MyStyleable_bar app:bar"));
148 
149   EXPECT_THAT(output, HasSubstr("@link android.R.attr#baz"));
150   EXPECT_THAT(output, HasSubstr("@link app.R.attr#foo"));
151   EXPECT_THAT(output, HasSubstr("@link app.R.attr#bar"));
152 }
153 
TEST(JavaClassGeneratorTest,AttrPrivateIsWrittenAsAttr)154 TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
155   std::unique_ptr<ResourceTable> table =
156       test::ResourceTableBuilder()
157           .AddSimple("android:attr/two", ResourceId(0x01010001))
158           .AddSimple("android:^attr-private/one", ResourceId(0x01010000))
159           .Build();
160 
161   std::unique_ptr<IAaptContext> context =
162       test::ContextBuilder()
163           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
164           .SetNameManglerPolicy(NameManglerPolicy{"android"})
165           .Build();
166   JavaClassGenerator generator(context.get(), table.get(), {});
167 
168   std::string output;
169   StringOutputStream out(&output);
170   ASSERT_TRUE(generator.Generate("android", &out));
171   out.Flush();
172 
173   EXPECT_THAT(output, HasSubstr("public static final class attr"));
174   EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
175 }
176 
TEST(JavaClassGeneratorTest,OnlyWritePublicResources)177 TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
178   StdErrDiagnostics diag;
179   std::unique_ptr<ResourceTable> table =
180       test::ResourceTableBuilder()
181           .AddSimple("android:id/one", ResourceId(0x01020000))
182           .AddSimple("android:id/two", ResourceId(0x01020001))
183           .AddSimple("android:id/three", ResourceId(0x01020002))
184           .SetSymbolState("android:id/one", ResourceId(0x01020000), Visibility::Level::kPublic)
185           .SetSymbolState("android:id/two", ResourceId(0x01020001), Visibility::Level::kPrivate)
186           .Build();
187 
188   std::unique_ptr<IAaptContext> context =
189       test::ContextBuilder()
190           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
191           .SetNameManglerPolicy(NameManglerPolicy{"android"})
192           .Build();
193 
194   JavaClassGeneratorOptions options;
195   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
196   {
197     JavaClassGenerator generator(context.get(), table.get(), options);
198     std::string output;
199     StringOutputStream out(&output);
200     ASSERT_TRUE(generator.Generate("android", &out));
201     out.Flush();
202 
203     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
204     EXPECT_THAT(output, Not(HasSubstr("two")));
205     EXPECT_THAT(output, Not(HasSubstr("three")));
206   }
207 
208   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
209   {
210     JavaClassGenerator generator(context.get(), table.get(), options);
211     std::string output;
212     StringOutputStream out(&output);
213     ASSERT_TRUE(generator.Generate("android", &out));
214     out.Flush();
215 
216     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
217     EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
218     EXPECT_THAT(output, Not(HasSubstr("three")));
219   }
220 
221   options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
222   {
223     JavaClassGenerator generator(context.get(), table.get(), options);
224     std::string output;
225     StringOutputStream out(&output);
226     ASSERT_TRUE(generator.Generate("android", &out));
227     out.Flush();
228 
229     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
230     EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
231     EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
232   }
233 }
234 
235 /*
236  * TODO(adamlesinski): Re-enable this once we get merging working again.
237  * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
238     ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
239                             ResourceId{ 0x01, 0x02, 0x0000 }));
240     ResourceTable table;
241     table.setPackage(u"com.lib");
242     ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test"
243 }, {},
244                                   Source{ "lib.xml", 33 },
245 util::make_unique<Id>()));
246     ASSERT_TRUE(mTable->merge(std::move(table)));
247 
248     Linker linker(mTable,
249                   std::make_shared<MockResolver>(mTable, std::map<ResourceName,
250 ResourceId>()),
251                   {});
252     ASSERT_TRUE(linker.linkAndValidate());
253 
254     JavaClassGenerator generator(mTable, {});
255 
256     std::stringstream out;
257     EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
258     std::string output = out.str();
259     EXPECT_NE(std::string::npos, output.find("int foo ="));
260     EXPECT_EQ(std::string::npos, output.find("int test ="));
261 
262     out.str("");
263     EXPECT_TRUE(generator.generate(u"com.lib", out));
264     output = out.str();
265     EXPECT_NE(std::string::npos, output.find("int test ="));
266     EXPECT_EQ(std::string::npos, output.find("int foo ="));
267 }*/
268 
TEST(JavaClassGeneratorTest,EmitOtherPackagesAttributesInStyleable)269 TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
270   std::unique_ptr<ResourceTable> table =
271       test::ResourceTableBuilder()
272           .AddValue("android:attr/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
273           .AddValue("com.lib:attr/bar", ResourceId(0x02010000), test::AttributeBuilder().Build())
274           .AddValue("android:styleable/foo", ResourceId(0x01030000),
275                     test::StyleableBuilder()
276                         .AddItem("android:attr/bar", ResourceId(0x01010000))
277                         .AddItem("com.lib:attr/bar", ResourceId(0x02010000))
278                         .Build())
279           .Build();
280 
281   std::unique_ptr<IAaptContext> context =
282       test::ContextBuilder()
283           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
284           .SetNameManglerPolicy(NameManglerPolicy{"android"})
285           .Build();
286   JavaClassGenerator generator(context.get(), table.get(), {});
287 
288   std::string output;
289   StringOutputStream out(&output);
290   EXPECT_TRUE(generator.Generate("android", &out));
291   out.Flush();
292 
293   EXPECT_THAT(output, HasSubstr("int foo_bar="));
294   EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
295 }
296 
TEST(JavaClassGeneratorTest,CommentsForSimpleResourcesArePresent)297 TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
298   std::unique_ptr<ResourceTable> table =
299       test::ResourceTableBuilder()
300           .AddSimple("android:id/foo", ResourceId(0x01010000))
301           .Build();
302   test::GetValue<Id>(table.get(), "android:id/foo")
303       ->SetComment(std::string("This is a comment\n@deprecated"));
304 
305   std::unique_ptr<IAaptContext> context =
306       test::ContextBuilder()
307           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
308           .SetNameManglerPolicy(NameManglerPolicy{"android"})
309           .Build();
310   JavaClassGenerator generator(context.get(), table.get(), {});
311 
312   std::string output;
313   StringOutputStream out(&output);
314   ASSERT_TRUE(generator.Generate("android", &out));
315   out.Flush();
316 
317   const char* expected_text =
318       R"EOF(/**
319      * This is a comment
320      * @deprecated
321      */
322     @Deprecated
323     public static final int foo=0x01010000;)EOF";
324   EXPECT_THAT(output, HasSubstr(expected_text));
325 }
326 
TEST(JavaClassGeneratorTest,CommentsForEnumAndFlagAttributesArePresent)327 TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {
328   std::unique_ptr<Attribute> flagAttr =
329       test::AttributeBuilder()
330           .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
331           .SetComment("Flag attribute")
332           .AddItemWithComment("flagOne", 0x01, "Flag comment 1")
333           .AddItemWithComment("flagTwo", 0x02, "@deprecated Flag comment 2")
334           .Build();
335   std::unique_ptr<Attribute> enumAttr =
336       test::AttributeBuilder()
337           .SetTypeMask(android::ResTable_map::TYPE_ENUM)
338           .SetComment("Enum attribute")
339           .AddItemWithComment("enumOne", 0x01, "@TestApi Enum comment 1")
340           .AddItemWithComment("enumTwo", 0x02, "Enum comment 2")
341           .Build();
342 
343   std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
344                                              .AddValue("android:attr/one", std::move(flagAttr))
345                                              .AddValue("android:attr/two", std::move(enumAttr))
346                                              .Build();
347 
348   std::unique_ptr<IAaptContext> context =
349       test::ContextBuilder()
350           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
351           .SetNameManglerPolicy(NameManglerPolicy{"android"})
352           .Build();
353   JavaClassGeneratorOptions options;
354   options.use_final = false;
355   JavaClassGenerator generator(context.get(), table.get(), options);
356 
357   std::string output;
358   StringOutputStream out(&output);
359   ASSERT_TRUE(generator.Generate("android", &out));
360   out.Flush();
361 
362   // Special annotations from the enum/flag values should NOT generate
363   // annotations for the attribute value.
364   EXPECT_THAT(output, Not(HasSubstr("@Deprecated")));
365   EXPECT_THAT(output, Not(HasSubstr("@android.annotation.TestApi")));
366 
367   EXPECT_THAT(output, HasSubstr("Flag attribute"));
368   EXPECT_THAT(output, HasSubstr("flagOne"));
369   EXPECT_THAT(output, HasSubstr("Flag comment 1"));
370   EXPECT_THAT(output, HasSubstr("flagTwo"));
371   EXPECT_THAT(output, HasSubstr("@deprecated Flag comment 2"));
372 
373   EXPECT_THAT(output, HasSubstr("Enum attribute"));
374   EXPECT_THAT(output, HasSubstr("enumOne"));
375   EXPECT_THAT(output, HasSubstr("@TestApi Enum comment 1"));
376   EXPECT_THAT(output, HasSubstr("enumTwo"));
377   EXPECT_THAT(output, HasSubstr("Enum comment 2"));
378 }
379 
TEST(JavaClassGeneratorTest,CommentsForStyleablesAndNestedAttributesArePresent)380 TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
381   Attribute attr;
382   attr.SetComment(StringPiece("This is an attribute"));
383 
384   Styleable styleable;
385   styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
386   styleable.SetComment(StringPiece("This is a styleable"));
387 
388   CloningValueTransformer cloner(nullptr);
389   std::unique_ptr<ResourceTable> table =
390       test::ResourceTableBuilder()
391           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
392           .AddValue("android:styleable/Container",
393                     std::unique_ptr<Styleable>(styleable.Transform(cloner)))
394           .Build();
395 
396   std::unique_ptr<IAaptContext> context =
397       test::ContextBuilder()
398           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
399           .SetNameManglerPolicy(NameManglerPolicy{"android"})
400           .Build();
401   JavaClassGeneratorOptions options;
402   options.use_final = false;
403   JavaClassGenerator generator(context.get(), table.get(), options);
404 
405   std::string output;
406   StringOutputStream out(&output);
407   ASSERT_TRUE(generator.Generate("android", &out));
408   out.Flush();
409 
410   EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
411   EXPECT_THAT(output, HasSubstr("@see #Container_one"));
412   EXPECT_THAT(output, HasSubstr("attr name android:one"));
413   EXPECT_THAT(output, HasSubstr("attr description"));
414   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
415   EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
416 }
417 
TEST(JavaClassGeneratorTest,CommentsForStyleableHiddenAttributesAreNotPresent)418 TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
419   Attribute attr;
420   attr.SetComment(StringPiece("This is an attribute @hide"));
421 
422   Styleable styleable;
423   styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
424   styleable.SetComment(StringPiece("This is a styleable"));
425 
426   CloningValueTransformer cloner(nullptr);
427   std::unique_ptr<ResourceTable> table =
428       test::ResourceTableBuilder()
429           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
430           .AddValue("android:styleable/Container",
431                     std::unique_ptr<Styleable>(styleable.Transform(cloner)))
432           .Build();
433 
434   std::unique_ptr<IAaptContext> context =
435       test::ContextBuilder()
436           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
437           .SetNameManglerPolicy(NameManglerPolicy{"android"})
438           .Build();
439   JavaClassGeneratorOptions options;
440   options.use_final = false;
441   JavaClassGenerator generator(context.get(), table.get(), options);
442 
443   std::string output;
444   StringOutputStream out(&output);
445   ASSERT_TRUE(generator.Generate("android", &out));
446   out.Flush();
447 
448   EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
449   EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
450   EXPECT_THAT(output, HasSubstr("attr name android:one"));
451   EXPECT_THAT(output, HasSubstr("attr description"));
452   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
453   EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
454 }
455 
TEST(JavaClassGeneratorTest,StyleableAndIndicesAreColocated)456 TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
457   std::unique_ptr<ResourceTable> table =
458       test::ResourceTableBuilder()
459           .AddValue("android:attr/layout_gravity", util::make_unique<Attribute>())
460           .AddValue("android:attr/background", util::make_unique<Attribute>())
461           .AddValue("android:styleable/ActionBar",
462                     test::StyleableBuilder()
463                         .AddItem("android:attr/background", ResourceId(0x01010000))
464                         .Build())
465           .AddValue("android:styleable/ActionBar.LayoutParams",
466                     test::StyleableBuilder()
467                         .AddItem("android:attr/layout_gravity", ResourceId(0x01010001))
468                         .Build())
469           .Build();
470 
471   std::unique_ptr<IAaptContext> context =
472       test::ContextBuilder()
473           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
474           .SetNameManglerPolicy(NameManglerPolicy{"android"})
475           .Build();
476 
477   JavaClassGeneratorOptions options;
478   JavaClassGenerator generator(context.get(), table.get(), {});
479 
480   std::string output;
481   StringOutputStream out(&output);
482   ASSERT_TRUE(generator.Generate("android", &out));
483   out.Flush();
484 
485   std::string::size_type actionbar_pos = output.find("int[] ActionBar");
486   ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
487 
488   std::string::size_type actionbar_background_pos = output.find("int ActionBar_background");
489   ASSERT_THAT(actionbar_background_pos, Ne(std::string::npos));
490 
491   std::string::size_type actionbar_layout_params_pos = output.find("int[] ActionBar_LayoutParams");
492   ASSERT_THAT(actionbar_layout_params_pos, Ne(std::string::npos));
493 
494   std::string::size_type actionbar_layout_params_layout_gravity_pos =
495       output.find("int ActionBar_LayoutParams_layout_gravity");
496   ASSERT_THAT(actionbar_layout_params_layout_gravity_pos, Ne(std::string::npos));
497 
498   EXPECT_THAT(actionbar_pos, Lt(actionbar_background_pos));
499   EXPECT_THAT(actionbar_pos, Lt(actionbar_layout_params_pos));
500   EXPECT_THAT(actionbar_background_pos, Lt(actionbar_layout_params_pos));
501   EXPECT_THAT(actionbar_layout_params_pos, Lt(actionbar_layout_params_layout_gravity_pos));
502 }
503 
TEST(JavaClassGeneratorTest,CommentsForRemovedAttributesAreNotPresentInClass)504 TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
505   Attribute attr;
506   attr.SetComment(StringPiece("removed"));
507 
508   std::unique_ptr<ResourceTable> table =
509       test::ResourceTableBuilder()
510           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
511           .Build();
512 
513   std::unique_ptr<IAaptContext> context =
514       test::ContextBuilder()
515           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
516           .SetNameManglerPolicy(NameManglerPolicy{"android"})
517           .Build();
518   JavaClassGeneratorOptions options;
519   options.use_final = false;
520   JavaClassGenerator generator(context.get(), table.get(), options);
521 
522   std::string output;
523   StringOutputStream out(&output);
524   ASSERT_TRUE(generator.Generate("android", &out));
525   out.Flush();
526 
527   EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
528   EXPECT_THAT(output, Not(HasSubstr("@attr description")));
529 
530   // We should find @removed only in the attribute javadoc and not anywhere else
531   // (i.e. the class javadoc).
532   const std::string kRemoved("removed");
533   ASSERT_THAT(output, HasSubstr(kRemoved));
534   std::string after_first_match = output.substr(output.find(kRemoved) + kRemoved.size());
535   EXPECT_THAT(after_first_match, Not(HasSubstr(kRemoved)));
536 }
537 
TEST(JavaClassGeneratorTest,GenerateOnResourcesLoadedCallbackForSharedLibrary)538 TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
539   std::unique_ptr<ResourceTable> table =
540       test::ResourceTableBuilder()
541           .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>())
542           .AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
543           .AddValue(
544               "android:style/foo", ResourceId(0x00030000),
545               test::StyleBuilder()
546                   .AddItem("android:attr/foo", ResourceId(0x00010000), util::make_unique<Id>())
547                   .Build())
548           .Build();
549 
550   std::unique_ptr<IAaptContext> context =
551       test::ContextBuilder().SetPackageId(0x00).SetCompilationPackage("android").Build();
552 
553   JavaClassGeneratorOptions options;
554   options.use_final = false;
555   options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
556   JavaClassGenerator generator(context.get(), table.get(), options);
557 
558   std::string output;
559   StringOutputStream out(&output);
560   ASSERT_TRUE(generator.Generate("android", &out));
561   out.Flush();
562 
563   EXPECT_THAT(output, HasSubstr(
564           R"(  public static void onResourcesLoaded(int p) {
565     com.foo.R.onResourcesLoaded(p);
566     com.boo.R.onResourcesLoaded(p);
567     final int packageIdBits = p << 24;
568     attr.foo = (attr.foo & 0x00ffffff) | packageIdBits;
569     id.foo = (id.foo & 0x00ffffff) | packageIdBits;
570     style.foo = (style.foo & 0x00ffffff) | packageIdBits;
571   })"));
572 }
573 
TEST(JavaClassGeneratorTest,OnlyGenerateRText)574 TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
575   std::unique_ptr<ResourceTable> table =
576       test::ResourceTableBuilder()
577           .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>())
578           .AddValue("android:styleable/hey.dude", ResourceId(0x01020000),
579                     test::StyleableBuilder()
580                         .AddItem("android:attr/foo", ResourceId(0x01010000))
581                         .Build())
582           .Build();
583 
584   std::unique_ptr<IAaptContext> context =
585       test::ContextBuilder().SetPackageId(0x01).SetCompilationPackage("android").Build();
586   JavaClassGenerator generator(context.get(), table.get(), {});
587 
588   ASSERT_TRUE(generator.Generate("android", nullptr));
589 }
590 
TEST(JavaClassGeneratorTest,SortsDynamicAttributesAfterFrameworkAttributes)591 TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
592   std::unique_ptr<ResourceTable> table =
593       test::ResourceTableBuilder()
594           .AddValue("android:attr/framework_attr", ResourceId(0x01010000),
595                     test::AttributeBuilder().Build())
596           .AddValue("lib:attr/dynamic_attr", ResourceId(0x00010000),
597                     test::AttributeBuilder().Build())
598           .AddValue("lib:styleable/MyStyleable", ResourceId(0x00030000),
599                     test::StyleableBuilder()
600                         .AddItem("android:attr/framework_attr", ResourceId(0x01010000))
601                         .AddItem("lib:attr/dynamic_attr", ResourceId(0x00010000))
602                         .Build())
603           .Build();
604 
605   std::unique_ptr<IAaptContext> context =
606       test::ContextBuilder()
607           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
608           .SetNameManglerPolicy(NameManglerPolicy{"custom"})
609           .SetCompilationPackage("custom")
610           .Build();
611   JavaClassGenerator generator(context.get(), table.get(), {});
612 
613   std::string output;
614   StringOutputStream out(&output);
615   EXPECT_TRUE(generator.Generate("lib", &out));
616   out.Flush();
617 
618   EXPECT_THAT(output, HasSubstr("public static final int[] MyStyleable={"));
619   EXPECT_THAT(output, HasSubstr("0x01010000, lib.R.attr.dynamic_attr"));
620   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_framework_attr=0;"));
621   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
622 }
623 
TEST(JavaClassGeneratorTest,SkipMacros)624 TEST(JavaClassGeneratorTest, SkipMacros) {
625   std::unique_ptr<ResourceTable> table =
626       test::ResourceTableBuilder()
627           .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
628           .Build();
629 
630   std::unique_ptr<IAaptContext> context =
631       test::ContextBuilder()
632           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
633           .SetNameManglerPolicy(NameManglerPolicy{"android"})
634           .Build();
635   JavaClassGenerator generator(context.get(), table.get(), {});
636 
637   std::string output;
638   StringOutputStream out(&output);
639   EXPECT_TRUE(generator.Generate("android", &out));
640   out.Flush();
641 
642   EXPECT_THAT(output, Not(HasSubstr("bar")));
643 }
644 
645 }  // namespace aapt
646