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