Extend testing of prAnswer

- Modify munger to take (mutable)
  std::unique_ptr<SessionDescriptionInterface> rather than
  cricket::SessionDescription (that latter is embedded in the former)

- For all pranswer test cases, do a final SetRemoteDescription(kAnswer) and
check that signaling_state == stable

Add new test cases:
1) A test case that only applies it as prAnswer on caller (callee is stable)
2) A test case that "scramble" sdb between prAnswer and Anser.

Bug: None
Change-Id: Ifedd92ade01ae781a2e59d0569133c486c7093fe
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/360781
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42891}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 429c4de..fd94772 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -2440,6 +2440,7 @@
       "../rtc_base:network",
       "../rtc_base:network_constants",
       "../rtc_base:null_socket_server",
+      "../rtc_base:random",
       "../rtc_base:refcount",
       "../rtc_base:rtc_base_tests_utils",
       "../rtc_base:rtc_certificate_generator",
diff --git a/pc/data_channel_integrationtest.cc b/pc/data_channel_integrationtest.cc
index 4489618..1a23a17 100644
--- a/pc/data_channel_integrationtest.cc
+++ b/pc/data_channel_integrationtest.cc
@@ -111,8 +111,8 @@
       : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {}
 };
 
-void MakeActiveSctpOffer(cricket::SessionDescription* desc) {
-  auto& transport_infos = desc->transport_infos();
+void MakeActiveSctpOffer(std::unique_ptr<SessionDescriptionInterface>& desc) {
+  auto& transport_infos = desc->description()->transport_infos();
   for (auto& transport_info : transport_infos) {
     transport_info.description.connection_role = cricket::CONNECTIONROLE_ACTIVE;
   }
@@ -770,9 +770,10 @@
   ASSERT_TRUE(ExpectNewFrames(media_expectations));
 }
 
-static void MakeSpecCompliantSctpOffer(cricket::SessionDescription* desc) {
+static void MakeSpecCompliantSctpOffer(
+    std::unique_ptr<SessionDescriptionInterface>& desc) {
   cricket::SctpDataContentDescription* dcd_offer =
-      GetFirstSctpDataContentDescription(desc);
+      GetFirstSctpDataContentDescription(desc->description());
   // See https://crbug.com/webrtc/11211 - this function is a no-op
   ASSERT_TRUE(dcd_offer);
   dcd_offer->set_use_sctpmap(false);
@@ -913,10 +914,11 @@
   ConnectFakeSignaling();
   caller()->CreateDataChannel();
 
-  callee()->SetReceivedSdpMunger([this](cricket::SessionDescription* desc) {
-    MakeActiveSctpOffer(desc);
-    callee()->CreateDataChannel();
-  });
+  callee()->SetReceivedSdpMunger(
+      [this](std::unique_ptr<SessionDescriptionInterface>& desc) {
+        MakeActiveSctpOffer(desc);
+        callee()->CreateDataChannel();
+      });
   caller()->CreateAndSetAndSignalOffer();
   ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
   ASSERT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index 8362c8b..7bf6b6d 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -84,6 +84,7 @@
 #include "rtc_base/firewall_socket_server.h"
 #include "rtc_base/gunit.h"
 #include "rtc_base/logging.h"
+#include "rtc_base/random.h"
 #include "rtc_base/socket_address.h"
 #include "rtc_base/ssl_certificate.h"
 #include "rtc_base/ssl_fingerprint.h"
@@ -605,9 +606,10 @@
   caller()->AddAudioVideoTracks();
   callee()->AddAudioVideoTracks();
   // Remove the bundle group from the SDP received by the callee.
-  callee()->SetReceivedSdpMunger([](cricket::SessionDescription* desc) {
-    desc->RemoveGroupByName("BUNDLE");
-  });
+  callee()->SetReceivedSdpMunger(
+      [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        sdp->description()->RemoveGroupByName("BUNDLE");
+      });
   caller()->CreateAndSetAndSignalOffer();
   ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
   {
@@ -673,11 +675,12 @@
       callee()->CreateLocalVideoTrackWithRotation(kVideoRotation_270));
 
   // Remove the CVO extension from the offered SDP.
-  callee()->SetReceivedSdpMunger([](cricket::SessionDescription* desc) {
-    cricket::VideoContentDescription* video =
-        GetFirstVideoContentDescription(desc);
-    video->ClearRtpHeaderExtensions();
-  });
+  callee()->SetReceivedSdpMunger(
+      [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        cricket::VideoContentDescription* video =
+            GetFirstVideoContentDescription(sdp->description());
+        video->ClearRtpHeaderExtensions();
+      });
   // Wait for video frames to be received by both sides.
   caller()->CreateAndSetAndSignalOffer();
   ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
@@ -845,8 +848,8 @@
   // Renegotiate, rejecting the video m= section.
   if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) {
     caller()->SetGeneratedSdpMunger(
-        [](cricket::SessionDescription* description) {
-          for (cricket::ContentInfo& content : description->contents()) {
+        [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+          for (cricket::ContentInfo& content : sdp->description()->contents()) {
             if (cricket::IsVideoContent(&content)) {
               content.rejected = true;
             }
@@ -1010,10 +1013,11 @@
 }
 
 // Used for the test below.
-void RemoveBundleGroupSsrcsAndMidExtension(cricket::SessionDescription* desc) {
-  RemoveSsrcsAndKeepMsids(desc);
-  desc->RemoveGroupByName("BUNDLE");
-  for (ContentInfo& content : desc->contents()) {
+void RemoveBundleGroupSsrcsAndMidExtension(
+    std::unique_ptr<SessionDescriptionInterface>& sdp) {
+  RemoveSsrcsAndKeepMsids(sdp);
+  sdp->description()->RemoveGroupByName("BUNDLE");
+  for (ContentInfo& content : sdp->description()->contents()) {
     cricket::MediaContentDescription* media = content.media_description();
     cricket::RtpHeaderExtensions extensions = media->rtp_header_extensions();
     extensions.erase(std::remove_if(extensions.begin(), extensions.end(),
@@ -1058,9 +1062,9 @@
 
 // Used for the test below.
 void ModifyPayloadTypesAndRemoveMidExtension(
-    cricket::SessionDescription* desc) {
+    std::unique_ptr<SessionDescriptionInterface>& sdp) {
   int pt = 96;
-  for (ContentInfo& content : desc->contents()) {
+  for (ContentInfo& content : sdp->description()->contents()) {
     cricket::MediaContentDescription* media = content.media_description();
     cricket::RtpHeaderExtensions extensions = media->rtp_header_extensions();
     extensions.erase(std::remove_if(extensions.begin(), extensions.end(),
@@ -1154,9 +1158,10 @@
   ASSERT_TRUE(ExpectNewFrames(media_expectations));
 }
 
-static void MakeSpecCompliantMaxBundleOffer(cricket::SessionDescription* desc) {
+static void MakeSpecCompliantMaxBundleOffer(
+    std::unique_ptr<SessionDescriptionInterface>& sdp) {
   bool first = true;
-  for (cricket::ContentInfo& content : desc->contents()) {
+  for (cricket::ContentInfo& content : sdp->description()->contents()) {
     if (first) {
       first = false;
       continue;
@@ -1164,7 +1169,8 @@
     content.bundle_only = true;
   }
   first = true;
-  for (cricket::TransportInfo& transport : desc->transport_infos()) {
+  for (cricket::TransportInfo& transport :
+       sdp->description()->transport_infos()) {
     if (first) {
       first = false;
       continue;
@@ -2567,9 +2573,10 @@
 
   // Remove all but one audio/video codec (opus and VP8), and change the
   // casing of the caller's generated offer.
-  caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* description) {
+  caller()->SetGeneratedSdpMunger([](std::unique_ptr<
+                                      SessionDescriptionInterface>& sdp) {
     cricket::AudioContentDescription* audio =
-        GetFirstAudioContentDescription(description);
+        GetFirstAudioContentDescription(sdp->description());
     ASSERT_NE(nullptr, audio);
     auto audio_codecs = audio->codecs();
     audio_codecs.erase(std::remove_if(audio_codecs.begin(), audio_codecs.end(),
@@ -2582,7 +2589,7 @@
     audio->set_codecs(audio_codecs);
 
     cricket::VideoContentDescription* video =
-        GetFirstVideoContentDescription(description);
+        GetFirstVideoContentDescription(sdp->description());
     ASSERT_NE(nullptr, video);
     auto video_codecs = video->codecs();
     video_codecs.erase(std::remove_if(video_codecs.begin(), video_codecs.end(),
@@ -3231,9 +3238,9 @@
   ConnectFakeSignaling();
   caller()->AddVideoTrack();
   callee()->AddVideoTrack();
-  auto munger = [](cricket::SessionDescription* desc) {
+  auto munger = [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
     cricket::VideoContentDescription* video =
-        GetFirstVideoContentDescription(desc);
+        GetFirstVideoContentDescription(sdp->description());
     auto codecs = video->codecs();
     for (auto&& codec : codecs) {
       if (codec.name == "H264") {
@@ -3254,17 +3261,18 @@
   caller()->CreateAndSetAndSignalOffer();
   ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
   // Observe that after munging the parameter is present in generated SDP.
-  caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
-    cricket::VideoContentDescription* video =
-        GetFirstVideoContentDescription(desc);
-    for (auto&& codec : video->codecs()) {
-      if (codec.name == "H264") {
-        std::string value;
-        EXPECT_TRUE(
-            codec.GetParam(cricket::kH264FmtpSpsPpsIdrInKeyframe, &value));
-      }
-    }
-  });
+  caller()->SetGeneratedSdpMunger(
+      [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        cricket::VideoContentDescription* video =
+            GetFirstVideoContentDescription(sdp->description());
+        for (auto&& codec : video->codecs()) {
+          if (codec.name == "H264") {
+            std::string value;
+            EXPECT_TRUE(
+                codec.GetParam(cricket::kH264FmtpSpsPpsIdrInKeyframe, &value));
+          }
+        }
+      });
   caller()->CreateOfferAndWait();
 }
 
@@ -3785,24 +3793,25 @@
   auto send_transceiver = audio_transceiver_or_error.MoveValue();
   // Munge the SDP to include NACK and RRTR on Opus, and remove all other
   // codecs.
-  caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
-    for (ContentInfo& content : desc->contents()) {
-      cricket::MediaContentDescription* media = content.media_description();
-      std::vector<cricket::Codec> codecs = media->codecs();
-      std::vector<cricket::Codec> codecs_out;
-      for (cricket::Codec codec : codecs) {
-        if (codec.name == "opus") {
-          codec.AddFeedbackParam(cricket::FeedbackParam(
-              cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
-          codec.AddFeedbackParam(cricket::FeedbackParam(
-              cricket::kRtcpFbParamRrtr, cricket::kParamValueEmpty));
-          codecs_out.push_back(codec);
+  caller()->SetGeneratedSdpMunger(
+      [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        for (ContentInfo& content : sdp->description()->contents()) {
+          cricket::MediaContentDescription* media = content.media_description();
+          std::vector<cricket::Codec> codecs = media->codecs();
+          std::vector<cricket::Codec> codecs_out;
+          for (cricket::Codec codec : codecs) {
+            if (codec.name == "opus") {
+              codec.AddFeedbackParam(cricket::FeedbackParam(
+                  cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
+              codec.AddFeedbackParam(cricket::FeedbackParam(
+                  cricket::kRtcpFbParamRrtr, cricket::kParamValueEmpty));
+              codecs_out.push_back(codec);
+            }
+          }
+          EXPECT_FALSE(codecs_out.empty());
+          media->set_codecs(codecs_out);
         }
-      }
-      EXPECT_FALSE(codecs_out.empty());
-      media->set_codecs(codecs_out);
-    }
-  });
+      });
 
   caller()->CreateAndSetAndSignalOffer();
   // Check for failure in helpers
@@ -3835,22 +3844,23 @@
   auto send_transceiver = video_transceiver_or_error.MoveValue();
   // Munge the SDP to include NACK and RRTR on VP8, and remove all other
   // codecs.
-  caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
-    for (ContentInfo& content : desc->contents()) {
-      cricket::MediaContentDescription* media = content.media_description();
-      std::vector<cricket::Codec> codecs = media->codecs();
-      std::vector<cricket::Codec> codecs_out;
-      for (cricket::Codec codec : codecs) {
-        if (codec.name == "VP8") {
-          ASSERT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam(
-              cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
-          codecs_out.push_back(codec);
+  caller()->SetGeneratedSdpMunger(
+      [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        for (ContentInfo& content : sdp->description()->contents()) {
+          cricket::MediaContentDescription* media = content.media_description();
+          std::vector<cricket::Codec> codecs = media->codecs();
+          std::vector<cricket::Codec> codecs_out;
+          for (cricket::Codec codec : codecs) {
+            if (codec.name == "VP8") {
+              ASSERT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam(
+                  cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
+              codecs_out.push_back(codec);
+            }
+          }
+          EXPECT_FALSE(codecs_out.empty());
+          media->set_codecs(codecs_out);
         }
-      }
-      EXPECT_FALSE(codecs_out.empty());
-      media->set_codecs(codecs_out);
-    }
-  });
+      });
 
   caller()->CreateAndSetAndSignalOffer();
   // Check for failure in helpers
@@ -3877,14 +3887,181 @@
   ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
   ConnectFakeSignaling();
   caller()->pc()->AddTransceiver(caller()->CreateLocalAudioTrack());
-  callee()->SetAnswerWithPrAnswer(true);
+  caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack());
+
+  callee()->SetGeneratedSdpMunger(
+      [](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        SetSdpType(sdp, SdpType::kPrAnswer);
+      });
+  std::unique_ptr<SessionDescriptionInterface> answer;
+  caller()->SetReceivedSdpMunger(
+      [&](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        answer = sdp->Clone();
+      });
   caller()->CreateAndSetAndSignalOffer();
   ASSERT_FALSE(HasFailure());
   EXPECT_EQ(caller()->pc()->signaling_state(),
             PeerConnectionInterface::kHaveRemotePrAnswer);
   EXPECT_EQ(callee()->pc()->signaling_state(),
             PeerConnectionInterface::kHaveLocalPrAnswer);
-  // Note: there should be code here for applying the permanent answer.
+
+  // // Apply the pranswer as a definitive one.
+  SetSdpType(answer, SdpType::kAnswer);
+  EXPECT_TRUE(caller()->SetRemoteDescription(std::move(answer)));
+  EXPECT_EQ(caller()->pc()->signaling_state(),
+            PeerConnectionInterface::kStable);
+}
+
+// Let caller get a prAnswer followed by answer.
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
+       PrAnswerStateTransitionsAsymmetric) {
+  RTCConfiguration config;
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
+  ConnectFakeSignaling();
+  caller()->pc()->AddTransceiver(caller()->CreateLocalAudioTrack());
+  caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack());
+
+  std::unique_ptr<SessionDescriptionInterface> answer;
+  caller()->SetReceivedSdpMunger(
+      [&](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        answer = sdp->Clone();
+        SetSdpType(sdp, SdpType::kPrAnswer);
+      });
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_FALSE(HasFailure());
+  EXPECT_EQ(caller()->pc()->signaling_state(),
+            PeerConnectionInterface::kHaveRemotePrAnswer);
+  EXPECT_EQ(callee()->pc()->signaling_state(),
+            PeerConnectionInterface::kStable);
+
+  // // Apply the pranswer as a definitive one.
+  EXPECT_TRUE(caller()->SetRemoteDescription(std::move(answer)));
+  EXPECT_EQ(caller()->pc()->signaling_state(),
+            PeerConnectionInterface::kStable);
+}
+
+int ReassignPayloadIds(std::unique_ptr<SessionDescriptionInterface>& sdp) {
+  int swaps = 0;
+  for (ContentInfo& content : sdp->description()->contents()) {
+    if (!content.media_description()) {
+      continue;
+    }
+    std::vector<cricket::Codec> codecs = content.media_description()->codecs();
+    int left = 0;
+    int right = codecs.size() - 1;
+    while (left < right) {
+      if (!codecs[left].IsMediaCodec()) {
+        left++;
+        continue;
+      }
+      if (!codecs[right].IsMediaCodec()) {
+        right--;
+        continue;
+      }
+      auto tmp = codecs[left].id;
+      codecs[left].id = codecs[right].id;
+      codecs[right].id = tmp;
+      left++;
+      right--;
+      swaps++;
+    }
+    content.media_description()->set_codecs(codecs);
+  }
+  return swaps;
+}
+
+int SetNewSsrcs(std::unique_ptr<SessionDescriptionInterface>& sdp) {
+  int assignments = 0;
+  std::unordered_set<uint32_t> already_used_ssrcs;
+  for (ContentInfo& content : sdp->description()->contents()) {
+    if (!content.media_description()) {
+      continue;
+    }
+    for (const auto& stream : content.media_description()->streams()) {
+      for (const auto& ssrc : stream.ssrcs) {
+        already_used_ssrcs.insert(ssrc);
+      }
+    }
+  }
+
+  Random random(/* random_seed= */ 77);
+  auto ssrc_generator = [&]() -> uint32_t {
+    do {
+      auto ssrc = random.Rand(1u, 0xFFFFFFF0u);
+      if (already_used_ssrcs.find(ssrc) == already_used_ssrcs.end()) {
+        already_used_ssrcs.insert(ssrc);
+        return ssrc;
+      }
+    } while (true);
+  };
+
+  for (ContentInfo& content : sdp->description()->contents()) {
+    if (!content.media_description()) {
+      continue;
+    }
+    for (auto& stream : content.media_description()->mutable_streams()) {
+      // Only reassign primary ssrc for now...
+      // but we should maybe also reassign ssrcs for ssrc groups?.
+      if (stream.ssrcs.size() == 1) {
+        assignments++;
+        stream.ssrcs[0] = ssrc_generator();
+      }
+    }
+  }
+  return assignments;
+}
+
+void SetNewFingerprint(std::unique_ptr<SessionDescriptionInterface>& sdp) {
+  auto identity = rtc::SSLIdentity::Create("NewIdentity", rtc::KT_DEFAULT);
+  auto new_fingerprint =
+      rtc::SSLFingerprint::CreateUnique("sha-256", *identity.get());
+  for (auto& transport_info : sdp->description()->transport_infos()) {
+    transport_info.description.identity_fingerprint =
+        absl::WrapUnique(new rtc::SSLFingerprint(*new_fingerprint.get()));
+  }
+}
+
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
+       PrAnswerStateTransitionsAsymmetricScrambled) {
+  RTCConfiguration config;
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
+  ConnectFakeSignaling();
+  webrtc::RtpEncodingParameters init_send_encodings;
+  init_send_encodings.active = false;
+  caller()->pc()->AddTrack(caller()->CreateLocalAudioTrack(), {"name"},
+                           {init_send_encodings});
+  caller()->pc()->AddTrack(caller()->CreateLocalVideoTrack(), {"name"},
+                           {init_send_encodings});
+  callee()->pc()->AddTrack(callee()->CreateLocalAudioTrack(), {"name"},
+                           {init_send_encodings});
+  callee()->pc()->AddTrack(callee()->CreateLocalVideoTrack(), {"name"},
+                           {init_send_encodings});
+
+  std::unique_ptr<SessionDescriptionInterface> answer;
+  caller()->SetReceivedSdpMunger(
+      [&](std::unique_ptr<SessionDescriptionInterface>& sdp) {
+        answer = sdp->Clone();
+        SetSdpType(sdp, SdpType::kPrAnswer);
+      });
+  caller()->CreateAndSetAndSignalOffer();
+
+  ASSERT_FALSE(HasFailure());
+  ASSERT_EQ(caller()->pc()->signaling_state(),
+            PeerConnectionInterface::kHaveRemotePrAnswer);
+  ASSERT_EQ(callee()->pc()->signaling_state(),
+            PeerConnectionInterface::kStable);
+
+  // Now scramble the answer sdp so that it (really!) different from the first
+  // prAnswer.
+  // Note: this is maybe {possibly...probably?} a spec violation.
+  ASSERT_GT(SetNewSsrcs(answer), 0);
+  ASSERT_GT(ReassignPayloadIds(answer), 0);
+  SetNewFingerprint(answer);
+
+  // Apply the modified answer as a definitive one.
+  EXPECT_TRUE(caller()->SetRemoteDescription(std::move(answer)));
+  EXPECT_EQ(caller()->pc()->signaling_state(),
+            PeerConnectionInterface::kStable);
 }
 
 }  // namespace
diff --git a/pc/test/integration_test_helpers.cc b/pc/test/integration_test_helpers.cc
index 7ffe4db..9a439f4 100644
--- a/pc/test/integration_test_helpers.cc
+++ b/pc/test/integration_test_helpers.cc
@@ -18,15 +18,16 @@
   return options;
 }
 
-void RemoveSsrcsAndMsids(cricket::SessionDescription* desc) {
-  for (ContentInfo& content : desc->contents()) {
+void RemoveSsrcsAndMsids(std::unique_ptr<SessionDescriptionInterface>& sdp) {
+  for (ContentInfo& content : sdp->description()->contents()) {
     content.media_description()->mutable_streams().clear();
   }
-  desc->set_msid_signaling(0);
+  sdp->description()->set_msid_signaling(0);
 }
 
-void RemoveSsrcsAndKeepMsids(cricket::SessionDescription* desc) {
-  for (ContentInfo& content : desc->contents()) {
+void RemoveSsrcsAndKeepMsids(
+    std::unique_ptr<SessionDescriptionInterface>& sdp) {
+  for (ContentInfo& content : sdp->description()->contents()) {
     std::string track_id;
     std::vector<std::string> stream_ids;
     if (!content.media_description()->streams().empty()) {
@@ -43,6 +44,13 @@
   }
 }
 
+void SetSdpType(std::unique_ptr<SessionDescriptionInterface>& sdp,
+                SdpType sdpType) {
+  std::string str;
+  sdp->ToString(&str);
+  sdp = CreateSessionDescription(sdpType, str);
+}
+
 int FindFirstMediaStatsIndexByKind(
     const std::string& kind,
     const std::vector<const RTCInboundRtpStreamStats*>& inbound_rtps) {
diff --git a/pc/test/integration_test_helpers.h b/pc/test/integration_test_helpers.h
index 458aa3a..5b9d632 100644
--- a/pc/test/integration_test_helpers.h
+++ b/pc/test/integration_test_helpers.h
@@ -165,11 +165,16 @@
 
 // Remove all stream information (SSRCs, track IDs, etc.) and "msid-semantic"
 // attribute from received SDP, simulating a legacy endpoint.
-void RemoveSsrcsAndMsids(cricket::SessionDescription* desc);
+void RemoveSsrcsAndMsids(std::unique_ptr<SessionDescriptionInterface>& desc);
 
 // Removes all stream information besides the stream ids, simulating an
 // endpoint that only signals a=msid lines to convey stream_ids.
-void RemoveSsrcsAndKeepMsids(cricket::SessionDescription* desc);
+void RemoveSsrcsAndKeepMsids(
+    std::unique_ptr<SessionDescriptionInterface>& desc);
+
+// Set SdpType.
+void SetSdpType(std::unique_ptr<SessionDescriptionInterface>& sdp,
+                SdpType sdpType);
 
 // Replaces the stream's primary SSRC and updates the first SSRC of all
 // ssrc-groups.
@@ -265,14 +270,16 @@
   // used to test SDP being applied that a PeerConnection would normally not
   // generate, but a non-JSEP endpoint might.
   void SetReceivedSdpMunger(
-      std::function<void(cricket::SessionDescription*)> munger) {
+      std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
+          munger) {
     received_sdp_munger_ = std::move(munger);
   }
 
   // Similar to the above, but this is run on SDP immediately after it's
   // generated.
   void SetGeneratedSdpMunger(
-      std::function<void(cricket::SessionDescription*)> munger) {
+      std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
+          munger) {
     generated_sdp_munger_ = std::move(munger);
   }
 
@@ -658,9 +665,21 @@
     candidates_expected_ = candidate_count;
   }
 
-  // For testing PR-Answer functionality
-  // If true, an offer will get a pr-answer back.
-  void SetAnswerWithPrAnswer(bool value) { answer_with_pr_answer_ = value; }
+  bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc) {
+    auto observer = rtc::make_ref_counted<FakeSetRemoteDescriptionObserver>();
+    std::string str;
+    desc->ToString(&str);
+    RTC_LOG(LS_INFO) << debug_name_ << ": SetRemoteDescription SDP:\n" << str;
+    pc()->SetRemoteDescription(std::move(desc), observer);  // desc.release());
+    RemoveUnusedVideoRenderers();
+    EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout);
+    auto err = observer->error();
+    if (!err.ok()) {
+      RTC_LOG(LS_WARNING) << debug_name_
+                          << ": SetRemoteDescription error: " << err.message();
+    }
+    return observer->error().ok();
+  }
 
  private:
   // Constructor used by friend class PeerConnectionIntegrationBaseTest.
@@ -736,7 +755,7 @@
     std::unique_ptr<SessionDescriptionInterface> desc =
         CreateSessionDescription(SdpType::kOffer, msg);
     if (received_sdp_munger_) {
-      received_sdp_munger_(desc->description());
+      received_sdp_munger_(desc);
     }
 
     EXPECT_TRUE(SetRemoteDescription(std::move(desc)));
@@ -748,11 +767,6 @@
     }
     auto answer = CreateAnswer();
     ASSERT_NE(nullptr, answer);
-    if (answer_with_pr_answer_) {
-      std::string answer_string;
-      answer->ToString(&answer_string);
-      answer = CreateSessionDescription(SdpType::kPrAnswer, answer_string);
-    }
     EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(answer)));
   }
 
@@ -762,7 +776,7 @@
     std::unique_ptr<SessionDescriptionInterface> desc =
         CreateSessionDescription(type, msg);
     if (received_sdp_munger_) {
-      received_sdp_munger_(desc->description());
+      received_sdp_munger_(desc);
     }
 
     EXPECT_TRUE(SetRemoteDescription(std::move(desc)));
@@ -786,7 +800,7 @@
     }
     auto description = observer->MoveDescription();
     if (generated_sdp_munger_) {
-      generated_sdp_munger_(description->description());
+      generated_sdp_munger_(description);
     }
     return description;
   }
@@ -813,15 +827,6 @@
     return true;
   }
 
-  bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc) {
-    auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
-    RTC_LOG(LS_INFO) << debug_name_ << ": SetRemoteDescription";
-    pc()->SetRemoteDescription(observer.get(), desc.release());
-    RemoveUnusedVideoRenderers();
-    EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout);
-    return observer->result();
-  }
-
   // This is a work around to remove unused fake_video_renderers from
   // transceivers that have either stopped or are no longer receiving.
   void RemoveUnusedVideoRenderers() {
@@ -1059,8 +1064,10 @@
 
   SdpSemantics sdp_semantics_;
   PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options_;
-  std::function<void(cricket::SessionDescription*)> received_sdp_munger_;
-  std::function<void(cricket::SessionDescription*)> generated_sdp_munger_;
+  std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
+      received_sdp_munger_;
+  std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
+      generated_sdp_munger_;
   std::function<void()> remote_offer_handler_;
   MockAsyncDnsResolver* remote_async_dns_resolver_ = nullptr;
   // Result variables for the mock DNS resolver
@@ -1097,8 +1104,6 @@
   uint64_t audio_concealed_stat_ = 0;
   std::string rtp_stats_id_;
 
-  bool answer_with_pr_answer_ = false;
-
   ScopedTaskSafety task_safety_;
 
   friend class PeerConnectionIntegrationBaseTest;