blob: d2139d0ab485f8c69d043842a31f2592f5f78393 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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 "Resolver.h"
18#include "ResourceTable.h"
19#include "ResourceValues.h"
20#include "SourceXmlPullParser.h"
21#include "Util.h"
22#include "XmlFlattener.h"
23
24#include <androidfw/AssetManager.h>
25#include <androidfw/ResourceTypes.h>
26#include <gtest/gtest.h>
27#include <sstream>
28#include <string>
29
Adam Lesinskica2fc352015-04-03 12:08:26 -070030using namespace android;
31
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080032namespace aapt {
33
34constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
35
Adam Lesinski24aad162015-04-24 19:19:30 -070036struct MockResolver : public IResolver {
37 MockResolver(const StringPiece16& defaultPackage,
38 const std::map<ResourceName, ResourceId>& items) :
39 mPackage(defaultPackage.toString()), mAttr(false, ResTable_map::TYPE_ANY),
40 mItems(items) {
41 }
42
43 virtual const std::u16string& getDefaultPackage() const override {
44 return mPackage;
45 }
46
47 virtual Maybe<ResourceId> findId(const ResourceName& name) override {
48 const auto iter = mItems.find(name);
49 if (iter != mItems.end()) {
50 return iter->second;
51 }
52 return {};
53 }
54
55 virtual Maybe<Entry> findAttribute(const ResourceName& name) override {
56 Maybe<ResourceId> result = findId(name);
57 if (result) {
58 if (name.type == ResourceType::kAttr) {
59 return Entry{ result.value(), &mAttr };
60 } else {
61 return Entry{ result.value() };
62 }
63 }
64 return {};
65 }
66
67 virtual Maybe<ResourceName> findName(ResourceId resId) override {
68 for (auto& p : mItems) {
69 if (p.second == resId) {
70 return p.first;
71 }
72 }
73 return {};
74 }
75
76 std::u16string mPackage;
77 Attribute mAttr;
78 std::map<ResourceName, ResourceId> mItems;
79};
80
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080081class XmlFlattenerTest : public ::testing::Test {
82public:
83 virtual void SetUp() override {
Adam Lesinski24aad162015-04-24 19:19:30 -070084 std::shared_ptr<IResolver> resolver = std::make_shared<MockResolver>(u"android",
85 std::map<ResourceName, ResourceId>({
86 { ResourceName{ u"android", ResourceType::kAttr, u"attr" },
87 ResourceId{ 0x01010000u } },
88 { ResourceName{ u"android", ResourceType::kId, u"id" },
89 ResourceId{ 0x01020000u } },
90 { ResourceName{ u"com.lib", ResourceType::kAttr, u"attr" },
91 ResourceId{ 0x01010001u } },
92 { ResourceName{ u"com.lib", ResourceType::kId, u"id" },
93 ResourceId{ 0x01020001u } }}));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080094
Adam Lesinski24aad162015-04-24 19:19:30 -070095 mFlattener = std::make_shared<XmlFlattener>(nullptr, resolver);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 }
97
Adam Lesinski24aad162015-04-24 19:19:30 -070098 ::testing::AssertionResult testFlatten(const std::string& in, ResXMLTree* outTree) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080099 std::stringstream input(kXmlPreamble);
Adam Lesinski24aad162015-04-24 19:19:30 -0700100 input << in << std::endl;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800101 std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
102 BigBuffer outBuffer(1024);
Adam Lesinski24aad162015-04-24 19:19:30 -0700103 XmlFlattener::Options xmlOptions;
104 xmlOptions.defaultPackage = u"android";
105 if (!mFlattener->flatten(Source{ "test" }, xmlParser, &outBuffer, xmlOptions)) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800106 return ::testing::AssertionFailure();
107 }
108
109 std::unique_ptr<uint8_t[]> data = util::copy(outBuffer);
Adam Lesinskica2fc352015-04-03 12:08:26 -0700110 if (outTree->setTo(data.get(), outBuffer.size(), true) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800111 return ::testing::AssertionFailure();
112 }
113 return ::testing::AssertionSuccess();
114 }
115
116 std::shared_ptr<XmlFlattener> mFlattener;
117};
118
119TEST_F(XmlFlattenerTest, ParseSimpleView) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700120 std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
121 " android:attr=\"@id/id\">\n"
122 "</View>";
Adam Lesinskica2fc352015-04-03 12:08:26 -0700123 ResXMLTree tree;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800124 ASSERT_TRUE(testFlatten(input, &tree));
125
Adam Lesinskica2fc352015-04-03 12:08:26 -0700126 while (tree.next() != ResXMLTree::END_DOCUMENT) {
127 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800128 }
129}
130
Adam Lesinski24aad162015-04-24 19:19:30 -0700131TEST_F(XmlFlattenerTest, ParseViewWithPackageAlias) {
132 std::string input = "<View xmlns:ns1=\"http://schemas.android.com/apk/res/android\"\n"
133 " xmlns:ns2=\"http://schemas.android.com/apk/res/android\"\n"
134 " ns1:attr=\"@ns2:id/id\">\n"
135 "</View>";
136 ResXMLTree tree;
137 ASSERT_TRUE(testFlatten(input, &tree));
138
139 while (tree.next() != ResXMLTree::END_DOCUMENT) {
140 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
141 }
142}
143
144::testing::AssertionResult attributeNameAndValueEquals(ResXMLTree* tree, size_t index,
145 ResourceId nameId, ResourceId valueId) {
146 if (index >= tree->getAttributeCount()) {
147 return ::testing::AssertionFailure() << "index " << index << " is out of bounds ("
148 << tree->getAttributeCount() << ")";
149 }
150
151 if (tree->getAttributeNameResID(index) != nameId.id) {
152 return ::testing::AssertionFailure()
153 << "attribute at index " << index << " has ID "
154 << ResourceId{ (uint32_t) tree->getAttributeNameResID(index) }
155 << ". Expected ID " << nameId;
156 }
157
158 if (tree->getAttributeDataType(index) != Res_value::TYPE_REFERENCE) {
159 return ::testing::AssertionFailure() << "attribute at index " << index << " has value of "
160 << "type " << std::hex
161 << tree->getAttributeDataType(index) << std::dec
162 << ". Expected reference (" << std::hex
163 << Res_value::TYPE_REFERENCE << std::dec << ")";
164 }
165
166 if ((uint32_t) tree->getAttributeData(index) != valueId.id) {
167 return ::testing::AssertionFailure()
168 << "attribute at index " << index << " has value " << "with ID "
169 << ResourceId{ (uint32_t) tree->getAttributeData(index) }
170 << ". Expected ID " << valueId;
171 }
172 return ::testing::AssertionSuccess();
173}
174
175TEST_F(XmlFlattenerTest, ParseViewWithShadowedPackageAlias) {
176 std::string input = "<View xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
177 " app:attr=\"@app:id/id\">\n"
178 " <View xmlns:app=\"http://schemas.android.com/apk/res/com.lib\"\n"
179 " app:attr=\"@app:id/id\"/>\n"
180 "</View>";
181 ResXMLTree tree;
182 ASSERT_TRUE(testFlatten(input, &tree));
183
184 while (tree.next() != ResXMLTree::START_TAG) {
185 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
186 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
187 }
188
189 ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010000u },
190 ResourceId{ 0x01020000u }));
191
192 while (tree.next() != ResXMLTree::START_TAG) {
193 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
194 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
195 }
196
197 ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u },
198 ResourceId{ 0x01020001u }));
199}
200
201TEST_F(XmlFlattenerTest, ParseViewWithLocalPackageAndAliasOfTheSameName) {
202 std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/com.lib\"\n"
203 " android:attr=\"@id/id\"/>";
204 ResXMLTree tree;
205 ASSERT_TRUE(testFlatten(input, &tree));
206
207 while (tree.next() != ResXMLTree::START_TAG) {
208 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
209 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
210 }
211
212 // We expect the 'android:attr' to be converted to 'com.lib:attr' due to the namespace
213 // assignment.
214 // However, we didn't give '@id/id' a package, so it should use the default package
215 // 'android', and not be converted from 'android' to 'com.lib'.
216 ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u },
217 ResourceId{ 0x01020000u }));
218}
219
220/*
221 * The device ResXMLParser in libandroidfw differentiates between empty namespace and null
222 * namespace.
223 */
224TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
225 std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
226 " package=\"android\"/>";
227
228 ResXMLTree tree;
229 ASSERT_TRUE(testFlatten(input, &tree));
230
231 while (tree.next() != ResXMLTree::START_TAG) {
232 ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
233 ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
234 }
235
236 const StringPiece16 kPackage = u"package";
237 EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
238}
239
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800240} // namespace aapt