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