XmlFlattener_test.cpp revision 4670805ea441edb8b280f9312571e7799f1284cf
1ba8b377d89a3bd37ef6112e21c852be75e07db0aYing Wang/*
2ba8b377d89a3bd37ef6112e21c852be75e07db0aYing Wang * Copyright (C) 2015 The Android Open Source Project
3cdb52b51862573410512a1c1578f199528b9f6beStephen Hines *
47bc754bca995a1d57e3573ac37edd9531f59e432Stephen Hines * Licensed under the Apache License, Version 2.0 (the "License");
5b34b7aeb31d1dc60105c8e83c8188d25850616e2Stephen Hines * you may not use this file except in compliance with the License.
6b34b7aeb31d1dc60105c8e83c8188d25850616e2Stephen Hines * You may obtain a copy of the License at
71f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang *
802268f01dfefc439cd1af0700fbd8bd2b68c4f90Dan Willemsen *      http://www.apache.org/licenses/LICENSE-2.0
902268f01dfefc439cd1af0700fbd8bd2b68c4f90Dan Willemsen *
1002268f01dfefc439cd1af0700fbd8bd2b68c4f90Dan Willemsen * Unless required by applicable law or agreed to in writing, software
111f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang * distributed under the License is distributed on an "AS IS" BASIS,
12b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert * See the License for the specific language governing permissions and
14b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert * limitations under the License.
15b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert */
16b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert
17b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "format/binary/XmlFlattener.h"
18b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert
19b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "androidfw/ResourceTypes.h"
20b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert
21b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "link/Linkers.h"
22b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "test/Test.h"
23b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "util/BigBuffer.h"
24b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert#include "util/Util.h"
25b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert
26b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::aapt::test::StrEq;
27b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::android::StringPiece16;
28b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::Eq;
29b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::Ge;
30b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::IsNull;
31b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::Ne;
32b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertusing ::testing::NotNull;
33b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert
34b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertnamespace aapt {
35b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert
36b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albertclass XmlFlattenerTest : public ::testing::Test {
37b5b2ffe3be103325db681e671cbbde7c7d89e768Dan Albert public:
381f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang  void SetUp() override {
396feb6d5607ce86a446645564212043964628f540Ying Wang    context_ = test::ContextBuilder()
401f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang                   .SetCompilationPackage("com.app.test")
416feb6d5607ce86a446645564212043964628f540Ying Wang                   .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
426feb6d5607ce86a446645564212043964628f540Ying Wang                   .AddSymbolSource(
436feb6d5607ce86a446645564212043964628f540Ying Wang                       test::StaticSymbolSourceBuilder()
446feb6d5607ce86a446645564212043964628f540Ying Wang                           .AddPublicSymbol("android:attr/id", ResourceId(0x010100d0),
456feb6d5607ce86a446645564212043964628f540Ying Wang                                            test::AttributeBuilder().Build())
4602cefc93a8b7555dd5e45f39348ab1473192983bTim Murray                           .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
471f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang                           .AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
489ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                                            test::AttributeBuilder().Build())
499ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                           .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
509ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                                            test::AttributeBuilder().Build())
519ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                           .AddSymbol("com.app.test.feature:id/foo", ResourceId(0x80020000))
529ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                           .AddSymbol("com.app.test.feature:attr/foo", ResourceId(0x80010000),
539ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                                      test::AttributeBuilder().Build())
549ecbf832593980facba06c9031b8539b9cfdfd26Dan Willemsen                           .Build())
55057aaea54a8dd6aa769290b1c0f471a17aaf1a22Dan Willemsen                   .Build();
56057aaea54a8dd6aa769290b1c0f471a17aaf1a22Dan Willemsen  }
571f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang
581f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang  ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree,
591f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang                                     const XmlFlattenerOptions& options = {}) {
601f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    using namespace android;  // For NO_ERROR on windows because it is a macro.
611f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang
621f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    BigBuffer buffer(1024);
631f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    XmlFlattener flattener(&buffer, options);
641f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    if (!flattener.Consume(context_.get(), doc)) {
651f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang      return ::testing::AssertionFailure() << "failed to flatten XML Tree";
661f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    }
671f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang
681f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
691f9828387d8d3d0b1a02f99633ac58a68aa366adYing Wang    if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
70e5d4e74f9f8c09b79cac8125fb58bdfbb4f1aa9cYing Wang      return ::testing::AssertionFailure() << "flattened XML is corrupt";
71d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh    }
723ea1e51ebe96a03ceb208b93a799c34c7dd1bc4eChih-Hung Hsieh    return ::testing::AssertionSuccess();
733ea1e51ebe96a03ceb208b93a799c34c7dd1bc4eChih-Hung Hsieh  }
7482c78c544591e3fe0634fbc76639b9349fd739b3Chih-Hung Hsieh
75795bed9329ce36f7ce414ce38eb29d9ab34aa6aeDmitry Shmidt protected:
76d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  std::unique_ptr<test::Context> context_;
77d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh};
782ee4c1abbfe278d21d1ae75b39d229fa5f8834e6Chih-Hung Hsieh
79d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung HsiehTEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
803d723c33126e5e16b546ba3cf5bd56791e53ef13Chih-Hung Hsieh  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
813d723c33126e5e16b546ba3cf5bd56791e53ef13Chih-Hung Hsieh      <View xmlns:test="http://com.test" attr="hey">
82fa8d823f7c89dee9c35e72a5a9d8197affe79822Ed Tam          <Layout test:hello="hi" />
83fa8d823f7c89dee9c35e72a5a9d8197affe79822Ed Tam          <Layout>Some text\\</Layout>
84d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh      </View>)");
85d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh
86d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  android::ResXMLTree tree;
87d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  ASSERT_TRUE(Flatten(doc.get(), &tree));
88d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));
89d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh
90d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  size_t len;
91d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test"));
92d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test"));
93d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh
94d9cd1fafb5db622cb4277f961dd81975a0c0d330Chih-Hung Hsieh  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
95460171a3e49b6dc753ec87e296d1f0a14f61ae68Chih-Hung Hsieh  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="&quot;">\\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
289}  // namespace aapt
290