| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "xml/XmlDom.h" |
| |
| #include <string> |
| |
| #include "format/binary/XmlFlattener.h" |
| #include "io/StringStream.h" |
| #include "test/Test.h" |
| |
| using ::aapt::io::StringInputStream; |
| using ::aapt::test::ValueEq; |
| using ::testing::Eq; |
| using ::testing::NotNull; |
| using ::testing::Pointee; |
| using ::testing::SizeIs; |
| using ::testing::StrEq; |
| |
| namespace aapt { |
| namespace xml { |
| |
| TEST(XmlDomTest, Inflate) { |
| std::string input = R"(<?xml version="1.0" encoding="utf-8"?> |
| <Layout xmlns:android="http://schemas.android.com/apk/res/android" |
| android:layout_width="match_parent" |
| android:layout_height="wrap_content"> |
| <TextView android:id="@+id/id" |
| android:layout_width="wrap_content" |
| android:layout_height="wrap_content" /> |
| </Layout>)"; |
| |
| StdErrDiagnostics diag; |
| StringInputStream in(input); |
| std::unique_ptr<XmlResource> doc = Inflate(&in, &diag, android::Source("test.xml")); |
| ASSERT_THAT(doc, NotNull()); |
| |
| Element* el = doc->root.get(); |
| EXPECT_THAT(el->namespace_decls, SizeIs(1u)); |
| EXPECT_THAT(el->namespace_decls[0].uri, StrEq(xml::kSchemaAndroid)); |
| EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android")); |
| } |
| |
| TEST(XmlDomTest, BinaryInflate) { |
| std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); |
| std::unique_ptr<XmlResource> doc = util::make_unique<XmlResource>(); |
| doc->root = util::make_unique<Element>(); |
| doc->root->name = "Layout"; |
| doc->root->line_number = 2u; |
| |
| xml::Attribute attr; |
| attr.name = "text"; |
| attr.namespace_uri = kSchemaAndroid; |
| attr.compiled_attribute = AaptAttribute( |
| aapt::Attribute(android::ResTable_map::TYPE_REFERENCE | android::ResTable_map::TYPE_STRING), |
| ResourceId(0x01010001u)); |
| attr.value = "@string/foo"; |
| attr.compiled_value = test::BuildReference("string/foo", ResourceId(0x7f010000u)); |
| doc->root->attributes.push_back(std::move(attr)); |
| |
| NamespaceDecl decl; |
| decl.uri = kSchemaAndroid; |
| decl.prefix = "android"; |
| decl.line_number = 2u; |
| doc->root->namespace_decls.push_back(decl); |
| |
| android::BigBuffer buffer(4096); |
| XmlFlattenerOptions options; |
| options.keep_raw_values = true; |
| XmlFlattener flattener(&buffer, options); |
| ASSERT_TRUE(flattener.Consume(context.get(), doc.get())); |
| |
| auto block = android::util::Copy(buffer); |
| std::unique_ptr<XmlResource> new_doc = Inflate(block.get(), buffer.size(), nullptr); |
| ASSERT_THAT(new_doc, NotNull()); |
| |
| EXPECT_THAT(new_doc->root->name, StrEq("Layout")); |
| EXPECT_THAT(new_doc->root->line_number, Eq(2u)); |
| |
| ASSERT_THAT(new_doc->root->attributes, SizeIs(1u)); |
| EXPECT_THAT(new_doc->root->attributes[0].name, StrEq("text")); |
| EXPECT_THAT(new_doc->root->attributes[0].namespace_uri, StrEq(kSchemaAndroid)); |
| |
| // We only check that the resource ID was preserved. There is no where to encode the types that |
| // the Attribute accepts (eg: string|reference). |
| ASSERT_TRUE(new_doc->root->attributes[0].compiled_attribute); |
| EXPECT_THAT(new_doc->root->attributes[0].compiled_attribute.value().id, |
| Eq(ResourceId(0x01010001u))); |
| |
| EXPECT_THAT(new_doc->root->attributes[0].value, StrEq("@string/foo")); |
| EXPECT_THAT(new_doc->root->attributes[0].compiled_value, |
| Pointee(ValueEq(Reference(ResourceId(0x7f010000u))))); |
| |
| ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u)); |
| EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid)); |
| EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android")); |
| EXPECT_THAT(new_doc->root->namespace_decls[0].line_number, Eq(2u)); |
| } |
| |
| // Escaping is handled after parsing of the values for resource-specific values. |
| TEST(XmlDomTest, ForwardEscapes) { |
| std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"( |
| <element value="\?hello" pattern="\\d{5}">\\d{5}</element>)"); |
| |
| Element* el = doc->root.get(); |
| |
| Attribute* attr = el->FindAttribute({}, "pattern"); |
| ASSERT_THAT(attr, NotNull()); |
| EXPECT_THAT(attr->value, Eq("\\\\d{5}")); |
| |
| attr = el->FindAttribute({}, "value"); |
| ASSERT_THAT(attr, NotNull()); |
| EXPECT_THAT(attr->value, Eq("\\?hello")); |
| |
| ASSERT_THAT(el->children, SizeIs(1u)); |
| |
| Text* text = xml::NodeCast<xml::Text>(el->children[0].get()); |
| ASSERT_THAT(text, NotNull()); |
| EXPECT_THAT(text->text, Eq("\\\\d{5}")); |
| } |
| |
| TEST(XmlDomTest, XmlEscapeSequencesAreParsed) { |
| std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(<element value=""" />)"); |
| Attribute* attr = doc->root->FindAttribute({}, "value"); |
| ASSERT_THAT(attr, NotNull()); |
| EXPECT_THAT(attr->value, Eq("\"")); |
| } |
| |
| class TestVisitor : public PackageAwareVisitor { |
| public: |
| using PackageAwareVisitor::Visit; |
| |
| void Visit(Element* el) override { |
| if (el->name == "View1") { |
| EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false})); |
| } else if (el->name == "View2") { |
| EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false})); |
| EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false})); |
| } else if (el->name == "View3") { |
| EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false})); |
| EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false})); |
| EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false})); |
| } else if (el->name == "View4") { |
| EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false})); |
| EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false})); |
| EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false})); |
| EXPECT_THAT(TransformPackageAlias("four"), Eq(ExtractedPackage{"", true})); |
| } |
| } |
| }; |
| |
| TEST(XmlDomTest, PackageAwareXmlVisitor) { |
| std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"( |
| <View1 xmlns:one="http://schemas.android.com/apk/res/com.one"> |
| <View2 xmlns:two="http://schemas.android.com/apk/res/com.two"> |
| <View3 xmlns:three="http://schemas.android.com/apk/res/com.three"> |
| <View4 xmlns:four="http://schemas.android.com/apk/res-auto" /> |
| </View3> |
| </View2> |
| </View1>)"); |
| |
| TestVisitor visitor; |
| doc->root->Accept(&visitor); |
| } |
| |
| } // namespace xml |
| } // namespace aapt |