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
2890919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinskiusing ::aapt::test::ValueEq;
29e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinskiusing ::android::StringPiece;
30e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinskiusing ::testing::Eq;
31e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinskiusing ::testing::NotNull;
3290919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinskiusing ::testing::Pointee;
33d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskinamespace aapt {
356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3690919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinskiconstexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
381ab598f46c3ff520a67f9d80194847741f3467abAdam LesinskiTEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
39ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::stringstream input(kXmlPreamble);
4190919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  input << R"(<attr name="foo"/>)" << std::endl;
42cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceTable table;
43ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
44ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xml::XmlPullParser xml_parser(input);
45ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(parser.Parse(&xml_parser));
46769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski}
47769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskiclass ResourceParserTest : public ::testing::Test {
49ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski public:
5090919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  void SetUp() override {
5190919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski    context_ = test::ContextBuilder().Build();
5290919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  }
531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
54ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ::testing::AssertionResult TestParse(const StringPiece& str) {
55ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return TestParse(str, ConfigDescription{});
56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
5752364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
5890919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  ::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) {
59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::stringstream input(kXmlPreamble);
60cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    input << "<resources>\n" << str << "\n</resources>" << std::endl;
61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    ResourceParserOptions parserOptions;
6290919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski    ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config,
6390919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski                          parserOptions);
64cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    xml::XmlPullParser xmlParser(input);
65ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser.Parse(&xmlParser)) {
66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return ::testing::AssertionSuccess();
676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
68cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return ::testing::AssertionFailure();
69cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
71ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski protected:
72ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceTable table_;
73ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IAaptContext> context_;
746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski};
756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseQuotedString) {
77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">   \"  hey there \" </string>";
78ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
80ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("  hey there "), *str->value);
837542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseEscapedString) {
87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">\\?123</string>";
88ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
90ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("?123"), *str->value);
937542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
969f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam LesinskiTEST_F(ResourceParserTest, ParseFormattedString) {
97cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">%d %s</string>";
98ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
999f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input = "<string name=\"foo\">%1$d %2$s</string>";
101ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1029f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski}
1039f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
1048c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam LesinskiTEST_F(ResourceParserTest, ParseStyledString) {
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Use a surrogate pair unicode point so that we can verify that the span
1067542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // indices use UTF-16 length and not UTF-8 length.
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
1088049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski      "<string name=\"foo\">This is my aunt\u2019s <b>fickle <small>string</small></b></string>";
109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1108c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
111ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
1138c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
1148049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  const std::string expected_str = "This is my aunt\u2019s fickle string";
115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(expected_str, *str->value->str);
1168049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(2u, str->value->spans.size());
1177542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
1188c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
120ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(17u, str->value->spans[0].first_char);
1218049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(30u, str->value->spans[0].last_char);
1228049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski
1238049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(std::string("small"), *str->value->spans[1].name);
1248049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(24u, str->value->spans[1].first_char);
1258049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  EXPECT_EQ(30u, str->value->spans[1].last_char);
1268c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski}
1278c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
1288c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam LesinskiTEST_F(ResourceParserTest, ParseStringWithWhitespace) {
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">  This is what  I think  </string>";
130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1318c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
132ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
133cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("This is what I think"), *str->value);
1357542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
1368c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input = "<string name=\"foo2\">\"  This is what  I think  \"</string>";
138ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1398c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
140ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  str = test::GetValue<String>(&table_, "string/foo2");
141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("  This is what  I think  "), *str->value);
1438c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski}
1448c3f31f022f7e094fd227ef0c2987e0846cb3e43Adam Lesinski
1457542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
1467542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1477542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1487542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          There are <xliff:source>no</xliff:source> apples</string>)EOF";
1497542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_TRUE(TestParse(input));
1507542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1517542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
1527542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_NE(nullptr, str);
1537542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(StringPiece("There are no apples"), StringPiece(*str->value));
1547542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_TRUE(str->untranslatable_sections.empty());
1557542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski}
1567542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1577542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, NestedXliffGTagsAreIllegal) {
1587542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1597542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1607542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)EOF";
1617542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_FALSE(TestParse(input));
1627542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski}
1637542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1647542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInString) {
1657542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1667542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1677542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          There are <xliff:g id="count">%1$d</xliff:g> apples</string>)EOF";
168ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
1691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
170ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
171cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, str);
172cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
1737542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1747542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_EQ(1u, str->untranslatable_sections.size());
1757542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1767542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // We expect indices and lengths that span to include the whitespace
1777542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // before %1$d. This is due to how the StringBuilder withholds whitespace unless
1787542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // needed (to deal with line breaks, etc.).
1797542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(9u, str->untranslatable_sections[0].start);
1807542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(14u, str->untranslatable_sections[0].end);
1817542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski}
1827542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1837542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam LesinskiTEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) {
1847542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::string input = R"EOF(
1857542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
1867542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)EOF";
1877542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_TRUE(TestParse(input));
1887542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1897542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
1907542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_NE(nullptr, str);
1917542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value->str));
1927542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1937542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  ASSERT_EQ(1u, str->untranslatable_sections.size());
1947542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1957542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // We expect indices and lengths that span to include the whitespace
1967542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // before %1$d. This is due to how the StringBuilder withholds whitespace unless
1977542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // needed (to deal with line breaks, etc.).
1987542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(9u, str->untranslatable_sections[0].start);
1997542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  EXPECT_EQ(14u, str->untranslatable_sections[0].end);
2001ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
2011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
202dfa5e0705ff82f15e228ba076bc192893bcbe118Adam LesinskiTEST_F(ResourceParserTest, ParseNull) {
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<integer name=\"foo\">@null</integer>";
204ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
205cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
206cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The Android runtime treats a value of android::Res_value::TYPE_NULL as
207cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // a non-existing value, and this causes problems in styles when trying to
2087542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // resolve an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
209cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // with a data value of 0.
21090919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  Reference* null_ref = test::GetValue<Reference>(&table_, "integer/foo");
21190919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  ASSERT_THAT(null_ref, NotNull());
21290919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_FALSE(null_ref->name);
21390919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_FALSE(null_ref->id);
21490919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_EQ(Reference::Type::kResource, null_ref->reference_type);
215dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski}
216dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski
217dfa5e0705ff82f15e228ba076bc192893bcbe118Adam LesinskiTEST_F(ResourceParserTest, ParseEmpty) {
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<integer name=\"foo\">@empty</integer>";
219ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
220dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski
22190919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, integer);
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
225dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski}
226dfa5e0705ff82f15e228ba076bc192893bcbe118Adam Lesinski
2276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAttr) {
228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
229cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" format=\"string\"/>\n"
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"bar\"/>";
231ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
233ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
235ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
237ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr = test::GetValue<Attribute>(&table_, "attr/bar");
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
239ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->type_mask);
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// Old AAPT allowed attributes to be defined under different configurations, but
243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// ultimately
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// stored them with the default configuration. Check that we have the same
245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// behavior.
24690919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam LesinskiTEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
247ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
24890919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  std::string input = R"(
24990919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski      <attr name="foo" />
25090919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski      <declare-styleable name="bar">
25190919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski        <attr name="baz" />
25290919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski      </declare-styleable>)";
253ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input, watch_config));
25452364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
25590919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo", watch_config));
25690919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz", watch_config));
25790919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(&table_, "styleable/bar", watch_config));
25852364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
259ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo"));
260ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz"));
261ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Styleable>(&table_, "styleable/bar"));
26252364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski}
26352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
264a587065721053ad54e34f484868142407d59512dAdam LesinskiTEST_F(ResourceParserTest, ParseAttrWithMinMax) {
265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
267ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
268a587065721053ad54e34f484868142407d59512dAdam Lesinski
269ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
271ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->type_mask);
272ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(10, attr->min_int);
273ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(23, attr->max_int);
274a587065721053ad54e34f484868142407d59512dAdam Lesinski}
275a587065721053ad54e34f484868142407d59512dAdam Lesinski
276a587065721053ad54e34f484868142407d59512dAdam LesinskiTEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
279ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
280a587065721053ad54e34f484868142407d59512dAdam Lesinski}
281a587065721053ad54e34f484868142407d59512dAdam Lesinski
2826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"Styleable\">\n"
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"foo\" />\n"
286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>\n"
287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\" format=\"string\"/>";
288ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
2896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
292ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
2936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
296cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
297cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"Theme\">"
298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"foo\" />\n"
299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>\n"
300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"Window\">\n"
301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"foo\" format=\"boolean\"/>\n"
302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>";
303ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
3046f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
305ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
307ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->type_mask);
3086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseEnumAttr) {
311cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
312cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\">\n"
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bar\" value=\"0\"/>\n"
314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bat\" value=\"1\"/>\n"
315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"baz\" value=\"2\"/>\n"
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</attr>";
317ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
3186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
319ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo");
320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_NE(enum_attr, nullptr);
321ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->type_mask, android::ResTable_map::TYPE_ENUM);
322ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_EQ(enum_attr->symbols.size(), 3u);
3236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
324ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
325ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[0].symbol.name.value().entry, "bar");
326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[0].value, 0u);
3276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
328ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
329ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[1].symbol.name.value().entry, "bat");
330ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[1].value, 1u);
3316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
333ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[2].symbol.name.value().entry, "baz");
334ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(enum_attr->symbols[2].value, 2u);
3356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseFlagAttr) {
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\">\n"
340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <flag name=\"bar\" value=\"0\"/>\n"
341cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <flag name=\"bat\" value=\"1\"/>\n"
342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <flag name=\"baz\" value=\"2\"/>\n"
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</attr>";
344ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
3456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
346ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo");
347ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_NE(nullptr, flag_attr);
348ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->type_mask, android::ResTable_map::TYPE_FLAGS);
349ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_EQ(flag_attr->symbols.size(), 3u);
3506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
352ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[0].symbol.name.value().entry, "bar");
353ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[0].value, 0u);
3546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
355ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
356ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[1].symbol.name.value().entry, "bat");
357ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[1].value, 1u);
3586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
359ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  AAPT_ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
360ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[2].symbol.name.value().entry, "baz");
361ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_attr->symbols[2].value, 2u);
3626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
363ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<BinaryPrimitive> flag_value =
364ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat");
365ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_NE(nullptr, flag_value);
366ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(flag_value->value.data, 1u | 2u);
3676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
370cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
371cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<attr name=\"foo\">\n"
372cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bar\" value=\"0\"/>\n"
373cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bat\" value=\"1\"/>\n"
374cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <enum name=\"bat\" value=\"2\"/>\n"
375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</attr>";
376ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
3776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseStyle) {
380cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
381cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<style name=\"foo\" parent=\"@style/fu\">\n"
382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"bar\">#ffffffff</item>\n"
383cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"bat\">@string/hey</item>\n"
384cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"baz\"><b>hey</b></item>\n"
385cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</style>";
386ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
387cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
388ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
390cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
392ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("style/fu"),
393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->parent.value().name.value());
394cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(3u, style->entries.size());
395cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->entries[0].key.name);
397ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[0].key.name.value());
399cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->entries[1].key.name);
401ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[1].key.name.value());
403cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->entries[2].key.name);
405ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/baz"),
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[2].key.name.value());
4076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
4086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
409769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
411ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
412769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
413ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
414cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
417ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("com.app:style/Theme"),
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->parent.value().name.value());
419769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski}
420769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
42124aad163bc88cb10d2275385e9afc3de7f342d65Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
423cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "       name=\"foo\" parent=\"app:Theme\"/>";
425ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
42624aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
427ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
429cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
431ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("android:style/Theme"),
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->parent.value().name.value());
43324aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski}
43424aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
43524aad163bc88cb10d2275385e9afc3de7f342d65Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
436cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
437cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" "
438cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "name=\"foo\">\n"
439cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item name=\"app:bar\">0</item>\n"
440cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</style>";
441ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
44224aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
443ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
445cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(1u, style->entries.size());
446ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("android:attr/bar"),
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            style->entries[0].key.name.value());
44824aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski}
44924aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski
450bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam LesinskiTEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
451cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<style name=\"foo.bar\"/>";
452ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
453bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
454ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
455cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
456cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
457cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent.value().name);
458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(style->parent.value().name.value(),
459ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            test::ParseNameOrDie("style/foo"));
460ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(style->parent_inferred);
461bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski}
462bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
463cacb28f2d60858106e2819cc7d95a65e8bda890bAdam LesinskiTEST_F(ResourceParserTest,
464cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski       ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
465cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
466ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
467bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
468ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
469cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
470cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_EXPECT_FALSE(style->parent);
471ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_FALSE(style->parent_inferred);
472bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski}
473bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski
47424b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam LesinskiTEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
475cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
476cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
477ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
47824b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski
479ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Style* style = test::GetValue<Style>(&table_, "style/foo");
480cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, style);
481cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(style->parent);
482ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(style->parent.value().private_reference);
48324b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski}
48424b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski
4856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
486cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<string name=\"foo\">@+id/bar</string>";
487ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
4886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
489ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Id* id = test::GetValue<Id>(&table_, "id/bar");
490cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(id, nullptr);
4916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
4926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
4936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
494cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
495cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"foo\">\n"
496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"bar\" />\n"
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"bat\" format=\"string|reference\"/>\n"
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"baz\">\n"
499cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "    <enum name=\"foo\" value=\"1\"/>\n"
500cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  </attr>\n"
501cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>";
502ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
503cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
504cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Maybe<ResourceTable::SearchResult> result =
505ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_.FindResource(test::ParseNameOrDie("styleable/foo"));
506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
507ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
508cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
509ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(attr, nullptr);
511ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(attr->IsWeak());
512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
513ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr = test::GetValue<Attribute>(&table_, "attr/bat");
514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(attr, nullptr);
515ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(attr->IsWeak());
516cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
517ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr = test::GetValue<Attribute>(&table_, "attr/baz");
518cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(attr, nullptr);
519ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(attr->IsWeak());
520cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(1u, attr->symbols.size());
521cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
522ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Id>(&table_, "id/foo"));
523cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
524ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(styleable, nullptr);
526cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(3u, styleable->entries.size());
527cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
528ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
529cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            styleable->entries[0].name.value());
530ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
531cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            styleable->entries[1].name.value());
5326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
5336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
534467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam LesinskiTEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
535cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
536cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<declare-styleable name=\"foo\" "
537cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
538cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"*android:bar\" />\n"
539cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <attr name=\"privAndroid:bat\" />\n"
540cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</declare-styleable>";
541ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
542ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
543cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, styleable);
544cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(2u, styleable->entries.size());
545cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
546ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(styleable->entries[0].private_reference);
547cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(styleable->entries[0].name);
548cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package);
549cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
550ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_TRUE(styleable->entries[1].private_reference);
551cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(styleable->entries[1].name);
552cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
553467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski}
554467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
5556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseArray) {
556cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
557cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<array name=\"foo\">\n"
558cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item>@string/ref</item>\n"
559cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item>hey</item>\n"
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item>23</item>\n"
561cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</array>";
562ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
5636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
564ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Array* array = test::GetValue<Array>(&table_, "array/foo");
565cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(array, nullptr);
566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(3u, array->items.size());
5676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
568ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, ValueCast<Reference>(array->items[0].get()));
569ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, ValueCast<String>(array->items[1].get()));
570ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(array->items[2].get()));
5716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
5726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5739ba47d813075fcb05c5e1532c137c93b394631cbAdam LesinskiTEST_F(ResourceParserTest, ParseStringArray) {
574d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  std::string input = R"EOF(
575d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      <string-array name="foo">
576d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski        <item>"Werk"</item>"
577d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      </string-array>)EOF";
578ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
579ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValue<Array>(&table_, "array/foo"));
5809ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski}
5819ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
582d5fd76a2ff78400505ade936fc36e707d69ecf72Adam LesinskiTEST_F(ResourceParserTest, ParseArrayWithFormat) {
583d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  std::string input = R"EOF(
584d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      <array name="foo" format="string">
585d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski        <item>100</item>
586d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      </array>)EOF";
587d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_TRUE(TestParse(input));
588d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
589d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  Array* array = test::GetValue<Array>(&table_, "array/foo");
590d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_NE(nullptr, array);
591d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
592d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_EQ(1u, array->items.size());
593d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
594d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  String* str = ValueCast<String>(array->items[0].get());
595d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_NE(nullptr, str);
596d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  EXPECT_EQ(std::string("100"), *str->value);
597d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski}
598d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
599d5fd76a2ff78400505ade936fc36e707d69ecf72Adam LesinskiTEST_F(ResourceParserTest, ParseArrayWithBadFormat) {
600d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  std::string input = R"EOF(
601d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      <array name="foo" format="integer">
602d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski        <item>Hi</item>
603d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      </array>)EOF";
604d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  ASSERT_FALSE(TestParse(input));
605d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski}
606d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
6076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParsePlural) {
608cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
609cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<plurals name=\"foo\">\n"
610cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item quantity=\"other\">apples</item>\n"
611cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  <item quantity=\"one\">apple</item>\n"
612cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</plurals>";
613ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
6148f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski
6158f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  Plural* plural = test::GetValue<Plural>(&table_, "plurals/foo");
6168f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  ASSERT_NE(nullptr, plural);
6178f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Zero]);
6188f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Two]);
6198f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Few]);
6208f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_EQ(nullptr, plural->values[Plural::Many]);
6218f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski
6228f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_NE(nullptr, plural->values[Plural::One]);
6238f7c550e20a6edbc9af7bb48675afaf8bcb3783fAdam Lesinski  EXPECT_NE(nullptr, plural->values[Plural::Other]);
6246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
6266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseCommentsWithResource) {
627cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
628cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--This is a comment-->\n"
629cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<string name=\"foo\">Hi</string>";
630ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
6316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
632ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* value = test::GetValue<String>(&table_, "string/foo");
633cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, value);
634ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(value->GetComment(), "This is a comment");
635e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski}
636e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
637e78fd617ec60139a973a01925fa7adad31febb39Adam LesinskiTEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
638cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
639cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--One-->\n"
640cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--Two-->\n"
641cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<string name=\"foo\">Hi</string>";
642e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
643ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
644e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
645ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* value = test::GetValue<String>(&table_, "string/foo");
646cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, value);
647ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(value->GetComment(), "Two");
648e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski}
649e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
650e78fd617ec60139a973a01925fa7adad31febb39Adam LesinskiTEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
651cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
652cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--One-->\n"
653cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<string name=\"foo\">\n"
654cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "  Hi\n"
655cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "<!--Two-->\n"
656cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      "</string>";
657e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski
658ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
6591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
660ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  String* value = test::GetValue<String>(&table_, "string/foo");
661cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, value);
662ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(value->GetComment(), "One");
6636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
665ca5638fd85098c3d0a699492751043545f75553aAdam LesinskiTEST_F(ResourceParserTest, ParseNestedComments) {
666cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // We only care about declare-styleable and enum/flag attributes because
667cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // comments
668cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // from those end up in R.java
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
670ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        <declare-styleable name="foo">
671ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <!-- The name of the bar -->
672ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <attr name="barName" format="string|reference" />
673ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        </declare-styleable>
674ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
675ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        <attr name="foo">
676ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <!-- The very first -->
677ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski          <enum name="one" value="1" />
678ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        </attr>)EOF";
679ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
680ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
681ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
682cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, styleable);
683cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(1u, styleable->entries.size());
684ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(StringPiece("The name of the bar"),
686ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            styleable->entries.front().GetComment());
687ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
688ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
689cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, attr);
690cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_EQ(1u, attr->symbols.size());
691ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_EQ(StringPiece("The very first"),
693ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            attr->symbols.front().symbol.GetComment());
694ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski}
695ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
6966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/*
6976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Declaring an ID as public should not require a separate definition
6986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * (as an ID has no value).
6996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
7006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
701cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = "<public type=\"id\" name=\"foo\"/>";
702ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
7036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
704ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Id* id = test::GetValue<Id>(&table_, "id/foo");
705cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, id);
7066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
7076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
708e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam LesinskiTEST_F(ResourceParserTest, KeepAllProducts) {
709cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
7107751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="foo" product="phone">hi</string>
7117751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="foo" product="no-sdcard">ho</string>
7127751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="bar" product="">wee</string>
7137751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="baz">woo</string>
7147751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="bit" product="phablet">hoot</string>
7157751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        <string name="bot" product="default">yes</string>
7167751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski    )EOF";
717ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
718ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
719ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
720ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/foo",
721ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "phone"));
722ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
723ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/foo",
724ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "no-sdcard"));
725cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_NE(nullptr,
726ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            test::GetValueForConfigAndProduct<String>(
727ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                &table_, "string/bar", ConfigDescription::DefaultConfig(), ""));
728cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  EXPECT_NE(nullptr,
729ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            test::GetValueForConfigAndProduct<String>(
730ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                &table_, "string/baz", ConfigDescription::DefaultConfig(), ""));
731ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
732ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/bit",
733ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "phablet"));
734ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
735ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         &table_, "string/bot",
736ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         ConfigDescription::DefaultConfig(), "default"));
7379ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski}
7389ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
73927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam LesinskiTEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
740cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(
74127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    <public-group type="attr" first-id="0x01010040">
74227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski      <public name="foo" />
74327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski      <public name="bar" />
74427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    </public-group>)EOF";
745ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
746cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
747cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Maybe<ResourceTable::SearchResult> result =
748ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_.FindResource(test::ParseNameOrDie("attr/foo"));
749cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
750cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
751cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().package->id);
752cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().type->id);
753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().entry->id);
754ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceId actual_id(result.value().package->id.value(),
755ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                       result.value().type->id.value(),
756ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                       result.value().entry->id.value());
757ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(ResourceId(0x01010040), actual_id);
758cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
759ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
760cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
761cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
762cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().package->id);
763cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().type->id);
764cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result.value().entry->id);
765ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  actual_id = ResourceId(result.value().package->id.value(),
766ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         result.value().type->id.value(),
767ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         result.value().entry->id.value());
768ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(ResourceId(0x01010041), actual_id);
76927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski}
77027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
771fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam LesinskiTEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
772cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input =
773cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
774ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
775fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam Lesinski
776cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
777ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_FALSE(TestParse(input));
778fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam Lesinski}
779fa10505ceaf9d4c41b76bb1b32f257926e96e441Adam Lesinski
7804488f1c74a0f7df09f2b201f7caa228d729e8389Adam LesinskiTEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
781cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
782ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
783a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
784cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Maybe<ResourceTable::SearchResult> result =
785ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_.FindResource(test::ParseNameOrDie("string/bar"));
786cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  AAPT_ASSERT_TRUE(result);
787cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const ResourceEntry* entry = result.value().entry;
788cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ASSERT_NE(nullptr, entry);
789ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state);
7904488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski  EXPECT_TRUE(entry->symbol_status.allow_new);
791a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski}
792a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
7937ff3ee19f4b831a526baf4b928d1ac172d070d82Adam LesinskiTEST_F(ResourceParserTest, ParseItemElementWithFormat) {
794e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  std::string input = R"(<item name="foo" type="integer" format="float">0.3</item>)";
795ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ASSERT_TRUE(TestParse(input));
7967ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
797e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
798e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_THAT(val, NotNull());
799e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  EXPECT_THAT(val->value.dataType, Eq(android::Res_value::TYPE_FLOAT));
800e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski
801e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  input = R"(<item name="bar" type="integer" format="fraction">100</item>)";
802e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_FALSE(TestParse(input));
803e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski}
804e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski
805e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski// An <item> without a format specifier accepts all types of values.
806e597d68d33c76c2b830f5497ed4ba74c5193a056Adam LesinskiTEST_F(ResourceParserTest, ParseItemElementWithoutFormat) {
807e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  std::string input = R"(<item name="foo" type="integer">100%p</item>)";
808e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_TRUE(TestParse(input));
8097ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
810e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
811e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  ASSERT_THAT(val, NotNull());
812e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski  EXPECT_THAT(val->value.dataType, Eq(android::Res_value::TYPE_FRACTION));
8137ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
8147ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
81586d67df8d57b9537666f9b54a9ca563779a2288bAdam LesinskiTEST_F(ResourceParserTest, ParseConfigVaryingItem) {
81686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  std::string input = R"EOF(<item name="foo" type="configVarying">Hey</item>)EOF";
81786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_TRUE(TestParse(input));
81886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_NE(nullptr, test::GetValue<String>(&table_, "configVarying/foo"));
81986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski}
82086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
82186d67df8d57b9537666f9b54a9ca563779a2288bAdam LesinskiTEST_F(ResourceParserTest, ParseBagElement) {
82286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  std::string input =
82386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      R"EOF(<bag name="bag" type="configVarying"><item name="test">Hello!</item></bag>)EOF";
82486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_TRUE(TestParse(input));
82586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
82686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  Style* val = test::GetValue<Style>(&table_, "configVarying/bag");
82786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_NE(nullptr, val);
82886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
82986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  ASSERT_EQ(1u, val->entries.size());
83086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  EXPECT_EQ(Reference(test::ParseNameOrDie("attr/test")), val->entries[0].key);
83186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  EXPECT_NE(nullptr, ValueCast<RawString>(val->entries[0].value.get()));
83286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski}
83386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
83490919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam LesinskiTEST_F(ResourceParserTest, ParseElementWithNoValue) {
83590919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  std::string input = R"(
83690919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski      <item type="drawable" format="reference" name="foo" />
83790919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski      <string name="foo" />)";
83890919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  ASSERT_TRUE(TestParse(input));
83990919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  ASSERT_THAT(test::GetValue(&table_, "drawable/foo"), Pointee(ValueEq(Reference())));
84090919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski
84190919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  String* str = test::GetValue<String>(&table_, "string/foo");
84290919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  ASSERT_THAT(str, NotNull());
84390919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  EXPECT_THAT(*str->value, Eq(""));
84490919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski}
84590919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski
846cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
847