blob: b59eb95e74487f9557f8b7213d576e104fba8e91 [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 "XmlPullParser.h"
22
23#include "test/Context.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080024
25#include <gtest/gtest.h>
26#include <sstream>
27#include <string>
28
29namespace aapt {
30
31constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
32
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
34 std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
35 std::stringstream input(kXmlPreamble);
36 input << "<attr name=\"foo\"/>" << std::endl;
37 ResourceTable table;
38 ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
39 XmlPullParser xmlParser(input);
40 ASSERT_FALSE(parser.parse(&xmlParser));
Adam Lesinski769de982015-04-10 19:43:55 -070041}
42
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043struct ResourceParserTest : public ::testing::Test {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070044 ResourceTable mTable;
45 std::unique_ptr<IAaptContext> mContext;
46
47 void SetUp() override {
48 mContext = test::ContextBuilder().build();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080049 }
50
Adam Lesinski9ba47d82015-10-13 11:37:10 -070051 ::testing::AssertionResult testParse(const StringPiece& str,
52 Maybe<std::u16string> product = {}) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080053 std::stringstream input(kXmlPreamble);
Adam Lesinski24aad162015-04-24 19:19:30 -070054 input << "<resources>\n" << str << "\n</resources>" << std::endl;
Adam Lesinski9f222042015-11-04 13:51:45 -080055 ResourceParserOptions parserOptions;
56 parserOptions.product = product;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070057 ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
Adam Lesinski9f222042015-11-04 13:51:45 -080058 parserOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070059 XmlPullParser xmlParser(input);
60 if (parser.parse(&xmlParser)) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080061 return ::testing::AssertionSuccess();
62 }
63 return ::testing::AssertionFailure();
64 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080065};
66
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067TEST_F(ResourceParserTest, ParseQuotedString) {
Adam Lesinski24aad162015-04-24 19:19:30 -070068 std::string input = "<string name=\"foo\"> \" hey there \" </string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080069 ASSERT_TRUE(testParse(input));
70
Adam Lesinski1ab598f2015-08-14 14:26:04 -070071 String* str = test::getValue<String>(&mTable, u"@string/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080072 ASSERT_NE(nullptr, str);
73 EXPECT_EQ(std::u16string(u" hey there "), *str->value);
74}
75
76TEST_F(ResourceParserTest, ParseEscapedString) {
Adam Lesinski24aad162015-04-24 19:19:30 -070077 std::string input = "<string name=\"foo\">\\?123</string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080078 ASSERT_TRUE(testParse(input));
79
Adam Lesinski1ab598f2015-08-14 14:26:04 -070080 String* str = test::getValue<String>(&mTable, u"@string/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080081 ASSERT_NE(nullptr, str);
82 EXPECT_EQ(std::u16string(u"?123"), *str->value);
83}
84
Adam Lesinski9f222042015-11-04 13:51:45 -080085TEST_F(ResourceParserTest, ParseFormattedString) {
86 std::string input = "<string name=\"foo\">%d %s</string>";
87 ASSERT_FALSE(testParse(input));
88
89 input = "<string name=\"foo\">%1$d %2$s</string>";
90 ASSERT_TRUE(testParse(input));
91}
92
Adam Lesinski1ab598f2015-08-14 14:26:04 -070093TEST_F(ResourceParserTest, IgnoreXliffTags) {
94 std::string input = "<string name=\"foo\" \n"
95 " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
96 " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
97 ASSERT_TRUE(testParse(input));
98
99 String* str = test::getValue<String>(&mTable, u"@string/foo");
100 ASSERT_NE(nullptr, str);
101 EXPECT_EQ(StringPiece16(u"There are %1$d apples"), StringPiece16(*str->value));
102}
103
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700104TEST_F(ResourceParserTest, ParseNull) {
105 std::string input = "<integer name=\"foo\">@null</integer>";
106 ASSERT_TRUE(testParse(input));
107
108 // The Android runtime treats a value of android::Res_value::TYPE_NULL as
109 // a non-existing value, and this causes problems in styles when trying to resolve
110 // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
111 // with a data value of 0.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700112 BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700113 ASSERT_NE(nullptr, integer);
114 EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
115 EXPECT_EQ(0u, integer->value.data);
116}
117
118TEST_F(ResourceParserTest, ParseEmpty) {
119 std::string input = "<integer name=\"foo\">@empty</integer>";
120 ASSERT_TRUE(testParse(input));
121
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700122 BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700123 ASSERT_NE(nullptr, integer);
124 EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
125 EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
126}
127
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800128TEST_F(ResourceParserTest, ParseAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700129 std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
130 "<attr name=\"bar\"/>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800131 ASSERT_TRUE(testParse(input));
132
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
134 ASSERT_NE(nullptr, attr);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800135 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
136
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 attr = test::getValue<Attribute>(&mTable, u"@attr/bar");
138 ASSERT_NE(nullptr, attr);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800139 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
140}
141
142TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700143 std::string input = "<declare-styleable name=\"Styleable\">\n"
144 " <attr name=\"foo\" />\n"
145 "</declare-styleable>\n"
146 "<attr name=\"foo\" format=\"string\"/>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800147 ASSERT_TRUE(testParse(input));
148
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700149 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800150 ASSERT_NE(nullptr, attr);
151 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
152}
153
154TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700155 std::string input = "<declare-styleable name=\"Theme\">"
156 " <attr name=\"foo\" />\n"
157 "</declare-styleable>\n"
158 "<declare-styleable name=\"Window\">\n"
159 " <attr name=\"foo\" format=\"boolean\"/>\n"
160 "</declare-styleable>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800161 ASSERT_TRUE(testParse(input));
162
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700163 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800164 ASSERT_NE(nullptr, attr);
165 EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
166}
167
168TEST_F(ResourceParserTest, ParseEnumAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700169 std::string input = "<attr name=\"foo\">\n"
170 " <enum name=\"bar\" value=\"0\"/>\n"
171 " <enum name=\"bat\" value=\"1\"/>\n"
172 " <enum name=\"baz\" value=\"2\"/>\n"
173 "</attr>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800174 ASSERT_TRUE(testParse(input));
175
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700176 Attribute* enumAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800177 ASSERT_NE(enumAttr, nullptr);
178 EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
179 ASSERT_EQ(enumAttr->symbols.size(), 3u);
180
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700181 AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name);
182 EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, u"bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800183 EXPECT_EQ(enumAttr->symbols[0].value, 0u);
184
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700185 AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name);
186 EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, u"bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800187 EXPECT_EQ(enumAttr->symbols[1].value, 1u);
188
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700189 AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name);
190 EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, u"baz");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800191 EXPECT_EQ(enumAttr->symbols[2].value, 2u);
192}
193
194TEST_F(ResourceParserTest, ParseFlagAttr) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700195 std::string input = "<attr name=\"foo\">\n"
196 " <flag name=\"bar\" value=\"0\"/>\n"
197 " <flag name=\"bat\" value=\"1\"/>\n"
198 " <flag name=\"baz\" value=\"2\"/>\n"
199 "</attr>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800200 ASSERT_TRUE(testParse(input));
201
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700202 Attribute* flagAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800203 ASSERT_NE(flagAttr, nullptr);
204 EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
205 ASSERT_EQ(flagAttr->symbols.size(), 3u);
206
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700207 AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name);
208 EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, u"bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800209 EXPECT_EQ(flagAttr->symbols[0].value, 0u);
210
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700211 AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name);
212 EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, u"bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800213 EXPECT_EQ(flagAttr->symbols[1].value, 1u);
214
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700215 AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name);
216 EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, u"baz");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800217 EXPECT_EQ(flagAttr->symbols[2].value, 2u);
218
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700219 std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr,
220 u"baz|bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800221 ASSERT_NE(flagValue, nullptr);
222 EXPECT_EQ(flagValue->value.data, 1u | 2u);
223}
224
225TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700226 std::string input = "<attr name=\"foo\">\n"
227 " <enum name=\"bar\" value=\"0\"/>\n"
228 " <enum name=\"bat\" value=\"1\"/>\n"
229 " <enum name=\"bat\" value=\"2\"/>\n"
230 "</attr>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800231 ASSERT_FALSE(testParse(input));
232}
233
234TEST_F(ResourceParserTest, ParseStyle) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700235 std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n"
236 " <item name=\"bar\">#ffffffff</item>\n"
237 " <item name=\"bat\">@string/hey</item>\n"
238 " <item name=\"baz\"><b>hey</b></item>\n"
239 "</style>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800240 ASSERT_TRUE(testParse(input));
241
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700242 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800243 ASSERT_NE(style, nullptr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700244 AAPT_ASSERT_TRUE(style->parent);
245 AAPT_ASSERT_TRUE(style->parent.value().name);
246 EXPECT_EQ(test::parseNameOrDie(u"@style/fu"), style->parent.value().name.value());
247 ASSERT_EQ(3u, style->entries.size());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800248
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700249 AAPT_ASSERT_TRUE(style->entries[0].key.name);
250 EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), style->entries[0].key.name.value());
251
252 AAPT_ASSERT_TRUE(style->entries[1].key.name);
253 EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), style->entries[1].key.name.value());
254
255 AAPT_ASSERT_TRUE(style->entries[2].key.name);
256 EXPECT_EQ(test::parseNameOrDie(u"@attr/baz"), style->entries[2].key.name.value());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800257}
258
Adam Lesinski769de982015-04-10 19:43:55 -0700259TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700260 std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
Adam Lesinski769de982015-04-10 19:43:55 -0700261 ASSERT_TRUE(testParse(input));
262
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700263 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski769de982015-04-10 19:43:55 -0700264 ASSERT_NE(style, nullptr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700265 AAPT_ASSERT_TRUE(style->parent);
266 AAPT_ASSERT_TRUE(style->parent.value().name);
267 EXPECT_EQ(test::parseNameOrDie(u"@com.app:style/Theme"), style->parent.value().name.value());
Adam Lesinski769de982015-04-10 19:43:55 -0700268}
269
Adam Lesinski24aad162015-04-24 19:19:30 -0700270TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
271 std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
272 " name=\"foo\" parent=\"app:Theme\"/>";
273 ASSERT_TRUE(testParse(input));
274
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700275 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski24aad162015-04-24 19:19:30 -0700276 ASSERT_NE(style, nullptr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700277 AAPT_ASSERT_TRUE(style->parent);
278 AAPT_ASSERT_TRUE(style->parent.value().name);
279 EXPECT_EQ(test::parseNameOrDie(u"@android:style/Theme"), style->parent.value().name.value());
Adam Lesinski24aad162015-04-24 19:19:30 -0700280}
281
282TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
283 std::string input =
284 "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n"
285 " <item name=\"app:bar\">0</item>\n"
286 "</style>";
287 ASSERT_TRUE(testParse(input));
288
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700289 Style* style = test::getValue<Style>(&mTable, u"@style/foo");
Adam Lesinski24aad162015-04-24 19:19:30 -0700290 ASSERT_NE(style, nullptr);
291 ASSERT_EQ(1u, style->entries.size());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700292 EXPECT_EQ(test::parseNameOrDie(u"@android:attr/bar"), style->entries[0].key.name.value());
Adam Lesinski24aad162015-04-24 19:19:30 -0700293}
294
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700295TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
296 std::string input = "<style name=\"foo.bar\"/>";
297 ASSERT_TRUE(testParse(input));
298
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700299 Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700300 ASSERT_NE(style, nullptr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700301 AAPT_ASSERT_TRUE(style->parent);
302 AAPT_ASSERT_TRUE(style->parent.value().name);
303 EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie(u"@style/foo"));
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700304 EXPECT_TRUE(style->parentInferred);
305}
306
307TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
308 std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
309 ASSERT_TRUE(testParse(input));
310
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700312 ASSERT_NE(style, nullptr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700313 AAPT_EXPECT_FALSE(style->parent);
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700314 EXPECT_FALSE(style->parentInferred);
315}
316
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800317TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700318 std::string input = "<string name=\"foo\">@+id/bar</string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800319 ASSERT_TRUE(testParse(input));
320
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700321 Id* id = test::getValue<Id>(&mTable, u"@id/bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800322 ASSERT_NE(id, nullptr);
323}
324
325TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700326 std::string input = "<declare-styleable name=\"foo\">\n"
327 " <attr name=\"bar\" />\n"
328 " <attr name=\"bat\" format=\"string|reference\"/>\n"
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700329 " <attr name=\"baz\">\n"
330 " <enum name=\"foo\" value=\"1\"/>\n"
331 " </attr>\n"
Adam Lesinski24aad162015-04-24 19:19:30 -0700332 "</declare-styleable>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800333 ASSERT_TRUE(testParse(input));
334
Adam Lesinski9f222042015-11-04 13:51:45 -0800335 Maybe<ResourceTable::SearchResult> result =
336 mTable.findResource(test::parseNameOrDie(u"@styleable/foo"));
337 AAPT_ASSERT_TRUE(result);
338 EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
339
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700340 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800341 ASSERT_NE(attr, nullptr);
342 EXPECT_TRUE(attr->isWeak());
343
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700344 attr = test::getValue<Attribute>(&mTable, u"@attr/bat");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800345 ASSERT_NE(attr, nullptr);
346 EXPECT_TRUE(attr->isWeak());
347
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700348 attr = test::getValue<Attribute>(&mTable, u"@attr/baz");
349 ASSERT_NE(attr, nullptr);
350 EXPECT_TRUE(attr->isWeak());
351 EXPECT_EQ(1u, attr->symbols.size());
352
353 EXPECT_NE(nullptr, test::getValue<Id>(&mTable, u"@id/foo"));
354
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700355 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800356 ASSERT_NE(styleable, nullptr);
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700357 ASSERT_EQ(3u, styleable->entries.size());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800358
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700359 EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), styleable->entries[0].name.value());
360 EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800361}
362
363TEST_F(ResourceParserTest, ParseArray) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700364 std::string input = "<array name=\"foo\">\n"
365 " <item>@string/ref</item>\n"
366 " <item>hey</item>\n"
367 " <item>23</item>\n"
368 "</array>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800369 ASSERT_TRUE(testParse(input));
370
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700371 Array* array = test::getValue<Array>(&mTable, u"@array/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800372 ASSERT_NE(array, nullptr);
373 ASSERT_EQ(3u, array->items.size());
374
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700375 EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
376 EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
377 EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800378}
379
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700380TEST_F(ResourceParserTest, ParseStringArray) {
381 std::string input = "<string-array name=\"foo\">\n"
382 " <item>\"Werk\"</item>\n"
383 "</string-array>\n";
384 ASSERT_TRUE(testParse(input));
385 EXPECT_NE(nullptr, test::getValue<Array>(&mTable, u"@array/foo"));
386}
387
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800388TEST_F(ResourceParserTest, ParsePlural) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700389 std::string input = "<plurals name=\"foo\">\n"
390 " <item quantity=\"other\">apples</item>\n"
391 " <item quantity=\"one\">apple</item>\n"
392 "</plurals>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800393 ASSERT_TRUE(testParse(input));
394}
395
396TEST_F(ResourceParserTest, ParseCommentsWithResource) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700397 std::string input = "<!--This is a comment-->\n"
Adam Lesinski24aad162015-04-24 19:19:30 -0700398 "<string name=\"foo\">Hi</string>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800399 ASSERT_TRUE(testParse(input));
400
Adam Lesinskie78fd612015-10-22 12:48:43 -0700401 String* value = test::getValue<String>(&mTable, u"@string/foo");
402 ASSERT_NE(nullptr, value);
403 EXPECT_EQ(value->getComment(), u"This is a comment");
404}
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700405
Adam Lesinskie78fd612015-10-22 12:48:43 -0700406TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
407 std::string input = "<!--One-->\n"
408 "<!--Two-->\n"
409 "<string name=\"foo\">Hi</string>";
410
411 ASSERT_TRUE(testParse(input));
412
413 String* value = test::getValue<String>(&mTable, u"@string/foo");
414 ASSERT_NE(nullptr, value);
415 EXPECT_EQ(value->getComment(), u"Two");
416}
417
418TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
419 std::string input = "<!--One-->\n"
420 "<string name=\"foo\">\n"
421 " Hi\n"
422 "<!--Two-->\n"
423 "</string>";
424
425 ASSERT_TRUE(testParse(input));
426
427 String* value = test::getValue<String>(&mTable, u"@string/foo");
428 ASSERT_NE(nullptr, value);
429 EXPECT_EQ(value->getComment(), u"One");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800430}
431
Adam Lesinskica5638f2015-10-21 14:42:43 -0700432TEST_F(ResourceParserTest, ParseNestedComments) {
433 // We only care about declare-styleable and enum/flag attributes because comments
434 // from those end up in R.java
435 std::string input = R"EOF(
436 <declare-styleable name="foo">
437 <!-- The name of the bar -->
438 <attr name="barName" format="string|reference" />
439 </declare-styleable>
440
441 <attr name="foo">
442 <!-- The very first -->
443 <enum name="one" value="1" />
444 </attr>)EOF";
445 ASSERT_TRUE(testParse(input));
446
447 Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
448 ASSERT_NE(nullptr, styleable);
449 ASSERT_EQ(1u, styleable->entries.size());
450
451 EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment());
452
453 Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
454 ASSERT_NE(nullptr, attr);
455 ASSERT_EQ(1u, attr->symbols.size());
456
457 EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment());
458}
459
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800460/*
461 * Declaring an ID as public should not require a separate definition
462 * (as an ID has no value).
463 */
464TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700465 std::string input = "<public type=\"id\" name=\"foo\"/>";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800466 ASSERT_TRUE(testParse(input));
467
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700468 Id* id = test::getValue<Id>(&mTable, u"@id/foo");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800469 ASSERT_NE(nullptr, id);
470}
471
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700472TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
473 std::string input = "<string name=\"foo\" product=\"phone\">hi</string>\n"
474 "<string name=\"foo\" product=\"no-sdcard\">ho</string>\n"
475 "<string name=\"bar\" product=\"\">wee</string>\n"
476 "<string name=\"baz\">woo</string>\n";
477 ASSERT_TRUE(testParse(input, std::u16string(u"no-sdcard")));
478
479 String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
480 ASSERT_NE(nullptr, fooStr);
481 EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value);
482
483 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
484 EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
485}
486
487TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
488 std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
489 ASSERT_FALSE(testParse(input, std::u16string(u"phone")));
490}
491
Adam Lesinski27afb9e2015-11-06 18:25:04 -0800492TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
493 std::string input = R"EOF(
494 <public-group type="attr" first-id="0x01010040">
495 <public name="foo" />
496 <public name="bar" />
497 </public-group>)EOF";
498 ASSERT_TRUE(testParse(input));
499
500 Maybe<ResourceTable::SearchResult> result = mTable.findResource(
501 test::parseNameOrDie(u"@attr/foo"));
502 AAPT_ASSERT_TRUE(result);
503
504 AAPT_ASSERT_TRUE(result.value().package->id);
505 AAPT_ASSERT_TRUE(result.value().type->id);
506 AAPT_ASSERT_TRUE(result.value().entry->id);
507 ResourceId actualId(result.value().package->id.value(),
508 result.value().type->id.value(),
509 result.value().entry->id.value());
510 EXPECT_EQ(ResourceId(0x01010040), actualId);
511
512 result = mTable.findResource(test::parseNameOrDie(u"@attr/bar"));
513 AAPT_ASSERT_TRUE(result);
514
515 AAPT_ASSERT_TRUE(result.value().package->id);
516 AAPT_ASSERT_TRUE(result.value().type->id);
517 AAPT_ASSERT_TRUE(result.value().entry->id);
518 actualId = ResourceId(result.value().package->id.value(),
519 result.value().type->id.value(),
520 result.value().entry->id.value());
521 EXPECT_EQ(ResourceId(0x01010041), actualId);
522}
523
Adam Lesinskifa105052015-11-07 13:34:39 -0800524TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
525 std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
526 ASSERT_TRUE(testParse(input));
527
528 input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
529 ASSERT_FALSE(testParse(input));
530}
531
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800532} // namespace aapt