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