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
21using ::testing::Eq;
22using ::testing::IsNull;
23using ::testing::Not;
24using ::testing::NotNull;
25using ::testing::SizeIs;
26using ::testing::StrEq;
27
28namespace aapt {
29
30TEST(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
44TEST(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
95TEST(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
144TEST(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
188TEST(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
207TEST(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