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