1 /*
2 * Copyright (C) 2016 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 "compile/InlineXmlFormatParser.h"
18
19 #include "test/Test.h"
20
21 using ::testing::Eq;
22 using ::testing::IsNull;
23 using ::testing::Not;
24 using ::testing::NotNull;
25 using ::testing::SizeIs;
26 using ::testing::StrEq;
27
28 namespace aapt {
29
TEST(InlineXmlFormatParserTest,PassThrough)30 TEST(InlineXmlFormatParserTest, PassThrough) {
31 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
32 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
33 <View xmlns:android="http://schemas.android.com/apk/res/android">
34 <View android:text="hey">
35 <View android:id="hi" />
36 </View>
37 </View>)");
38
39 InlineXmlFormatParser parser;
40 ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
41 EXPECT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(0u));
42 }
43
44 TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
45 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
46 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
47 <View1 xmlns:android="http://schemas.android.com/apk/res/android"
48 xmlns:aapt="http://schemas.android.com/aapt">
49 <aapt:attr name="android:text">
50 <View2 android:text="hey">
51 <View3 android:id="hi" />
52 </View2>
53 </aapt:attr>
54 </View1>)");
55
56 doc->file.name = test::ParseNameOrDie("layout/main");
57 doc->file.type = ResourceFile::Type::kProtoXml;
58
59 InlineXmlFormatParser parser;
60 ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
61
62 // One XML resource should have been extracted.
63 EXPECT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(1u));
64
65 xml::Element* el = doc->root.get();
66 ASSERT_THAT(el, NotNull());
67 EXPECT_THAT(el->name, StrEq("View1"));
68
69 // The <aapt:attr> tag should be extracted.
70 EXPECT_THAT(el->FindChild(xml::kSchemaAapt, "attr"), IsNull());
71
72 // The 'android:text' attribute should be set with a reference.
73 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "text");
74 ASSERT_THAT(attr, NotNull());
75
76 ResourceNameRef name_ref;
77 ASSERT_TRUE(ResourceUtils::ParseReference(attr->value, &name_ref));
78
79 xml::XmlResource* extracted_doc = parser.GetExtractedInlineXmlDocuments()[0].get();
80 ASSERT_THAT(extracted_doc, NotNull());
81
82 // Make sure the generated reference is correct.
83 EXPECT_THAT(extracted_doc->file.name, Eq(name_ref));
84
85 // Make sure the ResourceFile::Type is the same.
86 EXPECT_THAT(extracted_doc->file.type, Eq(ResourceFile::Type::kProtoXml));
87
88 // Verify the structure of the extracted XML.
89 el = extracted_doc->root.get();
90 ASSERT_THAT(el, NotNull());
91 EXPECT_THAT(el->name, StrEq("View2"));
92 EXPECT_THAT(el->FindChild({}, "View3"), NotNull());
93 }
94
95 TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
96 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
97 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
98 <View1 xmlns:android="http://schemas.android.com/apk/res/android"
99 xmlns:aapt="http://schemas.android.com/aapt">
100 <aapt:attr name="android:text">
101 <View2 android:text="hey">
102 <View3 android:id="hi" />
103 </View2>
104 </aapt:attr>
105
106 <aapt:attr name="android:drawable">
107 <vector />
108 </aapt:attr>
109 </View1>)");
110
111 doc->file.name = test::ParseNameOrDie("layout/main");
112
113 InlineXmlFormatParser parser;
114 ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
115 ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(2u));
116
117 xml::Element* el = doc->root.get();
118 ASSERT_THAT(el, NotNull());
119 EXPECT_THAT(el->name, StrEq("View1"));
120
121 xml::Attribute* attr_text = el->FindAttribute(xml::kSchemaAndroid, "text");
122 ASSERT_THAT(attr_text, NotNull());
123
124 xml::Attribute* attr_drawable = el->FindAttribute(xml::kSchemaAndroid, "drawable");
125 ASSERT_THAT(attr_drawable, NotNull());
126
127 // The two extracted resources should have different names.
128 EXPECT_THAT(attr_text->value, Not(Eq(attr_drawable->value)));
129
130 // The child <aapt:attr> elements should be gone.
131 EXPECT_THAT(el->FindChild(xml::kSchemaAapt, "attr"), IsNull());
132
133 xml::XmlResource* extracted_doc_text = parser.GetExtractedInlineXmlDocuments()[0].get();
134 ASSERT_THAT(extracted_doc_text, NotNull());
135 ASSERT_THAT(extracted_doc_text->root, NotNull());
136 EXPECT_THAT(extracted_doc_text->root->name, StrEq("View2"));
137
138 xml::XmlResource* extracted_doc_drawable = parser.GetExtractedInlineXmlDocuments()[1].get();
139 ASSERT_THAT(extracted_doc_drawable, NotNull());
140 ASSERT_THAT(extracted_doc_drawable->root, NotNull());
141 EXPECT_THAT(extracted_doc_drawable->root->name, StrEq("vector"));
142 }
143
144 TEST(InlineXmlFormatParserTest, ExtractNestedXmlResources) {
145 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
146 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
147 <base_root xmlns:android="http://schemas.android.com/apk/res/android"
148 xmlns:aapt="http://schemas.android.com/aapt">
149 <aapt:attr name="inline_xml">
150 <inline_root>
151 <aapt:attr name="nested_inline_xml">
152 <nested_inline_root/>
153 </aapt:attr>
154 <aapt:attr name="another_nested_inline_xml">
155 <root/>
156 </aapt:attr>
157 </inline_root>
158 </aapt:attr>
159 <aapt:attr name="turtles">
160 <root1>
161 <aapt:attr name="all">
162 <root2>
163 <aapt:attr name="the">
164 <root3>
165 <aapt:attr name="way">
166 <root4>
167 <aapt:attr name="down">
168 <root5/>
169 </aapt:attr>
170 </root4>
171 </aapt:attr>
172 </root3>
173 </aapt:attr>
174 </root2>
175 </aapt:attr>
176 </root1>
177 </aapt:attr>
178 </base_root>)");
179
180 doc->file.name = test::ParseNameOrDie("layout/main");
181
182 InlineXmlFormatParser parser;
183 ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
184 // Confirm that all of the nested inline xmls are parsed out.
185 ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(8u));
186 }
187
188 TEST(InlineXmlFormatParserTest, ExtractIntoAppAttribute) {
189 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
190 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
191 <parent xmlns:app="http://schemas.android.com/apk/res-auto"
192 xmlns:aapt="http://schemas.android.com/aapt">
193 <aapt:attr name="app:foo">
194 <child />
195 </aapt:attr>
196 </parent>)");
197
198 doc->file.name = test::ParseNameOrDie("layout/main");
199
200 InlineXmlFormatParser parser;
201 ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
202
203 ASSERT_THAT(doc->root, NotNull());
204 EXPECT_THAT(doc->root->FindAttribute(xml::kSchemaAuto, "foo"), NotNull());
205 }
206
207 TEST(InlineXmlFormatParserTest, ExtractIntoNoNamespaceAttribute) {
208 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
209 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
210 <parent xmlns:aapt="http://schemas.android.com/aapt">
211 <aapt:attr name="foo">
212 <child />
213 </aapt:attr>
214 </parent>)");
215
216 doc->file.name = test::ParseNameOrDie("layout/main");
217
218 InlineXmlFormatParser parser;
219 ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
220
221 ASSERT_THAT(doc->root, NotNull());
222 EXPECT_THAT(doc->root->FindAttribute({}, "foo"), NotNull());
223 }
224
225 } // namespace aapt
226