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