• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "ResourceParser.h"
18 
19 #include <sstream>
20 #include <string>
21 
22 #include "ResourceTable.h"
23 #include "ResourceUtils.h"
24 #include "ResourceValues.h"
25 #include "io/StringInputStream.h"
26 #include "test/Test.h"
27 #include "xml/XmlPullParser.h"
28 
29 using ::aapt::io::StringInputStream;
30 using ::aapt::test::StrValueEq;
31 using ::aapt::test::ValueEq;
32 using ::android::ResTable_map;
33 using ::android::Res_value;
34 using ::android::StringPiece;
35 using ::testing::Eq;
36 using ::testing::IsEmpty;
37 using ::testing::IsNull;
38 using ::testing::NotNull;
39 using ::testing::Pointee;
40 using ::testing::SizeIs;
41 
42 namespace aapt {
43 
44 constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
45 
TEST(ResourceParserSingleTest,FailToParseWithNoRootResourcesElement)46 TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
47   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
48   ResourceTable table;
49   ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
50 
51   std::string input = kXmlPreamble;
52   input += R"(<attr name="foo"/>)";
53   StringInputStream in(input);
54   xml::XmlPullParser xml_parser(&in);
55   ASSERT_FALSE(parser.Parse(&xml_parser));
56 }
57 
58 class ResourceParserTest : public ::testing::Test {
59  public:
SetUp()60   void SetUp() override {
61     context_ = test::ContextBuilder().Build();
62   }
63 
TestParse(const StringPiece & str)64   ::testing::AssertionResult TestParse(const StringPiece& str) {
65     return TestParse(str, ConfigDescription{});
66   }
67 
TestParse(const StringPiece & str,const ConfigDescription & config)68   ::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) {
69     ResourceParserOptions parserOptions;
70     ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config,
71                           parserOptions);
72 
73     std::string input = kXmlPreamble;
74     input += "<resources>\n";
75     input.append(str.data(), str.size());
76     input += "\n</resources>";
77     StringInputStream in(input);
78     xml::XmlPullParser xmlParser(&in);
79     if (parser.Parse(&xmlParser)) {
80       return ::testing::AssertionSuccess();
81     }
82     return ::testing::AssertionFailure();
83   }
84 
85  protected:
86   ResourceTable table_;
87   std::unique_ptr<IAaptContext> context_;
88 };
89 
TEST_F(ResourceParserTest,ParseQuotedString)90 TEST_F(ResourceParserTest, ParseQuotedString) {
91   ASSERT_TRUE(TestParse(R"(<string name="foo">   "  hey there " </string>)"));
92 
93   String* str = test::GetValue<String>(&table_, "string/foo");
94   ASSERT_THAT(str, NotNull());
95   EXPECT_THAT(*str, StrValueEq("  hey there "));
96   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
97 }
98 
TEST_F(ResourceParserTest,ParseEscapedString)99 TEST_F(ResourceParserTest, ParseEscapedString) {
100   ASSERT_TRUE(TestParse(R"(<string name="foo">\?123</string>)"));
101 
102   String* str = test::GetValue<String>(&table_, "string/foo");
103   ASSERT_THAT(str, NotNull());
104   EXPECT_THAT(*str, StrValueEq("?123"));
105   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
106 
107   ASSERT_TRUE(TestParse(R"(<string name="bar">This isn\’t a bad string</string>)"));
108   str = test::GetValue<String>(&table_, "string/bar");
109   ASSERT_THAT(str, NotNull());
110   EXPECT_THAT(*str, StrValueEq("This isn’t a bad string"));
111 }
112 
TEST_F(ResourceParserTest,ParseFormattedString)113 TEST_F(ResourceParserTest, ParseFormattedString) {
114   ASSERT_FALSE(TestParse(R"(<string name="foo">%d %s</string>)"));
115   ASSERT_TRUE(TestParse(R"(<string name="foo">%1$d %2$s</string>)"));
116 }
117 
TEST_F(ResourceParserTest,ParseStyledString)118 TEST_F(ResourceParserTest, ParseStyledString) {
119   // Use a surrogate pair unicode point so that we can verify that the span
120   // indices use UTF-16 length and not UTF-8 length.
121   std::string input =
122       "<string name=\"foo\">This is my aunt\u2019s <b>fickle <small>string</small></b></string>";
123   ASSERT_TRUE(TestParse(input));
124 
125   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
126   ASSERT_THAT(str, NotNull());
127 
128   EXPECT_THAT(str->value->value, Eq("This is my aunt\u2019s fickle string"));
129   EXPECT_THAT(str->value->spans, SizeIs(2));
130   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
131 
132   EXPECT_THAT(*str->value->spans[0].name, Eq("b"));
133   EXPECT_THAT(str->value->spans[0].first_char, Eq(17u));
134   EXPECT_THAT(str->value->spans[0].last_char, Eq(30u));
135 
136   EXPECT_THAT(*str->value->spans[1].name, Eq("small"));
137   EXPECT_THAT(str->value->spans[1].first_char, Eq(24u));
138   EXPECT_THAT(str->value->spans[1].last_char, Eq(30u));
139 }
140 
TEST_F(ResourceParserTest,ParseStringWithWhitespace)141 TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
142   ASSERT_TRUE(TestParse(R"(<string name="foo">  This is what  I think  </string>)"));
143 
144   String* str = test::GetValue<String>(&table_, "string/foo");
145   ASSERT_THAT(str, NotNull());
146   EXPECT_THAT(*str->value, Eq("This is what I think"));
147   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
148 
149   ASSERT_TRUE(TestParse(R"(<string name="foo2">"  This is what  I think  "</string>)"));
150 
151   str = test::GetValue<String>(&table_, "string/foo2");
152   ASSERT_THAT(str, NotNull());
153   EXPECT_THAT(*str, StrValueEq("  This is what  I think  "));
154 }
155 
TEST_F(ResourceParserTest,IgnoreXliffTagsOtherThanG)156 TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
157   std::string input = R"(
158       <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
159           There are <xliff:source>no</xliff:source> apples</string>)";
160   ASSERT_TRUE(TestParse(input));
161 
162   String* str = test::GetValue<String>(&table_, "string/foo");
163   ASSERT_THAT(str, NotNull());
164   EXPECT_THAT(*str, StrValueEq("There are no apples"));
165   EXPECT_THAT(str->untranslatable_sections, IsEmpty());
166 }
167 
TEST_F(ResourceParserTest,NestedXliffGTagsAreIllegal)168 TEST_F(ResourceParserTest, NestedXliffGTagsAreIllegal) {
169   std::string input = R"(
170       <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
171           Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)";
172   EXPECT_FALSE(TestParse(input));
173 }
174 
TEST_F(ResourceParserTest,RecordUntranslateableXliffSectionsInString)175 TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInString) {
176   std::string input = R"(
177       <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
178           There are <xliff:g id="count">%1$d</xliff:g> apples</string>)";
179   ASSERT_TRUE(TestParse(input));
180 
181   String* str = test::GetValue<String>(&table_, "string/foo");
182   ASSERT_THAT(str, NotNull());
183   EXPECT_THAT(*str, StrValueEq("There are %1$d apples"));
184   ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
185 
186   // We expect indices and lengths that span to include the whitespace
187   // before %1$d. This is due to how the StringBuilder withholds whitespace unless
188   // needed (to deal with line breaks, etc.).
189   EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
190   EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
191 }
192 
TEST_F(ResourceParserTest,RecordUntranslateableXliffSectionsInStyledString)193 TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) {
194   std::string input = R"(
195       <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
196           There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)";
197   ASSERT_TRUE(TestParse(input));
198 
199   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
200   ASSERT_THAT(str, NotNull());
201   EXPECT_THAT(str->value->value, Eq("There are %1$d apples"));
202   ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
203 
204   // We expect indices and lengths that span to include the whitespace
205   // before %1$d. This is due to how the StringBuilder withholds whitespace unless
206   // needed (to deal with line breaks, etc.).
207   EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
208   EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
209 }
210 
TEST_F(ResourceParserTest,ParseNull)211 TEST_F(ResourceParserTest, ParseNull) {
212   std::string input = R"(<integer name="foo">@null</integer>)";
213   ASSERT_TRUE(TestParse(input));
214 
215   // The Android runtime treats a value of android::Res_value::TYPE_NULL as
216   // a non-existing value, and this causes problems in styles when trying to
217   // resolve an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
218   // with a data value of 0.
219   Reference* null_ref = test::GetValue<Reference>(&table_, "integer/foo");
220   ASSERT_THAT(null_ref, NotNull());
221   EXPECT_FALSE(null_ref->name);
222   EXPECT_FALSE(null_ref->id);
223   EXPECT_THAT(null_ref->reference_type, Eq(Reference::Type::kResource));
224 }
225 
TEST_F(ResourceParserTest,ParseEmpty)226 TEST_F(ResourceParserTest, ParseEmpty) {
227   std::string input = R"(<integer name="foo">@empty</integer>)";
228   ASSERT_TRUE(TestParse(input));
229 
230   BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
231   ASSERT_THAT(integer, NotNull());
232   EXPECT_THAT(integer->value.dataType, Eq(Res_value::TYPE_NULL));
233   EXPECT_THAT(integer->value.data, Eq(Res_value::DATA_NULL_EMPTY));
234 }
235 
TEST_F(ResourceParserTest,ParseAttr)236 TEST_F(ResourceParserTest, ParseAttr) {
237   std::string input = R"(
238       <attr name="foo" format="string"/>
239       <attr name="bar"/>)";
240   ASSERT_TRUE(TestParse(input));
241 
242   Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
243   ASSERT_THAT(attr, NotNull());
244   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_STRING));
245 
246   attr = test::GetValue<Attribute>(&table_, "attr/bar");
247   ASSERT_THAT(attr, NotNull());
248   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
249 }
250 
251 // Old AAPT allowed attributes to be defined under different configurations, but ultimately
252 // stored them with the default configuration. Check that we have the same behavior.
TEST_F(ResourceParserTest,ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig)253 TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
254   const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
255   std::string input = R"(
256       <attr name="foo" />
257       <declare-styleable name="bar">
258         <attr name="baz" />
259       </declare-styleable>)";
260   ASSERT_TRUE(TestParse(input, watch_config));
261 
262   EXPECT_THAT(test::GetValueForConfig<Attribute>(&table_, "attr/foo", watch_config), IsNull());
263   EXPECT_THAT(test::GetValueForConfig<Attribute>(&table_, "attr/baz", watch_config), IsNull());
264   EXPECT_THAT(test::GetValueForConfig<Styleable>(&table_, "styleable/bar", watch_config), IsNull());
265 
266   EXPECT_THAT(test::GetValue<Attribute>(&table_, "attr/foo"), NotNull());
267   EXPECT_THAT(test::GetValue<Attribute>(&table_, "attr/baz"), NotNull());
268   EXPECT_THAT(test::GetValue<Styleable>(&table_, "styleable/bar"), NotNull());
269 }
270 
TEST_F(ResourceParserTest,ParseAttrWithMinMax)271 TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
272   std::string input = R"(<attr name="foo" min="10" max="23" format="integer"/>)";
273   ASSERT_TRUE(TestParse(input));
274 
275   Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
276   ASSERT_THAT(attr, NotNull());
277   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_INTEGER));
278   EXPECT_THAT(attr->min_int, Eq(10));
279   EXPECT_THAT(attr->max_int, Eq(23));
280 }
281 
TEST_F(ResourceParserTest,FailParseAttrWithMinMaxButNotInteger)282 TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
283   ASSERT_FALSE(TestParse(R"(<attr name="foo" min="10" max="23" format="string"/>)"));
284 }
285 
TEST_F(ResourceParserTest,ParseUseAndDeclOfAttr)286 TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
287   std::string input = R"(
288       <declare-styleable name="Styleable">
289         <attr name="foo" />
290       </declare-styleable>
291       <attr name="foo" format="string"/>)";
292   ASSERT_TRUE(TestParse(input));
293 
294   Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
295   ASSERT_THAT(attr, NotNull());
296   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_STRING));
297 }
298 
TEST_F(ResourceParserTest,ParseDoubleUseOfAttr)299 TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
300   std::string input = R"(
301       <declare-styleable name="Theme">
302         <attr name="foo" />
303       </declare-styleable>
304       <declare-styleable name="Window">
305         <attr name="foo" format="boolean"/>
306       </declare-styleable>)";
307   ASSERT_TRUE(TestParse(input));
308 
309   Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
310   ASSERT_THAT(attr, NotNull());
311   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_BOOLEAN));
312 }
313 
TEST_F(ResourceParserTest,ParseEnumAttr)314 TEST_F(ResourceParserTest, ParseEnumAttr) {
315   std::string input = R"(
316       <attr name="foo">
317         <enum name="bar" value="0"/>
318         <enum name="bat" value="1"/>
319         <enum name="baz" value="2"/>
320       </attr>)";
321   ASSERT_TRUE(TestParse(input));
322 
323   Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo");
324   ASSERT_THAT(enum_attr, NotNull());
325   EXPECT_THAT(enum_attr->type_mask, Eq(ResTable_map::TYPE_ENUM));
326   ASSERT_THAT(enum_attr->symbols, SizeIs(3));
327 
328   ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
329   EXPECT_THAT(enum_attr->symbols[0].symbol.name.value().entry, Eq("bar"));
330   EXPECT_THAT(enum_attr->symbols[0].value, Eq(0u));
331 
332   ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
333   EXPECT_THAT(enum_attr->symbols[1].symbol.name.value().entry, Eq("bat"));
334   EXPECT_THAT(enum_attr->symbols[1].value, Eq(1u));
335 
336   ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
337   EXPECT_THAT(enum_attr->symbols[2].symbol.name.value().entry, Eq("baz"));
338   EXPECT_THAT(enum_attr->symbols[2].value, Eq(2u));
339 }
340 
TEST_F(ResourceParserTest,ParseFlagAttr)341 TEST_F(ResourceParserTest, ParseFlagAttr) {
342   std::string input = R"(
343       <attr name="foo">
344         <flag name="bar" value="0"/>
345         <flag name="bat" value="1"/>
346         <flag name="baz" value="2"/>
347       </attr>)";
348   ASSERT_TRUE(TestParse(input));
349 
350   Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo");
351   ASSERT_THAT(flag_attr, NotNull());
352   EXPECT_THAT(flag_attr->type_mask, Eq(ResTable_map::TYPE_FLAGS));
353   ASSERT_THAT(flag_attr->symbols, SizeIs(3));
354 
355   ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
356   EXPECT_THAT(flag_attr->symbols[0].symbol.name.value().entry, Eq("bar"));
357   EXPECT_THAT(flag_attr->symbols[0].value, Eq(0u));
358 
359   ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
360   EXPECT_THAT(flag_attr->symbols[1].symbol.name.value().entry, Eq("bat"));
361   EXPECT_THAT(flag_attr->symbols[1].value, Eq(1u));
362 
363   ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
364   EXPECT_THAT(flag_attr->symbols[2].symbol.name.value().entry, Eq("baz"));
365   EXPECT_THAT(flag_attr->symbols[2].value, Eq(2u));
366 
367   std::unique_ptr<BinaryPrimitive> flag_value =
368       ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat");
369   ASSERT_THAT(flag_value, NotNull());
370   EXPECT_THAT(flag_value->value.data, Eq(1u | 2u));
371 }
372 
TEST_F(ResourceParserTest,FailToParseEnumAttrWithNonUniqueKeys)373 TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
374   std::string input = R"(
375       <attr name="foo">
376         <enum name="bar" value="0"/>
377         <enum name="bat" value="1"/>
378         <enum name="bat" value="2"/>
379       </attr>)";
380   ASSERT_FALSE(TestParse(input));
381 }
382 
TEST_F(ResourceParserTest,ParseStyle)383 TEST_F(ResourceParserTest, ParseStyle) {
384   std::string input = R"(
385       <style name="foo" parent="@style/fu">
386         <item name="bar">#ffffffff</item>
387         <item name="bat">@string/hey</item>
388         <item name="baz"><b>hey</b></item>
389       </style>)";
390   ASSERT_TRUE(TestParse(input));
391 
392   Style* style = test::GetValue<Style>(&table_, "style/foo");
393   ASSERT_THAT(style, NotNull());
394   ASSERT_TRUE(style->parent);
395   EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/fu"))));
396   ASSERT_THAT(style->entries, SizeIs(3));
397 
398   EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
399   EXPECT_THAT(style->entries[1].key.name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
400   EXPECT_THAT(style->entries[2].key.name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
401 }
402 
TEST_F(ResourceParserTest,ParseStyleWithShorthandParent)403 TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
404   ASSERT_TRUE(TestParse(R"(<style name="foo" parent="com.app:Theme"/>)"));
405 
406   Style* style = test::GetValue<Style>(&table_, "style/foo");
407   ASSERT_THAT(style, NotNull());
408   ASSERT_TRUE(style->parent);
409   EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("com.app:style/Theme"))));
410 }
411 
TEST_F(ResourceParserTest,ParseStyleWithPackageAliasedParent)412 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
413   std::string input = R"(
414       <style xmlns:app="http://schemas.android.com/apk/res/android"
415           name="foo" parent="app:Theme"/>)";
416   ASSERT_TRUE(TestParse(input));
417 
418   Style* style = test::GetValue<Style>(&table_, "style/foo");
419   ASSERT_THAT(style, NotNull());
420   ASSERT_TRUE(style->parent);
421   ASSERT_TRUE(style->parent.value().name);
422   EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("android:style/Theme"))));
423 }
424 
425 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
426   std::string input = R"(
427       <style xmlns:app="http://schemas.android.com/apk/res/android" name="foo">
428         <item name="app:bar">0</item>
429       </style>)";
430   ASSERT_TRUE(TestParse(input));
431 
432   Style* style = test::GetValue<Style>(&table_, "style/foo");
433   ASSERT_THAT(style, NotNull());
434   ASSERT_THAT(style->entries, SizeIs(1));
435   EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar"))));
436 }
437 
438 TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
439   ASSERT_TRUE(TestParse(R"(<style name="foo.bar"/>)"));
440 
441   Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
442   ASSERT_THAT(style, NotNull());
443   ASSERT_TRUE(style->parent);
444   EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/foo"))));
445   EXPECT_TRUE(style->parent_inferred);
446 }
447 
448 TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
449   ASSERT_TRUE(TestParse(R"(<style name="foo.bar" parent=""/>)"));
450 
451   Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
452   ASSERT_THAT(style, NotNull());
453   EXPECT_FALSE(style->parent);
454   EXPECT_FALSE(style->parent_inferred);
455 }
456 
457 TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
458   ASSERT_TRUE(TestParse(R"(<style name="foo" parent="*android:style/bar" />)"));
459 
460   Style* style = test::GetValue<Style>(&table_, "style/foo");
461   ASSERT_THAT(style, NotNull());
462   ASSERT_TRUE(style->parent);
463   EXPECT_TRUE(style->parent.value().private_reference);
464 }
465 
466 TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
467   ASSERT_TRUE(TestParse(R"(<string name="foo">@+id/bar</string>)"));
468   ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull());
469 }
470 
471 TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
472   std::string input = R"(
473       <declare-styleable name="foo">
474         <attr name="bar" />
475         <attr name="bat" format="string|reference"/>
476         <attr name="baz">
477           <enum name="foo" value="1"/>
478         </attr>
479       </declare-styleable>)";
480   ASSERT_TRUE(TestParse(input));
481 
482   Maybe<ResourceTable::SearchResult> result =
483       table_.FindResource(test::ParseNameOrDie("styleable/foo"));
484   ASSERT_TRUE(result);
485   EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
486 
487   Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
488   ASSERT_THAT(attr, NotNull());
489   EXPECT_TRUE(attr->IsWeak());
490 
491   attr = test::GetValue<Attribute>(&table_, "attr/bat");
492   ASSERT_THAT(attr, NotNull());
493   EXPECT_TRUE(attr->IsWeak());
494 
495   attr = test::GetValue<Attribute>(&table_, "attr/baz");
496   ASSERT_THAT(attr, NotNull());
497   EXPECT_TRUE(attr->IsWeak());
498   EXPECT_THAT(attr->symbols, SizeIs(1));
499 
500   EXPECT_THAT(test::GetValue<Id>(&table_, "id/foo"), NotNull());
501 
502   Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
503   ASSERT_THAT(styleable, NotNull());
504   ASSERT_THAT(styleable->entries, SizeIs(3));
505 
506   EXPECT_THAT(styleable->entries[0].name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
507   EXPECT_THAT(styleable->entries[1].name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
508   EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
509 }
510 
511 TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
512   std::string input = R"(
513       <declare-styleable xmlns:privAndroid="http://schemas.android.com/apk/prv/res/android"
514           name="foo">
515         <attr name="*android:bar" />
516         <attr name="privAndroid:bat" />
517       </declare-styleable>)";
518   ASSERT_TRUE(TestParse(input));
519   Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
520   ASSERT_THAT(styleable, NotNull());
521   ASSERT_THAT(styleable->entries, SizeIs(2));
522 
523   EXPECT_TRUE(styleable->entries[0].private_reference);
524   ASSERT_TRUE(styleable->entries[0].name);
525   EXPECT_THAT(styleable->entries[0].name.value().package, Eq("android"));
526 
527   EXPECT_TRUE(styleable->entries[1].private_reference);
528   ASSERT_TRUE(styleable->entries[1].name);
529   EXPECT_THAT(styleable->entries[1].name.value().package, Eq("android"));
530 }
531 
532 TEST_F(ResourceParserTest, ParseArray) {
533   std::string input = R"(
534       <array name="foo">
535         <item>@string/ref</item>
536         <item>hey</item>
537         <item>23</item>
538       </array>)";
539   ASSERT_TRUE(TestParse(input));
540 
541   Array* array = test::GetValue<Array>(&table_, "array/foo");
542   ASSERT_THAT(array, NotNull());
543   ASSERT_THAT(array->elements, SizeIs(3));
544 
545   EXPECT_THAT(ValueCast<Reference>(array->elements[0].get()), NotNull());
546   EXPECT_THAT(ValueCast<String>(array->elements[1].get()), NotNull());
547   EXPECT_THAT(ValueCast<BinaryPrimitive>(array->elements[2].get()), NotNull());
548 }
549 
550 TEST_F(ResourceParserTest, ParseStringArray) {
551   std::string input = R"(
552       <string-array name="foo">
553         <item>"Werk"</item>"
554       </string-array>)";
555   ASSERT_TRUE(TestParse(input));
556   EXPECT_THAT(test::GetValue<Array>(&table_, "array/foo"), NotNull());
557 }
558 
TEST_F(ResourceParserTest,ParseArrayWithFormat)559 TEST_F(ResourceParserTest, ParseArrayWithFormat) {
560   std::string input = R"(
561       <array name="foo" format="string">
562         <item>100</item>
563       </array>)";
564   ASSERT_TRUE(TestParse(input));
565 
566   Array* array = test::GetValue<Array>(&table_, "array/foo");
567   ASSERT_THAT(array, NotNull());
568   ASSERT_THAT(array->elements, SizeIs(1));
569 
570   String* str = ValueCast<String>(array->elements[0].get());
571   ASSERT_THAT(str, NotNull());
572   EXPECT_THAT(*str, StrValueEq("100"));
573 }
574 
TEST_F(ResourceParserTest,ParseArrayWithBadFormat)575 TEST_F(ResourceParserTest, ParseArrayWithBadFormat) {
576   std::string input = R"(
577       <array name="foo" format="integer">
578         <item>Hi</item>
579       </array>)";
580   ASSERT_FALSE(TestParse(input));
581 }
582 
TEST_F(ResourceParserTest,ParsePlural)583 TEST_F(ResourceParserTest, ParsePlural) {
584   std::string input = R"(
585       <plurals name="foo">
586         <item quantity="other">apples</item>
587         <item quantity="one">apple</item>
588       </plurals>)";
589   ASSERT_TRUE(TestParse(input));
590 
591   Plural* plural = test::GetValue<Plural>(&table_, "plurals/foo");
592   ASSERT_THAT(plural, NotNull());
593   EXPECT_THAT(plural->values[Plural::Zero], IsNull());
594   EXPECT_THAT(plural->values[Plural::Two], IsNull());
595   EXPECT_THAT(plural->values[Plural::Few], IsNull());
596   EXPECT_THAT(plural->values[Plural::Many], IsNull());
597 
598   EXPECT_THAT(plural->values[Plural::One], NotNull());
599   EXPECT_THAT(plural->values[Plural::Other], NotNull());
600 }
601 
TEST_F(ResourceParserTest,ParseCommentsWithResource)602 TEST_F(ResourceParserTest, ParseCommentsWithResource) {
603   std::string input = R"(
604       <!--This is a comment-->
605       <string name="foo">Hi</string>)";
606   ASSERT_TRUE(TestParse(input));
607 
608   String* value = test::GetValue<String>(&table_, "string/foo");
609   ASSERT_THAT(value, NotNull());
610   EXPECT_THAT(value->GetComment(), Eq("This is a comment"));
611 }
612 
TEST_F(ResourceParserTest,DoNotCombineMultipleComments)613 TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
614   std::string input = R"(
615       <!--One-->
616       <!--Two-->
617       <string name="foo">Hi</string>)";
618 
619   ASSERT_TRUE(TestParse(input));
620 
621   String* value = test::GetValue<String>(&table_, "string/foo");
622   ASSERT_THAT(value, NotNull());
623   EXPECT_THAT(value->GetComment(), Eq("Two"));
624 }
625 
TEST_F(ResourceParserTest,IgnoreCommentBeforeEndTag)626 TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
627   std::string input = R"(
628       <!--One-->
629       <string name="foo">
630         Hi
631       <!--Two-->
632       </string>)";
633   ASSERT_TRUE(TestParse(input));
634 
635   String* value = test::GetValue<String>(&table_, "string/foo");
636   ASSERT_THAT(value, NotNull());
637   EXPECT_THAT(value->GetComment(), Eq("One"));
638 }
639 
TEST_F(ResourceParserTest,ParseNestedComments)640 TEST_F(ResourceParserTest, ParseNestedComments) {
641   // We only care about declare-styleable and enum/flag attributes because
642   // comments from those end up in R.java
643   std::string input = R"(
644       <declare-styleable name="foo">
645         <!-- The name of the bar -->
646         <attr name="barName" format="string|reference" />
647       </declare-styleable>
648 
649       <attr name="foo">
650         <!-- The very first -->
651         <enum name="one" value="1" />
652       </attr>)";
653   ASSERT_TRUE(TestParse(input));
654 
655   Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
656   ASSERT_THAT(styleable, NotNull());
657   ASSERT_THAT(styleable->entries, SizeIs(1));
658   EXPECT_THAT(styleable->entries[0].GetComment(), Eq("The name of the bar"));
659 
660   Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
661   ASSERT_THAT(attr, NotNull());
662   ASSERT_THAT(attr->symbols, SizeIs(1));
663   EXPECT_THAT(attr->symbols[0].symbol.GetComment(), Eq("The very first"));
664 }
665 
666 // Declaring an ID as public should not require a separate definition (as an ID has no value).
TEST_F(ResourceParserTest,ParsePublicIdAsDefinition)667 TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
668   ASSERT_TRUE(TestParse(R"(<public type="id" name="foo"/>)"));
669   ASSERT_THAT(test::GetValue<Id>(&table_, "id/foo"), NotNull());
670 }
671 
TEST_F(ResourceParserTest,KeepAllProducts)672 TEST_F(ResourceParserTest, KeepAllProducts) {
673   std::string input = R"(
674       <string name="foo" product="phone">hi</string>
675       <string name="foo" product="no-sdcard">ho</string>
676       <string name="bar" product="">wee</string>
677       <string name="baz">woo</string>
678       <string name="bit" product="phablet">hoot</string>
679       <string name="bot" product="default">yes</string>)";
680   ASSERT_TRUE(TestParse(input));
681 
682   ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/foo", ConfigDescription::DefaultConfig(), "phone"), NotNull());
683   ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/foo",ConfigDescription::DefaultConfig(), "no-sdcard"), NotNull());
684   ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bar", ConfigDescription::DefaultConfig(), ""), NotNull());
685   ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/baz", ConfigDescription::DefaultConfig(), ""), NotNull());
686   ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bit", ConfigDescription::DefaultConfig(), "phablet"), NotNull());
687   ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bot", ConfigDescription::DefaultConfig(), "default"), NotNull());
688 }
689 
TEST_F(ResourceParserTest,AutoIncrementIdsInPublicGroup)690 TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
691   std::string input = R"(
692       <public-group type="attr" first-id="0x01010040">
693         <public name="foo" />
694         <public name="bar" />
695       </public-group>)";
696   ASSERT_TRUE(TestParse(input));
697 
698   Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
699   ASSERT_TRUE(result);
700 
701   ASSERT_TRUE(result.value().package->id);
702   ASSERT_TRUE(result.value().type->id);
703   ASSERT_TRUE(result.value().entry->id);
704   ResourceId actual_id(result.value().package->id.value(),
705                        result.value().type->id.value(),
706                        result.value().entry->id.value());
707   EXPECT_THAT(actual_id, Eq(ResourceId(0x01010040)));
708 
709   result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
710   ASSERT_TRUE(result);
711 
712   ASSERT_TRUE(result.value().package->id);
713   ASSERT_TRUE(result.value().type->id);
714   ASSERT_TRUE(result.value().entry->id);
715   actual_id = ResourceId(result.value().package->id.value(),
716                          result.value().type->id.value(),
717                          result.value().entry->id.value());
718   EXPECT_THAT(actual_id, Eq(ResourceId(0x01010041)));
719 }
720 
TEST_F(ResourceParserTest,ExternalTypesShouldOnlyBeReferences)721 TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
722   ASSERT_TRUE(TestParse(R"(<item type="layout" name="foo">@layout/bar</item>)"));
723   ASSERT_FALSE(TestParse(R"(<item type="layout" name="bar">"this is a string"</item>)"));
724 }
725 
TEST_F(ResourceParserTest,AddResourcesElementShouldAddEntryWithUndefinedSymbol)726 TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
727   ASSERT_TRUE(TestParse(R"(<add-resource name="bar" type="string" />)"));
728 
729   Maybe<ResourceTable::SearchResult> result =
730       table_.FindResource(test::ParseNameOrDie("string/bar"));
731   ASSERT_TRUE(result);
732   const ResourceEntry* entry = result.value().entry;
733   ASSERT_THAT(entry, NotNull());
734   EXPECT_THAT(entry->symbol_status.state, Eq(SymbolState::kUndefined));
735   EXPECT_TRUE(entry->symbol_status.allow_new);
736 }
737 
TEST_F(ResourceParserTest,ParseItemElementWithFormat)738 TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
739   ASSERT_TRUE(TestParse(R"(<item name="foo" type="integer" format="float">0.3</item>)"));
740 
741   BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
742   ASSERT_THAT(val, NotNull());
743   EXPECT_THAT(val->value.dataType, Eq(Res_value::TYPE_FLOAT));
744 
745   ASSERT_FALSE(TestParse(R"(<item name="bar" type="integer" format="fraction">100</item>)"));
746 }
747 
748 // An <item> without a format specifier accepts all types of values.
TEST_F(ResourceParserTest,ParseItemElementWithoutFormat)749 TEST_F(ResourceParserTest, ParseItemElementWithoutFormat) {
750   ASSERT_TRUE(TestParse(R"(<item name="foo" type="integer">100%p</item>)"));
751 
752   BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
753   ASSERT_THAT(val, NotNull());
754   EXPECT_THAT(val->value.dataType, Eq(Res_value::TYPE_FRACTION));
755 }
756 
TEST_F(ResourceParserTest,ParseConfigVaryingItem)757 TEST_F(ResourceParserTest, ParseConfigVaryingItem) {
758   ASSERT_TRUE(TestParse(R"(<item name="foo" type="configVarying">Hey</item>)"));
759   ASSERT_THAT(test::GetValue<String>(&table_, "configVarying/foo"), NotNull());
760 }
761 
TEST_F(ResourceParserTest,ParseBagElement)762 TEST_F(ResourceParserTest, ParseBagElement) {
763   std::string input = R"(
764       <bag name="bag" type="configVarying">
765         <item name="test">Hello!</item>
766       </bag>)";
767   ASSERT_TRUE(TestParse(input));
768 
769   Style* val = test::GetValue<Style>(&table_, "configVarying/bag");
770   ASSERT_THAT(val, NotNull());
771   ASSERT_THAT(val->entries, SizeIs(1));
772 
773   EXPECT_THAT(val->entries[0].key, Eq(Reference(test::ParseNameOrDie("attr/test"))));
774   EXPECT_THAT(ValueCast<RawString>(val->entries[0].value.get()), NotNull());
775 }
776 
TEST_F(ResourceParserTest,ParseElementWithNoValue)777 TEST_F(ResourceParserTest, ParseElementWithNoValue) {
778   std::string input = R"(
779       <item type="drawable" format="reference" name="foo" />
780       <string name="foo" />)";
781   ASSERT_TRUE(TestParse(input));
782   ASSERT_THAT(test::GetValue(&table_, "drawable/foo"), Pointee(ValueEq(Reference())));
783 
784   String* str = test::GetValue<String>(&table_, "string/foo");
785   ASSERT_THAT(str, NotNull());
786   EXPECT_THAT(*str, StrValueEq(""));
787 }
788 
TEST_F(ResourceParserTest,ParsePlatformIndependentNewline)789 TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) {
790   ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)"));
791 }
792 
793 }  // namespace aapt
794