ResourceParser_test.cpp revision 58a20a6482a56a262fd83a617482641e3a981db1
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 "ResourceParser.h" 18#include "ResourceTable.h" 19#include "ResourceUtils.h" 20#include "ResourceValues.h" 21#include "test/Test.h" 22#include "xml/XmlPullParser.h" 23 24#include <sstream> 25#include <string> 26 27namespace aapt { 28 29constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 30 31TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) { 32 std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); 33 std::stringstream input(kXmlPreamble); 34 input << "<attr name=\"foo\"/>" << std::endl; 35 ResourceTable table; 36 ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {}); 37 xml::XmlPullParser xmlParser(input); 38 ASSERT_FALSE(parser.parse(&xmlParser)); 39} 40 41struct ResourceParserTest : public ::testing::Test { 42 ResourceTable mTable; 43 std::unique_ptr<IAaptContext> mContext; 44 45 void SetUp() override { 46 mContext = test::ContextBuilder().build(); 47 } 48 49 ::testing::AssertionResult testParse(const StringPiece& str) { 50 return testParse(str, ConfigDescription{}); 51 } 52 53 ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) { 54 std::stringstream input(kXmlPreamble); 55 input << "<resources>\n" << str << "\n</resources>" << std::endl; 56 ResourceParserOptions parserOptions; 57 ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config, 58 parserOptions); 59 xml::XmlPullParser xmlParser(input); 60 if (parser.parse(&xmlParser)) { 61 return ::testing::AssertionSuccess(); 62 } 63 return ::testing::AssertionFailure(); 64 } 65}; 66 67TEST_F(ResourceParserTest, ParseQuotedString) { 68 std::string input = "<string name=\"foo\"> \" hey there \" </string>"; 69 ASSERT_TRUE(testParse(input)); 70 71 String* str = test::getValue<String>(&mTable, "string/foo"); 72 ASSERT_NE(nullptr, str); 73 EXPECT_EQ(std::string(" hey there "), *str->value); 74} 75 76TEST_F(ResourceParserTest, ParseEscapedString) { 77 std::string input = "<string name=\"foo\">\\?123</string>"; 78 ASSERT_TRUE(testParse(input)); 79 80 String* str = test::getValue<String>(&mTable, "string/foo"); 81 ASSERT_NE(nullptr, str); 82 EXPECT_EQ(std::string("?123"), *str->value); 83} 84 85TEST_F(ResourceParserTest, ParseFormattedString) { 86 std::string input = "<string name=\"foo\">%d %s</string>"; 87 ASSERT_FALSE(testParse(input)); 88 89 input = "<string name=\"foo\">%1$d %2$s</string>"; 90 ASSERT_TRUE(testParse(input)); 91} 92 93TEST_F(ResourceParserTest, IgnoreXliffTags) { 94 std::string input = "<string name=\"foo\" \n" 95 " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" 96 " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>"; 97 ASSERT_TRUE(testParse(input)); 98 99 String* str = test::getValue<String>(&mTable, "string/foo"); 100 ASSERT_NE(nullptr, str); 101 EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value)); 102} 103 104TEST_F(ResourceParserTest, ParseNull) { 105 std::string input = "<integer name=\"foo\">@null</integer>"; 106 ASSERT_TRUE(testParse(input)); 107 108 // The Android runtime treats a value of android::Res_value::TYPE_NULL as 109 // a non-existing value, and this causes problems in styles when trying to resolve 110 // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE 111 // with a data value of 0. 112 BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, "integer/foo"); 113 ASSERT_NE(nullptr, integer); 114 EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType); 115 EXPECT_EQ(0u, integer->value.data); 116} 117 118TEST_F(ResourceParserTest, ParseEmpty) { 119 std::string input = "<integer name=\"foo\">@empty</integer>"; 120 ASSERT_TRUE(testParse(input)); 121 122 BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, "integer/foo"); 123 ASSERT_NE(nullptr, integer); 124 EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType); 125 EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data); 126} 127 128TEST_F(ResourceParserTest, ParseAttr) { 129 std::string input = "<attr name=\"foo\" format=\"string\"/>\n" 130 "<attr name=\"bar\"/>"; 131 ASSERT_TRUE(testParse(input)); 132 133 Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo"); 134 ASSERT_NE(nullptr, attr); 135 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask); 136 137 attr = test::getValue<Attribute>(&mTable, "attr/bar"); 138 ASSERT_NE(nullptr, attr); 139 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask); 140} 141 142// Old AAPT allowed attributes to be defined under different configurations, but ultimately 143// stored them with the default configuration. Check that we have the same behavior. 144TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) { 145 const ConfigDescription watchConfig = test::parseConfigOrDie("watch"); 146 std::string input = R"EOF( 147 <attr name="foo" /> 148 <declare-styleable name="bar"> 149 <attr name="baz" /> 150 </declare-styleable>)EOF"; 151 ASSERT_TRUE(testParse(input, watchConfig)); 152 153 EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "attr/foo", watchConfig)); 154 EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "attr/baz", watchConfig)); 155 EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, "styleable/bar", watchConfig)); 156 157 EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/foo")); 158 EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/baz")); 159 EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, "styleable/bar")); 160} 161 162TEST_F(ResourceParserTest, ParseAttrWithMinMax) { 163 std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>"; 164 ASSERT_TRUE(testParse(input)); 165 166 Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo"); 167 ASSERT_NE(nullptr, attr); 168 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask); 169 EXPECT_EQ(10, attr->minInt); 170 EXPECT_EQ(23, attr->maxInt); 171} 172 173TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) { 174 std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>"; 175 ASSERT_FALSE(testParse(input)); 176} 177 178TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) { 179 std::string input = "<declare-styleable name=\"Styleable\">\n" 180 " <attr name=\"foo\" />\n" 181 "</declare-styleable>\n" 182 "<attr name=\"foo\" format=\"string\"/>"; 183 ASSERT_TRUE(testParse(input)); 184 185 Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo"); 186 ASSERT_NE(nullptr, attr); 187 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask); 188} 189 190TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) { 191 std::string input = "<declare-styleable name=\"Theme\">" 192 " <attr name=\"foo\" />\n" 193 "</declare-styleable>\n" 194 "<declare-styleable name=\"Window\">\n" 195 " <attr name=\"foo\" format=\"boolean\"/>\n" 196 "</declare-styleable>"; 197 ASSERT_TRUE(testParse(input)); 198 199 Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo"); 200 ASSERT_NE(nullptr, attr); 201 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask); 202} 203 204TEST_F(ResourceParserTest, ParseEnumAttr) { 205 std::string input = "<attr name=\"foo\">\n" 206 " <enum name=\"bar\" value=\"0\"/>\n" 207 " <enum name=\"bat\" value=\"1\"/>\n" 208 " <enum name=\"baz\" value=\"2\"/>\n" 209 "</attr>"; 210 ASSERT_TRUE(testParse(input)); 211 212 Attribute* enumAttr = test::getValue<Attribute>(&mTable, "attr/foo"); 213 ASSERT_NE(enumAttr, nullptr); 214 EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM); 215 ASSERT_EQ(enumAttr->symbols.size(), 3u); 216 217 AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name); 218 EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, "bar"); 219 EXPECT_EQ(enumAttr->symbols[0].value, 0u); 220 221 AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name); 222 EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, "bat"); 223 EXPECT_EQ(enumAttr->symbols[1].value, 1u); 224 225 AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name); 226 EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, "baz"); 227 EXPECT_EQ(enumAttr->symbols[2].value, 2u); 228} 229 230TEST_F(ResourceParserTest, ParseFlagAttr) { 231 std::string input = "<attr name=\"foo\">\n" 232 " <flag name=\"bar\" value=\"0\"/>\n" 233 " <flag name=\"bat\" value=\"1\"/>\n" 234 " <flag name=\"baz\" value=\"2\"/>\n" 235 "</attr>"; 236 ASSERT_TRUE(testParse(input)); 237 238 Attribute* flagAttr = test::getValue<Attribute>(&mTable, "attr/foo"); 239 ASSERT_NE(nullptr, flagAttr); 240 EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS); 241 ASSERT_EQ(flagAttr->symbols.size(), 3u); 242 243 AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name); 244 EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, "bar"); 245 EXPECT_EQ(flagAttr->symbols[0].value, 0u); 246 247 AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name); 248 EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, "bat"); 249 EXPECT_EQ(flagAttr->symbols[1].value, 1u); 250 251 AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name); 252 EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, "baz"); 253 EXPECT_EQ(flagAttr->symbols[2].value, 2u); 254 255 std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr, 256 "baz|bat"); 257 ASSERT_NE(nullptr, flagValue); 258 EXPECT_EQ(flagValue->value.data, 1u | 2u); 259} 260 261TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) { 262 std::string input = "<attr name=\"foo\">\n" 263 " <enum name=\"bar\" value=\"0\"/>\n" 264 " <enum name=\"bat\" value=\"1\"/>\n" 265 " <enum name=\"bat\" value=\"2\"/>\n" 266 "</attr>"; 267 ASSERT_FALSE(testParse(input)); 268} 269 270TEST_F(ResourceParserTest, ParseStyle) { 271 std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n" 272 " <item name=\"bar\">#ffffffff</item>\n" 273 " <item name=\"bat\">@string/hey</item>\n" 274 " <item name=\"baz\"><b>hey</b></item>\n" 275 "</style>"; 276 ASSERT_TRUE(testParse(input)); 277 278 Style* style = test::getValue<Style>(&mTable, "style/foo"); 279 ASSERT_NE(nullptr, style); 280 AAPT_ASSERT_TRUE(style->parent); 281 AAPT_ASSERT_TRUE(style->parent.value().name); 282 EXPECT_EQ(test::parseNameOrDie("style/fu"), style->parent.value().name.value()); 283 ASSERT_EQ(3u, style->entries.size()); 284 285 AAPT_ASSERT_TRUE(style->entries[0].key.name); 286 EXPECT_EQ(test::parseNameOrDie("attr/bar"), style->entries[0].key.name.value()); 287 288 AAPT_ASSERT_TRUE(style->entries[1].key.name); 289 EXPECT_EQ(test::parseNameOrDie("attr/bat"), style->entries[1].key.name.value()); 290 291 AAPT_ASSERT_TRUE(style->entries[2].key.name); 292 EXPECT_EQ(test::parseNameOrDie("attr/baz"), style->entries[2].key.name.value()); 293} 294 295TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) { 296 std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>"; 297 ASSERT_TRUE(testParse(input)); 298 299 Style* style = test::getValue<Style>(&mTable, "style/foo"); 300 ASSERT_NE(nullptr, style); 301 AAPT_ASSERT_TRUE(style->parent); 302 AAPT_ASSERT_TRUE(style->parent.value().name); 303 EXPECT_EQ(test::parseNameOrDie("com.app:style/Theme"), style->parent.value().name.value()); 304} 305 306TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) { 307 std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n" 308 " name=\"foo\" parent=\"app:Theme\"/>"; 309 ASSERT_TRUE(testParse(input)); 310 311 Style* style = test::getValue<Style>(&mTable, "style/foo"); 312 ASSERT_NE(nullptr, style); 313 AAPT_ASSERT_TRUE(style->parent); 314 AAPT_ASSERT_TRUE(style->parent.value().name); 315 EXPECT_EQ(test::parseNameOrDie("android:style/Theme"), style->parent.value().name.value()); 316} 317 318TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) { 319 std::string input = 320 "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n" 321 " <item name=\"app:bar\">0</item>\n" 322 "</style>"; 323 ASSERT_TRUE(testParse(input)); 324 325 Style* style = test::getValue<Style>(&mTable, "style/foo"); 326 ASSERT_NE(nullptr, style); 327 ASSERT_EQ(1u, style->entries.size()); 328 EXPECT_EQ(test::parseNameOrDie("android:attr/bar"), style->entries[0].key.name.value()); 329} 330 331TEST_F(ResourceParserTest, ParseStyleWithInferredParent) { 332 std::string input = "<style name=\"foo.bar\"/>"; 333 ASSERT_TRUE(testParse(input)); 334 335 Style* style = test::getValue<Style>(&mTable, "style/foo.bar"); 336 ASSERT_NE(nullptr, style); 337 AAPT_ASSERT_TRUE(style->parent); 338 AAPT_ASSERT_TRUE(style->parent.value().name); 339 EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie("style/foo")); 340 EXPECT_TRUE(style->parentInferred); 341} 342 343TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) { 344 std::string input = "<style name=\"foo.bar\" parent=\"\"/>"; 345 ASSERT_TRUE(testParse(input)); 346 347 Style* style = test::getValue<Style>(&mTable, "style/foo.bar"); 348 ASSERT_NE(nullptr, style); 349 AAPT_EXPECT_FALSE(style->parent); 350 EXPECT_FALSE(style->parentInferred); 351} 352 353TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) { 354 std::string input = R"EOF(<style name="foo" parent="*android:style/bar" />)EOF"; 355 ASSERT_TRUE(testParse(input)); 356 357 Style* style = test::getValue<Style>(&mTable, "style/foo"); 358 ASSERT_NE(nullptr, style); 359 AAPT_ASSERT_TRUE(style->parent); 360 EXPECT_TRUE(style->parent.value().privateReference); 361} 362 363TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) { 364 std::string input = "<string name=\"foo\">@+id/bar</string>"; 365 ASSERT_TRUE(testParse(input)); 366 367 Id* id = test::getValue<Id>(&mTable, "id/bar"); 368 ASSERT_NE(id, nullptr); 369} 370 371TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { 372 std::string input = "<declare-styleable name=\"foo\">\n" 373 " <attr name=\"bar\" />\n" 374 " <attr name=\"bat\" format=\"string|reference\"/>\n" 375 " <attr name=\"baz\">\n" 376 " <enum name=\"foo\" value=\"1\"/>\n" 377 " </attr>\n" 378 "</declare-styleable>"; 379 ASSERT_TRUE(testParse(input)); 380 381 Maybe<ResourceTable::SearchResult> result = 382 mTable.findResource(test::parseNameOrDie("styleable/foo")); 383 AAPT_ASSERT_TRUE(result); 384 EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); 385 386 Attribute* attr = test::getValue<Attribute>(&mTable, "attr/bar"); 387 ASSERT_NE(attr, nullptr); 388 EXPECT_TRUE(attr->isWeak()); 389 390 attr = test::getValue<Attribute>(&mTable, "attr/bat"); 391 ASSERT_NE(attr, nullptr); 392 EXPECT_TRUE(attr->isWeak()); 393 394 attr = test::getValue<Attribute>(&mTable, "attr/baz"); 395 ASSERT_NE(attr, nullptr); 396 EXPECT_TRUE(attr->isWeak()); 397 EXPECT_EQ(1u, attr->symbols.size()); 398 399 EXPECT_NE(nullptr, test::getValue<Id>(&mTable, "id/foo")); 400 401 Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo"); 402 ASSERT_NE(styleable, nullptr); 403 ASSERT_EQ(3u, styleable->entries.size()); 404 405 EXPECT_EQ(test::parseNameOrDie("attr/bar"), styleable->entries[0].name.value()); 406 EXPECT_EQ(test::parseNameOrDie("attr/bat"), styleable->entries[1].name.value()); 407} 408 409TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { 410 std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n" 411 " <attr name=\"*android:bar\" />\n" 412 " <attr name=\"privAndroid:bat\" />\n" 413 "</declare-styleable>"; 414 ASSERT_TRUE(testParse(input)); 415 Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo"); 416 ASSERT_NE(nullptr, styleable); 417 ASSERT_EQ(2u, styleable->entries.size()); 418 419 EXPECT_TRUE(styleable->entries[0].privateReference); 420 AAPT_ASSERT_TRUE(styleable->entries[0].name); 421 EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package); 422 423 EXPECT_TRUE(styleable->entries[1].privateReference); 424 AAPT_ASSERT_TRUE(styleable->entries[1].name); 425 EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package); 426} 427 428TEST_F(ResourceParserTest, ParseArray) { 429 std::string input = "<array name=\"foo\">\n" 430 " <item>@string/ref</item>\n" 431 " <item>hey</item>\n" 432 " <item>23</item>\n" 433 "</array>"; 434 ASSERT_TRUE(testParse(input)); 435 436 Array* array = test::getValue<Array>(&mTable, "array/foo"); 437 ASSERT_NE(array, nullptr); 438 ASSERT_EQ(3u, array->items.size()); 439 440 EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get())); 441 EXPECT_NE(nullptr, valueCast<String>(array->items[1].get())); 442 EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get())); 443} 444 445TEST_F(ResourceParserTest, ParseStringArray) { 446 std::string input = "<string-array name=\"foo\">\n" 447 " <item>\"Werk\"</item>\n" 448 "</string-array>\n"; 449 ASSERT_TRUE(testParse(input)); 450 EXPECT_NE(nullptr, test::getValue<Array>(&mTable, "array/foo")); 451} 452 453TEST_F(ResourceParserTest, ParsePlural) { 454 std::string input = "<plurals name=\"foo\">\n" 455 " <item quantity=\"other\">apples</item>\n" 456 " <item quantity=\"one\">apple</item>\n" 457 "</plurals>"; 458 ASSERT_TRUE(testParse(input)); 459} 460 461TEST_F(ResourceParserTest, ParseCommentsWithResource) { 462 std::string input = "<!--This is a comment-->\n" 463 "<string name=\"foo\">Hi</string>"; 464 ASSERT_TRUE(testParse(input)); 465 466 String* value = test::getValue<String>(&mTable, "string/foo"); 467 ASSERT_NE(nullptr, value); 468 EXPECT_EQ(value->getComment(), "This is a comment"); 469} 470 471TEST_F(ResourceParserTest, DoNotCombineMultipleComments) { 472 std::string input = "<!--One-->\n" 473 "<!--Two-->\n" 474 "<string name=\"foo\">Hi</string>"; 475 476 ASSERT_TRUE(testParse(input)); 477 478 String* value = test::getValue<String>(&mTable, "string/foo"); 479 ASSERT_NE(nullptr, value); 480 EXPECT_EQ(value->getComment(), "Two"); 481} 482 483TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) { 484 std::string input = "<!--One-->\n" 485 "<string name=\"foo\">\n" 486 " Hi\n" 487 "<!--Two-->\n" 488 "</string>"; 489 490 ASSERT_TRUE(testParse(input)); 491 492 String* value = test::getValue<String>(&mTable, "string/foo"); 493 ASSERT_NE(nullptr, value); 494 EXPECT_EQ(value->getComment(), "One"); 495} 496 497TEST_F(ResourceParserTest, ParseNestedComments) { 498 // We only care about declare-styleable and enum/flag attributes because comments 499 // from those end up in R.java 500 std::string input = R"EOF( 501 <declare-styleable name="foo"> 502 <!-- The name of the bar --> 503 <attr name="barName" format="string|reference" /> 504 </declare-styleable> 505 506 <attr name="foo"> 507 <!-- The very first --> 508 <enum name="one" value="1" /> 509 </attr>)EOF"; 510 ASSERT_TRUE(testParse(input)); 511 512 Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo"); 513 ASSERT_NE(nullptr, styleable); 514 ASSERT_EQ(1u, styleable->entries.size()); 515 516 EXPECT_EQ(StringPiece("The name of the bar"), styleable->entries.front().getComment()); 517 518 Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo"); 519 ASSERT_NE(nullptr, attr); 520 ASSERT_EQ(1u, attr->symbols.size()); 521 522 EXPECT_EQ(StringPiece("The very first"), attr->symbols.front().symbol.getComment()); 523} 524 525/* 526 * Declaring an ID as public should not require a separate definition 527 * (as an ID has no value). 528 */ 529TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) { 530 std::string input = "<public type=\"id\" name=\"foo\"/>"; 531 ASSERT_TRUE(testParse(input)); 532 533 Id* id = test::getValue<Id>(&mTable, "id/foo"); 534 ASSERT_NE(nullptr, id); 535} 536 537TEST_F(ResourceParserTest, KeepAllProducts) { 538 std::string input = R"EOF( 539 <string name="foo" product="phone">hi</string> 540 <string name="foo" product="no-sdcard">ho</string> 541 <string name="bar" product="">wee</string> 542 <string name="baz">woo</string> 543 <string name="bit" product="phablet">hoot</string> 544 <string name="bot" product="default">yes</string> 545 )EOF"; 546 ASSERT_TRUE(testParse(input)); 547 548 EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/foo", 549 ConfigDescription::defaultConfig(), 550 "phone")); 551 EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/foo", 552 ConfigDescription::defaultConfig(), 553 "no-sdcard")); 554 EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bar", 555 ConfigDescription::defaultConfig(), 556 "")); 557 EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/baz", 558 ConfigDescription::defaultConfig(), 559 "")); 560 EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bit", 561 ConfigDescription::defaultConfig(), 562 "phablet")); 563 EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bot", 564 ConfigDescription::defaultConfig(), 565 "default")); 566} 567 568TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) { 569 std::string input = R"EOF( 570 <public-group type="attr" first-id="0x01010040"> 571 <public name="foo" /> 572 <public name="bar" /> 573 </public-group>)EOF"; 574 ASSERT_TRUE(testParse(input)); 575 576 Maybe<ResourceTable::SearchResult> result = mTable.findResource( 577 test::parseNameOrDie("attr/foo")); 578 AAPT_ASSERT_TRUE(result); 579 580 AAPT_ASSERT_TRUE(result.value().package->id); 581 AAPT_ASSERT_TRUE(result.value().type->id); 582 AAPT_ASSERT_TRUE(result.value().entry->id); 583 ResourceId actualId(result.value().package->id.value(), 584 result.value().type->id.value(), 585 result.value().entry->id.value()); 586 EXPECT_EQ(ResourceId(0x01010040), actualId); 587 588 result = mTable.findResource(test::parseNameOrDie("attr/bar")); 589 AAPT_ASSERT_TRUE(result); 590 591 AAPT_ASSERT_TRUE(result.value().package->id); 592 AAPT_ASSERT_TRUE(result.value().type->id); 593 AAPT_ASSERT_TRUE(result.value().entry->id); 594 actualId = ResourceId(result.value().package->id.value(), 595 result.value().type->id.value(), 596 result.value().entry->id.value()); 597 EXPECT_EQ(ResourceId(0x01010041), actualId); 598} 599 600TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) { 601 std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF"; 602 ASSERT_TRUE(testParse(input)); 603 604 input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF"; 605 ASSERT_FALSE(testParse(input)); 606} 607 608TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) { 609 std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF"; 610 ASSERT_TRUE(testParse(input)); 611 612 Maybe<ResourceTable::SearchResult> result = mTable.findResource( 613 test::parseNameOrDie("string/bar")); 614 AAPT_ASSERT_TRUE(result); 615 const ResourceEntry* entry = result.value().entry; 616 ASSERT_NE(nullptr, entry); 617 EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state); 618} 619 620TEST_F(ResourceParserTest, ParseItemElementWithFormat) { 621 std::string input = R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF"; 622 ASSERT_TRUE(testParse(input)); 623 624 BinaryPrimitive* val = test::getValue<BinaryPrimitive>(&mTable, "integer/foo"); 625 ASSERT_NE(nullptr, val); 626 627 EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType); 628} 629 630} // namespace aapt 631