AAPT2: Fix Plural::Equals() method

Test: make aapt2_tests
Bug: 35902437
Change-Id: I8797f89af58876f891f0b0c5cce85fd7781c4e24
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 67ed476..eefa320 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -582,6 +582,16 @@
       "  <item quantity=\"one\">apple</item>\n"
       "</plurals>";
   ASSERT_TRUE(TestParse(input));
+
+  Plural* plural = test::GetValue<Plural>(&table_, "plurals/foo");
+  ASSERT_NE(nullptr, plural);
+  EXPECT_EQ(nullptr, plural->values[Plural::Zero]);
+  EXPECT_EQ(nullptr, plural->values[Plural::Two]);
+  EXPECT_EQ(nullptr, plural->values[Plural::Few]);
+  EXPECT_EQ(nullptr, plural->values[Plural::Many]);
+
+  EXPECT_NE(nullptr, plural->values[Plural::One]);
+  EXPECT_NE(nullptr, plural->values[Plural::Other]);
 }
 
 TEST_F(ResourceParserTest, ParseCommentsWithResource) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 2868b2a..0cb8c67 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -341,7 +341,7 @@
 }
 
 template <typename T>
-T* addPointer(T& val) {
+constexpr T* add_pointer(T& val) {
   return &val;
 }
 
@@ -362,7 +362,7 @@
 
   std::vector<const Symbol*> sorted_a;
   std::transform(symbols.begin(), symbols.end(), std::back_inserter(sorted_a),
-                 addPointer<const Symbol>);
+                 add_pointer<const Symbol>);
   std::sort(sorted_a.begin(), sorted_a.end(),
             [](const Symbol* a, const Symbol* b) -> bool {
               return a->symbol.name < b->symbol.name;
@@ -370,7 +370,7 @@
 
   std::vector<const Symbol*> sorted_b;
   std::transform(other->symbols.begin(), other->symbols.end(),
-                 std::back_inserter(sorted_b), addPointer<const Symbol>);
+                 std::back_inserter(sorted_b), add_pointer<const Symbol>);
   std::sort(sorted_b.begin(), sorted_b.end(),
             [](const Symbol* a, const Symbol* b) -> bool {
               return a->symbol.name < b->symbol.name;
@@ -599,7 +599,7 @@
 
   std::vector<const Entry*> sorted_a;
   std::transform(entries.begin(), entries.end(), std::back_inserter(sorted_a),
-                 addPointer<const Entry>);
+                 add_pointer<const Entry>);
   std::sort(sorted_a.begin(), sorted_a.end(),
             [](const Entry* a, const Entry* b) -> bool {
               return a->key.name < b->key.name;
@@ -607,7 +607,7 @@
 
   std::vector<const Entry*> sorted_b;
   std::transform(other->entries.begin(), other->entries.end(),
-                 std::back_inserter(sorted_b), addPointer<const Entry>);
+                 std::back_inserter(sorted_b), add_pointer<const Entry>);
   std::sort(sorted_b.begin(), sorted_b.end(),
             [](const Entry* a, const Entry* b) -> bool {
               return a->key.name < b->key.name;
@@ -695,18 +695,21 @@
     return false;
   }
 
-  if (values.size() != other->values.size()) {
-    return false;
+  auto one_iter = values.begin();
+  auto one_end_iter = values.end();
+  auto two_iter = other->values.begin();
+  for (; one_iter != one_end_iter; ++one_iter, ++two_iter) {
+    const std::unique_ptr<Item>& a = *one_iter;
+    const std::unique_ptr<Item>& b = *two_iter;
+    if (a != nullptr && b != nullptr) {
+      if (!a->Equals(b.get())) {
+        return false;
+      }
+    } else if (a != b) {
+      return false;
+    }
   }
-
-  return std::equal(values.begin(), values.end(), other->values.begin(),
-                    [](const std::unique_ptr<Item>& a,
-                       const std::unique_ptr<Item>& b) -> bool {
-                      if (bool(a) != bool(b)) {
-                        return false;
-                      }
-                      return bool(a) == bool(b) || a->Equals(b.get());
-                    });
+  return true;
 }
 
 Plural* Plural::Clone(StringPool* new_pool) const {
@@ -743,6 +746,10 @@
   if (values[Many]) {
     *out << " many=" << *values[Many];
   }
+
+  if (values[Other]) {
+    *out << " other=" << *values[Other];
+  }
 }
 
 static ::std::ostream& operator<<(::std::ostream& out,
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
new file mode 100644
index 0000000..6922580
--- /dev/null
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 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 "ResourceValues.h"
+
+#include "test/Test.h"
+
+namespace aapt {
+
+TEST(ResourceValuesTest, PluralEquals) {
+  StringPool pool;
+
+  Plural a;
+  a.values[Plural::One] = util::make_unique<String>(pool.MakeRef("one"));
+  a.values[Plural::Other] = util::make_unique<String>(pool.MakeRef("other"));
+
+  Plural b;
+  b.values[Plural::One] = util::make_unique<String>(pool.MakeRef("une"));
+  b.values[Plural::Other] = util::make_unique<String>(pool.MakeRef("autre"));
+
+  Plural c;
+  c.values[Plural::One] = util::make_unique<String>(pool.MakeRef("one"));
+  c.values[Plural::Other] = util::make_unique<String>(pool.MakeRef("other"));
+
+  EXPECT_FALSE(a.Equals(&b));
+  EXPECT_TRUE(a.Equals(&c));
+}
+
+TEST(ResourceValuesTest, PluralClone) {
+  StringPool pool;
+
+  Plural a;
+  a.values[Plural::One] = util::make_unique<String>(pool.MakeRef("one"));
+  a.values[Plural::Other] = util::make_unique<String>(pool.MakeRef("other"));
+
+  std::unique_ptr<Plural> b(a.Clone(&pool));
+  EXPECT_TRUE(a.Equals(b.get()));
+}
+
+TEST(ResourceValuesTest, ArrayEquals) {
+  StringPool pool;
+
+  Array a;
+  a.items.push_back(util::make_unique<String>(pool.MakeRef("one")));
+  a.items.push_back(util::make_unique<String>(pool.MakeRef("two")));
+
+  Array b;
+  b.items.push_back(util::make_unique<String>(pool.MakeRef("une")));
+  b.items.push_back(util::make_unique<String>(pool.MakeRef("deux")));
+
+  Array c;
+  c.items.push_back(util::make_unique<String>(pool.MakeRef("uno")));
+
+  Array d;
+  d.items.push_back(util::make_unique<String>(pool.MakeRef("one")));
+  d.items.push_back(util::make_unique<String>(pool.MakeRef("two")));
+
+  EXPECT_FALSE(a.Equals(&b));
+  EXPECT_FALSE(a.Equals(&c));
+  EXPECT_FALSE(b.Equals(&c));
+  EXPECT_TRUE(a.Equals(&d));
+}
+
+TEST(ResourceValuesTest, ArrayClone) {
+  StringPool pool;
+
+  Array a;
+  a.items.push_back(util::make_unique<String>(pool.MakeRef("one")));
+  a.items.push_back(util::make_unique<String>(pool.MakeRef("two")));
+
+  std::unique_ptr<Array> b(a.Clone(&pool));
+  EXPECT_TRUE(a.Equals(b.get()));
+}
+
+TEST(ResourceValuesTest, StyleEquals) {
+  StringPool pool;
+
+  std::unique_ptr<Style> a = test::StyleBuilder()
+      .SetParent("android:style/Parent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bar", ResourceUtils::TryParseInt("2"))
+      .Build();
+
+  std::unique_ptr<Style> b = test::StyleBuilder()
+      .SetParent("android:style/Parent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bar", ResourceUtils::TryParseInt("3"))
+      .Build();
+
+  std::unique_ptr<Style> c = test::StyleBuilder()
+      .SetParent("android:style/NoParent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bar", ResourceUtils::TryParseInt("2"))
+      .Build();
+
+  std::unique_ptr<Style> d = test::StyleBuilder()
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bar", ResourceUtils::TryParseInt("2"))
+      .Build();
+
+  std::unique_ptr<Style> e = test::StyleBuilder()
+      .SetParent("android:style/Parent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bat", ResourceUtils::TryParseInt("2"))
+      .Build();
+
+  std::unique_ptr<Style> f = test::StyleBuilder()
+      .SetParent("android:style/Parent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .Build();
+
+  std::unique_ptr<Style> g = test::StyleBuilder()
+      .SetParent("android:style/Parent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bar", ResourceUtils::TryParseInt("2"))
+      .Build();
+
+  EXPECT_FALSE(a->Equals(b.get()));
+  EXPECT_FALSE(a->Equals(c.get()));
+  EXPECT_FALSE(a->Equals(d.get()));
+  EXPECT_FALSE(a->Equals(e.get()));
+  EXPECT_FALSE(a->Equals(f.get()));
+
+  EXPECT_TRUE(a->Equals(g.get()));
+}
+
+TEST(ResourceValuesTest, StyleClone) {
+  std::unique_ptr<Style> a = test::StyleBuilder()
+      .SetParent("android:style/Parent")
+      .AddItem("android:attr/foo", ResourceUtils::TryParseInt("1"))
+      .AddItem("android:attr/bar", ResourceUtils::TryParseInt("2"))
+      .Build();
+
+  std::unique_ptr<Style> b(a->Clone(nullptr));
+  EXPECT_TRUE(a->Equals(b.get()));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/optimize/ResourceDeduper.cpp b/tools/aapt2/optimize/ResourceDeduper.cpp
index 3aab2e3..9d16268 100644
--- a/tools/aapt2/optimize/ResourceDeduper.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper.cpp
@@ -77,6 +77,8 @@
           DiagMessage(node_value->value->GetSource())
           << "removing dominated duplicate resource with name \""
           << entry_->name << "\"");
+      context_->GetDiagnostics()->Note(
+          DiagMessage(parent_value->value->GetSource()) << "dominated here");
     }
     node_value->value = {};
   }