ResourceParser_test.cpp revision 467f171315f9c2037fcd3eb5edcfabc40671bf7b
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, ParseUseAndDeclOfAttr) { 142 std::string input = "<declare-styleable name=\"Styleable\">\n" 143 " <attr name=\"foo\" />\n" 144 "</declare-styleable>\n" 145 "<attr name=\"foo\" format=\"string\"/>"; 146 ASSERT_TRUE(testParse(input)); 147 148 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); 149 ASSERT_NE(nullptr, attr); 150 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask); 151} 152 153TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) { 154 std::string input = "<declare-styleable name=\"Theme\">" 155 " <attr name=\"foo\" />\n" 156 "</declare-styleable>\n" 157 "<declare-styleable name=\"Window\">\n" 158 " <attr name=\"foo\" format=\"boolean\"/>\n" 159 "</declare-styleable>"; 160 ASSERT_TRUE(testParse(input)); 161 162 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); 163 ASSERT_NE(nullptr, attr); 164 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask); 165} 166 167TEST_F(ResourceParserTest, ParseEnumAttr) { 168 std::string input = "<attr name=\"foo\">\n" 169 " <enum name=\"bar\" value=\"0\"/>\n" 170 " <enum name=\"bat\" value=\"1\"/>\n" 171 " <enum name=\"baz\" value=\"2\"/>\n" 172 "</attr>"; 173 ASSERT_TRUE(testParse(input)); 174 175 Attribute* enumAttr = test::getValue<Attribute>(&mTable, u"@attr/foo"); 176 ASSERT_NE(enumAttr, nullptr); 177 EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM); 178 ASSERT_EQ(enumAttr->symbols.size(), 3u); 179 180 AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name); 181 EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, u"bar"); 182 EXPECT_EQ(enumAttr->symbols[0].value, 0u); 183 184 AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name); 185 EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, u"bat"); 186 EXPECT_EQ(enumAttr->symbols[1].value, 1u); 187 188 AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name); 189 EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, u"baz"); 190 EXPECT_EQ(enumAttr->symbols[2].value, 2u); 191} 192 193TEST_F(ResourceParserTest, ParseFlagAttr) { 194 std::string input = "<attr name=\"foo\">\n" 195 " <flag name=\"bar\" value=\"0\"/>\n" 196 " <flag name=\"bat\" value=\"1\"/>\n" 197 " <flag name=\"baz\" value=\"2\"/>\n" 198 "</attr>"; 199 ASSERT_TRUE(testParse(input)); 200 201 Attribute* flagAttr = test::getValue<Attribute>(&mTable, u"@attr/foo"); 202 ASSERT_NE(flagAttr, nullptr); 203 EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS); 204 ASSERT_EQ(flagAttr->symbols.size(), 3u); 205 206 AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name); 207 EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, u"bar"); 208 EXPECT_EQ(flagAttr->symbols[0].value, 0u); 209 210 AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name); 211 EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, u"bat"); 212 EXPECT_EQ(flagAttr->symbols[1].value, 1u); 213 214 AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name); 215 EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, u"baz"); 216 EXPECT_EQ(flagAttr->symbols[2].value, 2u); 217 218 std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr, 219 u"baz|bat"); 220 ASSERT_NE(flagValue, nullptr); 221 EXPECT_EQ(flagValue->value.data, 1u | 2u); 222} 223 224TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) { 225 std::string input = "<attr name=\"foo\">\n" 226 " <enum name=\"bar\" value=\"0\"/>\n" 227 " <enum name=\"bat\" value=\"1\"/>\n" 228 " <enum name=\"bat\" value=\"2\"/>\n" 229 "</attr>"; 230 ASSERT_FALSE(testParse(input)); 231} 232 233TEST_F(ResourceParserTest, ParseStyle) { 234 std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n" 235 " <item name=\"bar\">#ffffffff</item>\n" 236 " <item name=\"bat\">@string/hey</item>\n" 237 " <item name=\"baz\"><b>hey</b></item>\n" 238 "</style>"; 239 ASSERT_TRUE(testParse(input)); 240 241 Style* style = test::getValue<Style>(&mTable, u"@style/foo"); 242 ASSERT_NE(style, nullptr); 243 AAPT_ASSERT_TRUE(style->parent); 244 AAPT_ASSERT_TRUE(style->parent.value().name); 245 EXPECT_EQ(test::parseNameOrDie(u"@style/fu"), style->parent.value().name.value()); 246 ASSERT_EQ(3u, style->entries.size()); 247 248 AAPT_ASSERT_TRUE(style->entries[0].key.name); 249 EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), style->entries[0].key.name.value()); 250 251 AAPT_ASSERT_TRUE(style->entries[1].key.name); 252 EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), style->entries[1].key.name.value()); 253 254 AAPT_ASSERT_TRUE(style->entries[2].key.name); 255 EXPECT_EQ(test::parseNameOrDie(u"@attr/baz"), style->entries[2].key.name.value()); 256} 257 258TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) { 259 std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>"; 260 ASSERT_TRUE(testParse(input)); 261 262 Style* style = test::getValue<Style>(&mTable, u"@style/foo"); 263 ASSERT_NE(style, nullptr); 264 AAPT_ASSERT_TRUE(style->parent); 265 AAPT_ASSERT_TRUE(style->parent.value().name); 266 EXPECT_EQ(test::parseNameOrDie(u"@com.app:style/Theme"), style->parent.value().name.value()); 267} 268 269TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) { 270 std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n" 271 " name=\"foo\" parent=\"app:Theme\"/>"; 272 ASSERT_TRUE(testParse(input)); 273 274 Style* style = test::getValue<Style>(&mTable, u"@style/foo"); 275 ASSERT_NE(style, nullptr); 276 AAPT_ASSERT_TRUE(style->parent); 277 AAPT_ASSERT_TRUE(style->parent.value().name); 278 EXPECT_EQ(test::parseNameOrDie(u"@android:style/Theme"), style->parent.value().name.value()); 279} 280 281TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) { 282 std::string input = 283 "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n" 284 " <item name=\"app:bar\">0</item>\n" 285 "</style>"; 286 ASSERT_TRUE(testParse(input)); 287 288 Style* style = test::getValue<Style>(&mTable, u"@style/foo"); 289 ASSERT_NE(style, nullptr); 290 ASSERT_EQ(1u, style->entries.size()); 291 EXPECT_EQ(test::parseNameOrDie(u"@android:attr/bar"), style->entries[0].key.name.value()); 292} 293 294TEST_F(ResourceParserTest, ParseStyleWithInferredParent) { 295 std::string input = "<style name=\"foo.bar\"/>"; 296 ASSERT_TRUE(testParse(input)); 297 298 Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar"); 299 ASSERT_NE(style, nullptr); 300 AAPT_ASSERT_TRUE(style->parent); 301 AAPT_ASSERT_TRUE(style->parent.value().name); 302 EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie(u"@style/foo")); 303 EXPECT_TRUE(style->parentInferred); 304} 305 306TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) { 307 std::string input = "<style name=\"foo.bar\" parent=\"\"/>"; 308 ASSERT_TRUE(testParse(input)); 309 310 Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar"); 311 ASSERT_NE(style, nullptr); 312 AAPT_EXPECT_FALSE(style->parent); 313 EXPECT_FALSE(style->parentInferred); 314} 315 316TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) { 317 std::string input = "<string name=\"foo\">@+id/bar</string>"; 318 ASSERT_TRUE(testParse(input)); 319 320 Id* id = test::getValue<Id>(&mTable, u"@id/bar"); 321 ASSERT_NE(id, nullptr); 322} 323 324TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { 325 std::string input = "<declare-styleable name=\"foo\">\n" 326 " <attr name=\"bar\" />\n" 327 " <attr name=\"bat\" format=\"string|reference\"/>\n" 328 " <attr name=\"baz\">\n" 329 " <enum name=\"foo\" value=\"1\"/>\n" 330 " </attr>\n" 331 "</declare-styleable>"; 332 ASSERT_TRUE(testParse(input)); 333 334 Maybe<ResourceTable::SearchResult> result = 335 mTable.findResource(test::parseNameOrDie(u"@styleable/foo")); 336 AAPT_ASSERT_TRUE(result); 337 EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); 338 339 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar"); 340 ASSERT_NE(attr, nullptr); 341 EXPECT_TRUE(attr->isWeak()); 342 343 attr = test::getValue<Attribute>(&mTable, u"@attr/bat"); 344 ASSERT_NE(attr, nullptr); 345 EXPECT_TRUE(attr->isWeak()); 346 347 attr = test::getValue<Attribute>(&mTable, u"@attr/baz"); 348 ASSERT_NE(attr, nullptr); 349 EXPECT_TRUE(attr->isWeak()); 350 EXPECT_EQ(1u, attr->symbols.size()); 351 352 EXPECT_NE(nullptr, test::getValue<Id>(&mTable, u"@id/foo")); 353 354 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); 355 ASSERT_NE(styleable, nullptr); 356 ASSERT_EQ(3u, styleable->entries.size()); 357 358 EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), styleable->entries[0].name.value()); 359 EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value()); 360} 361 362TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { 363 std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n" 364 " <attr name=\"*android:bar\" />\n" 365 " <attr name=\"privAndroid:bat\" />\n" 366 "</declare-styleable>"; 367 ASSERT_TRUE(testParse(input)); 368 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); 369 ASSERT_NE(nullptr, styleable); 370 ASSERT_EQ(2u, styleable->entries.size()); 371 372 EXPECT_TRUE(styleable->entries[0].privateReference); 373 AAPT_ASSERT_TRUE(styleable->entries[0].name); 374 EXPECT_EQ(std::u16string(u"android"), styleable->entries[0].name.value().package); 375 376 EXPECT_TRUE(styleable->entries[1].privateReference); 377 AAPT_ASSERT_TRUE(styleable->entries[1].name); 378 EXPECT_EQ(std::u16string(u"android"), styleable->entries[1].name.value().package); 379} 380 381TEST_F(ResourceParserTest, ParseArray) { 382 std::string input = "<array name=\"foo\">\n" 383 " <item>@string/ref</item>\n" 384 " <item>hey</item>\n" 385 " <item>23</item>\n" 386 "</array>"; 387 ASSERT_TRUE(testParse(input)); 388 389 Array* array = test::getValue<Array>(&mTable, u"@array/foo"); 390 ASSERT_NE(array, nullptr); 391 ASSERT_EQ(3u, array->items.size()); 392 393 EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get())); 394 EXPECT_NE(nullptr, valueCast<String>(array->items[1].get())); 395 EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get())); 396} 397 398TEST_F(ResourceParserTest, ParseStringArray) { 399 std::string input = "<string-array name=\"foo\">\n" 400 " <item>\"Werk\"</item>\n" 401 "</string-array>\n"; 402 ASSERT_TRUE(testParse(input)); 403 EXPECT_NE(nullptr, test::getValue<Array>(&mTable, u"@array/foo")); 404} 405 406TEST_F(ResourceParserTest, ParsePlural) { 407 std::string input = "<plurals name=\"foo\">\n" 408 " <item quantity=\"other\">apples</item>\n" 409 " <item quantity=\"one\">apple</item>\n" 410 "</plurals>"; 411 ASSERT_TRUE(testParse(input)); 412} 413 414TEST_F(ResourceParserTest, ParseCommentsWithResource) { 415 std::string input = "<!--This is a comment-->\n" 416 "<string name=\"foo\">Hi</string>"; 417 ASSERT_TRUE(testParse(input)); 418 419 String* value = test::getValue<String>(&mTable, u"@string/foo"); 420 ASSERT_NE(nullptr, value); 421 EXPECT_EQ(value->getComment(), u"This is a comment"); 422} 423 424TEST_F(ResourceParserTest, DoNotCombineMultipleComments) { 425 std::string input = "<!--One-->\n" 426 "<!--Two-->\n" 427 "<string name=\"foo\">Hi</string>"; 428 429 ASSERT_TRUE(testParse(input)); 430 431 String* value = test::getValue<String>(&mTable, u"@string/foo"); 432 ASSERT_NE(nullptr, value); 433 EXPECT_EQ(value->getComment(), u"Two"); 434} 435 436TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) { 437 std::string input = "<!--One-->\n" 438 "<string name=\"foo\">\n" 439 " Hi\n" 440 "<!--Two-->\n" 441 "</string>"; 442 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"One"); 448} 449 450TEST_F(ResourceParserTest, ParseNestedComments) { 451 // We only care about declare-styleable and enum/flag attributes because comments 452 // from those end up in R.java 453 std::string input = R"EOF( 454 <declare-styleable name="foo"> 455 <!-- The name of the bar --> 456 <attr name="barName" format="string|reference" /> 457 </declare-styleable> 458 459 <attr name="foo"> 460 <!-- The very first --> 461 <enum name="one" value="1" /> 462 </attr>)EOF"; 463 ASSERT_TRUE(testParse(input)); 464 465 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); 466 ASSERT_NE(nullptr, styleable); 467 ASSERT_EQ(1u, styleable->entries.size()); 468 469 EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment()); 470 471 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); 472 ASSERT_NE(nullptr, attr); 473 ASSERT_EQ(1u, attr->symbols.size()); 474 475 EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment()); 476} 477 478/* 479 * Declaring an ID as public should not require a separate definition 480 * (as an ID has no value). 481 */ 482TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) { 483 std::string input = "<public type=\"id\" name=\"foo\"/>"; 484 ASSERT_TRUE(testParse(input)); 485 486 Id* id = test::getValue<Id>(&mTable, u"@id/foo"); 487 ASSERT_NE(nullptr, id); 488} 489 490TEST_F(ResourceParserTest, FilterProductsThatDontMatch) { 491 std::string input = "<string name=\"foo\" product=\"phone\">hi</string>\n" 492 "<string name=\"foo\" product=\"no-sdcard\">ho</string>\n" 493 "<string name=\"bar\" product=\"\">wee</string>\n" 494 "<string name=\"baz\">woo</string>\n"; 495 ASSERT_TRUE(testParse(input, std::u16string(u"no-sdcard"))); 496 497 String* fooStr = test::getValue<String>(&mTable, u"@string/foo"); 498 ASSERT_NE(nullptr, fooStr); 499 EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value); 500 501 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar")); 502 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz")); 503} 504 505TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) { 506 std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n"; 507 ASSERT_FALSE(testParse(input, std::u16string(u"phone"))); 508} 509 510TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) { 511 std::string input = R"EOF( 512 <public-group type="attr" first-id="0x01010040"> 513 <public name="foo" /> 514 <public name="bar" /> 515 </public-group>)EOF"; 516 ASSERT_TRUE(testParse(input)); 517 518 Maybe<ResourceTable::SearchResult> result = mTable.findResource( 519 test::parseNameOrDie(u"@attr/foo")); 520 AAPT_ASSERT_TRUE(result); 521 522 AAPT_ASSERT_TRUE(result.value().package->id); 523 AAPT_ASSERT_TRUE(result.value().type->id); 524 AAPT_ASSERT_TRUE(result.value().entry->id); 525 ResourceId actualId(result.value().package->id.value(), 526 result.value().type->id.value(), 527 result.value().entry->id.value()); 528 EXPECT_EQ(ResourceId(0x01010040), actualId); 529 530 result = mTable.findResource(test::parseNameOrDie(u"@attr/bar")); 531 AAPT_ASSERT_TRUE(result); 532 533 AAPT_ASSERT_TRUE(result.value().package->id); 534 AAPT_ASSERT_TRUE(result.value().type->id); 535 AAPT_ASSERT_TRUE(result.value().entry->id); 536 actualId = ResourceId(result.value().package->id.value(), 537 result.value().type->id.value(), 538 result.value().entry->id.value()); 539 EXPECT_EQ(ResourceId(0x01010041), actualId); 540} 541 542TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) { 543 std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF"; 544 ASSERT_TRUE(testParse(input)); 545 546 input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF"; 547 ASSERT_FALSE(testParse(input)); 548} 549 550} // namespace aapt 551