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 
TEST(JavaClassGeneratorTest,CommentsForStyleablesAndNestedAttributesArePresent)329 TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
330   Attribute attr;
331   attr.SetComment(StringPiece("This is an attribute"));
332 
333   Styleable styleable;
334   styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
335   styleable.SetComment(StringPiece("This is a styleable"));
336 
337   CloningValueTransformer cloner(nullptr);
338   std::unique_ptr<ResourceTable> table =
339       test::ResourceTableBuilder()
340           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
341           .AddValue("android:styleable/Container",
342                     std::unique_ptr<Styleable>(styleable.Transform(cloner)))
343           .Build();
344 
345   std::unique_ptr<IAaptContext> context =
346       test::ContextBuilder()
347           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
348           .SetNameManglerPolicy(NameManglerPolicy{"android"})
349           .Build();
350   JavaClassGeneratorOptions options;
351   options.use_final = false;
352   JavaClassGenerator generator(context.get(), table.get(), options);
353 
354   std::string output;
355   StringOutputStream out(&output);
356   ASSERT_TRUE(generator.Generate("android", &out));
357   out.Flush();
358 
359   EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
360   EXPECT_THAT(output, HasSubstr("@see #Container_one"));
361   EXPECT_THAT(output, HasSubstr("attr name android:one"));
362   EXPECT_THAT(output, HasSubstr("attr description"));
363   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
364   EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
365 }
366 
TEST(JavaClassGeneratorTest,CommentsForStyleableHiddenAttributesAreNotPresent)367 TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
368   Attribute attr;
369   attr.SetComment(StringPiece("This is an attribute @hide"));
370 
371   Styleable styleable;
372   styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
373   styleable.SetComment(StringPiece("This is a styleable"));
374 
375   CloningValueTransformer cloner(nullptr);
376   std::unique_ptr<ResourceTable> table =
377       test::ResourceTableBuilder()
378           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
379           .AddValue("android:styleable/Container",
380                     std::unique_ptr<Styleable>(styleable.Transform(cloner)))
381           .Build();
382 
383   std::unique_ptr<IAaptContext> context =
384       test::ContextBuilder()
385           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
386           .SetNameManglerPolicy(NameManglerPolicy{"android"})
387           .Build();
388   JavaClassGeneratorOptions options;
389   options.use_final = false;
390   JavaClassGenerator generator(context.get(), table.get(), options);
391 
392   std::string output;
393   StringOutputStream out(&output);
394   ASSERT_TRUE(generator.Generate("android", &out));
395   out.Flush();
396 
397   EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
398   EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
399   EXPECT_THAT(output, HasSubstr("attr name android:one"));
400   EXPECT_THAT(output, HasSubstr("attr description"));
401   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
402   EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
403 }
404 
TEST(JavaClassGeneratorTest,StyleableAndIndicesAreColocated)405 TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
406   std::unique_ptr<ResourceTable> table =
407       test::ResourceTableBuilder()
408           .AddValue("android:attr/layout_gravity", util::make_unique<Attribute>())
409           .AddValue("android:attr/background", util::make_unique<Attribute>())
410           .AddValue("android:styleable/ActionBar",
411                     test::StyleableBuilder()
412                         .AddItem("android:attr/background", ResourceId(0x01010000))
413                         .Build())
414           .AddValue("android:styleable/ActionBar.LayoutParams",
415                     test::StyleableBuilder()
416                         .AddItem("android:attr/layout_gravity", ResourceId(0x01010001))
417                         .Build())
418           .Build();
419 
420   std::unique_ptr<IAaptContext> context =
421       test::ContextBuilder()
422           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
423           .SetNameManglerPolicy(NameManglerPolicy{"android"})
424           .Build();
425 
426   JavaClassGeneratorOptions options;
427   JavaClassGenerator generator(context.get(), table.get(), {});
428 
429   std::string output;
430   StringOutputStream out(&output);
431   ASSERT_TRUE(generator.Generate("android", &out));
432   out.Flush();
433 
434   std::string::size_type actionbar_pos = output.find("int[] ActionBar");
435   ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
436 
437   std::string::size_type actionbar_background_pos = output.find("int ActionBar_background");
438   ASSERT_THAT(actionbar_background_pos, Ne(std::string::npos));
439 
440   std::string::size_type actionbar_layout_params_pos = output.find("int[] ActionBar_LayoutParams");
441   ASSERT_THAT(actionbar_layout_params_pos, Ne(std::string::npos));
442 
443   std::string::size_type actionbar_layout_params_layout_gravity_pos =
444       output.find("int ActionBar_LayoutParams_layout_gravity");
445   ASSERT_THAT(actionbar_layout_params_layout_gravity_pos, Ne(std::string::npos));
446 
447   EXPECT_THAT(actionbar_pos, Lt(actionbar_background_pos));
448   EXPECT_THAT(actionbar_pos, Lt(actionbar_layout_params_pos));
449   EXPECT_THAT(actionbar_background_pos, Lt(actionbar_layout_params_pos));
450   EXPECT_THAT(actionbar_layout_params_pos, Lt(actionbar_layout_params_layout_gravity_pos));
451 }
452 
TEST(JavaClassGeneratorTest,CommentsForRemovedAttributesAreNotPresentInClass)453 TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
454   Attribute attr;
455   attr.SetComment(StringPiece("removed"));
456 
457   std::unique_ptr<ResourceTable> table =
458       test::ResourceTableBuilder()
459           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
460           .Build();
461 
462   std::unique_ptr<IAaptContext> context =
463       test::ContextBuilder()
464           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
465           .SetNameManglerPolicy(NameManglerPolicy{"android"})
466           .Build();
467   JavaClassGeneratorOptions options;
468   options.use_final = false;
469   JavaClassGenerator generator(context.get(), table.get(), options);
470 
471   std::string output;
472   StringOutputStream out(&output);
473   ASSERT_TRUE(generator.Generate("android", &out));
474   out.Flush();
475 
476   EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
477   EXPECT_THAT(output, Not(HasSubstr("@attr description")));
478 
479   // We should find @removed only in the attribute javadoc and not anywhere else
480   // (i.e. the class javadoc).
481   const std::string kRemoved("removed");
482   ASSERT_THAT(output, HasSubstr(kRemoved));
483   std::string after_first_match = output.substr(output.find(kRemoved) + kRemoved.size());
484   EXPECT_THAT(after_first_match, Not(HasSubstr(kRemoved)));
485 }
486 
TEST(JavaClassGeneratorTest,GenerateOnResourcesLoadedCallbackForSharedLibrary)487 TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
488   std::unique_ptr<ResourceTable> table =
489       test::ResourceTableBuilder()
490           .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>())
491           .AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
492           .AddValue(
493               "android:style/foo", ResourceId(0x00030000),
494               test::StyleBuilder()
495                   .AddItem("android:attr/foo", ResourceId(0x00010000), util::make_unique<Id>())
496                   .Build())
497           .Build();
498 
499   std::unique_ptr<IAaptContext> context =
500       test::ContextBuilder().SetPackageId(0x00).SetCompilationPackage("android").Build();
501 
502   JavaClassGeneratorOptions options;
503   options.use_final = false;
504   options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
505   JavaClassGenerator generator(context.get(), table.get(), options);
506 
507   std::string output;
508   StringOutputStream out(&output);
509   ASSERT_TRUE(generator.Generate("android", &out));
510   out.Flush();
511 
512   EXPECT_THAT(output, HasSubstr(
513           R"(  public static void onResourcesLoaded(int p) {
514     com.foo.R.onResourcesLoaded(p);
515     com.boo.R.onResourcesLoaded(p);
516     final int packageIdBits = p << 24;
517     attr.foo = (attr.foo & 0x00ffffff) | packageIdBits;
518     id.foo = (id.foo & 0x00ffffff) | packageIdBits;
519     style.foo = (style.foo & 0x00ffffff) | packageIdBits;
520   })"));
521 }
522 
TEST(JavaClassGeneratorTest,OnlyGenerateRText)523 TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
524   std::unique_ptr<ResourceTable> table =
525       test::ResourceTableBuilder()
526           .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>())
527           .AddValue("android:styleable/hey.dude", ResourceId(0x01020000),
528                     test::StyleableBuilder()
529                         .AddItem("android:attr/foo", ResourceId(0x01010000))
530                         .Build())
531           .Build();
532 
533   std::unique_ptr<IAaptContext> context =
534       test::ContextBuilder().SetPackageId(0x01).SetCompilationPackage("android").Build();
535   JavaClassGenerator generator(context.get(), table.get(), {});
536 
537   ASSERT_TRUE(generator.Generate("android", nullptr));
538 }
539 
TEST(JavaClassGeneratorTest,SortsDynamicAttributesAfterFrameworkAttributes)540 TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
541   std::unique_ptr<ResourceTable> table =
542       test::ResourceTableBuilder()
543           .AddValue("android:attr/framework_attr", ResourceId(0x01010000),
544                     test::AttributeBuilder().Build())
545           .AddValue("lib:attr/dynamic_attr", ResourceId(0x00010000),
546                     test::AttributeBuilder().Build())
547           .AddValue("lib:styleable/MyStyleable", ResourceId(0x00030000),
548                     test::StyleableBuilder()
549                         .AddItem("android:attr/framework_attr", ResourceId(0x01010000))
550                         .AddItem("lib:attr/dynamic_attr", ResourceId(0x00010000))
551                         .Build())
552           .Build();
553 
554   std::unique_ptr<IAaptContext> context =
555       test::ContextBuilder()
556           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
557           .SetNameManglerPolicy(NameManglerPolicy{"custom"})
558           .SetCompilationPackage("custom")
559           .Build();
560   JavaClassGenerator generator(context.get(), table.get(), {});
561 
562   std::string output;
563   StringOutputStream out(&output);
564   EXPECT_TRUE(generator.Generate("lib", &out));
565   out.Flush();
566 
567   EXPECT_THAT(output, HasSubstr("public static final int[] MyStyleable={"));
568   EXPECT_THAT(output, HasSubstr("0x01010000, lib.R.attr.dynamic_attr"));
569   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_framework_attr=0;"));
570   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
571 }
572 
TEST(JavaClassGeneratorTest,SkipMacros)573 TEST(JavaClassGeneratorTest, SkipMacros) {
574   std::unique_ptr<ResourceTable> table =
575       test::ResourceTableBuilder()
576           .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
577           .Build();
578 
579   std::unique_ptr<IAaptContext> context =
580       test::ContextBuilder()
581           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
582           .SetNameManglerPolicy(NameManglerPolicy{"android"})
583           .Build();
584   JavaClassGenerator generator(context.get(), table.get(), {});
585 
586   std::string output;
587   StringOutputStream out(&output);
588   EXPECT_TRUE(generator.Generate("android", &out));
589   out.Flush();
590 
591   EXPECT_THAT(output, Not(HasSubstr("bar")));
592 }
593 
594 }  // namespace aapt
595