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