XmlFlattener_test.cpp revision 64587af8179affd38ee26543b748f2d63b7f67bb
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/Builders.h"
20#include "test/Context.h"
21#include "util/BigBuffer.h"
22#include "util/Util.h"
23
24#include <androidfw/ResourceTypes.h>
25#include <gtest/gtest.h>
26
27namespace aapt {
28
29class XmlFlattenerTest : public ::testing::Test {
30public:
31    void SetUp() override {
32        mContext = test::ContextBuilder()
33                .setCompilationPackage(u"com.app.test")
34                .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
35                .addSymbolSource(test::StaticSymbolSourceBuilder()
36                        .addSymbol(u"@android:attr/id", ResourceId(0x010100d0),
37                                   test::AttributeBuilder().build())
38                        .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000))
39                        .addSymbol(u"@android:attr/paddingStart", ResourceId(0x010103b3),
40                                   test::AttributeBuilder().build())
41                        .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
42                                   test::AttributeBuilder().build())
43                        .build())
44                .build();
45    }
46
47    ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
48                                       XmlFlattenerOptions options = {}) {
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) != android::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, AssignSpecialAttributeIndices) {
171    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
172            <View xmlns:android="http://schemas.android.com/apk/res/android"
173                  android:id="@id/id"
174                  class="str"
175                  style="@id/id"/>)EOF");
176
177    android::ResXMLTree tree;
178    ASSERT_TRUE(flatten(doc.get(), &tree));
179
180    while (tree.next() != android::ResXMLTree::START_TAG) {
181        ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
182        ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
183    }
184
185    EXPECT_EQ(tree.indexOfClass(), 0);
186    EXPECT_EQ(tree.indexOfStyle(), 1);
187}
188
189/*
190 * The device ResXMLParser in libandroidfw differentiates between empty namespace and null
191 * namespace.
192 */
193TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
194    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
195
196    android::ResXMLTree tree;
197    ASSERT_TRUE(flatten(doc.get(), &tree));
198
199    while (tree.next() != android::ResXMLTree::START_TAG) {
200        ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
201        ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
202    }
203
204    const StringPiece16 kPackage = u"package";
205    EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
206}
207
208} // namespace aapt
209