Move integration test helper functions from .h to .cc

This makes for a cleaner .h file.
Pure setters and getters are left in the .h file.

Bug: None
Change-Id: I4ca3911f5493884d49cbbe134fcaea88f13f441c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/470841
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Auto-Submit: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47679}
diff --git a/pc/test/integration_test_helpers.cc b/pc/test/integration_test_helpers.cc
index 2728edb..dad66e8 100644
--- a/pc/test/integration_test_helpers.cc
+++ b/pc/test/integration_test_helpers.cc
@@ -10,10 +10,13 @@
 
 #include "pc/test/integration_test_helpers.h"
 
+#include <algorithm>
+#include <climits>
 #include <cstddef>
 #include <cstdint>
 #include <memory>
 #include <optional>
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -22,15 +25,28 @@
 #include "absl/memory/memory.h"
 #include "absl/strings/string_view.h"
 #include "api/audio/builtin_audio_processing_builder.h"
+#include "api/audio_options.h"
+#include "api/candidate.h"
 #include "api/create_modular_peer_connection_factory.h"
+#include "api/crypto/crypto_options.h"
+#include "api/data_channel_interface.h"
+#include "api/dtls_transport_interface.h"
 #include "api/enable_media_with_defaults.h"
 #include "api/environment/environment.h"
+#include "api/environment/environment_factory.h"
+#include "api/field_trials.h"
 #include "api/jsep.h"
 #include "api/make_ref_counted.h"
 #include "api/media_stream_interface.h"
+#include "api/media_types.h"
 #include "api/peer_connection_interface.h"
 #include "api/rtc_error.h"
 #include "api/rtc_event_log/rtc_event_log_factory.h"
+#include "api/rtp_parameters.h"
+#include "api/rtp_receiver_interface.h"
+#include "api/rtp_sender_interface.h"
+#include "api/rtp_transceiver_direction.h"
+#include "api/rtp_transceiver_interface.h"
 #include "api/scoped_refptr.h"
 #include "api/sequence_checker.h"
 #include "api/stats/rtc_stats_report.h"
@@ -41,23 +57,43 @@
 #include "api/test/time_controller.h"
 #include "api/units/time_delta.h"
 #include "api/units/timestamp.h"
+#include "api/video/video_rotation.h"
 #include "logging/rtc_event_log/fake_rtc_event_log_factory.h"
 #include "media/base/stream_params.h"
+#include "p2p/base/port.h"
+#include "p2p/test/test_turn_customizer.h"
+#include "p2p/test/test_turn_server.h"
+#include "pc/peer_connection.h"
 #include "pc/peer_connection_factory.h"
+#include "pc/peer_connection_proxy.h"
 #include "pc/session_description.h"
 #include "pc/test/fake_audio_capture_module.h"
+#include "pc/test/fake_periodic_video_source.h"
+#include "pc/test/fake_periodic_video_track_source.h"
+#include "pc/test/fake_rtc_certificate_generator.h"
+#include "pc/test/fake_video_track_renderer.h"
 #include "pc/test/mock_peer_connection_observers.h"
+#include "pc/test/rtc_stats_obtainer.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/crypto_random.h"
+#include "rtc_base/fake_mdns_responder.h"
 #include "rtc_base/fake_network.h"
 #include "rtc_base/firewall_socket_server.h"
 #include "rtc_base/logging.h"
+#include "rtc_base/net_helper.h"
+#include "rtc_base/socket_address.h"
+#include "rtc_base/socket_factory.h"
 #include "rtc_base/socket_server.h"
+#include "rtc_base/ssl_stream_adapter.h"
+#include "rtc_base/task_queue_for_test.h"
 #include "rtc_base/thread.h"
 #include "rtc_base/virtual_socket_server.h"
 #include "system_wrappers/include/metrics.h"
 #include "test/create_test_environment.h"
+#include "test/create_test_field_trials.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
+#include "test/run_loop.h"
 #include "test/time_controller/simulated_time_controller.h"
 #include "test/wait_until.h"
 
@@ -104,118 +140,11 @@
   sdp = CreateSessionDescription(sdpType, str);
 }
 
-scoped_refptr<MockStatsObserver>
-PeerConnectionIntegrationWrapper::OldGetStatsForTrack(
-    MediaStreamTrackInterface* track) {
-  auto observer = make_ref_counted<MockStatsObserver>();
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-  EXPECT_TRUE(peer_connection_->GetStats(
-      observer.get(), nullptr,
-      PeerConnectionInterface::kStatsOutputLevelStandard));
-#pragma clang diagnostic pop
-  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
-                                       ::testing::IsTrue()),
-              IsRtcOk());
-  return observer;
-}
-
-scoped_refptr<const RTCStatsReport>
-PeerConnectionIntegrationWrapper::NewGetStats(WaitUntilSettings settings) {
-  auto callback = make_ref_counted<MockRTCStatsCollectorCallback>();
-  peer_connection_->GetStats(callback.get());
-  EXPECT_THAT(test_->GetWaiter(settings).Until(
-                  [&] { return callback->called(); }, ::testing::IsTrue()),
-              IsRtcOk());
-  return callback->report();
-}
-
-std::unique_ptr<SessionDescriptionInterface>
-PeerConnectionIntegrationWrapper::CreateOfferAndWait() {
-  auto observer = make_ref_counted<MockCreateSessionDescriptionObserver>();
-  pc()->CreateOffer(observer.get(), offer_answer_options_);
-  EXPECT_TRUE(test_->GetWaiter().Until([&] { return observer->called(); }));
-  if (!observer->result()) {
-    return nullptr;
+void ReplaceFirstSsrc(StreamParams& stream, uint32_t ssrc) {
+  stream.ssrcs[0] = ssrc;
+  for (auto& group : stream.ssrc_groups) {
+    group.ssrcs[0] = ssrc;
   }
-  auto description = observer->MoveDescription();
-  if (generated_sdp_munger_) {
-    generated_sdp_munger_(description);
-  }
-  return description;
-}
-
-bool PeerConnectionIntegrationWrapper::SetRemoteDescription(
-    std::unique_ptr<SessionDescriptionInterface> desc) {
-  auto observer = make_ref_counted<FakeSetRemoteDescriptionObserver>();
-  std::string sdp;
-  EXPECT_TRUE(desc->ToString(&sdp));
-  RTC_LOG(LS_INFO) << debug_name_
-                   << ": SetRemoteDescription SDP: type=" << desc->GetType()
-                   << " contents=\n"
-                   << sdp;
-  pc()->SetRemoteDescription(std::move(desc), observer);
-  RemoveUnusedVideoRenderers();
-  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
-                                       ::testing::IsTrue()),
-              IsRtcOk());
-  auto err = observer->error();
-  if (!err.ok()) {
-    RTC_LOG(LS_WARNING) << debug_name_
-                        << ": SetRemoteDescription error: " << err.message();
-  }
-  return observer->error().ok();
-}
-
-bool PeerConnectionIntegrationWrapper::SetLocalDescriptionAndSendSdpMessage(
-    std::unique_ptr<SessionDescriptionInterface> desc) {
-  auto observer = make_ref_counted<MockSetSessionDescriptionObserver>();
-  RTC_LOG(LS_INFO) << debug_name_ << ": SetLocalDescriptionAndSendSdpMessage";
-  SdpType type = desc->GetType();
-  std::string sdp;
-  EXPECT_TRUE(desc->ToString(&sdp));
-  RTC_LOG(LS_INFO) << debug_name_ << ": local SDP type=" << desc->GetType()
-                   << " contents=\n"
-                   << sdp;
-  pc()->SetLocalDescription(observer.get(), desc.release());
-  RemoveUnusedVideoRenderers();
-  SendSdpMessage(type, sdp);
-  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
-                                       ::testing::IsTrue()),
-              IsRtcOk());
-  return true;
-}
-
-std::unique_ptr<SessionDescriptionInterface>
-PeerConnectionIntegrationWrapper::CreateAnswer() {
-  auto observer = make_ref_counted<MockCreateSessionDescriptionObserver>();
-  pc()->CreateAnswer(observer.get(), offer_answer_options_);
-  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
-                                       ::testing::IsTrue()),
-              IsRtcOk());
-  if (!observer->result()) {
-    return nullptr;
-  }
-  auto description = observer->MoveDescription();
-  if (generated_sdp_munger_) {
-    generated_sdp_munger_(description);
-  }
-  return description;
-}
-
-void PeerConnectionIntegrationWrapper::ReceiveIceMessage(
-    const std::string& sdp_mid,
-    int sdp_mline_index,
-    const std::string& msg) {
-  RTC_LOG(LS_INFO) << debug_name_ << ": ReceiveIceMessage";
-  std::optional<RTCError> result;
-  pc()->AddIceCandidate(absl::WrapUnique(CreateIceCandidate(
-                            sdp_mid, sdp_mline_index, msg, nullptr)),
-                        [&result](RTCError r) { result = r; });
-  EXPECT_THAT(test_->GetWaiter().Until([&] { return result.has_value(); },
-                                       ::testing::IsTrue()),
-              IsRtcOk());
-  EXPECT_TRUE(result.value().ok());
 }
 
 int FindFirstMediaStatsIndexByKind(
@@ -229,13 +158,6 @@
   return -1;
 }
 
-void ReplaceFirstSsrc(StreamParams& stream, uint32_t ssrc) {
-  stream.ssrcs[0] = ssrc;
-  for (auto& group : stream.ssrc_groups) {
-    group.ssrcs[0] = ssrc;
-  }
-}
-
 TaskQueueMetronome::TaskQueueMetronome(TimeDelta tick_period)
     : tick_period_(tick_period) {}
 
@@ -255,7 +177,7 @@
                  [this] {
                    RTC_DCHECK_RUN_ON(&sequence_checker_);
                    std::vector<absl::AnyInvocable<void() &&>> callbacks;
-                   callbacks_.swap(callbacks);
+                   callbacks.swap(callbacks_);
                    for (auto& callback : callbacks)
                      std::move(callback)();
                  }),
@@ -268,7 +190,373 @@
   return tick_period_;
 }
 
-// Implementation of PeerConnectionIntegrationWrapper functions
+PeerConnectionIntegrationWrapper::PeerConnectionIntegrationWrapper(
+    const std::string& debug_name,
+    Environment env,
+    internal::PeerConnectionIntegrationTestBase* test)
+    : debug_name_(debug_name), env_(env), test_(test) {}
+
+PeerConnection* PeerConnectionIntegrationWrapper::pc_internal() const {
+  auto* pci =
+      static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
+          pc());
+  return static_cast<PeerConnection*>(pci->internal());
+}
+
+void PeerConnectionIntegrationWrapper::CreateAndSetAndSignalOffer() {
+  std::unique_ptr<SessionDescriptionInterface> offer = CreateOfferAndWait();
+  ASSERT_NE(nullptr, offer);
+  EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(offer)));
+}
+
+void PeerConnectionIntegrationWrapper::AddAudioVideoTracks() {
+  AddAudioTrack();
+  AddVideoTrack();
+  ResetRtpSenderObservers();
+}
+
+scoped_refptr<AudioTrackInterface>
+PeerConnectionIntegrationWrapper::CreateLocalAudioTrack() {
+  AudioOptions options;
+  // Disable highpass filter so that we can get all the test audio frames.
+  options.highpass_filter = false;
+  scoped_refptr<AudioSourceInterface> source =
+      peer_connection_factory_->CreateAudioSource(options);
+  // TODO(perkj): Test audio source when it is implemented. Currently audio
+  // always use the default input.
+  return peer_connection_factory_->CreateAudioTrack(CreateRandomUuid(),
+                                                    source.get());
+}
+
+scoped_refptr<VideoTrackInterface>
+PeerConnectionIntegrationWrapper::CreateLocalVideoTrack() {
+  FakePeriodicVideoSource::Config config;
+  config.timestamp_offset = env_.clock().CurrentTime();
+  return CreateLocalVideoTrackInternal(config);
+}
+
+scoped_refptr<VideoTrackInterface>
+PeerConnectionIntegrationWrapper::CreateLocalVideoTrackWithConfig(
+    FakePeriodicVideoSource::Config config) {
+  return CreateLocalVideoTrackInternal(config);
+}
+
+scoped_refptr<VideoTrackInterface>
+PeerConnectionIntegrationWrapper::CreateLocalVideoTrackWithRotation(
+    VideoRotation rotation) {
+  FakePeriodicVideoSource::Config config;
+  config.rotation = rotation;
+  config.timestamp_offset = env_.clock().CurrentTime();
+  return CreateLocalVideoTrackInternal(config);
+}
+
+scoped_refptr<RtpSenderInterface> PeerConnectionIntegrationWrapper::AddTrack(
+    scoped_refptr<MediaStreamTrackInterface> track,
+    const std::vector<std::string>& stream_ids) {
+  EXPECT_TRUE(track);
+  if (!track) {
+    return nullptr;
+  }
+  auto result = pc()->AddTrack(track, stream_ids);
+  EXPECT_EQ(RTCErrorType::NONE, result.error().type());
+  if (result.ok()) {
+    return result.MoveValue();
+  } else {
+    return nullptr;
+  }
+}
+
+std::vector<scoped_refptr<RtpReceiverInterface>>
+PeerConnectionIntegrationWrapper::GetReceiversOfType(
+    webrtc::MediaType media_type) {
+  std::vector<scoped_refptr<RtpReceiverInterface>> receivers;
+  for (const auto& receiver : pc()->GetReceivers()) {
+    if (receiver->media_type() == media_type) {
+      receivers.push_back(receiver);
+    }
+  }
+  return receivers;
+}
+
+scoped_refptr<RtpTransceiverInterface>
+PeerConnectionIntegrationWrapper::GetFirstTransceiverOfType(
+    webrtc::MediaType media_type) {
+  for (auto transceiver : pc()->GetTransceivers()) {
+    if (transceiver->receiver()->media_type() == media_type) {
+      return transceiver;
+    }
+  }
+  return nullptr;
+}
+
+bool PeerConnectionIntegrationWrapper::SignalingStateStable() {
+  return pc()->signaling_state() == PeerConnectionInterface::kStable;
+}
+
+bool PeerConnectionIntegrationWrapper::IceGatheringStateComplete() {
+  return pc()->ice_gathering_state() ==
+         PeerConnectionInterface::kIceGatheringComplete;
+}
+
+void PeerConnectionIntegrationWrapper::CreateDataChannel() {
+  CreateDataChannel(nullptr);
+}
+
+void PeerConnectionIntegrationWrapper::CreateDataChannel(
+    const DataChannelInit* init) {
+  CreateDataChannel(kDataChannelLabel, init);
+}
+
+void PeerConnectionIntegrationWrapper::CreateDataChannel(
+    const std::string& label,
+    const DataChannelInit* init) {
+  auto data_channel_or_error = pc()->CreateDataChannelOrError(label, init);
+  ASSERT_TRUE(data_channel_or_error.ok());
+  data_channels_.push_back(data_channel_or_error.MoveValue());
+  ASSERT_TRUE(data_channels_.back().get() != nullptr);
+  data_observers_.push_back(
+      std::make_unique<MockDataChannelObserver>(data_channels_.back().get()));
+}
+
+DataChannelInterface* PeerConnectionIntegrationWrapper::data_channel() {
+  if (data_channels_.empty()) {
+    return nullptr;
+  }
+  return data_channels_.back().get();
+}
+
+MockDataChannelObserver* PeerConnectionIntegrationWrapper::data_observer()
+    const {
+  if (data_observers_.empty()) {
+    return nullptr;
+  }
+  return data_observers_.back().get();
+}
+
+std::unique_ptr<SessionDescriptionInterface>
+PeerConnectionIntegrationWrapper::CreateAnswerForTest() {
+  return CreateAnswer();
+}
+
+int PeerConnectionIntegrationWrapper::audio_frames_received() const {
+  return fake_audio_capture_module_->frames_received();
+}
+
+int PeerConnectionIntegrationWrapper::min_video_frames_received_per_track()
+    const {
+  int min_frames = INT_MAX;
+  if (fake_video_renderers_.empty()) {
+    return 0;
+  }
+
+  for (const auto& pair : fake_video_renderers_) {
+    min_frames = std::min(min_frames, pair.second->num_rendered_frames());
+  }
+  return min_frames;
+}
+
+scoped_refptr<MockStatsObserver>
+PeerConnectionIntegrationWrapper::OldGetStatsForTrack(
+    MediaStreamTrackInterface* track) {
+  auto observer = make_ref_counted<MockStatsObserver>();
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  EXPECT_TRUE(peer_connection_->GetStats(
+      observer.get(), nullptr,
+      PeerConnectionInterface::kStatsOutputLevelStandard));
+#pragma clang diagnostic pop
+  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
+                                       ::testing::IsTrue()),
+              IsRtcOk());
+  return observer;
+}
+
+scoped_refptr<MockStatsObserver>
+PeerConnectionIntegrationWrapper::OldGetStats() {
+  return OldGetStatsForTrack(nullptr);
+}
+
+scoped_refptr<const RTCStatsReport>
+PeerConnectionIntegrationWrapper::NewGetStats(WaitUntilSettings settings) {
+  auto callback = make_ref_counted<MockRTCStatsCollectorCallback>();
+  peer_connection_->GetStats(callback.get());
+  EXPECT_THAT(test_->GetWaiter(settings).Until(
+                  [&] { return callback->called(); }, ::testing::IsTrue()),
+              IsRtcOk());
+  return callback->report();
+}
+
+scoped_refptr<const RTCStatsReport>
+PeerConnectionIntegrationWrapper::NewGetStats(test::RunLoop& run_loop) {
+  scoped_refptr<const RTCStatsReport> report;
+  auto callback = RTCStatsObtainer::Create(&report, run_loop.QuitClosure());
+  peer_connection_->GetStats(callback.get());
+  run_loop.Run();
+  EXPECT_TRUE(report);
+  return report;
+}
+
+std::string PeerConnectionIntegrationWrapper::DtlsCipher() {
+  auto report = NewGetStats();
+  if (!report)
+    return "";
+  auto stats = report->GetStatsOfType<RTCTransportStats>();
+  if (stats.empty() || !stats[0]->dtls_cipher.has_value())
+    return "";
+  return *stats[0]->dtls_cipher;
+}
+
+std::string PeerConnectionIntegrationWrapper::SrtpCipher() {
+  auto report = NewGetStats();
+  if (!report)
+    return "";
+  auto stats = report->GetStatsOfType<RTCTransportStats>();
+  if (stats.empty() || !stats[0]->srtp_cipher.has_value())
+    return "";
+  return *stats[0]->srtp_cipher;
+}
+
+int PeerConnectionIntegrationWrapper::rendered_width() {
+  EXPECT_FALSE(fake_video_renderers_.empty());
+  return fake_video_renderers_.empty()
+             ? 0
+             : fake_video_renderers_.begin()->second->width();
+}
+
+int PeerConnectionIntegrationWrapper::rendered_height() {
+  EXPECT_FALSE(fake_video_renderers_.empty());
+  return fake_video_renderers_.empty()
+             ? 0
+             : fake_video_renderers_.begin()->second->height();
+}
+
+double PeerConnectionIntegrationWrapper::rendered_aspect_ratio() {
+  if (rendered_height() == 0) {
+    return 0.0;
+  }
+  return static_cast<double>(rendered_width()) / rendered_height();
+}
+
+VideoRotation PeerConnectionIntegrationWrapper::rendered_rotation() {
+  EXPECT_FALSE(fake_video_renderers_.empty());
+  return fake_video_renderers_.empty()
+             ? kVideoRotation_0
+             : fake_video_renderers_.begin()->second->rotation();
+}
+
+int PeerConnectionIntegrationWrapper::local_rendered_width() {
+  return local_video_renderer_ ? local_video_renderer_->width() : 0;
+}
+
+int PeerConnectionIntegrationWrapper::local_rendered_height() {
+  return local_video_renderer_ ? local_video_renderer_->height() : 0;
+}
+
+double PeerConnectionIntegrationWrapper::local_rendered_aspect_ratio() {
+  if (local_rendered_height() == 0) {
+    return 0.0;
+  }
+  return static_cast<double>(local_rendered_width()) / local_rendered_height();
+}
+
+size_t PeerConnectionIntegrationWrapper::number_of_remote_streams() {
+  if (!pc()) {
+    return 0;
+  }
+  return pc()->remote_streams()->count();
+}
+
+StreamCollectionInterface* PeerConnectionIntegrationWrapper::remote_streams()
+    const {
+  if (!pc()) {
+    ADD_FAILURE();
+    return nullptr;
+  }
+  return pc()->remote_streams().get();
+}
+
+StreamCollectionInterface* PeerConnectionIntegrationWrapper::local_streams() {
+  if (!pc()) {
+    ADD_FAILURE();
+    return nullptr;
+  }
+  return pc()->local_streams().get();
+}
+
+PeerConnectionInterface::SignalingState
+PeerConnectionIntegrationWrapper::signaling_state() {
+  return pc()->signaling_state();
+}
+
+PeerConnectionInterface::IceConnectionState
+PeerConnectionIntegrationWrapper::ice_connection_state() {
+  return pc()->ice_connection_state();
+}
+
+PeerConnectionInterface::IceConnectionState
+PeerConnectionIntegrationWrapper::standardized_ice_connection_state() {
+  return pc()->standardized_ice_connection_state();
+}
+
+PeerConnectionInterface::IceGatheringState
+PeerConnectionIntegrationWrapper::ice_gathering_state() {
+  return pc()->ice_gathering_state();
+}
+
+void PeerConnectionIntegrationWrapper::ResetRtpReceiverObservers() {
+  rtp_receiver_observers_.clear();
+  for (const scoped_refptr<RtpReceiverInterface>& receiver :
+       pc()->GetReceivers()) {
+    std::unique_ptr<MockRtpReceiverObserver> observer(
+        new MockRtpReceiverObserver(receiver->media_type()));
+    receiver->SetObserver(observer.get());
+    rtp_receiver_observers_.push_back(std::move(observer));
+  }
+}
+
+void PeerConnectionIntegrationWrapper::ResetRtpSenderObservers() {
+  rtp_sender_observers_.clear();
+  for (const scoped_refptr<RtpSenderInterface>& sender : pc()->GetSenders()) {
+    std::unique_ptr<MockRtpSenderObserver> observer(
+        new MockRtpSenderObserver(sender->media_type()));
+    sender->SetObserver(observer.get());
+    rtp_sender_observers_.push_back(std::move(observer));
+  }
+}
+
+Candidate PeerConnectionIntegrationWrapper::last_candidate_gathered() const {
+  if (last_gathered_ice_candidate_) {
+    return last_gathered_ice_candidate_->candidate();
+  }
+  return Candidate();
+}
+
+void PeerConnectionIntegrationWrapper::SetMdnsResponder(
+    std::unique_ptr<FakeMdnsResponder> mdns_responder) {
+  RTC_DCHECK(mdns_responder != nullptr);
+  mdns_responder_ = mdns_responder.get();
+  network_manager()->set_mdns_responder(std::move(mdns_responder));
+}
+
+std::unique_ptr<SessionDescriptionInterface>
+PeerConnectionIntegrationWrapper::CreateOfferAndWait() {
+  auto observer = make_ref_counted<MockCreateSessionDescriptionObserver>();
+  pc()->CreateOffer(observer.get(), offer_answer_options_);
+  EXPECT_TRUE(test_->GetWaiter().Until([&] { return observer->called(); }));
+  if (!observer->result()) {
+    return nullptr;
+  }
+  auto description = observer->MoveDescription();
+  if (generated_sdp_munger_) {
+    generated_sdp_munger_(description);
+  }
+  return description;
+}
+
+bool PeerConnectionIntegrationWrapper::Rollback() {
+  return SetRemoteDescription(CreateRollbackSessionDescription());
+}
+
 void PeerConnectionIntegrationWrapper::StartWatchingDelayStats() {
   // Get the baseline numbers for audio_packets and audio_delay.
   auto received_stats = NewGetStats();
@@ -356,6 +644,102 @@
   audio_concealed_stat_ = *rtp_stats->concealed_samples;
 }
 
+bool PeerConnectionIntegrationWrapper::SetRemoteDescription(
+    std::unique_ptr<SessionDescriptionInterface> desc) {
+  auto observer = make_ref_counted<FakeSetRemoteDescriptionObserver>();
+  std::string sdp;
+  EXPECT_TRUE(desc->ToString(&sdp));
+  RTC_LOG(LS_INFO) << debug_name_
+                   << ": SetRemoteDescription SDP: type=" << desc->GetType()
+                   << " contents=\n"
+                   << sdp;
+  pc()->SetRemoteDescription(std::move(desc), observer);
+  RemoveUnusedVideoRenderers();
+  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
+                                       ::testing::IsTrue()),
+              IsRtcOk());
+  auto err = observer->error();
+  if (!err.ok()) {
+    RTC_LOG(LS_WARNING) << debug_name_
+                        << ": SetRemoteDescription error: " << err.message();
+  }
+  return observer->error().ok();
+}
+
+void PeerConnectionIntegrationWrapper::NegotiateCorruptionDetectionHeader() {
+  for (const auto& transceiver : pc()->GetTransceivers()) {
+    if (transceiver->media_type() != webrtc::MediaType::VIDEO) {
+      continue;
+    }
+    auto extensions = transceiver->GetHeaderExtensionsToNegotiate();
+    for (auto& extension : extensions) {
+      if (extension.uri == RtpExtension::kCorruptionDetectionUri) {
+        extension.direction = RtpTransceiverDirection::kSendRecv;
+      }
+    }
+    transceiver->SetHeaderExtensionsToNegotiate(extensions);
+  }
+}
+
+uint32_t PeerConnectionIntegrationWrapper::GetCorruptionScoreCount() {
+  scoped_refptr<const RTCStatsReport> report = NewGetStats();
+  auto inbound_stream_stats =
+      report->GetStatsOfType<RTCInboundRtpStreamStats>();
+  for (const auto& stat : inbound_stream_stats) {
+    if (*stat->kind == "video") {
+      return stat->corruption_measurements.value_or(0);
+    }
+  }
+  return 0;
+}
+
+uint32_t PeerConnectionIntegrationWrapper::GetReceivedFrameCount() {
+  scoped_refptr<const RTCStatsReport> report = NewGetStats();
+  auto inbound_stream_stats =
+      report->GetStatsOfType<RTCInboundRtpStreamStats>();
+  for (const auto& stat : inbound_stream_stats) {
+    if (*stat->kind == "video") {
+      return stat->frames_received.value_or(0);
+    }
+  }
+  return 0;
+}
+
+std::optional<int> PeerConnectionIntegrationWrapper::tls_version() {
+  return dtls_transport_information().tls_version();
+}
+
+std::optional<DtlsTransportTlsRole>
+PeerConnectionIntegrationWrapper::dtls_transport_role() {
+  return dtls_transport_information().role();
+}
+
+DtlsTransportInformation
+PeerConnectionIntegrationWrapper::dtls_transport_information() {
+  return network_thread_->BlockingCall([&] {
+    return pc()->GetSctpTransport()->dtls_transport()->Information();
+  });
+}
+
+bool PeerConnectionIntegrationWrapper::SetLocalDescriptionAndSendSdpMessage(
+    std::unique_ptr<SessionDescriptionInterface> desc) {
+  auto observer = make_ref_counted<MockSetSessionDescriptionObserver>();
+  RTC_LOG(LS_INFO) << debug_name_ << ": SetLocalDescriptionAndSendSdpMessage";
+  SdpType type = desc->GetType();
+  std::string sdp;
+  EXPECT_TRUE(desc->ToString(&sdp));
+  RTC_LOG(LS_INFO) << debug_name_ << ": local SDP type=" << desc->GetType()
+                   << " contents=\n"
+                   << sdp;
+  pc()->SetLocalDescription(observer.get(), desc.release());
+  RemoveUnusedVideoRenderers();
+  SendSdpMessage(type, sdp);
+  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
+                                       ::testing::IsTrue()),
+              IsRtcOk());
+  return true;
+}
+
 bool PeerConnectionIntegrationWrapper::Init(
     const PeerConnectionFactory::Options* options,
     const PeerConnectionInterface::RTCConfiguration* config,
@@ -437,6 +821,330 @@
   return peer_connection_.get() != nullptr;
 }
 
+scoped_refptr<PeerConnectionInterface>
+PeerConnectionIntegrationWrapper::CreatePeerConnection(
+    const PeerConnectionInterface::RTCConfiguration* config,
+    PeerConnectionDependencies dependencies) {
+  PeerConnectionInterface::RTCConfiguration modified_config;
+  modified_config.sdp_semantics = sdp_semantics_;
+  // If `config` is null, this will result in a default configuration being
+  // used.
+  if (config) {
+    modified_config = *config;
+  }
+  // Disable resolution adaptation; we don't want it interfering with the
+  // test results.
+  // TODO(deadbeef): Do something more robust. Since we're testing for aspect
+  // ratios and not specific resolutions, is this even necessary?
+  modified_config.set_cpu_adaptation(false);
+
+  dependencies.observer = this;
+  auto peer_connection_or_error =
+      peer_connection_factory_->CreatePeerConnectionOrError(
+          modified_config, std::move(dependencies));
+  return peer_connection_or_error.ok() ? peer_connection_or_error.MoveValue()
+                                       : nullptr;
+}
+
+scoped_refptr<VideoTrackInterface>
+PeerConnectionIntegrationWrapper::CreateLocalVideoTrackInternal(
+    FakePeriodicVideoSource::Config config) {
+  // Set max frame rate to 10fps to reduce the risk of test flakiness.
+  // TODO(deadbeef): Do something more robust.
+  config.frame_interval = TimeDelta::Millis(100);
+
+  video_track_sources_.emplace_back(
+      make_ref_counted<FakePeriodicVideoTrackSource>(config,
+                                                     false /* remote */));
+  scoped_refptr<VideoTrackInterface> track =
+      peer_connection_factory_->CreateVideoTrack(video_track_sources_.back(),
+                                                 CreateRandomUuid());
+  if (!local_video_renderer_) {
+    local_video_renderer_.reset(new FakeVideoTrackRenderer(track.get()));
+  }
+  return track;
+}
+
+void PeerConnectionIntegrationWrapper::HandleIncomingOffer(
+    const std::string& msg) {
+  RTC_LOG(LS_INFO) << debug_name_ << ": HandleIncomingOffer";
+  std::unique_ptr<SessionDescriptionInterface> desc =
+      CreateSessionDescription(SdpType::kOffer, msg);
+  if (received_sdp_munger_) {
+    received_sdp_munger_(desc);
+  }
+
+  EXPECT_TRUE(SetRemoteDescription(std::move(desc)));
+  // Setting a remote description may have changed the number of receivers,
+  // so reset the receiver observers.
+  ResetRtpReceiverObservers();
+  if (remote_offer_handler_) {
+    remote_offer_handler_();
+  }
+  std::unique_ptr<SessionDescriptionInterface> answer = CreateAnswer();
+  ASSERT_NE(nullptr, answer);
+  EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(answer)));
+}
+
+void PeerConnectionIntegrationWrapper::HandleIncomingAnswer(
+    SdpType type,
+    const std::string& msg) {
+  RTC_LOG(LS_INFO) << debug_name_ << ": HandleIncomingAnswer of type " << type;
+  std::unique_ptr<SessionDescriptionInterface> desc =
+      CreateSessionDescription(type, msg);
+  if (received_sdp_munger_) {
+    received_sdp_munger_(desc);
+    if (!desc) {
+      // Answer was "taken" by munger...so that it can be applied later ?
+      RTC_LOG(LS_INFO) << debug_name_ << ": answer NOT applied";
+      return;
+    }
+  }
+  EXPECT_TRUE(SetRemoteDescription(std::move(desc)));
+  // Set the RtpReceiverObserver after receivers are created.
+  ResetRtpReceiverObservers();
+}
+
+std::unique_ptr<SessionDescriptionInterface>
+PeerConnectionIntegrationWrapper::CreateAnswer() {
+  auto observer = make_ref_counted<MockCreateSessionDescriptionObserver>();
+  pc()->CreateAnswer(observer.get(), offer_answer_options_);
+  EXPECT_THAT(test_->GetWaiter().Until([&] { return observer->called(); },
+                                       ::testing::IsTrue()),
+              IsRtcOk());
+  if (!observer->result()) {
+    return nullptr;
+  }
+  auto description = observer->MoveDescription();
+  if (generated_sdp_munger_) {
+    generated_sdp_munger_(description);
+  }
+  return description;
+}
+
+void PeerConnectionIntegrationWrapper::RemoveUnusedVideoRenderers() {
+  if (sdp_semantics_ != SdpSemantics::kUnifiedPlan) {
+    return;
+  }
+  auto transceivers = pc()->GetTransceivers();
+  std::set<std::string> active_renderers;
+  for (auto& transceiver : transceivers) {
+    // Note - we don't check for direction here. This function is called
+    // before direction is set, and in that case, we should not remove
+    // the renderer.
+    if (transceiver->receiver()->media_type() == webrtc::MediaType::VIDEO) {
+      active_renderers.insert(transceiver->receiver()->track()->id());
+    }
+  }
+  for (auto it = fake_video_renderers_.begin();
+       it != fake_video_renderers_.end();) {
+    // Remove fake video renderers belonging to any non-active transceivers.
+    if (!active_renderers.count(it->first)) {
+      it = fake_video_renderers_.erase(it);
+    } else {
+      it++;
+    }
+  }
+}
+
+void PeerConnectionIntegrationWrapper::SendSdpMessage(SdpType type,
+                                                      const std::string& msg) {
+  if (signaling_delay_ms_ == 0) {
+    RelaySdpMessageIfReceiverExists(type, msg);
+  } else {
+    Thread::Current()->PostDelayedTask(
+        SafeTask(
+            task_safety_.flag(),
+            [this, type, msg] { RelaySdpMessageIfReceiverExists(type, msg); }),
+        TimeDelta::Millis(signaling_delay_ms_));
+  }
+}
+
+void PeerConnectionIntegrationWrapper::RelaySdpMessageIfReceiverExists(
+    SdpType type,
+    const std::string& msg) {
+  if (signaling_message_receiver_) {
+    signaling_message_receiver_->ReceiveSdpMessage(type, msg);
+  }
+}
+
+void PeerConnectionIntegrationWrapper::SendIceMessage(
+    const std::string& sdp_mid,
+    int sdp_mline_index,
+    const std::string& msg) {
+  if (signaling_delay_ms_ == 0) {
+    RelayIceMessageIfReceiverExists(sdp_mid, sdp_mline_index, msg);
+  } else {
+    Thread::Current()->PostDelayedTask(
+        SafeTask(task_safety_.flag(),
+                 [this, sdp_mid, sdp_mline_index, msg] {
+                   RelayIceMessageIfReceiverExists(sdp_mid, sdp_mline_index,
+                                                   msg);
+                 }),
+        TimeDelta::Millis(signaling_delay_ms_));
+  }
+}
+
+void PeerConnectionIntegrationWrapper::RelayIceMessageIfReceiverExists(
+    const std::string& sdp_mid,
+    int sdp_mline_index,
+    const std::string& msg) {
+  if (signaling_message_receiver_) {
+    signaling_message_receiver_->ReceiveIceMessage(sdp_mid, sdp_mline_index,
+                                                   msg);
+  }
+}
+
+void PeerConnectionIntegrationWrapper::ReceiveSdpMessage(
+    SdpType type,
+    const std::string& msg) {
+  if (type == SdpType::kOffer) {
+    HandleIncomingOffer(msg);
+  } else {
+    HandleIncomingAnswer(type, msg);
+  }
+}
+
+void PeerConnectionIntegrationWrapper::ReceiveIceMessage(
+    const std::string& sdp_mid,
+    int sdp_mline_index,
+    const std::string& msg) {
+  RTC_LOG(LS_INFO) << debug_name_ << ": ReceiveIceMessage";
+  std::optional<RTCError> result;
+  pc()->AddIceCandidate(absl::WrapUnique(CreateIceCandidate(
+                            sdp_mid, sdp_mline_index, msg, nullptr)),
+                        [&result](RTCError r) { result = r; });
+  EXPECT_THAT(test_->GetWaiter().Until([&] { return result.has_value(); },
+                                       ::testing::IsTrue()),
+              IsRtcOk());
+  EXPECT_TRUE(result.value().ok());
+}
+
+void PeerConnectionIntegrationWrapper::OnSignalingChange(
+    PeerConnectionInterface::SignalingState new_state) {
+  EXPECT_EQ(pc()->signaling_state(), new_state);
+  peer_connection_signaling_state_history_.push_back(new_state);
+}
+
+void PeerConnectionIntegrationWrapper::OnAddTrack(
+    scoped_refptr<RtpReceiverInterface> receiver,
+    const std::vector<scoped_refptr<MediaStreamInterface>>& streams) {
+  if (receiver->media_type() == webrtc::MediaType::VIDEO) {
+    scoped_refptr<VideoTrackInterface> video_track(
+        static_cast<VideoTrackInterface*>(receiver->track().get()));
+    ASSERT_TRUE(fake_video_renderers_.find(video_track->id()) ==
+                fake_video_renderers_.end());
+    fake_video_renderers_[video_track->id()] =
+        std::make_unique<FakeVideoTrackRenderer>(video_track.get());
+  }
+}
+
+void PeerConnectionIntegrationWrapper::OnRemoveTrack(
+    scoped_refptr<RtpReceiverInterface> receiver) {
+  if (receiver->media_type() == webrtc::MediaType::VIDEO) {
+    auto it = fake_video_renderers_.find(receiver->track()->id());
+    if (it != fake_video_renderers_.end()) {
+      fake_video_renderers_.erase(it);
+    } else {
+      RTC_LOG(LS_ERROR) << "OnRemoveTrack called for non-active renderer";
+    }
+  }
+}
+
+void PeerConnectionIntegrationWrapper::OnRenegotiationNeeded() {}
+
+void PeerConnectionIntegrationWrapper::OnIceConnectionChange(
+    PeerConnectionInterface::IceConnectionState new_state) {
+  EXPECT_EQ(pc()->ice_connection_state(), new_state);
+  ice_connection_state_history_.push_back(new_state);
+}
+
+void PeerConnectionIntegrationWrapper::OnStandardizedIceConnectionChange(
+    PeerConnectionInterface::IceConnectionState new_state) {
+  standardized_ice_connection_state_history_.push_back(new_state);
+}
+
+void PeerConnectionIntegrationWrapper::OnConnectionChange(
+    PeerConnectionInterface::PeerConnectionState new_state) {
+  peer_connection_state_history_.push_back(new_state);
+  if (connection_change_callback_) {
+    connection_change_callback_(new_state);
+  }
+}
+
+void PeerConnectionIntegrationWrapper::OnIceGatheringChange(
+    PeerConnectionInterface::IceGatheringState new_state) {
+  EXPECT_EQ(pc()->ice_gathering_state(), new_state);
+  ice_gathering_state_history_.push_back(new_state);
+}
+
+void PeerConnectionIntegrationWrapper::OnIceCandidate(
+    const IceCandidate* candidate) {
+  RTC_LOG(LS_INFO) << debug_name_ << ": OnIceCandidate";
+
+  if (remote_async_dns_resolver_) {
+    const auto& local_candidate = candidate->candidate();
+    if (local_candidate.address().IsUnresolvedIP()) {
+      RTC_DCHECK(local_candidate.is_local());
+      const auto resolved_ip = mdns_responder_->GetMappedAddressForName(
+          local_candidate.address().hostname());
+      RTC_DCHECK(!resolved_ip.IsNil());
+      remote_async_dns_resolved_addr_ = local_candidate.address();
+      remote_async_dns_resolved_addr_.SetResolvedIP(resolved_ip);
+      EXPECT_CALL(*remote_async_dns_resolver_, Start(_, _))
+          .WillOnce([](const SocketAddress& addr,
+                       absl::AnyInvocable<void()> callback) { callback(); });
+      EXPECT_CALL(*remote_async_dns_resolver_, result())
+          .WillOnce(ReturnRef(remote_async_dns_resolver_result_));
+      EXPECT_CALL(remote_async_dns_resolver_result_, GetResolvedAddress(_, _))
+          .WillOnce(DoAll(SetArgPointee<1>(remote_async_dns_resolved_addr_),
+                          Return(true)));
+    }
+  }
+
+  // Check if we expected to have a candidate.
+  EXPECT_GT(candidates_expected_, 1);
+  candidates_expected_--;
+  std::string ice_sdp = candidate->ToString();
+  if (signaling_message_receiver_ == nullptr || !signal_ice_candidates_) {
+    // Remote party may be deleted.
+    return;
+  }
+  SendIceMessage(candidate->sdp_mid(), candidate->sdp_mline_index(), ice_sdp);
+  last_gathered_ice_candidate_ =
+      CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
+                         candidate->candidate());
+}
+
+void PeerConnectionIntegrationWrapper::OnIceCandidateError(
+    const std::string& address,
+    int port,
+    const std::string& url,
+    int error_code,
+    const std::string& error_text) {
+  error_event_ =
+      IceCandidateErrorEvent(address, port, url, error_code, error_text);
+}
+
+void PeerConnectionIntegrationWrapper::OnDataChannel(
+    scoped_refptr<DataChannelInterface> data_channel) {
+  RTC_LOG(LS_INFO) << debug_name_ << ": OnDataChannel";
+  data_channels_.push_back(data_channel);
+  data_observers_.push_back(
+      std::make_unique<MockDataChannelObserver>(data_channel.get()));
+}
+
+bool PeerConnectionIntegrationWrapper::IdExists(
+    const RtpHeaderExtensions& extensions,
+    int id) {
+  for (const auto& extension : extensions) {
+    if (extension.id == id) {
+      return true;
+    }
+  }
+  return false;
+}
+
 namespace internal {
 
 // Utility class for tests that run multiple operations that cause excessive
@@ -494,6 +1202,291 @@
   DestroyPeerConnections();
 }
 
+bool PeerConnectionIntegrationTestBase::DtlsConnected() {
+  // TODO(deadbeef): kIceConnectionConnected currently means both ICE and DTLS
+  // are connected. This is an important distinction. Once we have separate
+  // ICE and DTLS state, this check needs to use the DTLS state.
+  return (callee()->ice_connection_state() ==
+              PeerConnectionInterface::kIceConnectionConnected ||
+          callee()->ice_connection_state() ==
+              PeerConnectionInterface::kIceConnectionCompleted) &&
+         (caller()->ice_connection_state() ==
+              PeerConnectionInterface::kIceConnectionConnected ||
+          caller()->ice_connection_state() ==
+              PeerConnectionInterface::kIceConnectionCompleted);
+}
+
+void PeerConnectionIntegrationTestBase::SetFieldTrials(
+    absl::string_view field_trials) {
+  RTC_CHECK(caller_ == nullptr);
+  RTC_CHECK(callee_ == nullptr);
+  field_trials_ = std::string(field_trials);
+}
+
+void PeerConnectionIntegrationTestBase::SetFieldTrials(
+    absl::string_view debug_name,
+    absl::string_view field_trials) {
+  RTC_CHECK(caller_ == nullptr);
+  RTC_CHECK(callee_ == nullptr);
+  field_trials_overrides_[std::string(debug_name)] = field_trials;
+}
+
+std::unique_ptr<PeerConnectionIntegrationWrapper>
+PeerConnectionIntegrationTestBase::CreatePeerConnectionWrapper(
+    const std::string& debug_name,
+    const PeerConnectionFactory::Options* options,
+    const RTCConfiguration* config,
+    PeerConnectionDependencies dependencies,
+    std::unique_ptr<FakeRtcEventLogFactory> event_log_factory,
+    bool reset_encoder_factory,
+    bool reset_decoder_factory,
+    bool create_media_engine) {
+  RTCConfiguration modified_config;
+  if (config) {
+    modified_config = *config;
+  }
+  modified_config.sdp_semantics = sdp_semantics_;
+  if (!dependencies.cert_generator) {
+    dependencies.cert_generator =
+        std::make_unique<FakeRTCCertificateGenerator>();
+  }
+  std::string field_trials = field_trials_;
+  EnvironmentFactory env = EnvironmentFactory(env_);
+
+  auto it = field_trials_overrides_.find(debug_name);
+  if (it != field_trials_overrides_.end()) {
+    field_trials = it->second;
+    dependencies.trials = std::make_unique<FieldTrials>(it->second);
+  }
+  env.Set(CreateTestFieldTrialsPtr(field_trials));
+
+  std::unique_ptr<PeerConnectionIntegrationWrapper> client =
+      CreatePeerConnectionWrapperInternal(debug_name, env.Create());
+
+  if (!client->Init(options, &modified_config, std::move(dependencies),
+                    fss_.get(), network_thread_.get(), worker_thread_.get(),
+                    std::move(event_log_factory), reset_encoder_factory,
+                    reset_decoder_factory, create_media_engine)) {
+    return nullptr;
+  }
+  return client;
+}
+
+std::unique_ptr<PeerConnectionIntegrationWrapper>
+PeerConnectionIntegrationTestBase::
+    CreatePeerConnectionWrapperWithFakeRtcEventLog(
+        const std::string& debug_name,
+        const PeerConnectionFactory::Options* options,
+        const RTCConfiguration* config,
+        PeerConnectionDependencies dependencies) {
+  return CreatePeerConnectionWrapper(debug_name, options, config,
+                                     std::move(dependencies),
+                                     std::make_unique<FakeRtcEventLogFactory>(),
+                                     /*reset_encoder_factory=*/false,
+                                     /*reset_decoder_factory=*/false);
+}
+
+bool PeerConnectionIntegrationTestBase::
+    CreatePeerConnectionWrappersWithSdpSemantics(
+        SdpSemantics caller_semantics,
+        SdpSemantics callee_semantics) {
+  // Can't specify the sdp_semantics in the passed-in configuration since it
+  // will be overwritten by CreatePeerConnectionWrapper with whatever is
+  // stored in sdp_semantics_. So get around this by modifying the instance
+  // variable before calling CreatePeerConnectionWrapper for the caller and
+  // callee PeerConnections.
+  SdpSemantics original_semantics = sdp_semantics_;
+  sdp_semantics_ = caller_semantics;
+  caller_ =
+      CreatePeerConnectionWrapper(kCallerName, nullptr, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/false,
+                                  /*reset_decoder_factory=*/false);
+  sdp_semantics_ = callee_semantics;
+  callee_ =
+      CreatePeerConnectionWrapper(kCalleeName, nullptr, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/false,
+                                  /*reset_decoder_factory=*/false);
+  sdp_semantics_ = original_semantics;
+  return caller_ && callee_;
+}
+
+bool PeerConnectionIntegrationTestBase::CreatePeerConnectionWrappersWithConfig(
+    const PeerConnectionInterface::RTCConfiguration& caller_config,
+    const PeerConnectionInterface::RTCConfiguration& callee_config,
+    bool create_media_engine) {
+  caller_ = CreatePeerConnectionWrapper(
+      kCallerName, nullptr, &caller_config, PeerConnectionDependencies(nullptr),
+      nullptr,
+      /*reset_encoder_factory=*/false,
+      /*reset_decoder_factory=*/false, create_media_engine);
+  callee_ = CreatePeerConnectionWrapper(
+      kCalleeName, nullptr, &callee_config, PeerConnectionDependencies(nullptr),
+      nullptr,
+      /*reset_encoder_factory=*/false,
+      /*reset_decoder_factory=*/false, create_media_engine);
+  return caller_ && callee_;
+}
+
+bool PeerConnectionIntegrationTestBase::
+    CreatePeerConnectionWrappersWithConfigAndDeps(
+        const PeerConnectionInterface::RTCConfiguration& caller_config,
+        PeerConnectionDependencies caller_dependencies,
+        const PeerConnectionInterface::RTCConfiguration& callee_config,
+        PeerConnectionDependencies callee_dependencies) {
+  caller_ = CreatePeerConnectionWrapper(kCallerName, nullptr, &caller_config,
+                                        std::move(caller_dependencies), nullptr,
+                                        /*reset_encoder_factory=*/false,
+                                        /*reset_decoder_factory=*/false);
+  callee_ = CreatePeerConnectionWrapper(kCalleeName, nullptr, &callee_config,
+                                        std::move(callee_dependencies), nullptr,
+                                        /*reset_encoder_factory=*/false,
+                                        /*reset_decoder_factory=*/false);
+  return caller_ && callee_;
+}
+
+bool PeerConnectionIntegrationTestBase::CreatePeerConnectionWrappersWithOptions(
+    const PeerConnectionFactory::Options& caller_options,
+    const PeerConnectionFactory::Options& callee_options) {
+  caller_ =
+      CreatePeerConnectionWrapper(kCallerName, &caller_options, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/false,
+                                  /*reset_decoder_factory=*/false);
+  callee_ =
+      CreatePeerConnectionWrapper(kCalleeName, &callee_options, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/false,
+                                  /*reset_decoder_factory=*/false);
+  return caller_ && callee_;
+}
+
+bool PeerConnectionIntegrationTestBase::
+    CreatePeerConnectionWrappersWithFakeRtcEventLog() {
+  PeerConnectionInterface::RTCConfiguration default_config;
+  caller_ = CreatePeerConnectionWrapperWithFakeRtcEventLog(
+      kCallerName, nullptr, &default_config,
+      PeerConnectionDependencies(nullptr));
+  callee_ = CreatePeerConnectionWrapperWithFakeRtcEventLog(
+      kCalleeName, nullptr, &default_config,
+      PeerConnectionDependencies(nullptr));
+  return caller_ && callee_;
+}
+
+std::unique_ptr<PeerConnectionIntegrationWrapper>
+PeerConnectionIntegrationTestBase::
+    CreatePeerConnectionWrapperWithAlternateKey() {
+  std::unique_ptr<FakeRTCCertificateGenerator> cert_generator(
+      new FakeRTCCertificateGenerator());
+  cert_generator->use_alternate_key();
+
+  PeerConnectionDependencies dependencies(nullptr);
+  dependencies.cert_generator = std::move(cert_generator);
+  return CreatePeerConnectionWrapper("New Peer", nullptr, nullptr,
+                                     std::move(dependencies), nullptr,
+                                     /*reset_encoder_factory=*/false,
+                                     /*reset_decoder_factory=*/false);
+}
+
+bool PeerConnectionIntegrationTestBase::
+    CreateOneDirectionalPeerConnectionWrappers(bool caller_to_callee) {
+  caller_ =
+      CreatePeerConnectionWrapper(kCallerName, nullptr, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/!caller_to_callee,
+                                  /*reset_decoder_factory=*/caller_to_callee);
+  callee_ =
+      CreatePeerConnectionWrapper(kCalleeName, nullptr, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/caller_to_callee,
+                                  /*reset_decoder_factory=*/!caller_to_callee);
+  return caller_ && callee_;
+}
+
+bool PeerConnectionIntegrationTestBase::
+    CreatePeerConnectionWrappersWithoutMediaEngine() {
+  caller_ =
+      CreatePeerConnectionWrapper(kCallerName, nullptr, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/false,
+                                  /*reset_decoder_factory=*/false,
+                                  /*create_media_engine=*/false);
+  callee_ =
+      CreatePeerConnectionWrapper(kCalleeName, nullptr, nullptr,
+                                  PeerConnectionDependencies(nullptr), nullptr,
+                                  /*reset_encoder_factory=*/false,
+                                  /*reset_decoder_factory=*/false,
+                                  /*create_media_engine=*/false);
+  return caller_ && callee_;
+}
+
+TestTurnServer* PeerConnectionIntegrationTestBase::CreateTurnServer(
+    SocketAddress internal_address,
+    SocketAddress external_address,
+    ProtocolType type,
+    const std::string& common_name) {
+  Thread* thread = network_thread();
+  SocketFactory* socket_factory = fss_.get();
+  std::unique_ptr<TestTurnServer> turn_server;
+  SendTask(network_thread(), [&] {
+    turn_server = std::make_unique<TestTurnServer>(
+        CreateTestEnvironment(), thread, socket_factory, internal_address,
+        external_address, type,
+        /*ignore_bad_certs=*/true, common_name);
+  });
+  turn_servers_.push_back(std::move(turn_server));
+  // Interactions with the turn server should be done on the network thread.
+  return turn_servers_.back().get();
+}
+
+TestTurnCustomizer* PeerConnectionIntegrationTestBase::CreateTurnCustomizer() {
+  std::unique_ptr<TestTurnCustomizer> turn_customizer;
+  SendTask(network_thread(),
+           [&] { turn_customizer = std::make_unique<TestTurnCustomizer>(); });
+  turn_customizers_.push_back(std::move(turn_customizer));
+  // Interactions with the turn customizer should be done on the network
+  // thread.
+  return turn_customizers_.back().get();
+}
+
+void PeerConnectionIntegrationTestBase::ExpectTurnCustomizerCountersIncremented(
+    TestTurnCustomizer* turn_customizer) {
+  SendTask(network_thread(), [turn_customizer] {
+    EXPECT_GT(turn_customizer->allow_channel_data_cnt_, 0u);
+    EXPECT_GT(turn_customizer->modify_cnt_, 0u);
+  });
+}
+
+void PeerConnectionIntegrationTestBase::ConnectFakeSignaling() {
+  caller_->set_signaling_message_receiver(callee_.get());
+  callee_->set_signaling_message_receiver(caller_.get());
+}
+
+void PeerConnectionIntegrationTestBase::ConnectFakeSignalingForSdpOnly() {
+  ConnectFakeSignaling();
+  SetSignalIceCandidates(false);
+}
+
+void PeerConnectionIntegrationTestBase::SetSignalingDelayMs(int delay_ms) {
+  caller_->set_signaling_delay_ms(delay_ms);
+  callee_->set_signaling_delay_ms(delay_ms);
+}
+
+void PeerConnectionIntegrationTestBase::SetSignalIceCandidates(bool signal) {
+  caller_->set_signal_ice_candidates(signal);
+  callee_->set_signal_ice_candidates(signal);
+}
+
+void PeerConnectionIntegrationTestBase::SendRtpDataWithRetries(
+    DataChannelInterface* dc,
+    const std::string& data,
+    int retries) {
+  for (int i = 0; i < retries; ++i) {
+    dc->Send(DataBuffer(data));
+  }
+}
+
 void PeerConnectionIntegrationTestBase::DestroyTurnServers() {
   ExecuteTask(*network_thread(), [this] {
     turn_servers_.clear();
@@ -513,6 +1506,206 @@
       std::make_unique<ScopedSetLoggingLevel>(new_severity);
 }
 
+void PeerConnectionIntegrationTestBase::DestroyPeerConnections() {
+  if (caller_) {
+    caller_->set_signaling_message_receiver(nullptr);
+    caller_->pc()->Close();
+  }
+  if (callee_) {
+    callee_->set_signaling_message_receiver(nullptr);
+    callee_->pc()->Close();
+  }
+  caller_.reset();
+  callee_.reset();
+}
+
+PeerConnectionIntegrationWrapper*
+PeerConnectionIntegrationTestBase::SetCallerPcWrapperAndReturnCurrent(
+    std::unique_ptr<PeerConnectionIntegrationWrapper> wrapper) {
+  PeerConnectionIntegrationWrapper* old = caller_.release();
+  caller_ = std::move(wrapper);
+  return old;
+}
+
+PeerConnectionIntegrationWrapper*
+PeerConnectionIntegrationTestBase::SetCalleePcWrapperAndReturnCurrent(
+    std::unique_ptr<PeerConnectionIntegrationWrapper> wrapper) {
+  PeerConnectionIntegrationWrapper* old = callee_.release();
+  callee_ = std::move(wrapper);
+  return old;
+}
+
+bool PeerConnectionIntegrationTestBase::ExpectNewFrames(
+    const MediaExpectations& media_expectations) {
+  // Make sure there are no bogus tracks confusing the issue.
+  caller()->RemoveUnusedVideoRenderers();
+  callee()->RemoveUnusedVideoRenderers();
+  // First initialize the expected frame counts based upon the current
+  // frame count.
+  int total_caller_audio_frames_expected = caller()->audio_frames_received();
+  if (media_expectations.caller_audio_expectation_ ==
+      MediaExpectations::kExpectSomeFrames) {
+    total_caller_audio_frames_expected +=
+        media_expectations.caller_audio_frames_expected_;
+  }
+  int total_caller_video_frames_expected =
+      caller()->min_video_frames_received_per_track();
+  if (media_expectations.caller_video_expectation_ ==
+      MediaExpectations::kExpectSomeFrames) {
+    total_caller_video_frames_expected +=
+        media_expectations.caller_video_frames_expected_;
+  }
+  int total_callee_audio_frames_expected = callee()->audio_frames_received();
+  if (media_expectations.callee_audio_expectation_ ==
+      MediaExpectations::kExpectSomeFrames) {
+    total_callee_audio_frames_expected +=
+        media_expectations.callee_audio_frames_expected_;
+  }
+  int total_callee_video_frames_expected =
+      callee()->min_video_frames_received_per_track();
+  if (media_expectations.callee_video_expectation_ ==
+      MediaExpectations::kExpectSomeFrames) {
+    total_callee_video_frames_expected +=
+        media_expectations.callee_video_frames_expected_;
+  }
+
+  // Wait for the expected frames.
+  EXPECT_THAT(
+      GetWaiter({.timeout = kMaxWaitForFrames})
+          .Until(
+              [&] {
+                return caller()->audio_frames_received() >=
+                           total_caller_audio_frames_expected &&
+                       caller()->min_video_frames_received_per_track() >=
+                           total_caller_video_frames_expected &&
+                       callee()->audio_frames_received() >=
+                           total_callee_audio_frames_expected &&
+                       callee()->min_video_frames_received_per_track() >=
+                           total_callee_video_frames_expected;
+              },
+              ::testing::IsTrue()),
+      IsRtcOk());
+  bool expectations_correct =
+      caller()->audio_frames_received() >= total_caller_audio_frames_expected &&
+      caller()->min_video_frames_received_per_track() >=
+          total_caller_video_frames_expected &&
+      callee()->audio_frames_received() >= total_callee_audio_frames_expected &&
+      callee()->min_video_frames_received_per_track() >=
+          total_callee_video_frames_expected;
+
+  // After the combined wait, print out a more detailed message upon
+  // failure.
+  EXPECT_GE(caller()->audio_frames_received(),
+            total_caller_audio_frames_expected);
+  EXPECT_GE(caller()->min_video_frames_received_per_track(),
+            total_caller_video_frames_expected);
+  EXPECT_GE(callee()->audio_frames_received(),
+            total_callee_audio_frames_expected);
+  EXPECT_GE(callee()->min_video_frames_received_per_track(),
+            total_callee_video_frames_expected);
+
+  // We want to make sure nothing unexpected was received.
+  if (media_expectations.caller_audio_expectation_ ==
+      MediaExpectations::kExpectNoFrames) {
+    EXPECT_EQ(caller()->audio_frames_received(),
+              total_caller_audio_frames_expected);
+    if (caller()->audio_frames_received() !=
+        total_caller_audio_frames_expected) {
+      expectations_correct = false;
+    }
+  }
+  if (media_expectations.caller_video_expectation_ ==
+      MediaExpectations::kExpectNoFrames) {
+    EXPECT_EQ(caller()->min_video_frames_received_per_track(),
+              total_caller_video_frames_expected);
+    if (caller()->min_video_frames_received_per_track() !=
+        total_caller_video_frames_expected) {
+      expectations_correct = false;
+    }
+  }
+  if (media_expectations.callee_audio_expectation_ ==
+      MediaExpectations::kExpectNoFrames) {
+    EXPECT_EQ(callee()->audio_frames_received(),
+              total_callee_audio_frames_expected);
+    if (callee()->audio_frames_received() !=
+        total_callee_audio_frames_expected) {
+      expectations_correct = false;
+    }
+  }
+  if (media_expectations.callee_video_expectation_ ==
+      MediaExpectations::kExpectNoFrames) {
+    EXPECT_EQ(callee()->min_video_frames_received_per_track(),
+              total_callee_video_frames_expected);
+    if (callee()->min_video_frames_received_per_track() !=
+        total_callee_video_frames_expected) {
+      expectations_correct = false;
+    }
+  }
+  return expectations_correct;
+}
+
+void PeerConnectionIntegrationTestBase::ClosePeerConnections() {
+  if (caller())
+    caller()->pc()->Close();
+  if (callee())
+    callee()->pc()->Close();
+}
+
+void PeerConnectionIntegrationTestBase::TestNegotiatedCipherSuite(
+    const RTCConfiguration& caller_config,
+    const RTCConfiguration& callee_config,
+    int expected_cipher_suite) {
+  ASSERT_TRUE(
+      CreatePeerConnectionWrappersWithConfig(caller_config, callee_config));
+  ConnectFakeSignaling();
+  caller()->AddAudioVideoTracks();
+  callee()->AddAudioVideoTracks();
+  caller()->CreateAndSetAndSignalOffer();
+  WaitUntilSettings settings;
+  settings.polling_interval = TimeDelta::Millis(100);
+  if (auto* tc = time_controller()) {
+    settings.clock = tc;
+  }
+  ASSERT_THAT(GetWaiter(settings).Until([&] { return DtlsConnected(); },
+                                        ::testing::IsTrue()),
+              IsRtcOk());
+  EXPECT_THAT(GetWaiter(settings).Until(
+                  [&] {
+                    auto report = caller()->NewGetStats(settings);
+                    if (!report) {
+                      return false;
+                    }
+                    auto transport_stats =
+                        report->GetStatsOfType<RTCTransportStats>();
+                    if (transport_stats.empty()) {
+                      return false;
+                    }
+                    return *transport_stats[0]->srtp_cipher ==
+                           SrtpCryptoSuiteToName(expected_cipher_suite);
+                  },
+                  ::testing::IsTrue()),
+              IsRtcOk());
+}
+
+void PeerConnectionIntegrationTestBase::TestGcmNegotiationUsesCipherSuite(
+    bool local_gcm_enabled,
+    bool remote_gcm_enabled,
+    bool aes_ctr_enabled,
+    int expected_cipher_suite) {
+  RTCConfiguration caller_config;
+  CryptoOptions caller_crypto;
+  caller_crypto.srtp.enable_gcm_crypto_suites = local_gcm_enabled;
+  caller_crypto.srtp.enable_aes128_sha1_80_crypto_cipher = aes_ctr_enabled;
+  caller_config.crypto_options = caller_crypto;
+  RTCConfiguration callee_config;
+  CryptoOptions callee_crypto;
+  callee_crypto.srtp.enable_gcm_crypto_suites = remote_gcm_enabled;
+  callee_crypto.srtp.enable_aes128_sha1_80_crypto_cipher = aes_ctr_enabled;
+  callee_config.crypto_options = callee_crypto;
+  TestNegotiatedCipherSuite(caller_config, callee_config,
+                            expected_cipher_suite);
+}
+
 std::unique_ptr<PeerConnectionIntegrationWrapper>
 PeerConnectionIntegrationTestBase::CreatePeerConnectionWrapperInternal(
     const std::string& debug_name,
@@ -528,8 +1721,34 @@
     : internal::PeerConnectionIntegrationTestBase(CreateTestEnvironment(),
                                                   sdp_semantics) {}
 
-PeerConnectionIntegrationTestWithSimulatedTime::
+PeerConnectionIntegrationBaseTest::~PeerConnectionIntegrationBaseTest() {
+  DestroyPeerConnections();
+  DestroyTurnServers();
+}
 
+void PeerConnectionIntegrationBaseTest::ExecuteTask(
+    TaskQueueBase& task_queue,
+    absl::AnyInvocable<void()> task) {
+  task_queue.PostTask([this, task = std::move(task)]() mutable {
+    task();
+    run_loop_.task_queue()->PostTask(run_loop_.QuitClosure());
+  });
+  run_loop_.Run();
+}
+
+Waiter PeerConnectionIntegrationBaseTest::GetWaiter(
+    WaitUntilSettings overrides) {
+  return Waiter(overrides);
+}
+
+std::unique_ptr<Thread> PeerConnectionIntegrationBaseTest::CreateThread(
+    absl::string_view name) {
+  auto thread = Thread::Create();
+  thread->Start();
+  return thread;
+}
+
+PeerConnectionIntegrationTestWithSimulatedTime::
     PeerConnectionIntegrationTestWithSimulatedTime(SdpSemantics sdp_semantics)
     : PeerConnectionIntegrationTestWithSimulatedTime(
           sdp_semantics,
@@ -549,6 +1768,25 @@
   DestroyThreads();
 }
 
+void PeerConnectionIntegrationTestWithSimulatedTime::ExecuteTask(
+    TaskQueueBase& task_queue,
+    absl::AnyInvocable<void()> task) {
+  task_queue.PostTask(std::move(task));
+  time_controller_->AdvanceTime(TimeDelta::Zero());
+}
+
+Waiter PeerConnectionIntegrationTestWithSimulatedTime::GetWaiter(
+    WaitUntilSettings overrides) {
+  overrides.clock = time_controller_.get();
+  return Waiter(overrides);
+}
+
+std::unique_ptr<Thread>
+PeerConnectionIntegrationTestWithSimulatedTime::CreateThread(
+    absl::string_view name) {
+  return time_controller_->CreateThread(std::string(name), nullptr);
+}
+
 PeerConnectionIntegrationTestWithSimulatedTime::
     PeerConnectionIntegrationTestWithSimulatedTime(
         SdpSemantics sdp_semantics,
diff --git a/pc/test/integration_test_helpers.h b/pc/test/integration_test_helpers.h
index 28d393f..fe59699 100644
--- a/pc/test/integration_test_helpers.h
+++ b/pc/test/integration_test_helpers.h
@@ -15,13 +15,11 @@
 #include <stdint.h>
 #include <stdio.h>
 
-#include <algorithm>
 #include <functional>
 #include <limits>
 #include <map>
 #include <memory>
 #include <optional>
-#include <set>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -29,14 +27,10 @@
 
 #include "absl/functional/any_invocable.h"
 #include "absl/strings/string_view.h"
-#include "api/audio_options.h"
 #include "api/candidate.h"
-#include "api/crypto/crypto_options.h"
 #include "api/data_channel_interface.h"
 #include "api/dtls_transport_interface.h"
 #include "api/environment/environment.h"
-#include "api/environment/environment_factory.h"
-#include "api/field_trials.h"
 #include "api/ice_transport_interface.h"
 #include "api/jsep.h"
 #include "api/make_ref_counted.h"
@@ -44,12 +38,9 @@
 #include "api/media_types.h"
 #include "api/metronome/metronome.h"
 #include "api/peer_connection_interface.h"
-#include "api/rtc_error.h"
 #include "api/rtc_event_log_output.h"
-#include "api/rtp_parameters.h"
 #include "api/rtp_receiver_interface.h"
 #include "api/rtp_sender_interface.h"
-#include "api/rtp_transceiver_direction.h"
 #include "api/rtp_transceiver_interface.h"
 #include "api/scoped_refptr.h"
 #include "api/sequence_checker.h"
@@ -58,10 +49,8 @@
 #include "api/task_queue/pending_task_safety_flag.h"
 #include "api/task_queue/task_queue_base.h"
 #include "api/test/mock_async_dns_resolver.h"
-#include "api/test/rtc_error_matchers.h"
 #include "api/test/time_controller.h"
 #include "api/units/time_delta.h"
-#include "api/units/timestamp.h"
 #include "api/video/video_rotation.h"
 #include "logging/rtc_event_log/fake_rtc_event_log_factory.h"
 #include "media/base/stream_params.h"
@@ -74,35 +63,23 @@
 #include "p2p/test/test_turn_server.h"
 #include "pc/peer_connection.h"
 #include "pc/peer_connection_factory.h"
-#include "pc/peer_connection_proxy.h"
 #include "pc/session_description.h"
 #include "pc/test/fake_audio_capture_module.h"
 #include "pc/test/fake_periodic_video_source.h"
-#include "pc/test/fake_periodic_video_track_source.h"
-#include "pc/test/fake_rtc_certificate_generator.h"
 #include "pc/test/fake_video_track_renderer.h"
 #include "pc/test/mock_peer_connection_observers.h"
-#include "pc/test/rtc_stats_obtainer.h"
 #include "pc/video_track_source.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/crypto_random.h"
 #include "rtc_base/fake_mdns_responder.h"
 #include "rtc_base/fake_network.h"
 #include "rtc_base/firewall_socket_server.h"
-#include "rtc_base/ip_address.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/net_helper.h"
 #include "rtc_base/socket_address.h"
-#include "rtc_base/socket_factory.h"
 #include "rtc_base/socket_server.h"
 #include "rtc_base/ssl_stream_adapter.h"
 #include "rtc_base/system/plan_b_only.h"
-#include "rtc_base/task_queue_for_test.h"
 #include "rtc_base/thread.h"
 #include "rtc_base/virtual_socket_server.h"
-#include "system_wrappers/include/clock.h"
-#include "test/create_test_environment.h"
-#include "test/create_test_field_trials.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 #include "test/run_loop.h"
@@ -261,8 +238,7 @@
   explicit PeerConnectionIntegrationWrapper(
       const std::string& debug_name,
       Environment env,
-      internal::PeerConnectionIntegrationTestBase* test)
-      : debug_name_(debug_name), env_(env), test_(test) {}
+      internal::PeerConnectionIntegrationTestBase* test);
 
   PeerConnectionFactoryInterface* pc_factory() const {
     return peer_connection_factory_.get();
@@ -272,21 +248,12 @@
 
   // Return the PC implementation, so that non-public interfaces
   // can be used in tests.
-  PeerConnection* pc_internal() const {
-    auto* pci =
-        static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
-            pc());
-    return static_cast<PeerConnection*>(pci->internal());
-  }
+  PeerConnection* pc_internal() const;
 
   // If a signaling message receiver is set (via ConnectFakeSignaling), this
   // will set the whole offer/answer exchange in motion. Just need to wait for
   // the signaling state to reach "stable".
-  void CreateAndSetAndSignalOffer() {
-    std::unique_ptr<SessionDescriptionInterface> offer = CreateOfferAndWait();
-    ASSERT_NE(nullptr, offer);
-    EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(offer)));
-  }
+  void CreateAndSetAndSignalOffer();
 
   // Sets the options to be used when CreateAndSetAndSignalOffer is called, or
   // when a remote offer is received (via fake signaling) and an answer is
@@ -364,11 +331,7 @@
     return peer_connection_signaling_state_history_;
   }
 
-  void AddAudioVideoTracks() {
-    AddAudioTrack();
-    AddVideoTrack();
-    ResetRtpSenderObservers();
-  }
+  void AddAudioVideoTracks();
 
   scoped_refptr<RtpSenderInterface> AddAudioTrack() {
     return AddTrack(CreateLocalAudioTrack());
@@ -378,129 +341,52 @@
     return AddTrack(CreateLocalVideoTrack());
   }
 
-  scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack() {
-    AudioOptions options;
-    // Disable highpass filter so that we can get all the test audio frames.
-    options.highpass_filter = false;
-    scoped_refptr<AudioSourceInterface> source =
-        peer_connection_factory_->CreateAudioSource(options);
-    // TODO(perkj): Test audio source when it is implemented. Currently audio
-    // always use the default input.
-    return peer_connection_factory_->CreateAudioTrack(CreateRandomUuid(),
-                                                      source.get());
-  }
+  scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack();
 
-  scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack() {
-    FakePeriodicVideoSource::Config config;
-    config.timestamp_offset = env_.clock().CurrentTime();
-    return CreateLocalVideoTrackInternal(config);
-  }
+  scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack();
 
   scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackWithConfig(
-      FakePeriodicVideoSource::Config config) {
-    return CreateLocalVideoTrackInternal(config);
-  }
+      FakePeriodicVideoSource::Config config);
 
   scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackWithRotation(
-      VideoRotation rotation) {
-    FakePeriodicVideoSource::Config config;
-    config.rotation = rotation;
-    config.timestamp_offset = env_.clock().CurrentTime();
-    return CreateLocalVideoTrackInternal(config);
-  }
+      VideoRotation rotation);
 
   scoped_refptr<RtpSenderInterface> AddTrack(
       scoped_refptr<MediaStreamTrackInterface> track,
-      const std::vector<std::string>& stream_ids = {}) {
-    EXPECT_TRUE(track);
-    if (!track) {
-      return nullptr;
-    }
-    auto result = pc()->AddTrack(track, stream_ids);
-    EXPECT_EQ(RTCErrorType::NONE, result.error().type());
-    if (result.ok()) {
-      return result.MoveValue();
-    } else {
-      return nullptr;
-    }
-  }
+      const std::vector<std::string>& stream_ids = {});
 
   std::vector<scoped_refptr<RtpReceiverInterface>> GetReceiversOfType(
-      webrtc::MediaType media_type) {
-    std::vector<scoped_refptr<RtpReceiverInterface>> receivers;
-    for (const auto& receiver : pc()->GetReceivers()) {
-      if (receiver->media_type() == media_type) {
-        receivers.push_back(receiver);
-      }
-    }
-    return receivers;
-  }
+      webrtc::MediaType media_type);
 
   scoped_refptr<RtpTransceiverInterface> GetFirstTransceiverOfType(
-      webrtc::MediaType media_type) {
-    for (auto transceiver : pc()->GetTransceivers()) {
-      if (transceiver->receiver()->media_type() == media_type) {
-        return transceiver;
-      }
-    }
-    return nullptr;
-  }
+      webrtc::MediaType media_type);
 
-  bool SignalingStateStable() {
-    return pc()->signaling_state() == PeerConnectionInterface::kStable;
-  }
+  bool SignalingStateStable();
 
-  bool IceGatheringStateComplete() {
-    return pc()->ice_gathering_state() ==
-           PeerConnectionInterface::kIceGatheringComplete;
-  }
+  bool IceGatheringStateComplete();
 
-  void CreateDataChannel() { CreateDataChannel(nullptr); }
+  void CreateDataChannel();
 
-  void CreateDataChannel(const DataChannelInit* init) {
-    CreateDataChannel(kDataChannelLabel, init);
-  }
+  void CreateDataChannel(const DataChannelInit* init);
 
-  void CreateDataChannel(const std::string& label,
-                         const DataChannelInit* init) {
-    auto data_channel_or_error = pc()->CreateDataChannelOrError(label, init);
-    ASSERT_TRUE(data_channel_or_error.ok());
-    data_channels_.push_back(data_channel_or_error.MoveValue());
-    ASSERT_TRUE(data_channels_.back().get() != nullptr);
-    data_observers_.push_back(
-        std::make_unique<MockDataChannelObserver>(data_channels_.back().get()));
-  }
+  void CreateDataChannel(const std::string& label, const DataChannelInit* init);
 
   // Return the last observed data channel.
-  DataChannelInterface* data_channel() {
-    if (data_channels_.empty()) {
-      return nullptr;
-    }
-    return data_channels_.back().get();
-  }
+  DataChannelInterface* data_channel();
   // Return all data channels.
   std::vector<scoped_refptr<DataChannelInterface>>& data_channels() {
     return data_channels_;
   }
 
-  MockDataChannelObserver* data_observer() const {
-    if (data_observers_.empty()) {
-      return nullptr;
-    }
-    return data_observers_.back().get();
-  }
+  MockDataChannelObserver* data_observer() const;
 
   std::vector<std::unique_ptr<MockDataChannelObserver>>& data_observers() {
     return data_observers_;
   }
 
-  std::unique_ptr<SessionDescriptionInterface> CreateAnswerForTest() {
-    return CreateAnswer();
-  }
+  std::unique_ptr<SessionDescriptionInterface> CreateAnswerForTest();
 
-  int audio_frames_received() const {
-    return fake_audio_capture_module_->frames_received();
-  }
+  int audio_frames_received() const;
 
   // Takes minimum of video frames received for each track.
   //
@@ -509,17 +395,7 @@
   //
   // To ensure that all video tracks received at least a certain number of
   // frames.
-  int min_video_frames_received_per_track() const {
-    int min_frames = INT_MAX;
-    if (fake_video_renderers_.empty()) {
-      return 0;
-    }
-
-    for (const auto& pair : fake_video_renderers_) {
-      min_frames = std::min(min_frames, pair.second->num_rendered_frames());
-    }
-    return min_frames;
-  }
+  int min_video_frames_received_per_track() const;
 
   // Returns a MockStatsObserver in a state after stats gathering finished,
   // which can be used to access the gathered stats.
@@ -527,9 +403,7 @@
       MediaStreamTrackInterface* track);
 
   // Version that doesn't take a track "filter", and gathers all stats.
-  scoped_refptr<MockStatsObserver> OldGetStats() {
-    return OldGetStatsForTrack(nullptr);
-  }
+  scoped_refptr<MockStatsObserver> OldGetStats();
 
   // Synchronously gets stats and returns them. If it times out, fails the test
   // and returns null.
@@ -539,118 +413,40 @@
   scoped_refptr<const RTCStatsReport> NewGetStats(
       WaitUntilSettings settings = {});
 
-  scoped_refptr<const RTCStatsReport> NewGetStats(test::RunLoop& run_loop) {
-    scoped_refptr<const RTCStatsReport> report;
-    auto callback = RTCStatsObtainer::Create(&report, run_loop.QuitClosure());
-    peer_connection_->GetStats(callback.get());
-    run_loop.Run();
-    EXPECT_TRUE(report);
-    return report;
-  }
+  scoped_refptr<const RTCStatsReport> NewGetStats(test::RunLoop& run_loop);
 
-  std::string DtlsCipher() {
-    auto report = NewGetStats();
-    if (!report)
-      return "";
-    auto stats = report->GetStatsOfType<RTCTransportStats>();
-    if (stats.empty() || !stats[0]->dtls_cipher.has_value())
-      return "";
-    return *stats[0]->dtls_cipher;
-  }
+  std::string DtlsCipher();
 
-  std::string SrtpCipher() {
-    auto report = NewGetStats();
-    if (!report)
-      return "";
-    auto stats = report->GetStatsOfType<RTCTransportStats>();
-    if (stats.empty() || !stats[0]->srtp_cipher.has_value())
-      return "";
-    return *stats[0]->srtp_cipher;
-  }
+  std::string SrtpCipher();
 
-  int rendered_width() {
-    EXPECT_FALSE(fake_video_renderers_.empty());
-    return fake_video_renderers_.empty()
-               ? 0
-               : fake_video_renderers_.begin()->second->width();
-  }
+  int rendered_width();
 
-  int rendered_height() {
-    EXPECT_FALSE(fake_video_renderers_.empty());
-    return fake_video_renderers_.empty()
-               ? 0
-               : fake_video_renderers_.begin()->second->height();
-  }
+  int rendered_height();
 
-  double rendered_aspect_ratio() {
-    if (rendered_height() == 0) {
-      return 0.0;
-    }
-    return static_cast<double>(rendered_width()) / rendered_height();
-  }
+  double rendered_aspect_ratio();
 
-  VideoRotation rendered_rotation() {
-    EXPECT_FALSE(fake_video_renderers_.empty());
-    return fake_video_renderers_.empty()
-               ? kVideoRotation_0
-               : fake_video_renderers_.begin()->second->rotation();
-  }
+  VideoRotation rendered_rotation();
 
-  int local_rendered_width() {
-    return local_video_renderer_ ? local_video_renderer_->width() : 0;
-  }
+  int local_rendered_width();
 
-  int local_rendered_height() {
-    return local_video_renderer_ ? local_video_renderer_->height() : 0;
-  }
+  int local_rendered_height();
 
-  double local_rendered_aspect_ratio() {
-    if (local_rendered_height() == 0) {
-      return 0.0;
-    }
-    return static_cast<double>(local_rendered_width()) /
-           local_rendered_height();
-  }
+  double local_rendered_aspect_ratio();
 
-  PLAN_B_ONLY size_t number_of_remote_streams() {
-    if (!pc()) {
-      return 0;
-    }
-    return pc()->remote_streams()->count();
-  }
+  PLAN_B_ONLY size_t number_of_remote_streams();
 
-  PLAN_B_ONLY StreamCollectionInterface* remote_streams() const {
-    if (!pc()) {
-      ADD_FAILURE();
-      return nullptr;
-    }
-    return pc()->remote_streams().get();
-  }
+  PLAN_B_ONLY StreamCollectionInterface* remote_streams() const;
 
-  PLAN_B_ONLY StreamCollectionInterface* local_streams() {
-    if (!pc()) {
-      ADD_FAILURE();
-      return nullptr;
-    }
-    return pc()->local_streams().get();
-  }
+  PLAN_B_ONLY StreamCollectionInterface* local_streams();
 
-  PeerConnectionInterface::SignalingState signaling_state() {
-    return pc()->signaling_state();
-  }
+  PeerConnectionInterface::SignalingState signaling_state();
 
-  PeerConnectionInterface::IceConnectionState ice_connection_state() {
-    return pc()->ice_connection_state();
-  }
+  PeerConnectionInterface::IceConnectionState ice_connection_state();
 
   PeerConnectionInterface::IceConnectionState
-  standardized_ice_connection_state() {
-    return pc()->standardized_ice_connection_state();
-  }
+  standardized_ice_connection_state();
 
-  PeerConnectionInterface::IceGatheringState ice_gathering_state() {
-    return pc()->ice_gathering_state();
-  }
+  PeerConnectionInterface::IceGatheringState ice_gathering_state();
 
   // Returns a MockRtpReceiverObserver for each RtpReceiver returned by
   // GetReceivers. They're updated automatically when a remote offer/answer
@@ -661,31 +457,14 @@
     return rtp_receiver_observers_;
   }
 
-  void ResetRtpReceiverObservers() {
-    rtp_receiver_observers_.clear();
-    for (const scoped_refptr<RtpReceiverInterface>& receiver :
-         pc()->GetReceivers()) {
-      std::unique_ptr<MockRtpReceiverObserver> observer(
-          new MockRtpReceiverObserver(receiver->media_type()));
-      receiver->SetObserver(observer.get());
-      rtp_receiver_observers_.push_back(std::move(observer));
-    }
-  }
+  void ResetRtpReceiverObservers();
 
   const std::vector<std::unique_ptr<MockRtpSenderObserver>>&
   rtp_sender_observers() {
     return rtp_sender_observers_;
   }
 
-  void ResetRtpSenderObservers() {
-    rtp_sender_observers_.clear();
-    for (const scoped_refptr<RtpSenderInterface>& sender : pc()->GetSenders()) {
-      std::unique_ptr<MockRtpSenderObserver> observer(
-          new MockRtpSenderObserver(sender->media_type()));
-      sender->SetObserver(observer.get());
-      rtp_sender_observers_.push_back(std::move(observer));
-    }
-  }
+  void ResetRtpSenderObservers();
 
   FakeNetworkManager* network_manager() const { return fake_network_manager_; }
 
@@ -693,12 +472,7 @@
     return event_log_factory_;
   }
 
-  Candidate last_candidate_gathered() const {
-    if (last_gathered_ice_candidate_) {
-      return last_gathered_ice_candidate_->candidate();
-    }
-    return Candidate();
-  }
+  Candidate last_candidate_gathered() const;
   const IceCandidate* last_gathered_ice_candidate() const {
     return last_gathered_ice_candidate_.get();
   }
@@ -706,17 +480,11 @@
 
   // Sets the mDNS responder for the owned fake network manager and keeps a
   // reference to the responder.
-  void SetMdnsResponder(std::unique_ptr<FakeMdnsResponder> mdns_responder) {
-    RTC_DCHECK(mdns_responder != nullptr);
-    mdns_responder_ = mdns_responder.get();
-    network_manager()->set_mdns_responder(std::move(mdns_responder));
-  }
+  void SetMdnsResponder(std::unique_ptr<FakeMdnsResponder> mdns_responder);
 
   // Returns null on failure.
   std::unique_ptr<SessionDescriptionInterface> CreateOfferAndWait();
-  bool Rollback() {
-    return SetRemoteDescription(CreateRollbackSessionDescription());
-  }
+  bool Rollback();
 
   // Functions for querying stats.
   void StartWatchingDelayStats();
@@ -734,63 +502,22 @@
 
   bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc);
 
-  void NegotiateCorruptionDetectionHeader() {
-    for (const auto& transceiver : pc()->GetTransceivers()) {
-      if (transceiver->media_type() != webrtc::MediaType::VIDEO) {
-        continue;
-      }
-      auto extensions = transceiver->GetHeaderExtensionsToNegotiate();
-      for (auto& extension : extensions) {
-        if (extension.uri == RtpExtension::kCorruptionDetectionUri) {
-          extension.direction = RtpTransceiverDirection::kSendRecv;
-        }
-      }
-      transceiver->SetHeaderExtensionsToNegotiate(extensions);
-    }
-  }
+  void NegotiateCorruptionDetectionHeader();
 
-  uint32_t GetCorruptionScoreCount() {
-    scoped_refptr<const RTCStatsReport> report = NewGetStats();
-    auto inbound_stream_stats =
-        report->GetStatsOfType<RTCInboundRtpStreamStats>();
-    for (const auto& stat : inbound_stream_stats) {
-      if (*stat->kind == "video") {
-        return stat->corruption_measurements.value_or(0);
-      }
-    }
-    return 0;
-  }
+  uint32_t GetCorruptionScoreCount();
 
-  uint32_t GetReceivedFrameCount() {
-    scoped_refptr<const RTCStatsReport> report = NewGetStats();
-    auto inbound_stream_stats =
-        report->GetStatsOfType<RTCInboundRtpStreamStats>();
-    for (const auto& stat : inbound_stream_stats) {
-      if (*stat->kind == "video") {
-        return stat->frames_received.value_or(0);
-      }
-    }
-    return 0;
-  }
+  uint32_t GetReceivedFrameCount();
 
   void set_connection_change_callback(
       std::function<void(PeerConnectionInterface::PeerConnectionState)> func) {
     connection_change_callback_ = std::move(func);
   }
 
-  std::optional<int> tls_version() {
-    return dtls_transport_information().tls_version();
-  }
+  std::optional<int> tls_version();
 
-  std::optional<DtlsTransportTlsRole> dtls_transport_role() {
-    return dtls_transport_information().role();
-  }
+  std::optional<DtlsTransportTlsRole> dtls_transport_role();
 
-  DtlsTransportInformation dtls_transport_information() {
-    return network_thread_->BlockingCall([&] {
-      return pc()->GetSctpTransport()->dtls_transport()->Information();
-    });
-  }
+  DtlsTransportInformation dtls_transport_information();
 
   // Setting the local description and sending the SDP message over the fake
   // signaling channel are combined into the same method because the SDP
@@ -814,27 +541,7 @@
 
   scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
       const PeerConnectionInterface::RTCConfiguration* config,
-      PeerConnectionDependencies dependencies) {
-    PeerConnectionInterface::RTCConfiguration modified_config;
-    modified_config.sdp_semantics = sdp_semantics_;
-    // If `config` is null, this will result in a default configuration being
-    // used.
-    if (config) {
-      modified_config = *config;
-    }
-    // Disable resolution adaptation; we don't want it interfering with the
-    // test results.
-    // TODO(deadbeef): Do something more robust. Since we're testing for aspect
-    // ratios and not specific resolutions, is this even necessary?
-    modified_config.set_cpu_adaptation(false);
-
-    dependencies.observer = this;
-    auto peer_connection_or_error =
-        peer_connection_factory_->CreatePeerConnectionOrError(
-            modified_config, std::move(dependencies));
-    return peer_connection_or_error.ok() ? peer_connection_or_error.MoveValue()
-                                         : nullptr;
-  }
+      PeerConnectionDependencies dependencies);
 
   void set_signaling_delay_ms(int delay_ms) { signaling_delay_ms_ = delay_ms; }
 
@@ -843,138 +550,34 @@
   }
 
   scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackInternal(
-      FakePeriodicVideoSource::Config config) {
-    // Set max frame rate to 10fps to reduce the risk of test flakiness.
-    // TODO(deadbeef): Do something more robust.
-    config.frame_interval = TimeDelta::Millis(100);
+      FakePeriodicVideoSource::Config config);
 
-    video_track_sources_.emplace_back(
-        make_ref_counted<FakePeriodicVideoTrackSource>(config,
-                                                       false /* remote */));
-    scoped_refptr<VideoTrackInterface> track =
-        peer_connection_factory_->CreateVideoTrack(video_track_sources_.back(),
-                                                   CreateRandomUuid());
-    if (!local_video_renderer_) {
-      local_video_renderer_.reset(new FakeVideoTrackRenderer(track.get()));
-    }
-    return track;
-  }
+  void HandleIncomingOffer(const std::string& msg);
 
-  void HandleIncomingOffer(const std::string& msg) {
-    RTC_LOG(LS_INFO) << debug_name_ << ": HandleIncomingOffer";
-    std::unique_ptr<SessionDescriptionInterface> desc =
-        CreateSessionDescription(SdpType::kOffer, msg);
-    if (received_sdp_munger_) {
-      received_sdp_munger_(desc);
-    }
-
-    EXPECT_TRUE(SetRemoteDescription(std::move(desc)));
-    // Setting a remote description may have changed the number of receivers,
-    // so reset the receiver observers.
-    ResetRtpReceiverObservers();
-    if (remote_offer_handler_) {
-      remote_offer_handler_();
-    }
-    std::unique_ptr<SessionDescriptionInterface> answer = CreateAnswer();
-    ASSERT_NE(nullptr, answer);
-    EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(answer)));
-  }
-
-  void HandleIncomingAnswer(SdpType type, const std::string& msg) {
-    RTC_LOG(LS_INFO) << debug_name_ << ": HandleIncomingAnswer of type "
-                     << type;
-    std::unique_ptr<SessionDescriptionInterface> desc =
-        CreateSessionDescription(type, msg);
-    if (received_sdp_munger_) {
-      received_sdp_munger_(desc);
-      if (!desc) {
-        // Answer was "taken" by munger...so that it can be applied later ?
-        RTC_LOG(LS_INFO) << debug_name_ << ": answer NOT applied";
-        return;
-      }
-    }
-    EXPECT_TRUE(SetRemoteDescription(std::move(desc)));
-    // Set the RtpReceiverObserver after receivers are created.
-    ResetRtpReceiverObservers();
-  }
+  void HandleIncomingAnswer(SdpType type, const std::string& msg);
 
   // Returns null on failure.
   std::unique_ptr<SessionDescriptionInterface> CreateAnswer();
 
   // This is a work around to remove unused fake_video_renderers from
   // transceivers that have either stopped or are no longer receiving.
-  void RemoveUnusedVideoRenderers() {
-    if (sdp_semantics_ != SdpSemantics::kUnifiedPlan) {
-      return;
-    }
-    auto transceivers = pc()->GetTransceivers();
-    std::set<std::string> active_renderers;
-    for (auto& transceiver : transceivers) {
-      // Note - we don't check for direction here. This function is called
-      // before direction is set, and in that case, we should not remove
-      // the renderer.
-      if (transceiver->receiver()->media_type() == webrtc::MediaType::VIDEO) {
-        active_renderers.insert(transceiver->receiver()->track()->id());
-      }
-    }
-    for (auto it = fake_video_renderers_.begin();
-         it != fake_video_renderers_.end();) {
-      // Remove fake video renderers belonging to any non-active transceivers.
-      if (!active_renderers.count(it->first)) {
-        it = fake_video_renderers_.erase(it);
-      } else {
-        it++;
-      }
-    }
-  }
+  void RemoveUnusedVideoRenderers();
 
   // Simulate sending a blob of SDP with delay `signaling_delay_ms_` (0 by
   // default).
-  void SendSdpMessage(SdpType type, const std::string& msg) {
-    if (signaling_delay_ms_ == 0) {
-      RelaySdpMessageIfReceiverExists(type, msg);
-    } else {
-      Thread::Current()->PostDelayedTask(
-          SafeTask(task_safety_.flag(),
-                   [this, type, msg] {
-                     RelaySdpMessageIfReceiverExists(type, msg);
-                   }),
-          TimeDelta::Millis(signaling_delay_ms_));
-    }
-  }
+  void SendSdpMessage(SdpType type, const std::string& msg);
 
-  void RelaySdpMessageIfReceiverExists(SdpType type, const std::string& msg) {
-    if (signaling_message_receiver_) {
-      signaling_message_receiver_->ReceiveSdpMessage(type, msg);
-    }
-  }
+  void RelaySdpMessageIfReceiverExists(SdpType type, const std::string& msg);
 
   // Simulate trickling an ICE candidate with delay `signaling_delay_ms_` (0 by
   // default).
   void SendIceMessage(const std::string& sdp_mid,
                       int sdp_mline_index,
-                      const std::string& msg) {
-    if (signaling_delay_ms_ == 0) {
-      RelayIceMessageIfReceiverExists(sdp_mid, sdp_mline_index, msg);
-    } else {
-      Thread::Current()->PostDelayedTask(
-          SafeTask(task_safety_.flag(),
-                   [this, sdp_mid, sdp_mline_index, msg] {
-                     RelayIceMessageIfReceiverExists(sdp_mid, sdp_mline_index,
-                                                     msg);
-                   }),
-          TimeDelta::Millis(signaling_delay_ms_));
-    }
-  }
+                      const std::string& msg);
 
   void RelayIceMessageIfReceiverExists(const std::string& sdp_mid,
                                        int sdp_mline_index,
-                                       const std::string& msg) {
-    if (signaling_message_receiver_) {
-      signaling_message_receiver_->ReceiveIceMessage(sdp_mid, sdp_mline_index,
-                                                     msg);
-    }
-  }
+                                       const std::string& msg);
 
   // SignalingMessageReceiver callbacks.
  public:
@@ -983,13 +586,7 @@
     signaling_message_receiver_ = signaling_message_receiver;
   }
 
-  void ReceiveSdpMessage(SdpType type, const std::string& msg) override {
-    if (type == SdpType::kOffer) {
-      HandleIncomingOffer(msg);
-    } else {
-      HandleIncomingAnswer(type, msg);
-    }
-  }
+  void ReceiveSdpMessage(SdpType type, const std::string& msg) override;
 
   void ReceiveIceMessage(const std::string& sdp_mid,
                          int sdp_mline_index,
@@ -998,125 +595,40 @@
  private:
   // PeerConnectionObserver callbacks.
   void OnSignalingChange(
-      PeerConnectionInterface::SignalingState new_state) override {
-    EXPECT_EQ(pc()->signaling_state(), new_state);
-    peer_connection_signaling_state_history_.push_back(new_state);
-  }
-  void OnAddTrack(scoped_refptr<RtpReceiverInterface> receiver,
-                  const std::vector<scoped_refptr<MediaStreamInterface>>&
-                      streams) override {
-    if (receiver->media_type() == webrtc::MediaType::VIDEO) {
-      scoped_refptr<VideoTrackInterface> video_track(
-          static_cast<VideoTrackInterface*>(receiver->track().get()));
-      ASSERT_TRUE(fake_video_renderers_.find(video_track->id()) ==
-                  fake_video_renderers_.end());
-      fake_video_renderers_[video_track->id()] =
-          std::make_unique<FakeVideoTrackRenderer>(video_track.get());
-    }
-  }
-  void OnRemoveTrack(scoped_refptr<RtpReceiverInterface> receiver) override {
-    if (receiver->media_type() == webrtc::MediaType::VIDEO) {
-      auto it = fake_video_renderers_.find(receiver->track()->id());
-      if (it != fake_video_renderers_.end()) {
-        fake_video_renderers_.erase(it);
-      } else {
-        RTC_LOG(LS_ERROR) << "OnRemoveTrack called for non-active renderer";
-      }
-    }
-  }
-  void OnRenegotiationNeeded() override {}
+      PeerConnectionInterface::SignalingState new_state) override;
+  void OnAddTrack(
+      scoped_refptr<RtpReceiverInterface> receiver,
+      const std::vector<scoped_refptr<MediaStreamInterface>>& streams) override;
+  void OnRemoveTrack(scoped_refptr<RtpReceiverInterface> receiver) override;
+  void OnRenegotiationNeeded() override;
   void OnIceConnectionChange(
-      PeerConnectionInterface::IceConnectionState new_state) override {
-    EXPECT_EQ(pc()->ice_connection_state(), new_state);
-    ice_connection_state_history_.push_back(new_state);
-  }
+      PeerConnectionInterface::IceConnectionState new_state) override;
   void OnStandardizedIceConnectionChange(
-      PeerConnectionInterface::IceConnectionState new_state) override {
-    standardized_ice_connection_state_history_.push_back(new_state);
-  }
+      PeerConnectionInterface::IceConnectionState new_state) override;
 
   void OnConnectionChange(
-      PeerConnectionInterface::PeerConnectionState new_state) override {
-    peer_connection_state_history_.push_back(new_state);
-    if (connection_change_callback_) {
-      connection_change_callback_(new_state);
-    }
-  }
+      PeerConnectionInterface::PeerConnectionState new_state) override;
 
   void OnIceGatheringChange(
-      PeerConnectionInterface::IceGatheringState new_state) override {
-    EXPECT_EQ(pc()->ice_gathering_state(), new_state);
-    ice_gathering_state_history_.push_back(new_state);
-  }
+      PeerConnectionInterface::IceGatheringState new_state) override;
 
   void OnIceSelectedCandidatePairChanged(
       const CandidatePairChangeEvent& event) override {
     ice_candidate_pair_change_history_.push_back(event);
   }
 
-  void OnIceCandidate(const IceCandidate* candidate) override {
-    RTC_LOG(LS_INFO) << debug_name_ << ": OnIceCandidate";
-
-    if (remote_async_dns_resolver_) {
-      const auto& local_candidate = candidate->candidate();
-      if (local_candidate.address().IsUnresolvedIP()) {
-        RTC_DCHECK(local_candidate.is_local());
-        const auto resolved_ip = mdns_responder_->GetMappedAddressForName(
-            local_candidate.address().hostname());
-        RTC_DCHECK(!resolved_ip.IsNil());
-        remote_async_dns_resolved_addr_ = local_candidate.address();
-        remote_async_dns_resolved_addr_.SetResolvedIP(resolved_ip);
-        EXPECT_CALL(*remote_async_dns_resolver_, Start(_, _))
-            .WillOnce([](const SocketAddress& addr,
-                         absl::AnyInvocable<void()> callback) { callback(); });
-        EXPECT_CALL(*remote_async_dns_resolver_, result())
-            .WillOnce(ReturnRef(remote_async_dns_resolver_result_));
-        EXPECT_CALL(remote_async_dns_resolver_result_, GetResolvedAddress(_, _))
-            .WillOnce(DoAll(SetArgPointee<1>(remote_async_dns_resolved_addr_),
-                            Return(true)));
-      }
-    }
-
-    // Check if we expected to have a candidate.
-    EXPECT_GT(candidates_expected_, 1);
-    candidates_expected_--;
-    std::string ice_sdp = candidate->ToString();
-    if (signaling_message_receiver_ == nullptr || !signal_ice_candidates_) {
-      // Remote party may be deleted.
-      return;
-    }
-    SendIceMessage(candidate->sdp_mid(), candidate->sdp_mline_index(), ice_sdp);
-    last_gathered_ice_candidate_ =
-        CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
-                           candidate->candidate());
-  }
+  void OnIceCandidate(const IceCandidate* candidate) override;
 
   void OnIceCandidateError(const std::string& address,
                            int port,
                            const std::string& url,
                            int error_code,
-                           const std::string& error_text) override {
-    error_event_ =
-        IceCandidateErrorEvent(address, port, url, error_code, error_text);
-  }
+                           const std::string& error_text) override;
 
   void OnIceCandidateRemoved(const IceCandidate* candidate) override {}
 
-  void OnDataChannel(
-      scoped_refptr<DataChannelInterface> data_channel) override {
-    RTC_LOG(LS_INFO) << debug_name_ << ": OnDataChannel";
-    data_channels_.push_back(data_channel);
-    data_observers_.push_back(
-        std::make_unique<MockDataChannelObserver>(data_channel.get()));
-  }
-  bool IdExists(const RtpHeaderExtensions& extensions, int id) {
-    for (const auto& extension : extensions) {
-      if (extension.id == id) {
-        return true;
-      }
-    }
-    return false;
-  }
+  void OnDataChannel(scoped_refptr<DataChannelInterface> data_channel) override;
+  bool IdExists(const RtpHeaderExtensions& extensions, int id);
 
  private:
   std::string debug_name_;
@@ -1377,36 +889,16 @@
            callee_->pc()->peer_connection_state() == state;
   }
 
-  bool DtlsConnected() {
-    // TODO(deadbeef): kIceConnectionConnected currently means both ICE and DTLS
-    // are connected. This is an important distinction. Once we have separate
-    // ICE and DTLS state, this check needs to use the DTLS state.
-    return (callee()->ice_connection_state() ==
-                PeerConnectionInterface::kIceConnectionConnected ||
-            callee()->ice_connection_state() ==
-                PeerConnectionInterface::kIceConnectionCompleted) &&
-           (caller()->ice_connection_state() ==
-                PeerConnectionInterface::kIceConnectionConnected ||
-            caller()->ice_connection_state() ==
-                PeerConnectionInterface::kIceConnectionCompleted);
-  }
+  bool DtlsConnected();
 
   // Sets field trials to pass to created PeerConnectionWrapper.
   // Must be called before PeerConnectionWrappers are created.
-  void SetFieldTrials(absl::string_view field_trials) {
-    RTC_CHECK(caller_ == nullptr);
-    RTC_CHECK(callee_ == nullptr);
-    field_trials_ = std::string(field_trials);
-  }
+  void SetFieldTrials(absl::string_view field_trials);
 
   // Sets field trials to pass to created PeerConnectionWrapper key:ed on
   // debug_name. Must be called before PeerConnectionWrappers are created.
   void SetFieldTrials(absl::string_view debug_name,
-                      absl::string_view field_trials) {
-    RTC_CHECK(caller_ == nullptr);
-    RTC_CHECK(callee_ == nullptr);
-    field_trials_overrides_[std::string(debug_name)] = field_trials;
-  }
+                      absl::string_view field_trials);
 
   // When `event_log_factory` is null, the default implementation of the event
   // log factory will be used.
@@ -1418,50 +910,14 @@
       std::unique_ptr<FakeRtcEventLogFactory> event_log_factory,
       bool reset_encoder_factory,
       bool reset_decoder_factory,
-      bool create_media_engine = true) {
-    RTCConfiguration modified_config;
-    if (config) {
-      modified_config = *config;
-    }
-    modified_config.sdp_semantics = sdp_semantics_;
-    if (!dependencies.cert_generator) {
-      dependencies.cert_generator =
-          std::make_unique<FakeRTCCertificateGenerator>();
-    }
-    std::string field_trials = field_trials_;
-    EnvironmentFactory env = EnvironmentFactory(env_);
-
-    auto it = field_trials_overrides_.find(debug_name);
-    if (it != field_trials_overrides_.end()) {
-      field_trials = it->second;
-      dependencies.trials = std::make_unique<FieldTrials>(it->second);
-    }
-    env.Set(CreateTestFieldTrialsPtr(field_trials));
-
-    std::unique_ptr<PeerConnectionIntegrationWrapper> client =
-        CreatePeerConnectionWrapperInternal(debug_name, env.Create());
-
-    if (!client->Init(options, &modified_config, std::move(dependencies),
-                      fss_.get(), network_thread_.get(), worker_thread_.get(),
-                      std::move(event_log_factory), reset_encoder_factory,
-                      reset_decoder_factory, create_media_engine)) {
-      return nullptr;
-    }
-    return client;
-  }
+      bool create_media_engine = true);
 
   std::unique_ptr<PeerConnectionIntegrationWrapper>
   CreatePeerConnectionWrapperWithFakeRtcEventLog(
       const std::string& debug_name,
       const PeerConnectionFactory::Options* options,
       const RTCConfiguration* config,
-      PeerConnectionDependencies dependencies) {
-    return CreatePeerConnectionWrapper(
-        debug_name, options, config, std::move(dependencies),
-        std::make_unique<FakeRtcEventLogFactory>(),
-        /*reset_encoder_factory=*/false,
-        /*reset_decoder_factory=*/false);
-  }
+      PeerConnectionDependencies dependencies);
 
   bool CreatePeerConnectionWrappers() {
     return CreatePeerConnectionWrappersWithConfig(
@@ -1471,208 +927,63 @@
 
   bool CreatePeerConnectionWrappersWithSdpSemantics(
       SdpSemantics caller_semantics,
-      SdpSemantics callee_semantics) {
-    // Can't specify the sdp_semantics in the passed-in configuration since it
-    // will be overwritten by CreatePeerConnectionWrapper with whatever is
-    // stored in sdp_semantics_. So get around this by modifying the instance
-    // variable before calling CreatePeerConnectionWrapper for the caller and
-    // callee PeerConnections.
-    SdpSemantics original_semantics = sdp_semantics_;
-    sdp_semantics_ = caller_semantics;
-    caller_ = CreatePeerConnectionWrapper(kCallerName, nullptr, nullptr,
-                                          PeerConnectionDependencies(nullptr),
-                                          nullptr,
-                                          /*reset_encoder_factory=*/false,
-                                          /*reset_decoder_factory=*/false);
-    sdp_semantics_ = callee_semantics;
-    callee_ = CreatePeerConnectionWrapper(kCalleeName, nullptr, nullptr,
-                                          PeerConnectionDependencies(nullptr),
-                                          nullptr,
-                                          /*reset_encoder_factory=*/false,
-                                          /*reset_decoder_factory=*/false);
-    sdp_semantics_ = original_semantics;
-    return caller_ && callee_;
-  }
+      SdpSemantics callee_semantics);
 
   bool CreatePeerConnectionWrappersWithConfig(
       const PeerConnectionInterface::RTCConfiguration& caller_config,
       const PeerConnectionInterface::RTCConfiguration& callee_config,
-      bool create_media_engine = true) {
-    caller_ = CreatePeerConnectionWrapper(
-        kCallerName, nullptr, &caller_config,
-        PeerConnectionDependencies(nullptr), nullptr,
-        /*reset_encoder_factory=*/false,
-        /*reset_decoder_factory=*/false, create_media_engine);
-    callee_ = CreatePeerConnectionWrapper(
-        kCalleeName, nullptr, &callee_config,
-        PeerConnectionDependencies(nullptr), nullptr,
-        /*reset_encoder_factory=*/false,
-        /*reset_decoder_factory=*/false, create_media_engine);
-    return caller_ && callee_;
-  }
+      bool create_media_engine = true);
 
   bool CreatePeerConnectionWrappersWithConfigAndDeps(
       const PeerConnectionInterface::RTCConfiguration& caller_config,
       PeerConnectionDependencies caller_dependencies,
       const PeerConnectionInterface::RTCConfiguration& callee_config,
-      PeerConnectionDependencies callee_dependencies) {
-    caller_ =
-        CreatePeerConnectionWrapper(kCallerName, nullptr, &caller_config,
-                                    std::move(caller_dependencies), nullptr,
-                                    /*reset_encoder_factory=*/false,
-                                    /*reset_decoder_factory=*/false);
-    callee_ =
-        CreatePeerConnectionWrapper(kCalleeName, nullptr, &callee_config,
-                                    std::move(callee_dependencies), nullptr,
-                                    /*reset_encoder_factory=*/false,
-                                    /*reset_decoder_factory=*/false);
-    return caller_ && callee_;
-  }
+      PeerConnectionDependencies callee_dependencies);
 
   bool CreatePeerConnectionWrappersWithOptions(
       const PeerConnectionFactory::Options& caller_options,
-      const PeerConnectionFactory::Options& callee_options) {
-    caller_ = CreatePeerConnectionWrapper(kCallerName, &caller_options, nullptr,
-                                          PeerConnectionDependencies(nullptr),
-                                          nullptr,
-                                          /*reset_encoder_factory=*/false,
-                                          /*reset_decoder_factory=*/false);
-    callee_ = CreatePeerConnectionWrapper(kCalleeName, &callee_options, nullptr,
-                                          PeerConnectionDependencies(nullptr),
-                                          nullptr,
-                                          /*reset_encoder_factory=*/false,
-                                          /*reset_decoder_factory=*/false);
-    return caller_ && callee_;
-  }
+      const PeerConnectionFactory::Options& callee_options);
 
-  bool CreatePeerConnectionWrappersWithFakeRtcEventLog() {
-    PeerConnectionInterface::RTCConfiguration default_config;
-    caller_ = CreatePeerConnectionWrapperWithFakeRtcEventLog(
-        kCallerName, nullptr, &default_config,
-        PeerConnectionDependencies(nullptr));
-    callee_ = CreatePeerConnectionWrapperWithFakeRtcEventLog(
-        kCalleeName, nullptr, &default_config,
-        PeerConnectionDependencies(nullptr));
-    return caller_ && callee_;
-  }
+  bool CreatePeerConnectionWrappersWithFakeRtcEventLog();
 
   std::unique_ptr<PeerConnectionIntegrationWrapper>
-  CreatePeerConnectionWrapperWithAlternateKey() {
-    std::unique_ptr<FakeRTCCertificateGenerator> cert_generator(
-        new FakeRTCCertificateGenerator());
-    cert_generator->use_alternate_key();
+  CreatePeerConnectionWrapperWithAlternateKey();
 
-    PeerConnectionDependencies dependencies(nullptr);
-    dependencies.cert_generator = std::move(cert_generator);
-    return CreatePeerConnectionWrapper("New Peer", nullptr, nullptr,
-                                       std::move(dependencies), nullptr,
-                                       /*reset_encoder_factory=*/false,
-                                       /*reset_decoder_factory=*/false);
-  }
+  bool CreateOneDirectionalPeerConnectionWrappers(bool caller_to_callee);
 
-  bool CreateOneDirectionalPeerConnectionWrappers(bool caller_to_callee) {
-    caller_ = CreatePeerConnectionWrapper(
-        kCallerName, nullptr, nullptr, PeerConnectionDependencies(nullptr),
-        nullptr,
-        /*reset_encoder_factory=*/!caller_to_callee,
-        /*reset_decoder_factory=*/caller_to_callee);
-    callee_ = CreatePeerConnectionWrapper(
-        kCalleeName, nullptr, nullptr, PeerConnectionDependencies(nullptr),
-        nullptr,
-        /*reset_encoder_factory=*/caller_to_callee,
-        /*reset_decoder_factory=*/!caller_to_callee);
-    return caller_ && callee_;
-  }
-
-  bool CreatePeerConnectionWrappersWithoutMediaEngine() {
-    caller_ = CreatePeerConnectionWrapper(kCallerName, nullptr, nullptr,
-                                          PeerConnectionDependencies(nullptr),
-                                          nullptr,
-                                          /*reset_encoder_factory=*/false,
-                                          /*reset_decoder_factory=*/false,
-                                          /*create_media_engine=*/false);
-    callee_ = CreatePeerConnectionWrapper(kCalleeName, nullptr, nullptr,
-                                          PeerConnectionDependencies(nullptr),
-                                          nullptr,
-                                          /*reset_encoder_factory=*/false,
-                                          /*reset_decoder_factory=*/false,
-                                          /*create_media_engine=*/false);
-    return caller_ && callee_;
-  }
+  bool CreatePeerConnectionWrappersWithoutMediaEngine();
 
   TestTurnServer* CreateTurnServer(
       SocketAddress internal_address,
       SocketAddress external_address,
       ProtocolType type = ProtocolType::PROTO_UDP,
-      const std::string& common_name = "test turn server") {
-    Thread* thread = network_thread();
-    SocketFactory* socket_factory = fss_.get();
-    std::unique_ptr<TestTurnServer> turn_server;
-    SendTask(network_thread(), [&] {
-      turn_server = std::make_unique<TestTurnServer>(
-          CreateTestEnvironment(), thread, socket_factory, internal_address,
-          external_address, type,
-          /*ignore_bad_certs=*/true, common_name);
-    });
-    turn_servers_.push_back(std::move(turn_server));
-    // Interactions with the turn server should be done on the network thread.
-    return turn_servers_.back().get();
-  }
+      const std::string& common_name = "test turn server");
 
-  TestTurnCustomizer* CreateTurnCustomizer() {
-    std::unique_ptr<TestTurnCustomizer> turn_customizer;
-    SendTask(network_thread(),
-             [&] { turn_customizer = std::make_unique<TestTurnCustomizer>(); });
-    turn_customizers_.push_back(std::move(turn_customizer));
-    // Interactions with the turn customizer should be done on the network
-    // thread.
-    return turn_customizers_.back().get();
-  }
+  TestTurnCustomizer* CreateTurnCustomizer();
 
   // Checks that the function counters for a TestTurnCustomizer are greater than
   // 0.
   void ExpectTurnCustomizerCountersIncremented(
-      TestTurnCustomizer* turn_customizer) {
-    SendTask(network_thread(), [turn_customizer] {
-      EXPECT_GT(turn_customizer->allow_channel_data_cnt_, 0u);
-      EXPECT_GT(turn_customizer->modify_cnt_, 0u);
-    });
-  }
+      TestTurnCustomizer* turn_customizer);
 
   // Once called, SDP blobs and ICE candidates will be automatically signaled
   // between PeerConnections.
-  void ConnectFakeSignaling() {
-    caller_->set_signaling_message_receiver(callee_.get());
-    callee_->set_signaling_message_receiver(caller_.get());
-  }
+  void ConnectFakeSignaling();
 
   // Once called, SDP blobs will be automatically signaled between
   // PeerConnections. Note that ICE candidates will not be signaled unless they
   // are in the exchanged SDP blobs.
-  void ConnectFakeSignalingForSdpOnly() {
-    ConnectFakeSignaling();
-    SetSignalIceCandidates(false);
-  }
+  void ConnectFakeSignalingForSdpOnly();
 
-  void SetSignalingDelayMs(int delay_ms) {
-    caller_->set_signaling_delay_ms(delay_ms);
-    callee_->set_signaling_delay_ms(delay_ms);
-  }
+  void SetSignalingDelayMs(int delay_ms);
 
-  void SetSignalIceCandidates(bool signal) {
-    caller_->set_signal_ice_candidates(signal);
-    callee_->set_signal_ice_candidates(signal);
-  }
+  void SetSignalIceCandidates(bool signal);
 
   // Messages may get lost on the unreliable DataChannel, so we send multiple
   // times to avoid test flakiness.
   void SendRtpDataWithRetries(DataChannelInterface* dc,
                               const std::string& data,
-                              int retries) {
-    for (int i = 0; i < retries; ++i) {
-      dc->Send(DataBuffer(data));
-    }
-  }
+                              int retries);
 
   Thread* network_thread() { return network_thread_.get(); }
 
@@ -1683,18 +994,7 @@
   // Destroy peer connections.
   // This can be used to ensure that all pointers to on-stack mocks
   // get dropped before exit.
-  void DestroyPeerConnections() {
-    if (caller_) {
-      caller_->set_signaling_message_receiver(nullptr);
-      caller_->pc()->Close();
-    }
-    if (callee_) {
-      callee_->set_signaling_message_receiver(nullptr);
-      callee_->pc()->Close();
-    }
-    caller_.reset();
-    callee_.reset();
-  }
+  void DestroyPeerConnections();
 
   void DestroyTurnServers();
   void DestroyThreads();
@@ -1702,22 +1002,14 @@
   // Set the `caller_` to the `wrapper` passed in and return the
   // original `caller_`.
   PeerConnectionIntegrationWrapper* SetCallerPcWrapperAndReturnCurrent(
-      std::unique_ptr<PeerConnectionIntegrationWrapper> wrapper) {
-    PeerConnectionIntegrationWrapper* old = caller_.release();
-    caller_ = std::move(wrapper);
-    return old;
-  }
+      std::unique_ptr<PeerConnectionIntegrationWrapper> wrapper);
 
   PeerConnectionIntegrationWrapper* callee() { return callee_.get(); }
 
   // Set the `callee_` to the `wrapper` passed in and return the
   // original `callee_`.
   PeerConnectionIntegrationWrapper* SetCalleePcWrapperAndReturnCurrent(
-      std::unique_ptr<PeerConnectionIntegrationWrapper> wrapper) {
-    PeerConnectionIntegrationWrapper* old = callee_.release();
-    callee_ = std::move(wrapper);
-    return old;
-  }
+      std::unique_ptr<PeerConnectionIntegrationWrapper> wrapper);
 
   FirewallSocketServer* firewall() const { return fss_.get(); }
 
@@ -1725,175 +1017,18 @@
   // kMaxWaitForFramesMs. The new expected frames are specified in
   // `media_expectations`. Returns false if any of the expectations were
   // not met.
-  bool ExpectNewFrames(const MediaExpectations& media_expectations) {
-    // Make sure there are no bogus tracks confusing the issue.
-    caller()->RemoveUnusedVideoRenderers();
-    callee()->RemoveUnusedVideoRenderers();
-    // First initialize the expected frame counts based upon the current
-    // frame count.
-    int total_caller_audio_frames_expected = caller()->audio_frames_received();
-    if (media_expectations.caller_audio_expectation_ ==
-        MediaExpectations::kExpectSomeFrames) {
-      total_caller_audio_frames_expected +=
-          media_expectations.caller_audio_frames_expected_;
-    }
-    int total_caller_video_frames_expected =
-        caller()->min_video_frames_received_per_track();
-    if (media_expectations.caller_video_expectation_ ==
-        MediaExpectations::kExpectSomeFrames) {
-      total_caller_video_frames_expected +=
-          media_expectations.caller_video_frames_expected_;
-    }
-    int total_callee_audio_frames_expected = callee()->audio_frames_received();
-    if (media_expectations.callee_audio_expectation_ ==
-        MediaExpectations::kExpectSomeFrames) {
-      total_callee_audio_frames_expected +=
-          media_expectations.callee_audio_frames_expected_;
-    }
-    int total_callee_video_frames_expected =
-        callee()->min_video_frames_received_per_track();
-    if (media_expectations.callee_video_expectation_ ==
-        MediaExpectations::kExpectSomeFrames) {
-      total_callee_video_frames_expected +=
-          media_expectations.callee_video_frames_expected_;
-    }
+  bool ExpectNewFrames(const MediaExpectations& media_expectations);
 
-    // Wait for the expected frames.
-    EXPECT_THAT(
-        GetWaiter({.timeout = kMaxWaitForFrames})
-            .Until(
-                [&] {
-                  return caller()->audio_frames_received() >=
-                             total_caller_audio_frames_expected &&
-                         caller()->min_video_frames_received_per_track() >=
-                             total_caller_video_frames_expected &&
-                         callee()->audio_frames_received() >=
-                             total_callee_audio_frames_expected &&
-                         callee()->min_video_frames_received_per_track() >=
-                             total_callee_video_frames_expected;
-                },
-                ::testing::IsTrue()),
-        IsRtcOk());
-    bool expectations_correct =
-        caller()->audio_frames_received() >=
-            total_caller_audio_frames_expected &&
-        caller()->min_video_frames_received_per_track() >=
-            total_caller_video_frames_expected &&
-        callee()->audio_frames_received() >=
-            total_callee_audio_frames_expected &&
-        callee()->min_video_frames_received_per_track() >=
-            total_callee_video_frames_expected;
-
-    // After the combined wait, print out a more detailed message upon
-    // failure.
-    EXPECT_GE(caller()->audio_frames_received(),
-              total_caller_audio_frames_expected);
-    EXPECT_GE(caller()->min_video_frames_received_per_track(),
-              total_caller_video_frames_expected);
-    EXPECT_GE(callee()->audio_frames_received(),
-              total_callee_audio_frames_expected);
-    EXPECT_GE(callee()->min_video_frames_received_per_track(),
-              total_callee_video_frames_expected);
-
-    // We want to make sure nothing unexpected was received.
-    if (media_expectations.caller_audio_expectation_ ==
-        MediaExpectations::kExpectNoFrames) {
-      EXPECT_EQ(caller()->audio_frames_received(),
-                total_caller_audio_frames_expected);
-      if (caller()->audio_frames_received() !=
-          total_caller_audio_frames_expected) {
-        expectations_correct = false;
-      }
-    }
-    if (media_expectations.caller_video_expectation_ ==
-        MediaExpectations::kExpectNoFrames) {
-      EXPECT_EQ(caller()->min_video_frames_received_per_track(),
-                total_caller_video_frames_expected);
-      if (caller()->min_video_frames_received_per_track() !=
-          total_caller_video_frames_expected) {
-        expectations_correct = false;
-      }
-    }
-    if (media_expectations.callee_audio_expectation_ ==
-        MediaExpectations::kExpectNoFrames) {
-      EXPECT_EQ(callee()->audio_frames_received(),
-                total_callee_audio_frames_expected);
-      if (callee()->audio_frames_received() !=
-          total_callee_audio_frames_expected) {
-        expectations_correct = false;
-      }
-    }
-    if (media_expectations.callee_video_expectation_ ==
-        MediaExpectations::kExpectNoFrames) {
-      EXPECT_EQ(callee()->min_video_frames_received_per_track(),
-                total_callee_video_frames_expected);
-      if (callee()->min_video_frames_received_per_track() !=
-          total_callee_video_frames_expected) {
-        expectations_correct = false;
-      }
-    }
-    return expectations_correct;
-  }
-
-  void ClosePeerConnections() {
-    if (caller())
-      caller()->pc()->Close();
-    if (callee())
-      callee()->pc()->Close();
-  }
+  void ClosePeerConnections();
 
   void TestNegotiatedCipherSuite(const RTCConfiguration& caller_config,
                                  const RTCConfiguration& callee_config,
-                                 int expected_cipher_suite) {
-    ASSERT_TRUE(
-        CreatePeerConnectionWrappersWithConfig(caller_config, callee_config));
-    ConnectFakeSignaling();
-    caller()->AddAudioVideoTracks();
-    callee()->AddAudioVideoTracks();
-    caller()->CreateAndSetAndSignalOffer();
-    WaitUntilSettings settings;
-    settings.polling_interval = TimeDelta::Millis(100);
-    if (auto* tc = time_controller()) {
-      settings.clock = tc;
-    }
-    ASSERT_THAT(GetWaiter(settings).Until([&] { return DtlsConnected(); },
-                                          ::testing::IsTrue()),
-                IsRtcOk());
-    EXPECT_THAT(GetWaiter(settings).Until(
-                    [&] {
-                      auto report = caller()->NewGetStats(settings);
-                      if (!report) {
-                        return false;
-                      }
-                      auto transport_stats =
-                          report->GetStatsOfType<RTCTransportStats>();
-                      if (transport_stats.empty()) {
-                        return false;
-                      }
-                      return *transport_stats[0]->srtp_cipher ==
-                             SrtpCryptoSuiteToName(expected_cipher_suite);
-                    },
-                    ::testing::IsTrue()),
-                IsRtcOk());
-  }
+                                 int expected_cipher_suite);
 
   void TestGcmNegotiationUsesCipherSuite(bool local_gcm_enabled,
                                          bool remote_gcm_enabled,
                                          bool aes_ctr_enabled,
-                                         int expected_cipher_suite) {
-    RTCConfiguration caller_config;
-    CryptoOptions caller_crypto;
-    caller_crypto.srtp.enable_gcm_crypto_suites = local_gcm_enabled;
-    caller_crypto.srtp.enable_aes128_sha1_80_crypto_cipher = aes_ctr_enabled;
-    caller_config.crypto_options = caller_crypto;
-    RTCConfiguration callee_config;
-    CryptoOptions callee_crypto;
-    callee_crypto.srtp.enable_gcm_crypto_suites = remote_gcm_enabled;
-    callee_crypto.srtp.enable_aes128_sha1_80_crypto_cipher = aes_ctr_enabled;
-    callee_config.crypto_options = callee_crypto;
-    TestNegotiatedCipherSuite(caller_config, callee_config,
-                              expected_cipher_suite);
-  }
+                                         int expected_cipher_suite);
 
  protected:
   void OverrideLoggingLevelForTest(LoggingSeverity new_severity);
@@ -1936,31 +1071,16 @@
     : public internal::PeerConnectionIntegrationTestBase {
  public:
   explicit PeerConnectionIntegrationBaseTest(SdpSemantics sdp_semantics);
-  ~PeerConnectionIntegrationBaseTest() override {
-    DestroyPeerConnections();
-    DestroyTurnServers();
-  }
+  ~PeerConnectionIntegrationBaseTest() override;
 
   test::RunLoop& run_loop() { return run_loop_; }
 
   void ExecuteTask(TaskQueueBase& task_queue,
-                   absl::AnyInvocable<void()> task) override {
-    task_queue.PostTask([this, task = std::move(task)]() mutable {
-      task();
-      run_loop_.task_queue()->PostTask(run_loop_.QuitClosure());
-    });
-    run_loop_.Run();
-  }
+                   absl::AnyInvocable<void()> task) override;
 
-  Waiter GetWaiter(WaitUntilSettings overrides = {}) override {
-    return Waiter(overrides);
-  }
+  Waiter GetWaiter(WaitUntilSettings overrides = {}) override;
 
-  std::unique_ptr<Thread> CreateThread(absl::string_view name) override {
-    auto thread = Thread::Create();
-    thread->Start();
-    return thread;
-  }
+  std::unique_ptr<Thread> CreateThread(absl::string_view name) override;
 
  protected:
   test::RunLoop run_loop_;
@@ -1973,19 +1093,11 @@
       SdpSemantics sdp_semantics);
 
   void ExecuteTask(TaskQueueBase& task_queue,
-                   absl::AnyInvocable<void()> task) override {
-    task_queue.PostTask(std::move(task));
-    time_controller_->AdvanceTime(TimeDelta::Zero());
-  }
+                   absl::AnyInvocable<void()> task) override;
 
-  Waiter GetWaiter(WaitUntilSettings overrides = {}) override {
-    overrides.clock = time_controller_.get();
-    return Waiter(overrides);
-  }
-  TimeController* time_controller() override { return time_controller_.get(); }
-  std::unique_ptr<Thread> CreateThread(absl::string_view name) override {
-    return time_controller_->CreateThread(std::string(name), nullptr);
-  }
+  Waiter GetWaiter(WaitUntilSettings overrides = {}) override;
+  TimeController* time_controller() { return time_controller_.get(); }
+  std::unique_ptr<Thread> CreateThread(absl::string_view name) override;
 
   ~PeerConnectionIntegrationTestWithSimulatedTime() override;