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 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