ResourceParser_test.cpp revision e597d68d33c76c2b830f5497ed4ba74c5193a056
16f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/*
26f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Copyright (C) 2015 The Android Open Source Project
36f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
46f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
56f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * you may not use this file except in compliance with the License.
66f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * You may obtain a copy of the License at
76f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
86f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
96f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Unless required by applicable law or agreed to in writing, software
116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * See the License for the specific language governing permissions and
146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * limitations under the License.
156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceParser.h"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <sstream>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <string>
21ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceTable.h"
231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceUtils.h"
246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceValues.h"
25d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski#include "test/Test.h"
26467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlPullParser.h"
276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
28e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinskiusing ::android::StringPiece;
29e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinskiusing ::testing::Eq;
30e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinskiusing ::testing::NotNull;
31d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskinamespace aapt {
336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
34cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskiconstexpr const char* kXmlPreamble =
35cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
371ab598f46c3ff520a67f9d80194847741f3467abAdam LesinskiTEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
38ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
39cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::stringstream input(kXmlPreamble);
40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input << "<attr name=\"foo\"/>" << std::endl;
41cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceTable table;
42ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
43ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xml::XmlPullParser xml_parser(input);
44ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(parser.Parse(&xml_parser));
45769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski}
46769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
47ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskiclass ResourceParserTest : public ::testing::Test {
48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski public:
49ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  void SetUp() override { context_ = test::ContextBuilder().Build(); }
501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
51ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ::testing::AssertionResult TestParse(const StringPiece& str) {
52ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return TestParse(str, ConfigDescription{});
53cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
5452364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
55ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ::testing::AssertionResult TestParse(const StringPiece& str,
56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                       const ConfigDescription& config) {
57cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::stringstream input(kXmlPreamble);
58cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    input << "<resources>\n" << str << "\n</resources>" << std::endl;
59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    ResourceParserOptions parserOptions;
60ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"},
61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          config, parserOptions);
62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    xml::XmlPullParser xmlParser(input);
63ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser.Parse(&xmlParser)) {
64cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return ::testing::AssertionSuccess();
656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return ::testing::AssertionFailure();
67cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
68ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
69ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski protected:
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceTable table_;
71ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IAaptContext> context_;
726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski};
736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseQuotedString) {
75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">   \"  hey there \" </string>";
76ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
78ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
79cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("  hey there "), *str->value);
817542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseEscapedString) {
85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">\\?123</string>";
86ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
88ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
89cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("?123"), *str->value);
917542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
949f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam LesinskiTEST_F(ResourceParserTest, ParseFormattedString) {
95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">%d %s</string>";
96ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
979f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input = "<string name=\"foo\">%1$d %2$s</string>";
99ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1009f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski}
1019f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
1028c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam LesinskiTEST_F(ResourceParserTest, ParseStyledString) {
103cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Use a surrogate pair unicode point so that we can verify that the span
1047542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // indices use UTF-16 length and not UTF-8 length.
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
1068049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski      "<string name=\"foo\">This is my aunt\u2019s <b>fickle <small>string</small></b></string>";
107ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1088c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
1118c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
1128049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  const std::string expected_str = "This is my aunt\u2019s fickle string";
113ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(expected_str, *str->value->str);
1148049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(2u, str->value->spans.size());
1157542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
1168c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
118ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(17u, str->value->spans[0].first_char);
1198049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(30u, str->value->spans[0].last_char);
1208049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski
1218049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(std::string("small"), *str->value->spans[1].name);
1228049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(24u, str->value->spans[1].first_char);
1238049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(30u, str->value->spans[1].last_char);
1248c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski}
1258c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
1268c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam LesinskiTEST_F(ResourceParserTest, ParseStringWithWhitespace) {
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">  This is what  I think  </string>";
128ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1298c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("This is what I think"), *str->value);
1337542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
1348c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input = "<string name=\"foo2\">\"  This is what  I think  \"</string>";
136ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1378c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
138ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  str = test::GetValue<String>(&table_, "string/foo2");
139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("  This is what  I think  "), *str->value);
1418c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski}
1428c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
1437542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
1447542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1457542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1467542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          There are <xliff:source>no</xliff:source> apples</string>)EOF";
1477542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_TRUE(TestParse(input));
1487542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1497542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
1507542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_NE(nullptr, str);
1517542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(StringPiece("There are no apples"), StringPiece(*str->value));
1527542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
1537542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski}
1547542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1557542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, NestedXliffGTagsAreIllegal) {
1567542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1577542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1587542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)EOF";
1597542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_FALSE(TestParse(input));
1607542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski}
1617542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1627542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInString) {
1637542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1647542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1657542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          There are <xliff:g id="count">%1$d</xliff:g> apples</string>)EOF";
166ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
168ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
1717542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1727542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_EQ(1u, str->untranslatable_sections.size());
1737542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1747542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // We expect indices and lengths that span to include the whitespace
1757542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // before %1$d. This is due to how the StringBuilder withholds whitespace unless
1767542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // needed (to deal with line breaks, etc.).
1777542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(9u, str->untranslatable_sections[0].start);
1787542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(14u, str->untranslatable_sections[0].end);
1797542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski}
1807542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1817542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) {
1827542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1837542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1847542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)EOF";
1857542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_TRUE(TestParse(input));
1867542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1877542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
1887542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_NE(nullptr, str);
1897542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value->str));
1907542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1917542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_EQ(1u, str->untranslatable_sections.size());
1927542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1937542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // We expect indices and lengths that span to include the whitespace
1947542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // before %1$d. This is due to how the StringBuilder withholds whitespace unless
1957542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // needed (to deal with line breaks, etc.).
1967542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(9u, str->untranslatable_sections[0].start);
1977542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(14u, str->untranslatable_sections[0].end);
1981ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1991ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
200dfa5e0705ff82f15e228ba076bc192893bcbe118Adam LesinskiTEST_F(ResourceParserTest, ParseNull) {
201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<integer name=\"foo\">@null</integer>";
202ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The Android runtime treats a value of android::Res_value::TYPE_NULL as
205cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // a non-existing value, and this causes problems in styles when trying to
2067542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // resolve an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
207cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // with a data value of 0.
2087542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
209cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, integer);
2107542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(0u, integer->value.data);
212dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski}
213dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski
214dfa5e0705ff82f15e228ba076bc192893bcbe118Adam LesinskiTEST_F(ResourceParserTest, ParseEmpty) {
215cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<integer name=\"foo\">@empty</integer>";
216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
217dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BinaryPrimitive* integer =
219ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, integer);
221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
223dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski}
224dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski
2256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAttr) {
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" format=\"string\"/>\n"
228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"bar\"/>";
229ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
231ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
233ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
235ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr = test::GetValue<Attribute>(&table_, "attr/bar");
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
237ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->type_mask);
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// Old AAPT allowed attributes to be defined under different configurations, but
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// ultimately
242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// stored them with the default configuration. Check that we have the same
243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// behavior.
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam LesinskiTEST_F(ResourceParserTest,
245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski       ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
246ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
24852364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        <attr name="foo" />
24952364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        <declare-styleable name="bar">
25052364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski          <attr name="baz" />
25152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        </declare-styleable>)EOF";
252ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input, watch_config));
25352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
254ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo",
255ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                        watch_config));
256ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz",
257ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                        watch_config));
258ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(
259ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "styleable/bar", watch_config));
26052364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
261ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo"));
262ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz"));
263ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Styleable>(&table_, "styleable/bar"));
26452364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski}
26552364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
266a587065721053ad54e34f484868142407d59512dAdam LesinskiTEST_F(ResourceParserTest, ParseAttrWithMinMax) {
267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
269ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
270a587065721053ad54e34f484868142407d59512dAdam Lesinski
271ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
273ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->type_mask);
274ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(10, attr->min_int);
275ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(23, attr->max_int);
276a587065721053ad54e34f484868142407d59512dAdam Lesinski}
277a587065721053ad54e34f484868142407d59512dAdam Lesinski
278a587065721053ad54e34f484868142407d59512dAdam LesinskiTEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
281ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
282a587065721053ad54e34f484868142407d59512dAdam Lesinski}
283a587065721053ad54e34f484868142407d59512dAdam Lesinski
2846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"Styleable\">\n"
287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"foo\" />\n"
288cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>\n"
289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" format=\"string\"/>";
290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
2916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
292ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
294ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
2956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"Theme\">"
300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"foo\" />\n"
301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>\n"
302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"Window\">\n"
303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"foo\" format=\"boolean\"/>\n"
304cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>";
305ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
3066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
307ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
309ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->type_mask);
3106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseEnumAttr) {
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\">\n"
315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bar\" value=\"0\"/>\n"
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bat\" value=\"1\"/>\n"
317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"baz\" value=\"2\"/>\n"
318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</attr>";
319ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
3206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
321ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo");
322ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_NE(enum_attr, nullptr);
323ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->type_mask, android::ResTable_map::TYPE_ENUM);
324ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_EQ(enum_attr->symbols.size(), 3u);
3256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
327ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[0].symbol.name.value().entry, "bar");
328ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[0].value, 0u);
3296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
330ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
331ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[1].symbol.name.value().entry, "bat");
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[1].value, 1u);
3336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
334ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
335ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[2].symbol.name.value().entry, "baz");
336ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[2].value, 2u);
3376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseFlagAttr) {
340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
341cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\">\n"
342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <flag name=\"bar\" value=\"0\"/>\n"
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <flag name=\"bat\" value=\"1\"/>\n"
344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <flag name=\"baz\" value=\"2\"/>\n"
345cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</attr>";
346ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
3476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
348ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo");
349ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_NE(nullptr, flag_attr);
350ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->type_mask, android::ResTable_map::TYPE_FLAGS);
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_EQ(flag_attr->symbols.size(), 3u);
3526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
353ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
354ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[0].symbol.name.value().entry, "bar");
355ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[0].value, 0u);
3566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
357ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
358ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[1].symbol.name.value().entry, "bat");
359ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[1].value, 1u);
3606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
361ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
362ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[2].symbol.name.value().entry, "baz");
363ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[2].value, 2u);
3646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
365ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<BinaryPrimitive> flag_value =
366ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat");
367ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_NE(nullptr, flag_value);
368ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_value->value.data, 1u | 2u);
3696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
372cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
373cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\">\n"
374cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bar\" value=\"0\"/>\n"
375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bat\" value=\"1\"/>\n"
376cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bat\" value=\"2\"/>\n"
377cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</attr>";
378ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
3796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseStyle) {
382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
383cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<style name=\"foo\" parent=\"@style/fu\">\n"
384cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"bar\">#ffffffff</item>\n"
385cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"bat\">@string/hey</item>\n"
386cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"baz\"><b>hey</b></item>\n"
387cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</style>";
388ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
390ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
394ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("style/fu"),
395cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->parent.value().name.value());
396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(3u, style->entries.size());
397cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->entries[0].key.name);
399ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[0].key.name.value());
401cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->entries[1].key.name);
403ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[1].key.name.value());
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->entries[2].key.name);
407ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/baz"),
408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[2].key.name.value());
4096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
4106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
411769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
413ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
414769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
415ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
419ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("com.app:style/Theme"),
420cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->parent.value().name.value());
421769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski}
422769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
42324aad163bc88cb10d2275385e9afc3de7f342d65Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
425cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
426cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "       name=\"foo\" parent=\"app:Theme\"/>";
427ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
42824aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
429ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
431cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
433ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("android:style/Theme"),
434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->parent.value().name.value());
43524aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski}
43624aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
43724aad163bc88cb10d2275385e9afc3de7f342d65Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
438cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
439cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" "
440cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "name=\"foo\">\n"
441cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"app:bar\">0</item>\n"
442cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</style>";
443ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
44424aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
445ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
446cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(1u, style->entries.size());
448ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("android:attr/bar"),
449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[0].key.name.value());
45024aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski}
45124aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
452bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
453cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<style name=\"foo.bar\"/>";
454ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
455bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
456ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
457cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(style->parent.value().name.value(),
461ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            test::ParseNameOrDie("style/foo"));
462ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(style->parent_inferred);
463bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski}
464bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
465cacb28f2d60858106e2819cc7d95a65e8bda890bAdam LesinskiTEST_F(ResourceParserTest,
466cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski       ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
467cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
468ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
469bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
470ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
471cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
472cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_EXPECT_FALSE(style->parent);
473ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_FALSE(style->parent_inferred);
474bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski}
475bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
47624b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam LesinskiTEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
477cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
478cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
479ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
48024b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski
481ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
482cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
483cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
484ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(style->parent.value().private_reference);
48524b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski}
48624b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski
4876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
488cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">@+id/bar</string>";
489ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
4906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
491ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Id* id = test::GetValue<Id>(&table_, "id/bar");
492cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(id, nullptr);
4936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
4946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
4956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"foo\">\n"
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"bar\" />\n"
499cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"bat\" format=\"string|reference\"/>\n"
500cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"baz\">\n"
501cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "    <enum name=\"foo\" value=\"1\"/>\n"
502cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  </attr>\n"
503cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>";
504ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
505cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Maybe<ResourceTable::SearchResult> result =
507ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_.FindResource(test::ParseNameOrDie("styleable/foo"));
508cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
509ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
511ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(attr, nullptr);
513ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(attr->IsWeak());
514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
515ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr = test::GetValue<Attribute>(&table_, "attr/bat");
516cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(attr, nullptr);
517ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(attr->IsWeak());
518cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
519ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr = test::GetValue<Attribute>(&table_, "attr/baz");
520cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(attr, nullptr);
521ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(attr->IsWeak());
522cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(1u, attr->symbols.size());
523cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
524ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Id>(&table_, "id/foo"));
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
526ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
527cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(styleable, nullptr);
528cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(3u, styleable->entries.size());
529cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
530ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
531cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            styleable->entries[0].name.value());
532ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
533cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            styleable->entries[1].name.value());
5346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
5356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
536467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam LesinskiTEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
537cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
538cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"foo\" "
539cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
540cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"*android:bar\" />\n"
541cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"privAndroid:bat\" />\n"
542cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>";
543ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
544ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
545cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, styleable);
546cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(2u, styleable->entries.size());
547cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
548ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(styleable->entries[0].private_reference);
549cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(styleable->entries[0].name);
550cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package);
551cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
552ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(styleable->entries[1].private_reference);
553cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(styleable->entries[1].name);
554cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
555467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski}
556467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
5576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseArray) {
558cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
559cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<array name=\"foo\">\n"
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item>@string/ref</item>\n"
561cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item>hey</item>\n"
562cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item>23</item>\n"
563cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</array>";
564ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
5656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
566ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Array* array = test::GetValue<Array>(&table_, "array/foo");
567cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(array, nullptr);
568cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(3u, array->items.size());
5696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
570ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, ValueCast<Reference>(array->items[0].get()));
571ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, ValueCast<String>(array->items[1].get()));
572ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(array->items[2].get()));
5736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
5746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5759ba47d813075fcb05c5e1532c137c93b394631cbAdam LesinskiTEST_F(ResourceParserTest, ParseStringArray) {
576d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  std::string input = R"EOF(
577d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      <string-array name="foo">
578d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski        <item>"Werk"</item>"
579d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      </string-array>)EOF";
580ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
581ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Array>(&table_, "array/foo"));
5829ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski}
5839ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
584d5fd76a2ff78400505ade936fc36e707d69ecf72Adam LesinskiTEST_F(ResourceParserTest, ParseArrayWithFormat) {
585d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  std::string input = R"EOF(
586d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      <array name="foo" format="string">
587d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski        <item>100</item>
588d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      </array>)EOF";
589d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_TRUE(TestParse(input));
590d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
591d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  Array* array = test::GetValue<Array>(&table_, "array/foo");
592d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_NE(nullptr, array);
593d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
594d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_EQ(1u, array->items.size());
595d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
596d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  String* str = ValueCast<String>(array->items[0].get());
597d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_NE(nullptr, str);
598d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  EXPECT_EQ(std::string("100"), *str->value);
599d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski}
600d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
601d5fd76a2ff78400505ade936fc36e707d69ecf72Adam LesinskiTEST_F(ResourceParserTest, ParseArrayWithBadFormat) {
602d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  std::string input = R"EOF(
603d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      <array name="foo" format="integer">
604d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski        <item>Hi</item>
605d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      </array>)EOF";
606d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_FALSE(TestParse(input));
607d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski}
608d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
6096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParsePlural) {
610cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
611cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<plurals name=\"foo\">\n"
612cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item quantity=\"other\">apples</item>\n"
613cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item quantity=\"one\">apple</item>\n"
614cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</plurals>";
615ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
6168f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski
6178f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  Plural* plural = test::GetValue<Plural>(&table_, "plurals/foo");
6188f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  ASSERT_NE(nullptr, plural);
6198f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Zero]);
6208f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Two]);
6218f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Few]);
6228f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Many]);
6238f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski
6248f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_NE(nullptr, plural->values[Plural::One]);
6258f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_NE(nullptr, plural->values[Plural::Other]);
6266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
6286f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseCommentsWithResource) {
629cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
630cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--This is a comment-->\n"
631cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<string name=\"foo\">Hi</string>";
632ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
6336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
634ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* value = test::GetValue<String>(&table_, "string/foo");
635cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, value);
636ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(value->GetComment(), "This is a comment");
637e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski}
638e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
639e78fd617ec60139a973a01925fa7adad31febb39Adam LesinskiTEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
640cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
641cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--One-->\n"
642cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--Two-->\n"
643cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<string name=\"foo\">Hi</string>";
644e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
645ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
646e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
647ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* value = test::GetValue<String>(&table_, "string/foo");
648cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, value);
649ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(value->GetComment(), "Two");
650e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski}
651e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
652e78fd617ec60139a973a01925fa7adad31febb39Adam LesinskiTEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
653cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
654cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--One-->\n"
655cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<string name=\"foo\">\n"
656cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  Hi\n"
657cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--Two-->\n"
658cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</string>";
659e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
660ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
6611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
662ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* value = test::GetValue<String>(&table_, "string/foo");
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, value);
664ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(value->GetComment(), "One");
6656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
667ca5638fd85098c3d0a699492751043545f75553aAdam LesinskiTEST_F(ResourceParserTest, ParseNestedComments) {
668cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // We only care about declare-styleable and enum/flag attributes because
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // comments
670cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // from those end up in R.java
671cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
672ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        <declare-styleable name="foo">
673ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <!-- The name of the bar -->
674ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <attr name="barName" format="string|reference" />
675ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        </declare-styleable>
676ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
677ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        <attr name="foo">
678ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <!-- The very first -->
679ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <enum name="one" value="1" />
680ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        </attr>)EOF";
681ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
682ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
683ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
684cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, styleable);
685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(1u, styleable->entries.size());
686ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
687cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(StringPiece("The name of the bar"),
688ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            styleable->entries.front().GetComment());
689ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
690ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
691cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(1u, attr->symbols.size());
693ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
694cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(StringPiece("The very first"),
695ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            attr->symbols.front().symbol.GetComment());
696ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski}
697ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
6986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/*
6996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Declaring an ID as public should not require a separate definition
7006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * (as an ID has no value).
7016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
7026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
703cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<public type=\"id\" name=\"foo\"/>";
704ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
7056f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
706ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Id* id = test::GetValue<Id>(&table_, "id/foo");
707cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, id);
7086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
7096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
710e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam LesinskiTEST_F(ResourceParserTest, KeepAllProducts) {
711cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
7127751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="foo" product="phone">hi</string>
7137751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="foo" product="no-sdcard">ho</string>
7147751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="bar" product="">wee</string>
7157751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="baz">woo</string>
7167751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="bit" product="phablet">hoot</string>
7177751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="bot" product="default">yes</string>
7187751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski    )EOF";
719ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
720ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
721ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
722ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/foo",
723ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "phone"));
724ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
725ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/foo",
726ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "no-sdcard"));
727cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_NE(nullptr,
728ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            test::GetValueForConfigAndProduct<String>(
729ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                &table_, "string/bar", ConfigDescription::DefaultConfig(), ""));
730cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_NE(nullptr,
731ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            test::GetValueForConfigAndProduct<String>(
732ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                &table_, "string/baz", ConfigDescription::DefaultConfig(), ""));
733ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
734ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/bit",
735ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "phablet"));
736ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
737ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/bot",
738ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "default"));
7399ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski}
7409ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
74127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam LesinskiTEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
742cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
74327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    <public-group type="attr" first-id="0x01010040">
74427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski      <public name="foo" />
74527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski      <public name="bar" />
74627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    </public-group>)EOF";
747ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
748cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
749cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Maybe<ResourceTable::SearchResult> result =
750ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_.FindResource(test::ParseNameOrDie("attr/foo"));
751cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
752cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().package->id);
754cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().type->id);
755cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().entry->id);
756ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceId actual_id(result.value().package->id.value(),
757ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                       result.value().type->id.value(),
758ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                       result.value().entry->id.value());
759ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(ResourceId(0x01010040), actual_id);
760cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
761ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
762cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
763cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
764cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().package->id);
765cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().type->id);
766cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().entry->id);
767ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  actual_id = ResourceId(result.value().package->id.value(),
768ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         result.value().type->id.value(),
769ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         result.value().entry->id.value());
770ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(ResourceId(0x01010041), actual_id);
77127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski}
77227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
773fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam LesinskiTEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
774cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
775cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
776ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
777fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam Lesinski
778cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
779ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
780fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam Lesinski}
781fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam Lesinski
7824488f1c74a0f7df09f2b201f7caa228d729e8389Adam LesinskiTEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
783cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
784ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
785a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
786cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Maybe<ResourceTable::SearchResult> result =
787ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_.FindResource(test::ParseNameOrDie("string/bar"));
788cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
789cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const ResourceEntry* entry = result.value().entry;
790cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, entry);
791ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state);
7924488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski  EXPECT_TRUE(entry->symbol_status.allow_new);
793a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski}
794a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
7957ff3ee19f4b831a526baf4b928d1ac172d070d82Adam LesinskiTEST_F(ResourceParserTest, ParseItemElementWithFormat) {
796e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  std::string input = R"(<item name="foo" type="integer" format="float">0.3</item>)";
797ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
7987ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
799e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
800e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_THAT(val, NotNull());
801e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  EXPECT_THAT(val->value.dataType, Eq(android::Res_value::TYPE_FLOAT));
802e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski
803e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  input = R"(<item name="bar" type="integer" format="fraction">100</item>)";
804e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_FALSE(TestParse(input));
805e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski}
806e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski
807e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski// An <item> without a format specifier accepts all types of values.
808e597d68d33c76c2b830f5497ed4ba74c5193a056Adam LesinskiTEST_F(ResourceParserTest, ParseItemElementWithoutFormat) {
809e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  std::string input = R"(<item name="foo" type="integer">100%p</item>)";
810e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_TRUE(TestParse(input));
8117ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
812e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
813e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_THAT(val, NotNull());
814e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  EXPECT_THAT(val->value.dataType, Eq(android::Res_value::TYPE_FRACTION));
8157ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
8167ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
81786d67df8d57b9537666f9b54a9ca563779a2288bAdam LesinskiTEST_F(ResourceParserTest, ParseConfigVaryingItem) {
81886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  std::string input = R"EOF(<item name="foo" type="configVarying">Hey</item>)EOF";
81986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_TRUE(TestParse(input));
82086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_NE(nullptr, test::GetValue<String>(&table_, "configVarying/foo"));
82186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski}
82286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
82386d67df8d57b9537666f9b54a9ca563779a2288bAdam LesinskiTEST_F(ResourceParserTest, ParseBagElement) {
82486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  std::string input =
82586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      R"EOF(<bag name="bag" type="configVarying"><item name="test">Hello!</item></bag>)EOF";
82686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_TRUE(TestParse(input));
82786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
82886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  Style* val = test::GetValue<Style>(&table_, "configVarying/bag");
82986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_NE(nullptr, val);
83086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
83186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_EQ(1u, val->entries.size());
83286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  EXPECT_EQ(Reference(test::ParseNameOrDie("attr/test")), val->entries[0].key);
83386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  EXPECT_NE(nullptr, ValueCast<RawString>(val->entries[0].value.get()));
83486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski}
83586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
836cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
837