XmlFlattener_test.cpp revision 0dcb20c72742a1de30b4984c4b8996d76cb7a428
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 "format/binary/XmlFlattener.h"
18
19#include "androidfw/ResourceTypes.h"
20
21#include "link/Linkers.h"
22#include "test/Test.h"
23#include "util/BigBuffer.h"
24#include "util/Util.h"
25
26using ::aapt::test::StrEq;
27using ::android::StringPiece16;
28using ::testing::Eq;
29using ::testing::Ge;
30using ::testing::IsNull;
31using ::testing::Ne;
32using ::testing::NotNull;
33
34namespace aapt {
35
36class XmlFlattenerTest : public ::testing::Test {
37 public:
38  void SetUp() override {
39    context_ = test::ContextBuilder()
40                   .SetCompilationPackage("com.app.test")
41                   .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
42                   .AddSymbolSource(
43                       test::StaticSymbolSourceBuilder()
44                           .AddPublicSymbol("android:attr/id", ResourceId(0x010100d0),
45                                            test::AttributeBuilder().Build())
46                           .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
47                           .AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
48                                            test::AttributeBuilder().Build())
49                           .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
50                                            test::AttributeBuilder().Build())
51                           .AddSymbol("com.app.test.feature:id/foo", ResourceId(0x80020000))
52                           .AddSymbol("com.app.test.feature:attr/foo", ResourceId(0x80010000),
53                                      test::AttributeBuilder().Build())
54                           .Build())
55                   .Build();
56  }
57
58  ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree,
59                                     const XmlFlattenerOptions& options = {}) {
60    using namespace android;  // For NO_ERROR on windows because it is a macro.
61
62    BigBuffer buffer(1024);
63    XmlFlattener flattener(&buffer, options);
64    if (!flattener.Consume(context_.get(), doc)) {
65      return ::testing::AssertionFailure() << "failed to flatten XML Tree";
66    }
67
68    std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
69    if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
70      return ::testing::AssertionFailure() << "flattened XML is corrupt";
71    }
72    return ::testing::AssertionSuccess();
73  }
74
75 protected:
76  std::unique_ptr<test::Context> context_;
77};
78
79TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
80  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
81      <View xmlns:test="http://com.test" attr="hey">
82          <Layout test:hello="hi" />
83          <Layout>Some text\\</Layout>
84      </View>)");
85
86  android::ResXMLTree tree;
87  ASSERT_TRUE(Flatten(doc.get(), &tree));
88  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));
89
90  size_t len;
91  EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test"));
92  EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test"));
93
94  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
95  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
289TEST_F(XmlFlattenerTest, ProcessQuotes) {
290  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
291      R"(<root>
292          <item>Regular text</item>
293          <item>"Text in double quotes"</item>
294          <item>'Text in single quotes'</item>
295          <item>Text containing "double quotes"</item>
296          <item>Text containing 'single quotes'</item>
297      </root>)");
298
299  size_t len;
300  android::ResXMLTree tree;
301
302  XmlFlattenerOptions options;
303  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
304
305  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
306  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
307  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
308  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
309  EXPECT_THAT(tree.getText(&len), StrEq(u"Regular text"));
310  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
311
312  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
313  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
314  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
315  EXPECT_THAT(tree.getText(&len), StrEq(u"\"Text in double quotes\""));
316  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
317
318  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
319  EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
320  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
321  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
322  EXPECT_THAT(tree.getText(&len), StrEq(u"'Text in single quotes'"));
323  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
324
325  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
326  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
327  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
328  EXPECT_THAT(tree.getText(&len), StrEq(u"Text containing \"double quotes\""));
329  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
330
331  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
332  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
333  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
334  EXPECT_THAT(tree.getText(&len), StrEq(u"Text containing 'single quotes'"));
335  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
336  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
337
338  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
339}
340
341TEST_F(XmlFlattenerTest, ProcessWhitepspace) {
342  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
343      R"(<root>
344          <item>   Compact   Spaces   </item>
345          <item>
346                 A
347          </item>
348          <item>B   </item>
349          <item>C </item>
350          <item> D  </item>
351          <item>   E</item>
352          <item> F</item>
353          <item>  G </item>
354          <item> H </item>
355<item>
356I
357</item>
358<item>
359
360   J
361
362</item>
363          <item>
364          </item>
365      </root>)");
366
367  size_t len;
368  android::ResXMLTree tree;
369
370  XmlFlattenerOptions options;
371  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
372
373  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
374  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
375  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
376  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
377  EXPECT_THAT(tree.getText(&len), StrEq(u" Compact   Spaces "));
378  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
379
380  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
381  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
382  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
383  EXPECT_THAT(tree.getText(&len), StrEq(u" A "));
384  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
385
386  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
387  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
388  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
389  EXPECT_THAT(tree.getText(&len), StrEq(u"B "));
390  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
391
392  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
393  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
394  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
395  EXPECT_THAT(tree.getText(&len), StrEq(u"C "));
396  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
397
398  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
399  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
400  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
401  EXPECT_THAT(tree.getText(&len), StrEq(u" D "));
402  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
403
404  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
405  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
406  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
407  EXPECT_THAT(tree.getText(&len), StrEq(u" E"));
408  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
409
410  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
411  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
412  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
413  EXPECT_THAT(tree.getText(&len), StrEq(u" F"));
414  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
415
416  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
417  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
418  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
419  EXPECT_THAT(tree.getText(&len), StrEq(u" G "));
420  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
421
422  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
423  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
424  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
425  EXPECT_THAT(tree.getText(&len), StrEq(u" H "));
426  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
427
428  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
429  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
430  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
431  EXPECT_THAT(tree.getText(&len), StrEq(u" I "));
432  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
433
434  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
435  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
436  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
437  EXPECT_THAT(tree.getText(&len), StrEq(u" J "));
438  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
439
440  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
441  EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
442  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
443  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
444
445  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
446}
447
448TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) {
449  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
450
451  // Raw values are kept when encoding an attribute with no compiled value, regardless of option.
452  XmlFlattenerOptions options;
453  options.keep_raw_values = false;
454
455  android::ResXMLTree tree;
456  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
457
458  while (tree.next() != android::ResXMLTree::START_TAG) {
459    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
460    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
461  }
462
463  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
464  EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
465  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
466  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
467}
468
469TEST_F(XmlFlattenerTest, FlattenCompiledStringValuePreservesRawValue) {
470  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
471  doc->root->attributes[0].compiled_value =
472      util::make_unique<String>(doc->string_pool.MakeRef("bar"));
473
474  // Raw values are kept when encoding a string anyways.
475  XmlFlattenerOptions options;
476  options.keep_raw_values = false;
477
478  android::ResXMLTree tree;
479  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
480
481  while (tree.next() != android::ResXMLTree::START_TAG) {
482    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
483    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
484  }
485
486  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
487  EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
488  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
489  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
490}
491
492TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse) {
493  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
494  doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
495
496  XmlFlattenerOptions options;
497  options.keep_raw_values = false;
498
499  android::ResXMLTree tree;
500  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
501
502  while (tree.next() != android::ResXMLTree::START_TAG) {
503    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
504    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
505  }
506
507  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
508  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
509  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
510}
511
512TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue) {
513  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
514  doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
515
516  XmlFlattenerOptions options;
517  options.keep_raw_values = true;
518
519  android::ResXMLTree tree;
520  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
521
522  while (tree.next() != android::ResXMLTree::START_TAG) {
523    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
524    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
525  }
526
527  ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
528  EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
529
530  size_t len;
531  EXPECT_THAT(tree.getAttributeStringValue(0, &len), StrEq(u"true"));
532
533  EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
534}
535
536}  // namespace aapt
537