298 lines
11 KiB
C++
298 lines
11 KiB
C++
/* -*- 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 <string>
|
|
#include <ostream>
|
|
#include <regex>
|
|
|
|
#include "mozilla/Assertions.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");
|
|
|
|
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) {
|
|
LOG_EXPECT(result, expect, ("Serialization is equal"));
|
|
return result;
|
|
}
|
|
// Do a deep comparison
|
|
result = true;
|
|
|
|
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) {
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|