diff options
Diffstat (limited to '')
-rw-r--r-- | dom/media/webrtc/sdp/ParsingResultComparer.cpp | 331 |
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 |