ResourceParser_test.cpp revision 7542162cb1b1fd2ce8a26dd7f3fedc8de8160d38
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 19#include <sstream> 20#include <string> 21 22#include "ResourceTable.h" 23#include "ResourceUtils.h" 24#include "ResourceValues.h" 25#include "test/Test.h" 26#include "xml/XmlPullParser.h" 27 28using android::StringPiece; 29 30namespace aapt { 31 32constexpr const char* kXmlPreamble = 33 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 34 35TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) { 36 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); 37 std::stringstream input(kXmlPreamble); 38 input << "<attr name=\"foo\"/>" << std::endl; 39 ResourceTable table; 40 ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {}); 41 xml::XmlPullParser xml_parser(input); 42 ASSERT_FALSE(parser.Parse(&xml_parser)); 43} 44 45class ResourceParserTest : public ::testing::Test { 46 public: 47 void SetUp() override { context_ = test::ContextBuilder().Build(); } 48 49 ::testing::AssertionResult TestParse(const StringPiece& str) { 50 return TestParse(str, ConfigDescription{}); 51 } 52 53 ::testing::AssertionResult TestParse(const StringPiece& str, 54 const ConfigDescription& config) { 55 std::stringstream input(kXmlPreamble); 56 input << "<resources>\n" << str << "\n</resources>" << std::endl; 57 ResourceParserOptions parserOptions; 58 ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, 59 config, parserOptions); 60 xml::XmlPullParser xmlParser(input); 61 if (parser.Parse(&xmlParser)) { 62 return ::testing::AssertionSuccess(); 63 } 64 return ::testing::AssertionFailure(); 65 } 66 67 protected: 68 ResourceTable table_; 69 std::unique_ptr<IAaptContext> context_; 70}; 71 72TEST_F(ResourceParserTest, ParseQuotedString) { 73 std::string input = "<string name=\"foo\"> \" hey there \" </string>"; 74 ASSERT_TRUE(TestParse(input)); 75 76 String* str = test::GetValue<String>(&table_, "string/foo"); 77 ASSERT_NE(nullptr, str); 78 EXPECT_EQ(std::string(" hey there "), *str->value); 79 EXPECT_TRUE(str->untranslatable_sections.empty()); 80} 81 82TEST_F(ResourceParserTest, ParseEscapedString) { 83 std::string input = "<string name=\"foo\">\\?123</string>"; 84 ASSERT_TRUE(TestParse(input)); 85 86 String* str = test::GetValue<String>(&table_, "string/foo"); 87 ASSERT_NE(nullptr, str); 88 EXPECT_EQ(std::string("?123"), *str->value); 89 EXPECT_TRUE(str->untranslatable_sections.empty()); 90} 91 92TEST_F(ResourceParserTest, ParseFormattedString) { 93 std::string input = "<string name=\"foo\">%d %s</string>"; 94 ASSERT_FALSE(TestParse(input)); 95 96 input = "<string name=\"foo\">%1$d %2$s</string>"; 97 ASSERT_TRUE(TestParse(input)); 98} 99 100TEST_F(ResourceParserTest, ParseStyledString) { 101 // Use a surrogate pair unicode point so that we can verify that the span 102 // indices use UTF-16 length and not UTF-8 length. 103 std::string input = 104 "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>"; 105 ASSERT_TRUE(TestParse(input)); 106 107 StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); 108 ASSERT_NE(nullptr, str); 109 110 const std::string expected_str = "This is my aunt\u2019s string"; 111 EXPECT_EQ(expected_str, *str->value->str); 112 EXPECT_EQ(1u, str->value->spans.size()); 113 EXPECT_TRUE(str->untranslatable_sections.empty()); 114 115 EXPECT_EQ(std::string("b"), *str->value->spans[0].name); 116 EXPECT_EQ(17u, str->value->spans[0].first_char); 117 EXPECT_EQ(23u, str->value->spans[0].last_char); 118} 119 120TEST_F(ResourceParserTest, ParseStringWithWhitespace) { 121 std::string input = "<string name=\"foo\"> This is what I think </string>"; 122 ASSERT_TRUE(TestParse(input)); 123 124 String* str = test::GetValue<String>(&table_, "string/foo"); 125 ASSERT_NE(nullptr, str); 126 EXPECT_EQ(std::string("This is what I think"), *str->value); 127 EXPECT_TRUE(str->untranslatable_sections.empty()); 128 129 input = "<string name=\"foo2\">\" This is what I think \"</string>"; 130 ASSERT_TRUE(TestParse(input)); 131 132 str = test::GetValue<String>(&table_, "string/foo2"); 133 ASSERT_NE(nullptr, str); 134 EXPECT_EQ(std::string(" This is what I think "), *str->value); 135} 136 137TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) { 138 std::string input = R"EOF( 139 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 140 There are <xliff:source>no</xliff:source> apples</string>)EOF"; 141 ASSERT_TRUE(TestParse(input)); 142 143 String* str = test::GetValue<String>(&table_, "string/foo"); 144 ASSERT_NE(nullptr, str); 145 EXPECT_EQ(StringPiece("There are no apples"), StringPiece(*str->value)); 146 EXPECT_TRUE(str->untranslatable_sections.empty()); 147} 148 149TEST_F(ResourceParserTest, NestedXliffGTagsAreIllegal) { 150 std::string input = R"EOF( 151 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 152 Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)EOF"; 153 EXPECT_FALSE(TestParse(input)); 154} 155 156TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInString) { 157 std::string input = R"EOF( 158 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 159 There are <xliff:g id="count">%1$d</xliff:g> apples</string>)EOF"; 160 ASSERT_TRUE(TestParse(input)); 161 162 String* str = test::GetValue<String>(&table_, "string/foo"); 163 ASSERT_NE(nullptr, str); 164 EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value)); 165 166 ASSERT_EQ(1u, str->untranslatable_sections.size()); 167 168 // We expect indices and lengths that span to include the whitespace 169 // before %1$d. This is due to how the StringBuilder withholds whitespace unless 170 // needed (to deal with line breaks, etc.). 171 EXPECT_EQ(9u, str->untranslatable_sections[0].start); 172 EXPECT_EQ(14u, str->untranslatable_sections[0].end); 173} 174 175TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) { 176 std::string input = R"EOF( 177 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 178 There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)EOF"; 179 ASSERT_TRUE(TestParse(input)); 180 181 StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); 182 ASSERT_NE(nullptr, str); 183 EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value->str)); 184 185 ASSERT_EQ(1u, str->untranslatable_sections.size()); 186 187 // We expect indices and lengths that span to include the whitespace 188 // before %1$d. This is due to how the StringBuilder withholds whitespace unless 189 // needed (to deal with line breaks, etc.). 190 EXPECT_EQ(9u, str->untranslatable_sections[0].start); 191 EXPECT_EQ(14u, str->untranslatable_sections[0].end); 192} 193 194TEST_F(ResourceParserTest, ParseNull) { 195 std::string input = "<integer name=\"foo\">@null</integer>"; 196 ASSERT_TRUE(TestParse(input)); 197 198 // The Android runtime treats a value of android::Res_value::TYPE_NULL as 199 // a non-existing value, and this causes problems in styles when trying to 200 // resolve an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE 201 // with a data value of 0. 202 BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo"); 203 ASSERT_NE(nullptr, integer); 204 EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType); 205 EXPECT_EQ(0u, integer->value.data); 206} 207 208TEST_F(ResourceParserTest, ParseEmpty) { 209 std::string input = "<integer name=\"foo\">@empty</integer>"; 210 ASSERT_TRUE(TestParse(input)); 211 212 BinaryPrimitive* integer = 213 test::GetValue<BinaryPrimitive>(&table_, "integer/foo"); 214 ASSERT_NE(nullptr, integer); 215 EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType); 216 EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data); 217} 218 219TEST_F(ResourceParserTest, ParseAttr) { 220 std::string input = 221 "<attr name=\"foo\" format=\"string\"/>\n" 222 "<attr name=\"bar\"/>"; 223 ASSERT_TRUE(TestParse(input)); 224 225 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 226 ASSERT_NE(nullptr, attr); 227 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask); 228 229 attr = test::GetValue<Attribute>(&table_, "attr/bar"); 230 ASSERT_NE(nullptr, attr); 231 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->type_mask); 232} 233 234// Old AAPT allowed attributes to be defined under different configurations, but 235// ultimately 236// stored them with the default configuration. Check that we have the same 237// behavior. 238TEST_F(ResourceParserTest, 239 ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) { 240 const ConfigDescription watch_config = test::ParseConfigOrDie("watch"); 241 std::string input = R"EOF( 242 <attr name="foo" /> 243 <declare-styleable name="bar"> 244 <attr name="baz" /> 245 </declare-styleable>)EOF"; 246 ASSERT_TRUE(TestParse(input, watch_config)); 247 248 EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo", 249 watch_config)); 250 EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz", 251 watch_config)); 252 EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>( 253 &table_, "styleable/bar", watch_config)); 254 255 EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo")); 256 EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz")); 257 EXPECT_NE(nullptr, test::GetValue<Styleable>(&table_, "styleable/bar")); 258} 259 260TEST_F(ResourceParserTest, ParseAttrWithMinMax) { 261 std::string input = 262 "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>"; 263 ASSERT_TRUE(TestParse(input)); 264 265 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 266 ASSERT_NE(nullptr, attr); 267 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->type_mask); 268 EXPECT_EQ(10, attr->min_int); 269 EXPECT_EQ(23, attr->max_int); 270} 271 272TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) { 273 std::string input = 274 "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>"; 275 ASSERT_FALSE(TestParse(input)); 276} 277 278TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) { 279 std::string input = 280 "<declare-styleable name=\"Styleable\">\n" 281 " <attr name=\"foo\" />\n" 282 "</declare-styleable>\n" 283 "<attr name=\"foo\" format=\"string\"/>"; 284 ASSERT_TRUE(TestParse(input)); 285 286 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 287 ASSERT_NE(nullptr, attr); 288 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask); 289} 290 291TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) { 292 std::string input = 293 "<declare-styleable name=\"Theme\">" 294 " <attr name=\"foo\" />\n" 295 "</declare-styleable>\n" 296 "<declare-styleable name=\"Window\">\n" 297 " <attr name=\"foo\" format=\"boolean\"/>\n" 298 "</declare-styleable>"; 299 ASSERT_TRUE(TestParse(input)); 300 301 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 302 ASSERT_NE(nullptr, attr); 303 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->type_mask); 304} 305 306TEST_F(ResourceParserTest, ParseEnumAttr) { 307 std::string input = 308 "<attr name=\"foo\">\n" 309 " <enum name=\"bar\" value=\"0\"/>\n" 310 " <enum name=\"bat\" value=\"1\"/>\n" 311 " <enum name=\"baz\" value=\"2\"/>\n" 312 "</attr>"; 313 ASSERT_TRUE(TestParse(input)); 314 315 Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo"); 316 ASSERT_NE(enum_attr, nullptr); 317 EXPECT_EQ(enum_attr->type_mask, android::ResTable_map::TYPE_ENUM); 318 ASSERT_EQ(enum_attr->symbols.size(), 3u); 319 320 AAPT_ASSERT_TRUE(enum_attr->symbols[0].symbol.name); 321 EXPECT_EQ(enum_attr->symbols[0].symbol.name.value().entry, "bar"); 322 EXPECT_EQ(enum_attr->symbols[0].value, 0u); 323 324 AAPT_ASSERT_TRUE(enum_attr->symbols[1].symbol.name); 325 EXPECT_EQ(enum_attr->symbols[1].symbol.name.value().entry, "bat"); 326 EXPECT_EQ(enum_attr->symbols[1].value, 1u); 327 328 AAPT_ASSERT_TRUE(enum_attr->symbols[2].symbol.name); 329 EXPECT_EQ(enum_attr->symbols[2].symbol.name.value().entry, "baz"); 330 EXPECT_EQ(enum_attr->symbols[2].value, 2u); 331} 332 333TEST_F(ResourceParserTest, ParseFlagAttr) { 334 std::string input = 335 "<attr name=\"foo\">\n" 336 " <flag name=\"bar\" value=\"0\"/>\n" 337 " <flag name=\"bat\" value=\"1\"/>\n" 338 " <flag name=\"baz\" value=\"2\"/>\n" 339 "</attr>"; 340 ASSERT_TRUE(TestParse(input)); 341 342 Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo"); 343 ASSERT_NE(nullptr, flag_attr); 344 EXPECT_EQ(flag_attr->type_mask, android::ResTable_map::TYPE_FLAGS); 345 ASSERT_EQ(flag_attr->symbols.size(), 3u); 346 347 AAPT_ASSERT_TRUE(flag_attr->symbols[0].symbol.name); 348 EXPECT_EQ(flag_attr->symbols[0].symbol.name.value().entry, "bar"); 349 EXPECT_EQ(flag_attr->symbols[0].value, 0u); 350 351 AAPT_ASSERT_TRUE(flag_attr->symbols[1].symbol.name); 352 EXPECT_EQ(flag_attr->symbols[1].symbol.name.value().entry, "bat"); 353 EXPECT_EQ(flag_attr->symbols[1].value, 1u); 354 355 AAPT_ASSERT_TRUE(flag_attr->symbols[2].symbol.name); 356 EXPECT_EQ(flag_attr->symbols[2].symbol.name.value().entry, "baz"); 357 EXPECT_EQ(flag_attr->symbols[2].value, 2u); 358 359 std::unique_ptr<BinaryPrimitive> flag_value = 360 ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat"); 361 ASSERT_NE(nullptr, flag_value); 362 EXPECT_EQ(flag_value->value.data, 1u | 2u); 363} 364 365TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) { 366 std::string input = 367 "<attr name=\"foo\">\n" 368 " <enum name=\"bar\" value=\"0\"/>\n" 369 " <enum name=\"bat\" value=\"1\"/>\n" 370 " <enum name=\"bat\" value=\"2\"/>\n" 371 "</attr>"; 372 ASSERT_FALSE(TestParse(input)); 373} 374 375TEST_F(ResourceParserTest, ParseStyle) { 376 std::string input = 377 "<style name=\"foo\" parent=\"@style/fu\">\n" 378 " <item name=\"bar\">#ffffffff</item>\n" 379 " <item name=\"bat\">@string/hey</item>\n" 380 " <item name=\"baz\"><b>hey</b></item>\n" 381 "</style>"; 382 ASSERT_TRUE(TestParse(input)); 383 384 Style* style = test::GetValue<Style>(&table_, "style/foo"); 385 ASSERT_NE(nullptr, style); 386 AAPT_ASSERT_TRUE(style->parent); 387 AAPT_ASSERT_TRUE(style->parent.value().name); 388 EXPECT_EQ(test::ParseNameOrDie("style/fu"), 389 style->parent.value().name.value()); 390 ASSERT_EQ(3u, style->entries.size()); 391 392 AAPT_ASSERT_TRUE(style->entries[0].key.name); 393 EXPECT_EQ(test::ParseNameOrDie("attr/bar"), 394 style->entries[0].key.name.value()); 395 396 AAPT_ASSERT_TRUE(style->entries[1].key.name); 397 EXPECT_EQ(test::ParseNameOrDie("attr/bat"), 398 style->entries[1].key.name.value()); 399 400 AAPT_ASSERT_TRUE(style->entries[2].key.name); 401 EXPECT_EQ(test::ParseNameOrDie("attr/baz"), 402 style->entries[2].key.name.value()); 403} 404 405TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) { 406 std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>"; 407 ASSERT_TRUE(TestParse(input)); 408 409 Style* style = test::GetValue<Style>(&table_, "style/foo"); 410 ASSERT_NE(nullptr, style); 411 AAPT_ASSERT_TRUE(style->parent); 412 AAPT_ASSERT_TRUE(style->parent.value().name); 413 EXPECT_EQ(test::ParseNameOrDie("com.app:style/Theme"), 414 style->parent.value().name.value()); 415} 416 417TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) { 418 std::string input = 419 "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n" 420 " name=\"foo\" parent=\"app:Theme\"/>"; 421 ASSERT_TRUE(TestParse(input)); 422 423 Style* style = test::GetValue<Style>(&table_, "style/foo"); 424 ASSERT_NE(nullptr, style); 425 AAPT_ASSERT_TRUE(style->parent); 426 AAPT_ASSERT_TRUE(style->parent.value().name); 427 EXPECT_EQ(test::ParseNameOrDie("android:style/Theme"), 428 style->parent.value().name.value()); 429} 430 431TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) { 432 std::string input = 433 "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" " 434 "name=\"foo\">\n" 435 " <item name=\"app:bar\">0</item>\n" 436 "</style>"; 437 ASSERT_TRUE(TestParse(input)); 438 439 Style* style = test::GetValue<Style>(&table_, "style/foo"); 440 ASSERT_NE(nullptr, style); 441 ASSERT_EQ(1u, style->entries.size()); 442 EXPECT_EQ(test::ParseNameOrDie("android:attr/bar"), 443 style->entries[0].key.name.value()); 444} 445 446TEST_F(ResourceParserTest, ParseStyleWithInferredParent) { 447 std::string input = "<style name=\"foo.bar\"/>"; 448 ASSERT_TRUE(TestParse(input)); 449 450 Style* style = test::GetValue<Style>(&table_, "style/foo.bar"); 451 ASSERT_NE(nullptr, style); 452 AAPT_ASSERT_TRUE(style->parent); 453 AAPT_ASSERT_TRUE(style->parent.value().name); 454 EXPECT_EQ(style->parent.value().name.value(), 455 test::ParseNameOrDie("style/foo")); 456 EXPECT_TRUE(style->parent_inferred); 457} 458 459TEST_F(ResourceParserTest, 460 ParseStyleWithInferredParentOverridenByEmptyParentAttribute) { 461 std::string input = "<style name=\"foo.bar\" parent=\"\"/>"; 462 ASSERT_TRUE(TestParse(input)); 463 464 Style* style = test::GetValue<Style>(&table_, "style/foo.bar"); 465 ASSERT_NE(nullptr, style); 466 AAPT_EXPECT_FALSE(style->parent); 467 EXPECT_FALSE(style->parent_inferred); 468} 469 470TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) { 471 std::string input = 472 R"EOF(<style name="foo" parent="*android:style/bar" />)EOF"; 473 ASSERT_TRUE(TestParse(input)); 474 475 Style* style = test::GetValue<Style>(&table_, "style/foo"); 476 ASSERT_NE(nullptr, style); 477 AAPT_ASSERT_TRUE(style->parent); 478 EXPECT_TRUE(style->parent.value().private_reference); 479} 480 481TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) { 482 std::string input = "<string name=\"foo\">@+id/bar</string>"; 483 ASSERT_TRUE(TestParse(input)); 484 485 Id* id = test::GetValue<Id>(&table_, "id/bar"); 486 ASSERT_NE(id, nullptr); 487} 488 489TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { 490 std::string input = 491 "<declare-styleable name=\"foo\">\n" 492 " <attr name=\"bar\" />\n" 493 " <attr name=\"bat\" format=\"string|reference\"/>\n" 494 " <attr name=\"baz\">\n" 495 " <enum name=\"foo\" value=\"1\"/>\n" 496 " </attr>\n" 497 "</declare-styleable>"; 498 ASSERT_TRUE(TestParse(input)); 499 500 Maybe<ResourceTable::SearchResult> result = 501 table_.FindResource(test::ParseNameOrDie("styleable/foo")); 502 AAPT_ASSERT_TRUE(result); 503 EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state); 504 505 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar"); 506 ASSERT_NE(attr, nullptr); 507 EXPECT_TRUE(attr->IsWeak()); 508 509 attr = test::GetValue<Attribute>(&table_, "attr/bat"); 510 ASSERT_NE(attr, nullptr); 511 EXPECT_TRUE(attr->IsWeak()); 512 513 attr = test::GetValue<Attribute>(&table_, "attr/baz"); 514 ASSERT_NE(attr, nullptr); 515 EXPECT_TRUE(attr->IsWeak()); 516 EXPECT_EQ(1u, attr->symbols.size()); 517 518 EXPECT_NE(nullptr, test::GetValue<Id>(&table_, "id/foo")); 519 520 Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo"); 521 ASSERT_NE(styleable, nullptr); 522 ASSERT_EQ(3u, styleable->entries.size()); 523 524 EXPECT_EQ(test::ParseNameOrDie("attr/bar"), 525 styleable->entries[0].name.value()); 526 EXPECT_EQ(test::ParseNameOrDie("attr/bat"), 527 styleable->entries[1].name.value()); 528} 529 530TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { 531 std::string input = 532 "<declare-styleable name=\"foo\" " 533 "xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n" 534 " <attr name=\"*android:bar\" />\n" 535 " <attr name=\"privAndroid:bat\" />\n" 536 "</declare-styleable>"; 537 ASSERT_TRUE(TestParse(input)); 538 Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo"); 539 ASSERT_NE(nullptr, styleable); 540 ASSERT_EQ(2u, styleable->entries.size()); 541 542 EXPECT_TRUE(styleable->entries[0].private_reference); 543 AAPT_ASSERT_TRUE(styleable->entries[0].name); 544 EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package); 545 546 EXPECT_TRUE(styleable->entries[1].private_reference); 547 AAPT_ASSERT_TRUE(styleable->entries[1].name); 548 EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package); 549} 550 551TEST_F(ResourceParserTest, ParseArray) { 552 std::string input = 553 "<array name=\"foo\">\n" 554 " <item>@string/ref</item>\n" 555 " <item>hey</item>\n" 556 " <item>23</item>\n" 557 "</array>"; 558 ASSERT_TRUE(TestParse(input)); 559 560 Array* array = test::GetValue<Array>(&table_, "array/foo"); 561 ASSERT_NE(array, nullptr); 562 ASSERT_EQ(3u, array->items.size()); 563 564 EXPECT_NE(nullptr, ValueCast<Reference>(array->items[0].get())); 565 EXPECT_NE(nullptr, ValueCast<String>(array->items[1].get())); 566 EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(array->items[2].get())); 567} 568 569TEST_F(ResourceParserTest, ParseStringArray) { 570 std::string input = 571 "<string-array name=\"foo\">\n" 572 " <item>\"Werk\"</item>\n" 573 "</string-array>\n"; 574 ASSERT_TRUE(TestParse(input)); 575 EXPECT_NE(nullptr, test::GetValue<Array>(&table_, "array/foo")); 576} 577 578TEST_F(ResourceParserTest, ParsePlural) { 579 std::string input = 580 "<plurals name=\"foo\">\n" 581 " <item quantity=\"other\">apples</item>\n" 582 " <item quantity=\"one\">apple</item>\n" 583 "</plurals>"; 584 ASSERT_TRUE(TestParse(input)); 585} 586 587TEST_F(ResourceParserTest, ParseCommentsWithResource) { 588 std::string input = 589 "<!--This is a comment-->\n" 590 "<string name=\"foo\">Hi</string>"; 591 ASSERT_TRUE(TestParse(input)); 592 593 String* value = test::GetValue<String>(&table_, "string/foo"); 594 ASSERT_NE(nullptr, value); 595 EXPECT_EQ(value->GetComment(), "This is a comment"); 596} 597 598TEST_F(ResourceParserTest, DoNotCombineMultipleComments) { 599 std::string input = 600 "<!--One-->\n" 601 "<!--Two-->\n" 602 "<string name=\"foo\">Hi</string>"; 603 604 ASSERT_TRUE(TestParse(input)); 605 606 String* value = test::GetValue<String>(&table_, "string/foo"); 607 ASSERT_NE(nullptr, value); 608 EXPECT_EQ(value->GetComment(), "Two"); 609} 610 611TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) { 612 std::string input = 613 "<!--One-->\n" 614 "<string name=\"foo\">\n" 615 " Hi\n" 616 "<!--Two-->\n" 617 "</string>"; 618 619 ASSERT_TRUE(TestParse(input)); 620 621 String* value = test::GetValue<String>(&table_, "string/foo"); 622 ASSERT_NE(nullptr, value); 623 EXPECT_EQ(value->GetComment(), "One"); 624} 625 626TEST_F(ResourceParserTest, ParseNestedComments) { 627 // We only care about declare-styleable and enum/flag attributes because 628 // comments 629 // from those end up in R.java 630 std::string input = R"EOF( 631 <declare-styleable name="foo"> 632 <!-- The name of the bar --> 633 <attr name="barName" format="string|reference" /> 634 </declare-styleable> 635 636 <attr name="foo"> 637 <!-- The very first --> 638 <enum name="one" value="1" /> 639 </attr>)EOF"; 640 ASSERT_TRUE(TestParse(input)); 641 642 Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo"); 643 ASSERT_NE(nullptr, styleable); 644 ASSERT_EQ(1u, styleable->entries.size()); 645 646 EXPECT_EQ(StringPiece("The name of the bar"), 647 styleable->entries.front().GetComment()); 648 649 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 650 ASSERT_NE(nullptr, attr); 651 ASSERT_EQ(1u, attr->symbols.size()); 652 653 EXPECT_EQ(StringPiece("The very first"), 654 attr->symbols.front().symbol.GetComment()); 655} 656 657/* 658 * Declaring an ID as public should not require a separate definition 659 * (as an ID has no value). 660 */ 661TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) { 662 std::string input = "<public type=\"id\" name=\"foo\"/>"; 663 ASSERT_TRUE(TestParse(input)); 664 665 Id* id = test::GetValue<Id>(&table_, "id/foo"); 666 ASSERT_NE(nullptr, id); 667} 668 669TEST_F(ResourceParserTest, KeepAllProducts) { 670 std::string input = R"EOF( 671 <string name="foo" product="phone">hi</string> 672 <string name="foo" product="no-sdcard">ho</string> 673 <string name="bar" product="">wee</string> 674 <string name="baz">woo</string> 675 <string name="bit" product="phablet">hoot</string> 676 <string name="bot" product="default">yes</string> 677 )EOF"; 678 ASSERT_TRUE(TestParse(input)); 679 680 EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>( 681 &table_, "string/foo", 682 ConfigDescription::DefaultConfig(), "phone")); 683 EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>( 684 &table_, "string/foo", 685 ConfigDescription::DefaultConfig(), "no-sdcard")); 686 EXPECT_NE(nullptr, 687 test::GetValueForConfigAndProduct<String>( 688 &table_, "string/bar", ConfigDescription::DefaultConfig(), "")); 689 EXPECT_NE(nullptr, 690 test::GetValueForConfigAndProduct<String>( 691 &table_, "string/baz", ConfigDescription::DefaultConfig(), "")); 692 EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>( 693 &table_, "string/bit", 694 ConfigDescription::DefaultConfig(), "phablet")); 695 EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>( 696 &table_, "string/bot", 697 ConfigDescription::DefaultConfig(), "default")); 698} 699 700TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) { 701 std::string input = R"EOF( 702 <public-group type="attr" first-id="0x01010040"> 703 <public name="foo" /> 704 <public name="bar" /> 705 </public-group>)EOF"; 706 ASSERT_TRUE(TestParse(input)); 707 708 Maybe<ResourceTable::SearchResult> result = 709 table_.FindResource(test::ParseNameOrDie("attr/foo")); 710 AAPT_ASSERT_TRUE(result); 711 712 AAPT_ASSERT_TRUE(result.value().package->id); 713 AAPT_ASSERT_TRUE(result.value().type->id); 714 AAPT_ASSERT_TRUE(result.value().entry->id); 715 ResourceId actual_id(result.value().package->id.value(), 716 result.value().type->id.value(), 717 result.value().entry->id.value()); 718 EXPECT_EQ(ResourceId(0x01010040), actual_id); 719 720 result = table_.FindResource(test::ParseNameOrDie("attr/bar")); 721 AAPT_ASSERT_TRUE(result); 722 723 AAPT_ASSERT_TRUE(result.value().package->id); 724 AAPT_ASSERT_TRUE(result.value().type->id); 725 AAPT_ASSERT_TRUE(result.value().entry->id); 726 actual_id = ResourceId(result.value().package->id.value(), 727 result.value().type->id.value(), 728 result.value().entry->id.value()); 729 EXPECT_EQ(ResourceId(0x01010041), actual_id); 730} 731 732TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) { 733 std::string input = 734 R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF"; 735 ASSERT_TRUE(TestParse(input)); 736 737 input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF"; 738 ASSERT_FALSE(TestParse(input)); 739} 740 741TEST_F(ResourceParserTest, 742 AddResourcesElementShouldAddEntryWithUndefinedSymbol) { 743 std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF"; 744 ASSERT_TRUE(TestParse(input)); 745 746 Maybe<ResourceTable::SearchResult> result = 747 table_.FindResource(test::ParseNameOrDie("string/bar")); 748 AAPT_ASSERT_TRUE(result); 749 const ResourceEntry* entry = result.value().entry; 750 ASSERT_NE(nullptr, entry); 751 EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state); 752} 753 754TEST_F(ResourceParserTest, ParseItemElementWithFormat) { 755 std::string input = 756 R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF"; 757 ASSERT_TRUE(TestParse(input)); 758 759 BinaryPrimitive* val = 760 test::GetValue<BinaryPrimitive>(&table_, "integer/foo"); 761 ASSERT_NE(nullptr, val); 762 763 EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType); 764} 765 766TEST_F(ResourceParserTest, ParseConfigVaryingItem) { 767 std::string input = R"EOF(<item name="foo" type="configVarying">Hey</item>)EOF"; 768 ASSERT_TRUE(TestParse(input)); 769 ASSERT_NE(nullptr, test::GetValue<String>(&table_, "configVarying/foo")); 770} 771 772TEST_F(ResourceParserTest, ParseBagElement) { 773 std::string input = 774 R"EOF(<bag name="bag" type="configVarying"><item name="test">Hello!</item></bag>)EOF"; 775 ASSERT_TRUE(TestParse(input)); 776 777 Style* val = test::GetValue<Style>(&table_, "configVarying/bag"); 778 ASSERT_NE(nullptr, val); 779 780 ASSERT_EQ(1u, val->entries.size()); 781 EXPECT_EQ(Reference(test::ParseNameOrDie("attr/test")), val->entries[0].key); 782 EXPECT_NE(nullptr, ValueCast<RawString>(val->entries[0].value.get())); 783} 784 785} // namespace aapt 786