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