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