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 29 using ::aapt::io::StringInputStream; 30 using ::aapt::test::StrValueEq; 31 using ::aapt::test::ValueEq; 32 using ::android::Res_value; 33 using ::android::ResTable_map; 34 using ::android::StringPiece; 35 using ::testing::Eq; 36 using ::testing::IsEmpty; 37 using ::testing::IsNull; 38 using ::testing::NotNull; 39 using ::testing::Pointee; 40 using ::testing::SizeIs; 41 using ::testing::StrEq; 42 43 namespace aapt { 44 45 constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 46 47 TEST(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 59 class 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 91 TEST_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 110 TEST_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 isnt a bad string")); 122 } 123 124 TEST_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 129 TEST_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 152 TEST_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 167 TEST_F(ResourceParserTest, ParseStringTruncateASCII) { 168 // Tuncate leading and trailing whitespace 169 EXPECT_TRUE(TestParse(R"(<string name="foo"> Hello </string>)")); 170 171 String* str = test::GetValue<String>(&table_, "string/foo"); 172 ASSERT_THAT(str, NotNull()); 173 EXPECT_THAT(*str->value, StrEq("Hello")); 174 EXPECT_THAT(str->untranslatable_sections, IsEmpty()); 175 176 // AAPT does not truncate unicode whitespace 177 EXPECT_TRUE(TestParse(R"(<string name="foo2">\u0020\Hello\u0020</string>)")); 178 179 str = test::GetValue<String>(&table_, "string/foo2"); 180 ASSERT_THAT(str, NotNull()); 181 EXPECT_THAT(*str->value, StrEq(" Hello ")); 182 EXPECT_THAT(str->untranslatable_sections, IsEmpty()); 183 184 // Preserve non-ASCII whitespace including extended ASCII characters 185 EXPECT_TRUE(TestParse(R"(<string name="foo3"> Hello </string>)")); 186 187 str = test::GetValue<String>(&table_, "string/foo3"); 188 ASSERT_THAT(str, NotNull()); 189 EXPECT_THAT(*str->value, StrEq("\xC2\xA0Hello\xC2\xA0")); 190 EXPECT_THAT(str->untranslatable_sections, IsEmpty()); 191 192 EXPECT_TRUE(TestParse(R"(<string name="foo4">200561</string>)")); 193 194 str = test::GetValue<String>(&table_, "string/foo4"); 195 ASSERT_THAT(str, NotNull()); 196 EXPECT_THAT(*str->value, StrEq("200561")); 197 EXPECT_THAT(str->untranslatable_sections, IsEmpty()); 198 } 199 200 TEST_F(ResourceParserTest, ParseStyledStringWithWhitespace) { 201 std::string input = R"(<string name="foo"> <b> My <i> favorite</i> string </b> </string>)"; 202 ASSERT_TRUE(TestParse(input)); 203 204 StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); 205 ASSERT_THAT(str, NotNull()); 206 EXPECT_THAT(str->value->value, StrEq(" My favorite string ")); 207 EXPECT_THAT(str->untranslatable_sections, IsEmpty()); 208 209 ASSERT_THAT(str->value->spans, SizeIs(2u)); 210 EXPECT_THAT(*str->value->spans[0].name, StrEq("b")); 211 EXPECT_THAT(str->value->spans[0].first_char, Eq(1u)); 212 EXPECT_THAT(str->value->spans[0].last_char, Eq(21u)); 213 214 EXPECT_THAT(*str->value->spans[1].name, StrEq("i")); 215 EXPECT_THAT(str->value->spans[1].first_char, Eq(5u)); 216 EXPECT_THAT(str->value->spans[1].last_char, Eq(13u)); 217 } 218 219 TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) { 220 std::string input = R"( 221 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 222 There are <xliff:source>no</xliff:source> apples</string>)"; 223 ASSERT_TRUE(TestParse(input)); 224 225 String* str = test::GetValue<String>(&table_, "string/foo"); 226 ASSERT_THAT(str, NotNull()); 227 EXPECT_THAT(*str, StrValueEq("There are no apples")); 228 EXPECT_THAT(str->untranslatable_sections, IsEmpty()); 229 } 230 231 TEST_F(ResourceParserTest, NestedXliffGTagsAreIllegal) { 232 std::string input = R"( 233 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 234 Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)"; 235 EXPECT_FALSE(TestParse(input)); 236 } 237 238 TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInString) { 239 std::string input = R"( 240 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 241 There are <xliff:g id="count">%1$d</xliff:g> apples</string>)"; 242 ASSERT_TRUE(TestParse(input)); 243 244 String* str = test::GetValue<String>(&table_, "string/foo"); 245 ASSERT_THAT(str, NotNull()); 246 EXPECT_THAT(*str, StrValueEq("There are %1$d apples")); 247 248 ASSERT_THAT(str->untranslatable_sections, SizeIs(1)); 249 EXPECT_THAT(str->untranslatable_sections[0].start, Eq(10u)); 250 EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u)); 251 } 252 253 TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) { 254 std::string input = R"( 255 <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 256 There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)"; 257 ASSERT_TRUE(TestParse(input)); 258 259 StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); 260 ASSERT_THAT(str, NotNull()); 261 EXPECT_THAT(str->value->value, Eq(" There are %1$d apples")); 262 263 ASSERT_THAT(str->untranslatable_sections, SizeIs(1)); 264 EXPECT_THAT(str->untranslatable_sections[0].start, Eq(11u)); 265 EXPECT_THAT(str->untranslatable_sections[0].end, Eq(15u)); 266 267 ASSERT_THAT(str->value->spans, SizeIs(1u)); 268 EXPECT_THAT(*str->value->spans[0].name, StrEq("b")); 269 EXPECT_THAT(str->value->spans[0].first_char, Eq(11u)); 270 EXPECT_THAT(str->value->spans[0].last_char, Eq(14u)); 271 } 272 273 TEST_F(ResourceParserTest, ParseNull) { 274 std::string input = R"(<integer name="foo">@null</integer>)"; 275 ASSERT_TRUE(TestParse(input)); 276 277 // The Android runtime treats a value of android::Res_value::TYPE_NULL as 278 // a non-existing value, and this causes problems in styles when trying to 279 // resolve an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE 280 // with a data value of 0. 281 Reference* null_ref = test::GetValue<Reference>(&table_, "integer/foo"); 282 ASSERT_THAT(null_ref, NotNull()); 283 EXPECT_FALSE(null_ref->name); 284 EXPECT_FALSE(null_ref->id); 285 EXPECT_THAT(null_ref->reference_type, Eq(Reference::Type::kResource)); 286 } 287 288 TEST_F(ResourceParserTest, ParseEmpty) { 289 std::string input = R"(<integer name="foo">@empty</integer>)"; 290 ASSERT_TRUE(TestParse(input)); 291 292 BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo"); 293 ASSERT_THAT(integer, NotNull()); 294 EXPECT_THAT(integer->value.dataType, Eq(Res_value::TYPE_NULL)); 295 EXPECT_THAT(integer->value.data, Eq(Res_value::DATA_NULL_EMPTY)); 296 } 297 298 TEST_F(ResourceParserTest, ParseAttr) { 299 std::string input = R"( 300 <attr name="foo" format="string"/> 301 <attr name="bar"/>)"; 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_STRING)); 307 308 attr = test::GetValue<Attribute>(&table_, "attr/bar"); 309 ASSERT_THAT(attr, NotNull()); 310 EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY)); 311 } 312 313 // Old AAPT allowed attributes to be defined under different configurations, but ultimately 314 // stored them with the default configuration. Check that we have the same behavior. 315 TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) { 316 const ConfigDescription watch_config = test::ParseConfigOrDie("watch"); 317 std::string input = R"( 318 <attr name="foo" /> 319 <declare-styleable name="bar"> 320 <attr name="baz" /> 321 </declare-styleable>)"; 322 ASSERT_TRUE(TestParse(input, watch_config)); 323 324 EXPECT_THAT(test::GetValueForConfig<Attribute>(&table_, "attr/foo", watch_config), IsNull()); 325 EXPECT_THAT(test::GetValueForConfig<Attribute>(&table_, "attr/baz", watch_config), IsNull()); 326 EXPECT_THAT(test::GetValueForConfig<Styleable>(&table_, "styleable/bar", watch_config), IsNull()); 327 328 EXPECT_THAT(test::GetValue<Attribute>(&table_, "attr/foo"), NotNull()); 329 EXPECT_THAT(test::GetValue<Attribute>(&table_, "attr/baz"), NotNull()); 330 EXPECT_THAT(test::GetValue<Styleable>(&table_, "styleable/bar"), NotNull()); 331 } 332 333 TEST_F(ResourceParserTest, ParseAttrWithMinMax) { 334 std::string input = R"(<attr name="foo" min="10" max="23" format="integer"/>)"; 335 ASSERT_TRUE(TestParse(input)); 336 337 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 338 ASSERT_THAT(attr, NotNull()); 339 EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_INTEGER)); 340 EXPECT_THAT(attr->min_int, Eq(10)); 341 EXPECT_THAT(attr->max_int, Eq(23)); 342 } 343 344 TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) { 345 ASSERT_FALSE(TestParse(R"(<attr name="foo" min="10" max="23" format="string"/>)")); 346 } 347 348 TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) { 349 std::string input = R"( 350 <declare-styleable name="Styleable"> 351 <attr name="foo" /> 352 </declare-styleable> 353 <attr name="foo" format="string"/>)"; 354 ASSERT_TRUE(TestParse(input)); 355 356 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 357 ASSERT_THAT(attr, NotNull()); 358 EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_STRING)); 359 } 360 361 TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) { 362 std::string input = R"( 363 <declare-styleable name="Theme"> 364 <attr name="foo" /> 365 </declare-styleable> 366 <declare-styleable name="Window"> 367 <attr name="foo" format="boolean"/> 368 </declare-styleable>)"; 369 ASSERT_TRUE(TestParse(input)); 370 371 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 372 ASSERT_THAT(attr, NotNull()); 373 EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_BOOLEAN)); 374 } 375 376 TEST_F(ResourceParserTest, ParseEnumAttr) { 377 std::string input = R"( 378 <attr name="foo"> 379 <enum name="bar" value="0"/> 380 <enum name="bat" value="1"/> 381 <enum name="baz" value="2"/> 382 </attr>)"; 383 ASSERT_TRUE(TestParse(input)); 384 385 Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo"); 386 ASSERT_THAT(enum_attr, NotNull()); 387 EXPECT_THAT(enum_attr->type_mask, Eq(ResTable_map::TYPE_ENUM)); 388 ASSERT_THAT(enum_attr->symbols, SizeIs(3)); 389 390 ASSERT_TRUE(enum_attr->symbols[0].symbol.name); 391 EXPECT_THAT(enum_attr->symbols[0].symbol.name.value().entry, Eq("bar")); 392 EXPECT_THAT(enum_attr->symbols[0].value, Eq(0u)); 393 394 ASSERT_TRUE(enum_attr->symbols[1].symbol.name); 395 EXPECT_THAT(enum_attr->symbols[1].symbol.name.value().entry, Eq("bat")); 396 EXPECT_THAT(enum_attr->symbols[1].value, Eq(1u)); 397 398 ASSERT_TRUE(enum_attr->symbols[2].symbol.name); 399 EXPECT_THAT(enum_attr->symbols[2].symbol.name.value().entry, Eq("baz")); 400 EXPECT_THAT(enum_attr->symbols[2].value, Eq(2u)); 401 } 402 403 TEST_F(ResourceParserTest, ParseFlagAttr) { 404 std::string input = R"( 405 <attr name="foo"> 406 <flag name="bar" value="0"/> 407 <flag name="bat" value="1"/> 408 <flag name="baz" value="2"/> 409 </attr>)"; 410 ASSERT_TRUE(TestParse(input)); 411 412 Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo"); 413 ASSERT_THAT(flag_attr, NotNull()); 414 EXPECT_THAT(flag_attr->type_mask, Eq(ResTable_map::TYPE_FLAGS)); 415 ASSERT_THAT(flag_attr->symbols, SizeIs(3)); 416 417 ASSERT_TRUE(flag_attr->symbols[0].symbol.name); 418 EXPECT_THAT(flag_attr->symbols[0].symbol.name.value().entry, Eq("bar")); 419 EXPECT_THAT(flag_attr->symbols[0].value, Eq(0u)); 420 421 ASSERT_TRUE(flag_attr->symbols[1].symbol.name); 422 EXPECT_THAT(flag_attr->symbols[1].symbol.name.value().entry, Eq("bat")); 423 EXPECT_THAT(flag_attr->symbols[1].value, Eq(1u)); 424 425 ASSERT_TRUE(flag_attr->symbols[2].symbol.name); 426 EXPECT_THAT(flag_attr->symbols[2].symbol.name.value().entry, Eq("baz")); 427 EXPECT_THAT(flag_attr->symbols[2].value, Eq(2u)); 428 429 std::unique_ptr<BinaryPrimitive> flag_value = 430 ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat"); 431 ASSERT_THAT(flag_value, NotNull()); 432 EXPECT_THAT(flag_value->value.data, Eq(1u | 2u)); 433 } 434 435 TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) { 436 std::string input = R"( 437 <attr name="foo"> 438 <enum name="bar" value="0"/> 439 <enum name="bat" value="1"/> 440 <enum name="bat" value="2"/> 441 </attr>)"; 442 ASSERT_FALSE(TestParse(input)); 443 } 444 445 TEST_F(ResourceParserTest, ParseStyle) { 446 std::string input = R"( 447 <style name="foo" parent="@style/fu"> 448 <item name="bar">#ffffffff</item> 449 <item name="bat">@string/hey</item> 450 <item name="baz"><b>hey</b></item> 451 </style>)"; 452 ASSERT_TRUE(TestParse(input)); 453 454 Style* style = test::GetValue<Style>(&table_, "style/foo"); 455 ASSERT_THAT(style, NotNull()); 456 ASSERT_TRUE(style->parent); 457 EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/fu")))); 458 ASSERT_THAT(style->entries, SizeIs(3)); 459 460 EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("attr/bar")))); 461 EXPECT_THAT(style->entries[1].key.name, Eq(make_value(test::ParseNameOrDie("attr/bat")))); 462 EXPECT_THAT(style->entries[2].key.name, Eq(make_value(test::ParseNameOrDie("attr/baz")))); 463 } 464 465 TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) { 466 ASSERT_TRUE(TestParse(R"(<style name="foo" parent="com.app:Theme"/>)")); 467 468 Style* style = test::GetValue<Style>(&table_, "style/foo"); 469 ASSERT_THAT(style, NotNull()); 470 ASSERT_TRUE(style->parent); 471 EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("com.app:style/Theme")))); 472 } 473 474 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) { 475 std::string input = R"( 476 <style xmlns:app="http://schemas.android.com/apk/res/android" 477 name="foo" parent="app:Theme"/>)"; 478 ASSERT_TRUE(TestParse(input)); 479 480 Style* style = test::GetValue<Style>(&table_, "style/foo"); 481 ASSERT_THAT(style, NotNull()); 482 ASSERT_TRUE(style->parent); 483 ASSERT_TRUE(style->parent.value().name); 484 EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("android:style/Theme")))); 485 } 486 487 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) { 488 std::string input = R"( 489 <style xmlns:app="http://schemas.android.com/apk/res/android" name="foo"> 490 <item name="app:bar">0</item> 491 </style>)"; 492 ASSERT_TRUE(TestParse(input)); 493 494 Style* style = test::GetValue<Style>(&table_, "style/foo"); 495 ASSERT_THAT(style, NotNull()); 496 ASSERT_THAT(style->entries, SizeIs(1)); 497 EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar")))); 498 } 499 500 TEST_F(ResourceParserTest, ParseStyleWithInferredParent) { 501 ASSERT_TRUE(TestParse(R"(<style name="foo.bar"/>)")); 502 503 Style* style = test::GetValue<Style>(&table_, "style/foo.bar"); 504 ASSERT_THAT(style, NotNull()); 505 ASSERT_TRUE(style->parent); 506 EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/foo")))); 507 EXPECT_TRUE(style->parent_inferred); 508 } 509 510 TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) { 511 ASSERT_TRUE(TestParse(R"(<style name="foo.bar" parent=""/>)")); 512 513 Style* style = test::GetValue<Style>(&table_, "style/foo.bar"); 514 ASSERT_THAT(style, NotNull()); 515 EXPECT_FALSE(style->parent); 516 EXPECT_FALSE(style->parent_inferred); 517 } 518 519 TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) { 520 ASSERT_TRUE(TestParse(R"(<style name="foo" parent="*android:style/bar" />)")); 521 522 Style* style = test::GetValue<Style>(&table_, "style/foo"); 523 ASSERT_THAT(style, NotNull()); 524 ASSERT_TRUE(style->parent); 525 EXPECT_TRUE(style->parent.value().private_reference); 526 } 527 528 TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) { 529 ASSERT_TRUE(TestParse(R"(<string name="foo">@+id/bar</string>)")); 530 ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull()); 531 } 532 533 TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { 534 std::string input = R"( 535 <declare-styleable name="foo"> 536 <attr name="bar" /> 537 <attr name="bat" format="string|reference"/> 538 <attr name="baz"> 539 <enum name="foo" value="1"/> 540 </attr> 541 </declare-styleable>)"; 542 ASSERT_TRUE(TestParse(input)); 543 544 Maybe<ResourceTable::SearchResult> result = 545 table_.FindResource(test::ParseNameOrDie("styleable/foo")); 546 ASSERT_TRUE(result); 547 EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic)); 548 549 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar"); 550 ASSERT_THAT(attr, NotNull()); 551 EXPECT_TRUE(attr->IsWeak()); 552 553 attr = test::GetValue<Attribute>(&table_, "attr/bat"); 554 ASSERT_THAT(attr, NotNull()); 555 EXPECT_TRUE(attr->IsWeak()); 556 557 attr = test::GetValue<Attribute>(&table_, "attr/baz"); 558 ASSERT_THAT(attr, NotNull()); 559 EXPECT_TRUE(attr->IsWeak()); 560 EXPECT_THAT(attr->symbols, SizeIs(1)); 561 562 EXPECT_THAT(test::GetValue<Id>(&table_, "id/foo"), NotNull()); 563 564 Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo"); 565 ASSERT_THAT(styleable, NotNull()); 566 ASSERT_THAT(styleable->entries, SizeIs(3)); 567 568 EXPECT_THAT(styleable->entries[0].name, Eq(make_value(test::ParseNameOrDie("attr/bar")))); 569 EXPECT_THAT(styleable->entries[1].name, Eq(make_value(test::ParseNameOrDie("attr/bat")))); 570 EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz")))); 571 } 572 573 TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { 574 std::string input = R"( 575 <declare-styleable xmlns:privAndroid="http://schemas.android.com/apk/prv/res/android" 576 name="foo"> 577 <attr name="*android:bar" /> 578 <attr name="privAndroid:bat" /> 579 </declare-styleable>)"; 580 ASSERT_TRUE(TestParse(input)); 581 Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo"); 582 ASSERT_THAT(styleable, NotNull()); 583 ASSERT_THAT(styleable->entries, SizeIs(2)); 584 585 EXPECT_TRUE(styleable->entries[0].private_reference); 586 ASSERT_TRUE(styleable->entries[0].name); 587 EXPECT_THAT(styleable->entries[0].name.value().package, Eq("android")); 588 589 EXPECT_TRUE(styleable->entries[1].private_reference); 590 ASSERT_TRUE(styleable->entries[1].name); 591 EXPECT_THAT(styleable->entries[1].name.value().package, Eq("android")); 592 } 593 594 TEST_F(ResourceParserTest, ParseArray) { 595 std::string input = R"( 596 <array name="foo"> 597 <item>@string/ref</item> 598 <item>hey</item> 599 <item>23</item> 600 </array>)"; 601 ASSERT_TRUE(TestParse(input)); 602 603 Array* array = test::GetValue<Array>(&table_, "array/foo"); 604 ASSERT_THAT(array, NotNull()); 605 ASSERT_THAT(array->elements, SizeIs(3)); 606 607 EXPECT_THAT(ValueCast<Reference>(array->elements[0].get()), NotNull()); 608 EXPECT_THAT(ValueCast<String>(array->elements[1].get()), NotNull()); 609 EXPECT_THAT(ValueCast<BinaryPrimitive>(array->elements[2].get()), NotNull()); 610 } 611 612 TEST_F(ResourceParserTest, ParseStringArray) { 613 std::string input = R"( 614 <string-array name="foo"> 615 <item>"Werk"</item>" 616 </string-array>)"; 617 ASSERT_TRUE(TestParse(input)); 618 EXPECT_THAT(test::GetValue<Array>(&table_, "array/foo"), NotNull()); 619 } 620 621 TEST_F(ResourceParserTest, ParseArrayWithFormat) { 622 std::string input = R"( 623 <array name="foo" format="string"> 624 <item>100</item> 625 </array>)"; 626 ASSERT_TRUE(TestParse(input)); 627 628 Array* array = test::GetValue<Array>(&table_, "array/foo"); 629 ASSERT_THAT(array, NotNull()); 630 ASSERT_THAT(array->elements, SizeIs(1)); 631 632 String* str = ValueCast<String>(array->elements[0].get()); 633 ASSERT_THAT(str, NotNull()); 634 EXPECT_THAT(*str, StrValueEq("100")); 635 } 636 637 TEST_F(ResourceParserTest, ParseArrayWithBadFormat) { 638 std::string input = R"( 639 <array name="foo" format="integer"> 640 <item>Hi</item> 641 </array>)"; 642 ASSERT_FALSE(TestParse(input)); 643 } 644 645 TEST_F(ResourceParserTest, ParsePlural) { 646 std::string input = R"( 647 <plurals name="foo"> 648 <item quantity="other">apples</item> 649 <item quantity="one">apple</item> 650 </plurals>)"; 651 ASSERT_TRUE(TestParse(input)); 652 653 Plural* plural = test::GetValue<Plural>(&table_, "plurals/foo"); 654 ASSERT_THAT(plural, NotNull()); 655 EXPECT_THAT(plural->values[Plural::Zero], IsNull()); 656 EXPECT_THAT(plural->values[Plural::Two], IsNull()); 657 EXPECT_THAT(plural->values[Plural::Few], IsNull()); 658 EXPECT_THAT(plural->values[Plural::Many], IsNull()); 659 660 EXPECT_THAT(plural->values[Plural::One], NotNull()); 661 EXPECT_THAT(plural->values[Plural::Other], NotNull()); 662 } 663 664 TEST_F(ResourceParserTest, ParseCommentsWithResource) { 665 std::string input = R"( 666 <!--This is a comment--> 667 <string name="foo">Hi</string>)"; 668 ASSERT_TRUE(TestParse(input)); 669 670 String* value = test::GetValue<String>(&table_, "string/foo"); 671 ASSERT_THAT(value, NotNull()); 672 EXPECT_THAT(value->GetComment(), Eq("This is a comment")); 673 } 674 675 TEST_F(ResourceParserTest, DoNotCombineMultipleComments) { 676 std::string input = R"( 677 <!--One--> 678 <!--Two--> 679 <string name="foo">Hi</string>)"; 680 681 ASSERT_TRUE(TestParse(input)); 682 683 String* value = test::GetValue<String>(&table_, "string/foo"); 684 ASSERT_THAT(value, NotNull()); 685 EXPECT_THAT(value->GetComment(), Eq("Two")); 686 } 687 688 TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) { 689 std::string input = R"( 690 <!--One--> 691 <string name="foo"> 692 Hi 693 <!--Two--> 694 </string>)"; 695 ASSERT_TRUE(TestParse(input)); 696 697 String* value = test::GetValue<String>(&table_, "string/foo"); 698 ASSERT_THAT(value, NotNull()); 699 EXPECT_THAT(value->GetComment(), Eq("One")); 700 } 701 702 TEST_F(ResourceParserTest, ParseNestedComments) { 703 // We only care about declare-styleable and enum/flag attributes because 704 // comments from those end up in R.java 705 std::string input = R"( 706 <declare-styleable name="foo"> 707 <!-- The name of the bar --> 708 <attr name="barName" format="string|reference" /> 709 </declare-styleable> 710 711 <attr name="foo"> 712 <!-- The very first --> 713 <enum name="one" value="1" /> 714 </attr>)"; 715 ASSERT_TRUE(TestParse(input)); 716 717 Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo"); 718 ASSERT_THAT(styleable, NotNull()); 719 ASSERT_THAT(styleable->entries, SizeIs(1)); 720 EXPECT_THAT(styleable->entries[0].GetComment(), Eq("The name of the bar")); 721 722 Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo"); 723 ASSERT_THAT(attr, NotNull()); 724 ASSERT_THAT(attr->symbols, SizeIs(1)); 725 EXPECT_THAT(attr->symbols[0].symbol.GetComment(), Eq("The very first")); 726 } 727 728 // Declaring an ID as public should not require a separate definition (as an ID has no value). 729 TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) { 730 ASSERT_TRUE(TestParse(R"(<public type="id" name="foo"/>)")); 731 ASSERT_THAT(test::GetValue<Id>(&table_, "id/foo"), NotNull()); 732 } 733 734 TEST_F(ResourceParserTest, KeepAllProducts) { 735 std::string input = R"( 736 <string name="foo" product="phone">hi</string> 737 <string name="foo" product="no-sdcard">ho</string> 738 <string name="bar" product="">wee</string> 739 <string name="baz">woo</string> 740 <string name="bit" product="phablet">hoot</string> 741 <string name="bot" product="default">yes</string>)"; 742 ASSERT_TRUE(TestParse(input)); 743 744 ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/foo", ConfigDescription::DefaultConfig(), "phone"), NotNull()); 745 ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/foo",ConfigDescription::DefaultConfig(), "no-sdcard"), NotNull()); 746 ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bar", ConfigDescription::DefaultConfig(), ""), NotNull()); 747 ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/baz", ConfigDescription::DefaultConfig(), ""), NotNull()); 748 ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bit", ConfigDescription::DefaultConfig(), "phablet"), NotNull()); 749 ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bot", ConfigDescription::DefaultConfig(), "default"), NotNull()); 750 } 751 752 TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) { 753 std::string input = R"( 754 <public-group type="attr" first-id="0x01010040"> 755 <public name="foo" /> 756 <public name="bar" /> 757 </public-group>)"; 758 ASSERT_TRUE(TestParse(input)); 759 760 Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo")); 761 ASSERT_TRUE(result); 762 763 ASSERT_TRUE(result.value().package->id); 764 ASSERT_TRUE(result.value().type->id); 765 ASSERT_TRUE(result.value().entry->id); 766 ResourceId actual_id(result.value().package->id.value(), 767 result.value().type->id.value(), 768 result.value().entry->id.value()); 769 EXPECT_THAT(actual_id, Eq(ResourceId(0x01010040))); 770 771 result = table_.FindResource(test::ParseNameOrDie("attr/bar")); 772 ASSERT_TRUE(result); 773 774 ASSERT_TRUE(result.value().package->id); 775 ASSERT_TRUE(result.value().type->id); 776 ASSERT_TRUE(result.value().entry->id); 777 actual_id = ResourceId(result.value().package->id.value(), 778 result.value().type->id.value(), 779 result.value().entry->id.value()); 780 EXPECT_THAT(actual_id, Eq(ResourceId(0x01010041))); 781 } 782 783 TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) { 784 std::string input = R"( 785 <!-- private --> 786 <java-symbol type="string" name="foo" /> 787 <!-- public --> 788 <public type="string" name="foo" id="0x01020000" /> 789 <!-- private2 --> 790 <java-symbol type="string" name="foo" />)"; 791 ASSERT_TRUE(TestParse(input)); 792 793 Maybe<ResourceTable::SearchResult> result = 794 table_.FindResource(test::ParseNameOrDie("string/foo")); 795 ASSERT_TRUE(result); 796 797 ResourceEntry* entry = result.value().entry; 798 ASSERT_THAT(entry, NotNull()); 799 EXPECT_THAT(entry->visibility.level, Eq(Visibility::Level::kPublic)); 800 EXPECT_THAT(entry->visibility.comment, StrEq("public")); 801 } 802 803 TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) { 804 ASSERT_TRUE(TestParse(R"(<item type="layout" name="foo">@layout/bar</item>)")); 805 ASSERT_FALSE(TestParse(R"(<item type="layout" name="bar">"this is a string"</item>)")); 806 } 807 808 TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) { 809 ASSERT_TRUE(TestParse(R"(<add-resource name="bar" type="string" />)")); 810 811 Maybe<ResourceTable::SearchResult> result = 812 table_.FindResource(test::ParseNameOrDie("string/bar")); 813 ASSERT_TRUE(result); 814 const ResourceEntry* entry = result.value().entry; 815 ASSERT_THAT(entry, NotNull()); 816 EXPECT_THAT(entry->visibility.level, Eq(Visibility::Level::kUndefined)); 817 EXPECT_TRUE(entry->allow_new); 818 } 819 820 TEST_F(ResourceParserTest, ParseItemElementWithFormat) { 821 ASSERT_TRUE(TestParse(R"(<item name="foo" type="integer" format="float">0.3</item>)")); 822 823 BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo"); 824 ASSERT_THAT(val, NotNull()); 825 EXPECT_THAT(val->value.dataType, Eq(Res_value::TYPE_FLOAT)); 826 827 ASSERT_FALSE(TestParse(R"(<item name="bar" type="integer" format="fraction">100</item>)")); 828 } 829 830 // An <item> without a format specifier accepts all types of values. 831 TEST_F(ResourceParserTest, ParseItemElementWithoutFormat) { 832 ASSERT_TRUE(TestParse(R"(<item name="foo" type="integer">100%p</item>)")); 833 834 BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo"); 835 ASSERT_THAT(val, NotNull()); 836 EXPECT_THAT(val->value.dataType, Eq(Res_value::TYPE_FRACTION)); 837 } 838 839 TEST_F(ResourceParserTest, ParseConfigVaryingItem) { 840 ASSERT_TRUE(TestParse(R"(<item name="foo" type="configVarying">Hey</item>)")); 841 ASSERT_THAT(test::GetValue<String>(&table_, "configVarying/foo"), NotNull()); 842 } 843 844 TEST_F(ResourceParserTest, ParseBagElement) { 845 std::string input = R"( 846 <bag name="bag" type="configVarying"> 847 <item name="test">Hello!</item> 848 </bag>)"; 849 ASSERT_TRUE(TestParse(input)); 850 851 Style* val = test::GetValue<Style>(&table_, "configVarying/bag"); 852 ASSERT_THAT(val, NotNull()); 853 ASSERT_THAT(val->entries, SizeIs(1)); 854 855 EXPECT_THAT(val->entries[0].key, Eq(Reference(test::ParseNameOrDie("attr/test")))); 856 EXPECT_THAT(ValueCast<RawString>(val->entries[0].value.get()), NotNull()); 857 } 858 859 TEST_F(ResourceParserTest, ParseElementWithNoValue) { 860 std::string input = R"( 861 <item type="drawable" format="reference" name="foo" /> 862 <string name="foo" />)"; 863 ASSERT_TRUE(TestParse(input)); 864 ASSERT_THAT(test::GetValue(&table_, "drawable/foo"), Pointee(ValueEq(Reference()))); 865 866 String* str = test::GetValue<String>(&table_, "string/foo"); 867 ASSERT_THAT(str, NotNull()); 868 EXPECT_THAT(*str, StrValueEq("")); 869 } 870 871 TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { 872 ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)")); 873 } 874 875 TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) { 876 std::string input = R"( 877 <overlayable policy="illegal_policy"> 878 <item type="string" name="foo" /> 879 </overlayable>)"; 880 EXPECT_FALSE(TestParse(input)); 881 882 input = R"( 883 <overlayable policy="system"> 884 <item name="foo" /> 885 </overlayable>)"; 886 EXPECT_FALSE(TestParse(input)); 887 888 input = R"( 889 <overlayable policy="system"> 890 <item type="attr" /> 891 </overlayable>)"; 892 EXPECT_FALSE(TestParse(input)); 893 894 input = R"( 895 <overlayable policy="system"> 896 <item type="bad_type" name="foo" /> 897 </overlayable>)"; 898 EXPECT_FALSE(TestParse(input)); 899 900 input = R"(<overlayable policy="system" />)"; 901 EXPECT_TRUE(TestParse(input)); 902 903 input = R"(<overlayable />)"; 904 EXPECT_TRUE(TestParse(input)); 905 906 input = R"( 907 <overlayable policy="system"> 908 <item type="string" name="foo" /> 909 <item type="dimen" name="foo" /> 910 </overlayable>)"; 911 ASSERT_TRUE(TestParse(input)); 912 913 input = R"( 914 <overlayable> 915 <item type="string" name="bar" /> 916 </overlayable>)"; 917 ASSERT_TRUE(TestParse(input)); 918 919 Maybe<ResourceTable::SearchResult> search_result = 920 table_.FindResource(test::ParseNameOrDie("string/bar")); 921 ASSERT_TRUE(search_result); 922 ASSERT_THAT(search_result.value().entry, NotNull()); 923 EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); 924 EXPECT_TRUE(search_result.value().entry->overlayable); 925 } 926 927 TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { 928 std::string input = R"( 929 <overlayable> 930 <item type="string" name="foo" /> 931 <item type="string" name="foo" /> 932 </overlayable>)"; 933 EXPECT_FALSE(TestParse(input)); 934 } 935 936 TEST_F(ResourceParserTest, ParseIdItem) { 937 std::string input = R"( 938 <item name="foo" type="id">@id/bar</item> 939 <item name="bar" type="id"/> 940 <item name="baz" type="id"></item>)"; 941 ASSERT_TRUE(TestParse(input)); 942 943 ASSERT_THAT(test::GetValue<Reference>(&table_, "id/foo"), NotNull()); 944 ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull()); 945 ASSERT_THAT(test::GetValue<Id>(&table_, "id/baz"), NotNull()); 946 947 input = R"( 948 <id name="foo2">@id/bar</id> 949 <id name="bar2"/> 950 <id name="baz2"></id>)"; 951 ASSERT_TRUE(TestParse(input)); 952 953 ASSERT_THAT(test::GetValue<Reference>(&table_, "id/foo2"), NotNull()); 954 ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar2"), NotNull()); 955 ASSERT_THAT(test::GetValue<Id>(&table_, "id/baz2"), NotNull()); 956 957 // Reject attribute references 958 input = R"(<item name="foo3" type="id">?attr/bar"</item>)"; 959 ASSERT_FALSE(TestParse(input)); 960 961 // Reject non-references 962 input = R"(<item name="foo4" type="id">0x7f010001</item>)"; 963 ASSERT_FALSE(TestParse(input)); 964 input = R"(<item name="foo5" type="id">@drawable/my_image</item>)"; 965 ASSERT_FALSE(TestParse(input)); 966 input = R"(<item name="foo6" type="id"><string name="biz"></string></item>)"; 967 ASSERT_FALSE(TestParse(input)); 968 969 // Ids that reference other resource ids cannot be public 970 input = R"(<public name="foo7" type="id">@id/bar7</item>)"; 971 ASSERT_FALSE(TestParse(input)); 972 } 973 974 } // namespace aapt 975