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