blob: cf0fcd11b9035df22387935110ea5cb1d7a58d51 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070019#include "ResourceUtils.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "test/Context.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080022#include "xml/XmlPullParser.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080023
24#include <gtest/gtest.h>
25#include <sstream>
26#include <string>
27
28namespace aapt {
29
30constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
31
Adam Lesinski1ab598f2015-08-14 14:26:04 -070032TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
33 std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
34 std::stringstream input(kXmlPreamble);
35 input << "<attr name=\"foo\"/>" << std::endl;
36 ResourceTable table;
37 ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
Adam Lesinski467f1712015-11-16 17:35:44 -080038 xml::XmlPullParser xmlParser(input);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070039 ASSERT_FALSE(parser.parse(&xmlParser));
Adam Lesinski769de982015-04-10 19:43:55 -070040}
41
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080042struct ResourceParserTest : public ::testing::Test {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043 ResourceTable mTable;
44 std::unique_ptr<IAaptContext> mContext;
45
46 void SetUp() override {
47 mContext = test::ContextBuilder().build();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080048 }
49
Adam Lesinski52364f72016-01-11 13:10:24 -080050 ::testing::AssertionResult testParse(const StringPiece& str) {
51 return testParse(str, ConfigDescription{}, {});
52 }
53
54 ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
55 return testParse(str, config, {});
56 }
57
Adam Lesinski9ba47d82015-10-13 11:37:10 -070058 ::testing::AssertionResult testParse(const StringPiece& str,
Adam Lesinski52364f72016-01-11 13:10:24 -080059 std::initializer_list<std::u16string> products) {
60 return testParse(str, {}, std::move(products));
61 }
62
63 ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
64 std::initializer_list<std::u16string> products) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080065 std::stringstream input(kXmlPreamble);
Adam Lesinski24aad162015-04-24 19:19:30 -070066 input << "<resources>\n" << str << "\n</resources>" << std::endl;
Adam Lesinski9f222042015-11-04 13:51:45 -080067 ResourceParserOptions parserOptions;
Adam Lesinski7751afc2016-01-06 15:45:28 -080068 parserOptions.products = products;
Adam Lesinski52364f72016-01-11 13:10:24 -080069 ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
Adam Lesinski9f222042015-11-04 13:51:45 -080070 parserOptions);
Adam Lesinski467f1712015-11-16 17:35:44 -080071 xml::XmlPullParser xmlParser(input);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070072 if (parser.parse(&xmlParser)) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080073 return ::testing::AssertionSuccess();
74 }
75 return ::testing::AssertionFailure();
76 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080077};
78
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080079TEST_F(ResourceParserTest, ParseQuotedString) {
Adam Lesinski24aad162015-04-24 19:19:30 -070080 std::string input = "<string name=\"foo\"> \" hey there \" </string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080081 ASSERT_TRUE(testParse(input));
82
Adam Lesinski1ab598f2015-08-14 14:26:04 -070083 String* str = test::getValue<String>(&mTable, u"@string/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080084 ASSERT_NE(nullptr, str);
85 EXPECT_EQ(std::u16string(u" hey there "), *str->value);
86}
87
88TEST_F(ResourceParserTest, ParseEscapedString) {
Adam Lesinski24aad162015-04-24 19:19:30 -070089 std::string input = "<string name=\"foo\">\\?123</string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080090 ASSERT_TRUE(testParse(input));
91
Adam Lesinski1ab598f2015-08-14 14:26:04 -070092 String* str = test::getValue<String>(&mTable, u"@string/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080093 ASSERT_NE(nullptr, str);
94 EXPECT_EQ(std::u16string(u"?123"), *str->value);
95}
96
Adam Lesinski9f222042015-11-04 13:51:45 -080097TEST_F(ResourceParserTest, ParseFormattedString) {
98 std::string input = "<string name=\"foo\">%d %s</string>";
99 ASSERT_FALSE(testParse(input));
100
101 input = "<string name=\"foo\">%1$d %2$s</string>";
102 ASSERT_TRUE(testParse(input));
103}
104
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700105TEST_F(ResourceParserTest, IgnoreXliffTags) {
106 std::string input = "<string name=\"foo\" \n"
107 " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
108 " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
109 ASSERT_TRUE(testParse(input));
110
111 String* str = test::getValue<String>(&mTable, u"@string/foo");
112 ASSERT_NE(nullptr, str);
113 EXPECT_EQ(StringPiece16(u"There are %1$d apples"), StringPiece16(*str->value));
114}
115
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700116TEST_F(ResourceParserTest, ParseNull) {
117 std::string input = "<integer name=\"foo\">@null</integer>";
118 ASSERT_TRUE(testParse(input));
119
120 // The Android runtime treats a value of android::Res_value::TYPE_NULL as
121 // a non-existing value, and this causes problems in styles when trying to resolve
122 // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
123 // with a data value of 0.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700124 BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700125 ASSERT_NE(nullptr, integer);
126 EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
127 EXPECT_EQ(0u, integer->value.data);
128}
129
130TEST_F(ResourceParserTest, ParseEmpty) {
131 std::string input = "<integer name=\"foo\">@empty</integer>";
132 ASSERT_TRUE(testParse(input));
133
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700134 BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700135 ASSERT_NE(nullptr, integer);
136 EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
137 EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
138}
139
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800140TEST_F(ResourceParserTest, ParseAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700141 std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
142 "<attr name=\"bar\"/>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800143 ASSERT_TRUE(testParse(input));
144
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700145 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
146 ASSERT_NE(nullptr, attr);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800147 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
148
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700149 attr = test::getValue<Attribute>(&mTable, u"@attr/bar");
150 ASSERT_NE(nullptr, attr);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800151 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
152}
153
Adam Lesinski52364f72016-01-11 13:10:24 -0800154// Old AAPT allowed attributes to be defined under different configurations, but ultimately
155// stored them with the default configuration. Check that we have the same behavior.
156TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
157 const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
158 std::string input = R"EOF(
159 <attr name="foo" />
160 <declare-styleable name="bar">
161 <attr name="baz" />
162 </declare-styleable>)EOF";
163 ASSERT_TRUE(testParse(input, watchConfig));
164
165 EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/foo", watchConfig));
166 EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/baz", watchConfig));
167 EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, u"@styleable/bar", watchConfig));
168
169 EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/foo"));
170 EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/baz"));
171 EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, u"@styleable/bar"));
172}
173
Adam Lesinskia5870652015-11-20 15:32:30 -0800174TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
175 std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
176 ASSERT_TRUE(testParse(input));
177
178 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
179 ASSERT_NE(nullptr, attr);
180 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask);
181 EXPECT_EQ(10, attr->minInt);
182 EXPECT_EQ(23, attr->maxInt);
183}
184
185TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
186 std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
187 ASSERT_FALSE(testParse(input));
188}
189
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800190TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700191 std::string input = "<declare-styleable name=\"Styleable\">\n"
192 " <attr name=\"foo\" />\n"
193 "</declare-styleable>\n"
194 "<attr name=\"foo\" format=\"string\"/>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800195 ASSERT_TRUE(testParse(input));
196
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700197 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800198 ASSERT_NE(nullptr, attr);
199 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
200}
201
202TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700203 std::string input = "<declare-styleable name=\"Theme\">"
204 " <attr name=\"foo\" />\n"
205 "</declare-styleable>\n"
206 "<declare-styleable name=\"Window\">\n"
207 " <attr name=\"foo\" format=\"boolean\"/>\n"
208 "</declare-styleable>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800209 ASSERT_TRUE(testParse(input));
210
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700211 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800212 ASSERT_NE(nullptr, attr);
213 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
214}
215
216TEST_F(ResourceParserTest, ParseEnumAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700217 std::string input = "<attr name=\"foo\">\n"
218 " <enum name=\"bar\" value=\"0\"/>\n"
219 " <enum name=\"bat\" value=\"1\"/>\n"
220 " <enum name=\"baz\" value=\"2\"/>\n"
221 "</attr>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800222 ASSERT_TRUE(testParse(input));
223
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700224 Attribute* enumAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800225 ASSERT_NE(enumAttr, nullptr);
226 EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
227 ASSERT_EQ(enumAttr->symbols.size(), 3u);
228
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700229 AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name);
230 EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, u"bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800231 EXPECT_EQ(enumAttr->symbols[0].value, 0u);
232
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700233 AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name);
234 EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, u"bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800235 EXPECT_EQ(enumAttr->symbols[1].value, 1u);
236
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700237 AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name);
238 EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, u"baz");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800239 EXPECT_EQ(enumAttr->symbols[2].value, 2u);
240}
241
242TEST_F(ResourceParserTest, ParseFlagAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700243 std::string input = "<attr name=\"foo\">\n"
244 " <flag name=\"bar\" value=\"0\"/>\n"
245 " <flag name=\"bat\" value=\"1\"/>\n"
246 " <flag name=\"baz\" value=\"2\"/>\n"
247 "</attr>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800248 ASSERT_TRUE(testParse(input));
249
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700250 Attribute* flagAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800251 ASSERT_NE(nullptr, flagAttr);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800252 EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
253 ASSERT_EQ(flagAttr->symbols.size(), 3u);
254
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700255 AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name);
256 EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, u"bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800257 EXPECT_EQ(flagAttr->symbols[0].value, 0u);
258
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700259 AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name);
260 EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, u"bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800261 EXPECT_EQ(flagAttr->symbols[1].value, 1u);
262
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700263 AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name);
264 EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, u"baz");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800265 EXPECT_EQ(flagAttr->symbols[2].value, 2u);
266
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700267 std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr,
268 u"baz|bat");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800269 ASSERT_NE(nullptr, flagValue);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800270 EXPECT_EQ(flagValue->value.data, 1u | 2u);
271}
272
273TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700274 std::string input = "<attr name=\"foo\">\n"
275 " <enum name=\"bar\" value=\"0\"/>\n"
276 " <enum name=\"bat\" value=\"1\"/>\n"
277 " <enum name=\"bat\" value=\"2\"/>\n"
278 "</attr>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800279 ASSERT_FALSE(testParse(input));
280}
281
282TEST_F(ResourceParserTest, ParseStyle) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700283 std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n"
284 " <item name=\"bar\">#ffffffff</item>\n"
285 " <item name=\"bat\">@string/hey</item>\n"
286 " <item name=\"baz\"><b>hey</b></item>\n"
287 "</style>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800288 ASSERT_TRUE(testParse(input));
289
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700290 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800291 ASSERT_NE(nullptr, style);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700292 AAPT_ASSERT_TRUE(style->parent);
293 AAPT_ASSERT_TRUE(style->parent.value().name);
294 EXPECT_EQ(test::parseNameOrDie(u"@style/fu"), style->parent.value().name.value());
295 ASSERT_EQ(3u, style->entries.size());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800296
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700297 AAPT_ASSERT_TRUE(style->entries[0].key.name);
298 EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), style->entries[0].key.name.value());
299
300 AAPT_ASSERT_TRUE(style->entries[1].key.name);
301 EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), style->entries[1].key.name.value());
302
303 AAPT_ASSERT_TRUE(style->entries[2].key.name);
304 EXPECT_EQ(test::parseNameOrDie(u"@attr/baz"), style->entries[2].key.name.value());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800305}
306
Adam Lesinski769de982015-04-10 19:43:55 -0700307TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700308 std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
Adam Lesinski769de982015-04-10 19:43:55 -0700309 ASSERT_TRUE(testParse(input));
310
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800312 ASSERT_NE(nullptr, style);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700313 AAPT_ASSERT_TRUE(style->parent);
314 AAPT_ASSERT_TRUE(style->parent.value().name);
315 EXPECT_EQ(test::parseNameOrDie(u"@com.app:style/Theme"), style->parent.value().name.value());
Adam Lesinski769de982015-04-10 19:43:55 -0700316}
317
Adam Lesinski24aad162015-04-24 19:19:30 -0700318TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
319 std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
320 " name=\"foo\" parent=\"app:Theme\"/>";
321 ASSERT_TRUE(testParse(input));
322
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700323 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800324 ASSERT_NE(nullptr, style);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700325 AAPT_ASSERT_TRUE(style->parent);
326 AAPT_ASSERT_TRUE(style->parent.value().name);
327 EXPECT_EQ(test::parseNameOrDie(u"@android:style/Theme"), style->parent.value().name.value());
Adam Lesinski24aad162015-04-24 19:19:30 -0700328}
329
330TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
331 std::string input =
332 "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n"
333 " <item name=\"app:bar\">0</item>\n"
334 "</style>";
335 ASSERT_TRUE(testParse(input));
336
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700337 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800338 ASSERT_NE(nullptr, style);
Adam Lesinski24aad162015-04-24 19:19:30 -0700339 ASSERT_EQ(1u, style->entries.size());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700340 EXPECT_EQ(test::parseNameOrDie(u"@android:attr/bar"), style->entries[0].key.name.value());
Adam Lesinski24aad162015-04-24 19:19:30 -0700341}
342
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700343TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
344 std::string input = "<style name=\"foo.bar\"/>";
345 ASSERT_TRUE(testParse(input));
346
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700347 Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800348 ASSERT_NE(nullptr, style);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700349 AAPT_ASSERT_TRUE(style->parent);
350 AAPT_ASSERT_TRUE(style->parent.value().name);
351 EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie(u"@style/foo"));
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700352 EXPECT_TRUE(style->parentInferred);
353}
354
355TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
356 std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
357 ASSERT_TRUE(testParse(input));
358
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700359 Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800360 ASSERT_NE(nullptr, style);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700361 AAPT_EXPECT_FALSE(style->parent);
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700362 EXPECT_FALSE(style->parentInferred);
363}
364
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800365TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
366 std::string input = R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
367 ASSERT_TRUE(testParse(input));
368
369 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
370 ASSERT_NE(nullptr, style);
371 AAPT_ASSERT_TRUE(style->parent);
372 EXPECT_TRUE(style->parent.value().privateReference);
373}
374
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800375TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700376 std::string input = "<string name=\"foo\">@+id/bar</string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800377 ASSERT_TRUE(testParse(input));
378
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700379 Id* id = test::getValue<Id>(&mTable, u"@id/bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800380 ASSERT_NE(id, nullptr);
381}
382
383TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700384 std::string input = "<declare-styleable name=\"foo\">\n"
385 " <attr name=\"bar\" />\n"
386 " <attr name=\"bat\" format=\"string|reference\"/>\n"
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700387 " <attr name=\"baz\">\n"
388 " <enum name=\"foo\" value=\"1\"/>\n"
389 " </attr>\n"
Adam Lesinski24aad162015-04-24 19:19:30 -0700390 "</declare-styleable>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800391 ASSERT_TRUE(testParse(input));
392
Adam Lesinski9f222042015-11-04 13:51:45 -0800393 Maybe<ResourceTable::SearchResult> result =
394 mTable.findResource(test::parseNameOrDie(u"@styleable/foo"));
395 AAPT_ASSERT_TRUE(result);
396 EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
397
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700398 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800399 ASSERT_NE(attr, nullptr);
400 EXPECT_TRUE(attr->isWeak());
401
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700402 attr = test::getValue<Attribute>(&mTable, u"@attr/bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800403 ASSERT_NE(attr, nullptr);
404 EXPECT_TRUE(attr->isWeak());
405
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700406 attr = test::getValue<Attribute>(&mTable, u"@attr/baz");
407 ASSERT_NE(attr, nullptr);
408 EXPECT_TRUE(attr->isWeak());
409 EXPECT_EQ(1u, attr->symbols.size());
410
411 EXPECT_NE(nullptr, test::getValue<Id>(&mTable, u"@id/foo"));
412
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700413 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800414 ASSERT_NE(styleable, nullptr);
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700415 ASSERT_EQ(3u, styleable->entries.size());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800416
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700417 EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), styleable->entries[0].name.value());
418 EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800419}
420
Adam Lesinski467f1712015-11-16 17:35:44 -0800421TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
422 std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
423 " <attr name=\"*android:bar\" />\n"
424 " <attr name=\"privAndroid:bat\" />\n"
425 "</declare-styleable>";
426 ASSERT_TRUE(testParse(input));
427 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
428 ASSERT_NE(nullptr, styleable);
429 ASSERT_EQ(2u, styleable->entries.size());
430
431 EXPECT_TRUE(styleable->entries[0].privateReference);
432 AAPT_ASSERT_TRUE(styleable->entries[0].name);
433 EXPECT_EQ(std::u16string(u"android"), styleable->entries[0].name.value().package);
434
435 EXPECT_TRUE(styleable->entries[1].privateReference);
436 AAPT_ASSERT_TRUE(styleable->entries[1].name);
437 EXPECT_EQ(std::u16string(u"android"), styleable->entries[1].name.value().package);
438}
439
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800440TEST_F(ResourceParserTest, ParseArray) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700441 std::string input = "<array name=\"foo\">\n"
442 " <item>@string/ref</item>\n"
443 " <item>hey</item>\n"
444 " <item>23</item>\n"
445 "</array>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800446 ASSERT_TRUE(testParse(input));
447
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700448 Array* array = test::getValue<Array>(&mTable, u"@array/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800449 ASSERT_NE(array, nullptr);
450 ASSERT_EQ(3u, array->items.size());
451
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700452 EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
453 EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
454 EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800455}
456
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700457TEST_F(ResourceParserTest, ParseStringArray) {
458 std::string input = "<string-array name=\"foo\">\n"
459 " <item>\"Werk\"</item>\n"
460 "</string-array>\n";
461 ASSERT_TRUE(testParse(input));
462 EXPECT_NE(nullptr, test::getValue<Array>(&mTable, u"@array/foo"));
463}
464
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800465TEST_F(ResourceParserTest, ParsePlural) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700466 std::string input = "<plurals name=\"foo\">\n"
467 " <item quantity=\"other\">apples</item>\n"
468 " <item quantity=\"one\">apple</item>\n"
469 "</plurals>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800470 ASSERT_TRUE(testParse(input));
471}
472
473TEST_F(ResourceParserTest, ParseCommentsWithResource) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700474 std::string input = "<!--This is a comment-->\n"
Adam Lesinski24aad162015-04-24 19:19:30 -0700475 "<string name=\"foo\">Hi</string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800476 ASSERT_TRUE(testParse(input));
477
Adam Lesinskie78fd612015-10-22 12:48:43 -0700478 String* value = test::getValue<String>(&mTable, u"@string/foo");
479 ASSERT_NE(nullptr, value);
480 EXPECT_EQ(value->getComment(), u"This is a comment");
481}
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700482
Adam Lesinskie78fd612015-10-22 12:48:43 -0700483TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
484 std::string input = "<!--One-->\n"
485 "<!--Two-->\n"
486 "<string name=\"foo\">Hi</string>";
487
488 ASSERT_TRUE(testParse(input));
489
490 String* value = test::getValue<String>(&mTable, u"@string/foo");
491 ASSERT_NE(nullptr, value);
492 EXPECT_EQ(value->getComment(), u"Two");
493}
494
495TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
496 std::string input = "<!--One-->\n"
497 "<string name=\"foo\">\n"
498 " Hi\n"
499 "<!--Two-->\n"
500 "</string>";
501
502 ASSERT_TRUE(testParse(input));
503
504 String* value = test::getValue<String>(&mTable, u"@string/foo");
505 ASSERT_NE(nullptr, value);
506 EXPECT_EQ(value->getComment(), u"One");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800507}
508
Adam Lesinskica5638f2015-10-21 14:42:43 -0700509TEST_F(ResourceParserTest, ParseNestedComments) {
510 // We only care about declare-styleable and enum/flag attributes because comments
511 // from those end up in R.java
512 std::string input = R"EOF(
513 <declare-styleable name="foo">
514 <!-- The name of the bar -->
515 <attr name="barName" format="string|reference" />
516 </declare-styleable>
517
518 <attr name="foo">
519 <!-- The very first -->
520 <enum name="one" value="1" />
521 </attr>)EOF";
522 ASSERT_TRUE(testParse(input));
523
524 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
525 ASSERT_NE(nullptr, styleable);
526 ASSERT_EQ(1u, styleable->entries.size());
527
528 EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment());
529
530 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
531 ASSERT_NE(nullptr, attr);
532 ASSERT_EQ(1u, attr->symbols.size());
533
534 EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment());
535}
536
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800537/*
538 * Declaring an ID as public should not require a separate definition
539 * (as an ID has no value).
540 */
541TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700542 std::string input = "<public type=\"id\" name=\"foo\"/>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800543 ASSERT_TRUE(testParse(input));
544
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700545 Id* id = test::getValue<Id>(&mTable, u"@id/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800546 ASSERT_NE(nullptr, id);
547}
548
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700549TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
Adam Lesinski7751afc2016-01-06 15:45:28 -0800550 std::string input = R"EOF(
551 <string name="foo" product="phone">hi</string>
552 <string name="foo" product="no-sdcard">ho</string>
553 <string name="bar" product="">wee</string>
554 <string name="baz">woo</string>
555 <string name="bit" product="phablet">hoot</string>
556 <string name="bot" product="default">yes</string>
557 )EOF";
558 ASSERT_TRUE(testParse(input, { std::u16string(u"no-sdcard"), std::u16string(u"phablet") }));
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700559
560 String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
561 ASSERT_NE(nullptr, fooStr);
562 EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value);
563
564 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
565 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
Adam Lesinski7751afc2016-01-06 15:45:28 -0800566 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bit"));
567 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bot"));
568}
569
570TEST_F(ResourceParserTest, FilterProductsThatBothMatchInOrder) {
571 std::string input = R"EOF(
572 <string name="foo" product="phone">phone</string>
573 <string name="foo" product="default">default</string>
574 )EOF";
575 ASSERT_TRUE(testParse(input, { std::u16string(u"phone") }));
576
577 String* foo = test::getValue<String>(&mTable, u"@string/foo");
578 ASSERT_NE(nullptr, foo);
579 EXPECT_EQ(std::u16string(u"phone"), *foo->value);
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700580}
581
582TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
583 std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
Adam Lesinski7751afc2016-01-06 15:45:28 -0800584 ASSERT_FALSE(testParse(input, { std::u16string(u"phone") }));
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700585}
586
Adam Lesinski27afb9e2015-11-06 18:25:04 -0800587TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
588 std::string input = R"EOF(
589 <public-group type="attr" first-id="0x01010040">
590 <public name="foo" />
591 <public name="bar" />
592 </public-group>)EOF";
593 ASSERT_TRUE(testParse(input));
594
595 Maybe<ResourceTable::SearchResult> result = mTable.findResource(
596 test::parseNameOrDie(u"@attr/foo"));
597 AAPT_ASSERT_TRUE(result);
598
599 AAPT_ASSERT_TRUE(result.value().package->id);
600 AAPT_ASSERT_TRUE(result.value().type->id);
601 AAPT_ASSERT_TRUE(result.value().entry->id);
602 ResourceId actualId(result.value().package->id.value(),
603 result.value().type->id.value(),
604 result.value().entry->id.value());
605 EXPECT_EQ(ResourceId(0x01010040), actualId);
606
607 result = mTable.findResource(test::parseNameOrDie(u"@attr/bar"));
608 AAPT_ASSERT_TRUE(result);
609
610 AAPT_ASSERT_TRUE(result.value().package->id);
611 AAPT_ASSERT_TRUE(result.value().type->id);
612 AAPT_ASSERT_TRUE(result.value().entry->id);
613 actualId = ResourceId(result.value().package->id.value(),
614 result.value().type->id.value(),
615 result.value().entry->id.value());
616 EXPECT_EQ(ResourceId(0x01010041), actualId);
617}
618
Adam Lesinskifa105052015-11-07 13:34:39 -0800619TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
620 std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
621 ASSERT_TRUE(testParse(input));
622
623 input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
624 ASSERT_FALSE(testParse(input));
625}
626
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800627TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
628 std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
629 ASSERT_TRUE(testParse(input));
630
631 Maybe<ResourceTable::SearchResult> result = mTable.findResource(
632 test::parseNameOrDie(u"@string/bar"));
633 AAPT_ASSERT_TRUE(result);
634 const ResourceEntry* entry = result.value().entry;
635 ASSERT_NE(nullptr, entry);
636 EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
637}
638
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800639TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
640 std::string input = R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF";
641 ASSERT_TRUE(testParse(input));
642
643 BinaryPrimitive* val = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
644 ASSERT_NE(nullptr, val);
645
646 EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);
647}
648
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800649} // namespace aapt