Bug 1363667 - P4 - RTP Source Observer unit tests draft
authorNico Grunbaum
Tue, 14 Nov 2017 10:26:04 -0800
changeset 706489 c8f3d9fa1a565739b703fcb018ebadd399f8ffa0
parent 706488 1b855ef09588c73864adfaaf14427a8f4dfb031a
child 706490 2df6552617a2b4bc94516c758ea7e8829db837a1
push id91808
push userna-g@nostrum.com
push dateSat, 02 Dec 2017 00:48:28 +0000
bugs1363667
milestone59.0a1
Bug 1363667 - P4 - RTP Source Observer unit tests MozReview-Commit-ID: DuFqdReT7JD
media/webrtc/signaling/gtest/moz.build
media/webrtc/signaling/gtest/rtpsources_unittests.cpp
--- a/media/webrtc/signaling/gtest/moz.build
+++ b/media/webrtc/signaling/gtest/moz.build
@@ -22,16 +22,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'uiki
       '/media/webrtc/trunk',
       '/netwerk/srtp/src/include',
     ]
 
     SOURCES += [
         'jsep_session_unittest.cpp',
         'jsep_track_unittest.cpp',
         'mediaconduit_unittests.cpp',
+        'rtpsources_unittests.cpp',
         'sdp_unittests.cpp',
         'videoconduit_unittests.cpp',
     ]
 
     # See Bug 1372950, mediapipeline tests seem to cause crashes on Windows
     if CONFIG['OS_TARGET'] != 'WINNT':
         SOURCES += [
             'mediapipeline_unittest.cpp',
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/gtest/rtpsources_unittests.cpp
@@ -0,0 +1,352 @@
+#include <RtpSourceObserver.h>
+#include "webrtc/modules/include/module_common_types.h"
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+namespace test {
+class RtpSourcesTest : public ::testing::Test
+{
+using RtpSourceHistory = RtpSourceObserver::RtpSourceHistory;
+using RtpSourceEntry = mozilla::RtpSourceObserver::RtpSourceEntry;
+public:
+
+  // History Tests
+
+  // Test init happens as expected
+  void TestInitState() {
+    RtpSourceHistory history;
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
+    const auto& e = history.mLatestEviction;
+    EXPECT_FALSE(history.mHasEvictedEntry);
+    EXPECT_EQ(e.jitterAdjustedTimestamp, 0);
+    EXPECT_FALSE(e.hasAudioLevel);
+    EXPECT_EQ(e.audioLevel, 0);
+  }
+
+  // Test adding into the jitter window
+  void TestInsertIntoJitterWindow() {
+    const bool hasAudioLevel = true;
+    const uint8_t audioLevel = 10;
+    const int64_t jitter = 10;
+    RtpSourceHistory history;
+    const int64_t times[] = {100,
+                             120,
+                             140,
+                             160,
+                             180,
+                             200,
+                             220};
+    const size_t numEntries = sizeof(times) / sizeof(times[0]);
+    for (auto i : times) {
+      history.Insert(i, i + jitter, hasAudioLevel, audioLevel);
+     }
+     ASSERT_EQ(history.mDetailedHistory.size(), numEntries);
+     for (auto i : times) {
+       auto entry = history.FindClosestNotAfter(i + jitter);
+       ASSERT_NE(entry, nullptr);
+       if (entry) {
+         EXPECT_EQ(entry->jitterAdjustedTimestamp, i + jitter);
+         EXPECT_EQ(entry->hasAudioLevel, hasAudioLevel);
+         EXPECT_EQ(entry->audioLevel, audioLevel);
+       }
+     }
+  }
+
+  // Tests inserting before the jitter window, in long term history.
+  void TestInsertIntoLongTerm() {
+    RtpSourceHistory history;
+
+    constexpr int64_t timeNow = 100000;
+    // Should be discarded as too old
+    constexpr int64_t time0 = timeNow - (10 * 1000) - 1;
+    // Shold be commited
+    constexpr int64_t time1 = timeNow - (10 * 1000);
+    // Should be commited because it is newer
+    constexpr int64_t time2 = timeNow - (5 * 1000);
+    // Should be discarded because it is older
+    constexpr int64_t time3 = timeNow - (7 * 1000);
+    // Prune time0 should not prune time2
+    constexpr int64_t pruneTime0 = time2 + (10 * 1000);
+    // Prune time1 should prune time2
+    constexpr int64_t pruneTime1 = time2 + (10 * 1000) + 1;
+
+    // time0
+    history.Insert(timeNow, time0, true, 0);
+    EXPECT_TRUE(history.Empty());
+    EXPECT_FALSE(history.mHasEvictedEntry);
+
+    // time1
+    history.Insert(timeNow, time1, true, 0);
+    // Check that the jitter window buffer hasn't been used
+    EXPECT_TRUE(history.Empty());
+    ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time1);
+    EXPECT_TRUE(history.mHasEvictedEntry);
+
+    // time2
+    history.Insert(timeNow, time2, true, 0);
+    EXPECT_TRUE(history.Empty());
+    ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time2);
+    EXPECT_TRUE(history.mHasEvictedEntry);
+
+    // time3
+    history.Insert(timeNow, time3, true, 0);
+    EXPECT_TRUE(history.Empty());
+    ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time2);
+    EXPECT_TRUE(history.mHasEvictedEntry);
+
+    // pruneTime0
+    history.Prune(pruneTime0);
+    EXPECT_TRUE(history.Empty());
+    ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time2);
+    EXPECT_TRUE(history.mHasEvictedEntry);
+
+    // pruneTime1
+    history.Prune(pruneTime1);
+    EXPECT_TRUE(history.Empty());
+    EXPECT_FALSE(history.mHasEvictedEntry);
+  }
+
+  // Tests that a value inserted into the jitter window will age into long term
+  // storage
+  void TestAgeIntoLongTerm() {
+    RtpSourceHistory history;
+    constexpr int64_t jitterWindow = history.kMinJitterWindow;
+    constexpr int64_t jitter = jitterWindow / 2;
+    constexpr int64_t timeNow0 = 100000;
+    constexpr int64_t time0 = timeNow0;
+    constexpr int64_t time1 = timeNow0 + jitter;
+    // Prune at timeNow1 should evict time0
+    constexpr int64_t timeNow1 = time0 + jitterWindow + 1;
+    // Prune at timeNow2 should evict time1
+    constexpr int64_t timeNow2 = time1 + jitterWindow + 1;
+
+    // time0
+    history.Insert(timeNow0, time0, false, 1);
+    EXPECT_FALSE(history.Empty());
+    // Jitter window should not have grown
+    ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
+    EXPECT_FALSE(history.mHasEvictedEntry);
+
+    // time1
+    history.Insert(timeNow0, time1, true, 2);
+    ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(2));
+    EXPECT_FALSE(history.mHasEvictedEntry);
+
+    // Prune at timeNow1
+    history.Prune(timeNow1);
+    ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
+    EXPECT_TRUE(history.mHasEvictedEntry);
+    ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time0);
+    ASSERT_EQ(history.mLatestEviction.hasAudioLevel, false);
+    ASSERT_EQ(history.mLatestEviction.audioLevel, 1);
+
+    // Prune at timeNow1
+    history.Prune(timeNow2);
+    EXPECT_EQ(history.mMaxJitterWindow, jitterWindow);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
+    EXPECT_TRUE(history.mHasEvictedEntry);
+    EXPECT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time1);
+    EXPECT_EQ(history.mLatestEviction.hasAudioLevel, true);
+    EXPECT_EQ(history.mLatestEviction.audioLevel, 2);
+
+  }
+
+  // Packets have a maximum audio level of 127
+  void TestMaximumAudioLevel() {
+    RtpSourceHistory history;
+    constexpr int64_t timeNow = 0;
+    const int64_t jitterAdjusted = timeNow + 10;
+    const bool hasAudioLevel = true;
+    const uint8_t audioLevel0 = 127;
+    // should result in in hasAudioLevel = false
+    const uint8_t audioLevel1 = 255;
+    // should result in in hasAudioLevel = false
+    const uint8_t audioLevel2 = 128;
+
+    // audio level 0
+    history.Insert(timeNow, jitterAdjusted, hasAudioLevel, audioLevel0);
+    ASSERT_FALSE(history.mHasEvictedEntry);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
+    {
+      auto* entry = history.FindClosestNotAfter(jitterAdjusted);
+      ASSERT_NE(entry, nullptr);
+      EXPECT_TRUE(entry->hasAudioLevel);
+      EXPECT_EQ(entry->audioLevel, audioLevel0);
+    }
+    // audio level 1
+    history.Insert(timeNow, jitterAdjusted, hasAudioLevel, audioLevel1);
+    ASSERT_FALSE(history.mHasEvictedEntry);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
+    {
+      auto* entry = history.FindClosestNotAfter(jitterAdjusted);
+      ASSERT_NE(entry, nullptr);
+      EXPECT_FALSE(entry->hasAudioLevel);
+      EXPECT_EQ(entry->audioLevel, audioLevel1);
+    }
+    // audio level 2
+    history.Insert(timeNow, jitterAdjusted, hasAudioLevel, audioLevel2);
+    ASSERT_FALSE(history.mHasEvictedEntry);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
+    {
+      auto* entry = history.FindClosestNotAfter(jitterAdjusted);
+      ASSERT_NE(entry, nullptr);
+      EXPECT_FALSE(entry->hasAudioLevel);
+      EXPECT_EQ(entry->audioLevel, audioLevel2);
+    }
+  }
+
+  void TestEmptyPrune() {
+    // Empty Prune
+    RtpSourceHistory history;
+    const int64_t timeNow = 0;
+    history.Prune(timeNow);
+    EXPECT_TRUE(history.Empty());
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
+    EXPECT_FALSE(history.mHasEvictedEntry);
+  }
+
+  void TestSinglePrune() {
+    RtpSourceHistory history;
+    constexpr int64_t timeNow = 10000;
+    constexpr int64_t jitter = history.kMinJitterWindow / 2;
+    const int64_t jitterAdjusted = timeNow + jitter;
+
+    history.Insert(timeNow, jitterAdjusted, 0, false);
+    history.Prune(timeNow + (jitter * 3) + 1);
+    EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
+    EXPECT_TRUE(history.mHasEvictedEntry);
+    EXPECT_EQ(jitterAdjusted, history.mLatestEviction.jitterAdjustedTimestamp);
+  }
+
+  // Observer tests that keys are properly handled
+  void TestKeyManipulation() {
+    constexpr unsigned int ssrc = 4682;
+    constexpr unsigned int csrc = 4682;
+    auto ssrcKey = RtpSourceObserver::GetKey(
+        ssrc, dom::RTCRtpSourceEntryType::Synchronization);
+    auto csrcKey = RtpSourceObserver::GetKey(
+        csrc, dom::RTCRtpSourceEntryType::Contributing);
+    // SSRC and CSRC keys are not the same
+    EXPECT_NE(ssrcKey, csrcKey);
+    // Keys can be reversed back to source
+    EXPECT_EQ(RtpSourceObserver::GetSourceFromKey(ssrcKey), ssrc);
+    EXPECT_EQ(RtpSourceObserver::GetSourceFromKey(csrcKey), csrc);
+    // Keys can be reversed back to type
+    EXPECT_EQ(RtpSourceObserver::GetTypeFromKey(ssrcKey),
+              dom::RTCRtpSourceEntryType::Synchronization);
+    EXPECT_EQ(RtpSourceObserver::GetTypeFromKey(csrcKey),
+              dom::RTCRtpSourceEntryType::Contributing);
+  }
+
+  // Observer a header with a single Csrc
+  void TestObserveOneCsrc() {
+    RtpSourceObserver observer;
+    webrtc::WebRtcRTPHeader header;
+    constexpr unsigned int ssrc = 857265;
+    constexpr unsigned int csrc = 3268365;
+    constexpr int64_t timestamp = 10000;
+    constexpr int64_t jitter = 0;
+
+    header.header.ssrc = ssrc;
+    header.header.numCSRCs = 1;
+    header.header.arrOfCSRCs[0] = csrc;
+    observer.OnRtpPacket(&header, timestamp, jitter);
+
+    // One for the SSRC, one for the CSRC
+    EXPECT_EQ(observer.mRtpSources.size(), static_cast<size_t>(2));
+    nsTArray<dom::RTCRtpSourceEntry> outLevels;
+    observer.GetRtpSources(timestamp, outLevels);
+    EXPECT_EQ(outLevels.Length(), static_cast<size_t>(2));
+    bool ssrcFound = false;
+    bool csrcFound = true;
+    for (auto& entry : outLevels) {
+      if (entry.mSource.Value() == ssrc) {
+        ssrcFound = true;
+        EXPECT_EQ(entry.mSourceType.Value(),
+                  dom::RTCRtpSourceEntryType::Synchronization);
+      }
+      if (entry.mSource.Value() == csrc) {
+        csrcFound = true;
+        EXPECT_EQ(entry.mSourceType.Value(),
+                  dom::RTCRtpSourceEntryType::Contributing);
+      }
+    }
+    EXPECT_TRUE(ssrcFound);
+    EXPECT_TRUE(csrcFound);
+  }
+
+  // Observer a header with two CSRCs
+  void TestObserveTwoCsrcs() {
+    RtpSourceObserver observer;
+    webrtc::WebRtcRTPHeader header;
+    constexpr unsigned int ssrc = 239485;
+    constexpr unsigned int csrc0 = 3425;
+    constexpr unsigned int csrc1 = 36457;
+    constexpr int64_t timestamp = 10000;
+    constexpr int64_t jitter = 0;
+
+    header.header.ssrc = ssrc;
+    header.header.numCSRCs = 2;
+    header.header.arrOfCSRCs[0] = csrc0;
+    header.header.arrOfCSRCs[1] = csrc1;
+    observer.OnRtpPacket(&header, timestamp, jitter);
+
+    // One for the SSRC, two for the CSRCs
+    EXPECT_EQ(observer.mRtpSources.size(), static_cast<size_t>(3));
+    nsTArray<dom::RTCRtpSourceEntry> outLevels;
+    observer.GetRtpSources(timestamp, outLevels);
+    EXPECT_EQ(outLevels.Length(), static_cast<size_t>(3));
+    bool ssrcFound = false;
+    bool csrc0Found = true;
+    bool csrc1Found = true;
+    for (auto& entry : outLevels) {
+      if (entry.mSource.Value() == ssrc) {
+        ssrcFound = true;
+        EXPECT_EQ(entry.mSourceType.Value(),
+                  dom::RTCRtpSourceEntryType::Synchronization);
+      }
+      if (entry.mSource.Value() == csrc0) {
+        csrc0Found = true;
+        EXPECT_EQ(entry.mSourceType.Value(),
+                  dom::RTCRtpSourceEntryType::Contributing);
+      }
+      if (entry.mSource.Value() == csrc1) {
+        csrc1Found = true;
+        EXPECT_EQ(entry.mSourceType.Value(),
+                  dom::RTCRtpSourceEntryType::Contributing);
+      }
+    }
+    EXPECT_TRUE(ssrcFound);
+    EXPECT_TRUE(csrc0Found);
+    EXPECT_TRUE(csrc1Found);
+  }
+
+  // Observer a header with a CSRC with audio level extension
+  void TestObserveCsrcWithAudioLevel() {
+    RtpSourceObserver observer;
+    webrtc::WebRtcRTPHeader header;
+  }
+
+};
+
+TEST_F(RtpSourcesTest, TestInitState) { TestInitState(); }
+TEST_F(RtpSourcesTest, TestInsertIntoJitterWindow) {
+  TestInsertIntoJitterWindow();
+}
+TEST_F(RtpSourcesTest, TestAgeIntoLongTerm){ TestAgeIntoLongTerm(); }
+TEST_F(RtpSourcesTest, TestInsertIntoLongTerm) { TestInsertIntoLongTerm(); }
+TEST_F(RtpSourcesTest, TestMaximumAudioLevel) { TestMaximumAudioLevel(); }
+TEST_F(RtpSourcesTest, TestEmptyPrune) { TestEmptyPrune(); }
+TEST_F(RtpSourcesTest, TestSinglePrune) { TestSinglePrune(); }
+TEST_F(RtpSourcesTest, TestKeyManipulation) { TestKeyManipulation(); }
+TEST_F(RtpSourcesTest, TestObserveOneCsrc) { TestObserveOneCsrc(); }
+TEST_F(RtpSourcesTest, TestObserveTwoCsrcs) { TestObserveTwoCsrcs(); }
+TEST_F(RtpSourcesTest, TestObserveCsrcWithAudioLevel) {
+  TestObserveCsrcWithAudioLevel();
+}
+}
\ No newline at end of file