ResourceParser_test.cpp revision 6f6ceb7e1456698b1f33e04536bfb3227f9fcfcb
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"
186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceTable.h"
196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceValues.h"
206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "SourceXmlPullParser.h"
216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include <gtest/gtest.h>
236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include <sstream>
246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include <string>
256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskinamespace aapt {
276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
286f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskiconstexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST(ResourceParserReferenceTest, ParseReferenceWithNoPackage) {
316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef actual;
336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool create = false;
346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool privateRef = false;
356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(ResourceParser::tryParseReference(u"@color/foo", &actual, &create, &privateRef));
366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(expected, actual);
376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(create);
386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(privateRef);
396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST(ResourceParserReferenceTest, ParseReferenceWithPackage) {
426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef actual;
446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool create = false;
456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool privateRef = false;
466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(ResourceParser::tryParseReference(u"@android:color/foo", &actual, &create,
476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                                                  &privateRef));
486f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(expected, actual);
496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(create);
506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(privateRef);
516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST(ResourceParserReferenceTest, ParseReferenceWithSurroundingWhitespace) {
546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef actual;
566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool create = false;
576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool privateRef = false;
586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(ResourceParser::tryParseReference(u"\t @android:color/foo\n \n\t", &actual,
596f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                                                  &create, &privateRef));
606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(expected, actual);
616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(create);
626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(privateRef);
636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST(ResourceParserReferenceTest, ParseAutoCreateIdReference) {
666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef actual;
686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool create = false;
696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool privateRef = false;
706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(ResourceParser::tryParseReference(u"@+android:id/foo", &actual, &create,
716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                                                  &privateRef));
726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(expected, actual);
736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(create);
746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(privateRef);
756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST(ResourceParserReferenceTest, ParsePrivateReference) {
786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef actual;
806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool create = false;
816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool privateRef = false;
826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(ResourceParser::tryParseReference(u"@*android:id/foo", &actual, &create,
836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                                                  &privateRef));
846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(expected, actual);
856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(create);
866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(privateRef);
876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST(ResourceParserReferenceTest, FailToParseAutoCreateNonIdReference) {
906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool create = false;
916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool privateRef = false;
926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceNameRef actual;
936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_FALSE(ResourceParser::tryParseReference(u"@+android:color/foo", &actual, &create,
946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                                                   &privateRef));
956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskistruct ResourceParserTest : public ::testing::Test {
986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    virtual void SetUp() override {
996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        mTable = std::make_shared<ResourceTable>();
1006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        mTable->setPackage(u"android");
1016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
1026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ::testing::AssertionResult testParse(std::istream& in) {
1046f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        std::stringstream input(kXmlPreamble);
1056f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        input << "<resources>" << std::endl
1066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski              << in.rdbuf() << std::endl
1076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski              << "</resources>" << std::endl;
1086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        ResourceParser parser(mTable, Source{ "test" }, {},
1096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                              std::make_shared<SourceXmlPullParser>(input));
1106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        if (parser.parse()) {
1116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            return ::testing::AssertionSuccess();
1126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
1136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return ::testing::AssertionFailure();
1146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
1156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    template <typename T>
1176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const T* findResource(const ResourceNameRef& name, const ConfigDescription& config) {
1186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        using std::begin;
1196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        using std::end;
1206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        const ResourceTableType* type;
1226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        const ResourceEntry* entry;
1236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        std::tie(type, entry) = mTable->findResource(name);
1246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        if (!type || !entry) {
1256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            return nullptr;
1266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
1276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1286f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        for (const auto& configValue : entry->values) {
1296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            if (configValue.config == config) {
1306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                return dynamic_cast<const T*>(configValue.value.get());
1316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
1326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
1336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return nullptr;
1346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
1356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    template <typename T>
1376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const T* findResource(const ResourceNameRef& name) {
1386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return findResource<T>(name, {});
1396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
1406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::shared_ptr<ResourceTable> mTable;
1426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski};
1436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, FailToParseWithNoRootResourcesElement) {
1456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input(kXmlPreamble);
1466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<attr name=\"foo\"/>" << std::endl;
1476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ResourceParser parser(mTable, {}, {}, std::make_shared<SourceXmlPullParser>(input));
1486f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_FALSE(parser.parse());
1496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
1506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseQuotedString) {
1526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input("<string name=\"foo\">   \"  hey there \" </string>");
1536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
1546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const String* str = findResource<String>(ResourceName{
1566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kString, u"foo"});
1576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(nullptr, str);
1586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(std::u16string(u"  hey there "), *str->value);
1596f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
1606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseEscapedString) {
1626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input("<string name=\"foo\">\\?123</string>");
1636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
1646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const String* str = findResource<String>(ResourceName{
1666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kString, u"foo" });
1676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(nullptr, str);
1686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(std::u16string(u"?123"), *str->value);
1696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
1706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAttr) {
1726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
1736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<attr name=\"foo\" format=\"string\"/>" << std::endl
1746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "<attr name=\"bar\"/>" << std::endl;
1756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
1766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Attribute* attr = findResource<Attribute>(ResourceName{
1786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"foo"});
1796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_NE(nullptr, attr);
1806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
1816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    attr = findResource<Attribute>(ResourceName{
1836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"bar"});
1846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_NE(nullptr, attr);
1856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
1866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
1876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
1896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
1906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<declare-styleable name=\"Styleable\">" << std::endl
1916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <attr name=\"foo\" />" << std::endl
1926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</declare-styleable>" << std::endl
1936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "<attr name=\"foo\" format=\"string\"/>" << std::endl;
1946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
1956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Attribute* attr = findResource<Attribute>(ResourceName{
1976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"foo"});
1986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(nullptr, attr);
1996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
2006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
2036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
2046f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<declare-styleable name=\"Theme\">" << std::endl
2056f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <attr name=\"foo\" />" << std::endl
2066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</declare-styleable>" << std::endl
2076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "<declare-styleable name=\"Window\">" << std::endl
2086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <attr name=\"foo\" format=\"boolean\"/>" << std::endl
2096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</declare-styleable>" << std::endl;
2106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
2126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Attribute* attr = findResource<Attribute>(ResourceName{
2146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"foo"});
2156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(nullptr, attr);
2166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
2176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseEnumAttr) {
2206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
2216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<attr name=\"foo\">" << std::endl
2226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <enum name=\"bar\" value=\"0\"/>" << std::endl
2236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <enum name=\"bat\" value=\"1\"/>" << std::endl
2246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <enum name=\"baz\" value=\"2\"/>" << std::endl
2256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</attr>" << std::endl;
2266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
2276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2286f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Attribute* enumAttr = findResource<Attribute>(ResourceName{
2296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"foo"});
2306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(enumAttr, nullptr);
2316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
2326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_EQ(enumAttr->symbols.size(), 3u);
2336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->symbols[0].symbol.name.entry, u"bar");
2356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->symbols[0].value, 0u);
2366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->symbols[1].symbol.name.entry, u"bat");
2386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->symbols[1].value, 1u);
2396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->symbols[2].symbol.name.entry, u"baz");
2416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(enumAttr->symbols[2].value, 2u);
2426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseFlagAttr) {
2456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
2466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<attr name=\"foo\">" << std::endl
2476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <flag name=\"bar\" value=\"0\"/>" << std::endl
2486f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <flag name=\"bat\" value=\"1\"/>" << std::endl
2496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <flag name=\"baz\" value=\"2\"/>" << std::endl
2506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</attr>" << std::endl;
2516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
2526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Attribute* flagAttr = findResource<Attribute>(ResourceName{
2546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"foo"});
2556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(flagAttr, nullptr);
2566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
2576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_EQ(flagAttr->symbols.size(), 3u);
2586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2596f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->symbols[0].symbol.name.entry, u"bar");
2606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->symbols[0].value, 0u);
2616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->symbols[1].symbol.name.entry, u"bat");
2636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->symbols[1].value, 1u);
2646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->symbols[2].symbol.name.entry, u"baz");
2666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagAttr->symbols[2].value, 2u);
2676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::unique_ptr<BinaryPrimitive> flagValue =
2696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            ResourceParser::tryParseFlagSymbol(*flagAttr, u"baz|bat");
2706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(flagValue, nullptr);
2716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(flagValue->value.data, 1u | 2u);
2726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
2756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
2766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<attr name=\"foo\">" << std::endl
2776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <enum name=\"bar\" value=\"0\"/>" << std::endl
2786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <enum name=\"bat\" value=\"1\"/>" << std::endl
2796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <enum name=\"bat\" value=\"2\"/>" << std::endl
2806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</attr>" << std::endl;
2816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_FALSE(testParse(input));
2826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseStyle) {
2856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
2866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<style name=\"foo\" parent=\"fu\">" << std::endl
2876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item name=\"bar\">#ffffffff</item>" << std::endl
2886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item name=\"bat\">@string/hey</item>" << std::endl
2896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item name=\"baz\"><b>hey</b></item>" << std::endl
2906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</style>" << std::endl;
2916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
2926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Style* style = findResource<Style>(ResourceName{
2946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kStyle, u"foo"});
2956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(style, nullptr);
2966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"fu"), style->parent.name);
2976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_EQ(style->entries.size(), 3u);
2986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(style->entries[0].key.name,
3006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski              (ResourceName{ u"android", ResourceType::kAttr, u"bar" }));
3016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(style->entries[1].key.name,
3026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski              (ResourceName{ u"android", ResourceType::kAttr, u"bat" }));
3036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(style->entries[2].key.name,
3046f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski              (ResourceName{ u"android", ResourceType::kAttr, u"baz" }));
3056f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
3086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
3096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<string name=\"foo\">@+id/bar</string>" << std::endl;
3106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
3116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"bar"});
3136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(id, nullptr);
3146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
3176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
3186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<declare-styleable name=\"foo\">" << std::endl
3196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <attr name=\"bar\" />" << std::endl
3206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <attr name=\"bat\" format=\"string|reference\"/>" << std::endl
3216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</declare-styleable>" << std::endl;
3226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
3236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Attribute* attr = findResource<Attribute>(ResourceName{
3256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kAttr, u"bar"});
3266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(attr, nullptr);
3276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(attr->isWeak());
3286f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    attr = findResource<Attribute>(ResourceName{ u"android", ResourceType::kAttr, u"bat"});
3306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(attr, nullptr);
3316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_TRUE(attr->isWeak());
3326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Styleable* styleable = findResource<Styleable>(ResourceName{
3346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kStyleable, u"foo" });
3356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(styleable, nullptr);
3366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_EQ(2u, styleable->entries.size());
3376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bar"}), styleable->entries[0].name);
3396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bat"}), styleable->entries[1].name);
3406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseArray) {
3436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
3446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<array name=\"foo\">" << std::endl
3456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item>@string/ref</item>" << std::endl
3466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item>hey</item>" << std::endl
3476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item>23</item>" << std::endl
3486f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</array>" << std::endl;
3496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
3506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Array* array = findResource<Array>(ResourceName{
3526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kArray, u"foo" });
3536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(array, nullptr);
3546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_EQ(3u, array->items.size());
3556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_NE(nullptr, dynamic_cast<const Reference*>(array->items[0].get()));
3576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_NE(nullptr, dynamic_cast<const String*>(array->items[1].get()));
3586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_NE(nullptr, dynamic_cast<const BinaryPrimitive*>(array->items[2].get()));
3596f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParsePlural) {
3626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
3636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<plurals name=\"foo\">" << std::endl
3646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item quantity=\"other\">apples</item>" << std::endl
3656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "  <item quantity=\"one\">apple</item>" << std::endl
3666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "</plurals>" << std::endl
3676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << std::endl;
3686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
3696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParseCommentsWithResource) {
3726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input;
3736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    input << "<!-- This is a comment -->" << std::endl
3746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski          << "<string name=\"foo\">Hi</string>" << std::endl;
3756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
3766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const ResourceTableType* type;
3786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const ResourceEntry* entry;
3796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::tie(type, entry) = mTable->findResource(ResourceName{
3806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            u"android", ResourceType::kString, u"foo"});
3816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(type, nullptr);
3826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(entry, nullptr);
3836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_FALSE(entry->values.empty());
3846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    EXPECT_EQ(entry->values.front().comment, u"This is a comment");
3856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/*
3886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Declaring an ID as public should not require a separate definition
3896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * (as an ID has no value).
3906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
3916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam LesinskiTEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
3926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::stringstream input("<public type=\"id\" name=\"foo\"/>");
3936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_TRUE(testParse(input));
3946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"foo" });
3966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    ASSERT_NE(nullptr, id);
3976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski} // namespace aapt
400