1 /* 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 "flatten/XmlFlattener.h" 18 #include "link/Linkers.h" 19 #include "test/Builders.h" 20 #include "test/Context.h" 21 #include "util/BigBuffer.h" 22 #include "util/Util.h" 23 24 #include <androidfw/ResourceTypes.h> 25 #include <gtest/gtest.h> 26 27 namespace aapt { 28 29 class XmlFlattenerTest : public ::testing::Test { 30 public: 31 void SetUp() override { 32 mContext = test::ContextBuilder() 33 .setCompilationPackage(u"com.app.test") 34 .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) 35 .addSymbolSource(test::StaticSymbolSourceBuilder() 36 .addSymbol(u"@android:attr/id", ResourceId(0x010100d0), 37 test::AttributeBuilder().build()) 38 .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000)) 39 .addSymbol(u"@android:attr/paddingStart", ResourceId(0x010103b3), 40 test::AttributeBuilder().build()) 41 .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435), 42 test::AttributeBuilder().build()) 43 .build()) 44 .build(); 45 } 46 47 ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree, 48 XmlFlattenerOptions options = {}) { 49 using namespace android; // For NO_ERROR on windows because it is a macro. 50 51 BigBuffer buffer(1024); 52 XmlFlattener flattener(&buffer, options); 53 if (!flattener.consume(mContext.get(), doc)) { 54 return ::testing::AssertionFailure() << "failed to flatten XML Tree"; 55 } 56 57 std::unique_ptr<uint8_t[]> data = util::copy(buffer); 58 if (outTree->setTo(data.get(), buffer.size(), true) != NO_ERROR) { 59 return ::testing::AssertionFailure() << "flattened XML is corrupt"; 60 } 61 return ::testing::AssertionSuccess(); 62 } 63 64 protected: 65 std::unique_ptr<IAaptContext> mContext; 66 }; 67 68 TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) { 69 std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( 70 <View xmlns:test="http://com.test" 71 attr="hey"> 72 <Layout test:hello="hi" /> 73 <Layout>Some text</Layout> 74 </View>)EOF"); 75 76 77 android::ResXMLTree tree; 78 ASSERT_TRUE(flatten(doc.get(), &tree)); 79 80 ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE); 81 82 size_t len; 83 const char16_t* namespacePrefix = tree.getNamespacePrefix(&len); 84 EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test"); 85 86 const char16_t* namespaceUri = tree.getNamespaceUri(&len); 87 ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test"); 88 89 ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG); 90 91 ASSERT_EQ(tree.getElementNamespace(&len), nullptr); 92 const char16_t* tagName = tree.getElementName(&len); 93 EXPECT_EQ(StringPiece16(tagName, len), u"View"); 94 95 ASSERT_EQ(1u, tree.getAttributeCount()); 96 ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr); 97 const char16_t* attrName = tree.getAttributeName(0, &len); 98 EXPECT_EQ(StringPiece16(attrName, len), u"attr"); 99 100 EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size())); 101 102 ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG); 103 104 ASSERT_EQ(tree.getElementNamespace(&len), nullptr); 105 tagName = tree.getElementName(&len); 106 EXPECT_EQ(StringPiece16(tagName, len), u"Layout"); 107 108 ASSERT_EQ(1u, tree.getAttributeCount()); 109 const char16_t* attrNamespace = tree.getAttributeNamespace(0, &len); 110 EXPECT_EQ(StringPiece16(attrNamespace, len), u"http://com.test"); 111 112 attrName = tree.getAttributeName(0, &len); 113 EXPECT_EQ(StringPiece16(attrName, len), u"hello"); 114 115 ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG); 116 ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG); 117 118 ASSERT_EQ(tree.getElementNamespace(&len), nullptr); 119 tagName = tree.getElementName(&len); 120 EXPECT_EQ(StringPiece16(tagName, len), u"Layout"); 121 ASSERT_EQ(0u, tree.getAttributeCount()); 122 123 ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT); 124 const char16_t* text = tree.getText(&len); 125 EXPECT_EQ(StringPiece16(text, len), u"Some text"); 126 127 ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG); 128 ASSERT_EQ(tree.getElementNamespace(&len), nullptr); 129 tagName = tree.getElementName(&len); 130 EXPECT_EQ(StringPiece16(tagName, len), u"Layout"); 131 132 ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG); 133 ASSERT_EQ(tree.getElementNamespace(&len), nullptr); 134 tagName = tree.getElementName(&len); 135 EXPECT_EQ(StringPiece16(tagName, len), u"View"); 136 137 ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE); 138 namespacePrefix = tree.getNamespacePrefix(&len); 139 EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test"); 140 141 namespaceUri = tree.getNamespaceUri(&len); 142 ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test"); 143 144 ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT); 145 } 146 147 TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) { 148 std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( 149 <View xmlns:android="http://schemas.android.com/apk/res/android" 150 android:paddingStart="1dp" 151 android:colorAccent="#ffffff"/>)EOF"); 152 153 XmlReferenceLinker linker; 154 ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); 155 ASSERT_TRUE(linker.getSdkLevels().count(17) == 1); 156 ASSERT_TRUE(linker.getSdkLevels().count(21) == 1); 157 158 android::ResXMLTree tree; 159 XmlFlattenerOptions options; 160 options.maxSdkLevel = 17; 161 ASSERT_TRUE(flatten(doc.get(), &tree, options)); 162 163 while (tree.next() != android::ResXMLTree::START_TAG) { 164 ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); 165 ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); 166 } 167 168 ASSERT_EQ(1u, tree.getAttributeCount()); 169 EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0)); 170 } 171 172 TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) { 173 std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( 174 <View xmlns:android="http://schemas.android.com/apk/res/android" 175 android:id="@id/id" 176 class="str" 177 style="@id/id"/>)EOF"); 178 179 android::ResXMLTree tree; 180 ASSERT_TRUE(flatten(doc.get(), &tree)); 181 182 while (tree.next() != android::ResXMLTree::START_TAG) { 183 ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); 184 ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); 185 } 186 187 EXPECT_EQ(tree.indexOfClass(), 0); 188 EXPECT_EQ(tree.indexOfStyle(), 1); 189 } 190 191 /* 192 * The device ResXMLParser in libandroidfw differentiates between empty namespace and null 193 * namespace. 194 */ 195 TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) { 196 std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>"); 197 198 android::ResXMLTree tree; 199 ASSERT_TRUE(flatten(doc.get(), &tree)); 200 201 while (tree.next() != android::ResXMLTree::START_TAG) { 202 ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); 203 ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); 204 } 205 206 const StringPiece16 kPackage = u"package"; 207 EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0); 208 } 209 210 } // namespace aapt 211