Home | History | Annotate | Download | only in java
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    337 TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
    338 
    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 
    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 
    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 
    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 
    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("void onResourcesLoaded"));
    526   EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
    527   EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded"));
    528 }
    529 
    530 TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
    531   std::unique_ptr<ResourceTable> table =
    532       test::ResourceTableBuilder()
    533           .SetPackageId("android", 0x01)
    534           .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>())
    535           .AddValue("android:styleable/hey.dude", ResourceId(0x01020000),
    536                     test::StyleableBuilder()
    537                         .AddItem("android:attr/foo", ResourceId(0x01010000))
    538                         .Build())
    539           .Build();
    540 
    541   std::unique_ptr<IAaptContext> context =
    542       test::ContextBuilder().SetPackageId(0x01).SetCompilationPackage("android").Build();
    543   JavaClassGenerator generator(context.get(), table.get(), {});
    544 
    545   ASSERT_TRUE(generator.Generate("android", nullptr));
    546 }
    547 
    548 }  // namespace aapt
    549