Home | History | Annotate | Download | only in aapt2
      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">&#32;Hello&#32;</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">&#160;Hello&#160;</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