summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/sdp/ParsingResultComparer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/sdp/ParsingResultComparer.cpp')
-rw-r--r--dom/media/webrtc/sdp/ParsingResultComparer.cpp331
1 files changed, 331 insertions, 0 deletions
diff --git a/dom/media/webrtc/sdp/ParsingResultComparer.cpp b/dom/media/webrtc/sdp/ParsingResultComparer.cpp
new file mode 100644
index 0000000000..8592fa52b3
--- /dev/null
+++ b/dom/media/webrtc/sdp/ParsingResultComparer.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/Sdp.h"
+#include "sdp/ParsingResultComparer.h"
+#include "sdp/SipccSdpParser.h"
+#include "sdp/RsdparsaSdpParser.h"
+#include "sdp/SdpTelemetry.h"
+
+#include <string>
+#include <ostream>
+#include <regex>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Logging.h"
+
+using mozilla::LogLevel;
+static mozilla::LazyLogModule sSdpDiffLogger("sdpdiff_logger");
+
+#define LOGD(msg) MOZ_LOG(sSdpDiffLogger, LogLevel::Debug, msg)
+#define LOGE(msg) MOZ_LOG(sSdpDiffLogger, LogLevel::Error, msg)
+
+#define LOG_EXPECT(result, expect, msg) \
+ { \
+ if (((expect) == SdpComparisonResult::Equal) == (result)) { \
+ LOGD(msg); \
+ } else { \
+ LOGE(("UNEXPECTED COMPARISON RESULT: vvvvvv")); \
+ LOGE(msg); \
+ } \
+ }
+
+namespace mozilla {
+
+using AttributeType = SdpAttribute::AttributeType;
+
+template <typename T>
+std::string ToString(const T& serializable) {
+ std::ostringstream os;
+
+ os << serializable;
+ return os.str();
+}
+bool ParsingResultComparer::Compare(const Results& aResA, const Results& aResB,
+ const std::string& aOriginalSdp,
+ const SdpPref::AlternateParseModes& aMode) {
+ MOZ_ASSERT(aResA, "aResA must not be a nullptr");
+ MOZ_ASSERT(aResB, "aResB must not be a nullptr");
+ MOZ_ASSERT(aResA->ParserName() != aResB->ParserName(),
+ "aResA and aResB must be from different parsers");
+ SdpTelemetry::RecordCompare(aResA, aResB, aMode);
+
+ ParsingResultComparer comparer;
+ if (!aResA->Sdp() || !aResB->Sdp()) {
+ return !aResA->Sdp() && !aResB->Sdp();
+ }
+ if (SipccSdpParser::IsNamed(aResA->ParserName())) {
+ MOZ_ASSERT(RsdparsaSdpParser::IsNamed(aResB->ParserName()));
+ return comparer.Compare(*aResB->Sdp(), *aResA->Sdp(), aOriginalSdp,
+ SdpComparisonResult::Equal);
+ }
+ MOZ_ASSERT(SipccSdpParser::IsNamed(aResB->ParserName()));
+ MOZ_ASSERT(RsdparsaSdpParser::IsNamed(aResA->ParserName()));
+ return comparer.Compare(*aResA->Sdp(), *aResB->Sdp(), aOriginalSdp,
+ SdpComparisonResult::Equal);
+}
+
+bool ParsingResultComparer::Compare(const Sdp& rsdparsaSdp, const Sdp& sipccSdp,
+ const std::string& originalSdp,
+ const SdpComparisonResult expect) {
+ mOriginalSdp = originalSdp;
+ const std::string sipccSdpStr = sipccSdp.ToString();
+ const std::string rsdparsaSdpStr = rsdparsaSdp.ToString();
+
+ bool result = rsdparsaSdpStr == sipccSdpStr;
+ LOG_EXPECT(result, expect, ("The original sdp: \n%s", mOriginalSdp.c_str()));
+ if (result) {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ u"serialization_is_equal"_ns, 1);
+ LOG_EXPECT(result, expect, ("Serialization is equal"));
+ return result;
+ }
+ // Do a deep comparison
+ result = true;
+
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ u"serialization_is_not_equal"_ns, 1);
+ LOG_EXPECT(result, expect,
+ ("Serialization is not equal\n"
+ " --- Sipcc SDP ---\n"
+ "%s\n"
+ "--- Rsdparsa SDP ---\n"
+ "%s\n",
+ sipccSdpStr.c_str(), rsdparsaSdpStr.c_str()));
+
+ const std::string rsdparsaOriginStr = ToString(rsdparsaSdp.GetOrigin());
+ const std::string sipccOriginStr = ToString(sipccSdp.GetOrigin());
+
+ // Compare the session level
+ if (rsdparsaOriginStr != sipccOriginStr) {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, u"o="_ns,
+ 1);
+ result = false;
+ LOG_EXPECT(result, expect,
+ ("origin is not equal\nrust origin: %s\nsipcc origin: %s",
+ rsdparsaOriginStr.c_str(), sipccOriginStr.c_str()));
+ }
+
+ if (MOZ_LOG_TEST(sSdpDiffLogger, LogLevel::Debug)) {
+ const auto rust_sess_attr_count = rsdparsaSdp.GetAttributeList().Count();
+ const auto sipcc_sess_attr_count = sipccSdp.GetAttributeList().Count();
+
+ if (rust_sess_attr_count != sipcc_sess_attr_count) {
+ LOG_EXPECT(false, expect,
+ ("Session level attribute count is NOT equal, rsdparsa: %u, "
+ "sipcc: %u\n",
+ rust_sess_attr_count, sipcc_sess_attr_count));
+ }
+ }
+
+ result &= CompareAttrLists(rsdparsaSdp.GetAttributeList(),
+ sipccSdp.GetAttributeList(), -1);
+
+ const uint32_t sipccMediaSecCount =
+ static_cast<uint32_t>(sipccSdp.GetMediaSectionCount());
+ const uint32_t rsdparsaMediaSecCount =
+ static_cast<uint32_t>(rsdparsaSdp.GetMediaSectionCount());
+
+ if (sipccMediaSecCount != rsdparsaMediaSecCount) {
+ result = false;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ u"inequal_msec_count"_ns, 1);
+ LOG_EXPECT(result, expect,
+ ("Media section count is NOT equal, rsdparsa: %d, sipcc: %d \n",
+ rsdparsaMediaSecCount, sipccMediaSecCount));
+ }
+
+ for (size_t i = 0; i < std::min(sipccMediaSecCount, rsdparsaMediaSecCount);
+ i++) {
+ result &= CompareMediaSections(rsdparsaSdp.GetMediaSection(i),
+ sipccSdp.GetMediaSection(i));
+ }
+
+ return result;
+}
+
+bool ParsingResultComparer::CompareMediaSections(
+ const SdpMediaSection& rustMediaSection,
+ const SdpMediaSection& sipccMediaSection,
+ const SdpComparisonResult expect) const {
+ bool result = true;
+ auto trackMediaLineMismatch = [&result, &expect](
+ auto rustValue, auto sipccValue,
+ const nsString& valueDescription) {
+ result = false;
+ nsString typeStr = u"m="_ns;
+ typeStr += valueDescription;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, typeStr,
+ 1);
+ LOG_EXPECT(result, expect,
+ ("The media line values %s are not equal\n"
+ "rsdparsa value: %s\n"
+ "sipcc value: %s\n",
+ NS_LossyConvertUTF16toASCII(valueDescription).get(),
+ ToString(rustValue).c_str(), ToString(sipccValue).c_str()));
+ };
+
+ auto compareMediaLineValue = [trackMediaLineMismatch](
+ auto rustValue, auto sipccValue,
+ const nsString& valueDescription) {
+ if (rustValue != sipccValue) {
+ trackMediaLineMismatch(rustValue, sipccValue, valueDescription);
+ }
+ };
+
+ auto compareSimpleMediaLineValue =
+ [&rustMediaSection, &sipccMediaSection, compareMediaLineValue](
+ auto valGetFuncPtr, const nsString& valueDescription) {
+ compareMediaLineValue((rustMediaSection.*valGetFuncPtr)(),
+ (sipccMediaSection.*valGetFuncPtr)(),
+ valueDescription);
+ };
+
+ compareSimpleMediaLineValue(&SdpMediaSection::GetMediaType, u"media_type"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetPort, u"port"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetPortCount, u"port_count"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetProtocol, u"protocol"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::IsReceiving,
+ u"is_receiving"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::IsSending, u"is_sending"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetDirection, u"direction"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetLevel, u"level"_ns);
+
+ compareMediaLineValue(ToString(rustMediaSection.GetConnection()),
+ ToString(sipccMediaSection.GetConnection()),
+ u"connection"_ns);
+
+ result &= CompareAttrLists(rustMediaSection.GetAttributeList(),
+ sipccMediaSection.GetAttributeList(),
+ static_cast<int>(rustMediaSection.GetLevel()));
+ return result;
+}
+
+bool ParsingResultComparer::CompareAttrLists(
+ const SdpAttributeList& rustAttrlist, const SdpAttributeList& sipccAttrlist,
+ int level, const SdpComparisonResult expect) const {
+ bool result = true;
+
+ for (size_t i = AttributeType::kFirstAttribute;
+ i <= static_cast<size_t>(AttributeType::kLastAttribute); i++) {
+ const AttributeType type = static_cast<AttributeType>(i);
+ std::string attrStr;
+ if (type != AttributeType::kDirectionAttribute) {
+ attrStr = "a=" + SdpAttribute::GetAttributeTypeString(type);
+ } else {
+ attrStr = "a=_direction_attribute_";
+ }
+
+ if (sipccAttrlist.HasAttribute(type, false)) {
+ auto sipccAttrStr = ToString(*sipccAttrlist.GetAttribute(type, false));
+
+ if (!rustAttrlist.HasAttribute(type, false)) {
+ result = false;
+ nsString typeStr;
+ typeStr.AssignASCII(attrStr.c_str());
+ typeStr += u"_missing"_ns;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ typeStr, 1);
+ LOG_EXPECT(result, expect,
+ ("Rust is missing the attribute: %s\n", attrStr.c_str()));
+ LOG_EXPECT(result, expect,
+ ("Rust is missing: %s\n", sipccAttrStr.c_str()));
+
+ continue;
+ }
+
+ auto rustAttrStr = ToString(*rustAttrlist.GetAttribute(type, false));
+
+ if (rustAttrStr != sipccAttrStr) {
+ if (type == AttributeType::kFmtpAttribute) {
+ if (rustAttrlist.GetFmtp() == sipccAttrlist.GetFmtp()) {
+ continue;
+ }
+ }
+
+ std::string originalAttrStr = GetAttributeLines(attrStr, level);
+ if (rustAttrStr != originalAttrStr) {
+ result = false;
+ nsString typeStr;
+ typeStr.AssignASCII(attrStr.c_str());
+ typeStr += u"_inequal"_ns;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ typeStr, 1);
+ LOG_EXPECT(result, expect,
+ ("%s is neither equal to sipcc nor to the orginal sdp\n"
+ "--------------rsdparsa attribute---------------\n"
+ "%s"
+ "--------------sipcc attribute---------------\n"
+ "%s"
+ "--------------original attribute---------------\n"
+ "%s\n",
+ attrStr.c_str(), rustAttrStr.c_str(),
+ sipccAttrStr.c_str(), originalAttrStr.c_str()));
+ } else {
+ LOG_EXPECT(
+ result, expect,
+ ("But the rust serialization is equal to the orignal sdp\n"));
+ }
+ }
+ } else {
+ if (rustAttrlist.HasAttribute(type, false)) {
+ nsString typeStr;
+ typeStr.AssignASCII(attrStr.c_str());
+ typeStr += u"_unexpected"_ns;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ typeStr, 1);
+ }
+ }
+ }
+
+ return result;
+}
+
+std::vector<std::string> SplitLines(const std::string& sdp) {
+ std::stringstream ss(sdp);
+ std::string to;
+ std::vector<std::string> lines;
+
+ while (std::getline(ss, to, '\n')) {
+ lines.push_back(to);
+ }
+
+ return lines;
+}
+
+std::string ParsingResultComparer::GetAttributeLines(
+ const std::string& attrType, int level) const {
+ std::vector<std::string> lines = SplitLines(mOriginalSdp);
+ std::string attrToFind = attrType + ":";
+ std::string attrLines;
+ int currentLevel = -1;
+ // Filters rtcp-fb lines that contain "x-..." types
+ // This is because every SDP from Edge contains these rtcp-fb x- types
+ // for example: a=rtcp-fb:121 x-foo
+ std::regex customRtcpFbLines(R"(a\=rtcp\-fb\:(\d+|\*).* x\-.*)");
+
+ for (auto& line : lines) {
+ if (line.find("m=") == 0) {
+ if (level > currentLevel) {
+ attrLines.clear();
+ currentLevel++;
+ } else {
+ break;
+ }
+ } else if (line.find(attrToFind) == 0) {
+ if (std::regex_match(line, customRtcpFbLines)) {
+ continue;
+ }
+
+ attrLines += (line + '\n');
+ }
+ }
+
+ return attrLines;
+}
+
+} // namespace mozilla