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