ParsingResultComparer.cpp (11076B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "sdp/ParsingResultComparer.h" 8 9 #include <ostream> 10 #include <regex> 11 #include <string> 12 13 #include "mozilla/Assertions.h" 14 #include "mozilla/Logging.h" 15 #include "sdp/RsdparsaSdpParser.h" 16 #include "sdp/Sdp.h" 17 #include "sdp/SipccSdpParser.h" 18 19 using mozilla::LogLevel; 20 static mozilla::LazyLogModule sSdpDiffLogger("sdpdiff_logger"); 21 22 #define LOGD(msg) MOZ_LOG(sSdpDiffLogger, LogLevel::Debug, msg) 23 #define LOGE(msg) MOZ_LOG(sSdpDiffLogger, LogLevel::Error, msg) 24 25 #define LOG_EXPECT(result, expect, msg) \ 26 { \ 27 if (((expect) == SdpComparisonResult::Equal) == (result)) { \ 28 LOGD(msg); \ 29 } else { \ 30 LOGE(("UNEXPECTED COMPARISON RESULT: vvvvvv")); \ 31 LOGE(msg); \ 32 } \ 33 } 34 35 namespace mozilla { 36 37 using AttributeType = SdpAttribute::AttributeType; 38 39 template <typename T> 40 std::string ToString(const T& serializable) { 41 std::ostringstream os; 42 43 os << serializable; 44 return os.str(); 45 } 46 bool ParsingResultComparer::Compare(const Results& aResA, const Results& aResB, 47 const std::string& aOriginalSdp, 48 const SdpPref::AlternateParseModes& aMode) { 49 MOZ_ASSERT(aResA, "aResA must not be a nullptr"); 50 MOZ_ASSERT(aResB, "aResB must not be a nullptr"); 51 MOZ_ASSERT(aResA->ParserName() != aResB->ParserName(), 52 "aResA and aResB must be from different parsers"); 53 54 ParsingResultComparer comparer; 55 if (!aResA->Sdp() || !aResB->Sdp()) { 56 return !aResA->Sdp() && !aResB->Sdp(); 57 } 58 if (SipccSdpParser::IsNamed(aResA->ParserName())) { 59 MOZ_ASSERT(RsdparsaSdpParser::IsNamed(aResB->ParserName())); 60 return comparer.Compare(*aResB->Sdp(), *aResA->Sdp(), aOriginalSdp, 61 SdpComparisonResult::Equal); 62 } 63 MOZ_ASSERT(SipccSdpParser::IsNamed(aResB->ParserName())); 64 MOZ_ASSERT(RsdparsaSdpParser::IsNamed(aResA->ParserName())); 65 return comparer.Compare(*aResA->Sdp(), *aResB->Sdp(), aOriginalSdp, 66 SdpComparisonResult::Equal); 67 } 68 69 bool ParsingResultComparer::Compare(const Sdp& rsdparsaSdp, const Sdp& sipccSdp, 70 const std::string& originalSdp, 71 const SdpComparisonResult expect) { 72 mOriginalSdp = originalSdp; 73 const std::string sipccSdpStr = sipccSdp.ToString(); 74 const std::string rsdparsaSdpStr = rsdparsaSdp.ToString(); 75 76 bool result = rsdparsaSdpStr == sipccSdpStr; 77 LOG_EXPECT(result, expect, ("The original sdp: \n%s", mOriginalSdp.c_str())); 78 if (result) { 79 LOG_EXPECT(result, expect, ("Serialization is equal")); 80 return result; 81 } 82 // Do a deep comparison 83 result = true; 84 85 LOG_EXPECT(result, expect, 86 ("Serialization is not equal\n" 87 " --- Sipcc SDP ---\n" 88 "%s\n" 89 "--- Rsdparsa SDP ---\n" 90 "%s\n", 91 sipccSdpStr.c_str(), rsdparsaSdpStr.c_str())); 92 93 const std::string rsdparsaOriginStr = ToString(rsdparsaSdp.GetOrigin()); 94 const std::string sipccOriginStr = ToString(sipccSdp.GetOrigin()); 95 96 // Compare the session level 97 if (rsdparsaOriginStr != sipccOriginStr) { 98 result = false; 99 LOG_EXPECT(result, expect, 100 ("origin is not equal\nrust origin: %s\nsipcc origin: %s", 101 rsdparsaOriginStr.c_str(), sipccOriginStr.c_str())); 102 } 103 104 if (MOZ_LOG_TEST(sSdpDiffLogger, LogLevel::Debug)) { 105 const auto rust_sess_attr_count = rsdparsaSdp.GetAttributeList().Count(); 106 const auto sipcc_sess_attr_count = sipccSdp.GetAttributeList().Count(); 107 108 if (rust_sess_attr_count != sipcc_sess_attr_count) { 109 LOG_EXPECT(false, expect, 110 ("Session level attribute count is NOT equal, rsdparsa: %u, " 111 "sipcc: %u\n", 112 rust_sess_attr_count, sipcc_sess_attr_count)); 113 } 114 } 115 116 result &= CompareAttrLists(rsdparsaSdp.GetAttributeList(), 117 sipccSdp.GetAttributeList(), -1); 118 119 const uint32_t sipccMediaSecCount = 120 static_cast<uint32_t>(sipccSdp.GetMediaSectionCount()); 121 const uint32_t rsdparsaMediaSecCount = 122 static_cast<uint32_t>(rsdparsaSdp.GetMediaSectionCount()); 123 124 if (sipccMediaSecCount != rsdparsaMediaSecCount) { 125 result = false; 126 LOG_EXPECT(result, expect, 127 ("Media section count is NOT equal, rsdparsa: %d, sipcc: %d \n", 128 rsdparsaMediaSecCount, sipccMediaSecCount)); 129 } 130 131 for (size_t i = 0; i < std::min(sipccMediaSecCount, rsdparsaMediaSecCount); 132 i++) { 133 result &= CompareMediaSections(rsdparsaSdp.GetMediaSection(i), 134 sipccSdp.GetMediaSection(i)); 135 } 136 137 return result; 138 } 139 140 bool ParsingResultComparer::CompareMediaSections( 141 const SdpMediaSection& rustMediaSection, 142 const SdpMediaSection& sipccMediaSection, 143 const SdpComparisonResult expect) const { 144 bool result = true; 145 auto trackMediaLineMismatch = [&result, &expect]( 146 auto rustValue, auto sipccValue, 147 const nsString& valueDescription) { 148 result = false; 149 LOG_EXPECT(result, expect, 150 ("The media line values %s are not equal\n" 151 "rsdparsa value: %s\n" 152 "sipcc value: %s\n", 153 NS_LossyConvertUTF16toASCII(valueDescription).get(), 154 ToString(rustValue).c_str(), ToString(sipccValue).c_str())); 155 }; 156 157 auto compareMediaLineValue = [trackMediaLineMismatch]( 158 auto rustValue, auto sipccValue, 159 const nsString& valueDescription) { 160 if (rustValue != sipccValue) { 161 trackMediaLineMismatch(rustValue, sipccValue, valueDescription); 162 } 163 }; 164 165 auto compareSimpleMediaLineValue = 166 [&rustMediaSection, &sipccMediaSection, compareMediaLineValue]( 167 auto valGetFuncPtr, const nsString& valueDescription) { 168 compareMediaLineValue((rustMediaSection.*valGetFuncPtr)(), 169 (sipccMediaSection.*valGetFuncPtr)(), 170 valueDescription); 171 }; 172 173 compareSimpleMediaLineValue(&SdpMediaSection::GetMediaType, u"media_type"_ns); 174 compareSimpleMediaLineValue(&SdpMediaSection::GetPort, u"port"_ns); 175 compareSimpleMediaLineValue(&SdpMediaSection::GetPortCount, u"port_count"_ns); 176 compareSimpleMediaLineValue(&SdpMediaSection::GetProtocol, u"protocol"_ns); 177 compareSimpleMediaLineValue(&SdpMediaSection::IsReceiving, 178 u"is_receiving"_ns); 179 compareSimpleMediaLineValue(&SdpMediaSection::IsSending, u"is_sending"_ns); 180 compareSimpleMediaLineValue(&SdpMediaSection::GetDirection, u"direction"_ns); 181 compareSimpleMediaLineValue(&SdpMediaSection::GetLevel, u"level"_ns); 182 183 compareMediaLineValue(ToString(rustMediaSection.GetConnection()), 184 ToString(sipccMediaSection.GetConnection()), 185 u"connection"_ns); 186 187 result &= CompareAttrLists(rustMediaSection.GetAttributeList(), 188 sipccMediaSection.GetAttributeList(), 189 static_cast<int>(rustMediaSection.GetLevel())); 190 return result; 191 } 192 193 bool ParsingResultComparer::CompareAttrLists( 194 const SdpAttributeList& rustAttrlist, const SdpAttributeList& sipccAttrlist, 195 int level, const SdpComparisonResult expect) const { 196 bool result = true; 197 198 for (size_t i = AttributeType::kFirstAttribute; 199 i <= static_cast<size_t>(AttributeType::kLastAttribute); i++) { 200 const AttributeType type = static_cast<AttributeType>(i); 201 std::string attrStr; 202 if (type != AttributeType::kDirectionAttribute) { 203 attrStr = "a=" + SdpAttribute::GetAttributeTypeString(type); 204 } else { 205 attrStr = "a=_direction_attribute_"; 206 } 207 208 if (sipccAttrlist.HasAttribute(type, false)) { 209 auto sipccAttrStr = ToString(*sipccAttrlist.GetAttribute(type, false)); 210 211 if (!rustAttrlist.HasAttribute(type, false)) { 212 result = false; 213 LOG_EXPECT(result, expect, 214 ("Rust is missing the attribute: %s\n", attrStr.c_str())); 215 LOG_EXPECT(result, expect, 216 ("Rust is missing: %s\n", sipccAttrStr.c_str())); 217 218 continue; 219 } 220 221 auto rustAttrStr = ToString(*rustAttrlist.GetAttribute(type, false)); 222 223 if (rustAttrStr != sipccAttrStr) { 224 if (type == AttributeType::kFmtpAttribute) { 225 if (rustAttrlist.GetFmtp() == sipccAttrlist.GetFmtp()) { 226 continue; 227 } 228 } 229 230 std::string originalAttrStr = GetAttributeLines(attrStr, level); 231 if (rustAttrStr != originalAttrStr) { 232 result = false; 233 LOG_EXPECT(result, expect, 234 ("%s is neither equal to sipcc nor to the orginal sdp\n" 235 "--------------rsdparsa attribute---------------\n" 236 "%s" 237 "--------------sipcc attribute---------------\n" 238 "%s" 239 "--------------original attribute---------------\n" 240 "%s\n", 241 attrStr.c_str(), rustAttrStr.c_str(), 242 sipccAttrStr.c_str(), originalAttrStr.c_str())); 243 } else { 244 LOG_EXPECT( 245 result, expect, 246 ("But the rust serialization is equal to the orignal sdp\n")); 247 } 248 } 249 } 250 } 251 252 return result; 253 } 254 255 std::vector<std::string> SplitLines(const std::string& sdp) { 256 std::stringstream ss(sdp); 257 std::string to; 258 std::vector<std::string> lines; 259 260 while (std::getline(ss, to, '\n')) { 261 lines.push_back(to); 262 } 263 264 return lines; 265 } 266 267 std::string ParsingResultComparer::GetAttributeLines( 268 const std::string& attrType, int level) const { 269 std::vector<std::string> lines = SplitLines(mOriginalSdp); 270 std::string attrToFind = attrType + ":"; 271 std::string attrLines; 272 int currentLevel = -1; 273 // Filters rtcp-fb lines that contain "x-..." types 274 // This is because every SDP from Edge contains these rtcp-fb x- types 275 // for example: a=rtcp-fb:121 x-foo 276 std::regex customRtcpFbLines(R"(a\=rtcp\-fb\:(\d+|\*).* x\-.*)"); 277 278 for (auto& line : lines) { 279 if (line.find("m=") == 0) { 280 if (level > currentLevel) { 281 attrLines.clear(); 282 currentLevel++; 283 } else { 284 break; 285 } 286 } else if (line.find(attrToFind) == 0) { 287 if (std::regex_match(line, customRtcpFbLines)) { 288 continue; 289 } 290 291 attrLines += (line + '\n'); 292 } 293 } 294 295 return attrLines; 296 } 297 298 } // namespace mozilla