/*
 *  Copyright 2004 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.
 */

#include "rtc_base/network.h"

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <span>
#include <string>
#include <utility>
#include <vector>

#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "api/environment/environment.h"
#include "api/field_trials.h"
#include "api/sequence_checker.h"
#include "rtc_base/checks.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
#include "rtc_base/net_helpers.h"
#include "rtc_base/net_test_helpers.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/network_monitor.h"
#include "rtc_base/network_monitor_factory.h"
#include "rtc_base/physical_socket_server.h"
#include "rtc_base/socket_address.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/wait_until.h"

// IWYU pragma: begin_keep
#if defined(WEBRTC_POSIX)
#include <net/if.h>
#include <sys/types.h>
#include <unistd.h>

#include "rtc_base/ifaddrs_converter.h"
#endif  // defined(WEBRTC_POSIX)
// IWYU pragma: end_keep

using ::testing::Contains;
using ::testing::IsTrue;
using ::testing::Not;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;

#define MAYBE_SKIP_IPV4                        \
  if (!::webrtc::HasIPv4Enabled()) {           \
    RTC_LOG(LS_INFO) << "No IPv4... skipping"; \
    return;                                    \
  }

namespace webrtc {

namespace {

IPAddress IPFromString(absl::string_view str) {
  IPAddress ip;
  RTC_CHECK(IPFromString(str, &ip));
  return ip;
}

class FakeNetworkMonitor : public NetworkMonitorInterface {
 public:
  void Start() override { started_ = true; }
  void Stop() override { started_ = false; }
  bool started() { return started_; }
  InterfaceInfo GetInterfaceInfo(absl::string_view if_name) override {
    InterfaceInfo if_info = {
        .adapter_type = ADAPTER_TYPE_UNKNOWN,
        .available = absl::c_count(unavailable_adapters_, if_name) == 0,
    };
    if (absl::StartsWith(if_name, "wifi")) {
      if_info.adapter_type = ADAPTER_TYPE_WIFI;
    } else if (absl::StartsWith(if_name, "cellular")) {
      if_info.adapter_type = ADAPTER_TYPE_CELLULAR;
    }
    return if_info;
  }

  // Used to test IsAdapterAvailable.
  void set_unavailable_adapters(std::vector<std::string> unavailable_adapters) {
    unavailable_adapters_ = unavailable_adapters;
  }

  bool SupportsBindSocketToNetwork() const override { return true; }

  NetworkBindingResult BindSocketToNetwork(int socket_fd,
                                           const IPAddress& address,
                                           absl::string_view if_name) override {
    if (absl::c_count(addresses_, address) > 0) {
      return NetworkBindingResult::SUCCESS;
    }

    for (auto const& iter : adapters_) {
      if (absl::StrContains(if_name, iter)) {
        return NetworkBindingResult::SUCCESS;
      }
    }
    return NetworkBindingResult::ADDRESS_NOT_FOUND;
  }

  void set_ip_addresses(std::vector<IPAddress> addresses) {
    addresses_ = addresses;
  }

  void set_adapters(std::vector<std::string> adapters) { adapters_ = adapters; }

  void InovkeNetworksChangedCallbackForTesting() {
    InvokeNetworksChangedCallback();
  }

 private:
  bool started_ = false;
  std::vector<std::string> adapters_;
  std::vector<std::string> unavailable_adapters_;
  std::vector<IPAddress> addresses_;
};

class FakeNetworkMonitorFactory : public NetworkMonitorFactory {
 public:
  FakeNetworkMonitorFactory() {}
  NetworkMonitorInterface* CreateNetworkMonitor(
      const FieldTrialsView& field_trials) override {
    return new FakeNetworkMonitor();
  }
};

bool SameNameAndPrefix(const Network& a, const Network& b) {
  if (a.name() != b.name()) {
    RTC_LOG(LS_INFO) << "Different interface names.";
    return false;
  }
  if (a.prefix_length() != b.prefix_length() || a.prefix() != b.prefix()) {
    RTC_LOG(LS_INFO) << "Different IP prefixes.";
    return false;
  }
  return true;
}

std::vector<const Network*> CopyNetworkPointers(
    const std::vector<std::unique_ptr<Network>>& owning_list) {
  std::vector<const Network*> ptr_list;
  ptr_list.reserve(owning_list.size());
  for (const auto& network : owning_list) {
    ptr_list.push_back(network.get());
  }
  return ptr_list;
}

}  // namespace

class NetworkTest : public ::testing::Test {
 public:
  void OnNetworksChanged() { callback_called_ = true; }

  void MergeNetworkList(BasicNetworkManager& network_manager,
                        std::vector<std::unique_ptr<Network>> list,
                        bool* changed) {
    // MergeNetworkList is protected in `BasicNetworkManager`, but
    // `NetworkTest` is a friend.
    network_manager.MergeNetworkList(std::move(list), changed);
  }

  bool IsIgnoredNetwork(BasicNetworkManager& network_manager,
                        const Network& network) {
    RTC_DCHECK_RUN_ON(network_manager.thread_);
    return network_manager.IsIgnoredNetwork(network);
  }

  IPAddress QueryDefaultLocalAddress(BasicNetworkManager& network_manager,
                                     int family) {
    RTC_DCHECK_RUN_ON(network_manager.thread_);
    return network_manager.QueryDefaultLocalAddress(family);
  }

  std::vector<std::unique_ptr<Network>> GetNetworks(
      const BasicNetworkManager& network_manager,
      bool include_ignored) {
    RTC_DCHECK_RUN_ON(network_manager.thread_);
    std::vector<std::unique_ptr<Network>> list;
    network_manager.CreateNetworks(include_ignored, &list);
    return list;
  }

  FakeNetworkMonitor* GetNetworkMonitor(BasicNetworkManager& network_manager) {
    RTC_DCHECK_RUN_ON(network_manager.thread_);
    return static_cast<FakeNetworkMonitor*>(
        network_manager.network_monitor_.get());
  }
  void ClearNetworks(BasicNetworkManager& network_manager) {
    network_manager.networks_.clear();
    network_manager.networks_map_.clear();
  }

  AdapterType GetAdapterType(BasicNetworkManager& network_manager) {
    std::vector<const Network*> list = network_manager.GetNetworks();
    RTC_CHECK_EQ(1, list.size());
    return list[0]->type();
  }

#if defined(WEBRTC_POSIX)
  // Separated from CreateNetworks for tests.
  static void CallConvertIfAddrs(
      const BasicNetworkManager& network_manager,
      struct ifaddrs* interfaces,
      bool include_ignored,
      std::vector<std::unique_ptr<Network>>* networks) {
    RTC_DCHECK_RUN_ON(network_manager.thread_);
    // Use the base IfAddrsConverter for test cases.
    std::unique_ptr<IfAddrsConverter> ifaddrs_converter(new IfAddrsConverter());
    network_manager.ConvertIfAddrs(interfaces, ifaddrs_converter.get(),
                                   include_ignored, networks);
  }

  struct sockaddr_in6* CreateIpv6Addr(absl::string_view ip_string,
                                      uint32_t scope_id) {
    struct sockaddr_in6* ipv6_addr =
        static_cast<struct sockaddr_in6*>(malloc(sizeof(struct sockaddr_in6)));
    memset(ipv6_addr, 0, sizeof(struct sockaddr_in6));
    ipv6_addr->sin6_family = AF_INET6;
    ipv6_addr->sin6_scope_id = scope_id;
    IPAddress ip;
    IPFromString(ip_string, &ip);
    ipv6_addr->sin6_addr = ip.ipv6_address();
    return ipv6_addr;
  }

  // Pointers created here need to be released via ReleaseIfAddrs.
  struct ifaddrs* AddIpv6Address(struct ifaddrs* list,
                                 char* if_name,
                                 absl::string_view ipv6_address,
                                 absl::string_view ipv6_netmask,
                                 uint32_t scope_id) {
    struct ifaddrs* if_addr = new struct ifaddrs;
    memset(if_addr, 0, sizeof(struct ifaddrs));
    if_addr->ifa_name = if_name;
    if_addr->ifa_addr = reinterpret_cast<struct sockaddr*>(
        CreateIpv6Addr(ipv6_address, scope_id));
    if_addr->ifa_netmask =
        reinterpret_cast<struct sockaddr*>(CreateIpv6Addr(ipv6_netmask, 0));
    if_addr->ifa_next = list;
    if_addr->ifa_flags = IFF_RUNNING;
    return if_addr;
  }

  struct ifaddrs* InstallIpv6Network(char* if_name,
                                     absl::string_view ipv6_address,
                                     absl::string_view ipv6_mask,
                                     BasicNetworkManager& network_manager) {
    ifaddrs* addr_list = nullptr;
    addr_list = AddIpv6Address(addr_list, if_name, ipv6_address, ipv6_mask, 0);
    std::vector<std::unique_ptr<Network>> result;
    bool changed;
    CallConvertIfAddrs(network_manager, addr_list, true, &result);
    network_manager.MergeNetworkList(std::move(result), &changed);
    return addr_list;
  }

  struct sockaddr_in* CreateIpv4Addr(absl::string_view ip_string) {
    struct sockaddr_in* ipv4_addr =
        static_cast<struct sockaddr_in*>(malloc(sizeof(struct sockaddr_in)));
    memset(ipv4_addr, 0, sizeof(struct sockaddr_in));
    ipv4_addr->sin_family = AF_INET;
    IPAddress ip;
    IPFromString(ip_string, &ip);
    ipv4_addr->sin_addr = ip.ipv4_address();
    return ipv4_addr;
  }

  // Pointers created here need to be released via ReleaseIfAddrs.
  struct ifaddrs* AddIpv4Address(struct ifaddrs* list,
                                 char* if_name,
                                 absl::string_view ipv4_address,
                                 absl::string_view ipv4_netmask) {
    struct ifaddrs* if_addr = new struct ifaddrs;
    memset(if_addr, 0, sizeof(struct ifaddrs));
    if_addr->ifa_name = if_name;
    if_addr->ifa_addr =
        reinterpret_cast<struct sockaddr*>(CreateIpv4Addr(ipv4_address));
    if_addr->ifa_netmask =
        reinterpret_cast<struct sockaddr*>(CreateIpv4Addr(ipv4_netmask));
    if_addr->ifa_next = list;
    if_addr->ifa_flags = IFF_RUNNING;
    return if_addr;
  }

  struct ifaddrs* InstallIpv4Network(char* if_name,
                                     absl::string_view ipv4_address,
                                     absl::string_view ipv4_mask,
                                     BasicNetworkManager& network_manager) {
    ifaddrs* addr_list = nullptr;
    addr_list = AddIpv4Address(addr_list, if_name, ipv4_address, ipv4_mask);
    std::vector<std::unique_ptr<Network>> result;
    bool changed;
    CallConvertIfAddrs(network_manager, addr_list, true, &result);
    network_manager.MergeNetworkList(std::move(result), &changed);
    return addr_list;
  }

  void ReleaseIfAddrs(struct ifaddrs* list) {
    struct ifaddrs* if_addr = list;
    while (if_addr != nullptr) {
      struct ifaddrs* next_addr = if_addr->ifa_next;
      free(if_addr->ifa_addr);
      free(if_addr->ifa_netmask);
      delete if_addr;
      if_addr = next_addr;
    }
  }
#endif  // defined(WEBRTC_POSIX)

 protected:
  const Environment env_ = CreateTestEnvironment();
  test::RunLoop main_thread_;
  bool callback_called_ = false;
};

class TestBasicNetworkManager : public BasicNetworkManager {
 public:
  using BasicNetworkManager::BasicNetworkManager;
  using BasicNetworkManager::QueryDefaultLocalAddress;
  using BasicNetworkManager::set_default_local_addresses;
};

// Test that the Network ctor works properly.
TEST_F(NetworkTest, TestNetworkConstruct) {
  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
                        IPAddress(0x12345600U), 24);
  EXPECT_EQ("test_eth0", ipv4_network1.name());
  EXPECT_EQ("Test Network Adapter 1", ipv4_network1.description());
  EXPECT_EQ(IPAddress(0x12345600U), ipv4_network1.prefix());
  EXPECT_EQ(24, ipv4_network1.prefix_length());
  EXPECT_EQ(AF_INET, ipv4_network1.family());
  EXPECT_FALSE(ipv4_network1.ignored());
  EXPECT_EQ(NetworkSlice::NO_SLICE, ipv4_network1.network_slice());
}

TEST_F(NetworkTest, TestIsIgnoredNetworkIgnoresIPsStartingWith0) {
  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
                        IPAddress(0x12345600U), 24, ADAPTER_TYPE_ETHERNET);
  Network ipv4_network2("test_eth1", "Test Network Adapter 2",
                        IPAddress(0x010000U), 24, ADAPTER_TYPE_ETHERNET);
  PhysicalSocketServer socket_server;
  BasicNetworkManager network_manager(env_, &socket_server);
  network_manager.StartUpdating();
  EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1));
  EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2));
}

// TODO(phoglund): Remove when ignore list goes away.
TEST_F(NetworkTest, TestIgnoreList) {
  Network ignore_me("ignore_me", "Ignore me please!", IPAddress(0x12345600U),
                    24);
  Network include_me("include_me", "Include me please!", IPAddress(0x12345600U),
                     24);
  PhysicalSocketServer socket_server;
  BasicNetworkManager default_network_manager(env_, &socket_server);
  default_network_manager.StartUpdating();
  EXPECT_FALSE(IsIgnoredNetwork(default_network_manager, ignore_me));
  EXPECT_FALSE(IsIgnoredNetwork(default_network_manager, include_me));

  BasicNetworkManager ignoring_network_manager(env_, &socket_server);
  std::vector<std::string> ignore_list;
  ignore_list.push_back("ignore_me");
  ignoring_network_manager.set_network_ignore_list(ignore_list);
  ignoring_network_manager.StartUpdating();
  EXPECT_TRUE(IsIgnoredNetwork(ignoring_network_manager, ignore_me));
  EXPECT_FALSE(IsIgnoredNetwork(ignoring_network_manager, include_me));
}

// Test is failing on Windows opt: b/11288214
TEST_F(NetworkTest, DISABLED_TestCreateNetworks) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  std::vector<std::unique_ptr<Network>> result = GetNetworks(manager, true);
  // We should be able to bind to any addresses we find.
  for (auto it = result.begin(); it != result.end(); ++it) {
    sockaddr_storage storage;
    memset(&storage, 0, sizeof(storage));
    IPAddress ip = (*it)->GetBestIP();
    SocketAddress bindaddress(ip, 0);
    bindaddress.SetScopeID((*it)->scope_id());
    // TODO(thaloun): Use Socket once it supports IPv6.
    int fd = static_cast<int>(socket(ip.family(), SOCK_STREAM, IPPROTO_TCP));
    if (fd > 0) {
      size_t ipsize = bindaddress.ToSockAddrStorage(&storage);
      EXPECT_GE(ipsize, 0U);
      int success = ::bind(fd, reinterpret_cast<sockaddr*>(&storage),
                           static_cast<int>(ipsize));
#if defined(WEBRTC_WIN)
      if (success)
        RTC_LOG_GLE(LS_ERROR) << "Socket bind failed.";
#endif
      EXPECT_EQ(0, success);
#if defined(WEBRTC_WIN)
      closesocket(fd);
#else
      close(fd);
#endif
    }
  }
}

// Test StartUpdating() and StopUpdating(). network_permission_state starts with
// ALLOWED.
TEST_F(NetworkTest, TestUpdateNetworks) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this] { OnNetworksChanged(); }});
  EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED,
            manager.enumeration_permission());
  manager.StartUpdating();
  main_thread_.Flush();
  EXPECT_TRUE(callback_called_);
  callback_called_ = false;
  // Callback should be triggered immediately when StartUpdating
  // is called, after network update signal is already sent.
  manager.StartUpdating();
  EXPECT_TRUE(manager.started());
  main_thread_.Flush();
  EXPECT_TRUE(callback_called_);
  manager.StopUpdating();
  EXPECT_TRUE(manager.started());
  manager.StopUpdating();
  EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED,
            manager.enumeration_permission());
  EXPECT_FALSE(manager.started());
  manager.StopUpdating();
  EXPECT_FALSE(manager.started());
  callback_called_ = false;
  // Callback should be triggered immediately after StartUpdating is called
  // when start_count_ is reset to 0.
  manager.StartUpdating();
  main_thread_.Flush();
  EXPECT_TRUE(callback_called_);
}

// Verify that MergeNetworkList() merges network lists properly.
TEST_F(NetworkTest, TestBasicMergeNetworkList) {
  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
                        IPAddress(0x12345600U), 24);
  Network ipv4_network2("test_eth1", "Test Network Adapter 2",
                        IPAddress(0x00010000U), 16);
  ipv4_network1.AddIP(IPAddress(0x12345678));
  ipv4_network2.AddIP(IPAddress(0x00010004));
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);

  // Add ipv4_network1 to the list of networks.
  std::vector<std::unique_ptr<Network>> list;
  list.push_back(ipv4_network1.Clone());
  bool changed;
      MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);
  list.clear();  // It is fine to call .clear() on a moved-from vector.

  std::vector<const Network*> current = manager.GetNetworks();
  EXPECT_EQ(1U, current.size());
  EXPECT_TRUE(SameNameAndPrefix(ipv4_network1, *current[0]));
  const Network* net1 = current[0];
  uint16_t net_id1 = net1->id();
  EXPECT_EQ(1, net_id1);

  // Replace ipv4_network1 with ipv4_network2.
  list.push_back(ipv4_network2.Clone());
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);
  list.clear();

  current = manager.GetNetworks();
  EXPECT_EQ(1U, current.size());
  EXPECT_TRUE(SameNameAndPrefix(ipv4_network2, *current[0]));
  const Network* net2 = current[0];
  uint16_t net_id2 = net2->id();
  // Network id will increase.
  EXPECT_LT(net_id1, net_id2);

  // Add Network2 back.
  list.push_back(ipv4_network1.Clone());
  list.push_back(ipv4_network2.Clone());
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);
  list.clear();

  // Verify that we get previous instances of Network objects.
  current = manager.GetNetworks();
  EXPECT_EQ(2U, current.size());
  EXPECT_TRUE((net1 == current[0] && net2 == current[1]) ||
              (net1 == current[1] && net2 == current[0]));
  EXPECT_TRUE((net_id1 == current[0]->id() && net_id2 == current[1]->id()) ||
              (net_id1 == current[1]->id() && net_id2 == current[0]->id()));

  // Call MergeNetworkList() again and verify that we don't get update
  // notification.
  list.push_back(ipv4_network2.Clone());
  list.push_back(ipv4_network1.Clone());
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_FALSE(changed);
  list.clear();

  // Verify that we get previous instances of Network objects.
  current = manager.GetNetworks();
  EXPECT_EQ(2U, current.size());
  EXPECT_TRUE((net1 == current[0] && net2 == current[1]) ||
              (net1 == current[1] && net2 == current[0]));
  EXPECT_TRUE((net_id1 == current[0]->id() && net_id2 == current[1]->id()) ||
              (net_id1 == current[1]->id() && net_id2 == current[0]->id()));
}

// Sets up some test IPv6 networks and appends them to list.
// Four networks are added - public and link local, for two interfaces.
void SetupNetworks(std::vector<std::unique_ptr<Network>>* list) {
  IPAddress ip;
  IPAddress prefix;
  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:ef12", &ip));
  EXPECT_TRUE(IPFromString("abcd::", &prefix));
  // First, fake link-locals.
  Network ipv6_eth0_linklocalnetwork("test_eth0", "Test NetworkAdapter 1",
                                     prefix, 64);
  ipv6_eth0_linklocalnetwork.AddIP(ip);
  EXPECT_TRUE(IPFromString("abcd::5678:abcd:ef12:3456", &ip));
  Network ipv6_eth1_linklocalnetwork("test_eth1", "Test NetworkAdapter 2",
                                     prefix, 64);
  ipv6_eth1_linklocalnetwork.AddIP(ip);
  // Public networks:
  EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &ip));
  prefix = TruncateIP(ip, 64);
  Network ipv6_eth0_publicnetwork1_ip1("test_eth0", "Test NetworkAdapter 1",
                                       prefix, 64);
  ipv6_eth0_publicnetwork1_ip1.AddIP(ip);
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip));
  prefix = TruncateIP(ip, 64);
  Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 1",
                                       prefix, 64);
  ipv6_eth1_publicnetwork1_ip1.AddIP(ip);
  list->push_back(ipv6_eth0_linklocalnetwork.Clone());
  list->push_back(ipv6_eth1_linklocalnetwork.Clone());
  list->push_back(ipv6_eth0_publicnetwork1_ip1.Clone());
  list->push_back(ipv6_eth1_publicnetwork1_ip1.Clone());
}

// Test that the basic network merging case works.
TEST_F(NetworkTest, TestIPv6MergeNetworkList) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this]() { OnNetworksChanged(); }});
  std::vector<std::unique_ptr<Network>> networks;
  SetupNetworks(&networks);
  std::vector<const Network*> original_list = CopyNetworkPointers(networks);
  bool changed = false;
  MergeNetworkList(manager, std::move(networks), &changed);
  EXPECT_TRUE(changed);
  std::vector<const Network*> list = manager.GetNetworks();
  // Verify that the original members are in the merged list.
  EXPECT_THAT(list, UnorderedElementsAreArray(original_list));
}

// Tests that when two network lists that describe the same set of networks are
// merged, that the changed callback is not called, and that the original
// objects remain in the result list.
TEST_F(NetworkTest, TestNoChangeMerge) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this]() { OnNetworksChanged(); }});
  std::vector<std::unique_ptr<Network>> networks;
  SetupNetworks(&networks);
  std::vector<const Network*> original_list = CopyNetworkPointers(networks);
  bool changed = false;
  MergeNetworkList(manager, std::move(networks), &changed);
  EXPECT_TRUE(changed);
  // Second list that describes the same networks but with new objects.
  std::vector<std::unique_ptr<Network>> second_networks;
  SetupNetworks(&second_networks);
  std::vector<const Network*> second_list =
      CopyNetworkPointers(second_networks);
  changed = false;
  MergeNetworkList(manager, std::move(second_networks), &changed);
  EXPECT_FALSE(changed);
  std::vector<const Network*> resulting_list = manager.GetNetworks();
  // Verify that the original members are in the merged list.
  EXPECT_THAT(resulting_list, UnorderedElementsAreArray(original_list));
  // Doublecheck that the new networks aren't in the list.
  for (const Network* network : second_list) {
    EXPECT_THAT(resulting_list, Not(Contains(network)));
  }
}

// Test that we can merge a network that is the same as another network but with
// a different IP. The original network should remain in the list, but have its
// IP changed.
TEST_F(NetworkTest, MergeWithChangedIP) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this]() { OnNetworksChanged(); }});
  std::vector<std::unique_ptr<Network>> original_list;
  SetupNetworks(&original_list);
  // Make a network that we're going to change.
  IPAddress ip;
  EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:faa:fee:faa", &ip));
  IPAddress prefix = TruncateIP(ip, 64);
  std::unique_ptr<Network> network_to_change = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", prefix, 64);
  std::unique_ptr<Network> changed_network = network_to_change->Clone();
  network_to_change->AddIP(ip);
  IPAddress changed_ip;
  EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:f00:f00:f00", &changed_ip));
  changed_network->AddIP(changed_ip);
  const Network* const network_to_change_ptr = network_to_change.get();
  original_list.push_back(std::move(network_to_change));
  const size_t original_size = original_list.size();
  bool changed = false;
  MergeNetworkList(manager, std::move(original_list), &changed);
  std::vector<std::unique_ptr<Network>> second_list;
  SetupNetworks(&second_list);
  second_list.push_back(std::move(changed_network));
  changed = false;
  MergeNetworkList(manager, std::move(second_list), &changed);
  EXPECT_TRUE(changed);
  std::vector<const Network*> list = manager.GetNetworks();
  EXPECT_EQ(original_size, list.size());
  // Make sure the original network is still in the merged list.
  EXPECT_THAT(list, Contains(network_to_change_ptr));
  EXPECT_EQ(changed_ip, network_to_change_ptr->GetIPs().at(0));
}

TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this]() { OnNetworksChanged(); }});
  std::vector<std::unique_ptr<Network>> original_list;
  SetupNetworks(&original_list);
  const Network* const network_ptr = original_list[2].get();
  bool changed = false;
  MergeNetworkList(manager, std::move(original_list), &changed);
  EXPECT_TRUE(changed);
  IPAddress ip;
  IPAddress check_ip;
  IPAddress prefix;
  // Add a second IP to the public network on eth0 (2401:fa00:4:1000/64).
  EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c6", &ip));
  prefix = TruncateIP(ip, 64);
  Network ipv6_eth0_publicnetwork1_ip2("test_eth0", "Test NetworkAdapter 1",
                                       prefix, 64);
  // This is the IP that already existed in the public network on eth0.
  EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &check_ip));
  ipv6_eth0_publicnetwork1_ip2.AddIP(ip);

  std::vector<std::unique_ptr<Network>> second_list;
  SetupNetworks(&second_list);
  second_list.push_back(ipv6_eth0_publicnetwork1_ip2.Clone());
  changed = false;
  const auto network_copy = second_list[2]->Clone();
  MergeNetworkList(manager, std::move(second_list), &changed);
  EXPECT_TRUE(changed);
  // There should still be four networks.
  std::vector<const Network*> list = manager.GetNetworks();
  EXPECT_EQ(4U, list.size());
  // Check the gathered IPs.
  int matchcount = 0;
  for (const Network* network : list) {
    if (SameNameAndPrefix(*network, *network_copy)) {
      ++matchcount;
      EXPECT_EQ(1, matchcount);
      // This should be the same network object as before.
      EXPECT_EQ(network, network_ptr);
      // But with two addresses now.
      EXPECT_THAT(network->GetIPs(),
                  UnorderedElementsAre(InterfaceAddress(check_ip),
                                       InterfaceAddress(ip)));
    } else {
      // Check the IP didn't get added anywhere it wasn't supposed to.
      EXPECT_THAT(network->GetIPs(), Not(Contains(InterfaceAddress(ip))));
    }
  }
}

// Test that merge correctly distinguishes multiple networks on an interface.
TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this]() { OnNetworksChanged(); }});
  std::vector<std::unique_ptr<Network>> original_list;
  SetupNetworks(&original_list);
  bool changed = false;
  MergeNetworkList(manager, std::move(original_list), &changed);
  EXPECT_TRUE(changed);
  IPAddress ip;
  IPAddress prefix;
  // A second network for eth0.
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:5bff:fee5:c3", &ip));
  prefix = TruncateIP(ip, 64);
  Network ipv6_eth0_publicnetwork2_ip1("test_eth0", "Test NetworkAdapter 1",
                                       prefix, 64);
  ipv6_eth0_publicnetwork2_ip1.AddIP(ip);
  std::vector<std::unique_ptr<Network>> second_list;
  SetupNetworks(&second_list);
  second_list.push_back(ipv6_eth0_publicnetwork2_ip1.Clone());
  changed = false;
  MergeNetworkList(manager, std::move(second_list), &changed);
  EXPECT_TRUE(changed);
  // There should be five networks now.
  std::vector<const Network*> list = manager.GetNetworks();
  EXPECT_EQ(5U, list.size());
  // Check the resulting addresses.
  for (const Network* network : list) {
    if (network->prefix() == ipv6_eth0_publicnetwork2_ip1.prefix() &&
        network->name() == ipv6_eth0_publicnetwork2_ip1.name()) {
      // Check the new network has 1 IP and that it's the correct one.
      EXPECT_EQ(1U, network->GetIPs().size());
      EXPECT_EQ(ip, network->GetIPs().at(0));
    } else {
      // Check the IP didn't get added anywhere it wasn't supposed to.
      EXPECT_THAT(network->GetIPs(), Not(Contains(InterfaceAddress(ip))));
    }
  }
}

// Test that DumpNetworks does not crash.
TEST_F(NetworkTest, TestCreateAndDumpNetworks) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();
  std::vector<std::unique_ptr<Network>> list = GetNetworks(manager, true);
  bool changed;
  MergeNetworkList(manager, std::move(list), &changed);
  manager.DumpNetworks();
}

TEST_F(NetworkTest, TestIPv6Toggle) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();
  bool ipv6_found = false;
  for (const auto& network : GetNetworks(manager, true)) {
    if (network->prefix().family() == AF_INET6) {
      ipv6_found = true;
      break;
    }
  }
  EXPECT_TRUE(ipv6_found);
}

// Test that when network interfaces are sorted and given preference values,
// IPv6 comes first.
TEST_F(NetworkTest, IPv6NetworksPreferredOverIPv4) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
                        IPAddress(0x12345600U), 24);
  ipv4_network1.AddIP(IPAddress(0x12345600U));

  IPAddress ip;
  IPAddress prefix;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip));
  prefix = TruncateIP(ip, 64);
  Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 2",
                                       prefix, 64);
  ipv6_eth1_publicnetwork1_ip1.AddIP(ip);

  std::vector<std::unique_ptr<Network>> list;
  list.push_back(ipv4_network1.Clone());
  list.push_back(ipv6_eth1_publicnetwork1_ip1.Clone());
  const Network* net1 = list[0].get();
  const Network* net2 = list[1].get();

  bool changed = false;
  MergeNetworkList(manager, std::move(list), &changed);
  ASSERT_TRUE(changed);
  // After sorting IPv6 network should be higher order than IPv4 networks.
  EXPECT_TRUE(net1->preference() < net2->preference());
}

// When two interfaces are equivalent in everything but name, they're expected
// to be preference-ordered by name. For example, "eth0" before "eth1".
TEST_F(NetworkTest, NetworksSortedByInterfaceName) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  auto eth0 = std::make_unique<Network>("test_eth0", "Test Network Adapter 1",
                                        IPAddress(0x65432100U), 24);
  eth0->AddIP(IPAddress(0x65432100U));
  auto eth1 = std::make_unique<Network>("test_eth1", "Test Network Adapter 2",
                                        IPAddress(0x12345600U), 24);
  eth1->AddIP(IPAddress(0x12345600U));
  std::vector<std::unique_ptr<Network>> list;
  const Network* eth0_ptr = eth0.get();
  const Network* eth1_ptr = eth1.get();
  // Add them to the list in the opposite of the expected sorted order, to
  // ensure sorting actually occurs.
  list.push_back(std::move(eth1));
  list.push_back(std::move(eth0));

  bool changed = false;
  MergeNetworkList(manager, std::move(list), &changed);
  ASSERT_TRUE(changed);
  // "test_eth0" should be preferred over "test_eth1".
  EXPECT_TRUE(eth0_ptr->preference() > eth1_ptr->preference());
}

TEST_F(NetworkTest, TestNetworkAdapterTypes) {
  Network wifi("wlan0", "Wireless Adapter", IPAddress(0x12345600U), 24,
               ADAPTER_TYPE_WIFI);
  EXPECT_EQ(ADAPTER_TYPE_WIFI, wifi.type());
  Network ethernet("eth0", "Ethernet", IPAddress(0x12345600U), 24,
                   ADAPTER_TYPE_ETHERNET);
  EXPECT_EQ(ADAPTER_TYPE_ETHERNET, ethernet.type());
  Network cellular("test_cell", "Cellular Adapter", IPAddress(0x12345600U), 24,
                   ADAPTER_TYPE_CELLULAR);
  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, cellular.type());
  Network vpn("bridge_test", "VPN Adapter", IPAddress(0x12345600U), 24,
              ADAPTER_TYPE_VPN);
  EXPECT_EQ(ADAPTER_TYPE_VPN, vpn.type());
  Network unknown("test", "Test Adapter", IPAddress(0x12345600U), 24,
                  ADAPTER_TYPE_UNKNOWN);
  EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, unknown.type());
}

#if defined(WEBRTC_POSIX)
// Verify that we correctly handle interfaces with no address.
TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) {
  ifaddrs list;
  memset(&list, 0, sizeof(list));
  list.ifa_name = const_cast<char*>("test_iface");

  std::vector<std::unique_ptr<Network>> result;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();
  CallConvertIfAddrs(manager, &list, true, &result);
  EXPECT_TRUE(result.empty());
}

// Verify that if there are two addresses on one interface, only one network
// is generated.
TEST_F(NetworkTest, TestConvertIfAddrsMultiAddressesOnOneInterface) {
  char if_name[20] = "rmnet0";
  ifaddrs* list = nullptr;
  list = AddIpv6Address(list, if_name, "1000:2000:3000:4000:0:0:0:1",
                        "FFFF:FFFF:FFFF:FFFF::", 0);
  list = AddIpv6Address(list, if_name, "1000:2000:3000:4000:0:0:0:2",
                        "FFFF:FFFF:FFFF:FFFF::", 0);
  std::vector<std::unique_ptr<Network>> result;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();
  CallConvertIfAddrs(manager, list, true, &result);
  EXPECT_EQ(1U, result.size());
  bool changed;
  // This ensures we release the objects created in CallConvertIfAddrs.
  MergeNetworkList(manager, std::move(result), &changed);
  ReleaseIfAddrs(list);
}

TEST_F(NetworkTest, TestConvertIfAddrsNotRunning) {
  ifaddrs list;
  memset(&list, 0, sizeof(list));
  list.ifa_name = const_cast<char*>("test_iface");
  sockaddr ifa_addr;
  ifa_addr.sa_family = AF_UNSPEC;
  sockaddr ifa_netmask;
  list.ifa_addr = &ifa_addr;
  list.ifa_netmask = &ifa_netmask;

  std::vector<std::unique_ptr<Network>> result;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();
  CallConvertIfAddrs(manager, &list, true, &result);
  EXPECT_TRUE(result.empty());
}

TEST_F(NetworkTest, TestConvertIfAddrsGetsNullAddr) {
  ifaddrs list;
  memset(&list, 0, sizeof(list));
  list.ifa_name = const_cast<char*>("test_iface");
  list.ifa_addr = nullptr;
  list.ifa_netmask = nullptr;

  std::vector<std::unique_ptr<Network>> result;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();
  CallConvertIfAddrs(manager, &list, true, &result);
  EXPECT_TRUE(result.empty());
}

// Tests that the network type can be determined from the network monitor when
// it would otherwise be unknown.
TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) {
  char if_name[20] = "wifi0";
  std::string ipv6_address = "1000:2000:3000:4000:0:0:0:1";
  std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::";
  const Environment env = env_;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager_without_monitor(
      env, &socket_server, /*network_monitor_factory=*/nullptr);
  manager_without_monitor.StartUpdating();
  // A network created without a network monitor will get UNKNOWN type.
  ifaddrs* addr_list = InstallIpv6Network(if_name, ipv6_address, ipv6_mask,
                                          manager_without_monitor);
  EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, GetAdapterType(manager_without_monitor));
  ReleaseIfAddrs(addr_list);

  // With the fake network monitor the type should be correctly determined.
  FakeNetworkMonitorFactory factory;
  BasicNetworkManager manager_with_monitor(env, &socket_server, &factory);
  manager_with_monitor.StartUpdating();
  // Add the same ipv6 address as before but it has the right network type
  // detected by the network monitor now.
  addr_list = InstallIpv6Network(if_name, ipv6_address, ipv6_mask,
                                 manager_with_monitor);
  EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager_with_monitor));
  ReleaseIfAddrs(addr_list);
}

// Test that the network type can be determined based on name matching in
// a few cases. Note that UNKNOWN type for non-matching strings has been tested
// in the above test.
TEST_F(NetworkTest, TestGetAdapterTypeFromNameMatching) {
  std::string ipv4_address1 = "192.0.0.121";
  std::string ipv4_mask = "255.255.255.0";
  std::string ipv6_address1 = "1000:2000:3000:4000:0:0:0:1";
  std::string ipv6_address2 = "1000:2000:3000:8000:0:0:0:1";
  std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::";
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();

  // IPSec interface; name is in form "ipsec<index>".
  char if_name[20] = "ipsec11";
  ifaddrs* addr_list =
      InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_VPN, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  // Tailscale interface; name is in form "tailscale<index>".
  strcpy(if_name, "tailscale0");
  addr_list = InstallIpv4Network(if_name, ipv4_address1, ipv4_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_VPN, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  strcpy(if_name, "lo0");
  addr_list = InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_LOOPBACK, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  strcpy(if_name, "eth0");
  addr_list = InstallIpv4Network(if_name, ipv4_address1, ipv4_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_ETHERNET, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  strcpy(if_name, "wlan0");
  addr_list = InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

#if defined(WEBRTC_IOS)
  strcpy(if_name, "pdp_ip0");
  addr_list = InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  strcpy(if_name, "en0");
  addr_list = InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

#elif defined(WEBRTC_ANDROID)
  strcpy(if_name, "rmnet0");
  addr_list = InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  strcpy(if_name, "v4-rmnet_data0");
  addr_list = InstallIpv6Network(if_name, ipv6_address2, ipv6_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);

  strcpy(if_name, "clat4");
  addr_list = InstallIpv4Network(if_name, ipv4_address1, ipv4_mask, manager);
  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);
#endif
}

// Test that an adapter won't be included in the network list if there's a
// network monitor that says it's unavailable.
TEST_F(NetworkTest, TestNetworkMonitorIsAdapterAvailable) {
  char if_name1[20] = "pdp_ip0";
  char if_name2[20] = "pdp_ip1";
  ifaddrs* list = nullptr;
  list = AddIpv6Address(list, if_name1, "1000:2000:3000:4000:0:0:0:1",
                        "FFFF:FFFF:FFFF:FFFF::", 0);
  list = AddIpv6Address(list, if_name2, "1000:2000:3000:4000:0:0:0:2",
                        "FFFF:FFFF:FFFF:FFFF::", 0);
  std::vector<std::unique_ptr<Network>> result;

  // Sanity check that both interfaces are included by default.
  FakeNetworkMonitorFactory factory;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server, &factory);
  manager.StartUpdating();
  CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result);
  EXPECT_EQ(2u, result.size());
  bool changed;
  // This ensures we release the objects created in CallConvertIfAddrs.
  MergeNetworkList(manager, std::move(result), &changed);
  result.clear();

  // Now simulate one interface being unavailable.
  FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager);
  network_monitor->set_unavailable_adapters({if_name1});
  CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result);
  EXPECT_EQ(1u, result.size());
  EXPECT_EQ(if_name2, result[0]->name());

  MergeNetworkList(manager, std::move(result), &changed);
  ReleaseIfAddrs(list);
}

#endif  // defined(WEBRTC_POSIX)

// Test MergeNetworkList successfully combines all IPs for the same
// prefix/length into a single Network.
TEST_F(NetworkTest, TestMergeNetworkList) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  std::vector<std::unique_ptr<Network>> list;

  // Create 2 IPAddress classes with only last digit different.
  IPAddress ip1, ip2;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:2", &ip2));

  // Create 2 networks with the same prefix and length.
  auto net1 = std::make_unique<Network>("em1", "em1", TruncateIP(ip1, 64), 64);
  auto net2 = std::make_unique<Network>("em1", "em1", TruncateIP(ip1, 64), 64);

  // Add different IP into each.
  net1->AddIP(ip1);
  net2->AddIP(ip2);

  list.push_back(std::move(net1));
  list.push_back(std::move(net2));
  bool changed;
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);

  std::vector<const Network*> list2 = manager.GetNetworks();

  // Make sure the resulted networklist has only 1 element and 2
  // IPAddresses.
  EXPECT_EQ(list2.size(), 1uL);
  EXPECT_EQ(list2[0]->GetIPs().size(), 2uL);
  EXPECT_THAT(list2[0]->GetIPs(), UnorderedElementsAre(InterfaceAddress(ip1),
                                                       InterfaceAddress(ip2)));
}

// Test that MergeNetworkList successfully detects the change if
// a network becomes inactive and then active again.
TEST_F(NetworkTest, TestMergeNetworkListWithInactiveNetworks) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  Network network1("test_wifi", "Test Network Adapter 1",
                   IPAddress(0x12345600U), 24);
  Network network2("test_eth0", "Test Network Adapter 2",
                   IPAddress(0x00010000U), 16);
  network1.AddIP(IPAddress(0x12345678));
  network2.AddIP(IPAddress(0x00010004));
  std::vector<std::unique_ptr<Network>> list;
  auto net1 = network1.Clone();
  const Network* const net1_ptr = net1.get();
  list.push_back(std::move(net1));
  bool changed;
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);
  list.clear();

  std::vector<const Network*> current = manager.GetNetworks();
  ASSERT_EQ(1U, current.size());
  EXPECT_EQ(net1_ptr, current[0]);

  list.clear();
  auto net2 = network2.Clone();
  const Network* const net2_ptr = net2.get();
  list.push_back(std::move(net2));
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);
  list.clear();

  current = manager.GetNetworks();
  ASSERT_EQ(1U, current.size());
  EXPECT_EQ(net2_ptr, current[0]);
  // Now network1 is inactive. Try to merge it again.
  list.clear();
  list.push_back(network1.Clone());
  MergeNetworkList(manager, std::move(list), &changed);
  EXPECT_TRUE(changed);
  list.clear();
  current = manager.GetNetworks();
  ASSERT_EQ(1U, current.size());
  EXPECT_TRUE(current[0]->active());
  EXPECT_EQ(net1_ptr, current[0]);
}

// Test that the filtering logic follows the defined ruleset in network.h.
TEST_F(NetworkTest, TestIPv6Selection) {
  InterfaceAddress ip;
  std::string ipstr;

  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c3";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_DEPRECATED, &ip));

  // Create a network with this prefix.
  Network ipv6_network("test_eth0", "Test NetworkAdapter", TruncateIP(ip, 64),
                       64);
  EXPECT_EQ(AF_INET6, ipv6_network.family());

  // When there is no address added, it should return an unspecified
  // address.
  EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress());
  EXPECT_TRUE(IPIsUnspec(ipv6_network.GetBestIP()));

  // Deprecated one should not be returned.
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress());

  // Add ULA one. ULA is unique local address which is starting either
  // with 0xfc or 0xfd.
  ipstr = "fd00:fa00:4:1000:be30:5bff:fee5:c4";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip));
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));

  // Add global one.
  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c5";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip));
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));

  // Add global dynamic temporary one.
  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c6";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_TEMPORARY, &ip));
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));
}

// Test that the filtering logic follows the defined ruleset in network.h.
TEST_F(NetworkTest, TestGetBestIPWithPreferGlobalIPv6ToLinkLocalEnabled) {
  InterfaceAddress ip, link_local;
  std::string ipstr;

  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c3";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_DEPRECATED, &ip));

  // Create a network with this prefix.
  Network ipv6_network("test_eth0", "Test NetworkAdapter", TruncateIP(ip, 64),
                       64, ADAPTER_TYPE_UNKNOWN);

  // When there is no address added, it should return an unspecified
  // address.
  EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress());
  EXPECT_TRUE(IPIsUnspec(ipv6_network.GetBestIP()));

  // Deprecated one should not be returned.
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress());

  // Add ULA one. ULA is unique local address which is starting either
  // with 0xfc or 0xfd.
  ipstr = "fd00:fa00:4:1000:be30:5bff:fee5:c4";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip));
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));

  // Add link local one.
  ipstr = "fe80::aabb:ccff:fedd:eeff";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &link_local));
  ipv6_network.AddIP(link_local);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(link_local));

  // Add global one.
  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c5";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip));
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));

  // Add another link local address, then the compatible address is still global
  // one.
  ipstr = "fe80::aabb:ccff:fedd:eedd";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &link_local));
  ipv6_network.AddIP(link_local);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));

  // Add global dynamic temporary one.
  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c6";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_TEMPORARY, &ip));
  ipv6_network.AddIP(ip);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));

  // Add another link local address, then the compatible address is still global
  // dynamic one.
  ipstr = "fe80::aabb:ccff:fedd:eedd";
  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &link_local));
  ipv6_network.AddIP(link_local);
  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));
}

TEST_F(NetworkTest, TestNetworkMonitoring) {
  FakeNetworkMonitorFactory factory;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server,
                              {this, [this]() { OnNetworksChanged(); }},
                              &factory);
  manager.StartUpdating();
  FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager);
  EXPECT_TRUE(network_monitor && network_monitor->started());
  EXPECT_TRUE(WaitUntil([&] { return callback_called_; }));
  callback_called_ = false;

  // Clear the networks so that there will be network changes below.
  ClearNetworks(manager);
  // Network manager is started, so the callback is called when the network
  // monitor fires the network-change event.
  network_monitor->InovkeNetworksChangedCallbackForTesting();
  EXPECT_TRUE(WaitUntil([&] { return callback_called_; }));

  // Network manager is stopped.
  manager.StopUpdating();
  EXPECT_FALSE(GetNetworkMonitor(manager)->started());
}

// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
#if defined(WEBRTC_ANDROID)
#define MAYBE_DefaultLocalAddress DISABLED_DefaultLocalAddress
#else
#define MAYBE_DefaultLocalAddress DefaultLocalAddress
#endif
TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) {
  MAYBE_SKIP_IPV4;
  IPAddress ip;
  FakeNetworkMonitorFactory factory;
  PhysicalSocketServer socket_server;
  TestBasicNetworkManager manager(env_, &socket_server,
                                  {this, [this]() { OnNetworksChanged(); }},
                                  &factory);
  manager.StartUpdating();
  EXPECT_TRUE(WaitUntil([&] { return callback_called_; }));

  // Make sure we can query default local address when an address for such
  // address family exists.
  std::vector<const Network*> networks = manager.GetNetworks();
  EXPECT_TRUE(!networks.empty());
  for (const Network* network : networks) {
    if (network->GetBestIP().family() == AF_INET) {
      EXPECT_TRUE(QueryDefaultLocalAddress(manager, AF_INET) != IPAddress());
    } else if (network->GetBestIP().family() == AF_INET6 &&
               !IPIsLoopback(network->GetBestIP())) {
      // Existence of an IPv6 loopback address doesn't mean it has IPv6 network
      // enabled.
      EXPECT_TRUE(QueryDefaultLocalAddress(manager, AF_INET6) != IPAddress());
    }
  }

  // GetDefaultLocalAddress should return the valid default address after set.
  manager.set_default_local_addresses(GetLoopbackIP(AF_INET),
                                      GetLoopbackIP(AF_INET6));
  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET, &ip));
  EXPECT_EQ(ip, GetLoopbackIP(AF_INET));
  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET6, &ip));
  EXPECT_EQ(ip, GetLoopbackIP(AF_INET6));

  // More tests on GetDefaultLocalAddress with ipv6 addresses where the set
  // default address may be different from the best IP address of any network.
  InterfaceAddress ip1;
  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:1111",
                           IPV6_ADDRESS_FLAG_TEMPORARY, &ip1));
  // Create a network with a prefix of ip1.
  Network ipv6_network("test_eth0", "Test NetworkAdapter", TruncateIP(ip1, 64),
                       64);
  IPAddress ip2;
  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:2222", &ip2));
  ipv6_network.AddIP(ip1);
  ipv6_network.AddIP(ip2);
  std::vector<std::unique_ptr<Network>> list;
  list.push_back(ipv6_network.Clone());
  bool changed;
  MergeNetworkList(manager, std::move(list), &changed);
  // If the set default address is not in any network, GetDefaultLocalAddress
  // should return it.
  IPAddress ip3;
  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:3333", &ip3));
  manager.set_default_local_addresses(GetLoopbackIP(AF_INET), ip3);
  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET6, &ip));
  EXPECT_EQ(ip3, ip);
  // If the set default address is in a network, GetDefaultLocalAddress will
  // return the best IP in that network.
  manager.set_default_local_addresses(GetLoopbackIP(AF_INET), ip2);
  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET6, &ip));
  EXPECT_EQ(static_cast<IPAddress>(ip1), ip);

  manager.StopUpdating();
}

// Test that MergeNetworkList does not set change = true
// when changing from cellular_X to cellular_Y.
TEST_F(NetworkTest, TestWhenNetworkListChangeReturnsChangedFlag) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));
  auto net1 = std::make_unique<Network>("em1", "em1", TruncateIP(ip1, 64), 64);
  net1->set_type(ADAPTER_TYPE_CELLULAR_3G);
  net1->AddIP(ip1);
  std::vector<std::unique_ptr<Network>> list;
  list.push_back(std::move(net1));

  {
    bool changed;
    MergeNetworkList(manager, std::move(list), &changed);
    EXPECT_TRUE(changed);
    std::vector<const Network*> list2 = manager.GetNetworks();
    EXPECT_EQ(list2.size(), 1uL);
    EXPECT_EQ(ADAPTER_TYPE_CELLULAR_3G, list2[0]->type());
  }

  // Modify net1 from 3G to 4G
  {
    auto net2 =
        std::make_unique<Network>("em1", "em1", TruncateIP(ip1, 64), 64);
    net2->set_type(ADAPTER_TYPE_CELLULAR_4G);
    net2->AddIP(ip1);
    list.clear();
    list.push_back(std::move(net2));
    bool changed;
    MergeNetworkList(manager, std::move(list), &changed);

    // Change from 3G to 4G shall not trigger OnNetworksChanged,
    // i.e changed = false.
    EXPECT_FALSE(changed);
    std::vector<const Network*> list2 = manager.GetNetworks();
    ASSERT_EQ(list2.size(), 1uL);
    EXPECT_EQ(ADAPTER_TYPE_CELLULAR_4G, list2[0]->type());
  }

  // Don't modify.
  {
    auto net2 =
        std::make_unique<Network>("em1", "em1", TruncateIP(ip1, 64), 64);
    net2->set_type(ADAPTER_TYPE_CELLULAR_4G);
    net2->AddIP(ip1);
    list.clear();
    list.push_back(std::move(net2));
    bool changed;
    MergeNetworkList(manager, std::move(list), &changed);

    // No change.
    EXPECT_FALSE(changed);
    std::vector<const Network*> list2 = manager.GetNetworks();
    ASSERT_EQ(list2.size(), 1uL);
    EXPECT_EQ(ADAPTER_TYPE_CELLULAR_4G, list2[0]->type());
  }
}

#if defined(WEBRTC_POSIX)
TEST_F(NetworkTest, IgnoresMACBasedIPv6Address) {
  std::string ipv6_address = "2607:fc20:f340:1dc8:214:22ff:fe01:2345";
  std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.StartUpdating();

  // IPSec interface; name is in form "ipsec<index>".
  char if_name[20] = "ipsec11";
  ifaddrs* addr_list =
      InstallIpv6Network(if_name, ipv6_address, ipv6_mask, manager);

  std::vector<const Network*> list = manager.GetNetworks();
  EXPECT_EQ(list.size(), 0u);
  ReleaseIfAddrs(addr_list);
}

TEST_F(NetworkTest, WebRTC_AllowMACBasedIPv6Address) {
  std::string ipv6_address = "2607:fc20:f340:1dc8:214:22ff:fe01:2345";
  std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(
      CreateTestEnvironment(
          {.field_trials = "WebRTC-AllowMACBasedIPv6/Enabled/"}),
      &socket_server);
  manager.StartUpdating();

  // IPSec interface; name is in form "ipsec<index>".
  char if_name[20] = "ipsec11";
  ifaddrs* addr_list =
      InstallIpv6Network(if_name, ipv6_address, ipv6_mask, manager);

  std::vector<const Network*> list = manager.GetNetworks();
  EXPECT_EQ(list.size(), 1u);
  ReleaseIfAddrs(addr_list);
}
#endif

#if defined(WEBRTC_POSIX)
TEST_F(NetworkTest, WebRTC_BindUsingInterfaceName) {
  char if_name1[20] = "wlan0";
  char if_name2[20] = "v4-wlan0";
  ifaddrs* list = nullptr;
  list = AddIpv6Address(list, if_name1, "1000:2000:3000:4000:0:0:0:1",
                        "FFFF:FFFF:FFFF:FFFF::", 0);
  list = AddIpv4Address(list, if_name2, "192.168.0.2", "255.255.255.255");
  std::vector<std::unique_ptr<Network>> result;

  // Sanity check that both interfaces are included by default.
  FakeNetworkMonitorFactory factory;
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server, &factory);
  manager.StartUpdating();
  CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result);
  EXPECT_EQ(2u, result.size());
  ReleaseIfAddrs(list);
  bool changed;
  // This ensures we release the objects created in CallConvertIfAddrs.
  MergeNetworkList(manager, std::move(result), &changed);
  result.clear();

  FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager);

  IPAddress ipv6;
  EXPECT_TRUE(IPFromString("1000:2000:3000:4000:0:0:0:1", &ipv6));
  IPAddress ipv4;
  EXPECT_TRUE(IPFromString("192.168.0.2", &ipv4));

  // The network monitor only knwos about the ipv6 address, interface.
  network_monitor->set_adapters({"wlan0"});
  network_monitor->set_ip_addresses({ipv6});
  EXPECT_EQ(manager.BindSocketToNetwork(/* fd */ 77, ipv6),
            NetworkBindingResult::SUCCESS);

  // But it will bind anyway using string matching...
  EXPECT_EQ(manager.BindSocketToNetwork(/* fd */ 77, ipv4),
            NetworkBindingResult::SUCCESS);
}
#endif

TEST_F(NetworkTest, NetworkCostVpn_Default) {
  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));
  FieldTrials field_trials = CreateTestFieldTrials();

  Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64);
  net1->set_type(ADAPTER_TYPE_VPN);
  net1->set_underlying_type_for_vpn(ADAPTER_TYPE_ETHERNET);

  Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64);
  net2->set_type(ADAPTER_TYPE_ETHERNET);

  EXPECT_EQ(net1->GetCost(field_trials), net2->GetCost(field_trials));
  delete net1;
  delete net2;
}

TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) {
  FieldTrials field_trials =
      CreateTestFieldTrials("WebRTC-AddNetworkCostToVpn/Enabled/");

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64);
  net1->set_type(ADAPTER_TYPE_VPN);
  net1->set_underlying_type_for_vpn(ADAPTER_TYPE_ETHERNET);

  Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64);
  net2->set_type(ADAPTER_TYPE_ETHERNET);

  EXPECT_GT(net1->GetCost(field_trials), net2->GetCost(field_trials));
  delete net1;
  delete net2;
}

class NetworkTestWithDifferentiatedCellular
    : public NetworkTest,
      public testing::WithParamInterface<std::string> {
 protected:
  FieldTrials CreateFieldTrials(absl::string_view s = "") {
    return CreateTestFieldTrials(absl::StrCat(
        "WebRTC-UseDifferentiatedCellularCosts/", GetParam(), "/", s));
  }
};

INSTANTIATE_TEST_SUITE_P(NetworkTestWithDifferentiatedCellular,
                         NetworkTestWithDifferentiatedCellular,
                         testing::Values("Disabled", "Enabled"));

TEST_P(NetworkTestWithDifferentiatedCellular, NetworkCostSlice_Default) {
  FieldTrials field_trials = CreateFieldTrials();

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64,
               ADAPTER_TYPE_CELLULAR_5G);
  net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);

  Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64,
               ADAPTER_TYPE_CELLULAR_5G);

  EXPECT_EQ(net1.GetCost(field_trials), net2.GetCost(field_trials));
}

TEST_P(NetworkTestWithDifferentiatedCellular, NetworkCostSlice_Disabled) {
  FieldTrials field_trials =
      CreateFieldTrials("WebRTC-UnifiedCommunications/Disabled/");

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64,
               ADAPTER_TYPE_CELLULAR_5G);
  net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);

  Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64,
               ADAPTER_TYPE_CELLULAR_5G);

  EXPECT_EQ(net1.GetCost(field_trials), net2.GetCost(field_trials));
}

TEST_P(NetworkTestWithDifferentiatedCellular,
       NetworkCostSlice_SliceLessExpensive) {
  FieldTrials field_trials =
      CreateFieldTrials("WebRTC-UnifiedCommunications/Enabled/");

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64,
               ADAPTER_TYPE_CELLULAR_5G);
  net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);

  Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64,
               ADAPTER_TYPE_CELLULAR_5G);

  EXPECT_LT(net1.GetCost(field_trials), net2.GetCost(field_trials));
}

TEST_F(NetworkTest, NetworkCostSlice_IgnoredWhenInapplicable) {
  FieldTrials field_trials =
      CreateTestFieldTrials("WebRTC-UnifiedCommunications/Enabled/");

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  Network net1("wlan", "wlan", TruncateIP(ip1, 64), 64, ADAPTER_TYPE_WIFI);
  net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);

  Network net2("wlan", "wlan", TruncateIP(ip1, 64), 64, ADAPTER_TYPE_WIFI);

  EXPECT_EQ(net1.GetCost(field_trials), net2.GetCost(field_trials));
}

TEST_F(NetworkTest, GuessAdapterFromNetworkCost_Default) {
  FieldTrials field_trials =
      CreateTestFieldTrials("WebRTC-UseDifferentiatedCellularCosts/Enabled/");

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  for (auto type : kAllAdapterTypes) {
    if (type == ADAPTER_TYPE_VPN)
      continue;
    Network net1("em1", "em1", TruncateIP(ip1, 64), 64);
    net1.set_type(type);

    auto [guess, vpn, network_slice] =
        Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials));

    EXPECT_FALSE(vpn);
    if (type == ADAPTER_TYPE_LOOPBACK) {
      EXPECT_EQ(guess, ADAPTER_TYPE_ETHERNET);
    } else {
      EXPECT_EQ(type, guess);
    }
    EXPECT_EQ(NetworkSlice::NO_SLICE, network_slice);
  }
}

TEST_F(NetworkTest, GuessAdapterFromNetworkCost_Vpn) {
  FieldTrials field_trials = CreateTestFieldTrials(
      "WebRTC-AddNetworkCostToVpn/Enabled/"
      "WebRTC-UseDifferentiatedCellularCosts/Enabled/");

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  for (auto type : kAllAdapterTypes) {
    if (type == ADAPTER_TYPE_VPN)
      continue;
    Network net1("em1", "em1", TruncateIP(ip1, 64), 64);
    net1.set_type(ADAPTER_TYPE_VPN);
    net1.set_underlying_type_for_vpn(type);

    auto [guess, vpn, network_slice] =
        Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials));

    EXPECT_TRUE(vpn);
    if (type == ADAPTER_TYPE_LOOPBACK) {
      EXPECT_EQ(guess, ADAPTER_TYPE_ETHERNET);
    } else {
      EXPECT_EQ(type, guess);
    }
    EXPECT_EQ(NetworkSlice::NO_SLICE, network_slice);
  }
}

TEST_P(NetworkTestWithDifferentiatedCellular,
       GuessAdapterFromNetworkCost_Slice) {
  FieldTrials field_trials = CreateFieldTrials(
      "WebRTC-AddNetworkCostToVpn/Enabled/"
      "WebRTC-UnifiedCommunications/Enabled/");

  bool differentiated_cellular_enabled = GetParam() == "Enabled";

  IPAddress ip1;
  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));

  constexpr AdapterType sliceable_cellular_types[] = {ADAPTER_TYPE_CELLULAR_5G,
                                                      ADAPTER_TYPE_CELLULAR};

  for (AdapterType actual_type : sliceable_cellular_types) {
    AdapterType expected_guess =
        differentiated_cellular_enabled ? actual_type : ADAPTER_TYPE_CELLULAR;

    {
      Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64);
      net1.set_type(actual_type);
      net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);

      auto [guess, vpn, network_slice] =
          Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials));

      EXPECT_EQ(expected_guess, guess);
      EXPECT_FALSE(vpn);
      EXPECT_EQ(NetworkSlice::UNIFIED_COMMUNICATIONS, network_slice);
    }

    {
      Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64);
      net2.set_type(ADAPTER_TYPE_VPN);
      net2.set_underlying_type_for_vpn(actual_type);
      net2.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);

      auto [guess, vpn, network_slice] =
          Network::GuessAdapterFromNetworkCost(net2.GetCost(field_trials));

      EXPECT_EQ(expected_guess, guess);
      EXPECT_TRUE(vpn);
      EXPECT_EQ(NetworkSlice::UNIFIED_COMMUNICATIONS, network_slice);
    }
  }
}

TEST_F(NetworkTest, VpnList) {
  PhysicalSocketServer socket_server;
  {
    BasicNetworkManager manager(env_, &socket_server);
    manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)});
    manager.StartUpdating();
    EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32));
    EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.12.1"), 24));
    EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 16));
    EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 24));
    EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.133.1.1"), 32));
    EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.133.0.0"), 16));
    EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 15));
  }
  {
    BasicNetworkManager manager(env_, &socket_server);
    manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 24)});
    manager.StartUpdating();
    EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32));
    EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.1"), 32));
  }
}

#if defined(WEBRTC_POSIX)
// TODO(webrtc:13114): Implement the InstallIpv4Network for windows.
TEST_F(NetworkTest, VpnListOverrideAdapterType) {
  PhysicalSocketServer socket_server;
  BasicNetworkManager manager(env_, &socket_server);
  manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)});
  manager.StartUpdating();

  char if_name[20] = "eth0";
  auto addr_list =
      InstallIpv4Network(if_name, "192.168.1.23", "255.255.255.255", manager);

  std::vector<const Network*> list = manager.GetNetworks();
  ASSERT_EQ(1u, list.size());
  EXPECT_EQ(ADAPTER_TYPE_VPN, list[0]->type());
  EXPECT_EQ(ADAPTER_TYPE_ETHERNET, list[0]->underlying_type_for_vpn());
  ClearNetworks(manager);
  ReleaseIfAddrs(addr_list);
}
#endif  // defined(WEBRTC_POSIX)

TEST_F(NetworkTest, HardcodedVpn) {
  const uint8_t cisco[] = {0x0, 0x5, 0x9A, 0x3C, 0x7A, 0x0};
  const uint8_t global[] = {0x2, 0x50, 0x41, 0x0, 0x0, 0x1};
  const uint8_t unknown[] = {0x2, 0x50, 0x41, 0x0, 0x0, 0x0};
  const uint8_t five_bytes[] = {0x2, 0x50, 0x41, 0x0, 0x0};
  EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(cisco));
  EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(global));

  EXPECT_FALSE(
      NetworkManagerBase::IsVpnMacAddress(std::span<const uint8_t>(cisco, 5)));
  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(five_bytes));
  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(unknown));
  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress({}));
}

TEST(CompareNetworks, IrreflexivityTest) {
  // x < x is false
  auto network = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network, network));
}

TEST(CompareNetworks, AsymmetryTest) {
  // x < y and y < x cannot be both true
  auto network_a = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_b = std::make_unique<Network>(
      "test_eth1", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_b));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_b, network_a));

  auto network_c = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345500U), 24);
  auto network_d = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_c, network_d));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_d, network_c));
}

TEST(CompareNetworks, TransitivityTest) {
  // x < y and y < z imply x < z
  auto network_a = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_b = std::make_unique<Network>(
      "test_eth1", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_c = std::make_unique<Network>(
      "test_eth2", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_b));
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_b, network_c));

  auto network_d = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_e = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345700U), 24);
  auto network_f = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345800U), 24);
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_d, network_e));
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_e, network_f));
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_d, network_f));
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_c));
}

TEST(CompareNetworks, TransitivityOfIncomparabilityTest) {
  // x == y and y == z imply x == z,
  // where x == y means x < y and y < x are both false
  auto network_a = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 23);
  auto network_b = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_c = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345700U), 24);

  // network_a < network_b
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_b));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_b, network_a));

  // network_b < network_c
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_b, network_c));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_c, network_b));

  // network_a < network_c
  EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_c));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_c, network_a));

  auto network_d = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_e = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);
  auto network_f = std::make_unique<Network>(
      "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24);

  // network_d == network_e
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_d, network_e));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_e, network_d));

  // network_e == network_f
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_e, network_f));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_f, network_e));

  // network_d == network_f
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_d, network_f));
  EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_f, network_d));
}

TEST_F(NetworkTest, TestNetworkSliceChanged) {
  Network network("test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U),
                  24);
  int callback_count = 0;
  NetworkSlice last_slice = NetworkSlice::NO_SLICE;
  network.SubscribeNetworkSliceChanged(&network, [&](const Network* n) {
    callback_count++;
    last_slice = n->network_slice();
  });

  network.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);
  EXPECT_EQ(1, callback_count);
  EXPECT_EQ(NetworkSlice::UNIFIED_COMMUNICATIONS, last_slice);

  // Setting the same value should not trigger the callback.
  network.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS);
  EXPECT_EQ(1, callback_count);

  network.UnsubscribeNetworkSliceChanged(&network);
  network.set_network_slice(NetworkSlice::NO_SLICE);
  EXPECT_EQ(1, callback_count);
}

}  // namespace webrtc
