Add PeerConnection interop integration tests

These tests verify the behavior between Plan B and
Unified Plan PeerConnections.

Bug: webrtc:7600
Change-Id: Ic41a0e692d32cde6fe7719ada2dbffd4281c008c
Reviewed-on: https://webrtc-review.googlesource.com/43244
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21782}
diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc
index aee5ca1..2768358 100644
--- a/pc/peerconnection_integrationtest.cc
+++ b/pc/peerconnection_integrationtest.cc
@@ -80,9 +80,11 @@
 using webrtc::ObserverInterface;
 using webrtc::PeerConnection;
 using webrtc::PeerConnectionInterface;
+using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
 using webrtc::PeerConnectionFactory;
 using webrtc::PeerConnectionProxy;
 using webrtc::RTCErrorType;
+using webrtc::RtpSenderInterface;
 using webrtc::RtpReceiverInterface;
 using webrtc::SdpSemantics;
 using webrtc::SdpType;
@@ -309,9 +311,13 @@
     AddVideoTrack();
   }
 
-  void AddAudioTrack() { AddTrack(CreateLocalAudioTrack()); }
+  rtc::scoped_refptr<RtpSenderInterface> AddAudioTrack() {
+    return AddTrack(CreateLocalAudioTrack());
+  }
 
-  void AddVideoTrack() { AddTrack(CreateLocalVideoTrack()); }
+  rtc::scoped_refptr<RtpSenderInterface> AddVideoTrack() {
+    return AddTrack(CreateLocalVideoTrack());
+  }
 
   rtc::scoped_refptr<webrtc::AudioTrackInterface> CreateLocalAudioTrack() {
     FakeConstraints constraints;
@@ -340,10 +346,23 @@
     return CreateLocalVideoTrackInternal(FakeConstraints(), rotation);
   }
 
-  void AddTrack(rtc::scoped_refptr<MediaStreamTrackInterface> track,
-                const std::vector<std::string>& stream_labels = {}) {
+  rtc::scoped_refptr<RtpSenderInterface> AddTrack(
+      rtc::scoped_refptr<MediaStreamTrackInterface> track,
+      const std::vector<std::string>& stream_labels = {}) {
     auto result = pc()->AddTrack(track, stream_labels);
     EXPECT_EQ(RTCErrorType::NONE, result.error().type());
+    return result.MoveValue();
+  }
+
+  std::vector<rtc::scoped_refptr<RtpReceiverInterface>> GetReceiversOfType(
+      cricket::MediaType media_type) {
+    std::vector<rtc::scoped_refptr<RtpReceiverInterface>> receivers;
+    for (auto receiver : pc()->GetReceivers()) {
+      if (receiver->media_type() == media_type) {
+        receivers.push_back(receiver);
+      }
+    }
+    return receivers;
   }
 
   bool SignalingStateStable() {
@@ -3662,6 +3681,208 @@
       kMaxWaitForFramesMs);
 }
 
+// Tests that verify interoperability between Plan B and Unified Plan
+// PeerConnections.
+class PeerConnectionIntegrationInteropTest
+    : public PeerConnectionIntegrationTest,
+      public ::testing::WithParamInterface<
+          std::tuple<SdpSemantics, SdpSemantics>> {
+ protected:
+  PeerConnectionIntegrationInteropTest()
+      : caller_semantics_(std::get<0>(GetParam())),
+        callee_semantics_(std::get<1>(GetParam())) {}
+
+  bool CreatePeerConnectionWrappersWithSemantics() {
+    RTCConfiguration caller_config;
+    caller_config.sdp_semantics = caller_semantics_;
+    RTCConfiguration callee_config;
+    callee_config.sdp_semantics = callee_semantics_;
+    return CreatePeerConnectionWrappersWithConfig(caller_config, callee_config);
+  }
+
+  const SdpSemantics caller_semantics_;
+  const SdpSemantics callee_semantics_;
+};
+
+TEST_P(PeerConnectionIntegrationInteropTest, NoMediaLocalToNoMediaRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+}
+
+TEST_P(PeerConnectionIntegrationInteropTest, OneAudioLocalToNoMediaRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+  auto audio_sender = caller()->AddAudioTrack();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  // Verify that one audio receiver has been created on the remote and that it
+  // has the same track ID as the sending track.
+  auto receivers = callee()->pc()->GetReceivers();
+  ASSERT_EQ(1u, receivers.size());
+  EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, receivers[0]->media_type());
+  EXPECT_EQ(receivers[0]->track()->id(), audio_sender->track()->id());
+
+  // Expect to receive only audio frames on the callee.
+  ExpectNewFramesReceivedWithWait(0, 0, kDefaultExpectedAudioFrameCount, 0,
+                                  kMaxWaitForFramesMs);
+}
+
+TEST_P(PeerConnectionIntegrationInteropTest, OneAudioOneVideoToNoMediaRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+  auto video_sender = caller()->AddVideoTrack();
+  auto audio_sender = caller()->AddAudioTrack();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  // Verify that one audio and one video receiver have been created on the
+  // remote and that they have the same track IDs as the sending tracks.
+  auto audio_receivers =
+      callee()->GetReceiversOfType(cricket::MEDIA_TYPE_AUDIO);
+  ASSERT_EQ(1u, audio_receivers.size());
+  EXPECT_EQ(audio_receivers[0]->track()->id(), audio_sender->track()->id());
+  auto video_receivers =
+      callee()->GetReceiversOfType(cricket::MEDIA_TYPE_VIDEO);
+  ASSERT_EQ(1u, video_receivers.size());
+  EXPECT_EQ(video_receivers[0]->track()->id(), video_sender->track()->id());
+
+  // Expect to receive audio and video frames only on the callee.
+  ExpectNewFramesReceivedWithWait(0, 0, kDefaultExpectedAudioFrameCount,
+                                  kDefaultExpectedVideoFrameCount,
+                                  kMaxWaitForFramesMs);
+}
+
+TEST_P(PeerConnectionIntegrationInteropTest,
+       OneAudioOneVideoLocalToOneAudioOneVideoRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+  caller()->AddAudioVideoTracks();
+  callee()->AddAudioVideoTracks();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  ExpectNewFramesReceivedWithWait(
+      kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount,
+      kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount,
+      kMaxWaitForFramesMs);
+}
+
+TEST_P(PeerConnectionIntegrationInteropTest,
+       ReverseRolesOneAudioLocalToOneVideoRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+  caller()->AddAudioTrack();
+  callee()->AddVideoTrack();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  // Verify that only the audio track has been negotiated.
+  EXPECT_EQ(0u, caller()->GetReceiversOfType(cricket::MEDIA_TYPE_VIDEO).size());
+  // Might also check that the callee's NegotiationNeeded flag is set.
+
+  // Reverse roles.
+  callee()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  // Expect to receive audio frames on the callee and video frames on the
+  // caller.
+  ExpectNewFramesReceivedWithWait(0, kDefaultExpectedVideoFrameCount,
+                                  kDefaultExpectedAudioFrameCount, 0,
+                                  kMaxWaitForFramesMs);
+}
+
+// Test that if one side offers two video tracks then the other side will only
+// see the first one and ignore the second.
+TEST_P(PeerConnectionIntegrationInteropTest, TwoVideoLocalToNoMediaRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+  auto first_sender = caller()->AddVideoTrack();
+  caller()->AddVideoTrack();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  // Verify that there is only one receiver and it corresponds to the first
+  // added track.
+  auto receivers = callee()->pc()->GetReceivers();
+  ASSERT_EQ(1u, receivers.size());
+  EXPECT_TRUE(receivers[0]->track()->enabled());
+  EXPECT_EQ(first_sender->track()->id(), receivers[0]->track()->id());
+
+  // Expect to receive video frames from the one track.
+  ExpectNewFramesReceivedWithWait(0, 0,
+                                  0, kDefaultExpectedVideoFrameCount,
+                                  kMaxWaitForFramesMs);
+}
+
+// Test that in the multi-track case each endpoint only looks at the first track
+// and ignores the second one.
+TEST_P(PeerConnectionIntegrationInteropTest, TwoVideoLocalToTwoVideoRemote) {
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithSemantics());
+  ConnectFakeSignaling();
+  caller()->AddVideoTrack();
+  caller()->AddVideoTrack();
+  callee()->AddVideoTrack();
+  callee()->AddVideoTrack();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+  PeerConnectionWrapper* plan_b =
+      (caller_semantics_ == SdpSemantics::kPlanB ? caller() : callee());
+  PeerConnectionWrapper* unified_plan =
+      (caller_semantics_ == SdpSemantics::kUnifiedPlan ? caller() : callee());
+
+  // Should have two senders each, one for each track.
+  EXPECT_EQ(2u, plan_b->pc()->GetSenders().size());
+  EXPECT_EQ(2u, unified_plan->pc()->GetSenders().size());
+
+  // Plan B will have one receiver since it only looks at the first video
+  // section. The receiver should have the same track ID as the sender's first
+  // track.
+  ASSERT_EQ(1u, plan_b->pc()->GetReceivers().size());
+  EXPECT_EQ(unified_plan->pc()->GetSenders()[0]->track()->id(),
+            plan_b->pc()->GetReceivers()[0]->track()->id());
+
+  // Unified Plan will have two receivers since they were created with the
+  // transceivers when the tracks were added.
+  ASSERT_EQ(2u, unified_plan->pc()->GetReceivers().size());
+
+  if (unified_plan == caller()) {
+    // If the Unified Plan endpoint was the caller, then the Plan B endpoint
+    // would have rejected the second video media section so we would expect the
+    // transceiver to be stopped.
+    EXPECT_FALSE(unified_plan->pc()->GetTransceivers()[0]->stopped());
+    EXPECT_TRUE(unified_plan->pc()->GetTransceivers()[1]->stopped());
+  } else {
+    // If the Unified Plan endpoint was the callee, then the Plan B endpoint
+    // would have offered only one video section so we would expect the first
+    // transceiver to map to the first track and the second transceiver to be
+    // missing a mid.
+    EXPECT_TRUE(unified_plan->pc()->GetTransceivers()[0]->mid());
+    EXPECT_FALSE(unified_plan->pc()->GetTransceivers()[1]->mid());
+  }
+
+  // Should be exchanging video frames for the first tracks on each endpoint.
+  ExpectNewFramesReceivedWithWait(0, kDefaultExpectedVideoFrameCount, 0,
+                                  kDefaultExpectedVideoFrameCount,
+                                  kMaxWaitForFramesMs);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    PeerConnectionIntegrationTest,
+    PeerConnectionIntegrationInteropTest,
+    Values(std::make_tuple(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
+           std::make_tuple(SdpSemantics::kUnifiedPlan, SdpSemantics::kPlanB)));
+
 }  // namespace
 
 #endif  // if !defined(THREAD_SANITIZER)