XmlFlattener_test.cpp revision 4670805ea441edb8b280f9312571e7799f1284cf
1ba8b377d89a3bd37ef6112e21c852be75e07db0aYing Wang/* 2ba8b377d89a3bd37ef6112e21c852be75e07db0aYing Wang * Copyright (C) 2015 The Android Open Source Project 3cdb52b51862573410512a1c1578f199528b9f6beStephen Hines * 47bc754bca995a1d57e3573ac37edd9531f59e432Stephen Hines * Licensed under the Apache License, Version 2.0 (the "License"); 5b34b7aeb31d1dc60105c8e83c8188d25850616e2Stephen Hines * you may not use this file except in compliance with the License. 6b34b7aeb31d1dc60105c8e83c8188d25850616e2Stephen Hines * You may obtain a copy of the License at 71f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang * 802268f01dfefc439cd1af0700fbd8bd2b68c4f90Dan Willemsen * http://www.apache.org/licenses/LICENSE-2.0 902268f01dfefc439cd1af0700fbd8bd2b68c4f90Dan Willemsen * 1002268f01dfefc439cd1af0700fbd8bd2b68c4f90Dan Willemsen * Unless required by applicable law or agreed to in writing, software 111f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang * distributed under the License is distributed on an "AS IS" BASIS, 12b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert * See the License for the specific language governing permissions and 14b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert * limitations under the License. 15b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert */ 16b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert 17b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "format/binary/XmlFlattener.h" 18b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert 19b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "androidfw/ResourceTypes.h" 20b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert 21b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "link/Linkers.h" 22b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "test/Test.h" 23b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "util/BigBuffer.h" 24b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "util/Util.h" 25b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert 26b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::aapt::test::StrEq; 27b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::android::StringPiece16; 28b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::Eq; 29b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::Ge; 30b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::IsNull; 31b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::Ne; 32b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::NotNull; 33b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert 34b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertnamespace aapt { 35b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert 36b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertclass XmlFlattenerTest : public ::testing::Test { 37b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert public: 381f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang void SetUp() override { 396feb6d5607ce86a446645564212043964628f540Ying Wang context_ = test::ContextBuilder() 401f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang .SetCompilationPackage("com.app.test") 416feb6d5607ce86a446645564212043964628f540Ying Wang .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"}) 426feb6d5607ce86a446645564212043964628f540Ying Wang .AddSymbolSource( 436feb6d5607ce86a446645564212043964628f540Ying Wang test::StaticSymbolSourceBuilder() 446feb6d5607ce86a446645564212043964628f540Ying Wang .AddPublicSymbol("android:attr/id", ResourceId(0x010100d0), 456feb6d5607ce86a446645564212043964628f540Ying Wang test::AttributeBuilder().Build()) 4602cefc93a8b7555dd5e45f39348ab1473192983bTim Murray .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000)) 471f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang .AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3), 489ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen test::AttributeBuilder().Build()) 499ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435), 509ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen test::AttributeBuilder().Build()) 519ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen .AddSymbol("com.app.test.feature:id/foo", ResourceId(0x80020000)) 529ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen .AddSymbol("com.app.test.feature:attr/foo", ResourceId(0x80010000), 539ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen test::AttributeBuilder().Build()) 549ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen .Build()) 55057aaea54a8dd6aa769290b1c0f471a17aaf1a22Dan Willemsen .Build(); 56057aaea54a8dd6aa769290b1c0f471a17aaf1a22Dan Willemsen } 571f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang 581f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree, 591f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang const XmlFlattenerOptions& options = {}) { 601f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang using namespace android; // For NO_ERROR on windows because it is a macro. 611f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang 621f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang BigBuffer buffer(1024); 631f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang XmlFlattener flattener(&buffer, options); 641f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang if (!flattener.Consume(context_.get(), doc)) { 651f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang return ::testing::AssertionFailure() << "failed to flatten XML Tree"; 661f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang } 671f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang 681f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang std::unique_ptr<uint8_t[]> data = util::Copy(buffer); 691f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) { 70e5d4e74f9f8c09b79cac8125fb58bdfbb4f1aa9cYing Wang return ::testing::AssertionFailure() << "flattened XML is corrupt"; 71d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh } 723ea1e51ebe96a03ceb208b93a799c34c7dd1bc4eChih-Hung Hsieh return ::testing::AssertionSuccess(); 733ea1e51ebe96a03ceb208b93a799c34c7dd1bc4eChih-Hung Hsieh } 7482c78c544591e3fe0634fbc76639b9349fd739b3Chih-Hung Hsieh 75795bed9329ce36f7ce414ce38eb29d9ab34aa6aeDmitry Shmidt protected: 76d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh std::unique_ptr<test::Context> context_; 77d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh}; 782ee4c1abbfe278d21d1ae75b39d229fa5f8834e6Chih-Hung Hsieh 79d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung HsiehTEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) { 803d723c33126e5e16b546ba3cf5bd56791e53ef13Chih-Hung Hsieh std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( 813d723c33126e5e16b546ba3cf5bd56791e53ef13Chih-Hung Hsieh <View xmlns:test="http://com.test" attr="hey"> 82fa8d823f7c89dee9c35e72a5a9d8197affe79822Ed Tam <Layout test:hello="hi" /> 83fa8d823f7c89dee9c35e72a5a9d8197affe79822Ed Tam <Layout>Some text\\</Layout> 84d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh </View>)"); 85d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh 86d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh android::ResXMLTree tree; 87d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh ASSERT_TRUE(Flatten(doc.get(), &tree)); 88d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE)); 89d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh 90d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh size_t len; 91d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test")); 92d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test")); 93d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh 94d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); 95460171a3e49b6dc753ec87e296d1f0a14f61ae68Chih-Hung Hsieh EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); 96 EXPECT_THAT(tree.getElementName(&len), StrEq(u"View")); 97 98 ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); 99 EXPECT_THAT(tree.getAttributeNamespace(0, &len), IsNull()); 100 EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"attr")); 101 102 const StringPiece16 kAttr(u"attr"); 103 EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kAttr.data(), kAttr.size()), Eq(0)); 104 105 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); 106 EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); 107 EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout")); 108 109 ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); 110 EXPECT_THAT(tree.getAttributeNamespace(0, &len), StrEq(u"http://com.test")); 111 EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"hello")); 112 113 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG)); 114 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); 115 116 EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); 117 EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout")); 118 ASSERT_THAT(tree.getAttributeCount(), Eq(0u)); 119 120 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT)); 121 EXPECT_THAT(tree.getText(&len), StrEq(u"Some text\\")); 122 123 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG)); 124 EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); 125 EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout")); 126 127 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG)); 128 EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); 129 EXPECT_THAT(tree.getElementName(&len), StrEq(u"View")); 130 131 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_NAMESPACE)); 132 EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test")); 133 EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test")); 134 135 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT)); 136} 137 138TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) { 139 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( 140 <View xmlns:tools="http://schemas.android.com/tools" 141 xmlns:foo="http://schemas.android.com/foo" 142 foo:bar="Foo" 143 tools:ignore="MissingTranslation"/>)"); 144 145 android::ResXMLTree tree; 146 ASSERT_TRUE(Flatten(doc.get(), &tree)); 147 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE)); 148 149 size_t len; 150 EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"foo")); 151 EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://schemas.android.com/foo")); 152 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); 153 154 EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"), 155 Eq(android::NAME_NOT_FOUND)); 156 EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), Ge(0)); 157} 158 159TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) { 160 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( 161 <View xmlns:android="http://schemas.android.com/apk/res/android" 162 android:id="@id/id" 163 class="str" 164 style="@id/id"/>)"); 165 166 android::ResXMLTree tree; 167 ASSERT_TRUE(Flatten(doc.get(), &tree)); 168 169 while (tree.next() != android::ResXMLTree::START_TAG) { 170 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 171 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 172 } 173 174 EXPECT_THAT(tree.indexOfClass(), Eq(0)); 175 EXPECT_THAT(tree.indexOfStyle(), Eq(1)); 176} 177 178// The device ResXMLParser in libandroidfw differentiates between empty namespace and null 179// namespace. 180TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) { 181 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package="android"/>)"); 182 183 android::ResXMLTree tree; 184 ASSERT_TRUE(Flatten(doc.get(), &tree)); 185 186 while (tree.next() != android::ResXMLTree::START_TAG) { 187 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 188 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 189 } 190 191 const StringPiece16 kPackage = u"package"; 192 EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), Ge(0)); 193} 194 195TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) { 196 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package=""/>)"); 197 198 android::ResXMLTree tree; 199 ASSERT_TRUE(Flatten(doc.get(), &tree)); 200 201 while (tree.next() != android::ResXMLTree::START_TAG) { 202 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 203 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 204 } 205 206 const StringPiece16 kPackage = u"package"; 207 ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()); 208 ASSERT_THAT(idx, Ge(0)); 209 210 size_t len; 211 EXPECT_THAT(tree.getAttributeStringValue(idx, &len), NotNull()); 212} 213 214TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) { 215 context_->SetCompilationPackage("com.app.test.feature"); 216 context_->SetPackageId(0x80); 217 context_->SetNameManglerPolicy({"com.app.test.feature"}); 218 219 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"( 220 <View xmlns:android="http://schemas.android.com/apk/res/android" 221 xmlns:app="http://schemas.android.com/apk/res-auto" 222 android:id="@id/foo" 223 app:foo="@id/foo" />)"); 224 225 XmlReferenceLinker linker; 226 ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); 227 228 // The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f). 229 android::DynamicRefTable dynamic_ref_table; 230 dynamic_ref_table.addMapping(0x80, 0x80); 231 232 android::ResXMLTree tree(&dynamic_ref_table); 233 ASSERT_TRUE(Flatten(doc.get(), &tree)); 234 235 while (tree.next() != android::ResXMLTree::START_TAG) { 236 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 237 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 238 } 239 240 ssize_t idx; 241 242 idx = tree.indexOfAttribute(xml::kSchemaAndroid, "id"); 243 ASSERT_THAT(idx, Ge(0)); 244 EXPECT_THAT(tree.indexOfID(), Eq(idx)); 245 EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x010100d0u)); 246 247 idx = tree.indexOfAttribute(xml::kSchemaAuto, "foo"); 248 ASSERT_THAT(idx, Ge(0)); 249 EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x80010000u)); 250 EXPECT_THAT(tree.getAttributeDataType(idx), Eq(android::Res_value::TYPE_REFERENCE)); 251 EXPECT_THAT(tree.getAttributeData(idx), Eq(int32_t(0x80020000))); 252} 253 254TEST_F(XmlFlattenerTest, ProcessEscapedStrings) { 255 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom( 256 R"(<element value="\?hello" pattern="\\d{5}" other=""">\\d{5}</element>)"); 257 258 android::ResXMLTree tree; 259 ASSERT_TRUE(Flatten(doc.get(), &tree)); 260 261 while (tree.next() != android::ResXMLTree::START_TAG) { 262 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 263 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 264 } 265 266 const StringPiece16 kValue = u"value"; 267 const StringPiece16 kPattern = u"pattern"; 268 const StringPiece16 kOther = u"other"; 269 270 size_t len; 271 ssize_t idx; 272 273 idx = tree.indexOfAttribute(nullptr, 0, kValue.data(), kValue.size()); 274 ASSERT_THAT(idx, Ge(0)); 275 EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"?hello")); 276 277 idx = tree.indexOfAttribute(nullptr, 0, kPattern.data(), kPattern.size()); 278 ASSERT_THAT(idx, Ge(0)); 279 EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\\d{5}")); 280 281 idx = tree.indexOfAttribute(nullptr, 0, kOther.data(), kOther.size()); 282 ASSERT_THAT(idx, Ge(0)); 283 EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\"")); 284 285 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT)); 286 EXPECT_THAT(tree.getText(&len), StrEq(u"\\d{5}")); 287} 288 289} // namespace aapt 290