1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "ResourceParser.h"
18 #include "ResourceTable.h"
19 #include "ResourceValues.h"
20 #include "SourceXmlPullParser.h"
21 
22 #include <gtest/gtest.h>
23 #include <sstream>
24 #include <string>
25 
26 namespace aapt {
27 
28 constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
29 
TEST(ResourceParserReferenceTest,ParseReferenceWithNoPackage)30 TEST(ResourceParserReferenceTest, ParseReferenceWithNoPackage) {
31     ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
32     ResourceNameRef actual;
33     bool create = false;
34     bool privateRef = false;
35     EXPECT_TRUE(ResourceParser::tryParseReference(u"@color/foo", &actual, &create, &privateRef));
36     EXPECT_EQ(expected, actual);
37     EXPECT_FALSE(create);
38     EXPECT_FALSE(privateRef);
39 }
40 
TEST(ResourceParserReferenceTest,ParseReferenceWithPackage)41 TEST(ResourceParserReferenceTest, ParseReferenceWithPackage) {
42     ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
43     ResourceNameRef actual;
44     bool create = false;
45     bool privateRef = false;
46     EXPECT_TRUE(ResourceParser::tryParseReference(u"@android:color/foo", &actual, &create,
47                                                   &privateRef));
48     EXPECT_EQ(expected, actual);
49     EXPECT_FALSE(create);
50     EXPECT_FALSE(privateRef);
51 }
52 
TEST(ResourceParserReferenceTest,ParseReferenceWithSurroundingWhitespace)53 TEST(ResourceParserReferenceTest, ParseReferenceWithSurroundingWhitespace) {
54     ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
55     ResourceNameRef actual;
56     bool create = false;
57     bool privateRef = false;
58     EXPECT_TRUE(ResourceParser::tryParseReference(u"\t @android:color/foo\n \n\t", &actual,
59                                                   &create, &privateRef));
60     EXPECT_EQ(expected, actual);
61     EXPECT_FALSE(create);
62     EXPECT_FALSE(privateRef);
63 }
64 
TEST(ResourceParserReferenceTest,ParseAutoCreateIdReference)65 TEST(ResourceParserReferenceTest, ParseAutoCreateIdReference) {
66     ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
67     ResourceNameRef actual;
68     bool create = false;
69     bool privateRef = false;
70     EXPECT_TRUE(ResourceParser::tryParseReference(u"@+android:id/foo", &actual, &create,
71                                                   &privateRef));
72     EXPECT_EQ(expected, actual);
73     EXPECT_TRUE(create);
74     EXPECT_FALSE(privateRef);
75 }
76 
TEST(ResourceParserReferenceTest,ParsePrivateReference)77 TEST(ResourceParserReferenceTest, ParsePrivateReference) {
78     ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
79     ResourceNameRef actual;
80     bool create = false;
81     bool privateRef = false;
82     EXPECT_TRUE(ResourceParser::tryParseReference(u"@*android:id/foo", &actual, &create,
83                                                   &privateRef));
84     EXPECT_EQ(expected, actual);
85     EXPECT_FALSE(create);
86     EXPECT_TRUE(privateRef);
87 }
88 
TEST(ResourceParserReferenceTest,FailToParseAutoCreateNonIdReference)89 TEST(ResourceParserReferenceTest, FailToParseAutoCreateNonIdReference) {
90     bool create = false;
91     bool privateRef = false;
92     ResourceNameRef actual;
93     EXPECT_FALSE(ResourceParser::tryParseReference(u"@+android:color/foo", &actual, &create,
94                                                    &privateRef));
95 }
96 
TEST(ResourceParserReferenceTest,ParseStyleParentReference)97 TEST(ResourceParserReferenceTest, ParseStyleParentReference) {
98     Reference ref;
99     std::string errStr;
100     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"@android:style/foo", &ref, &errStr));
101     EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
102 
103     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"@style/foo", &ref, &errStr));
104     EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
105 
106     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"?android:style/foo", &ref, &errStr));
107     EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
108 
109     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"?style/foo", &ref, &errStr));
110     EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
111 
112     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"android:style/foo", &ref, &errStr));
113     EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
114 
115     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"android:foo", &ref, &errStr));
116     EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
117 
118     EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"foo", &ref, &errStr));
119     EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
120 }
121 
122 struct ResourceParserTest : public ::testing::Test {
SetUpaapt::ResourceParserTest123     virtual void SetUp() override {
124         mTable = std::make_shared<ResourceTable>();
125         mTable->setPackage(u"android");
126     }
127 
testParseaapt::ResourceParserTest128     ::testing::AssertionResult testParse(const StringPiece& str) {
129         std::stringstream input(kXmlPreamble);
130         input << "<resources>\n" << str << "\n</resources>" << std::endl;
131         ResourceParser parser(mTable, Source{ "test" }, {},
132                               std::make_shared<SourceXmlPullParser>(input));
133         if (parser.parse()) {
134             return ::testing::AssertionSuccess();
135         }
136         return ::testing::AssertionFailure();
137     }
138 
139     template <typename T>
findResourceaapt::ResourceParserTest140     const T* findResource(const ResourceNameRef& name, const ConfigDescription& config) {
141         using std::begin;
142         using std::end;
143 
144         const ResourceTableType* type;
145         const ResourceEntry* entry;
146         std::tie(type, entry) = mTable->findResource(name);
147         if (!type || !entry) {
148             return nullptr;
149         }
150 
151         for (const auto& configValue : entry->values) {
152             if (configValue.config == config) {
153                 return dynamic_cast<const T*>(configValue.value.get());
154             }
155         }
156         return nullptr;
157     }
158 
159     template <typename T>
findResourceaapt::ResourceParserTest160     const T* findResource(const ResourceNameRef& name) {
161         return findResource<T>(name, {});
162     }
163 
164     std::shared_ptr<ResourceTable> mTable;
165 };
166 
TEST_F(ResourceParserTest,FailToParseWithNoRootResourcesElement)167 TEST_F(ResourceParserTest, FailToParseWithNoRootResourcesElement) {
168     std::stringstream input(kXmlPreamble);
169     input << "<attr name=\"foo\"/>" << std::endl;
170     ResourceParser parser(mTable, {}, {}, std::make_shared<SourceXmlPullParser>(input));
171     ASSERT_FALSE(parser.parse());
172 }
173 
TEST_F(ResourceParserTest,ParseQuotedString)174 TEST_F(ResourceParserTest, ParseQuotedString) {
175     std::string input = "<string name=\"foo\">   \"  hey there \" </string>";
176     ASSERT_TRUE(testParse(input));
177 
178     const String* str = findResource<String>(ResourceName{
179             u"android", ResourceType::kString, u"foo"});
180     ASSERT_NE(nullptr, str);
181     EXPECT_EQ(std::u16string(u"  hey there "), *str->value);
182 }
183 
TEST_F(ResourceParserTest,ParseEscapedString)184 TEST_F(ResourceParserTest, ParseEscapedString) {
185     std::string input = "<string name=\"foo\">\\?123</string>";
186     ASSERT_TRUE(testParse(input));
187 
188     const String* str = findResource<String>(ResourceName{
189             u"android", ResourceType::kString, u"foo" });
190     ASSERT_NE(nullptr, str);
191     EXPECT_EQ(std::u16string(u"?123"), *str->value);
192 }
193 
TEST_F(ResourceParserTest,ParseNull)194 TEST_F(ResourceParserTest, ParseNull) {
195     std::string input = "<integer name=\"foo\">@null</integer>";
196     ASSERT_TRUE(testParse(input));
197 
198     // The Android runtime treats a value of android::Res_value::TYPE_NULL as
199     // a non-existing value, and this causes problems in styles when trying to resolve
200     // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
201     // with a data value of 0.
202     const BinaryPrimitive* integer = findResource<BinaryPrimitive>(ResourceName{
203             u"android", ResourceType::kInteger, u"foo" });
204     ASSERT_NE(nullptr, integer);
205     EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
206     EXPECT_EQ(0u, integer->value.data);
207 }
208 
TEST_F(ResourceParserTest,ParseEmpty)209 TEST_F(ResourceParserTest, ParseEmpty) {
210     std::string input = "<integer name=\"foo\">@empty</integer>";
211     ASSERT_TRUE(testParse(input));
212 
213     const BinaryPrimitive* integer = findResource<BinaryPrimitive>(ResourceName{
214             u"android", ResourceType::kInteger, u"foo" });
215     ASSERT_NE(nullptr, integer);
216     EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
217     EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
218 }
219 
TEST_F(ResourceParserTest,ParseAttr)220 TEST_F(ResourceParserTest, ParseAttr) {
221     std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
222                         "<attr name=\"bar\"/>";
223     ASSERT_TRUE(testParse(input));
224 
225     const Attribute* attr = findResource<Attribute>(ResourceName{
226             u"android", ResourceType::kAttr, u"foo"});
227     EXPECT_NE(nullptr, attr);
228     EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
229 
230     attr = findResource<Attribute>(ResourceName{
231             u"android", ResourceType::kAttr, u"bar"});
232     EXPECT_NE(nullptr, attr);
233     EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
234 }
235 
TEST_F(ResourceParserTest,ParseUseAndDeclOfAttr)236 TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
237     std::string input = "<declare-styleable name=\"Styleable\">\n"
238                         "  <attr name=\"foo\" />\n"
239                         "</declare-styleable>\n"
240                         "<attr name=\"foo\" format=\"string\"/>";
241     ASSERT_TRUE(testParse(input));
242 
243     const Attribute* attr = findResource<Attribute>(ResourceName{
244             u"android", ResourceType::kAttr, u"foo"});
245     ASSERT_NE(nullptr, attr);
246     EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
247 }
248 
TEST_F(ResourceParserTest,ParseDoubleUseOfAttr)249 TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
250     std::string input = "<declare-styleable name=\"Theme\">"
251                         "  <attr name=\"foo\" />\n"
252                         "</declare-styleable>\n"
253                         "<declare-styleable name=\"Window\">\n"
254                         "  <attr name=\"foo\" format=\"boolean\"/>\n"
255                         "</declare-styleable>";
256     ASSERT_TRUE(testParse(input));
257 
258     const Attribute* attr = findResource<Attribute>(ResourceName{
259             u"android", ResourceType::kAttr, u"foo"});
260     ASSERT_NE(nullptr, attr);
261     EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
262 }
263 
TEST_F(ResourceParserTest,ParseEnumAttr)264 TEST_F(ResourceParserTest, ParseEnumAttr) {
265     std::string input = "<attr name=\"foo\">\n"
266                         "  <enum name=\"bar\" value=\"0\"/>\n"
267                         "  <enum name=\"bat\" value=\"1\"/>\n"
268                         "  <enum name=\"baz\" value=\"2\"/>\n"
269                         "</attr>";
270     ASSERT_TRUE(testParse(input));
271 
272     const Attribute* enumAttr = findResource<Attribute>(ResourceName{
273             u"android", ResourceType::kAttr, u"foo"});
274     ASSERT_NE(enumAttr, nullptr);
275     EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
276     ASSERT_EQ(enumAttr->symbols.size(), 3u);
277 
278     EXPECT_EQ(enumAttr->symbols[0].symbol.name.entry, u"bar");
279     EXPECT_EQ(enumAttr->symbols[0].value, 0u);
280 
281     EXPECT_EQ(enumAttr->symbols[1].symbol.name.entry, u"bat");
282     EXPECT_EQ(enumAttr->symbols[1].value, 1u);
283 
284     EXPECT_EQ(enumAttr->symbols[2].symbol.name.entry, u"baz");
285     EXPECT_EQ(enumAttr->symbols[2].value, 2u);
286 }
287 
TEST_F(ResourceParserTest,ParseFlagAttr)288 TEST_F(ResourceParserTest, ParseFlagAttr) {
289     std::string input = "<attr name=\"foo\">\n"
290                         "  <flag name=\"bar\" value=\"0\"/>\n"
291                         "  <flag name=\"bat\" value=\"1\"/>\n"
292                         "  <flag name=\"baz\" value=\"2\"/>\n"
293                         "</attr>";
294     ASSERT_TRUE(testParse(input));
295 
296     const Attribute* flagAttr = findResource<Attribute>(ResourceName{
297             u"android", ResourceType::kAttr, u"foo"});
298     ASSERT_NE(flagAttr, nullptr);
299     EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
300     ASSERT_EQ(flagAttr->symbols.size(), 3u);
301 
302     EXPECT_EQ(flagAttr->symbols[0].symbol.name.entry, u"bar");
303     EXPECT_EQ(flagAttr->symbols[0].value, 0u);
304 
305     EXPECT_EQ(flagAttr->symbols[1].symbol.name.entry, u"bat");
306     EXPECT_EQ(flagAttr->symbols[1].value, 1u);
307 
308     EXPECT_EQ(flagAttr->symbols[2].symbol.name.entry, u"baz");
309     EXPECT_EQ(flagAttr->symbols[2].value, 2u);
310 
311     std::unique_ptr<BinaryPrimitive> flagValue =
312             ResourceParser::tryParseFlagSymbol(*flagAttr, u"baz|bat");
313     ASSERT_NE(flagValue, nullptr);
314     EXPECT_EQ(flagValue->value.data, 1u | 2u);
315 }
316 
TEST_F(ResourceParserTest,FailToParseEnumAttrWithNonUniqueKeys)317 TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
318     std::string input = "<attr name=\"foo\">\n"
319                         "  <enum name=\"bar\" value=\"0\"/>\n"
320                         "  <enum name=\"bat\" value=\"1\"/>\n"
321                         "  <enum name=\"bat\" value=\"2\"/>\n"
322                         "</attr>";
323     ASSERT_FALSE(testParse(input));
324 }
325 
TEST_F(ResourceParserTest,ParseStyle)326 TEST_F(ResourceParserTest, ParseStyle) {
327     std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n"
328                         "  <item name=\"bar\">#ffffffff</item>\n"
329                         "  <item name=\"bat\">@string/hey</item>\n"
330                         "  <item name=\"baz\"><b>hey</b></item>\n"
331                         "</style>";
332     ASSERT_TRUE(testParse(input));
333 
334     const Style* style = findResource<Style>(ResourceName{
335             u"android", ResourceType::kStyle, u"foo"});
336     ASSERT_NE(style, nullptr);
337     EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"fu"), style->parent.name);
338     ASSERT_EQ(style->entries.size(), 3u);
339 
340     EXPECT_EQ(style->entries[0].key.name,
341               (ResourceName{ u"android", ResourceType::kAttr, u"bar" }));
342     EXPECT_EQ(style->entries[1].key.name,
343               (ResourceName{ u"android", ResourceType::kAttr, u"bat" }));
344     EXPECT_EQ(style->entries[2].key.name,
345               (ResourceName{ u"android", ResourceType::kAttr, u"baz" }));
346 }
347 
TEST_F(ResourceParserTest,ParseStyleWithShorthandParent)348 TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
349     std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
350     ASSERT_TRUE(testParse(input));
351 
352     const Style* style = findResource<Style>(
353             ResourceName{ u"android", ResourceType::kStyle, u"foo" });
354     ASSERT_NE(style, nullptr);
355     EXPECT_EQ(ResourceNameRef(u"com.app", ResourceType::kStyle, u"Theme"), style->parent.name);
356 }
357 
TEST_F(ResourceParserTest,ParseStyleWithPackageAliasedParent)358 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
359     std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
360                         "       name=\"foo\" parent=\"app:Theme\"/>";
361     ASSERT_TRUE(testParse(input));
362 
363     const Style* style = findResource<Style>(ResourceName{
364             u"android", ResourceType::kStyle, u"foo" });
365     ASSERT_NE(style, nullptr);
366     EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"Theme"), style->parent.name);
367 }
368 
TEST_F(ResourceParserTest,ParseStyleWithPackageAliasedItems)369 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
370     std::string input =
371             "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n"
372             "  <item name=\"app:bar\">0</item>\n"
373             "</style>";
374     ASSERT_TRUE(testParse(input));
375 
376     const Style* style = findResource<Style>(ResourceName{
377             u"android", ResourceType::kStyle, u"foo" });
378     ASSERT_NE(style, nullptr);
379     ASSERT_EQ(1u, style->entries.size());
380     EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kAttr, u"bar"),
381               style->entries[0].key.name);
382 }
383 
TEST_F(ResourceParserTest,ParseStyleWithInferredParent)384 TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
385     std::string input = "<style name=\"foo.bar\"/>";
386     ASSERT_TRUE(testParse(input));
387 
388     const Style* style = findResource<Style>(ResourceName{
389             u"android", ResourceType::kStyle, u"foo.bar" });
390     ASSERT_NE(style, nullptr);
391     EXPECT_EQ(style->parent.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
392     EXPECT_TRUE(style->parentInferred);
393 }
394 
TEST_F(ResourceParserTest,ParseStyleWithInferredParentOverridenByEmptyParentAttribute)395 TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
396     std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
397     ASSERT_TRUE(testParse(input));
398 
399     const Style* style = findResource<Style>(ResourceName{
400             u"android", ResourceType::kStyle, u"foo.bar" });
401     ASSERT_NE(style, nullptr);
402     EXPECT_FALSE(style->parent.name.isValid());
403     EXPECT_FALSE(style->parentInferred);
404 }
405 
TEST_F(ResourceParserTest,ParseAutoGeneratedIdReference)406 TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
407     std::string input = "<string name=\"foo\">@+id/bar</string>";
408     ASSERT_TRUE(testParse(input));
409 
410     const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"bar"});
411     ASSERT_NE(id, nullptr);
412 }
413 
TEST_F(ResourceParserTest,ParseAttributesDeclareStyleable)414 TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
415     std::string input = "<declare-styleable name=\"foo\">\n"
416                         "  <attr name=\"bar\" />\n"
417                         "  <attr name=\"bat\" format=\"string|reference\"/>\n"
418                         "</declare-styleable>";
419     ASSERT_TRUE(testParse(input));
420 
421     const Attribute* attr = findResource<Attribute>(ResourceName{
422             u"android", ResourceType::kAttr, u"bar"});
423     ASSERT_NE(attr, nullptr);
424     EXPECT_TRUE(attr->isWeak());
425 
426     attr = findResource<Attribute>(ResourceName{ u"android", ResourceType::kAttr, u"bat"});
427     ASSERT_NE(attr, nullptr);
428     EXPECT_TRUE(attr->isWeak());
429 
430     const Styleable* styleable = findResource<Styleable>(ResourceName{
431             u"android", ResourceType::kStyleable, u"foo" });
432     ASSERT_NE(styleable, nullptr);
433     ASSERT_EQ(2u, styleable->entries.size());
434 
435     EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bar"}), styleable->entries[0].name);
436     EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bat"}), styleable->entries[1].name);
437 }
438 
TEST_F(ResourceParserTest,ParseArray)439 TEST_F(ResourceParserTest, ParseArray) {
440     std::string input = "<array name=\"foo\">\n"
441                         "  <item>@string/ref</item>\n"
442                         "  <item>hey</item>\n"
443                         "  <item>23</item>\n"
444                         "</array>";
445     ASSERT_TRUE(testParse(input));
446 
447     const Array* array = findResource<Array>(ResourceName{
448             u"android", ResourceType::kArray, u"foo" });
449     ASSERT_NE(array, nullptr);
450     ASSERT_EQ(3u, array->items.size());
451 
452     EXPECT_NE(nullptr, dynamic_cast<const Reference*>(array->items[0].get()));
453     EXPECT_NE(nullptr, dynamic_cast<const String*>(array->items[1].get()));
454     EXPECT_NE(nullptr, dynamic_cast<const BinaryPrimitive*>(array->items[2].get()));
455 }
456 
TEST_F(ResourceParserTest,ParsePlural)457 TEST_F(ResourceParserTest, ParsePlural) {
458     std::string input = "<plurals name=\"foo\">\n"
459                         "  <item quantity=\"other\">apples</item>\n"
460                         "  <item quantity=\"one\">apple</item>\n"
461                         "</plurals>";
462     ASSERT_TRUE(testParse(input));
463 }
464 
TEST_F(ResourceParserTest,ParseCommentsWithResource)465 TEST_F(ResourceParserTest, ParseCommentsWithResource) {
466     std::string input = "<!-- This is a comment -->\n"
467                         "<string name=\"foo\">Hi</string>";
468     ASSERT_TRUE(testParse(input));
469 
470     const ResourceTableType* type;
471     const ResourceEntry* entry;
472     std::tie(type, entry) = mTable->findResource(ResourceName{
473             u"android", ResourceType::kString, u"foo"});
474     ASSERT_NE(type, nullptr);
475     ASSERT_NE(entry, nullptr);
476     ASSERT_FALSE(entry->values.empty());
477     EXPECT_EQ(entry->values.front().comment, u"This is a comment");
478 }
479 
480 /*
481  * Declaring an ID as public should not require a separate definition
482  * (as an ID has no value).
483  */
TEST_F(ResourceParserTest,ParsePublicIdAsDefinition)484 TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
485     std::string input = "<public type=\"id\" name=\"foo\"/>";
486     ASSERT_TRUE(testParse(input));
487 
488     const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"foo" });
489     ASSERT_NE(nullptr, id);
490 }
491 
492 } // namespace aapt
493