/*
 *  Copyright 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef PC_TEST_INTEGRATION_TEST_HELPERS_H_
#define PC_TEST_INTEGRATION_TEST_HELPERS_H_

#include <limits.h>
#include <stdint.h>
#include <stdio.h>

#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "api/candidate.h"
#include "api/data_channel_interface.h"
#include "api/dtls_transport_interface.h"
#include "api/environment/environment.h"
#include "api/ice_transport_interface.h"
#include "api/jsep.h"
#include "api/make_ref_counted.h"
#include "api/media_stream_interface.h"
#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_header_extension_id.h"
#include "api/rtp_receiver_interface.h"
#include "api/rtp_sender_interface.h"
#include "api/rtp_transceiver_interface.h"
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
#include "api/stats/rtc_stats_report.h"
#include "api/stats/rtcstats_objects.h"
#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/time_controller.h"
#include "api/units/time_delta.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/ice_transport_internal.h"
#include "p2p/base/port.h"
#include "p2p/base/port_allocator.h"
#include "p2p/test/fake_ice_transport.h"
#include "p2p/test/test_stun_server.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/session_description.h"
#include "pc/test/fake_audio_capture_module.h"
#include "pc/test/fake_periodic_video_source.h"
#include "pc/test/fake_video_track_renderer.h"
#include "pc/test/mock_peer_connection_observers.h"
#include "pc/video_track_source.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_server.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/system/plan_b_only.h"
#include "rtc_base/thread.h"
#include "rtc_base/virtual_socket_server.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"

namespace webrtc {

namespace internal {
class PeerConnectionIntegrationTestBase;
}  // namespace internal

using ::testing::_;
using ::testing::Combine;
using ::testing::Contains;
using ::testing::DoAll;
using ::testing::ElementsAre;
using ::testing::InvokeArgument;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::UnorderedElementsAreArray;
using ::testing::Values;
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;

constexpr TimeDelta kDefaultTimeout = TimeDelta::Millis(10000);
constexpr TimeDelta kLongTimeout = TimeDelta::Millis(60000);
constexpr TimeDelta kMaxWaitForStats = TimeDelta::Millis(3000);
constexpr TimeDelta kMaxWaitForActivation = TimeDelta::Millis(5000);
constexpr TimeDelta kMaxWaitForFrames = TimeDelta::Millis(10000);
// Default number of audio/video frames to wait for before considering a test
// successful.
static const int kDefaultExpectedAudioFrameCount = 3;
static const int kDefaultExpectedVideoFrameCount = 3;

static const char kDataChannelLabel[] = "data_channel";

// SRTP cipher name negotiated by the tests. This must be updated if the
// default changes.
static const int kDefaultSrtpCryptoSuite = kSrtpAes128CmSha1_80;
static const int kDefaultSrtpCryptoSuiteGcm = kSrtpAeadAes256Gcm;

static const SocketAddress kDefaultLocalAddress("192.168.1.1", 0);

// Helper function for constructing offer/answer options to initiate an ICE
// restart.
PeerConnectionInterface::RTCOfferAnswerOptions IceRestartOfferAnswerOptions();

// Remove all stream information (SSRCs, track IDs, etc.) and "msid-semantic"
// attribute from received SDP, simulating a legacy endpoint.
void RemoveSsrcsAndMsids(std::unique_ptr<SessionDescriptionInterface>& desc);

// Removes all stream information besides the stream ids, simulating an
// endpoint that only signals a=msid lines to convey stream_ids.
void RemoveSsrcsAndKeepMsids(
    std::unique_ptr<SessionDescriptionInterface>& desc);

// Set SdpType.
void SetSdpType(std::unique_ptr<SessionDescriptionInterface>& sdp,
                SdpType sdpType);

// Replaces the stream's primary SSRC and updates the first SSRC of all
// ssrc-groups.
void ReplaceFirstSsrc(StreamParams& stream, uint32_t ssrc);

int FindFirstMediaStatsIndexByKind(
    const std::string& kind,
    const std::vector<const RTCInboundRtpStreamStats*>& inbound_rtps);

// Tests whether a session description contains conflicting descriptions
// for a payload type within a bundle.
RTCError ValidateBundledPayloadTypes(const SessionDescription& description);

class TaskQueueMetronome : public Metronome {
 public:
  explicit TaskQueueMetronome(TimeDelta tick_period);
  ~TaskQueueMetronome() override;

  // Metronome implementation.
  void RequestCallOnNextTick(absl::AnyInvocable<void() &&> callback) override;
  TimeDelta TickPeriod() const override;

 private:
  const TimeDelta tick_period_;
  SequenceChecker sequence_checker_{SequenceChecker::kDetached};
  std::vector<absl::AnyInvocable<void() &&>> callbacks_;
  ScopedTaskSafetyDetached safety_;
};

class SignalingMessageReceiver {
 public:
  virtual void ReceiveSdpMessage(SdpType type, const std::string& msg) = 0;
  virtual void ReceiveIceMessage(const std::string& sdp_mid,
                                 int sdp_mline_index,
                                 const std::string& msg) = 0;

 protected:
  SignalingMessageReceiver() {}
  virtual ~SignalingMessageReceiver() {}
};

class MockRtpReceiverObserver : public RtpReceiverObserverInterface {
 public:
  explicit MockRtpReceiverObserver(webrtc::MediaType media_type)
      : expected_media_type_(media_type) {}

  void OnFirstPacketReceived(webrtc::MediaType media_type) override {
    ASSERT_EQ(expected_media_type_, media_type);
    first_packet_received_ = true;
  }
  void OnFirstPacketReceivedAfterReceptiveChange(
      webrtc::MediaType media_type) override {
    ASSERT_EQ(expected_media_type_, media_type);
    first_packet_received_after_receptive_change_ = true;
  }

  bool first_packet_received() const { return first_packet_received_; }
  bool first_packet_received_after_receptive_change() const {
    return first_packet_received_after_receptive_change_;
  }

  ~MockRtpReceiverObserver() override {}

 private:
  bool first_packet_received_ = false;
  bool first_packet_received_after_receptive_change_ = false;
  webrtc::MediaType expected_media_type_;
};

class MockRtpSenderObserver : public RtpSenderObserverInterface {
 public:
  explicit MockRtpSenderObserver(webrtc::MediaType media_type)
      : expected_media_type_(media_type) {}

  void OnFirstPacketSent(webrtc::MediaType media_type) override {
    ASSERT_EQ(expected_media_type_, media_type);
    first_packet_sent_ = true;
  }

  bool first_packet_sent() const { return first_packet_sent_; }

  ~MockRtpSenderObserver() override {}

 private:
  bool first_packet_sent_ = false;
  webrtc::MediaType expected_media_type_;
};

// Helper class that wraps a peer connection, observes it, and can accept
// signaling messages from another wrapper.
//
// Uses a fake network, fake A/V capture, and optionally fake
// encoders/decoders, though they aren't used by default since they don't
// advertise support of any codecs.
// TODO(steveanton): See how this could become a subclass of
// PeerConnectionWrapper defined in peerconnectionwrapper.h.
class PeerConnectionIntegrationWrapper : public PeerConnectionObserver,
                                         public SignalingMessageReceiver {
 public:
  explicit PeerConnectionIntegrationWrapper(
      const std::string& debug_name,
      Environment env,
      internal::PeerConnectionIntegrationTestBase* test);

  PeerConnectionFactoryInterface* pc_factory() const {
    return peer_connection_factory_.get();
  }

  PeerConnectionInterface* pc() const { return peer_connection_.get(); }

  // Return the PC implementation, so that non-public interfaces
  // can be used in tests.
  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();

  // Sets the options to be used when CreateAndSetAndSignalOffer is called, or
  // when a remote offer is received (via fake signaling) and an answer is
  // generated. By default, uses default options.
  void SetOfferAnswerOptions(
      const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
    offer_answer_options_ = options;
  }

  // Set a callback to be invoked when SDP is received via the fake signaling
  // channel, which provides an opportunity to munge (modify) the SDP. This is
  // used to test SDP being applied that a PeerConnection would normally not
  // generate, but a non-JSEP endpoint might.
  void SetReceivedSdpMunger(
      std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
          munger) {
    received_sdp_munger_ = std::move(munger);
  }

  // Similar to the above, but this is run on SDP immediately after it's
  // generated.
  void SetGeneratedSdpMunger(
      std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
          munger) {
    generated_sdp_munger_ = std::move(munger);
  }

  // Set a callback to be invoked when a remote offer is received via the fake
  // signaling channel. This provides an opportunity to change the
  // PeerConnection state before an answer is created and sent to the caller.
  void SetRemoteOfferHandler(std::function<void()> handler) {
    remote_offer_handler_ = std::move(handler);
  }

  void SetRemoteAsyncResolver(MockAsyncDnsResolver* resolver) {
    remote_async_dns_resolver_ = resolver;
  }

  // Every ICE connection state in order that has been seen by the observer.
  std::vector<PeerConnectionInterface::IceConnectionState>
  ice_connection_state_history() const {
    return ice_connection_state_history_;
  }
  void clear_ice_connection_state_history() {
    ice_connection_state_history_.clear();
  }

  // Every standardized ICE connection state in order that has been seen by the
  // observer.
  std::vector<PeerConnectionInterface::IceConnectionState>
  standardized_ice_connection_state_history() const {
    return standardized_ice_connection_state_history_;
  }

  // Every PeerConnection state in order that has been seen by the observer.
  std::vector<PeerConnectionInterface::PeerConnectionState>
  peer_connection_state_history() const {
    return peer_connection_state_history_;
  }

  // Every ICE gathering state in order that has been seen by the observer.
  std::vector<PeerConnectionInterface::IceGatheringState>
  ice_gathering_state_history() const {
    return ice_gathering_state_history_;
  }
  std::vector<CandidatePairChangeEvent> ice_candidate_pair_change_history()
      const {
    return ice_candidate_pair_change_history_;
  }

  // Every PeerConnection signaling state in order that has been seen by the
  // observer.
  std::vector<PeerConnectionInterface::SignalingState>
  peer_connection_signaling_state_history() const {
    return peer_connection_signaling_state_history_;
  }

  void AddAudioVideoTracks();

  scoped_refptr<RtpSenderInterface> AddAudioTrack() {
    return AddTrack(CreateLocalAudioTrack());
  }

  scoped_refptr<RtpSenderInterface> AddVideoTrack() {
    return AddTrack(CreateLocalVideoTrack());
  }

  scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack();

  scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack();

  scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackWithConfig(
      FakePeriodicVideoSource::Config config);

  scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackWithRotation(
      VideoRotation rotation);

  scoped_refptr<RtpSenderInterface> AddTrack(
      scoped_refptr<MediaStreamTrackInterface> track,
      const std::vector<std::string>& stream_ids = {});

  std::vector<scoped_refptr<RtpReceiverInterface>> GetReceiversOfType(
      webrtc::MediaType media_type);

  scoped_refptr<RtpTransceiverInterface> GetFirstTransceiverOfType(
      webrtc::MediaType media_type);

  bool SignalingStateStable();

  bool IceGatheringStateComplete();

  void CreateDataChannel();

  void CreateDataChannel(const DataChannelInit* init);

  void CreateDataChannel(const std::string& label, const DataChannelInit* init);

  // Return the last observed data channel.
  DataChannelInterface* data_channel();
  // Return all data channels.
  std::vector<scoped_refptr<DataChannelInterface>>& data_channels() {
    return data_channels_;
  }

  MockDataChannelObserver* data_observer() const;

  std::vector<std::unique_ptr<MockDataChannelObserver>>& data_observers() {
    return data_observers_;
  }

  std::unique_ptr<SessionDescriptionInterface> CreateAnswerForTest();

  int audio_frames_received() const;

  // Takes minimum of video frames received for each track.
  //
  // Can be used like:
  // EXPECT_GE(expected_frames, min_video_frames_received_per_track());
  //
  // To ensure that all video tracks received at least a certain number of
  // 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.
  scoped_refptr<MockStatsObserver> OldGetStatsForTrack(
      MediaStreamTrackInterface* track);

  // Version that doesn't take a track "filter", and gathers all stats.
  scoped_refptr<MockStatsObserver> OldGetStats();

  // Synchronously gets stats and returns them. If it times out, fails the test
  // and returns null.
  // TODO(bugs.webrtc.org/501310910): Unify NewGetStats implementations by
  // making RunLoop an interface that can be implemented for simulated time too.
  // TODO(tommi) - Remove this method in favor of the one that supports RunLoop.
  scoped_refptr<const RTCStatsReport> NewGetStats(
      WaitUntilSettings settings = {});

  scoped_refptr<const RTCStatsReport> NewGetStats(test::RunLoop& run_loop);

  std::string DtlsCipher();

  std::string SrtpCipher();

  int rendered_width();

  int rendered_height();

  double rendered_aspect_ratio();

  VideoRotation rendered_rotation();

  int local_rendered_width();

  int local_rendered_height();

  double local_rendered_aspect_ratio();

  PLAN_B_ONLY size_t number_of_remote_streams();

  PLAN_B_ONLY StreamCollectionInterface* remote_streams() const;

  PLAN_B_ONLY StreamCollectionInterface* local_streams();

  PeerConnectionInterface::SignalingState signaling_state();

  PeerConnectionInterface::IceConnectionState ice_connection_state();

  PeerConnectionInterface::IceConnectionState
  standardized_ice_connection_state();

  PeerConnectionInterface::IceGatheringState ice_gathering_state();

  // Returns a MockRtpReceiverObserver for each RtpReceiver returned by
  // GetReceivers. They're updated automatically when a remote offer/answer
  // from the fake signaling channel is applied, or when
  // ResetRtpReceiverObservers below is called.
  const std::vector<std::unique_ptr<MockRtpReceiverObserver>>&
  rtp_receiver_observers() {
    return rtp_receiver_observers_;
  }

  void ResetRtpReceiverObservers();

  const std::vector<std::unique_ptr<MockRtpSenderObserver>>&
  rtp_sender_observers() {
    return rtp_sender_observers_;
  }

  void ResetRtpSenderObservers();

  FakeNetworkManager* network_manager() const { return fake_network_manager_; }

  FakeRtcEventLogFactory* event_log_factory() const {
    return event_log_factory_;
  }

  Candidate last_candidate_gathered() const;
  const IceCandidate* last_gathered_ice_candidate() const {
    return last_gathered_ice_candidate_.get();
  }
  const IceCandidateErrorEvent& error_event() const { return error_event_; }

  // 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);

  // Returns null on failure.
  std::unique_ptr<SessionDescriptionInterface> CreateOfferAndWait();
  bool Rollback();

  // Functions for querying stats.
  void StartWatchingDelayStats();

  void UpdateDelayStats(std::string tag, int desc_size);

  bool AudioDelayStatsPercentageChecked() const {
    return audio_delay_stats_percentage_checked_;
  }

  // Sets number of candidates expected
  void ExpectCandidates(int candidate_count) {
    candidates_expected_ = candidate_count;
  }

  bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc);

  void NegotiateCorruptionDetectionHeader();

  uint32_t GetCorruptionScoreCount();

  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();

  std::optional<DtlsTransportTlsRole> dtls_transport_role();

  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
  // message needs to be sent as soon as SetLocalDescription finishes, without
  // waiting for the observer to be called. This ensures that ICE candidates
  // don't outrace the description.
  bool SetLocalDescriptionAndSendSdpMessage(
      std::unique_ptr<SessionDescriptionInterface> desc);

 public:
  bool Init(const PeerConnectionFactory::Options* options,
            const PeerConnectionInterface::RTCConfiguration* config,
            PeerConnectionDependencies dependencies,
            SocketServer* socket_server,
            Thread* network_thread,
            Thread* worker_thread,
            std::unique_ptr<FakeRtcEventLogFactory> event_log_factory,
            bool reset_encoder_factory,
            bool reset_decoder_factory,
            bool create_media_engine);

  scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
      const PeerConnectionInterface::RTCConfiguration* config,
      PeerConnectionDependencies dependencies);

  void set_signaling_delay_ms(int delay_ms) { signaling_delay_ms_ = delay_ms; }

  void set_signal_ice_candidates(bool signal) {
    signal_ice_candidates_ = signal;
  }

  scoped_refptr<VideoTrackInterface> CreateLocalVideoTrackInternal(
      FakePeriodicVideoSource::Config config);

  void HandleIncomingOffer(const std::string& msg);

  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();

  // Simulate sending a blob of SDP with delay `signaling_delay_ms_` (0 by
  // default).
  void SendSdpMessage(SdpType type, const std::string& 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);

  void RelayIceMessageIfReceiverExists(const std::string& sdp_mid,
                                       int sdp_mline_index,
                                       const std::string& msg);

  // SignalingMessageReceiver callbacks.
 public:
  void set_signaling_message_receiver(
      SignalingMessageReceiver* signaling_message_receiver) {
    signaling_message_receiver_ = signaling_message_receiver;
  }

  void ReceiveSdpMessage(SdpType type, const std::string& msg) override;

  void ReceiveIceMessage(const std::string& sdp_mid,
                         int sdp_mline_index,
                         const std::string& msg) override;

 private:
  // PeerConnectionObserver callbacks.
  void OnSignalingChange(
      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;
  void OnStandardizedIceConnectionChange(
      PeerConnectionInterface::IceConnectionState new_state) override;

  void OnConnectionChange(
      PeerConnectionInterface::PeerConnectionState new_state) override;

  void OnIceGatheringChange(
      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;

  void OnIceCandidateError(const std::string& address,
                           int port,
                           const std::string& url,
                           int error_code,
                           const std::string& error_text) override;

  void OnIceCandidateRemoved(const IceCandidate* candidate) override {}

  void OnDataChannel(scoped_refptr<DataChannelInterface> data_channel) override;
  bool IdExists(const RtpHeaderExtensions& extensions, RtpHeaderExtensionId id);

 private:
  std::string debug_name_;
  const Environment env_;

  // Network manager is owned by the `peer_connection_factory_`.
  FakeNetworkManager* fake_network_manager_ = nullptr;
  Thread* network_thread_;

  // Reference to the mDNS responder owned by `fake_network_manager_` after set.
  FakeMdnsResponder* mdns_responder_ = nullptr;

  scoped_refptr<PeerConnectionInterface> peer_connection_;
  scoped_refptr<PeerConnectionFactoryInterface> peer_connection_factory_;

  // Needed to keep track of number of frames sent.
  scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_;
  // Needed to keep track of number of frames received.
  std::map<std::string, std::unique_ptr<FakeVideoTrackRenderer>>
      fake_video_renderers_;
  // Needed to ensure frames aren't received for removed tracks.
  std::vector<std::unique_ptr<FakeVideoTrackRenderer>>
      removed_fake_video_renderers_;

  // For remote peer communication.
  SignalingMessageReceiver* signaling_message_receiver_ = nullptr;
  int signaling_delay_ms_ = 0;
  bool signal_ice_candidates_ = true;
  std::unique_ptr<IceCandidate> last_gathered_ice_candidate_;
  IceCandidateErrorEvent error_event_;

  // Store references to the video sources we've created, so that we can stop
  // them, if required.
  std::vector<scoped_refptr<VideoTrackSource>> video_track_sources_;
  // `local_video_renderer_` attached to the first created local video track.
  std::unique_ptr<FakeVideoTrackRenderer> local_video_renderer_;

  SdpSemantics sdp_semantics_;
  PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options_;
  std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
      received_sdp_munger_;
  std::function<void(std::unique_ptr<SessionDescriptionInterface>&)>
      generated_sdp_munger_;
  std::function<void()> remote_offer_handler_;
  MockAsyncDnsResolver* remote_async_dns_resolver_ = nullptr;
  // Result variables for the mock DNS resolver
  NiceMock<MockAsyncDnsResolverResult> remote_async_dns_resolver_result_;
  SocketAddress remote_async_dns_resolved_addr_;

  // All data channels either created or observed on this peerconnection
  std::vector<scoped_refptr<DataChannelInterface>> data_channels_;
  std::vector<std::unique_ptr<MockDataChannelObserver>> data_observers_;

  std::vector<std::unique_ptr<MockRtpReceiverObserver>> rtp_receiver_observers_;
  std::vector<std::unique_ptr<MockRtpSenderObserver>> rtp_sender_observers_;

  std::vector<PeerConnectionInterface::IceConnectionState>
      ice_connection_state_history_;
  std::vector<PeerConnectionInterface::IceConnectionState>
      standardized_ice_connection_state_history_;
  std::vector<PeerConnectionInterface::PeerConnectionState>
      peer_connection_state_history_;
  std::vector<PeerConnectionInterface::IceGatheringState>
      ice_gathering_state_history_;
  std::vector<CandidatePairChangeEvent> ice_candidate_pair_change_history_;
  std::vector<PeerConnectionInterface::SignalingState>
      peer_connection_signaling_state_history_;
  FakeRtcEventLogFactory* event_log_factory_;

  // Number of ICE candidates expected. The default is no limit.
  int candidates_expected_ = std::numeric_limits<int>::max();

  // Variables for tracking delay stats on an audio track
  int audio_packets_stat_ = 0;
  double audio_delay_stat_ = 0.0;
  uint64_t audio_samples_stat_ = 0;
  uint64_t audio_concealed_stat_ = 0;
  std::string rtp_stats_id_;
  bool audio_delay_stats_percentage_checked_ = false;

  std::function<void(PeerConnectionInterface::PeerConnectionState)>
      connection_change_callback_ = nullptr;

  ScopedTaskSafety task_safety_;
  internal::PeerConnectionIntegrationTestBase* test_ = nullptr;
};

class MockRtcEventLogOutput : public RtcEventLogOutput {
 public:
  ~MockRtcEventLogOutput() override = default;
  MOCK_METHOD(bool, IsActive, (), (const, override));
  MOCK_METHOD(bool, Write, (absl::string_view), (override));
};

// This helper object is used for both specifying how many audio/video frames
// are expected to be received for a caller/callee. It provides helper functions
// to specify these expectations. The object initially starts in a state of no
// expectations.
class MediaExpectations {
 public:
  enum ExpectFrames {
    kExpectSomeFrames,
    kExpectNoFrames,
    kNoExpectation,
  };

  void ExpectBidirectionalAudioAndVideo() {
    ExpectBidirectionalAudio();
    ExpectBidirectionalVideo();
  }

  void ExpectBidirectionalAudio() {
    CallerExpectsSomeAudio();
    CalleeExpectsSomeAudio();
  }

  void ExpectNoAudio() {
    CallerExpectsNoAudio();
    CalleeExpectsNoAudio();
  }

  void ExpectBidirectionalVideo() {
    CallerExpectsSomeVideo();
    CalleeExpectsSomeVideo();
  }

  void ExpectNoVideo() {
    CallerExpectsNoVideo();
    CalleeExpectsNoVideo();
  }

  void CallerExpectsSomeAudioAndVideo() {
    CallerExpectsSomeAudio();
    CallerExpectsSomeVideo();
  }

  void CalleeExpectsSomeAudioAndVideo() {
    CalleeExpectsSomeAudio();
    CalleeExpectsSomeVideo();
  }

  // Caller's audio functions.
  void CallerExpectsSomeAudio(
      int expected_audio_frames = kDefaultExpectedAudioFrameCount) {
    caller_audio_expectation_ = kExpectSomeFrames;
    caller_audio_frames_expected_ = expected_audio_frames;
  }

  void CallerExpectsNoAudio() {
    caller_audio_expectation_ = kExpectNoFrames;
    caller_audio_frames_expected_ = 0;
  }

  // Caller's video functions.
  void CallerExpectsSomeVideo(
      int expected_video_frames = kDefaultExpectedVideoFrameCount) {
    caller_video_expectation_ = kExpectSomeFrames;
    caller_video_frames_expected_ = expected_video_frames;
  }

  void CallerExpectsNoVideo() {
    caller_video_expectation_ = kExpectNoFrames;
    caller_video_frames_expected_ = 0;
  }

  // Callee's audio functions.
  void CalleeExpectsSomeAudio(
      int expected_audio_frames = kDefaultExpectedAudioFrameCount) {
    callee_audio_expectation_ = kExpectSomeFrames;
    callee_audio_frames_expected_ = expected_audio_frames;
  }

  void CalleeExpectsNoAudio() {
    callee_audio_expectation_ = kExpectNoFrames;
    callee_audio_frames_expected_ = 0;
  }

  // Callee's video functions.
  void CalleeExpectsSomeVideo(
      int expected_video_frames = kDefaultExpectedVideoFrameCount) {
    callee_video_expectation_ = kExpectSomeFrames;
    callee_video_frames_expected_ = expected_video_frames;
  }

  void CalleeExpectsNoVideo() {
    callee_video_expectation_ = kExpectNoFrames;
    callee_video_frames_expected_ = 0;
  }

  ExpectFrames caller_audio_expectation_ = kNoExpectation;
  ExpectFrames caller_video_expectation_ = kNoExpectation;
  ExpectFrames callee_audio_expectation_ = kNoExpectation;
  ExpectFrames callee_video_expectation_ = kNoExpectation;
  int caller_audio_frames_expected_ = 0;
  int caller_video_frames_expected_ = 0;
  int callee_audio_frames_expected_ = 0;
  int callee_video_frames_expected_ = 0;
};

class MockIceTransport : public IceTransportInterface {
 public:
  MockIceTransport(const std::string& name, int component)
      : internal_(std::make_unique<FakeIceTransportInternal>(name,
                                                             component,
                                                             nullptr)) {}
  ~MockIceTransport() override = default;
  IceTransportInternal* internal() override { return internal_.get(); }

 private:
  std::unique_ptr<FakeIceTransportInternal> internal_;
};

class MockIceTransportFactory : public IceTransportFactory {
 public:
  ~MockIceTransportFactory() override = default;
  scoped_refptr<IceTransportInterface> CreateIceTransport(
      const std::string& transport_name,
      int component,
      IceTransportInit init) override {
    RecordIceTransportCreated();
    return make_ref_counted<MockIceTransport>(transport_name, component);
  }
  MOCK_METHOD(void, RecordIceTransportCreated, ());
};

// Tests two PeerConnections connecting to each other end-to-end, using a
// virtual network, fake A/V capture and fake encoder/decoders. The
// PeerConnections share the threads/socket servers, but use separate versions
// of everything else (including "PeerConnectionFactory"s).
namespace internal {
class PeerConnectionIntegrationTestBase : public ::testing::Test {
 public:
  static constexpr char kCallerName[] = "Caller";
  static constexpr char kCalleeName[] = "Callee";

  explicit PeerConnectionIntegrationTestBase(Environment env,
                                             SdpSemantics sdp_semantics);
  PeerConnectionIntegrationTestBase(Environment env,
                                    SdpSemantics sdp_semantics,
                                    TimeController* time_controller);
  ~PeerConnectionIntegrationTestBase() override;

  virtual std::unique_ptr<PeerConnectionIntegrationWrapper>
  CreatePeerConnectionWrapperInternal(const std::string& debug_name,
                                      Environment env);

  virtual Waiter GetWaiter(WaitUntilSettings overrides = {}) = 0;
  virtual TimeController* time_controller() { return nullptr; }
  virtual std::unique_ptr<Thread> CreateThread(absl::string_view name) = 0;

  bool SignalingStateStable() {
    return caller_->SignalingStateStable() && callee_->SignalingStateStable();
  }

  bool PeerConnectionStateIs(
      PeerConnectionInterface::PeerConnectionState state) {
    return caller_->pc()->peer_connection_state() == state &&
           callee_->pc()->peer_connection_state() == state;
  }

  bool DtlsConnected();

  // Sets field trials to pass to created PeerConnectionWrapper.
  // Must be called before PeerConnectionWrappers are created.
  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);

  // When `event_log_factory` is null, the default implementation of the event
  // log factory will be used.
  std::unique_ptr<PeerConnectionIntegrationWrapper> 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 = true);

  std::unique_ptr<PeerConnectionIntegrationWrapper>
  CreatePeerConnectionWrapperWithFakeRtcEventLog(
      const std::string& debug_name,
      const PeerConnectionFactory::Options* options,
      const RTCConfiguration* config,
      PeerConnectionDependencies dependencies);

  bool CreatePeerConnectionWrappers() {
    return CreatePeerConnectionWrappersWithConfig(
        PeerConnectionInterface::RTCConfiguration(),
        PeerConnectionInterface::RTCConfiguration());
  }

  bool CreatePeerConnectionWrappersWithSdpSemantics(
      SdpSemantics caller_semantics,
      SdpSemantics callee_semantics);

  bool CreatePeerConnectionWrappersWithConfig(
      const PeerConnectionInterface::RTCConfiguration& caller_config,
      const PeerConnectionInterface::RTCConfiguration& callee_config,
      bool create_media_engine = true);

  bool CreatePeerConnectionWrappersWithConfigAndDeps(
      const PeerConnectionInterface::RTCConfiguration& caller_config,
      PeerConnectionDependencies caller_dependencies,
      const PeerConnectionInterface::RTCConfiguration& callee_config,
      PeerConnectionDependencies callee_dependencies);

  bool CreatePeerConnectionWrappersWithOptions(
      const PeerConnectionFactory::Options& caller_options,
      const PeerConnectionFactory::Options& callee_options);

  bool CreatePeerConnectionWrappersWithFakeRtcEventLog();

  std::unique_ptr<PeerConnectionIntegrationWrapper>
  CreatePeerConnectionWrapperWithAlternateKey();

  bool CreateOneDirectionalPeerConnectionWrappers(bool caller_to_callee);

  bool CreatePeerConnectionWrappersWithoutMediaEngine();

  TestTurnServer* CreateTurnServer(
      SocketAddress internal_address,
      SocketAddress external_address,
      ProtocolType type = ProtocolType::PROTO_UDP,
      const std::string& common_name = "test turn server");

  TestTurnCustomizer* CreateTurnCustomizer();

  // Checks that the function counters for a TestTurnCustomizer are greater than
  // 0.
  void ExpectTurnCustomizerCountersIncremented(
      TestTurnCustomizer* turn_customizer);

  // Once called, SDP blobs and ICE candidates will be automatically signaled
  // between PeerConnections.
  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();

  void SetSignalingDelayMs(int delay_ms);

  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);

  Thread* network_thread() { return network_thread_.get(); }

  VirtualSocketServer* virtual_socket_server() { return ss_.get(); }

  PeerConnectionIntegrationWrapper* caller() { return caller_.get(); }

  // Destroy peer connections.
  // This can be used to ensure that all pointers to on-stack mocks
  // get dropped before exit.
  void DestroyPeerConnections();

  void DestroyTurnServers();
  void DestroyThreads();

  // Set the `caller_` to the `wrapper` passed in and return the
  // original `caller_`.
  PeerConnectionIntegrationWrapper* SetCallerPcWrapperAndReturnCurrent(
      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);

  FirewallSocketServer* firewall() const { return fss_.get(); }

  // Expects the provided number of new frames to be received within
  // 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);

  void ClosePeerConnections();

  void TestNegotiatedCipherSuite(const RTCConfiguration& caller_config,
                                 const RTCConfiguration& callee_config,
                                 int expected_cipher_suite);

  void TestGcmNegotiationUsesCipherSuite(bool local_gcm_enabled,
                                         bool remote_gcm_enabled,
                                         bool aes_ctr_enabled,
                                         int expected_cipher_suite);

 protected:
  void OverrideLoggingLevelForTest(LoggingSeverity new_severity);

  SdpSemantics sdp_semantics_;
  const Environment env_;

  virtual void ExecuteTask(TaskQueueBase& task_queue,
                           absl::AnyInvocable<void()> task) = 0;

 private:
  // Support for optionally changing the default logging level for the duration
  // of the test. Scoped wider than other member variables to also affect
  // logging that's done in destructors.
  class ScopedSetLoggingLevel;
  std::unique_ptr<ScopedSetLoggingLevel> overridden_logging_level_;

  // `ss_` is used by `network_thread_` so it must be destroyed later.
  std::unique_ptr<VirtualSocketServer> ss_;
  std::unique_ptr<FirewallSocketServer> fss_;

  // `network_thread_` and `worker_thread_` are used by both
  // `caller_` and `callee_` so they must be destroyed
  // later.
  std::unique_ptr<Thread> network_thread_;
  std::unique_ptr<Thread> worker_thread_;
  // The turn servers and turn customizers should be accessed & deleted on the
  // network thread to avoid a race with the socket read/write that occurs
  // on the network thread.
  std::vector<std::unique_ptr<TestTurnServer>> turn_servers_;
  std::vector<std::unique_ptr<TestTurnCustomizer>> turn_customizers_;
  std::unique_ptr<PeerConnectionIntegrationWrapper> caller_;
  std::unique_ptr<PeerConnectionIntegrationWrapper> callee_;
  std::string field_trials_;
  std::map<std::string, std::string> field_trials_overrides_;
};
}  // namespace internal

class PeerConnectionIntegrationBaseTest
    : public internal::PeerConnectionIntegrationTestBase {
 public:
  explicit PeerConnectionIntegrationBaseTest(SdpSemantics sdp_semantics);
  ~PeerConnectionIntegrationBaseTest() override;

  test::RunLoop& run_loop() { return run_loop_; }

  void ExecuteTask(TaskQueueBase& task_queue,
                   absl::AnyInvocable<void()> task) override;

  Waiter GetWaiter(WaitUntilSettings overrides = {}) override;

  std::unique_ptr<Thread> CreateThread(absl::string_view name) override;

 protected:
  test::RunLoop run_loop_;
};

class PeerConnectionIntegrationTestWithSimulatedTime
    : public internal::PeerConnectionIntegrationTestBase {
 public:
  explicit PeerConnectionIntegrationTestWithSimulatedTime(
      SdpSemantics sdp_semantics);

  void ExecuteTask(TaskQueueBase& task_queue,
                   absl::AnyInvocable<void()> task) override;

  Waiter GetWaiter(WaitUntilSettings overrides = {}) override;
  TimeController* time_controller() { return time_controller_.get(); }
  std::unique_ptr<Thread> CreateThread(absl::string_view name) override;

  ~PeerConnectionIntegrationTestWithSimulatedTime() override;

 private:
  PeerConnectionIntegrationTestWithSimulatedTime(
      SdpSemantics sdp_semantics,
      std::unique_ptr<GlobalSimulatedTimeController> time_controller);

 protected:
  std::unique_ptr<GlobalSimulatedTimeController> time_controller_;
};

template <typename Base>
class PeerConnectionIntegrationIceStatesTestBase
    : public Base,
      public ::testing::WithParamInterface<
          std::tuple<SdpSemantics, std::tuple<std::string, uint32_t>>> {
 protected:
  PeerConnectionIntegrationIceStatesTestBase()
      : Base(std::get<0>(this->GetParam())) {
    port_allocator_flags_ = std::get<1>(std::get<1>(this->GetParam()));
  }

  void StartStunServer(const SocketAddress& server_address) {
    stun_server_ = TestStunServer::Create(
        this->env_, server_address, *this->firewall(), *this->network_thread());
  }

  bool TestIPv6() {
    return (port_allocator_flags_ & PORTALLOCATOR_ENABLE_IPV6);
  }

  std::vector<SocketAddress> CallerAddresses() {
    std::vector<SocketAddress> addresses;
    addresses.push_back(SocketAddress("1.1.1.1", 0));
    if (TestIPv6()) {
      addresses.push_back(SocketAddress("1111:0:a:b:c:d:e:f", 0));
    }
    return addresses;
  }

  std::vector<SocketAddress> CalleeAddresses() {
    std::vector<SocketAddress> addresses;
    addresses.push_back(SocketAddress("2.2.2.2", 0));
    if (TestIPv6()) {
      addresses.push_back(SocketAddress("2222:0:a:b:c:d:e:f", 0));
    }
    return addresses;
  }

  void SetUpNetworkInterfaces() {
    // Remove the default interfaces added by the test infrastructure.
    this->caller()->network_manager()->RemoveInterface(kDefaultLocalAddress);
    this->callee()->network_manager()->RemoveInterface(kDefaultLocalAddress);

    // Add network addresses for test.
    for (const auto& caller_address : CallerAddresses()) {
      this->caller()->network_manager()->AddInterface(caller_address);
    }
    for (const auto& callee_address : CalleeAddresses()) {
      this->callee()->network_manager()->AddInterface(callee_address);
    }
  }

  uint32_t port_allocator_flags() const { return port_allocator_flags_; }

 private:
  uint32_t port_allocator_flags_;
  TestStunServer::StunServerPtr stun_server_;
};

}  // namespace webrtc

#endif  // PC_TEST_INTEGRATION_TEST_HELPERS_H_
