RTCStats equality operator added.

This will be helpful in unittests to EXPECT_EQ reports. It should be a
useful operator to have outside of testing as well.

BUG=chromium:627816
NOTRY=True

Review-Url: https://codereview.webrtc.org/2441543002
Cr-Original-Commit-Position: refs/heads/master@{#14767}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 67c8bc4bf2cc10669e5cb7fd461469f30edcaa23
diff --git a/api/stats/rtcstats.h b/api/stats/rtcstats.h
index 6917548..0876080 100644
--- a/api/stats/rtcstats.h
+++ b/api/stats/rtcstats.h
@@ -62,9 +62,14 @@
   int64_t timestamp_us() const { return timestamp_us_; }
   // Returns the static member variable |kType| of the implementing class.
   virtual const char* type() const = 0;
-  // Returns a vector of pointers to all the RTCStatsMemberInterface members of
-  // this class. This allows for iteration of members.
+  // Returns a vector of pointers to all the |RTCStatsMemberInterface| members
+  // of this class. This allows for iteration of members. For a given class,
+  // |Members| always returns the same members in the same order.
   std::vector<const RTCStatsMemberInterface*> Members() const;
+  // Checks if the two stats objects are of the same type and have the same
+  // member values. These operators are exposed for testing.
+  bool operator==(const RTCStats& other) const;
+  bool operator!=(const RTCStats& other) const;
 
   // Creates a human readable string representation of the report, listing all
   // of its members (names and values).
@@ -209,6 +214,12 @@
   virtual bool is_sequence() const = 0;
   virtual bool is_string() const = 0;
   bool is_defined() const { return is_defined_; }
+  // Type and value comparator. The names are not compared. These operators are
+  // exposed for testing.
+  virtual bool operator==(const RTCStatsMemberInterface& other) const = 0;
+  bool operator!=(const RTCStatsMemberInterface& other) const {
+    return !(*this == other);
+  }
   virtual std::string ValueToString() const = 0;
 
   template<typename T>
@@ -253,6 +264,15 @@
   Type type() const override { return kType; }
   bool is_sequence() const override;
   bool is_string() const override;
+  bool operator==(const RTCStatsMemberInterface& other) const override {
+    if (type() != other.type())
+      return false;
+    const RTCStatsMember<T>& other_t =
+        static_cast<const RTCStatsMember<T>&>(other);
+    if (!is_defined_)
+      return !other_t.is_defined();
+    return value_ == other_t.value_;
+  }
   std::string ValueToString() const override;
 
   // Assignment operators.
diff --git a/stats/rtcstats.cc b/stats/rtcstats.cc
index 543ef42..fb9740a 100644
--- a/stats/rtcstats.cc
+++ b/stats/rtcstats.cc
@@ -48,6 +48,29 @@
 
 }  // namespace
 
+bool RTCStats::operator==(const RTCStats& other) const {
+  if (type() != other.type() || id() != other.id() ||
+      timestamp_us() != other.timestamp_us()) {
+    return false;
+  }
+  std::vector<const RTCStatsMemberInterface*> members = Members();
+  std::vector<const RTCStatsMemberInterface*> other_members = other.Members();
+  RTC_DCHECK_EQ(members.size(), other_members.size());
+  for (size_t i = 0; i < members.size(); ++i) {
+    const RTCStatsMemberInterface* member = members[i];
+    const RTCStatsMemberInterface* other_member = other_members[i];
+    RTC_DCHECK_EQ(member->type(), other_member->type());
+    RTC_DCHECK_EQ(member->name(), other_member->name());
+    if (*member != *other_member)
+      return false;
+  }
+  return true;
+}
+
+bool RTCStats::operator!=(const RTCStats& other) const {
+  return !(*this == other);
+}
+
 std::string RTCStats::ToString() const {
   std::ostringstream oss;
   oss << type() << " {\n  id: \"" << id_ << "\"\n  timestamp: "
diff --git a/stats/rtcstats_unittest.cc b/stats/rtcstats_unittest.cc
index ad839d8..f245194 100644
--- a/stats/rtcstats_unittest.cc
+++ b/stats/rtcstats_unittest.cc
@@ -113,6 +113,67 @@
   EXPECT_EQ(*stats.m_sequence_int32, numbers_sequence);
 }
 
+TEST(RTCStatsTest, EqualityOperator) {
+  RTCTestStats empty_stats("testId", 123);
+  EXPECT_EQ(empty_stats, empty_stats);
+
+  RTCTestStats stats_with_all_values = empty_stats;
+  stats_with_all_values.m_bool = true;
+  stats_with_all_values.m_int32 = 123;
+  stats_with_all_values.m_uint32 = 123;
+  stats_with_all_values.m_int64 = 123;
+  stats_with_all_values.m_uint64 = 123;
+  stats_with_all_values.m_double = 123.0;
+  stats_with_all_values.m_string = "123";
+  stats_with_all_values.m_sequence_bool = std::vector<bool>();
+  stats_with_all_values.m_sequence_int32 = std::vector<int32_t>();
+  stats_with_all_values.m_sequence_uint32 = std::vector<uint32_t>();
+  stats_with_all_values.m_sequence_int64 = std::vector<int64_t>();
+  stats_with_all_values.m_sequence_uint64 = std::vector<uint64_t>();
+  stats_with_all_values.m_sequence_double = std::vector<double>();
+  stats_with_all_values.m_sequence_string = std::vector<std::string>();
+  EXPECT_NE(stats_with_all_values, empty_stats);
+  EXPECT_EQ(stats_with_all_values, stats_with_all_values);
+  EXPECT_NE(stats_with_all_values.m_int32, stats_with_all_values.m_uint32);
+
+  RTCTestStats one_member_different[] = {
+    stats_with_all_values, stats_with_all_values, stats_with_all_values,
+    stats_with_all_values, stats_with_all_values, stats_with_all_values,
+    stats_with_all_values, stats_with_all_values, stats_with_all_values,
+    stats_with_all_values, stats_with_all_values, stats_with_all_values,
+    stats_with_all_values, stats_with_all_values,
+  };
+  for (size_t i = 0; i < 14; ++i) {
+    EXPECT_EQ(stats_with_all_values, one_member_different[i]);
+  }
+  one_member_different[0].m_bool = false;
+  one_member_different[1].m_int32 = 321;
+  one_member_different[2].m_uint32 = 321;
+  one_member_different[3].m_int64 = 321;
+  one_member_different[4].m_uint64 = 321;
+  one_member_different[5].m_double = 321.0;
+  one_member_different[6].m_string = "321";
+  one_member_different[7].m_sequence_bool->push_back(false);
+  one_member_different[8].m_sequence_int32->push_back(321);
+  one_member_different[9].m_sequence_uint32->push_back(321);
+  one_member_different[10].m_sequence_int64->push_back(321);
+  one_member_different[11].m_sequence_uint64->push_back(321);
+  one_member_different[12].m_sequence_double->push_back(321.0);
+  one_member_different[13].m_sequence_string->push_back("321");
+  for (size_t i = 0; i < 14; ++i) {
+    EXPECT_NE(stats_with_all_values, one_member_different[i]);
+  }
+
+  RTCTestStats empty_stats_different_id("testId2", 123);
+  EXPECT_NE(empty_stats, empty_stats_different_id);
+  RTCTestStats empty_stats_different_timestamp("testId", 321);
+  EXPECT_NE(empty_stats, empty_stats_different_timestamp);
+
+  RTCChildStats child("childId", 42);
+  RTCGrandChildStats grandchild("grandchildId", 42);
+  EXPECT_NE(child, grandchild);
+}
+
 TEST(RTCStatsTest, RTCStatsGrandChild) {
   RTCGrandChildStats stats("grandchild", 0.0);
   stats.child_int = 1;