chromium/chrome/browser/ash/net/network_diagnostics/fake_network_context.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_NET_NETWORK_DIAGNOSTICS_FAKE_NETWORK_CONTEXT_H_
#define CHROME_BROWSER_ASH_NET_NETWORK_DIAGNOSTICS_FAKE_NETWORK_CONTEXT_H_

#include <memory>
#include <optional>
#include <utility>

#include "base/containers/circular_deque.h"
#include "base/containers/span.h"
#include "chrome/browser/ash/net/network_diagnostics/fake_tcp_connected_socket.h"
#include "chrome/browser/ash/net/network_diagnostics/fake_udp_socket.h"
#include "services/network/test/test_network_context.h"

namespace ash::network_diagnostics {

// Used in unit tests, the FakeNetworkContext class simulates the behavior of a
// network context.
class FakeNetworkContext : public network::TestNetworkContext {
 public:
  struct DnsResult {
   public:
    DnsResult(int32_t result,
              net::ResolveErrorInfo resolve_error_info,
              std::optional<net::AddressList> resolved_addresses,
              std::optional<net::HostResolverEndpointResults>
                  endpoint_results_with_metadata);
    ~DnsResult();

    int result_;
    net::ResolveErrorInfo resolve_error_info_;
    std::optional<net::AddressList> resolved_addresses_;
    std::optional<net::HostResolverEndpointResults>
        endpoint_results_with_metadata_;
  };
  FakeNetworkContext();
  FakeNetworkContext(const FakeNetworkContext&) = delete;
  FakeNetworkContext& operator=(const FakeNetworkContext&) = delete;
  ~FakeNetworkContext() override;

  // network::TestNetworkContext:
  void ResolveHost(
      network::mojom::HostResolverHostPtr host,
      const net::NetworkAnonymizationKey& network_anonymization_key,
      network::mojom::ResolveHostParametersPtr optional_parameters,
      mojo::PendingRemote<network::mojom::ResolveHostClient> response_client)
      override;

  void CreateTCPConnectedSocket(
      const std::optional<net::IPEndPoint>& local_addr,
      const net::AddressList& remote_addr_list,
      network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
      mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
      mojo::PendingRemote<network::mojom::SocketObserver> observer,
      CreateTCPConnectedSocketCallback callback) override;

  void CreateUDPSocket(
      mojo::PendingReceiver<network::mojom::UDPSocket> receiver,
      mojo::PendingRemote<network::mojom::UDPSocketListener> listener) override;

  // Sets the fake TCP connect code. TODO(khegde): Change this to
  // SetTCPConnectCompleteCode.
  void SetTCPConnectCode(std::optional<net::Error>& tcp_connect_code);

  // Sets the fake TLS upgrade code.
  void SetTLSUpgradeCode(std::optional<net::Error>& tls_upgrade_code);

  // Sets the fake UDP connect code.
  void SetUdpConnectCode(net::Error udp_connect_code);

  // Sets the fake UDP send code.
  void SetUdpSendCode(net::Error udp_send_code);

  // Sets the state to mimic a fake disconnect during a UDP send attempt.
  void SetDisconnectDuringUdpSendAttempt(bool disconnect);

  // Sets the fake UDP on received code.
  void SetUdpOnReceivedCode(net::Error udp_on_received_code);

  // Sets the fake UDP on received data.
  void SetUdpOnReceivedData(base::span<const uint8_t> udp_on_received_data);

  // Sets the state to mimic a fake disconnect after receiving successful send
  // confirmation, but before receiving any data.
  void SetDisconnectDuringUdpReceiveAttempt(bool disconnect);

  // Sets the task environment used in testing. Used to fast forward the clock.
  void SetTaskEnvironmentForTesting(
      content::BrowserTaskEnvironment* task_environment);

  // Sets the UDP connection delay.
  void SetUdpConnectionDelay(base::TimeDelta connection_delay);

  // Sets the UDP send delay.
  void SetUdpSendDelay(base::TimeDelta send_delay);

  // Sets the UDP receive delay.
  void SetUdpReceiveDelay(base::TimeDelta receive_delay);

  // Sets the fake DNS result. Used to test a single host resolution.
  void set_fake_dns_result(std::unique_ptr<DnsResult> fake_dns_result) {
    CHECK(fake_dns_results_.empty());
    fake_dns_result_ = std::move(fake_dns_result);
  }

  // Sets the deque of fake DNS results. Used to test a sequence of host
  // resolutions.
  void set_fake_dns_results(
      base::circular_deque<std::unique_ptr<DnsResult>> fake_dns_results) {
    CHECK(!fake_dns_result_);
    fake_dns_results_ = std::move(fake_dns_results);
  }

  // If set to true, the binding pipe will be disconnected when attempting to
  // connect.
  void set_disconnect_during_host_resolution(bool disconnect) {
    host_resolution_disconnect_ = disconnect;
  }

  // If set to true, the binding pipe will be disconnected when attempting to
  // connect.
  void set_disconnect_during_tcp_connection_attempt(bool disconnect) {
    tcp_connection_attempt_disconnect_ = disconnect;
  }

  // If set to true, the binding pipe will be disconnected when attempting to
  // connect.
  void set_disconnect_during_tls_upgrade_attempt(bool disconnect) {
    tls_upgrade_attempt_disconnect_ = disconnect;
  }

  // If set to true, the binding pipe will be disconnected when attempting to
  // connect.
  void set_disconnect_during_udp_connection_attempt(bool disconnect) {
    udp_connection_attempt_disconnect_ = disconnect;
  }

 private:
  // Fake DNS lookup result. Persists across resolutions.
  // Cannot be set together with |fake_dns_results_|.
  std::unique_ptr<DnsResult> fake_dns_result_;
  // Fake DNS lookup results -- for every query the resolver pops and returns
  // the front entry.
  // Cannot be set together with |fake_dns_result_|.
  base::circular_deque<std::unique_ptr<DnsResult>> fake_dns_results_;
  // Provides the TCP socket functionality for tests.
  std::unique_ptr<FakeTCPConnectedSocket> fake_tcp_connected_socket_;
  // Provides the UDP socket functionality for tests.
  std::unique_ptr<FakeUdpSocket> fake_udp_socket_;
  // TCP connect code corresponding to the connection attempt.
  net::Error tcp_connect_code_ = net::OK;
  // UDP connect code corresponding to the connection attempt.
  net::Error udp_connect_code_ = net::OK;

  // Used to mimic the scenario where network::mojom::HostResolver receiver
  // is disconnected.
  bool host_resolution_disconnect_ = false;
  // Used to mimic the scenario where network::mojom::TCPConnectedSocket
  // receiver is disconnected.
  bool tcp_connection_attempt_disconnect_ = false;
  // Used to mimic the scenario where network::mojom::TLSClientSocket receiver
  // is disconnected.
  bool tls_upgrade_attempt_disconnect_ = false;
  // Used to mimic the scenario where network::mojom::UDPSocket receiver is
  // disconnected while connecting.
  bool udp_connection_attempt_disconnect_ = false;
};

}  // namespace ash::network_diagnostics

#endif  // CHROME_BROWSER_ASH_NET_NETWORK_DIAGNOSTICS_FAKE_NETWORK_CONTEXT_H_