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,StyleableAttributesWithDifferentPackageName)110 TEST(JavaClassGeneratorTest, StyleableAttributesWithDifferentPackageName) {
111   std::unique_ptr<ResourceTable> table =
112       test::ResourceTableBuilder()
113           .SetPackageId("android", 0x01)
114           .SetPackageId("app", 0x7f)
115           .AddValue("app:attr/foo", ResourceId(0x7f010000),
116                     test::AttributeBuilder().Build())
117           .AddValue("app:attr/bar", ResourceId(0x7f010001),
118                     test::AttributeBuilder().Build())
119           .AddValue("android:attr/baz", ResourceId(0x01010000),
120                     test::AttributeBuilder().Build())
121           .AddValue("app:styleable/MyStyleable", ResourceId(0x7f030000),
122                     test::StyleableBuilder()
123                         .AddItem("app:attr/foo", ResourceId(0x7f010000))
124                         .AddItem("attr/bar", ResourceId(0x7f010001))
125                         .AddItem("android:attr/baz", ResourceId(0x01010000))
126                         .Build())
127           .Build();
128 
129   std::unique_ptr<IAaptContext> context =
130       test::ContextBuilder()
131           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
132           .SetNameManglerPolicy(NameManglerPolicy{"custom"})
133           .SetCompilationPackage("custom")
134           .Build();
135   JavaClassGenerator generator(context.get(), table.get(), {});
136 
137   std::string output;
138   StringOutputStream out(&output);
139   EXPECT_TRUE(generator.Generate("app", &out));
140   out.Flush();
141 
142   EXPECT_THAT(output, Not(HasSubstr("public static final int baz=0x01010000;")));
143   EXPECT_THAT(output, HasSubstr("public static final int foo=0x7f010000;"));
144   EXPECT_THAT(output, HasSubstr("public static final int bar=0x7f010001;"));
145 
146   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_baz=0;"));
147   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_foo=1;"));
148   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_bar=2;"));
149 
150   EXPECT_THAT(output, HasSubstr("@link #MyStyleable_android_baz android:baz"));
151   EXPECT_THAT(output, HasSubstr("@link #MyStyleable_foo app:foo"));
152   EXPECT_THAT(output, HasSubstr("@link #MyStyleable_bar app:bar"));
153 
154   EXPECT_THAT(output, HasSubstr("@link android.R.attr#baz"));
155   EXPECT_THAT(output, HasSubstr("@link app.R.attr#foo"));
156   EXPECT_THAT(output, HasSubstr("@link app.R.attr#bar"));
157 }
158 
TEST(JavaClassGeneratorTest,AttrPrivateIsWrittenAsAttr)159 TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
160   std::unique_ptr<ResourceTable> table =
161       test::ResourceTableBuilder()
162           .SetPackageId("android", 0x01)
163           .AddSimple("android:attr/two", ResourceId(0x01010001))
164           .AddSimple("android:^attr-private/one", ResourceId(0x01010000))
165           .Build();
166 
167   std::unique_ptr<IAaptContext> context =
168       test::ContextBuilder()
169           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
170           .SetNameManglerPolicy(NameManglerPolicy{"android"})
171           .Build();
172   JavaClassGenerator generator(context.get(), table.get(), {});
173 
174   std::string output;
175   StringOutputStream out(&output);
176   ASSERT_TRUE(generator.Generate("android", &out));
177   out.Flush();
178 
179   EXPECT_THAT(output, HasSubstr("public static final class attr"));
180   EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
181 }
182 
TEST(JavaClassGeneratorTest,OnlyWritePublicResources)183 TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
184   StdErrDiagnostics diag;
185   std::unique_ptr<ResourceTable> table =
186       test::ResourceTableBuilder()
187           .SetPackageId("android", 0x01)
188           .AddSimple("android:id/one", ResourceId(0x01020000))
189           .AddSimple("android:id/two", ResourceId(0x01020001))
190           .AddSimple("android:id/three", ResourceId(0x01020002))
191           .SetSymbolState("android:id/one", ResourceId(0x01020000), Visibility::Level::kPublic)
192           .SetSymbolState("android:id/two", ResourceId(0x01020001), Visibility::Level::kPrivate)
193           .Build();
194 
195   std::unique_ptr<IAaptContext> context =
196       test::ContextBuilder()
197           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
198           .SetNameManglerPolicy(NameManglerPolicy{"android"})
199           .Build();
200 
201   JavaClassGeneratorOptions options;
202   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
203   {
204     JavaClassGenerator generator(context.get(), table.get(), options);
205     std::string output;
206     StringOutputStream out(&output);
207     ASSERT_TRUE(generator.Generate("android", &out));
208     out.Flush();
209 
210     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
211     EXPECT_THAT(output, Not(HasSubstr("two")));
212     EXPECT_THAT(output, Not(HasSubstr("three")));
213   }
214 
215   options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
216   {
217     JavaClassGenerator generator(context.get(), table.get(), options);
218     std::string output;
219     StringOutputStream out(&output);
220     ASSERT_TRUE(generator.Generate("android", &out));
221     out.Flush();
222 
223     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
224     EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
225     EXPECT_THAT(output, Not(HasSubstr("three")));
226   }
227 
228   options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
229   {
230     JavaClassGenerator generator(context.get(), table.get(), options);
231     std::string output;
232     StringOutputStream out(&output);
233     ASSERT_TRUE(generator.Generate("android", &out));
234     out.Flush();
235 
236     EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
237     EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
238     EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
239   }
240 }
241 
242 /*
243  * TODO(adamlesinski): Re-enable this once we get merging working again.
244  * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
245     ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
246                             ResourceId{ 0x01, 0x02, 0x0000 }));
247     ResourceTable table;
248     table.setPackage(u"com.lib");
249     ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test"
250 }, {},
251                                   Source{ "lib.xml", 33 },
252 util::make_unique<Id>()));
253     ASSERT_TRUE(mTable->merge(std::move(table)));
254 
255     Linker linker(mTable,
256                   std::make_shared<MockResolver>(mTable, std::map<ResourceName,
257 ResourceId>()),
258                   {});
259     ASSERT_TRUE(linker.linkAndValidate());
260 
261     JavaClassGenerator generator(mTable, {});
262 
263     std::stringstream out;
264     EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
265     std::string output = out.str();
266     EXPECT_NE(std::string::npos, output.find("int foo ="));
267     EXPECT_EQ(std::string::npos, output.find("int test ="));
268 
269     out.str("");
270     EXPECT_TRUE(generator.generate(u"com.lib", out));
271     output = out.str();
272     EXPECT_NE(std::string::npos, output.find("int test ="));
273     EXPECT_EQ(std::string::npos, output.find("int foo ="));
274 }*/
275 
TEST(JavaClassGeneratorTest,EmitOtherPackagesAttributesInStyleable)276 TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
277   std::unique_ptr<ResourceTable> table =
278       test::ResourceTableBuilder()
279           .SetPackageId("android", 0x01)
280           .SetPackageId("com.lib", 0x02)
281           .AddValue("android:attr/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
282           .AddValue("com.lib:attr/bar", ResourceId(0x02010000), test::AttributeBuilder().Build())
283           .AddValue("android:styleable/foo", ResourceId(0x01030000),
284                     test::StyleableBuilder()
285                         .AddItem("android:attr/bar", ResourceId(0x01010000))
286                         .AddItem("com.lib:attr/bar", ResourceId(0x02010000))
287                         .Build())
288           .Build();
289 
290   std::unique_ptr<IAaptContext> context =
291       test::ContextBuilder()
292           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
293           .SetNameManglerPolicy(NameManglerPolicy{"android"})
294           .Build();
295   JavaClassGenerator generator(context.get(), table.get(), {});
296 
297   std::string output;
298   StringOutputStream out(&output);
299   EXPECT_TRUE(generator.Generate("android", &out));
300   out.Flush();
301 
302   EXPECT_THAT(output, HasSubstr("int foo_bar="));
303   EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
304 }
305 
TEST(JavaClassGeneratorTest,CommentsForSimpleResourcesArePresent)306 TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
307   std::unique_ptr<ResourceTable> table =
308       test::ResourceTableBuilder()
309           .SetPackageId("android", 0x01)
310           .AddSimple("android:id/foo", ResourceId(0x01010000))
311           .Build();
312   test::GetValue<Id>(table.get(), "android:id/foo")
313       ->SetComment(std::string("This is a comment\n@deprecated"));
314 
315   std::unique_ptr<IAaptContext> context =
316       test::ContextBuilder()
317           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
318           .SetNameManglerPolicy(NameManglerPolicy{"android"})
319           .Build();
320   JavaClassGenerator generator(context.get(), table.get(), {});
321 
322   std::string output;
323   StringOutputStream out(&output);
324   ASSERT_TRUE(generator.Generate("android", &out));
325   out.Flush();
326 
327   const char* expected_text =
328       R"EOF(/**
329      * This is a comment
330      * @deprecated
331      */
332     @Deprecated
333     public static final int foo=0x01010000;)EOF";
334   EXPECT_THAT(output, HasSubstr(expected_text));
335 }
336 
TEST(JavaClassGeneratorTest,CommentsForEnumAndFlagAttributesArePresent)337 TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
338 
TEST(JavaClassGeneratorTest,CommentsForStyleablesAndNestedAttributesArePresent)339 TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
340   Attribute attr;
341   attr.SetComment(StringPiece("This is an attribute"));
342 
343   Styleable styleable;
344   styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
345   styleable.SetComment(StringPiece("This is a styleable"));
346 
347   std::unique_ptr<ResourceTable> table =
348       test::ResourceTableBuilder()
349           .SetPackageId("android", 0x01)
350           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
351           .AddValue("android:styleable/Container",
352                     std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
353           .Build();
354 
355   std::unique_ptr<IAaptContext> context =
356       test::ContextBuilder()
357           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
358           .SetNameManglerPolicy(NameManglerPolicy{"android"})
359           .Build();
360   JavaClassGeneratorOptions options;
361   options.use_final = false;
362   JavaClassGenerator generator(context.get(), table.get(), options);
363 
364   std::string output;
365   StringOutputStream out(&output);
366   ASSERT_TRUE(generator.Generate("android", &out));
367   out.Flush();
368 
369   EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
370   EXPECT_THAT(output, HasSubstr("@see #Container_one"));
371   EXPECT_THAT(output, HasSubstr("attr name android:one"));
372   EXPECT_THAT(output, HasSubstr("attr description"));
373   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
374   EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
375 }
376 
TEST(JavaClassGeneratorTest,CommentsForStyleableHiddenAttributesAreNotPresent)377 TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
378   Attribute attr;
379   attr.SetComment(StringPiece("This is an attribute @hide"));
380 
381   Styleable styleable;
382   styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
383   styleable.SetComment(StringPiece("This is a styleable"));
384 
385   std::unique_ptr<ResourceTable> table =
386       test::ResourceTableBuilder()
387           .SetPackageId("android", 0x01)
388           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
389           .AddValue("android:styleable/Container",
390                     std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
391           .Build();
392 
393   std::unique_ptr<IAaptContext> context =
394       test::ContextBuilder()
395           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
396           .SetNameManglerPolicy(NameManglerPolicy{"android"})
397           .Build();
398   JavaClassGeneratorOptions options;
399   options.use_final = false;
400   JavaClassGenerator generator(context.get(), table.get(), options);
401 
402   std::string output;
403   StringOutputStream out(&output);
404   ASSERT_TRUE(generator.Generate("android", &out));
405   out.Flush();
406 
407   EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
408   EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
409   EXPECT_THAT(output, HasSubstr("attr name android:one"));
410   EXPECT_THAT(output, HasSubstr("attr description"));
411   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
412   EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
413 }
414 
TEST(JavaClassGeneratorTest,StyleableAndIndicesAreColocated)415 TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
416   std::unique_ptr<ResourceTable> table =
417       test::ResourceTableBuilder()
418           .SetPackageId("android", 0x01)
419           .AddValue("android:attr/layout_gravity", util::make_unique<Attribute>())
420           .AddValue("android:attr/background", util::make_unique<Attribute>())
421           .AddValue("android:styleable/ActionBar",
422                     test::StyleableBuilder()
423                         .AddItem("android:attr/background", ResourceId(0x01010000))
424                         .Build())
425           .AddValue("android:styleable/ActionBar.LayoutParams",
426                     test::StyleableBuilder()
427                         .AddItem("android:attr/layout_gravity", ResourceId(0x01010001))
428                         .Build())
429           .Build();
430 
431   std::unique_ptr<IAaptContext> context =
432       test::ContextBuilder()
433           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
434           .SetNameManglerPolicy(NameManglerPolicy{"android"})
435           .Build();
436 
437   JavaClassGeneratorOptions options;
438   JavaClassGenerator generator(context.get(), table.get(), {});
439 
440   std::string output;
441   StringOutputStream out(&output);
442   ASSERT_TRUE(generator.Generate("android", &out));
443   out.Flush();
444 
445   std::string::size_type actionbar_pos = output.find("int[] ActionBar");
446   ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
447 
448   std::string::size_type actionbar_background_pos = output.find("int ActionBar_background");
449   ASSERT_THAT(actionbar_background_pos, Ne(std::string::npos));
450 
451   std::string::size_type actionbar_layout_params_pos = output.find("int[] ActionBar_LayoutParams");
452   ASSERT_THAT(actionbar_layout_params_pos, Ne(std::string::npos));
453 
454   std::string::size_type actionbar_layout_params_layout_gravity_pos =
455       output.find("int ActionBar_LayoutParams_layout_gravity");
456   ASSERT_THAT(actionbar_layout_params_layout_gravity_pos, Ne(std::string::npos));
457 
458   EXPECT_THAT(actionbar_pos, Lt(actionbar_background_pos));
459   EXPECT_THAT(actionbar_pos, Lt(actionbar_layout_params_pos));
460   EXPECT_THAT(actionbar_background_pos, Lt(actionbar_layout_params_pos));
461   EXPECT_THAT(actionbar_layout_params_pos, Lt(actionbar_layout_params_layout_gravity_pos));
462 }
463 
TEST(JavaClassGeneratorTest,CommentsForRemovedAttributesAreNotPresentInClass)464 TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
465   Attribute attr;
466   attr.SetComment(StringPiece("removed"));
467 
468   std::unique_ptr<ResourceTable> table =
469       test::ResourceTableBuilder()
470           .SetPackageId("android", 0x01)
471           .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
472           .Build();
473 
474   std::unique_ptr<IAaptContext> context =
475       test::ContextBuilder()
476           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
477           .SetNameManglerPolicy(NameManglerPolicy{"android"})
478           .Build();
479   JavaClassGeneratorOptions options;
480   options.use_final = false;
481   JavaClassGenerator generator(context.get(), table.get(), options);
482 
483   std::string output;
484   StringOutputStream out(&output);
485   ASSERT_TRUE(generator.Generate("android", &out));
486   out.Flush();
487 
488   EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
489   EXPECT_THAT(output, Not(HasSubstr("@attr description")));
490 
491   // We should find @removed only in the attribute javadoc and not anywhere else
492   // (i.e. the class javadoc).
493   const std::string kRemoved("removed");
494   ASSERT_THAT(output, HasSubstr(kRemoved));
495   std::string after_first_match = output.substr(output.find(kRemoved) + kRemoved.size());
496   EXPECT_THAT(after_first_match, Not(HasSubstr(kRemoved)));
497 }
498 
TEST(JavaClassGeneratorTest,GenerateOnResourcesLoadedCallbackForSharedLibrary)499 TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
500   std::unique_ptr<ResourceTable> table =
501       test::ResourceTableBuilder()
502           .SetPackageId("android", 0x00)
503           .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>())
504           .AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
505           .AddValue(
506               "android:style/foo", ResourceId(0x00030000),
507               test::StyleBuilder()
508                   .AddItem("android:attr/foo", ResourceId(0x00010000), util::make_unique<Id>())
509                   .Build())
510           .Build();
511 
512   std::unique_ptr<IAaptContext> context =
513       test::ContextBuilder().SetPackageId(0x00).SetCompilationPackage("android").Build();
514 
515   JavaClassGeneratorOptions options;
516   options.use_final = false;
517   options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
518   JavaClassGenerator generator(context.get(), table.get(), options);
519 
520   std::string output;
521   StringOutputStream out(&output);
522   ASSERT_TRUE(generator.Generate("android", &out));
523   out.Flush();
524 
525   EXPECT_THAT(output, HasSubstr(
526           R"(  public static void onResourcesLoaded(int p) {
527     com.foo.R.onResourcesLoaded(p);
528     com.boo.R.onResourcesLoaded(p);
529     final int packageIdBits = p << 24;
530     attr.foo = (attr.foo & 0x00ffffff) | packageIdBits;
531     id.foo = (id.foo & 0x00ffffff) | packageIdBits;
532     style.foo = (style.foo & 0x00ffffff) | packageIdBits;
533   })"));
534 }
535 
TEST(JavaClassGeneratorTest,OnlyGenerateRText)536 TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
537   std::unique_ptr<ResourceTable> table =
538       test::ResourceTableBuilder()
539           .SetPackageId("android", 0x01)
540           .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>())
541           .AddValue("android:styleable/hey.dude", ResourceId(0x01020000),
542                     test::StyleableBuilder()
543                         .AddItem("android:attr/foo", ResourceId(0x01010000))
544                         .Build())
545           .Build();
546 
547   std::unique_ptr<IAaptContext> context =
548       test::ContextBuilder().SetPackageId(0x01).SetCompilationPackage("android").Build();
549   JavaClassGenerator generator(context.get(), table.get(), {});
550 
551   ASSERT_TRUE(generator.Generate("android", nullptr));
552 }
553 
554 }  // namespace aapt
555