diff options
| -rw-r--r-- | libs/androidfw/include/androidfw/StringPiece.h | 1 | ||||
| -rw-r--r-- | tools/aapt2/flatten/XmlFlattener.cpp | 6 | ||||
| -rw-r--r-- | tools/aapt2/flatten/XmlFlattener_test.cpp | 225 | ||||
| -rw-r--r-- | tools/aapt2/readme.md | 2 | ||||
| -rw-r--r-- | tools/aapt2/test/Common.h | 6 | ||||
| -rw-r--r-- | tools/aapt2/util/Maybe.h | 32 | ||||
| -rw-r--r-- | tools/aapt2/util/Util.cpp | 15 | ||||
| -rw-r--r-- | tools/aapt2/util/Util.h | 3 | ||||
| -rw-r--r-- | tools/aapt2/util/Util_test.cpp | 142 | ||||
| -rw-r--r-- | tools/aapt2/xml/XmlDom_test.cpp | 54 | 
10 files changed, 233 insertions, 253 deletions
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h index a873d66803e7..99b424568a1f 100644 --- a/libs/androidfw/include/androidfw/StringPiece.h +++ b/libs/androidfw/include/androidfw/StringPiece.h @@ -37,6 +37,7 @@ class BasicStringPiece {   public:    using const_iterator = const TChar*;    using difference_type = size_t; +  using size_type = size_t;    // End of string marker.    constexpr static const size_t npos = static_cast<size_t>(-1); diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp index 0711749d0378..bfebedef2a1e 100644 --- a/tools/aapt2/flatten/XmlFlattener.cpp +++ b/tools/aapt2/flatten/XmlFlattener.cpp @@ -257,9 +257,11 @@ class XmlFlattenerVisitor : public xml::Visitor {        // Process plain strings to make sure they get properly escaped.        StringPiece raw_value = xml_attr->value; -      util::StringBuilder str_builder; + +      util::StringBuilder str_builder(true /*preserve_spaces*/); +      str_builder.Append(xml_attr->value); +        if (!options_.keep_raw_values) { -        str_builder.Append(xml_attr->value);          raw_value = str_builder.ToString();        } diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp index f1e903f2151e..a57e3178accd 100644 --- a/tools/aapt2/flatten/XmlFlattener_test.cpp +++ b/tools/aapt2/flatten/XmlFlattener_test.cpp @@ -23,7 +23,13 @@  #include "util/BigBuffer.h"  #include "util/Util.h" -using android::StringPiece16; +using ::aapt::test::StrEq; +using ::android::StringPiece16; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::Ne; +using ::testing::NotNull;  namespace aapt { @@ -72,163 +78,138 @@ class XmlFlattenerTest : public ::testing::Test {  };  TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) { -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF( -            <View xmlns:test="http://com.test" -                  attr="hey"> -              <Layout test:hello="hi" /> -              <Layout>Some text\\</Layout> -            </View>)EOF"); +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( +      <View xmlns:test="http://com.test" attr="hey"> +          <Layout test:hello="hi" /> +          <Layout>Some text\\</Layout> +      </View>)");    android::ResXMLTree tree;    ASSERT_TRUE(Flatten(doc.get(), &tree)); - -  ASSERT_EQ(android::ResXMLTree::START_NAMESPACE, tree.next()); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));    size_t len; -  const char16_t* namespace_prefix = tree.getNamespacePrefix(&len); -  EXPECT_EQ(StringPiece16(u"test"), StringPiece16(namespace_prefix, len)); - -  const char16_t* namespace_uri = tree.getNamespaceUri(&len); -  ASSERT_EQ(StringPiece16(u"http://com.test"), StringPiece16(namespace_uri, len)); - -  ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next()); - -  ASSERT_EQ(nullptr, tree.getElementNamespace(&len)); -  const char16_t* tag_name = tree.getElementName(&len); -  EXPECT_EQ(StringPiece16(u"View"), StringPiece16(tag_name, len)); - -  ASSERT_EQ(1u, tree.getAttributeCount()); -  ASSERT_EQ(nullptr, tree.getAttributeNamespace(0, &len)); -  const char16_t* attr_name = tree.getAttributeName(0, &len); -  EXPECT_EQ(StringPiece16(u"attr"), StringPiece16(attr_name, len)); - -  EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size())); +  EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test")); +  EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test")); -  ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next()); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); +  EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); +  EXPECT_THAT(tree.getElementName(&len), StrEq(u"View")); -  ASSERT_EQ(nullptr, tree.getElementNamespace(&len)); -  tag_name = tree.getElementName(&len); -  EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len)); +  ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); +  EXPECT_THAT(tree.getAttributeNamespace(0, &len), IsNull()); +  EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"attr")); -  ASSERT_EQ(1u, tree.getAttributeCount()); -  const char16_t* attr_namespace = tree.getAttributeNamespace(0, &len); -  EXPECT_EQ(StringPiece16(u"http://com.test"), StringPiece16(attr_namespace, len)); +  const StringPiece16 kAttr(u"attr"); +  EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kAttr.data(), kAttr.size()), Eq(0)); -  attr_name = tree.getAttributeName(0, &len); -  EXPECT_EQ(StringPiece16(u"hello"), StringPiece16(attr_name, len)); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); +  EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); +  EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout")); -  ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next()); -  ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next()); +  ASSERT_THAT(tree.getAttributeCount(), Eq(1u)); +  EXPECT_THAT(tree.getAttributeNamespace(0, &len), StrEq(u"http://com.test")); +  EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"hello")); -  ASSERT_EQ(nullptr, tree.getElementNamespace(&len)); -  tag_name = tree.getElementName(&len); -  EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len)); -  ASSERT_EQ(0u, tree.getAttributeCount()); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG)); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); -  ASSERT_EQ(android::ResXMLTree::TEXT, tree.next()); -  const char16_t* text = tree.getText(&len); -  EXPECT_EQ(StringPiece16(u"Some text\\"), StringPiece16(text, len)); +  EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); +  EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout")); +  ASSERT_THAT(tree.getAttributeCount(), Eq(0u)); -  ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next()); -  ASSERT_EQ(nullptr, tree.getElementNamespace(&len)); -  tag_name = tree.getElementName(&len); -  EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len)); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT)); +  EXPECT_THAT(tree.getText(&len), StrEq(u"Some text\\")); -  ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next()); -  ASSERT_EQ(nullptr, tree.getElementNamespace(&len)); -  tag_name = tree.getElementName(&len); -  EXPECT_EQ(StringPiece16(u"View"), StringPiece16(tag_name, len)); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG)); +  EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); +  EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout")); -  ASSERT_EQ(android::ResXMLTree::END_NAMESPACE, tree.next()); -  namespace_prefix = tree.getNamespacePrefix(&len); -  EXPECT_EQ(StringPiece16(u"test"), StringPiece16(namespace_prefix, len)); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG)); +  EXPECT_THAT(tree.getElementNamespace(&len), IsNull()); +  EXPECT_THAT(tree.getElementName(&len), StrEq(u"View")); -  namespace_uri = tree.getNamespaceUri(&len); -  ASSERT_EQ(StringPiece16(u"http://com.test"), StringPiece16(namespace_uri, len)); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_NAMESPACE)); +  EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test")); +  EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test")); -  ASSERT_EQ(android::ResXMLTree::END_DOCUMENT, tree.next()); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));  }  TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) { -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF( -            <View xmlns:tools="http://schemas.android.com/tools" -                xmlns:foo="http://schemas.android.com/foo" -                foo:bar="Foo" -                tools:ignore="MissingTranslation"/>)EOF"); +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( +      <View xmlns:tools="http://schemas.android.com/tools" +          xmlns:foo="http://schemas.android.com/foo" +          foo:bar="Foo" +          tools:ignore="MissingTranslation"/>)");    android::ResXMLTree tree;    ASSERT_TRUE(Flatten(doc.get(), &tree)); - -  ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));    size_t len; -  const char16_t* namespace_prefix = tree.getNamespacePrefix(&len); -  EXPECT_EQ(StringPiece16(namespace_prefix, len), u"foo"); - -  const char16_t* namespace_uri = tree.getNamespaceUri(&len); -  ASSERT_EQ(StringPiece16(namespace_uri, len), -            u"http://schemas.android.com/foo"); - -  ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG); +  EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"foo")); +  EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://schemas.android.com/foo")); +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG)); -  EXPECT_EQ(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"), -            android::NAME_NOT_FOUND); -  EXPECT_GE(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), 0); +  EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"), +              Eq(android::NAME_NOT_FOUND)); +  EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), Ge(0));  }  TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) { -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF( -            <View xmlns:android="http://schemas.android.com/apk/res/android" -                  android:id="@id/id" -                  class="str" -                  style="@id/id"/>)EOF"); +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( +      <View xmlns:android="http://schemas.android.com/apk/res/android" +          android:id="@id/id" +          class="str" +          style="@id/id"/>)");    android::ResXMLTree tree;    ASSERT_TRUE(Flatten(doc.get(), &tree));    while (tree.next() != android::ResXMLTree::START_TAG) { -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));    } -  EXPECT_EQ(tree.indexOfClass(), 0); -  EXPECT_EQ(tree.indexOfStyle(), 1); +  EXPECT_THAT(tree.indexOfClass(), Eq(0)); +  EXPECT_THAT(tree.indexOfStyle(), Eq(1));  }  // The device ResXMLParser in libandroidfw differentiates between empty namespace and null  // namespace.  TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) { -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"android\"/>"); +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package="android"/>)");    android::ResXMLTree tree;    ASSERT_TRUE(Flatten(doc.get(), &tree));    while (tree.next() != android::ResXMLTree::START_TAG) { -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));    }    const StringPiece16 kPackage = u"package"; -  EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0); +  EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), Ge(0));  }  TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) { -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"\"/>"); +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package=""/>)");    android::ResXMLTree tree;    ASSERT_TRUE(Flatten(doc.get(), &tree));    while (tree.next() != android::ResXMLTree::START_TAG) { -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));    }    const StringPiece16 kPackage = u"package";    ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()); -  ASSERT_GE(idx, 0); +  ASSERT_THAT(idx, Ge(0));    size_t len; -  EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len)); +  EXPECT_THAT(tree.getAttributeStringValue(idx, &len), NotNull());  }  TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) { @@ -236,11 +217,11 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {    context_->SetPackageId(0x80);    context_->SetNameManglerPolicy({"com.app.test.feature"}); -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF( +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(        <View xmlns:android="http://schemas.android.com/apk/res/android"              xmlns:app="http://schemas.android.com/apk/res-auto"              android:id="@id/foo" -            app:foo="@id/foo" />)EOF"); +            app:foo="@id/foo" />)");    XmlReferenceLinker linker;    ASSERT_TRUE(linker.Consume(context_.get(), doc.get())); @@ -253,59 +234,57 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {    ASSERT_TRUE(Flatten(doc.get(), &tree));    while (tree.next() != android::ResXMLTree::START_TAG) { -    ASSERT_NE(android::ResXMLTree::BAD_DOCUMENT, tree.getEventType()); -    ASSERT_NE(android::ResXMLTree::END_DOCUMENT, tree.getEventType()); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));    }    ssize_t idx;    idx = tree.indexOfAttribute(xml::kSchemaAndroid, "id"); -  ASSERT_GE(idx, 0); -  EXPECT_EQ(idx, tree.indexOfID()); -  EXPECT_EQ(ResourceId(0x010100d0), ResourceId(tree.getAttributeNameResID(idx))); +  ASSERT_THAT(idx, Ge(0)); +  EXPECT_THAT(tree.indexOfID(), Eq(idx)); +  EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x010100d0u));    idx = tree.indexOfAttribute(xml::kSchemaAuto, "foo"); -  ASSERT_GE(idx, 0); -  EXPECT_EQ(ResourceId(0x80010000), ResourceId(tree.getAttributeNameResID(idx))); -  EXPECT_EQ(android::Res_value::TYPE_REFERENCE, tree.getAttributeDataType(idx)); -  EXPECT_EQ(ResourceId(0x80020000), tree.getAttributeData(idx)); +  ASSERT_THAT(idx, Ge(0)); +  EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x80010000u)); +  EXPECT_THAT(tree.getAttributeDataType(idx), Eq(android::Res_value::TYPE_REFERENCE)); +  EXPECT_THAT(tree.getAttributeData(idx), Eq(int32_t(0x80020000)));  }  TEST_F(XmlFlattenerTest, ProcessEscapedStrings) {    std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom( -      R"EOF(<element value="\?hello" pattern="\\d{5}">\\d{5}</element>)EOF"); +      R"(<element value="\?hello" pattern="\\d{5}" other=""">\\d{5}</element>)");    android::ResXMLTree tree;    ASSERT_TRUE(Flatten(doc.get(), &tree));    while (tree.next() != android::ResXMLTree::START_TAG) { -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); -    ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT)); +    ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));    }    const StringPiece16 kValue = u"value";    const StringPiece16 kPattern = u"pattern"; +  const StringPiece16 kOther = u"other";    size_t len;    ssize_t idx; -  const char16_t* str16;    idx = tree.indexOfAttribute(nullptr, 0, kValue.data(), kValue.size()); -  ASSERT_GE(idx, 0); -  str16 = tree.getAttributeStringValue(idx, &len); -  ASSERT_NE(nullptr, str16); -  EXPECT_EQ(StringPiece16(u"?hello"), StringPiece16(str16, len)); +  ASSERT_THAT(idx, Ge(0)); +  EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"?hello"));    idx = tree.indexOfAttribute(nullptr, 0, kPattern.data(), kPattern.size()); -  ASSERT_GE(idx, 0); -  str16 = tree.getAttributeStringValue(idx, &len); -  ASSERT_NE(nullptr, str16); -  EXPECT_EQ(StringPiece16(u"\\d{5}"), StringPiece16(str16, len)); - -  ASSERT_EQ(android::ResXMLTree::TEXT, tree.next()); -  str16 = tree.getText(&len); -  ASSERT_NE(nullptr, str16); -  EXPECT_EQ(StringPiece16(u"\\d{5}"), StringPiece16(str16, len)); +  ASSERT_THAT(idx, Ge(0)); +  EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\\d{5}")); + +  idx = tree.indexOfAttribute(nullptr, 0, kOther.data(), kOther.size()); +  ASSERT_THAT(idx, Ge(0)); +  EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\"")); + +  ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT)); +  EXPECT_THAT(tree.getText(&len), StrEq(u"\\d{5}"));  }  }  // namespace aapt diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 01d4a4160615..e128c13460d7 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -11,6 +11,8 @@    the set of Proguard keep rules. (bug 62216174)  - Automatically version XML `<adaptive-icon>` resources to v26. (bug 62316340)  - Fixed issue where escaped unicode characters would generate malformed UTF-8. (bug 62839202) +- Fixed issue where apostrophes or quotes used in XML attribute values were ignored. +  (bug 62840406, 62840718)  ## Version 2.17  ### `aapt2 ...` diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index 11821031b131..d7b46caf8c94 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -136,6 +136,12 @@ void PrintTo(const Maybe<T>& value, std::ostream* out) {  namespace test { +MATCHER_P(StrEq, a, +          std::string(negation ? "isn't" : "is") + " equal to " + +              ::testing::PrintToString(android::StringPiece16(a))) { +  return android::StringPiece16(arg) == a; +} +  MATCHER_P(ValueEq, a,            std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {    return arg.Equals(&a); diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index b43f8e87fd68..9a82418e0a5a 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -281,16 +281,12 @@ inline Maybe<T> make_nothing() {    return Maybe<T>();  } -/** - * Define the == operator between Maybe<T> and Maybe<U> only if the operator T - * == U is defined. - * That way the compiler will show an error at the callsite when comparing two - * Maybe<> objects - * whose inner types can't be compared. - */ +// Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined. +// That way the compiler will show an error at the callsite when comparing two Maybe<> objects +// whose inner types can't be compared.  template <typename T, typename U> -typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==( -    const Maybe<T>& a, const Maybe<U>& b) { +typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a, +                                                                       const Maybe<U>& b) {    if (a && b) {      return a.value() == b.value();    } else if (!a && !b) { @@ -299,18 +295,22 @@ typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(    return false;  } -/** - * Same as operator== but negated. - */  template <typename T, typename U> -typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=( -    const Maybe<T>& a, const Maybe<U>& b) { +typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a, +                                                                       const U& b) { +  return a ? a.value() == b : false; +} + +// Same as operator== but negated. +template <typename T, typename U> +typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(const Maybe<T>& a, +                                                                       const Maybe<U>& b) {    return !(a == b);  }  template <typename T, typename U> -typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<( -    const Maybe<T>& a, const Maybe<U>& b) { +typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(const Maybe<T>& a, +                                                                      const Maybe<U>& b) {    if (a && b) {      return a.value() < b.value();    } else if (!a && !b) { diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index 9fde1b474bf3..51a75d7556ad 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -327,6 +327,9 @@ static bool IsCodepointSpace(char32_t codepoint) {    return isspace(static_cast<char>(codepoint));  } +StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces) { +} +  StringBuilder& StringBuilder::Append(const StringPiece& str) {    if (!error_.empty()) {      return *this; @@ -372,14 +375,12 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {        }        last_char_was_escape_ = false; -    } else if (codepoint == U'"') { +    } else if (!preserve_spaces_ && codepoint == U'"') {        if (!quote_ && trailing_space_) { -        // We found an opening quote, and we have -        // trailing space, so we should append that +        // We found an opening quote, and we have trailing space, so we should append that          // space now.          if (trailing_space_) { -          // We had trailing whitespace, so -          // replace with a single space. +          // We had trailing whitespace, so replace with a single space.            if (!str_.empty()) {              str_ += ' ';            } @@ -388,7 +389,7 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {        }        quote_ = !quote_; -    } else if (codepoint == U'\'' && !quote_) { +    } else if (!preserve_spaces_ && codepoint == U'\'' && !quote_) {        // This should be escaped.        error_ = "unescaped apostrophe";        return *this; @@ -405,7 +406,7 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {        }        last_char_was_escape_ = true;      } else { -      if (quote_) { +      if (preserve_spaces_ || quote_) {          // Quotes mean everything is taken, including whitespace.          AppendCodepointToUtf8String(codepoint, &str_);        } else { diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 8bca9dd739bf..410258c40209 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -171,6 +171,8 @@ bool VerifyJavaStringFormat(const android::StringPiece& str);  class StringBuilder {   public: +  explicit StringBuilder(bool preserve_spaces = false); +    StringBuilder& Append(const android::StringPiece& str);    const std::string& ToString() const;    const std::string& Error() const; @@ -184,6 +186,7 @@ class StringBuilder {    explicit operator bool() const;   private: +  bool preserve_spaces_;    std::string str_;    size_t utf16_len_ = 0;    bool quote_ = false; diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp index a09001a71efc..adb52911ab82 100644 --- a/tools/aapt2/util/Util_test.cpp +++ b/tools/aapt2/util/Util_test.cpp @@ -20,16 +20,17 @@  #include "test/Test.h" -using android::StringPiece; +using ::android::StringPiece; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::SizeIs;  namespace aapt {  TEST(UtilTest, TrimOnlyWhitespace) { -  const std::string full = "\n        "; - -  StringPiece trimmed = util::TrimWhitespace(full); +  const StringPiece trimmed = util::TrimWhitespace("\n        ");    EXPECT_TRUE(trimmed.empty()); -  EXPECT_EQ(0u, trimmed.size()); +  EXPECT_THAT(trimmed, SizeIs(0u));  }  TEST(UtilTest, StringEndsWith) { @@ -41,85 +42,74 @@ TEST(UtilTest, StringStartsWith) {  }  TEST(UtilTest, StringBuilderSplitEscapeSequence) { -  EXPECT_EQ(StringPiece("this is a new\nline."), util::StringBuilder() -                                                     .Append("this is a new\\") -                                                     .Append("nline.") -                                                     .ToString()); +  EXPECT_THAT(util::StringBuilder().Append("this is a new\\").Append("nline.").ToString(), +              Eq("this is a new\nline."));  }  TEST(UtilTest, StringBuilderWhitespaceRemoval) { -  EXPECT_EQ(StringPiece("hey guys this is so cool"), -            util::StringBuilder() -                .Append("    hey guys ") -                .Append(" this is so cool ") -                .ToString()); - -  EXPECT_EQ(StringPiece(" wow,  so many \t spaces. what?"), -            util::StringBuilder() -                .Append(" \" wow,  so many \t ") -                .Append("spaces. \"what? ") -                .ToString()); - -  EXPECT_EQ(StringPiece("where is the pie?"), util::StringBuilder() -                                                  .Append("  where \t ") -                                                  .Append(" \nis the " -                                                          " pie?") -                                                  .ToString()); +  EXPECT_THAT(util::StringBuilder().Append("    hey guys ").Append(" this is so cool ").ToString(), +              Eq("hey guys this is so cool")); +  EXPECT_THAT( +      util::StringBuilder().Append(" \" wow,  so many \t ").Append("spaces. \"what? ").ToString(), +      Eq(" wow,  so many \t spaces. what?")); +  EXPECT_THAT(util::StringBuilder().Append("  where \t ").Append(" \nis the pie?").ToString(), +              Eq("where is the pie?"));  }  TEST(UtilTest, StringBuilderEscaping) { -  EXPECT_EQ(StringPiece("hey guys\n this \t is so\\ cool"), -            util::StringBuilder() -                .Append("    hey guys\\n ") -                .Append(" this \\t is so\\\\ cool ") -                .ToString()); - -  EXPECT_EQ(StringPiece("@?#\\\'"), -            util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString()); +  EXPECT_THAT(util::StringBuilder() +                  .Append("    hey guys\\n ") +                  .Append(" this \\t is so\\\\ cool ") +                  .ToString(), +              Eq("hey guys\n this \t is so\\ cool")); +  EXPECT_THAT(util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString(), Eq("@?#\\\'"));  }  TEST(UtilTest, StringBuilderMisplacedQuote) { -  util::StringBuilder builder{}; +  util::StringBuilder builder;    EXPECT_FALSE(builder.Append("they're coming!"));  }  TEST(UtilTest, StringBuilderUnicodeCodes) { -  EXPECT_EQ(std::string("\u00AF\u0AF0 woah"), -            util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString()); - +  EXPECT_THAT(util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString(), +              Eq("\u00AF\u0AF0 woah"));    EXPECT_FALSE(util::StringBuilder().Append("\\u00 yo"));  } +TEST(UtilTest, StringBuilderPreserveSpaces) { +  EXPECT_THAT(util::StringBuilder(true /*preserve_spaces*/).Append("\"").ToString(), Eq("\"")); +} +  TEST(UtilTest, TokenizeInput) {    auto tokenizer = util::Tokenize(StringPiece("this| is|the|end"), '|');    auto iter = tokenizer.begin(); -  ASSERT_EQ(*iter, StringPiece("this")); +  ASSERT_THAT(*iter, Eq("this"));    ++iter; -  ASSERT_EQ(*iter, StringPiece(" is")); +  ASSERT_THAT(*iter, Eq(" is"));    ++iter; -  ASSERT_EQ(*iter, StringPiece("the")); +  ASSERT_THAT(*iter, Eq("the"));    ++iter; -  ASSERT_EQ(*iter, StringPiece("end")); +  ASSERT_THAT(*iter, Eq("end"));    ++iter; -  ASSERT_EQ(tokenizer.end(), iter); +  ASSERT_THAT(iter, Eq(tokenizer.end()));  }  TEST(UtilTest, TokenizeEmptyString) {    auto tokenizer = util::Tokenize(StringPiece(""), '|');    auto iter = tokenizer.begin(); -  ASSERT_NE(tokenizer.end(), iter); -  ASSERT_EQ(StringPiece(), *iter); +  ASSERT_THAT(iter, Ne(tokenizer.end())); +  ASSERT_THAT(*iter, Eq(StringPiece()));    ++iter; -  ASSERT_EQ(tokenizer.end(), iter); +  ASSERT_THAT(iter, Eq(tokenizer.end()));  }  TEST(UtilTest, TokenizeAtEnd) {    auto tokenizer = util::Tokenize(StringPiece("one."), '.');    auto iter = tokenizer.begin(); -  ASSERT_EQ(*iter, StringPiece("one")); +  ASSERT_THAT(*iter, Eq("one"));    ++iter; -  ASSERT_NE(iter, tokenizer.end()); -  ASSERT_EQ(*iter, StringPiece()); +  ASSERT_THAT(iter, Ne(tokenizer.end())); +  ASSERT_THAT(*iter, Eq(StringPiece()));  }  TEST(UtilTest, IsJavaClassName) { @@ -146,52 +136,34 @@ TEST(UtilTest, IsJavaPackageName) {  }  TEST(UtilTest, FullyQualifiedClassName) { -  Maybe<std::string> res = util::GetFullyQualifiedClassName("android", ".asdf"); -  ASSERT_TRUE(res); -  EXPECT_EQ(res.value(), "android.asdf"); - -  res = util::GetFullyQualifiedClassName("android", ".a.b"); -  ASSERT_TRUE(res); -  EXPECT_EQ(res.value(), "android.a.b"); - -  res = util::GetFullyQualifiedClassName("android", "a.b"); -  ASSERT_TRUE(res); -  EXPECT_EQ(res.value(), "a.b"); - -  res = util::GetFullyQualifiedClassName("", "a.b"); -  ASSERT_TRUE(res); -  EXPECT_EQ(res.value(), "a.b"); - -  res = util::GetFullyQualifiedClassName("android", "Class"); -  ASSERT_TRUE(res); -  EXPECT_EQ(res.value(), "android.Class"); - -  res = util::GetFullyQualifiedClassName("", ""); -  ASSERT_FALSE(res); - -  res = util::GetFullyQualifiedClassName("android", "./Apple"); -  ASSERT_FALSE(res); +  EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".asdf"), Eq("android.asdf")); +  EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".a.b"), Eq("android.a.b")); +  EXPECT_THAT(util::GetFullyQualifiedClassName("android", "a.b"), Eq("a.b")); +  EXPECT_THAT(util::GetFullyQualifiedClassName("", "a.b"), Eq("a.b")); +  EXPECT_THAT(util::GetFullyQualifiedClassName("android", "Class"), Eq("android.Class")); +  EXPECT_FALSE(util::GetFullyQualifiedClassName("", "")); +  EXPECT_FALSE(util::GetFullyQualifiedClassName("android", "./Apple"));  }  TEST(UtilTest, ExtractResourcePathComponents) {    StringPiece prefix, entry, suffix;    ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.xml", &prefix, &entry, &suffix)); -  EXPECT_EQ(prefix, "res/xml-sw600dp/"); -  EXPECT_EQ(entry, "entry"); -  EXPECT_EQ(suffix, ".xml"); +  EXPECT_THAT(prefix, Eq("res/xml-sw600dp/")); +  EXPECT_THAT(entry, Eq("entry")); +  EXPECT_THAT(suffix, Eq(".xml"));    ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.9.png", &prefix, &entry, &suffix)); -  EXPECT_EQ(prefix, "res/xml-sw600dp/"); -  EXPECT_EQ(entry, "entry"); -  EXPECT_EQ(suffix, ".9.png"); +  EXPECT_THAT(prefix, Eq("res/xml-sw600dp/")); +  EXPECT_THAT(entry, Eq("entry")); +  EXPECT_THAT(suffix, Eq(".9.png")); + +  ASSERT_TRUE(util::ExtractResFilePathParts("res//.", &prefix, &entry, &suffix)); +  EXPECT_THAT(prefix, Eq("res//")); +  EXPECT_THAT(entry, Eq("")); +  EXPECT_THAT(suffix, Eq("."));    EXPECT_FALSE(util::ExtractResFilePathParts("AndroidManifest.xml", &prefix, &entry, &suffix));    EXPECT_FALSE(util::ExtractResFilePathParts("res/.xml", &prefix, &entry, &suffix)); - -  ASSERT_TRUE(util::ExtractResFilePathParts("res//.", &prefix, &entry, &suffix)); -  EXPECT_EQ(prefix, "res//"); -  EXPECT_EQ(entry, ""); -  EXPECT_EQ(suffix, ".");  }  TEST(UtilTest, VerifyJavaStringFormat) { diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index c1aa10b89a2f..f0122e8c617a 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -21,7 +21,9 @@  #include "test/Test.h" +using ::testing::Eq;  using ::testing::NotNull; +using ::testing::SizeIs;  namespace aapt { @@ -30,47 +32,59 @@ constexpr const char* kXmlPreamble =  TEST(XmlDomTest, Inflate) {    std::stringstream in(kXmlPreamble); -  in << R"EOF( -        <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> -    )EOF"; - -  const Source source = {"test.xml"}; +  in << R"( +      <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>)"; + +  const Source source("test.xml");    StdErrDiagnostics diag;    std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source);    ASSERT_THAT(doc, NotNull());    xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get());    ASSERT_THAT(ns, NotNull()); -  EXPECT_EQ(ns->namespace_uri, xml::kSchemaAndroid); -  EXPECT_EQ(ns->namespace_prefix, "android"); +  EXPECT_THAT(ns->namespace_uri, Eq(xml::kSchemaAndroid)); +  EXPECT_THAT(ns->namespace_prefix, Eq("android"));  }  // Escaping is handled after parsing of the values for resource-specific values.  TEST(XmlDomTest, ForwardEscapes) { -  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF( -      <element value="\?hello" pattern="\\d{5}">\\d{5}</element>)EOF"); +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( +      <element value="\?hello" pattern="\\d{5}">\\d{5}</element>)"); -  xml::Element* el = xml::FindRootElement(doc->root.get()); +  xml::Element* el = xml::FindRootElement(doc.get());    ASSERT_THAT(el, NotNull());    xml::Attribute* attr = el->FindAttribute({}, "pattern");    ASSERT_THAT(attr, NotNull()); -  EXPECT_EQ("\\\\d{5}", attr->value); +  EXPECT_THAT(attr->value, Eq("\\\\d{5}"));    attr = el->FindAttribute({}, "value");    ASSERT_THAT(attr, NotNull()); -  EXPECT_EQ("\\?hello", attr->value); +  EXPECT_THAT(attr->value, Eq("\\?hello")); + +  ASSERT_THAT(el->children, SizeIs(1u)); -  ASSERT_EQ(1u, el->children.size());    xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());    ASSERT_THAT(text, NotNull()); -  EXPECT_EQ("\\\\d{5}", text->text); +  EXPECT_THAT(text->text, Eq("\\\\d{5}")); +} + +TEST(XmlDomTest, XmlEscapeSequencesAreParsed) { +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element value=""" />)"); + +  xml::Element* el = xml::FindRootElement(doc.get()); +  ASSERT_THAT(el, NotNull()); + +  xml::Attribute* attr = el->FindAttribute({}, "value"); +  ASSERT_THAT(attr, NotNull()); + +  EXPECT_THAT(attr->value, Eq("\""));  }  }  // namespace aapt  |