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 
26 using ::aapt::test::StrEq;
27 using ::android::StringPiece16;
28 using ::testing::Eq;
29 using ::testing::Ge;
30 using ::testing::IsNull;
31 using ::testing::Ne;
32 using ::testing::NotNull;
33 
34 namespace aapt {
35 
36 class XmlFlattenerTest : public ::testing::Test {
37  public:
SetUp()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 
Flatten(xml::XmlResource * doc,android::ResXMLTree * out_tree,const XmlFlattenerOptions & options={})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 
TEST_F(XmlFlattenerTest,FlattenXmlWithNoCompiledAttributes)79 TEST_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 
TEST_F(XmlFlattenerTest,FlattenCompiledXmlAndStripOnlyTools)138 TEST_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 
TEST_F(XmlFlattenerTest,AssignSpecialAttributeIndices)159 TEST_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.
180 TEST_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 
195 TEST_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 
214 TEST_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 
254 TEST_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 
TEST_F(XmlFlattenerTest,ProcessQuotes)289 TEST_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 
TEST_F(XmlFlattenerTest,ProcessWhitepspace)341 TEST_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>
356 I
357 </item>
358 <item>
359 
360    J
361 
362 </item>
363           <item>\t K \n </item>
364           <item>
365           </item>
366       </root>)");
367 
368   size_t len;
369   android::ResXMLTree tree;
370 
371   XmlFlattenerOptions options;
372   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
373 
374   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
375   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
376   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
377   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
378   EXPECT_THAT(tree.getText(&len), StrEq(u" Compact   Spaces "));
379   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
380 
381   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
382   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
383   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
384   EXPECT_THAT(tree.getText(&len), StrEq(u" A "));
385   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
386 
387   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
388   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
389   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
390   EXPECT_THAT(tree.getText(&len), StrEq(u"B "));
391   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
392 
393   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
394   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
395   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
396   EXPECT_THAT(tree.getText(&len), StrEq(u"C "));
397   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
398 
399   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
400   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
401   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
402   EXPECT_THAT(tree.getText(&len), StrEq(u" D "));
403   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
404 
405   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
406   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
407   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
408   EXPECT_THAT(tree.getText(&len), StrEq(u" E"));
409   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
410 
411   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
412   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
413   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
414   EXPECT_THAT(tree.getText(&len), StrEq(u" F"));
415   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
416 
417   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
418   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
419   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
420   EXPECT_THAT(tree.getText(&len), StrEq(u" G "));
421   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
422 
423   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
424   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
425   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
426   EXPECT_THAT(tree.getText(&len), StrEq(u" H "));
427   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
428 
429   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
430   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
431   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
432   EXPECT_THAT(tree.getText(&len), StrEq(u" I "));
433   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
434 
435   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
436   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
437   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
438   EXPECT_THAT(tree.getText(&len), StrEq(u" J "));
439   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
440 
441   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
442   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
443   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
444   EXPECT_THAT(tree.getText(&len), StrEq(u"\\t K \\n "));
445   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
446 
447   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
448   EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
449   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
450   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
451 
452   ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
453 }
454 
TEST_F(XmlFlattenerTest,FlattenRawValueOnlyMakesCompiledValueToo)455 TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) {
456   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
457 
458   // Raw values are kept when encoding an attribute with no compiled value, regardless of option.
459   XmlFlattenerOptions options;
460   options.keep_raw_values = false;
461 
462   android::ResXMLTree tree;
463   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
464 
465   while (tree.next() != android::ResXMLTree::START_TAG) {
466     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
467     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
468   }
469 
470   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
471   EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
472   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
473   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
474 }
475 
TEST_F(XmlFlattenerTest,FlattenCompiledStringValuePreservesRawValue)476 TEST_F(XmlFlattenerTest, FlattenCompiledStringValuePreservesRawValue) {
477   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
478   doc->root->attributes[0].compiled_value =
479       util::make_unique<String>(doc->string_pool.MakeRef("bar"));
480 
481   // Raw values are kept when encoding a string anyways.
482   XmlFlattenerOptions options;
483   options.keep_raw_values = false;
484 
485   android::ResXMLTree tree;
486   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
487 
488   while (tree.next() != android::ResXMLTree::START_TAG) {
489     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
490     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
491   }
492 
493   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
494   EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
495   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
496   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
497 }
498 
TEST_F(XmlFlattenerTest,FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse)499 TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse) {
500   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
501   doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
502 
503   XmlFlattenerOptions options;
504   options.keep_raw_values = false;
505 
506   android::ResXMLTree tree;
507   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
508 
509   while (tree.next() != android::ResXMLTree::START_TAG) {
510     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
511     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
512   }
513 
514   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
515   EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
516   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
517 }
518 
TEST_F(XmlFlattenerTest,FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue)519 TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue) {
520   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
521   doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
522 
523   XmlFlattenerOptions options;
524   options.keep_raw_values = true;
525 
526   android::ResXMLTree tree;
527   ASSERT_TRUE(Flatten(doc.get(), &tree, options));
528 
529   while (tree.next() != android::ResXMLTree::START_TAG) {
530     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
531     ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
532   }
533 
534   ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
535   EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
536 
537   size_t len;
538   EXPECT_THAT(tree.getAttributeStringValue(0, &len), StrEq(u"true"));
539 
540   EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
541 }
542 
543 }  // namespace aapt
544