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/BigBuffer.h"
20 #include "androidfw/ResourceTypes.h"
21 #include "link/Linkers.h"
22 #include "test/Test.h"
23 #include "util/Util.h"
24 
25 using ::aapt::test::StrEq;
26 using ::android::StringPiece16;
27 using ::testing::Eq;
28 using ::testing::Ge;
29 using ::testing::IsNull;
30 using ::testing::Ne;
31 using ::testing::NotNull;
32 
33 namespace aapt {
34 
35 class XmlFlattenerTest : public ::testing::Test {
36  public:
SetUp()37   void SetUp() override {
38     context_ = test::ContextBuilder()
39                    .SetCompilationPackage("com.app.test")
40                    .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
41                    .AddSymbolSource(
42                        test::StaticSymbolSourceBuilder()
43                            .AddPublicSymbol("android:attr/id", ResourceId(0x010100d0),
44                                             test::AttributeBuilder().Build())
45                            .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
46                            .AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
47                                             test::AttributeBuilder().Build())
48                            .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
49                                             test::AttributeBuilder().Build())
50                            .AddSymbol("com.app.test.feature:id/foo", ResourceId(0x80020000))
51                            .AddSymbol("com.app.test.feature:attr/foo", ResourceId(0x80010000),
52                                       test::AttributeBuilder().Build())
53                            .Build())
54                    .Build();
55   }
56 
Flatten(xml::XmlResource * doc,android::ResXMLTree * out_tree,const XmlFlattenerOptions & options={})57   ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree,
58                                      const XmlFlattenerOptions& options = {}) {
59     using namespace android;  // For NO_ERROR on windows because it is a macro.
60 
61     android::BigBuffer buffer(1024);
62     XmlFlattener flattener(&buffer, options);
63     if (!flattener.Consume(context_.get(), doc)) {
64       return ::testing::AssertionFailure() << "failed to flatten XML Tree";
65     }
66 
67     std::unique_ptr<uint8_t[]> data = android::util::Copy(buffer);
68     if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
69       return ::testing::AssertionFailure() << "flattened XML is corrupt";
70     }
71     return ::testing::AssertionSuccess();
72   }
73 
74  protected:
75   std::unique_ptr<test::Context> context_;
76 };
77 
TEST_F(XmlFlattenerTest,FlattenXmlWithNoCompiledAttributes)78 TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
79   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
80       <View xmlns:test="http://com.test" attr="hey">
81           <Layout test:hello="hi" />
82           <Layout>Some text\\</Layout>
83       </View>)");
84 
85   android::ResXMLTree tree;
86   ASSERT_TRUE(Flatten(doc.get(), &tree));
87   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));
88 
89   size_t len;
90   EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test"));
91   EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test"));
92 
93   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
94   EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
95   EXPECT_THAT(tree.getElementName(&len), StrEq(u"View"));
96 
97   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
98   EXPECT_THAT(tree.getAttributeNamespace(0, &len), IsNull());
99   EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"attr"));
100 
101   const StringPiece16 kAttr(u"attr");
102   EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kAttr.data(), kAttr.size()), Eq(0));
103 
104   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
105   EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
106   EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout"));
107 
108   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
109   EXPECT_THAT(tree.getAttributeNamespace(0, &len), StrEq(u"http://com.test"));
110   EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"hello"));
111 
112   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
113   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
114 
115   EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
116   EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout"));
117   ASSERT_THAT(tree.getAttributeCount(), Eq(0u));
118 
119   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
120   EXPECT_THAT(tree.getText(&len), StrEq(u"Some text\\\\"));
121 
122   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
123   EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
124   EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout"));
125 
126   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
127   EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
128   EXPECT_THAT(tree.getElementName(&len), StrEq(u"View"));
129 
130   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_NAMESPACE));
131   EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test"));
132   EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test"));
133 
134   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
135 }
136 
TEST_F(XmlFlattenerTest,FlattenCompiledXmlAndStripOnlyTools)137 TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
138   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
139       <View xmlns:tools="http://schemas.android.com/tools"
140           xmlns:foo="http://schemas.android.com/foo"
141           foo:bar="Foo"
142           tools:ignore="MissingTranslation"/>)");
143 
144   android::ResXMLTree tree;
145   ASSERT_TRUE(Flatten(doc.get(), &tree));
146   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));
147 
148   size_t len;
149   EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"foo"));
150   EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://schemas.android.com/foo"));
151   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
152 
153   EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
154               Eq(android::NAME_NOT_FOUND));
155   EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), Ge(0));
156 }
157 
TEST_F(XmlFlattenerTest,AssignSpecialAttributeIndices)158 TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
159   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
160       <View xmlns:android="http://schemas.android.com/apk/res/android"
161           android:id="@id/id"
162           class="str"
163           style="@id/id"/>)");
164 
165   android::ResXMLTree tree;
166   ASSERT_TRUE(Flatten(doc.get(), &tree));
167 
168   while (tree.next() != android::ResXMLTree::START_TAG) {
169     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
170     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
171   }
172 
173   EXPECT_THAT(tree.indexOfClass(), Eq(0));
174   EXPECT_THAT(tree.indexOfStyle(), Eq(1));
175 }
176 
177 // The device ResXMLParser in libandroidfw differentiates between empty namespace and null
178 // namespace.
179 TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
180   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package="android"/>)");
181 
182   android::ResXMLTree tree;
183   ASSERT_TRUE(Flatten(doc.get(), &tree));
184 
185   while (tree.next() != android::ResXMLTree::START_TAG) {
186     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
187     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
188   }
189 
190   const StringPiece16 kPackage = u"package";
191   EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), Ge(0));
192 }
193 
194 TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
195   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package=""/>)");
196 
197   android::ResXMLTree tree;
198   ASSERT_TRUE(Flatten(doc.get(), &tree));
199 
200   while (tree.next() != android::ResXMLTree::START_TAG) {
201     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
202     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
203   }
204 
205   const StringPiece16 kPackage = u"package";
206   ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
207   ASSERT_THAT(idx, Ge(0));
208 
209   size_t len;
210   EXPECT_THAT(tree.getAttributeStringValue(idx, &len), NotNull());
211 }
212 
213 TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
214   context_->SetCompilationPackage("com.app.test.feature");
215   context_->SetPackageId(0x80);
216   context_->SetNameManglerPolicy({"com.app.test.feature"});
217 
218   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
219       <View xmlns:android="http://schemas.android.com/apk/res/android"
220             xmlns:app="http://schemas.android.com/apk/res-auto"
221             android:id="@id/foo"
222             app:foo="@id/foo" />)");
223 
224   XmlReferenceLinker linker(nullptr);
225   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
226 
227   // The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
228   auto dynamic_ref_table = std::make_shared<android::DynamicRefTable>();
229   dynamic_ref_table->addMapping(0x80, 0x80);
230 
231   auto tree = android::ResXMLTree(std::move(dynamic_ref_table));
232   ASSERT_TRUE(Flatten(doc.get(), &tree));
233 
234   while (tree.next() != android::ResXMLTree::START_TAG) {
235     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
236     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
237   }
238 
239   ssize_t idx;
240 
241   idx = tree.indexOfAttribute(xml::kSchemaAndroid, "id");
242   ASSERT_THAT(idx, Ge(0));
243   EXPECT_THAT(tree.indexOfID(), Eq(idx));
244   EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x010100d0u));
245 
246   idx = tree.indexOfAttribute(xml::kSchemaAuto, "foo");
247   ASSERT_THAT(idx, Ge(0));
248   EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x80010000u));
249   EXPECT_THAT(tree.getAttributeDataType(idx), Eq(android::Res_value::TYPE_REFERENCE));
250   EXPECT_THAT(tree.getAttributeData(idx), Eq(int32_t(0x80020000)));
251 }
252 
253 TEST_F(XmlFlattenerTest, ProcessEscapedStrings) {
254   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
255       R"(<element value="\?hello" pattern="\\d{5}" other="&quot;">\\d{5}</element>)");
256 
257   android::ResXMLTree tree;
258   ASSERT_TRUE(Flatten(doc.get(), &tree));
259 
260   while (tree.next() != android::ResXMLTree::START_TAG) {
261     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
262     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
263   }
264 
265   const StringPiece16 kValue = u"value";
266   const StringPiece16 kPattern = u"pattern";
267   const StringPiece16 kOther = u"other";
268 
269   size_t len;
270   ssize_t idx;
271 
272   idx = tree.indexOfAttribute(nullptr, 0, kValue.data(), kValue.size());
273   ASSERT_THAT(idx, Ge(0));
274   EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"?hello"));
275 
276   idx = tree.indexOfAttribute(nullptr, 0, kPattern.data(), kPattern.size());
277   ASSERT_THAT(idx, Ge(0));
278   EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\\d{5}"));
279 
280   idx = tree.indexOfAttribute(nullptr, 0, kOther.data(), kOther.size());
281   ASSERT_THAT(idx, Ge(0));
282   EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\""));
283 
284   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
285   EXPECT_THAT(tree.getText(&len), StrEq(u"\\\\d{5}"));
286 }
287 
TEST_F(XmlFlattenerTest,ProcessQuotes)288 TEST_F(XmlFlattenerTest, ProcessQuotes) {
289   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
290       R"(<root>
291           <item>Regular text</item>
292           <item>"Text in double quotes"</item>
293           <item>'Text in single quotes'</item>
294           <item>Text containing "double quotes"</item>
295           <item>Text containing 'single quotes'</item>
296       </root>)");
297 
298   size_t len;
299   android::ResXMLTree tree;
300 
301   XmlFlattenerOptions options;
302   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
303 
304   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
305   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
306   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
307   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
308   EXPECT_THAT(tree.getText(&len), StrEq(u"Regular text"));
309   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
310 
311   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
312   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
313   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
314   EXPECT_THAT(tree.getText(&len), StrEq(u"\"Text in double quotes\""));
315   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
316 
317   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
318   EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
319   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
320   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
321   EXPECT_THAT(tree.getText(&len), StrEq(u"'Text in single quotes'"));
322   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
323 
324   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
325   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
326   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
327   EXPECT_THAT(tree.getText(&len), StrEq(u"Text containing \"double quotes\""));
328   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
329 
330   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
331   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
332   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
333   EXPECT_THAT(tree.getText(&len), StrEq(u"Text containing 'single quotes'"));
334   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
335   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
336 
337   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
338 }
339 
TEST_F(XmlFlattenerTest,ProcessWhitepspace)340 TEST_F(XmlFlattenerTest, ProcessWhitepspace) {
341   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
342       R"(<root>
343           <item>   Compact   Spaces   </item>
344           <item>
345                  A
346           </item>
347           <item>B   </item>
348           <item>C </item>
349           <item> D  </item>
350           <item>   E</item>
351           <item> F</item>
352           <item>  G </item>
353           <item> H </item>
354 <item>
355 I
356 </item>
357 <item>
358 
359    J
360 
361 </item>
362           <item>\t K \n </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::TEXT));
443   EXPECT_THAT(tree.getText(&len), StrEq(u"\\t K \\n "));
444   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
445 
446   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
447   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
448   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
449   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
450 
451   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
452 }
453 
TEST_F(XmlFlattenerTest,FlattenRawValueOnlyMakesCompiledValueToo)454 TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) {
455   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
456 
457   // Raw values are kept when encoding an attribute with no compiled value, regardless of option.
458   XmlFlattenerOptions options;
459   options.keep_raw_values = false;
460 
461   android::ResXMLTree tree;
462   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
463 
464   while (tree.next() != android::ResXMLTree::START_TAG) {
465     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
466     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
467   }
468 
469   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
470   EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
471   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
472   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
473 }
474 
TEST_F(XmlFlattenerTest,FlattenCompiledStringValuePreservesRawValue)475 TEST_F(XmlFlattenerTest, FlattenCompiledStringValuePreservesRawValue) {
476   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
477   doc->root->attributes[0].compiled_value =
478       util::make_unique<String>(doc->string_pool.MakeRef("bar"));
479 
480   // Raw values are kept when encoding a string anyways.
481   XmlFlattenerOptions options;
482   options.keep_raw_values = false;
483 
484   android::ResXMLTree tree;
485   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
486 
487   while (tree.next() != android::ResXMLTree::START_TAG) {
488     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
489     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
490   }
491 
492   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
493   EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
494   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
495   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
496 }
497 
TEST_F(XmlFlattenerTest,FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse)498 TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse) {
499   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
500   doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
501 
502   XmlFlattenerOptions options;
503   options.keep_raw_values = false;
504 
505   android::ResXMLTree tree;
506   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
507 
508   while (tree.next() != android::ResXMLTree::START_TAG) {
509     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
510     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
511   }
512 
513   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
514   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
515   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
516 }
517 
TEST_F(XmlFlattenerTest,FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue)518 TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue) {
519   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
520   doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
521 
522   XmlFlattenerOptions options;
523   options.keep_raw_values = true;
524 
525   android::ResXMLTree tree;
526   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
527 
528   while (tree.next() != android::ResXMLTree::START_TAG) {
529     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
530     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
531   }
532 
533   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
534   EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
535 
536   size_t len;
537   EXPECT_THAT(tree.getAttributeStringValue(0, &len), StrEq(u"true"));
538 
539   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
540 }
541 
542 }  // namespace aapt
543