XmlFlattener_test.cpp revision e1094a2e232277a719025aa5c97c492502c34f5b
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 "format/binary/XmlFlattener.h" 18 19#include "androidfw/ResourceTypes.h" 20 21#include "link/Linkers.h" 22#include "test/Test.h" 23#include "util/BigBuffer.h" 24#include "util/Util.h" 25 26using ::aapt::test::StrEq; 27using ::android::StringPiece16; 28using ::testing::Eq; 29using ::testing::Ge; 30using ::testing::IsNull; 31using ::testing::Ne; 32using ::testing::NotNull; 33 34namespace aapt { 35 36class XmlFlattenerTest : public ::testing::Test { 37 public: 38 void SetUp() override { 39 context_ = test::ContextBuilder() 40 .SetCompilationPackage("com.app.test") 41 .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"}) 42 .AddSymbolSource( 43 test::StaticSymbolSourceBuilder() 44 .AddPublicSymbol("android:attr/id", ResourceId(0x010100d0), 45 test::AttributeBuilder().Build()) 46 .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000)) 47 .AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3), 48 test::AttributeBuilder().Build()) 49 .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435), 50 test::AttributeBuilder().Build()) 51 .AddSymbol("com.app.test.feature:id/foo", ResourceId(0x80020000)) 52 .AddSymbol("com.app.test.feature:attr/foo", ResourceId(0x80010000), 53 test::AttributeBuilder().Build()) 54 .Build()) 55 .Build(); 56 } 57 58 ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree, 59 const XmlFlattenerOptions& options = {}) { 60 using namespace android; // For NO_ERROR on windows because it is a macro. 61 62 BigBuffer buffer(1024); 63 XmlFlattener flattener(&buffer, options); 64 if (!flattener.Consume(context_.get(), doc)) { 65 return ::testing::AssertionFailure() << "failed to flatten XML Tree"; 66 } 67 68 std::unique_ptr<uint8_t[]> data = util::Copy(buffer); 69 if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) { 70 return ::testing::AssertionFailure() << "flattened XML is corrupt"; 71 } 72 return ::testing::AssertionSuccess(); 73 } 74 75 protected: 76 std::unique_ptr<test::Context> context_; 77}; 78 79TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) { 80 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( 81 <View xmlns:test="http://com.test" attr="hey"> 82 <Layout test:hello="hi" /> 83 <Layout>Some text\\</Layout> 84 </View>)"); 85 86 android::ResXMLTree tree; 87 ASSERT_TRUE(Flatten(doc.get(), &tree)); 88 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE)); 89 90 size_t len; 91 EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test")); 92 EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test")); 93 94 ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); 95 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 289TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) { 290 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)"); 291 292 // Raw values are kept when encoding an attribute with no compiled value, regardless of option. 293 XmlFlattenerOptions options; 294 options.keep_raw_values = false; 295 296 android::ResXMLTree tree; 297 ASSERT_TRUE(Flatten(doc.get(), &tree, options)); 298 299 while (tree.next() != android::ResXMLTree::START_TAG) { 300 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 301 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 302 } 303 304 ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); 305 EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0)); 306 EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING)); 307 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0))); 308} 309 310TEST_F(XmlFlattenerTest, FlattenCompiledStringValuePreservesRawValue) { 311 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)"); 312 doc->root->attributes[0].compiled_value = 313 util::make_unique<String>(doc->string_pool.MakeRef("bar")); 314 315 // Raw values are kept when encoding a string anyways. 316 XmlFlattenerOptions options; 317 options.keep_raw_values = false; 318 319 android::ResXMLTree tree; 320 ASSERT_TRUE(Flatten(doc.get(), &tree, options)); 321 322 while (tree.next() != android::ResXMLTree::START_TAG) { 323 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 324 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 325 } 326 327 ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); 328 EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0)); 329 EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING)); 330 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0))); 331} 332 333TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse) { 334 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)"); 335 doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true); 336 337 XmlFlattenerOptions options; 338 options.keep_raw_values = false; 339 340 android::ResXMLTree tree; 341 ASSERT_TRUE(Flatten(doc.get(), &tree, options)); 342 343 while (tree.next() != android::ResXMLTree::START_TAG) { 344 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 345 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 346 } 347 348 ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); 349 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1)); 350 EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN)); 351} 352 353TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue) { 354 std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)"); 355 doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true); 356 357 XmlFlattenerOptions options; 358 options.keep_raw_values = true; 359 360 android::ResXMLTree tree; 361 ASSERT_TRUE(Flatten(doc.get(), &tree, options)); 362 363 while (tree.next() != android::ResXMLTree::START_TAG) { 364 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); 365 ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT)); 366 } 367 368 ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); 369 EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0)); 370 371 size_t len; 372 EXPECT_THAT(tree.getAttributeStringValue(0, &len), StrEq(u"true")); 373 374 EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN)); 375} 376 377} // namespace aapt 378