diff --git a/webrtc/api/stats/rtcstats.h b/webrtc/api/stats/rtcstats.h index 6917548302..0876080b00 100644 --- a/webrtc/api/stats/rtcstats.h +++ b/webrtc/api/stats/rtcstats.h @@ -62,9 +62,14 @@ class RTCStats { 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 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 @@ class RTCStatsMemberInterface { 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 @@ -253,6 +264,15 @@ class RTCStatsMember : public RTCStatsMemberInterface { 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& other_t = + static_cast&>(other); + if (!is_defined_) + return !other_t.is_defined(); + return value_ == other_t.value_; + } std::string ValueToString() const override; // Assignment operators. diff --git a/webrtc/stats/rtcstats.cc b/webrtc/stats/rtcstats.cc index 543ef42013..fb9740a12e 100644 --- a/webrtc/stats/rtcstats.cc +++ b/webrtc/stats/rtcstats.cc @@ -48,6 +48,29 @@ std::string VectorOfStringsToString(const std::vector& strings) { } // namespace +bool RTCStats::operator==(const RTCStats& other) const { + if (type() != other.type() || id() != other.id() || + timestamp_us() != other.timestamp_us()) { + return false; + } + std::vector members = Members(); + std::vector 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/webrtc/stats/rtcstats_unittest.cc b/webrtc/stats/rtcstats_unittest.cc index ad839d811d..f24519467c 100644 --- a/webrtc/stats/rtcstats_unittest.cc +++ b/webrtc/stats/rtcstats_unittest.cc @@ -113,6 +113,67 @@ TEST(RTCStatsTest, RTCStatsAndMembers) { 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(); + stats_with_all_values.m_sequence_int32 = std::vector(); + stats_with_all_values.m_sequence_uint32 = std::vector(); + stats_with_all_values.m_sequence_int64 = std::vector(); + stats_with_all_values.m_sequence_uint64 = std::vector(); + stats_with_all_values.m_sequence_double = std::vector(); + stats_with_all_values.m_sequence_string = std::vector(); + 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;