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 "MockResolver.h" 18#include "ResourceTable.h" 19#include "ResourceValues.h" 20#include "Util.h" 21#include "XmlFlattener.h" 22 23#include <androidfw/AssetManager.h> 24#include <androidfw/ResourceTypes.h> 25#include <gtest/gtest.h> 26#include <sstream> 27#include <string> 28 29using namespace android; 30 31namespace aapt { 32namespace xml { 33 34constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 35 36class XmlFlattenerTest : public ::testing::Test { 37public: 38 virtual void SetUp() override { 39 mResolver = std::make_shared<MockResolver>( 40 std::make_shared<ResourceTable>(), 41 std::map<ResourceName, ResourceId>({ 42 { ResourceName{ u"android", ResourceType::kAttr, u"attr" }, 43 ResourceId{ 0x01010000u } }, 44 { ResourceName{ u"android", ResourceType::kId, u"id" }, 45 ResourceId{ 0x01020000u } }, 46 { ResourceName{ u"com.lib", ResourceType::kAttr, u"attr" }, 47 ResourceId{ 0x01010001u } }, 48 { ResourceName{ u"com.lib", ResourceType::kId, u"id" }, 49 ResourceId{ 0x01020001u } }})); 50 } 51 52 ::testing::AssertionResult testFlatten(const std::string& in, ResXMLTree* outTree) { 53 std::stringstream input(kXmlPreamble); 54 input << in << std::endl; 55 56 SourceLogger logger(Source{ "test.xml" }); 57 std::unique_ptr<Node> root = inflate(&input, &logger); 58 if (!root) { 59 return ::testing::AssertionFailure(); 60 } 61 62 BigBuffer outBuffer(1024); 63 if (!flattenAndLink(Source{ "test.xml" }, root.get(), std::u16string(u"android"), 64 mResolver, {}, &outBuffer)) { 65 return ::testing::AssertionFailure(); 66 } 67 68 std::unique_ptr<uint8_t[]> data = util::copy(outBuffer); 69 if (outTree->setTo(data.get(), outBuffer.size(), true) != NO_ERROR) { 70 return ::testing::AssertionFailure(); 71 } 72 return ::testing::AssertionSuccess(); 73 } 74 75 std::shared_ptr<IResolver> mResolver; 76}; 77 78TEST_F(XmlFlattenerTest, ParseSimpleView) { 79 std::string input = R"EOF( 80 <View xmlns:android="http://schemas.android.com/apk/res/android" 81 android:attr="@id/id" 82 class="str" 83 style="@id/id"> 84 </View> 85 )EOF"; 86 ResXMLTree tree; 87 ASSERT_TRUE(testFlatten(input, &tree)); 88 89 while (tree.next() != ResXMLTree::START_TAG) { 90 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT); 91 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 92 } 93 94 const StringPiece16 androidNs = u"http://schemas.android.com/apk/res/android"; 95 const StringPiece16 attrName = u"attr"; 96 ssize_t idx = tree.indexOfAttribute(androidNs.data(), androidNs.size(), attrName.data(), 97 attrName.size()); 98 ASSERT_GE(idx, 0); 99 EXPECT_EQ(tree.getAttributeNameResID(idx), 0x01010000u); 100 EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE); 101 102 const StringPiece16 class16 = u"class"; 103 idx = tree.indexOfAttribute(nullptr, 0, class16.data(), class16.size()); 104 ASSERT_GE(idx, 0); 105 EXPECT_EQ(tree.getAttributeNameResID(idx), 0u); 106 EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_STRING); 107 EXPECT_EQ(tree.getAttributeData(idx), tree.getAttributeValueStringID(idx)); 108 109 const StringPiece16 style16 = u"style"; 110 idx = tree.indexOfAttribute(nullptr, 0, style16.data(), style16.size()); 111 ASSERT_GE(idx, 0); 112 EXPECT_EQ(tree.getAttributeNameResID(idx), 0u); 113 EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE); 114 EXPECT_EQ((uint32_t) tree.getAttributeData(idx), 0x01020000u); 115 EXPECT_EQ(tree.getAttributeValueStringID(idx), -1); 116 117 while (tree.next() != ResXMLTree::END_DOCUMENT) { 118 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 119 } 120} 121 122TEST_F(XmlFlattenerTest, ParseViewWithPackageAlias) { 123 std::string input = "<View xmlns:ns1=\"http://schemas.android.com/apk/res/android\"\n" 124 " xmlns:ns2=\"http://schemas.android.com/apk/res/android\"\n" 125 " ns1:attr=\"@ns2:id/id\">\n" 126 "</View>"; 127 ResXMLTree tree; 128 ASSERT_TRUE(testFlatten(input, &tree)); 129 130 while (tree.next() != ResXMLTree::END_DOCUMENT) { 131 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 132 } 133} 134 135::testing::AssertionResult attributeNameAndValueEquals(ResXMLTree* tree, size_t index, 136 ResourceId nameId, ResourceId valueId) { 137 if (index >= tree->getAttributeCount()) { 138 return ::testing::AssertionFailure() << "index " << index << " is out of bounds (" 139 << tree->getAttributeCount() << ")"; 140 } 141 142 if (tree->getAttributeNameResID(index) != nameId.id) { 143 return ::testing::AssertionFailure() 144 << "attribute at index " << index << " has ID " 145 << ResourceId{ (uint32_t) tree->getAttributeNameResID(index) } 146 << ". Expected ID " << nameId; 147 } 148 149 if (tree->getAttributeDataType(index) != Res_value::TYPE_REFERENCE) { 150 return ::testing::AssertionFailure() << "attribute at index " << index << " has value of " 151 << "type " << std::hex 152 << tree->getAttributeDataType(index) << std::dec 153 << ". Expected reference (" << std::hex 154 << Res_value::TYPE_REFERENCE << std::dec << ")"; 155 } 156 157 if ((uint32_t) tree->getAttributeData(index) != valueId.id) { 158 return ::testing::AssertionFailure() 159 << "attribute at index " << index << " has value " << "with ID " 160 << ResourceId{ (uint32_t) tree->getAttributeData(index) } 161 << ". Expected ID " << valueId; 162 } 163 return ::testing::AssertionSuccess(); 164} 165 166TEST_F(XmlFlattenerTest, ParseViewWithShadowedPackageAlias) { 167 std::string input = "<View xmlns:app=\"http://schemas.android.com/apk/res/android\"\n" 168 " app:attr=\"@app:id/id\">\n" 169 " <View xmlns:app=\"http://schemas.android.com/apk/res/com.lib\"\n" 170 " app:attr=\"@app:id/id\"/>\n" 171 "</View>"; 172 ResXMLTree tree; 173 ASSERT_TRUE(testFlatten(input, &tree)); 174 175 while (tree.next() != ResXMLTree::START_TAG) { 176 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 177 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT); 178 } 179 180 ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010000u }, 181 ResourceId{ 0x01020000u })); 182 183 while (tree.next() != ResXMLTree::START_TAG) { 184 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 185 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT); 186 } 187 188 ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u }, 189 ResourceId{ 0x01020001u })); 190} 191 192TEST_F(XmlFlattenerTest, ParseViewWithLocalPackageAndAliasOfTheSameName) { 193 std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/com.lib\"\n" 194 " android:attr=\"@id/id\"/>"; 195 ResXMLTree tree; 196 ASSERT_TRUE(testFlatten(input, &tree)); 197 198 while (tree.next() != ResXMLTree::START_TAG) { 199 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 200 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT); 201 } 202 203 // We expect the 'android:attr' to be converted to 'com.lib:attr' due to the namespace 204 // assignment. 205 // However, we didn't give '@id/id' a package, so it should use the default package 206 // 'android', and not be converted from 'android' to 'com.lib'. 207 ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u }, 208 ResourceId{ 0x01020000u })); 209} 210 211/* 212 * The device ResXMLParser in libandroidfw differentiates between empty namespace and null 213 * namespace. 214 */ 215TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) { 216 std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" 217 " package=\"android\"/>"; 218 219 ResXMLTree tree; 220 ASSERT_TRUE(testFlatten(input, &tree)); 221 222 while (tree.next() != ResXMLTree::START_TAG) { 223 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT); 224 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT); 225 } 226 227 const StringPiece16 kPackage = u"package"; 228 EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0); 229} 230 231} // namespace xml 232} // namespace aapt 233