Implement test class LoopbackMediaTransport

Bug: webrtc:9719
Change-Id: I82aa962d1cb8f2c8f56f766cb12562690e595045
Reviewed-on: https://webrtc-review.googlesource.com/c/105661
Commit-Queue: Niels Moller <nisse@webrtc.org>
Reviewed-by: Peter Slatala <psla@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25196}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 5aee948..0cfa592 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -550,34 +550,6 @@
     ]
   }
 
-  rtc_source_set("rtc_api_unittests") {
-    testonly = true
-
-    sources = [
-      "array_view_unittest.cc",
-      "ortc/mediadescription_unittest.cc",
-      "ortc/sessiondescription_unittest.cc",
-      "rtcerror_unittest.cc",
-      "rtpparameters_unittest.cc",
-    ]
-
-    if (!build_with_chromium && is_clang) {
-      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
-      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
-    }
-
-    deps = [
-      ":array_view",
-      ":libjingle_peerconnection_api",
-      ":ortc_api",
-      "../rtc_base:checks",
-      "../rtc_base:rtc_base_approved",
-      "../rtc_base:rtc_base_tests_utils",
-      "../test:test_support",
-      "units:units_unittests",
-    ]
-  }
-
   rtc_source_set("fake_media_transport") {
     testonly = true
 
@@ -590,4 +562,47 @@
       "../rtc_base:checks",
     ]
   }
+
+  rtc_source_set("loopback_media_transport") {
+    testonly = true
+
+    sources = [
+      "test/loopback_media_transport.h",
+    ]
+
+    deps = [
+      ":libjingle_peerconnection_api",
+      "../rtc_base:checks",
+    ]
+  }
+
+  rtc_source_set("rtc_api_unittests") {
+    testonly = true
+
+    sources = [
+      "array_view_unittest.cc",
+      "ortc/mediadescription_unittest.cc",
+      "ortc/sessiondescription_unittest.cc",
+      "rtcerror_unittest.cc",
+      "rtpparameters_unittest.cc",
+      "test/loopback_media_transport_unittest.cc",
+    ]
+
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
+
+    deps = [
+      ":array_view",
+      ":libjingle_peerconnection_api",
+      ":loopback_media_transport",
+      ":ortc_api",
+      "../rtc_base:checks",
+      "../rtc_base:rtc_base_approved",
+      "../rtc_base:rtc_base_tests_utils",
+      "../test:test_support",
+      "units:units_unittests",
+    ]
+  }
 }
diff --git a/api/test/loopback_media_transport.h b/api/test/loopback_media_transport.h
new file mode 100644
index 0000000..04e14b2
--- /dev/null
+++ b/api/test/loopback_media_transport.h
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_TEST_LOOPBACK_MEDIA_TRANSPORT_H_
+#define API_TEST_LOOPBACK_MEDIA_TRANSPORT_H_
+
+#include <utility>
+
+#include "api/media_transport_interface.h"
+
+namespace webrtc {
+
+// Contains two MediaTransportsInterfaces that are connected to each other.
+// Currently supports audio only.
+class MediaTransportPair {
+ public:
+  MediaTransportPair()
+      : pipe_{LoopbackMediaTransport(&pipe_[1]),
+              LoopbackMediaTransport(&pipe_[0])} {}
+
+  // Ownership stays with MediaTransportPair
+  MediaTransportInterface* first() { return &pipe_[0]; }
+  MediaTransportInterface* second() { return &pipe_[1]; }
+
+ private:
+  class LoopbackMediaTransport : public MediaTransportInterface {
+   public:
+    explicit LoopbackMediaTransport(LoopbackMediaTransport* other)
+        : other_(other) {}
+    ~LoopbackMediaTransport() { RTC_CHECK(sink_ == nullptr); }
+
+    RTCError SendAudioFrame(uint64_t channel_id,
+                            MediaTransportEncodedAudioFrame frame) override {
+      other_->OnData(channel_id, std::move(frame));
+      return RTCError::OK();
+    };
+
+    RTCError SendVideoFrame(
+        uint64_t channel_id,
+        const MediaTransportEncodedVideoFrame& frame) override {
+      return RTCError::OK();
+    }
+
+    RTCError RequestKeyFrame(uint64_t channel_id) override {
+      return RTCError::OK();
+    }
+
+    void SetReceiveAudioSink(MediaTransportAudioSinkInterface* sink) override {
+      if (sink) {
+        RTC_CHECK(sink_ == nullptr);
+      }
+      sink_ = sink;
+    }
+
+    void SetReceiveVideoSink(MediaTransportVideoSinkInterface* sink) override {}
+
+   private:
+    void OnData(uint64_t channel_id, MediaTransportEncodedAudioFrame frame) {
+      if (sink_) {
+        sink_->OnData(channel_id, frame);
+      }
+    }
+
+    MediaTransportAudioSinkInterface* sink_ = nullptr;
+    LoopbackMediaTransport* other_;
+  };
+
+  LoopbackMediaTransport pipe_[2];
+};
+
+}  // namespace webrtc
+
+#endif  // API_TEST_LOOPBACK_MEDIA_TRANSPORT_H_
diff --git a/api/test/loopback_media_transport_unittest.cc b/api/test/loopback_media_transport_unittest.cc
new file mode 100644
index 0000000..bff74b8
--- /dev/null
+++ b/api/test/loopback_media_transport_unittest.cc
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <vector>
+
+#include "api/test/loopback_media_transport.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+namespace {
+
+class MockMediaTransportAudioSinkInterface
+    : public MediaTransportAudioSinkInterface {
+ public:
+  MOCK_METHOD2(OnData, void(uint64_t, MediaTransportEncodedAudioFrame));
+};
+
+// Test only uses the sequence number.
+MediaTransportEncodedAudioFrame CreateAudioFrame(int sequence_number) {
+  static constexpr int kSamplingRateHz = 48000;
+  static constexpr int kStartingSampleIndex = 0;
+  static constexpr int kSamplesPerChannel = 480;
+  static constexpr uint8_t kPayloadType = 17;
+
+  return MediaTransportEncodedAudioFrame(
+      kSamplingRateHz, kStartingSampleIndex, kSamplesPerChannel,
+      sequence_number, MediaTransportEncodedAudioFrame::FrameType::kSpeech,
+      kPayloadType, std::vector<uint8_t>(kSamplesPerChannel));
+}
+
+}  // namespace
+
+TEST(LoopbackMediaTransport, AudioWithNoSinkSilentlyIgnored) {
+  MediaTransportPair transport_pair;
+  transport_pair.first()->SendAudioFrame(1, CreateAudioFrame(0));
+  transport_pair.second()->SendAudioFrame(2, CreateAudioFrame(0));
+}
+
+TEST(LoopbackMediaTransport, AudioDeliveredToSink) {
+  MediaTransportPair transport_pair;
+  testing::StrictMock<MockMediaTransportAudioSinkInterface> sink;
+  EXPECT_CALL(sink,
+              OnData(1, testing::Property(
+                            &MediaTransportEncodedAudioFrame::sequence_number,
+                            testing::Eq(10))));
+  transport_pair.second()->SetReceiveAudioSink(&sink);
+  transport_pair.first()->SendAudioFrame(1, CreateAudioFrame(10));
+
+  transport_pair.second()->SetReceiveAudioSink(nullptr);
+}
+
+}  // namespace webrtc