text: // Copyright 2018 The Chromium OS Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "shill/device.h"
  
  #include <ctype.h>
  #include <sys/socket.h>
  #include <linux/if.h>  // NOLINT - Needs typedefs from sys/socket.h.
  
  #include <algorithm>
  #include <memory>
  #include <string>
  #include <utility>
  #include <vector>
  
  #include <base/bind.h>
  #include <base/callback.h>
  #include <base/macros.h>
  #include <chromeos/dbus/service_constants.h>
  #include <gmock/gmock.h>
  #include <gtest/gtest.h>
  
  #include "shill/dhcp/dhcp_provider.h"
  #include "shill/dhcp/mock_dhcp_config.h"
  #include "shill/dhcp/mock_dhcp_properties.h"
  #include "shill/dhcp/mock_dhcp_provider.h"
  #include "shill/dns_server_tester.h"
  #include "shill/event_dispatcher.h"
  #include "shill/mock_adaptors.h"
  #include "shill/mock_connection.h"
  #include "shill/mock_control.h"
  #include "shill/mock_device.h"
  #include "shill/mock_device_info.h"
  #include "shill/mock_event_dispatcher.h"
  #include "shill/mock_ipconfig.h"
  #include "shill/mock_link_monitor.h"
  #include "shill/mock_manager.h"
  #include "shill/mock_metrics.h"
  #include "shill/mock_portal_detector.h"
  #include "shill/mock_service.h"
  #include "shill/mock_store.h"
  #include "shill/mock_traffic_monitor.h"
  #include "shill/net/mock_rtnl_handler.h"
  #include "shill/net/mock_time.h"
  #include "shill/net/ndisc.h"
  #include "shill/portal_detector.h"
  #include "shill/routing_table.h"
  #include "shill/static_ip_parameters.h"
  #include "shill/technology.h"
  #include "shill/test_event_dispatcher.h"
  #include "shill/testing.h"
  #include "shill/tethering.h"
  #include "shill/traffic_monitor.h"
  
  using base::Callback;
  using std::string;
  using std::vector;
  using ::testing::_;
  using ::testing::AtLeast;
  using ::testing::DoAll;
  using ::testing::HasSubstr;
  using ::testing::Invoke;
  using ::testing::Mock;
  using ::testing::NiceMock;
  using ::testing::Ref;
  using ::testing::Return;
  using ::testing::ReturnRef;
  using ::testing::SetArgPointee;
  using ::testing::StrEq;
  using ::testing::StrictMock;
  
  namespace shill {
  
  class TestDevice : public Device {
   public:
    TestDevice(Manager* manager,
               const std::string& link_name,
               const std::string& address,
               int interface_index,
               Technology technology)
        : Device(manager, link_name, address, interface_index, technology) {
      ON_CALL(*this, SetIPFlag(_, _, _))
          .WillByDefault(Invoke(this, &TestDevice::DeviceSetIPFlag));
      ON_CALL(*this, IsTrafficMonitorEnabled())
          .WillByDefault(
              Invoke(this, &TestDevice::DeviceIsTrafficMonitorEnabled));
      ON_CALL(*this, StartDNSTest(_, _, _))
          .WillByDefault(Invoke(this, &TestDevice::DeviceStartDNSTest));
      ON_CALL(*this, ShouldBringNetworkInterfaceDownAfterDisabled())
          .WillByDefault(Invoke(
              this,
              &TestDevice::DeviceShouldBringNetworkInterfaceDownAfterDisabled));
    }
  
    ~TestDevice() override = default;
  
    void Start(Error* error,
               const EnabledStateChangedCallback& callback) override {
      DCHECK(error);
    }
  
    void Stop(Error* error,
              const EnabledStateChangedCallback& callback) override {
      DCHECK(error);
    }
  
    MOCK_METHOD(bool, IsTrafficMonitorEnabled, (), (const, override));
    MOCK_METHOD(bool,
                ShouldBringNetworkInterfaceDownAfterDisabled,
                (),
                (const, override));
    MOCK_METHOD(bool,
                SetIPFlag,
                (IPAddress::Family, const std::string&, const std::string&),
                (override));
    MOCK_METHOD(bool,
                StartDNSTest,
                (const std::vector<std::string>&,
                 const bool,
                 const base::Callback<void(const DnsServerTester::Status)>&),
                (override));
    MOCK_METHOD(bool,
                StartConnectionDiagnosticsAfterPortalDetection,
                (const PortalDetector::Result&, const PortalDetector::Result&),
                (override));
  
    virtual bool DeviceIsTrafficMonitorEnabled() const {
      return Device::IsTrafficMonitorEnabled();
    }
  
    virtual bool DeviceSetIPFlag(IPAddress::Family family,
                                 const std::string& flag,
                                 const std::string& value) {
      return Device::SetIPFlag(family, flag, value);
    }
  
    virtual bool DeviceStartDNSTest(
        const std::vector<std::string>& dns_servers,
        const bool retry_until_success,
        const base::Callback<void(const DnsServerTester::Status)>& callback) {
      return Device::StartDNSTest(dns_servers, retry_until_success, callback);
    }
  
    virtual bool DeviceShouldBringNetworkInterfaceDownAfterDisabled() const {
      return Device::ShouldBringNetworkInterfaceDownAfterDisabled();
    }
  
    void device_set_mac_address(const std::string& mac_address) {
      Device::set_mac_address(mac_address);
    }
  };
  
  class DeviceTest : public testing::Test {
   public:
    DeviceTest()
        : manager_(control_interface(), dispatcher(), metrics()),
          device_(new NiceMock<TestDevice>(manager(),
                                           kDeviceName,
                                           kDeviceAddress,
                                           kDeviceInterfaceIndex,
                                           Technology::kUnknown)),
          device_info_(manager()) {
      manager()->set_mock_device_info(&device_info_);
      DHCPProvider::GetInstance()->control_interface_ = control_interface();
      DHCPProvider::GetInstance()->dispatcher_ = dispatcher();
      device_->time_ = &time_;
    }
    ~DeviceTest() override = default;
  
    void SetUp() override {
      device_->rtnl_handler_ = &rtnl_handler_;
      RoutingTable::GetInstance()->Start();
    }
  
   protected:
    static const char kDeviceName[];
    static const char kDeviceAddress[];
    static const int kDeviceInterfaceIndex;
  
    void OnIPConfigUpdated(const IPConfigRefPtr& ipconfig) {
      device_->OnIPConfigUpdated(ipconfig, true);
    }
  
    void OnIPConfigFailed(const IPConfigRefPtr& ipconfig) {
      device_->OnIPConfigFailed(ipconfig);
    }
  
    void OnIPConfigExpired(const IPConfigRefPtr& ipconfig) {
      device_->OnIPConfigExpired(ipconfig);
    }
  
    void SelectService(const ServiceRefPtr service) {
      device_->SelectService(service);
    }
  
    void SetConnection(ConnectionRefPtr connection) {
      device_->connection_ = connection;
    }
  
    void SetLinkMonitor(LinkMonitor* link_monitor) {
      device_->set_link_monitor(link_monitor);  // Passes ownership.
    }
  
    bool HasLinkMonitor() { return device_->link_monitor(); }
  
    bool StartLinkMonitor() { return device_->StartLinkMonitor(); }
  
    void StopLinkMonitor() { device_->StopLinkMonitor(); }
  
    uint64_t GetLinkMonitorResponseTime(Error* error) {
      return device_->GetLinkMonitorResponseTime(error);
    }
  
    MockTrafficMonitor* SetTrafficMonitor(
        std::unique_ptr<MockTrafficMonitor> traffic_monitor) {
      MockTrafficMonitor* underlying_traffic_monitor = traffic_monitor.get();
      device_->set_traffic_monitor_for_test(std::move(traffic_monitor));
      return underlying_traffic_monitor;
    }
  
    void StartTrafficMonitor() { device_->StartTrafficMonitor(); }
  
    void StopTrafficMonitor() { device_->StopTrafficMonitor(); }
  
    void NetworkProblemDetected(int reason) {
      device_->OnEncounterNetworkProblem(reason);
    }
  
    DeviceMockAdaptor* GetDeviceMockAdaptor() {
      return static_cast<DeviceMockAdaptor*>(device_->adaptor_.get());
    }
  
    MockControl* control_interface() { return &control_interface_; }
    EventDispatcher* dispatcher() { return &dispatcher_; }
    MockMetrics* metrics() { return &metrics_; }
    MockManager* manager() { return &manager_; }
  
    MOCK_METHOD(void, ReliableLinkCallback, ());
  
    void SetReliableLinkCallback() {
      device_->reliable_link_callback_.Reset(
          base::Bind(&DeviceTest::ReliableLinkCallback, base::Unretained(this)));
    }
  
    bool ReliableLinkCallbackIsCancelled() {
      return device_->reliable_link_callback_.IsCancelled();
    }
  
    void SetupIPv6Config() {
      const char kAddress[] = "2001:db8::1";
      const char kDnsServer1[] = "2001:db8::2";
      const char kDnsServer2[] = "2001:db8::3";
      IPConfig::Properties properties;
      properties.address = kAddress;
      properties.dns_servers = {kDnsServer1, kDnsServer2};
  
      device_->ip6config_ =
          new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
      device_->ip6config_->set_properties(properties);
    }
  
    bool SetHostname(const string& hostname) {
      return device_->SetHostname(hostname);
    }
  
    NiceMock<MockControl> control_interface_;
    EventDispatcherForTest dispatcher_;
    NiceMock<MockMetrics> metrics_;
    NiceMock<MockManager> manager_;
  
    scoped_refptr<TestDevice> device_;
    NiceMock<MockDeviceInfo> device_info_;
    MockTime time_;
    StrictMock<MockRTNLHandler> rtnl_handler_;
  };
  
  const char DeviceTest::kDeviceName[] = "testdevice";
  const char DeviceTest::kDeviceAddress[] = "address";
  const int DeviceTest::kDeviceInterfaceIndex = 0;
  
  TEST_F(DeviceTest, Contains) {
    EXPECT_TRUE(device_->store().Contains(kNameProperty));
    EXPECT_FALSE(device_->store().Contains(""));
  }
  
  TEST_F(DeviceTest, GetProperties) {
    brillo::VariantDictionary props;
    Error error;
    device_->store().GetProperties(&props, &error);
    ASSERT_FALSE(props.find(kNameProperty) == props.end());
    EXPECT_TRUE(props[kNameProperty].IsTypeCompatible<string>());
    EXPECT_EQ(props[kNameProperty].Get<string>(), string(kDeviceName));
  }
  
  // Note: there are currently no writeable Device properties that
  // aren't registered in a subclass.
  TEST_F(DeviceTest, SetReadOnlyProperty) {
    Error error;
    // Ensure that an attempt to write a R/O property returns InvalidArgs error.
    EXPECT_FALSE(device_->mutable_store()->SetAnyProperty(
        kAddressProperty, brillo::Any(string()), &error));
    EXPECT_EQ(Error::kInvalidArguments, error.type());
  }
  
  TEST_F(DeviceTest, ClearReadOnlyProperty) {
    Error error;
    EXPECT_FALSE(device_->mutable_store()->SetAnyProperty(
        kAddressProperty, brillo::Any(string()), &error));
  }
  
  TEST_F(DeviceTest, ClearReadOnlyDerivedProperty) {
    Error error;
    EXPECT_FALSE(device_->mutable_store()->SetAnyProperty(
        kIPConfigsProperty, brillo::Any(Strings()), &error));
  }
  
  TEST_F(DeviceTest, DestroyIPConfig) {
    ASSERT_EQ(nullptr, device_->ipconfig_);
    device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
    device_->ip6config_ = new IPConfig(control_interface(), kDeviceName);
    device_->dhcpv6_config_ = new IPConfig(control_interface(), kDeviceName);
    device_->DestroyIPConfig();
    ASSERT_EQ(nullptr, device_->ipconfig_);
    ASSERT_EQ(nullptr, device_->ip6config_);
    ASSERT_EQ(nullptr, device_->dhcpv6_config_);
  }
  
  TEST_F(DeviceTest, DestroyIPConfigNULL) {
    ASSERT_EQ(nullptr, device_->ipconfig_);
    ASSERT_EQ(nullptr, device_->ip6config_);
    ASSERT_EQ(nullptr, device_->dhcpv6_config_);
    device_->DestroyIPConfig();
    ASSERT_EQ(nullptr, device_->ipconfig_);
    ASSERT_EQ(nullptr, device_->ip6config_);
    ASSERT_EQ(nullptr, device_->dhcpv6_config_);
  }
  
  MATCHER_P(MatchesDhcpProperties, dhcp_props, "") {
    return dhcp_props == arg.properties();
  }
  
  TEST_F(DeviceTest, AcquireIPConfigWithSelectedService) {
    device_->ipconfig_ = new IPConfig(control_interface(), "randomname");
    auto dhcp_provider = std::make_unique<MockDHCPProvider>();
    device_->dhcp_provider_ = dhcp_provider.get();
  
    scoped_refptr<MockDHCPConfig> dhcp_config(
        new MockDHCPConfig(control_interface(), kDeviceName));
  
    const string service_storage_id = "service_storage_id";
    NiceMock<MockStore> storage;
    EXPECT_CALL(storage,
                GetString(service_storage_id, "DHCPProperty.Hostname", _))
        .WillOnce(DoAll(SetArgPointee<2>(string("name of host")), Return(true)));
    EXPECT_CALL(storage,
                GetString(service_storage_id, "DHCPProperty.VendorClass", _))
        .WillOnce(Return(false));
  
    scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
    auto service_dhcp_properties =
        std::make_unique<DhcpProperties>(/*manager=*/nullptr);
    service_dhcp_properties->Load(&storage, service_storage_id);
    service->dhcp_properties_ = std::move(service_dhcp_properties);
    SelectService(service);
  
    const string default_profile_storage_id = "default_profile_storage_id";
    NiceMock<MockStore> default_profile_storage;
    EXPECT_CALL(default_profile_storage,
                GetString(default_profile_storage_id, "DHCPProperty.Hostname", _))
        .WillOnce(Return(false));
    EXPECT_CALL(default_profile_storage, GetString(default_profile_storage_id,
                                                   "DHCPProperty.VendorClass", _))
        .WillOnce(DoAll(SetArgPointee<2>(string("vendorclass")), Return(true)));
  
    auto manager_dhcp_properties =
        std::make_unique<DhcpProperties>(/*manager=*/nullptr);
    manager_dhcp_properties->Load(&default_profile_storage,
                                  default_profile_storage_id);
    DhcpProperties combined_props = DhcpProperties::Combine(
        *manager_dhcp_properties, service->dhcp_properties());
  
  #ifndef DISABLE_DHCPV6
    device_->dhcpv6_config_ = new IPConfig(control_interface(), "randomname");
    scoped_refptr<MockDHCPConfig> dhcpv6_config(
        new MockDHCPConfig(control_interface(), kDeviceName));
  
    EXPECT_CALL(*manager(), IsDHCPv6EnabledForDevice(kDeviceName))
        .WillOnce(Return(true));
    EXPECT_CALL(*dhcp_provider, CreateIPv6Config(_, _))
        .WillOnce(Return(dhcpv6_config));
    EXPECT_CALL(*dhcpv6_config, RequestIP()).WillOnce(Return(true));
  #endif  // DISABLE_DHCPV6
    manager()->dhcp_properties_ = std::move(manager_dhcp_properties);
    EXPECT_CALL(*dhcp_provider,
                CreateIPv4Config(
                    _, _, _, MatchesDhcpProperties(combined_props.properties())))
        .WillOnce(Return(dhcp_config));
    EXPECT_CALL(*dhcp_config, RequestIP()).WillOnce(Return(true));
    EXPECT_TRUE(device_->AcquireIPConfig());
    ASSERT_NE(nullptr, device_->ipconfig_);
    EXPECT_EQ(kDeviceName, device_->ipconfig_->device_name());
    EXPECT_FALSE(device_->ipconfig_->update_callback_.is_null());
  #ifndef DISABLE_DHCPV6
    EXPECT_EQ(kDeviceName, device_->dhcpv6_config_->device_name());
    EXPECT_FALSE(device_->dhcpv6_config_->update_callback_.is_null());
  #endif  // DISABLE_DHCPV6
    device_->dhcp_provider_ = nullptr;
  }
  
  TEST_F(DeviceTest, AcquireIPConfigWithoutSelectedService) {
    device_->ipconfig_ = new IPConfig(control_interface(), "randomname");
    auto dhcp_provider = std::make_unique<MockDHCPProvider>();
    device_->dhcp_provider_ = dhcp_provider.get();
    scoped_refptr<MockDHCPConfig> dhcp_config(
        new MockDHCPConfig(control_interface(), kDeviceName));
    auto manager_dhcp_properties = std::make_unique<DhcpProperties>(manager());
    manager()->dhcp_properties_ = std::move(manager_dhcp_properties);
  #ifndef DISABLE_DHCPV6
    device_->dhcpv6_config_ = new IPConfig(control_interface(), "randomname");
    scoped_refptr<MockDHCPConfig> dhcpv6_config(
        new MockDHCPConfig(control_interface(), kDeviceName));
  
    EXPECT_CALL(*manager(), IsDHCPv6EnabledForDevice(kDeviceName))
        .WillOnce(Return(true));
    EXPECT_CALL(*dhcp_provider, CreateIPv6Config(_, _))
        .WillOnce(Return(dhcpv6_config));
    EXPECT_CALL(*dhcpv6_config, RequestIP()).WillOnce(Return(true));
  #endif  // DISABLE_DHCPV6
  
    EXPECT_CALL(*dhcp_provider,
                CreateIPv4Config(_, _, _,
                                 MatchesDhcpProperties(
                                     manager()->dhcp_properties().properties())))
        .WillOnce(Return(dhcp_config));
    EXPECT_CALL(*dhcp_config, RequestIP()).WillOnce(Return(true));
    EXPECT_TRUE(device_->AcquireIPConfig());
    ASSERT_NE(nullptr, device_->ipconfig_);
    EXPECT_EQ(kDeviceName, device_->ipconfig_->device_name());
    EXPECT_FALSE(device_->ipconfig_->update_callback_.is_null());
  #ifndef DISABLE_DHCPV6
    EXPECT_EQ(kDeviceName, device_->dhcpv6_config_->device_name());
    EXPECT_FALSE(device_->dhcpv6_config_->update_callback_.is_null());
  #endif  // DISABLE_DHCPV6
    device_->dhcp_provider_ = nullptr;
  }
  
  TEST_F(DeviceTest, ConfigWithMinimumMTU) {
    const int minimum_mtu = 1500;
  
    EXPECT_CALL(*manager(), GetMinimumMTU()).WillOnce(Return(minimum_mtu));
  
    device_->ipconfig_ = new IPConfig(control_interface(), "anothername");
    auto dhcp_provider = std::make_unique<MockDHCPProvider>();
    device_->dhcp_provider_ = dhcp_provider.get();
  
    scoped_refptr<MockDHCPConfig> dhcp_config(
        new NiceMock<MockDHCPConfig>(control_interface(), kDeviceName));
    EXPECT_CALL(*dhcp_provider, CreateIPv4Config(_, _, _, _))
        .WillOnce(Return(dhcp_config));
    EXPECT_CALL(*dhcp_config, set_minimum_mtu(minimum_mtu));
  
    device_->AcquireIPConfig();
  }
  
  TEST_F(DeviceTest, StartIPv6) {
    EXPECT_CALL(*device_,
                SetIPFlag(IPAddress::kFamilyIPv6,
                          StrEq(Device::kIPFlagDisableIPv6), StrEq("0")))
        .WillOnce(Return(true));
    EXPECT_CALL(
        *device_,
        SetIPFlag(IPAddress::kFamilyIPv6,
                  StrEq(Device::kIPFlagAcceptRouterAdvertisements), StrEq("2")))
        .WillOnce(Return(true));
    device_->StartIPv6();
  }
  
  TEST_F(DeviceTest, StartIPv6Disabled) {
    Error error;
    EXPECT_CALL(*device_,
                SetIPFlag(IPAddress::kFamilyIPv6,
                          StrEq(Device::kIPFlagDisableIPv6), StrEq("1")))
        .WillOnce(Return(true));
    device_->SetIPv6Disabled(true, &error);
    Mock::VerifyAndClearExpectations(device_.get());
    EXPECT_CALL(*device_, SetIPFlag(_, _, _)).Times(0);
    device_->StartIPv6();
    Mock::VerifyAndClearExpectations(device_.get());
    device_->SetIPv6Disabled(false, &error);
  }
  
  TEST_F(DeviceTest, MultiHomed) {
    // Device should have multi-homing disabled by default.
    EXPECT_CALL(*device_, SetIPFlag(_, _, _)).Times(0);
    device_->SetIsMultiHomed(false);
    Mock::VerifyAndClearExpectations(device_.get());
  
    // Disabled -> enabled should change flags on the device.
    EXPECT_CALL(*device_, SetIPFlag(IPAddress::kFamilyIPv4, StrEq("arp_announce"),
                                    StrEq("2")))
        .WillOnce(Return(true));
    EXPECT_CALL(*device_, SetIPFlag(IPAddress::kFamilyIPv4, StrEq("arp_ignore"),
                                    StrEq("1")))
        .WillOnce(Return(true));
    EXPECT_CALL(*device_,
                SetIPFlag(IPAddress::kFamilyIPv4, StrEq("rp_filter"), StrEq("2")))
        .WillOnce(Return(true));
    device_->SetIsMultiHomed(true);
    Mock::VerifyAndClearExpectations(device_.get());
  
    // Enabled -> enabled should be a no-op.
    EXPECT_CALL(*device_, SetIPFlag(_, _, _)).Times(0);
    device_->SetIsMultiHomed(true);
  
    // Disabling or enabling reverse-path filtering should also be a no-op
    // (since it is disabled due to multi-homing).
    device_->SetLooseRouting(false);
    device_->SetLooseRouting(true);
    Mock::VerifyAndClearExpectations(device_.get());
  
    // Enabled -> disabled should reset the flags back to the default, but
    // because non-default routing is enabled, rp_filter will be left
    // in loose mode.
    EXPECT_CALL(*device_, SetIPFlag(IPAddress::kFamilyIPv4, StrEq("arp_announce"),
                                    StrEq("0")))
        .WillOnce(Return(true));
    EXPECT_CALL(*device_, SetIPFlag(IPAddress::kFamilyIPv4, StrEq("arp_ignore"),
                                    StrEq("0")))
        .WillOnce(Return(true));
    device_->SetIsMultiHomed(false);
    Mock::VerifyAndClearExpectations(device_.get());
  
    // Re-enable reverse-path filtering.
    EXPECT_CALL(*device_,
                SetIPFlag(IPAddress::kFamilyIPv4, StrEq("rp_filter"), StrEq("1")))
        .WillOnce(Return(true));
    device_->SetLooseRouting(false);
    Mock::VerifyAndClearExpectations(device_.get());
  }
  
  TEST_F(DeviceTest, Load) {
    NiceMock<MockStore> storage;
    const string id = device_->GetStorageIdentifier();
    EXPECT_CALL(storage, ContainsGroup(id)).WillOnce(Return(true));
    EXPECT_CALL(storage, GetBool(id, Device::kStoragePowered, _))
        .WillOnce(Return(true));
    EXPECT_CALL(storage, GetUint64(id, Device::kStorageReceiveByteCount, _))
        .WillOnce(Return(true));
    EXPECT_CALL(storage, GetUint64(id, Device::kStorageTransmitByteCount, _))
        .WillOnce(Return(true));
    EXPECT_TRUE(device_->Load(&storage));
  }
  
  TEST_F(DeviceTest, Save) {
    NiceMock<MockStore> storage;
    const string id = device_->GetStorageIdentifier();
    EXPECT_CALL(storage, SetBool(id, Device::kStoragePowered, _))
        .WillOnce(Return(true));
    EXPECT_CALL(storage, SetUint64(id, Device::kStorageReceiveByteCount, _))
        .WillOnce(Return(true));
    EXPECT_CALL(storage, SetUint64(id, Device::kStorageTransmitByteCount, _))
        .Times(AtLeast(true));
    EXPECT_TRUE(device_->Save(&storage));
  }
  
  TEST_F(DeviceTest, SelectedService) {
    EXPECT_EQ(nullptr, device_->selected_service_);
    device_->SetServiceState(Service::kStateAssociating);
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    EXPECT_EQ(device_->selected_service_, service);
  
    EXPECT_CALL(*service, SetState(Service::kStateConfiguring));
    device_->SetServiceState(Service::kStateConfiguring);
    EXPECT_CALL(*service, SetFailure(Service::kFailureOutOfRange));
    device_->SetServiceFailure(Service::kFailureOutOfRange);
  
    // Service should be returned to "Idle" state
    EXPECT_CALL(*service, state()).WillOnce(Return(Service::kStateUnknown));
    EXPECT_CALL(*service, SetState(Service::kStateIdle));
    EXPECT_CALL(*service, SetConnection(IsNullRefPtr()));
    SelectService(nullptr);
  
    // A service in the "Failure" state should not be reset to "Idle"
    SelectService(service);
    EXPECT_CALL(*service, state()).WillOnce(Return(Service::kStateFailure));
    EXPECT_CALL(*service, SetConnection(IsNullRefPtr()));
    SelectService(nullptr);
  }
  
  TEST_F(DeviceTest, ResetConnection) {
    EXPECT_EQ(nullptr, device_->selected_service_);
    device_->SetServiceState(Service::kStateAssociating);
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    EXPECT_EQ(device_->selected_service_, service);
  
    // ResetConnection() should drop the connection and the selected service,
    // but should not change the service state.
    EXPECT_CALL(*service, SetState(_)).Times(0);
    EXPECT_CALL(*service, SetConnection(IsNullRefPtr()));
    device_->ResetConnection();
    EXPECT_EQ(nullptr, device_->selected_service_);
  }
  
  TEST_F(DeviceTest, LinkMonitorFailure) {
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    EXPECT_EQ(device_->selected_service(), service);
  
    time_t current_time = 1000;
  
    // Initial link monitor failure.
    EXPECT_CALL(time_, GetSecondsBoottime(_))
        .WillOnce(DoAll(SetArgPointee<0>(current_time), Return(true)));
    EXPECT_CALL(*metrics(), NotifyUnreliableLinkSignalStrength(_, _)).Times(0);
    device_->OnLinkMonitorFailure();
    EXPECT_FALSE(service->unreliable());
  
    // Another link monitor failure after 3 minutes, report signal strength.
    current_time += 180;
    EXPECT_CALL(time_, GetSecondsBoottime(_))
        .WillOnce(DoAll(SetArgPointee<0>(current_time), Return(true)));
    EXPECT_CALL(*metrics(), NotifyUnreliableLinkSignalStrength(_, _)).Times(1);
    device_->OnLinkMonitorFailure();
    EXPECT_TRUE(service->unreliable());
  
    // Device is connected with the reliable link callback setup, then
    // another link monitor failure after 3 minutes, which implies link is
    // still unreliable, reliable link callback should be cancelled.
    current_time += 180;
    SetReliableLinkCallback();
    EXPECT_CALL(time_, GetSecondsBoottime(_))
        .WillOnce(DoAll(SetArgPointee<0>(current_time), Return(true)));
    EXPECT_CALL(*metrics(), NotifyUnreliableLinkSignalStrength(_, _)).Times(1);
    device_->OnLinkMonitorFailure();
    EXPECT_TRUE(service->unreliable());
    EXPECT_TRUE(ReliableLinkCallbackIsCancelled());
  
    // Another link monitor failure after an hour, link is still reliable, signal
    // strength not reported.
    current_time += 3600;
    service->set_unreliable(false);
    EXPECT_CALL(time_, GetSecondsBoottime(_))
        .WillOnce(DoAll(SetArgPointee<0>(current_time), Return(true)));
    EXPECT_CALL(*metrics(), NotifyUnreliableLinkSignalStrength(_, _)).Times(0);
    device_->OnLinkMonitorFailure();
    EXPECT_FALSE(service->unreliable());
  }
  
  TEST_F(DeviceTest, LinkStatusResetOnSelectService) {
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    service->set_unreliable(true);
    SetReliableLinkCallback();
    EXPECT_FALSE(ReliableLinkCallbackIsCancelled());
  
    // Service is deselected, link status of the service should be resetted.
    EXPECT_CALL(*service, state()).WillOnce(Return(Service::kStateIdle));
    EXPECT_CALL(*service, SetState(_));
    EXPECT_CALL(*service, SetConnection(_));
    SelectService(nullptr);
    EXPECT_FALSE(service->unreliable());
    EXPECT_TRUE(ReliableLinkCallbackIsCancelled());
  }
  
  TEST_F(DeviceTest, IPConfigUpdatedFailure) {
    scoped_refptr<MockIPConfig> ipconfig =
        new MockIPConfig(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    EXPECT_CALL(*service, DisconnectWithFailure(Service::kFailureDHCP, _,
                                                HasSubstr("OnIPConfigFailure")));
    EXPECT_CALL(*service, SetConnection(IsNullRefPtr()));
    EXPECT_CALL(*ipconfig, ResetProperties());
    OnIPConfigFailed(ipconfig.get());
  }
  
  TEST_F(DeviceTest, IPConfigUpdatedFailureWithIPv6Config) {
    // Setup IPv6 configuration.
    SetupIPv6Config();
    EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
  
    // IPv4 configuration failed, fallback to use IPv6 configuration.
    scoped_refptr<MockIPConfig> ipconfig =
        new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    SetConnection(connection.get());
  
    EXPECT_CALL(*ipconfig, ResetProperties());
    EXPECT_CALL(*connection, IsIPv6()).WillRepeatedly(Return(false));
    EXPECT_CALL(*connection, UpdateFromIPConfig(device_->ip6config_));
    EXPECT_CALL(*service, IsOnline()).WillOnce(Return(false));
    EXPECT_CALL(*service, SetState(Service::kStateConnected));
    EXPECT_CALL(*service, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service, SetState(Service::kStateOnline));
    EXPECT_CALL(*service, SetConnection(NotNullRefPtr()));
    OnIPConfigFailed(ipconfig.get());
  }
  
  // IPv4 configuration failed with existing IPv6 connection.
  TEST_F(DeviceTest, IPConfigUpdatedFailureWithIPv6Connection) {
    // Setup IPv6 configuration.
    SetupIPv6Config();
    EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
  
    scoped_refptr<MockIPConfig> ipconfig =
        new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    SetConnection(connection.get());
  
    EXPECT_CALL(*ipconfig, ResetProperties());
    EXPECT_CALL(*connection, IsIPv6()).WillRepeatedly(Return(true));
    EXPECT_CALL(*service, DisconnectWithFailure(_, _, _)).Times(0);
    EXPECT_CALL(*service, SetConnection(IsNullRefPtr())).Times(0);
    OnIPConfigFailed(ipconfig.get());
    // Verify connection not teardown.
    EXPECT_THAT(device_->connection(), NotNullRefPtr());
  }
  
  TEST_F(DeviceTest, IPConfigUpdatedFailureWithStatic) {
    scoped_refptr<MockIPConfig> ipconfig =
        new MockIPConfig(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    service->static_ip_parameters_.args_.Set<string>(kAddressProperty, "1.1.1.1");
    service->static_ip_parameters_.args_.Set<int32_t>(kPrefixlenProperty, 16);
    // Even though we won't call DisconnectWithFailure, we should still have
    // the service learn from the failed DHCP attempt.
    EXPECT_CALL(*service, DisconnectWithFailure(_, _, _)).Times(0);
    EXPECT_CALL(*service, SetConnection(_)).Times(0);
    // The IPConfig should retain the previous values.
    EXPECT_CALL(*ipconfig, ResetProperties()).Times(0);
    OnIPConfigFailed(ipconfig.get());
  }
  
  TEST_F(DeviceTest, IPConfigUpdatedSuccess) {
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    scoped_refptr<MockIPConfig> ipconfig =
        new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
    device_->set_ipconfig(ipconfig);
    EXPECT_CALL(*service, IsOnline()).WillOnce(Return(false));
    EXPECT_CALL(*service, SetState(Service::kStateConnected));
    EXPECT_CALL(*metrics(), NotifyNetworkConnectionIPType(
                                device_->technology(),
                                Metrics::kNetworkConnectionIPTypeIPv4));
    EXPECT_CALL(*metrics(),
                NotifyIPv6ConnectivityStatus(device_->technology(), false));
    EXPECT_CALL(*service, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service, HasStaticNameServers()).WillRepeatedly(Return(false));
    EXPECT_CALL(*service, SetState(Service::kStateOnline));
    EXPECT_CALL(*service, SetConnection(NotNullRefPtr()));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
  
    OnIPConfigUpdated(ipconfig.get());
  }
  
  TEST_F(DeviceTest, IPConfigUpdatedAlreadyOnline) {
    // The service is already Online and selected, so it should not transition
    // back to Connected.
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    scoped_refptr<MockIPConfig> ipconfig =
        new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
    device_->set_ipconfig(ipconfig);
    EXPECT_CALL(*service, IsOnline()).WillOnce(Return(true));
    EXPECT_CALL(*service, SetState(Service::kStateConnected)).Times(0);
    EXPECT_CALL(*metrics(), NotifyNetworkConnectionIPType(
                                device_->technology(),
                                Metrics::kNetworkConnectionIPTypeIPv4));
    EXPECT_CALL(*metrics(),
                NotifyIPv6ConnectivityStatus(device_->technology(), false));
    EXPECT_CALL(*service, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service, HasStaticNameServers()).WillRepeatedly(Return(false));
  
    // Successful portal (non-)detection forces the service Online.
    EXPECT_CALL(*service, SetState(Service::kStateOnline));
    EXPECT_CALL(*service, SetConnection(NotNullRefPtr()));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
  
    OnIPConfigUpdated(ipconfig.get());
  }
  
  TEST_F(DeviceTest, IPConfigUpdatedSuccessNoSelectedService) {
    // Make sure shill doesn't crash if a service is disabled immediately
    // after receiving its IP config (selected_service_ is nullptr in this case).
    scoped_refptr<MockIPConfig> ipconfig =
        new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
    SelectService(nullptr);
    OnIPConfigUpdated(ipconfig.get());
  }
  
  TEST_F(DeviceTest, OnIPConfigExpired) {
    scoped_refptr<MockIPConfig> ipconfig =
        new NiceMock<MockIPConfig>(control_interface(), kDeviceName);
    const int kLeaseLength = 1234;
    ipconfig->properties_.lease_duration_seconds = kLeaseLength;
  
    EXPECT_CALL(
        *metrics(),
        SendToUMA("Network.Shill.Unknown.ExpiredLeaseLengthSeconds2",
                  kLeaseLength, Metrics::kMetricExpiredLeaseLengthSecondsMin,
                  Metrics::kMetricExpiredLeaseLengthSecondsMax,
                  Metrics::kMetricExpiredLeaseLengthSecondsNumBuckets));
  
    OnIPConfigExpired(ipconfig.get());
  }
  
  TEST_F(DeviceTest, SetEnabledNonPersistent) {
    EXPECT_FALSE(device_->enabled_);
    EXPECT_FALSE(device_->enabled_pending_);
    device_->enabled_persistent_ = false;
    Error error;
    device_->SetEnabledNonPersistent(true, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_TRUE(device_->enabled_pending_);
  
    // Enable while already enabled.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_persistent_ = false;
    device_->enabled_pending_ = true;
    device_->enabled_ = true;
    device_->SetEnabledNonPersistent(true, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_TRUE(device_->enabled_pending_);
    EXPECT_TRUE(device_->enabled_);
    EXPECT_TRUE(error.IsSuccess());
  
    // Enable while enabled but disabling.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_pending_ = false;
    device_->SetEnabledNonPersistent(true, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_FALSE(device_->enabled_pending_);
    EXPECT_TRUE(device_->enabled_);
    EXPECT_TRUE(error.IsSuccess());
  
    // Disable while already disabled.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_ = false;
    device_->SetEnabledNonPersistent(false, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_FALSE(device_->enabled_pending_);
    EXPECT_FALSE(device_->enabled_);
    EXPECT_TRUE(error.IsSuccess());
  
    // Disable while already enabling.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_pending_ = true;
    device_->SetEnabledNonPersistent(false, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_TRUE(device_->enabled_pending_);
    EXPECT_FALSE(device_->enabled_);
    EXPECT_TRUE(error.IsSuccess());
  }
  
  TEST_F(DeviceTest, SetEnabledPersistent) {
    EXPECT_FALSE(device_->enabled_);
    EXPECT_FALSE(device_->enabled_pending_);
    device_->enabled_persistent_ = false;
    EXPECT_CALL(*manager(), UpdateDevice(_));
    Error error;
    device_->SetEnabledPersistent(true, &error, ResultCallback());
    EXPECT_TRUE(device_->enabled_persistent_);
    EXPECT_TRUE(device_->enabled_pending_);
  
    // Enable while already enabled.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_persistent_ = false;
    device_->enabled_pending_ = true;
    device_->enabled_ = true;
    device_->SetEnabledPersistent(true, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_TRUE(device_->enabled_pending_);
    EXPECT_TRUE(device_->enabled_);
    EXPECT_TRUE(error.IsSuccess());
  
    // Enable while enabled but disabling.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_pending_ = false;
    device_->SetEnabledPersistent(true, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_FALSE(device_->enabled_pending_);
    EXPECT_TRUE(device_->enabled_);
    EXPECT_EQ(Error::kOperationFailed, error.type());
  
    // Disable while already disabled.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_ = false;
    device_->SetEnabledPersistent(false, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_FALSE(device_->enabled_pending_);
    EXPECT_FALSE(device_->enabled_);
    EXPECT_TRUE(error.IsSuccess());
  
    // Disable while already enabling.
    error.Populate(Error::kOperationInitiated);
    device_->enabled_pending_ = true;
    device_->SetEnabledPersistent(false, &error, ResultCallback());
    EXPECT_FALSE(device_->enabled_persistent_);
    EXPECT_TRUE(device_->enabled_pending_);
    EXPECT_FALSE(device_->enabled_);
    EXPECT_EQ(Error::kOperationFailed, error.type());
  }
  
  TEST_F(DeviceTest, Start) {
    EXPECT_FALSE(device_->running_);
    EXPECT_FALSE(device_->enabled_);
    EXPECT_FALSE(device_->enabled_pending_);
    device_->SetEnabled(true);
    EXPECT_TRUE(device_->running_);
    EXPECT_TRUE(device_->enabled_pending_);
    device_->OnEnabledStateChanged(ResultCallback(),
                                   Error(Error::kOperationFailed));
    EXPECT_FALSE(device_->enabled_pending_);
  }
  
  TEST_F(DeviceTest, Stop) {
    device_->enabled_ = true;
    device_->enabled_pending_ = true;
    device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
    SelectService(service);
  
    EXPECT_CALL(*service, state())
        .WillRepeatedly(Return(Service::kStateConnected));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitBoolChanged(kPoweredProperty, false));
    EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, 0, IFF_UP));
    device_->SetEnabled(false);
    device_->OnEnabledStateChanged(ResultCallback(), Error());
  
    EXPECT_EQ(nullptr, device_->ipconfig_);
    EXPECT_EQ(nullptr, device_->selected_service_);
  }
  
  TEST_F(DeviceTest, StopWithFixedIpParams) {
    device_->SetFixedIpParams(true);
    device_->enabled_ = true;
    device_->enabled_pending_ = true;
    device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
    SelectService(service);
  
    EXPECT_CALL(*service, state())
        .WillRepeatedly(Return(Service::kStateConnected));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitBoolChanged(kPoweredProperty, false));
    EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, _, _)).Times(0);
    device_->SetEnabled(false);
    device_->OnEnabledStateChanged(ResultCallback(), Error());
  
    EXPECT_EQ(nullptr, device_->ipconfig_);
    EXPECT_EQ(nullptr, device_->selected_service_);
  }
  
  TEST_F(DeviceTest, StopWithNetworkInterfaceDisabledAfterward) {
    device_->enabled_ = true;
    device_->enabled_pending_ = true;
    device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
    scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
    SelectService(service);
  
    EXPECT_CALL(*device_, ShouldBringNetworkInterfaceDownAfterDisabled())
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service, state())
        .WillRepeatedly(Return(Service::kStateConnected));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitBoolChanged(kPoweredProperty, false));
    device_->SetEnabled(false);
    EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, 0, IFF_UP));
    device_->OnEnabledStateChanged(ResultCallback(), Error());
  
    EXPECT_EQ(nullptr, device_->ipconfig_);
    EXPECT_EQ(nullptr, device_->selected_service_);
  }
  
  TEST_F(DeviceTest, StartProhibited) {
    DeviceRefPtr device(new TestDevice(manager(), kDeviceName, kDeviceAddress,
                                       kDeviceInterfaceIndex, Technology::kWifi));
    {
      Error error;
      manager()->SetProhibitedTechnologies("wifi", &error);
      EXPECT_TRUE(error.IsSuccess());
    }
  
    device->SetEnabled(true);
    EXPECT_FALSE(device->running());
  
    {
      Error error;
      manager()->SetProhibitedTechnologies("", &error);
      EXPECT_TRUE(error.IsSuccess());
    }
    device->SetEnabled(true);
    EXPECT_TRUE(device->running());
  }
  
  TEST_F(DeviceTest, Reset) {
    Error e;
    device_->Reset(&e, ResultCallback());
    EXPECT_EQ(Error::kNotSupported, e.type());
    EXPECT_EQ("Device doesn't support Reset.", e.message());
  }
  
  TEST_F(DeviceTest, ResumeWithIPConfig) {
    scoped_refptr<MockIPConfig> ipconfig =
        new MockIPConfig(control_interface(), kDeviceName);
    device_->set_ipconfig(ipconfig);
    EXPECT_CALL(*ipconfig, RenewIP());
    device_->OnAfterResume();
  }
  
  TEST_F(DeviceTest, ResumeWithoutIPConfig) {
    // Just test that we don't crash in this case.
    ASSERT_EQ(nullptr, device_->ipconfig());
    device_->OnAfterResume();
  }
  
  TEST_F(DeviceTest, ResumeWithLinkMonitor) {
    MockLinkMonitor* link_monitor = new StrictMock<MockLinkMonitor>();
    SetLinkMonitor(link_monitor);  // Passes ownership.
    EXPECT_CALL(*link_monitor, OnAfterResume());
    device_->OnAfterResume();
  }
  
  TEST_F(DeviceTest, ResumeWithoutLinkMonitor) {
    // Just test that we don't crash in this case.
    EXPECT_FALSE(HasLinkMonitor());
    device_->OnAfterResume();
  }
  
  TEST_F(DeviceTest, ResumeWithUnreliableLink) {
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    service->set_unreliable(true);
    SetReliableLinkCallback();
  
    // Link status should be resetted upon resume.
    device_->OnAfterResume();
    EXPECT_FALSE(service->unreliable());
    EXPECT_TRUE(ReliableLinkCallbackIsCancelled());
  }
  
  TEST_F(DeviceTest, OnConnected) {
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
  
    // Link is reliable, no need to post delayed task to reset link status.
    device_->OnConnected();
    EXPECT_TRUE(ReliableLinkCallbackIsCancelled());
  
    // Link is unreliable when connected, delayed task is posted to reset the
    // link state.
    service->set_unreliable(true);
    device_->OnConnected();
    EXPECT_FALSE(ReliableLinkCallbackIsCancelled());
  }
  
  TEST_F(DeviceTest, LinkMonitor) {
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    SetConnection(connection.get());
    MockLinkMonitor* link_monitor = new StrictMock<MockLinkMonitor>();
    SetLinkMonitor(link_monitor);  // Passes ownership.
    EXPECT_CALL(*link_monitor, Start()).Times(0);
    EXPECT_CALL(*manager(),
                IsTechnologyLinkMonitorEnabled(Technology(Technology::kUnknown)))
        .WillOnce(Return(false))
        .WillRepeatedly(Return(true));
    EXPECT_FALSE(StartLinkMonitor());
  
    EXPECT_CALL(*link_monitor, Start()).Times(0);
    EXPECT_CALL(*service, link_monitor_disabled())
        .WillOnce(Return(true))
        .WillRepeatedly(Return(false));
    EXPECT_FALSE(StartLinkMonitor());
  
    EXPECT_CALL(*link_monitor, Start())
        .WillOnce(Return(false))
        .WillOnce(Return(true));
    EXPECT_FALSE(StartLinkMonitor());
    EXPECT_TRUE(StartLinkMonitor());
  
    unsigned int kResponseTime = 123;
    EXPECT_CALL(*link_monitor, GetResponseTimeMilliseconds())
        .WillOnce(Return(kResponseTime));
    {
      Error error;
      EXPECT_EQ(kResponseTime, GetLinkMonitorResponseTime(&error));
      EXPECT_TRUE(error.IsSuccess());
    }
    StopLinkMonitor();
    {
      Error error;
      EXPECT_EQ(0, GetLinkMonitorResponseTime(&error));
      EXPECT_FALSE(error.IsSuccess());
    }
  }
  
  TEST_F(DeviceTest, LinkMonitorCancelledOnSelectService) {
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    SetConnection(connection.get());
    MockLinkMonitor* link_monitor = new StrictMock<MockLinkMonitor>();
    SetLinkMonitor(link_monitor);  // Passes ownership.
    EXPECT_CALL(*service, state()).WillOnce(Return(Service::kStateIdle));
    EXPECT_CALL(*service, SetState(_));
    EXPECT_CALL(*service, SetConnection(_));
    EXPECT_TRUE(HasLinkMonitor());
    SelectService(nullptr);
    EXPECT_FALSE(HasLinkMonitor());
  }
  
  TEST_F(DeviceTest, TrafficMonitor) {
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    SetConnection(connection.get());
    MockTrafficMonitor* traffic_monitor =
        SetTrafficMonitor(std::make_unique<StrictMock<MockTrafficMonitor>>());
  
    EXPECT_CALL(*device_, IsTrafficMonitorEnabled()).WillRepeatedly(Return(true));
    EXPECT_CALL(*traffic_monitor, Start());
    StartTrafficMonitor();
    EXPECT_CALL(*traffic_monitor, Stop());
    StopTrafficMonitor();
    Mock::VerifyAndClearExpectations(traffic_monitor);
  
    EXPECT_CALL(*metrics(), NotifyNetworkProblemDetected(
                                _, Metrics::kNetworkProblemDNSFailure))
        .Times(1);
    NetworkProblemDetected(TrafficMonitor::kNetworkProblemDNSFailure);
  
    // Verify traffic monitor not running when it is disabled.
    traffic_monitor =
        SetTrafficMonitor(std::make_unique<StrictMock<MockTrafficMonitor>>());
    EXPECT_CALL(*device_, IsTrafficMonitorEnabled())
        .WillRepeatedly(Return(false));
    EXPECT_CALL(*traffic_monitor, Start()).Times(0);
    StartTrafficMonitor();
    EXPECT_CALL(*traffic_monitor, Stop()).Times(0);
    StopTrafficMonitor();
  }
  
  TEST_F(DeviceTest, TrafficMonitorCancelledOnSelectService) {
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    SetConnection(connection.get());
    MockTrafficMonitor* traffic_monitor =
        SetTrafficMonitor(std::make_unique<StrictMock<MockTrafficMonitor>>());
    EXPECT_CALL(*device_, IsTrafficMonitorEnabled()).WillRepeatedly(Return(true));
    EXPECT_CALL(*service, state()).WillOnce(Return(Service::kStateIdle));
    EXPECT_CALL(*service, SetState(_));
    EXPECT_CALL(*service, SetConnection(_));
    EXPECT_CALL(*traffic_monitor, Stop());
    SelectService(nullptr);
  }
  
  TEST_F(DeviceTest, ShouldUseArpGateway) {
    EXPECT_FALSE(device_->ShouldUseArpGateway());
  }
  
  TEST_F(DeviceTest, IsConnectedViaTether) {
    EXPECT_FALSE(device_->IsConnectedViaTether());
  
    // An empty ipconfig doesn't mean we're tethered.
    device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
    EXPECT_FALSE(device_->IsConnectedViaTether());
  
    // Add an ipconfig property that indicates this is an Android tether.
    IPConfig::Properties properties;
    properties.vendor_encapsulated_options =
        ByteArray(Tethering::kAndroidVendorEncapsulatedOptions,
                  Tethering::kAndroidVendorEncapsulatedOptions +
                      strlen(Tethering::kAndroidVendorEncapsulatedOptions));
    device_->ipconfig_->UpdateProperties(properties, true);
    EXPECT_TRUE(device_->IsConnectedViaTether());
  
    const char kTestVendorEncapsulatedOptions[] = "Some other non-empty value";
    properties.vendor_encapsulated_options = ByteArray(
        kTestVendorEncapsulatedOptions,
        kTestVendorEncapsulatedOptions + sizeof(kTestVendorEncapsulatedOptions));
    device_->ipconfig_->UpdateProperties(properties, true);
    EXPECT_FALSE(device_->IsConnectedViaTether());
  }
  
  TEST_F(DeviceTest, AvailableIPConfigs) {
    EXPECT_EQ(vector<RpcIdentifier>(), device_->AvailableIPConfigs(nullptr));
    device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
    EXPECT_EQ(vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId},
              device_->AvailableIPConfigs(nullptr));
    device_->ip6config_ = new IPConfig(control_interface(), kDeviceName);
  
    // We don't really care that the RPC IDs for all IPConfig mock adaptors
    // are the same, or their ordering.  We just need to see that there are two
    // of them when both IPv6 and IPv4 IPConfigs are available.
    EXPECT_EQ(2, device_->AvailableIPConfigs(nullptr).size());
  
    device_->dhcpv6_config_ = new IPConfig(control_interface(), kDeviceName);
    EXPECT_EQ(3, device_->AvailableIPConfigs(nullptr).size());
  
    device_->dhcpv6_config_ = nullptr;
    EXPECT_EQ(2, device_->AvailableIPConfigs(nullptr).size());
  
    device_->ipconfig_ = nullptr;
    EXPECT_EQ(vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId},
              device_->AvailableIPConfigs(nullptr));
  
    device_->ip6config_ = nullptr;
    EXPECT_EQ(vector<RpcIdentifier>(), device_->AvailableIPConfigs(nullptr));
  }
  
  TEST_F(DeviceTest, OnIPv6AddressChanged) {
    EXPECT_CALL(*manager(), FilterPrependDNSServersByFamily(_))
        .WillRepeatedly(Return(vector<string>()));
  
    // An IPv6 clear while ip6config_ is nullptr will not emit a change.
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(kIPConfigsProperty, _))
        .Times(0);
    device_->OnIPv6AddressChanged(nullptr);
    EXPECT_THAT(device_->ip6config_, IsNullRefPtr());
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  
    IPAddress address0(IPAddress::kFamilyIPv6);
    const char kAddress0[] = "fe80::1aa9:5ff:abcd:1234";
    ASSERT_TRUE(address0.SetAddressFromString(kAddress0));
  
    // Add an IPv6 address while ip6config_ is nullptr.
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6AddressChanged(&address0);
    EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
    EXPECT_EQ(kAddress0, device_->ip6config_->properties().address);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  
    // If the IPv6 address does not change, no signal is emitted.
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(kIPConfigsProperty, _))
        .Times(0);
    device_->OnIPv6AddressChanged(&address0);
    EXPECT_EQ(kAddress0, device_->ip6config_->properties().address);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  
    IPAddress address1(IPAddress::kFamilyIPv6);
    const char kAddress1[] = "fe80::1aa9:5ff:abcd:5678";
    ASSERT_TRUE(address1.SetAddressFromString(kAddress1));
  
    // If the IPv6 address changes, a signal is emitted.
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6AddressChanged(&address1);
    EXPECT_EQ(kAddress1, device_->ip6config_->properties().address);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  
    // If the IPv6 prefix changes, a signal is emitted.
    address1.set_prefix(64);
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6AddressChanged(&address1);
    EXPECT_EQ(kAddress1, device_->ip6config_->properties().address);
  
    // Return the IPv6 address to nullptr.
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(kIPConfigsProperty,
                                              vector<RpcIdentifier>()));
    device_->OnIPv6AddressChanged(nullptr);
    EXPECT_THAT(device_->ip6config_, IsNullRefPtr());
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  }
  
  TEST_F(DeviceTest, OnIPv6DnsServerAddressesChanged) {
    EXPECT_CALL(*manager(), FilterPrependDNSServersByFamily(_))
        .WillRepeatedly(Return(vector<string>()));
  
    // With existing IPv4 connection, so no attempt to setup IPv6 connection.
    // IPv6 connection is being tested in OnIPv6ConfigurationCompleted test.
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    SetConnection(connection.get());
    EXPECT_CALL(*connection, IsIPv6()).WillRepeatedly(Return(false));
  
    // IPv6 DNS server addresses are not provided will not emit a change.
    EXPECT_CALL(device_info_,
                GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
        .WillOnce(Return(false));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(kIPConfigsProperty, _))
        .Times(0);
    device_->OnIPv6DnsServerAddressesChanged();
    EXPECT_THAT(device_->ip6config_, IsNullRefPtr());
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  
    const char kAddress1[] = "fe80::1aa9:5ff:abcd:1234";
    const char kAddress2[] = "fe80::1aa9:5ff:abcd:1235";
    const uint32_t kInfiniteLifetime = 0xffffffff;
    IPAddress ipv6_address1(IPAddress::kFamilyIPv6);
    IPAddress ipv6_address2(IPAddress::kFamilyIPv6);
    ASSERT_TRUE(ipv6_address1.SetAddressFromString(kAddress1));
    ASSERT_TRUE(ipv6_address2.SetAddressFromString(kAddress2));
    vector<IPAddress> dns_server_addresses = {ipv6_address1, ipv6_address2};
    vector<string> dns_server_addresses_str = {kAddress1, kAddress2};
  
    // Add IPv6 DNS server addresses while ip6config_ is nullptr.
    EXPECT_CALL(device_info_,
                GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
        .WillOnce(DoAll(SetArgPointee<1>(dns_server_addresses),
                        SetArgPointee<2>(kInfiniteLifetime), Return(true)));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6DnsServerAddressesChanged();
    EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
    EXPECT_EQ(dns_server_addresses_str,
              device_->ip6config_->properties().dns_servers);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  
    // Add an IPv6 address while IPv6 DNS server addresses already existed.
    IPAddress address3(IPAddress::kFamilyIPv6);
    const char kAddress3[] = "fe80::1aa9:5ff:abcd:1236";
    ASSERT_TRUE(address3.SetAddressFromString(kAddress3));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6AddressChanged(&address3);
    EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
    EXPECT_EQ(kAddress3, device_->ip6config_->properties().address);
    EXPECT_EQ(dns_server_addresses_str,
              device_->ip6config_->properties().dns_servers);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  
    // If the IPv6 DNS server addresses does not change, no signal is emitted.
    EXPECT_CALL(device_info_,
                GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
        .WillOnce(DoAll(SetArgPointee<1>(dns_server_addresses),
                        SetArgPointee<2>(kInfiniteLifetime), Return(true)));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(kIPConfigsProperty, _))
        .Times(0);
    device_->OnIPv6DnsServerAddressesChanged();
    EXPECT_EQ(dns_server_addresses_str,
              device_->ip6config_->properties().dns_servers);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  
    // Setting lifetime to 0 should expire and clear out the DNS server.
    const uint32_t kExpiredLifetime = 0;
    vector<string> empty_dns_server;
    EXPECT_CALL(device_info_,
                GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
        .WillOnce(DoAll(SetArgPointee<1>(dns_server_addresses),
                        SetArgPointee<2>(kExpiredLifetime), Return(true)));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6DnsServerAddressesChanged();
    EXPECT_EQ(empty_dns_server, device_->ip6config_->properties().dns_servers);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  
    // Set DNS server with lifetime of 1 hour.
    const uint32_t kLifetimeOneHr = 3600;
    EXPECT_CALL(device_info_,
                GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
        .WillOnce(DoAll(SetArgPointee<1>(dns_server_addresses),
                        SetArgPointee<2>(kLifetimeOneHr), Return(true)));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6DnsServerAddressesChanged();
    EXPECT_EQ(dns_server_addresses_str,
              device_->ip6config_->properties().dns_servers);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  
    // Return the DNS server addresses to nullptr.
    EXPECT_CALL(device_info_,
                GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
        .WillOnce(Return(false));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnIPv6DnsServerAddressesChanged();
    EXPECT_EQ(empty_dns_server, device_->ip6config_->properties().dns_servers);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
  }
  
  TEST_F(DeviceTest, OnIPv6ConfigurationCompleted) {
    EXPECT_CALL(*manager(), FilterPrependDNSServersByFamily(_))
        .WillRepeatedly(Return(vector<string>()));
    scoped_refptr<MockService> service(new StrictMock<MockService>(manager()));
    SelectService(service);
    scoped_refptr<MockConnection> connection(
        new StrictMock<MockConnection>(&device_info_));
    SetConnection(connection.get());
  
    // Setup initial IPv6 configuration.
    SetupIPv6Config();
    EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
  
    // IPv6 configuration update with non-IPv6 connection, no connection update.
    EXPECT_THAT(device_->connection(), NotNullRefPtr());
    IPAddress address1(IPAddress::kFamilyIPv6);
    const char kAddress1[] = "fe80::1aa9:5ff:abcd:1231";
    ASSERT_TRUE(address1.SetAddressFromString(kAddress1));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    EXPECT_CALL(*connection, IsIPv6()).WillRepeatedly(Return(false));
    EXPECT_CALL(*service, SetConnection(_)).Times(0);
    device_->OnIPv6AddressChanged(&address1);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
    Mock::VerifyAndClearExpectations(service.get());
    Mock::VerifyAndClearExpectations(connection.get());
  
    // IPv6 configuration update with IPv6 connection, connection update.
    IPAddress address2(IPAddress::kFamilyIPv6);
    const char kAddress2[] = "fe80::1aa9:5ff:abcd:1232";
    ASSERT_TRUE(address2.SetAddressFromString(kAddress2));
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    EXPECT_CALL(*connection, IsIPv6()).WillRepeatedly(Return(true));
    EXPECT_CALL(*connection, UpdateFromIPConfig(device_->ip6config_));
    EXPECT_CALL(*metrics(), NotifyNetworkConnectionIPType(
                                device_->technology(),
                                Metrics::kNetworkConnectionIPTypeIPv6));
    EXPECT_CALL(*metrics(),
                NotifyIPv6ConnectivityStatus(device_->technology(), true));
    EXPECT_CALL(*service, IsOnline()).WillOnce(Return(false));
    EXPECT_CALL(*service, SetState(Service::kStateConnected));
    EXPECT_CALL(*service, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service, SetState(Service::kStateOnline));
    EXPECT_CALL(*service, SetConnection(NotNullRefPtr()));
    EXPECT_CALL(*manager(), IsTechnologyLinkMonitorEnabled(_))
        .WillRepeatedly(Return(false));
    device_->OnIPv6AddressChanged(&address2);
    Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
    Mock::VerifyAndClearExpectations(&device_info_);
    Mock::VerifyAndClearExpectations(service.get());
    Mock::VerifyAndClearExpectations(connection.get());
  }
  
  TEST_F(DeviceTest, OnDHCPv6ConfigUpdated) {
    device_->dhcpv6_config_ = new IPConfig(control_interface(), kDeviceName);
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnDHCPv6ConfigUpdated(device_->dhcpv6_config_.get(), true);
  }
  
  TEST_F(DeviceTest, OnDHCPv6ConfigFailed) {
    device_->dhcpv6_config_ = new IPConfig(control_interface(), kDeviceName);
    IPConfig::Properties properties;
    properties.dhcpv6_addresses = {{{kDhcpv6AddressProperty, "2001:db8:0:1::1"}}};
    properties.dhcpv6_delegated_prefixes = {
        {{kDhcpv6AddressProperty, "2001:db8:0:100::"}}};
    properties.lease_duration_seconds = 1;
    device_->dhcpv6_config_->set_properties(properties);
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnDHCPv6ConfigFailed(device_->dhcpv6_config_.get());
    EXPECT_TRUE(device_->dhcpv6_config_->properties().dhcpv6_addresses.empty());
    EXPECT_TRUE(
        device_->dhcpv6_config_->properties().dhcpv6_delegated_prefixes.empty());
    EXPECT_EQ(0, device_->dhcpv6_config_->properties().lease_duration_seconds);
  }
  
  TEST_F(DeviceTest, OnDHCPv6ConfigExpired) {
    device_->dhcpv6_config_ = new IPConfig(control_interface(), kDeviceName);
    IPConfig::Properties properties;
    properties.dhcpv6_addresses = {{{kDhcpv6AddressProperty, "2001:db8:0:1::1"}}};
    properties.dhcpv6_delegated_prefixes = {
        {{kDhcpv6AddressProperty, "2001:db8:0:100::"}}};
    properties.lease_duration_seconds = 1;
    device_->dhcpv6_config_->set_properties(properties);
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitRpcIdentifierArrayChanged(
                    kIPConfigsProperty,
                    vector<RpcIdentifier>{IPConfigMockAdaptor::kRpcId}));
    device_->OnDHCPv6ConfigExpired(device_->dhcpv6_config_.get());
    EXPECT_TRUE(device_->dhcpv6_config_->properties().dhcpv6_addresses.empty());
    EXPECT_TRUE(
        device_->dhcpv6_config_->properties().dhcpv6_delegated_prefixes.empty());
    EXPECT_EQ(0, device_->dhcpv6_config_->properties().lease_duration_seconds);
  }
  
  TEST_F(DeviceTest, PrependIPv4DNSServers) {
    const struct {
      vector<string> ipconfig_servers;
      vector<string> prepend_servers;
      vector<string> expected_servers;
    } expectations[] = {
        {{}, {"8.8.8.8"}, {"8.8.8.8"}},
        {{"8.8.8.8"}, {}, {"8.8.8.8"}},
        {{"8.8.8.8"}, {"10.10.10.10"}, {"10.10.10.10", "8.8.8.8"}},
        {{"8.8.8.8", "10.10.10.10"}, {"10.10.10.10"}, {"10.10.10.10", "8.8.8.8"}},
        {{"8.8.8.8", "10.10.10.10"}, {"8.8.8.8"}, {"8.8.8.8", "10.10.10.10"}},
        {{"8.8.8.8", "9.9.9.9", "10.10.10.10"},
         {"9.9.9.9"},
         {"9.9.9.9", "8.8.8.8", "10.10.10.10"}},
    };
  
    for (const auto& expectation : expectations) {
      scoped_refptr<IPConfig> ipconfig =
          new IPConfig(control_interface(), kDeviceName);
  
      EXPECT_CALL(*manager(),
                  FilterPrependDNSServersByFamily(IPAddress::kFamilyIPv4))
          .WillOnce(Return(expectation.prepend_servers));
      IPConfig::Properties properties;
      properties.dns_servers = expectation.ipconfig_servers;
      properties.address_family = IPAddress::kFamilyIPv4;
      ipconfig->set_properties(properties);
  
      device_->set_ipconfig(ipconfig);
      OnIPConfigUpdated(ipconfig.get());
      EXPECT_EQ(expectation.expected_servers,
                device_->ipconfig()->properties().dns_servers);
    }
  }
  
  TEST_F(DeviceTest, PrependIPv6DNSServers) {
    vector<IPAddress> dns_server_addresses = {IPAddress("2001:4860:4860::8888"),
                                              IPAddress("2001:4860:4860::8844")};
  
    const uint32_t kAddressLifetime = 1000;
    EXPECT_CALL(device_info_, GetIPv6DnsServerAddresses(_, _, _))
        .WillRepeatedly(DoAll(SetArgPointee<1>(dns_server_addresses),
                              SetArgPointee<2>(kAddressLifetime), Return(true)));
    const vector<string> kOutputServers{"2001:4860:4860::8899"};
    EXPECT_CALL(*manager(),
                FilterPrependDNSServersByFamily(IPAddress::kFamilyIPv6))
        .WillOnce(Return(kOutputServers));
    device_->OnIPv6DnsServerAddressesChanged();
  
    const vector<string> kExpectedServers{
        "2001:4860:4860::8899", "2001:4860:4860::8888", "2001:4860:4860::8844"};
    EXPECT_EQ(kExpectedServers, device_->ip6config()->properties().dns_servers);
  }
  
  TEST_F(DeviceTest, PrependWithStaticConfiguration) {
    scoped_refptr<IPConfig> ipconfig =
        new IPConfig(control_interface(), kDeviceName);
  
    device_->set_ipconfig(ipconfig);
  
    scoped_refptr<MockService> service(new NiceMock<MockService>(manager()));
    EXPECT_CALL(*service, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(true));
    SelectService(service);
  
    auto parameters = service->mutable_static_ip_parameters();
    parameters->args_.Set<string>(kAddressProperty, "1.1.1.1");
    parameters->args_.Set<int32_t>(kPrefixlenProperty, 16);
  
    scoped_refptr<MockConnection> connection =
        new NiceMock<MockConnection>(&device_info_);
    SetConnection(connection);
  
    // Ensure that in the absence of statically configured nameservers that the
    // prepend DNS servers are still prepended.
    EXPECT_CALL(*service, HasStaticNameServers()).WillOnce(Return(false));
    const vector<string> kOutputServers{"8.8.8.8"};
    EXPECT_CALL(*manager(),
                FilterPrependDNSServersByFamily(IPAddress::kFamilyIPv4))
        .WillRepeatedly(Return(kOutputServers));
    OnIPConfigUpdated(ipconfig.get());
    EXPECT_EQ(kOutputServers, device_->ipconfig()->properties().dns_servers);
  
    // Ensure that when nameservers are statically configured that the prepend DNS
    // servers are not used.
    const vector<string> static_servers = {"4.4.4.4", "5.5.5.5"};
    parameters->args_.Set<Strings>(kNameServersProperty, static_servers);
    EXPECT_CALL(*service, HasStaticNameServers()).WillOnce(Return(true));
    OnIPConfigUpdated(ipconfig.get());
    EXPECT_EQ(static_servers, device_->ipconfig()->properties().dns_servers);
  }
  
  TEST_F(DeviceTest, ResolvePeerMacAddress) {
    IPAddress device_address(IPAddress::kFamilyIPv4);
    ASSERT_TRUE(device_address.SetAddressAndPrefixFromString("192.168.5.2/24"));
    EXPECT_CALL(device_info_, GetAddresses(device_->interface_index()))
        .WillRepeatedly(Return(std::vector<IPAddress>{device_address}));
  
    const char kResolvedMac[] = "00:11:22:33:44:55";
    const ByteString kMacBytes(
        Device::MakeHardwareAddressFromString(kResolvedMac));
    EXPECT_CALL(device_info_,
                GetMacAddressOfPeer(device_->interface_index(), _, _))
        .WillRepeatedly(DoAll(SetArgPointee<2>(kMacBytes), Return(true)));
  
    // Invalid peer address (not a valid IP address nor MAC address).
    Error error;
    string result;
    const char kInvalidPeer[] = "peer";
    EXPECT_FALSE(device_->ResolvePeerMacAddress(kInvalidPeer, &result, &error));
    EXPECT_EQ(Error::kInvalidArguments, error.type());
  
    // No direct connectivity to the peer.
    error.Reset();
    EXPECT_FALSE(device_->ResolvePeerMacAddress("192.168.1.1", &result, &error));
    EXPECT_EQ(Error::kInvalidArguments, error.type());
  
    // Provided IP address is in the ARP cache, return the resolved MAC address.
    error.Reset();
    EXPECT_TRUE(device_->ResolvePeerMacAddress("192.168.5.1", &result, &error));
    EXPECT_EQ(kResolvedMac, result);
  }
  
  TEST_F(DeviceTest, SetHostnameWithEmptyHostname) {
    EXPECT_CALL(*manager(), ShouldAcceptHostnameFrom(_)).Times(0);
    EXPECT_CALL(device_info_, SetHostname(_)).Times(0);
    EXPECT_FALSE(SetHostname(""));
  }
  
  TEST_F(DeviceTest, SetHostnameForDisallowedDevice) {
    EXPECT_CALL(*manager(), ShouldAcceptHostnameFrom(kDeviceName))
        .WillOnce(Return(false));
    EXPECT_CALL(device_info_, SetHostname(_)).Times(0);
    EXPECT_FALSE(SetHostname("wilson"));
  }
  
  TEST_F(DeviceTest, SetHostnameWithFailingDeviceInfo) {
    EXPECT_CALL(*manager(), ShouldAcceptHostnameFrom(kDeviceName))
        .WillOnce(Return(true));
    EXPECT_CALL(device_info_, SetHostname("wilson")).WillOnce(Return(false));
    EXPECT_FALSE(SetHostname("wilson"));
  }
  
  TEST_F(DeviceTest, SetHostnameMaximumHostnameLength) {
    EXPECT_CALL(*manager(), ShouldAcceptHostnameFrom(kDeviceName))
        .WillOnce(Return(true));
    EXPECT_CALL(
        device_info_,
        SetHostname(
            "wilson.was-a-good-ball.and-was.an-excellent-swimmer.in-high-seas"))
        .WillOnce(Return(true));
    EXPECT_TRUE(SetHostname(
        "wilson.was-a-good-ball.and-was.an-excellent-swimmer.in-high-seas"));
  }
  
  TEST_F(DeviceTest, SetHostnameTruncateDomainName) {
    EXPECT_CALL(*manager(), ShouldAcceptHostnameFrom(kDeviceName))
        .WillOnce(Return(true));
    EXPECT_CALL(device_info_, SetHostname("wilson")).WillOnce(Return(false));
    EXPECT_FALSE(SetHostname(
        "wilson.was-a-great-ball.and-was.an-excellent-swimmer.in-high-seas"));
  }
  
  TEST_F(DeviceTest, SetHostnameTruncateHostname) {
    EXPECT_CALL(*manager(), ShouldAcceptHostnameFrom(kDeviceName))
        .WillOnce(Return(true));
    EXPECT_CALL(
        device_info_,
        SetHostname(
            "wilson-was-a-great-ball-and-was-an-excellent-swimmer-in-high-sea"))
        .WillOnce(Return(true));
    EXPECT_TRUE(SetHostname(
        "wilson-was-a-great-ball-and-was-an-excellent-swimmer-in-high-sea-chop"));
  }
  
  TEST_F(DeviceTest, SetMacAddress) {
    constexpr char mac_address[] = "abcdefabcdef";
    EXPECT_CALL(*GetDeviceMockAdaptor(),
                EmitStringChanged(kAddressProperty, mac_address));
    EXPECT_NE(mac_address, device_->mac_address());
    device_->device_set_mac_address(mac_address);
    EXPECT_EQ(mac_address, device_->mac_address());
  }
  
  class DevicePortalDetectionTest : public DeviceTest {
   public:
    DevicePortalDetectionTest()
        : connection_(new StrictMock<MockConnection>(&device_info_)),
          service_(new StrictMock<MockService>(manager())),
          portal_detector_(new StrictMock<MockPortalDetector>(connection_)) {}
    ~DevicePortalDetectionTest() override = default;
    void SetUp() override {
      DeviceTest::SetUp();
      SelectService(service_);
      SetConnection(connection_.get());
      device_->portal_detector_.reset(portal_detector_);  // Passes ownership.
    }
  
   protected:
    static const int kPortalAttempts;
  
    bool StartPortalDetection() { return device_->StartPortalDetection(); }
    void StopPortalDetection() { device_->StopPortalDetection(); }
  
    void PortalDetectorCallback(const PortalDetector::Result& http_result,
                                const PortalDetector::Result& https_result) {
      device_->PortalDetectorCallback(http_result, https_result);
    }
    bool RequestPortalDetection() { return device_->RequestPortalDetection(); }
    void SetServiceConnectedState(Service::ConnectState state) {
      device_->SetServiceConnectedState(state);
    }
    void ExpectPortalDetectorReset() {
      EXPECT_EQ(nullptr, device_->portal_detector_);
    }
    void ExpectPortalDetectorSet() {
      EXPECT_NE(nullptr, device_->portal_detector_);
    }
    void ExpectPortalDetectorIsMock() {
      EXPECT_EQ(portal_detector_, device_->portal_detector_.get());
    }
    void InvokeFallbackDNSResultCallback(DnsServerTester::Status status) {
      device_->FallbackDNSResultCallback(status);
    }
    void InvokeConfigDNSResultCallback(DnsServerTester::Status status) {
      device_->ConfigDNSResultCallback(status);
    }
    void DestroyConnection() { device_->DestroyConnection(); }
    scoped_refptr<MockConnection> connection_;
    scoped_refptr<MockService> service_;
  
    // Used only for EXPECT_CALL().  Object is owned by device.
    MockPortalDetector* portal_detector_;
  };
  
  const int DevicePortalDetectionTest::kPortalAttempts = 2;
  
  TEST_F(DevicePortalDetectionTest, ServicePortalDetectionDisabled) {
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(true));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    EXPECT_FALSE(StartPortalDetection());
  }
  
  TEST_F(DevicePortalDetectionTest, TechnologyPortalDetectionDisabled) {
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillOnce(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillOnce(Return(false));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    EXPECT_FALSE(StartPortalDetection());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionProxyConfig) {
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillOnce(Return(true));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillOnce(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillOnce(Return(true));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    EXPECT_FALSE(StartPortalDetection());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionBadUrl) {
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillOnce(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillOnce(Return(true));
    const string http_portal_url, https_portal_url;
    const vector<string> fallback_urls;
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillRepeatedly(ReturnRef(http_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillRepeatedly(ReturnRef(https_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(fallback_urls));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    EXPECT_FALSE(StartPortalDetection());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionStart) {
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillOnce(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillOnce(Return(true));
    const string http_portal_url(PortalDetector::kDefaultHttpUrl);
    const string https_portal_url(PortalDetector::kDefaultHttpsUrl);
    const vector<string> fallback_urls(PortalDetector::kDefaultFallbackHttpUrls);
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillRepeatedly(ReturnRef(http_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillRepeatedly(ReturnRef(https_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(fallback_urls));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    EXPECT_CALL(*connection_, IsIPv6()).WillRepeatedly(Return(false));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection_, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
    EXPECT_TRUE(StartPortalDetection());
  
    // Drop all references to device_info before it falls out of scope.
    SetConnection(nullptr);
    StopPortalDetection();
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionStartIPv6) {
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillOnce(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillOnce(Return(true));
    const string http_portal_url(PortalDetector::kDefaultHttpUrl);
    const string https_portal_url(PortalDetector::kDefaultHttpsUrl);
    const vector<string> fallback_urls(PortalDetector::kDefaultFallbackHttpUrls);
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillRepeatedly(ReturnRef(http_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillRepeatedly(ReturnRef(https_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(fallback_urls));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline)).Times(0);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    EXPECT_CALL(*connection_, IsIPv6()).WillRepeatedly(Return(true));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection_, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
    EXPECT_TRUE(StartPortalDetection());
  
    // Drop all references to device_info before it falls out of scope.
    SetConnection(nullptr);
    StopPortalDetection();
  }
  
  MATCHER_P(IsPortalDetectorResult, result, "") {
    return (result.num_attempts == arg.num_attempts &&
            result.phase == arg.phase && result.status == arg.status);
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionFailure) {
    PortalDetector::Result http_result(PortalDetector::Phase::kConnection,
                                       PortalDetector::Status::kFailure,
                                       kPortalAttempts);
    PortalDetector::Result https_result(PortalDetector::Phase::kContent,
                                        PortalDetector::Status::kSuccess);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
                                          kPortalDetectionStatusFailure));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    EXPECT_CALL(*metrics(), SendEnumToUMA("Network.Shill.Unknown.PortalResult",
                                          Metrics::kPortalResultConnectionFailure,
                                          Metrics::kPortalResultMax));
    EXPECT_CALL(
        *metrics(),
        SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline", _, _, _, _))
        .Times(0);
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(http_result),
                              IsPortalDetectorResult(https_result)));
    PortalDetectorCallback(http_result, https_result);
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionSuccess) {
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_, SetPortalDetectionFailure(_, _)).Times(0);
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    EXPECT_CALL(*metrics(), SendEnumToUMA("Network.Shill.Unknown.PortalResult",
                                          Metrics::kPortalResultSuccess,
                                          Metrics::kPortalResultMax));
    EXPECT_CALL(
        *metrics(),
        SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline", kPortalAttempts,
                  Metrics::kMetricPortalAttemptsToOnlineMin,
                  Metrics::kMetricPortalAttemptsToOnlineMax,
                  Metrics::kMetricPortalAttemptsToOnlineNumBuckets));
    EXPECT_CALL(*metrics(),
                SendToUMA("Network.Shill.Unknown.PortalAttempts", _, _, _, _))
        .Times(0);
    PortalDetectorCallback(
        PortalDetector::Result(PortalDetector::Phase::kContent,
                               PortalDetector::Status::kSuccess, kPortalAttempts),
        PortalDetector::Result(PortalDetector::Phase::kContent,
                               PortalDetector::Status::kSuccess));
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionSuccessAfterFailure) {
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
                                          kPortalDetectionStatusFailure));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    EXPECT_CALL(*metrics(), SendEnumToUMA("Network.Shill.Unknown.PortalResult",
                                          Metrics::kPortalResultConnectionFailure,
                                          Metrics::kPortalResultMax));
    EXPECT_CALL(
        *metrics(),
        SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline", _, _, _, _))
        .Times(0);
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    PortalDetectorCallback(
        PortalDetector::Result(PortalDetector::Phase::kConnection,
                               PortalDetector::Status::kFailure, kPortalAttempts),
        PortalDetector::Result(PortalDetector::Phase::kContent,
                               PortalDetector::Status::kFailure));
    Mock::VerifyAndClearExpectations(metrics());
    EXPECT_CALL(*service_, SetPortalDetectionFailure(_, _)).Times(0);
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    EXPECT_CALL(*metrics(), SendEnumToUMA("Network.Shill.Unknown.PortalResult",
                                          Metrics::kPortalResultSuccess,
                                          Metrics::kPortalResultMax));
    EXPECT_CALL(
        *metrics(),
        SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline",
                  kPortalAttempts * 2, Metrics::kMetricPortalAttemptsToOnlineMin,
                  Metrics::kMetricPortalAttemptsToOnlineMax,
                  Metrics::kMetricPortalAttemptsToOnlineNumBuckets));
    PortalDetectorCallback(
        PortalDetector::Result(PortalDetector::Phase::kContent,
                               PortalDetector::Status::kSuccess,
                               kPortalAttempts * 2),
        PortalDetector::Result(PortalDetector::Phase::kContent,
                               PortalDetector::Status::kSuccess));
  }
  
  TEST_F(DevicePortalDetectionTest, RequestPortalDetection) {
    EXPECT_CALL(*service_, state())
        .WillOnce(Return(Service::kStateOnline))
        .WillRepeatedly(Return(Service::kStateNoConnectivity));
    EXPECT_FALSE(RequestPortalDetection());
  
    EXPECT_CALL(*connection_, IsDefault())
        .WillOnce(Return(false))
        .WillRepeatedly(Return(true));
    EXPECT_FALSE(RequestPortalDetection());
  
    EXPECT_CALL(*portal_detector_, IsInProgress()).WillOnce(Return(true));
    // Portal detection already running.
    EXPECT_TRUE(RequestPortalDetection());
  
    // Make sure our running mock portal detector was not replaced.
    ExpectPortalDetectorIsMock();
  
    // Throw away our pre-fabricated portal detector, and have the device create
    // a new one.
    StopPortalDetection();
    EXPECT_CALL(*service_, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillRepeatedly(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillRepeatedly(Return(false));
    const string kPortalCheckHttpUrl("http://portal");
    const string kPortalCheckHttpsUrl("https://portal");
    const vector<string> kPortalCheckFallbackHttpUrls(
        {"http://fallback", "http://other"});
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillOnce(ReturnRef(kPortalCheckHttpUrl));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillOnce(ReturnRef(kPortalCheckHttpsUrl));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(kPortalCheckFallbackHttpUrls));
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, IsIPv6()).WillRepeatedly(Return(false));
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection_, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
    EXPECT_TRUE(RequestPortalDetection());
  }
  
  TEST_F(DevicePortalDetectionTest, RequestStartConnectivityTest) {
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    EXPECT_CALL(*connection_, IsIPv6()).WillRepeatedly(Return(false));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection_, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
  
    EXPECT_EQ(nullptr, device_->connection_tester_);
    EXPECT_TRUE(device_->StartConnectivityTest());
    EXPECT_NE(nullptr, device_->connection_tester_);
  }
  
  TEST_F(DevicePortalDetectionTest, NotConnected) {
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(false));
    SetServiceConnectedState(Service::kStateNoConnectivity);
    // We don't check for the portal detector to be reset here, because
    // it would have been reset as a part of disconnection.
  }
  
  TEST_F(DevicePortalDetectionTest, NotPortal) {
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_, SetState(Service::kStateOnline));
    SetServiceConnectedState(Service::kStateOnline);
    ExpectPortalDetectorReset();
  }
  
  TEST_F(DevicePortalDetectionTest, NotDefault) {
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    SetServiceConnectedState(Service::kStateNoConnectivity);
    ExpectPortalDetectorReset();
  }
  
  TEST_F(DevicePortalDetectionTest, PortalIntervalIsZero) {
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(true));
    device_->portal_check_interval_seconds_ = 0;
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    SetServiceConnectedState(Service::kStateNoConnectivity);
    ExpectPortalDetectorReset();
  }
  
  TEST_F(DevicePortalDetectionTest, RestartPortalDetection) {
    int portal_check_interval = 3;
    device_->portal_check_interval_seconds_ = portal_check_interval;
    const string kPortalCheckHttpUrl("http://portal");
    const string kPortalCheckHttpsUrl("https://portal");
    const vector<string> kPortalCheckFallbackHttpUrls(
        {"http://fallback", "http://other"});
    PortalDetector::Properties props = PortalDetector::Properties(
        kPortalCheckHttpUrl, kPortalCheckHttpsUrl, kPortalCheckFallbackHttpUrls);
    for (int i = 0; i < 10; i++) {
      EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
      EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(true));
      EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
          .WillOnce(ReturnRef(kPortalCheckHttpUrl));
      EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
          .WillOnce(ReturnRef(kPortalCheckHttpsUrl));
      EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
          .WillRepeatedly(ReturnRef(kPortalCheckFallbackHttpUrls));
      EXPECT_CALL(*portal_detector_, AdjustStartDelay(portal_check_interval))
          .WillOnce(Return(portal_check_interval));
      EXPECT_CALL(*portal_detector_,
                  StartAfterDelay(props, portal_check_interval))
          .WillOnce(Return(true));
      EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
      SetServiceConnectedState(Service::kStateNoConnectivity);
      portal_check_interval =
          std::min(portal_check_interval * 2,
                   PortalDetector::kMaxPortalCheckIntervalSeconds);
    }
    ExpectPortalDetectorSet();
  }
  
  TEST_F(DevicePortalDetectionTest, CancelledOnSelectService) {
    ExpectPortalDetectorSet();
    EXPECT_CALL(*service_, state()).WillOnce(Return(Service::kStateIdle));
    EXPECT_CALL(*service_, SetState(_));
    EXPECT_CALL(*service_, SetConnection(_));
    SelectService(nullptr);
    ExpectPortalDetectorReset();
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionDNSFailure) {
    static const char* const kGoogleDNSServers[] = {"8.8.8.8", "8.8.4.4"};
    vector<string> fallback_dns_servers(kGoogleDNSServers, kGoogleDNSServers + 2);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
  
    // DNS Failure, start DNS test for fallback DNS servers.
    PortalDetector::Result result_dns_failure(PortalDetector::Phase::kDNS,
                                              PortalDetector::Status::kFailure,
                                              kPortalAttempts);
    PortalDetector::Result https_result(PortalDetector::Phase::kContent,
                                        PortalDetector::Status::kFailure);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseDns,
                                          kPortalDetectionStatusFailure));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(result_dns_failure),
                              IsPortalDetectorResult(https_result)));
    EXPECT_CALL(*device_, StartDNSTest(fallback_dns_servers, false, _)).Times(1);
    PortalDetectorCallback(result_dns_failure, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  
    // DNS Timeout, start DNS test for fallback DNS servers.
    PortalDetector::Result result_dns_timeout(PortalDetector::Phase::kDNS,
                                              PortalDetector::Status::kTimeout,
                                              kPortalAttempts);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseDns,
                                          kPortalDetectionStatusTimeout));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(result_dns_timeout),
                              IsPortalDetectorResult(https_result)));
    EXPECT_CALL(*device_, StartDNSTest(fallback_dns_servers, false, _)).Times(1);
    PortalDetectorCallback(result_dns_timeout, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  
    // Other Failure, DNS server tester not started.
    PortalDetector::Result result_connection_failure(
        PortalDetector::Phase::kConnection, PortalDetector::Status::kFailure,
        kPortalAttempts);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
                                          kPortalDetectionStatusFailure));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(result_connection_failure),
                              IsPortalDetectorResult(https_result)));
    EXPECT_CALL(*device_, StartDNSTest(_, _, _)).Times(0);
    PortalDetectorCallback(result_connection_failure, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionRedirect) {
    static const char* const kGoogleDNSServers[] = {"8.8.8.8", "8.8.4.4"};
    vector<string> fallback_dns_servers(kGoogleDNSServers, kGoogleDNSServers + 2);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
  
    PortalDetector::Result result_redirect(PortalDetector::Phase::kContent,
                                           PortalDetector::Status::kRedirect);
    PortalDetector::Result https_result(PortalDetector::Phase::kContent,
                                        PortalDetector::Status::kSuccess);
    result_redirect.redirect_url_string = PortalDetector::kDefaultHttpUrl;
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseContent,
                                          kPortalDetectionStatusRedirect));
    EXPECT_CALL(*service_, SetState(Service::kStateRedirectFound));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(result_redirect),
                              IsPortalDetectorResult(https_result)));
    PortalDetectorCallback(result_redirect, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionRedirectNoUrl) {
    static const char* const kGoogleDNSServers[] = {"8.8.8.8", "8.8.4.4"};
    vector<string> fallback_dns_servers(kGoogleDNSServers, kGoogleDNSServers + 2);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
  
    PortalDetector::Result result_redirect(PortalDetector::Phase::kContent,
                                           PortalDetector::Status::kRedirect);
    PortalDetector::Result https_result(PortalDetector::Phase::kContent,
                                        PortalDetector::Status::kSuccess);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseContent,
                                          kPortalDetectionStatusRedirect));
    EXPECT_CALL(*service_, SetState(Service::kStatePortalSuspected));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(result_redirect),
                              IsPortalDetectorResult(https_result)));
    PortalDetectorCallback(result_redirect, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionPortalSuspected) {
    static const char* const kGoogleDNSServers[] = {"8.8.8.8", "8.8.4.4"};
    vector<string> fallback_dns_servers(kGoogleDNSServers, kGoogleDNSServers + 2);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
  
    PortalDetector::Result http_result(PortalDetector::Phase::kContent,
                                       PortalDetector::Status::kSuccess);
    PortalDetector::Result https_result(PortalDetector::Phase::kContent,
                                        PortalDetector::Status::kFailure);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseContent,
                                          kPortalDetectionStatusSuccess));
    EXPECT_CALL(*service_, SetState(Service::kStatePortalSuspected));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(http_result),
                              IsPortalDetectorResult(https_result)));
    PortalDetectorCallback(http_result, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  }
  
  TEST_F(DevicePortalDetectionTest, PortalDetectionNoConnectivity) {
    static const char* const kGoogleDNSServers[] = {"8.8.8.8", "8.8.4.4"};
    vector<string> fallback_dns_servers(kGoogleDNSServers, kGoogleDNSServers + 2);
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
  
    PortalDetector::Result http_result(PortalDetector::Phase::kUnknown,
                                       PortalDetector::Status::kFailure);
    PortalDetector::Result https_result(PortalDetector::Phase::kContent,
                                        PortalDetector::Status::kFailure);
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillOnce(Return(true));
    EXPECT_CALL(*service_,
                SetPortalDetectionFailure(kPortalDetectionPhaseUnknown,
                                          kPortalDetectionStatusFailure));
    EXPECT_CALL(*service_, SetState(Service::kStateNoConnectivity));
    EXPECT_CALL(*connection_, IsDefault()).WillOnce(Return(false));
    EXPECT_CALL(*connection_, IsIPv6()).WillOnce(Return(false));
    EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                              IsPortalDetectorResult(http_result),
                              IsPortalDetectorResult(https_result)));
    PortalDetectorCallback(http_result, https_result);
    Mock::VerifyAndClearExpectations(device_.get());
  }
  
  TEST_F(DevicePortalDetectionTest, FallbackDNSResultCallback) {
    scoped_refptr<MockIPConfig> ipconfig =
        new MockIPConfig(control_interface(), kDeviceName);
    device_->set_ipconfig(ipconfig);
  
    // Fallback DNS test failed.
    EXPECT_CALL(*connection_, UpdateDNSServers(_)).Times(0);
    EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(0);
    EXPECT_CALL(*device_, StartDNSTest(_, _, _)).Times(0);
    EXPECT_CALL(*metrics(), NotifyFallbackDNSTestResult(
                                _, Metrics::kFallbackDNSTestResultFailure))
        .Times(1);
    InvokeFallbackDNSResultCallback(DnsServerTester::kStatusFailure);
    Mock::VerifyAndClearExpectations(connection_.get());
    Mock::VerifyAndClearExpectations(ipconfig.get());
    Mock::VerifyAndClearExpectations(metrics());
  
    // Fallback DNS test succeed with auto fallback disabled.
    EXPECT_CALL(*service_, is_dns_auto_fallback_allowed())
        .WillOnce(Return(false));
    EXPECT_CALL(*connection_, UpdateDNSServers(_)).Times(0);
    EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(0);
    EXPECT_CALL(*service_, NotifyIPConfigChanges()).Times(0);
    EXPECT_CALL(*device_, StartDNSTest(_, _, _)).Times(0);
    EXPECT_CALL(*metrics(), NotifyFallbackDNSTestResult(
                                _, Metrics::kFallbackDNSTestResultSuccess))
        .Times(1);
    InvokeFallbackDNSResultCallback(DnsServerTester::kStatusSuccess);
    Mock::VerifyAndClearExpectations(service_.get());
    Mock::VerifyAndClearExpectations(connection_.get());
    Mock::VerifyAndClearExpectations(ipconfig.get());
    Mock::VerifyAndClearExpectations(metrics());
  
    // Fallback DNS test succeed with auto fallback enabled.
    EXPECT_CALL(*service_, is_dns_auto_fallback_allowed()).WillOnce(Return(true));
    EXPECT_CALL(*service_, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillRepeatedly(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillRepeatedly(Return(false));
    const string kPortalCheckHttpUrl("http://portal");
    const string kPortalCheckHttpsUrl("https://portal");
    const vector<string> kPortalCheckFallbackHttpUrls(
        {"http://fallback", "http://other"});
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillOnce(ReturnRef(kPortalCheckHttpUrl));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillOnce(ReturnRef(kPortalCheckHttpsUrl));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(kPortalCheckFallbackHttpUrls));
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, IsIPv6()).WillRepeatedly(Return(false));
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection_, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
  
    EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(1);
    EXPECT_CALL(*connection_, UpdateDNSServers(_)).Times(1);
    EXPECT_CALL(*service_, NotifyIPConfigChanges()).Times(1);
    EXPECT_CALL(*device_, StartDNSTest(_, true, _)).Times(1);
    EXPECT_CALL(*metrics(), NotifyFallbackDNSTestResult(
                                _, Metrics::kFallbackDNSTestResultSuccess))
        .Times(1);
    InvokeFallbackDNSResultCallback(DnsServerTester::kStatusSuccess);
    Mock::VerifyAndClearExpectations(service_.get());
    Mock::VerifyAndClearExpectations(connection_.get());
    Mock::VerifyAndClearExpectations(ipconfig.get());
    Mock::VerifyAndClearExpectations(metrics());
  }
  
  TEST_F(DevicePortalDetectionTest, ConfigDNSResultCallback) {
    scoped_refptr<MockIPConfig> ipconfig =
        new MockIPConfig(control_interface(), kDeviceName);
    device_->set_ipconfig(ipconfig);
  
    // DNS test failed for configured DNS servers.
    EXPECT_CALL(*connection_, UpdateDNSServers(_)).Times(0);
    EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(0);
    InvokeConfigDNSResultCallback(DnsServerTester::kStatusFailure);
    Mock::VerifyAndClearExpectations(connection_.get());
    Mock::VerifyAndClearExpectations(ipconfig.get());
  
    // DNS test succeed for configured DNS servers.
    EXPECT_CALL(*service_, IsPortalDetectionDisabled())
        .WillRepeatedly(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillRepeatedly(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillRepeatedly(Return(false));
    const string kPortalCheckHttpUrl("http://portal");
    const string kPortalCheckHttpsUrl("https://portal");
    const vector<string> kPortalCheckFallbackHttpUrls(
        {"http://fallback", "http://other"});
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillOnce(ReturnRef(kPortalCheckHttpUrl));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillOnce(ReturnRef(kPortalCheckHttpsUrl));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(kPortalCheckFallbackHttpUrls));
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection_, IsIPv6()).WillRepeatedly(Return(false));
    EXPECT_CALL(*connection_, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection_, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
    EXPECT_CALL(*connection_, UpdateDNSServers(_)).Times(1);
    EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(1);
    EXPECT_CALL(*service_, NotifyIPConfigChanges()).Times(1);
    InvokeConfigDNSResultCallback(DnsServerTester::kStatusSuccess);
    Mock::VerifyAndClearExpectations(service_.get());
    Mock::VerifyAndClearExpectations(connection_.get());
    Mock::VerifyAndClearExpectations(ipconfig.get());
  }
  
  TEST_F(DevicePortalDetectionTest, DestroyConnection) {
    scoped_refptr<MockConnection> connection =
        new NiceMock<MockConnection>(&device_info_);
    // This test holds a single reference to the mock connection.
    EXPECT_TRUE(connection->HasOneRef());
  
    SetConnection(connection);
  
    EXPECT_CALL(*service_, IsPortalDetectionDisabled()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsConnected(nullptr)).WillRepeatedly(Return(true));
    EXPECT_CALL(*service_, HasProxyConfig()).WillOnce(Return(false));
    EXPECT_CALL(*service_, IsPortalDetectionAuto()).WillOnce(Return(true));
    EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
        .WillOnce(Return(true));
    const string http_portal_url(PortalDetector::kDefaultHttpUrl);
    const string https_portal_url(PortalDetector::kDefaultHttpsUrl);
    const vector<string> fallback_urls(PortalDetector::kDefaultFallbackHttpUrls);
    EXPECT_CALL(manager_, GetPortalCheckHttpUrl())
        .WillRepeatedly(ReturnRef(http_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckHttpsUrl())
        .WillRepeatedly(ReturnRef(https_portal_url));
    EXPECT_CALL(manager_, GetPortalCheckFallbackHttpUrls())
        .WillRepeatedly(ReturnRef(fallback_urls));
    const string kInterfaceName("int0");
    EXPECT_CALL(*connection, interface_name())
        .WillRepeatedly(ReturnRef(kInterfaceName));
    EXPECT_CALL(*connection, IsIPv6()).WillRepeatedly(Return(false));
    const vector<string> kDNSServers;
    EXPECT_CALL(*connection, dns_servers())
        .WillRepeatedly(ReturnRef(kDNSServers));
  
    EXPECT_TRUE(device_->StartConnectivityTest());
    EXPECT_TRUE(StartPortalDetection());
  
    // Ensure that the DestroyConnection method removes all connection references
    // except the one left in this scope.
    EXPECT_CALL(*service_, SetConnection(IsNullRefPtr()));
    DestroyConnection();
    EXPECT_TRUE(connection->HasOneRef());
  }
  
  class DeviceByteCountTest : public DeviceTest {
   public:
    DeviceByteCountTest()
        : rx_byte_count_(0),
          tx_byte_count_(0),
          rx_stored_byte_count_(0),
          tx_stored_byte_count_(0) {}
    ~DeviceByteCountTest() override = default;
  
    void SetUp() override {
      DeviceTest::SetUp();
      EXPECT_CALL(*manager(), device_info())
          .WillRepeatedly(Return(&device_info_));
      EXPECT_CALL(device_info_, GetByteCounts(kDeviceInterfaceIndex, _, _))
          .WillRepeatedly(Invoke(this, &DeviceByteCountTest::ReturnByteCounts));
      const string id = device_->GetStorageIdentifier();
      EXPECT_CALL(storage_, ContainsGroup(id)).WillRepeatedly(Return(true));
      EXPECT_CALL(storage_, GetUint64(id, Device::kStorageReceiveByteCount, _))
          .WillRepeatedly(
              Invoke(this, &DeviceByteCountTest::GetStoredReceiveCount));
      EXPECT_CALL(storage_, GetUint64(id, Device::kStorageTransmitByteCount, _))
          .WillRepeatedly(
              Invoke(this, &DeviceByteCountTest::GetStoredTransmitCount));
    }
  
    bool ReturnByteCounts(int interface_index, uint64_t* rx, uint64_t* tx) {
      *rx = rx_byte_count_;
      *tx = tx_byte_count_;
      return true;
    }
  
    bool ExpectByteCounts(DeviceRefPtr device,
                          int64_t expected_rx,
                          int64_t expected_tx) {
      int64_t actual_rx = device->GetReceiveByteCount();
      int64_t actual_tx = device->GetTransmitByteCount();
      EXPECT_EQ(expected_rx, actual_rx);
      EXPECT_EQ(expected_tx, actual_tx);
      return expected_rx == actual_rx && expected_tx == actual_tx;
    }
  
    void ExpectSavedCounts(DeviceRefPtr device,
                           int64_t expected_rx,
                           int64_t expected_tx) {
      EXPECT_CALL(storage_,
                  SetUint64(_, Device::kStorageReceiveByteCount, expected_rx))
          .WillOnce(Return(true));
      EXPECT_CALL(storage_,
                  SetUint64(_, Device::kStorageTransmitByteCount, expected_tx))
          .WillOnce(Return(true));
      EXPECT_TRUE(device->Save(&storage_));
    }
  
    bool GetStoredReceiveCount(const string& group,
                               const string& key,
                               uint64_t* value) {
      if (!rx_stored_byte_count_) {
        return false;
      }
      *value = rx_stored_byte_count_;
      return true;
    }
  
    bool GetStoredTransmitCount(const string& group,
                                const string& key,
                                uint64_t* value) {
      if (!tx_stored_byte_count_) {
        return false;
      }
      *value = tx_stored_byte_count_;
      return true;
    }
  
   protected:
    NiceMock<MockStore> storage_;
    uint64_t rx_byte_count_;
    uint64_t tx_byte_count_;
    uint64_t rx_stored_byte_count_;
    uint64_t tx_stored_byte_count_;
  };
  
  TEST_F(DeviceByteCountTest, GetByteCounts) {
    // On Device initialization, byte counts should be zero, independent of
    // the byte counts reported by the interface.
    rx_byte_count_ = 123;
    tx_byte_count_ = 456;
    DeviceRefPtr device(new TestDevice(manager(), kDeviceName, kDeviceAddress,
                                       kDeviceInterfaceIndex,
                                       Technology::kUnknown));
    EXPECT_TRUE(ExpectByteCounts(device, 0, 0));
  
    // Device should report any increase in the byte counts reported in the
    // interface.
    const int64_t delta_rx_count = 789;
    const int64_t delta_tx_count = 12;
    rx_byte_count_ += delta_rx_count;
    tx_byte_count_ += delta_tx_count;
    EXPECT_TRUE(ExpectByteCounts(device, delta_rx_count, delta_tx_count));
  
    // Expect the correct values to be saved to the profile.
    ExpectSavedCounts(device, delta_rx_count, delta_tx_count);
  
    // If Device is loaded from a profile that does not contain stored byte
    // counts, the byte counts reported should remain unchanged.
    EXPECT_TRUE(device->Load(&storage_));
    EXPECT_TRUE(ExpectByteCounts(device, delta_rx_count, delta_tx_count));
  
    // If Device is loaded from a profile that contains stored byte
    // counts, the byte counts reported should now reflect the stored values.
    rx_stored_byte_count_ = 345;
    tx_stored_byte_count_ = 678;
    EXPECT_TRUE(device->Load(&storage_));
    EXPECT_TRUE(
        ExpectByteCounts(device, rx_stored_byte_count_, tx_stored_byte_count_));
  
    // Increases to the interface receive count should be reflected as offsets
    // to the stored byte counts.
    rx_byte_count_ += delta_rx_count;
    tx_byte_count_ += delta_tx_count;
    EXPECT_TRUE(ExpectByteCounts(device, rx_stored_byte_count_ + delta_rx_count,
                                 tx_stored_byte_count_ + delta_tx_count));
  
    // Expect the correct values to be saved to the profile.
    ExpectSavedCounts(device, rx_stored_byte_count_ + delta_rx_count,
                      tx_stored_byte_count_ + delta_tx_count);
  
    // Expect that after resetting byte counts, read-back values return to zero,
    // and that the device requests this information to be persisted.
    EXPECT_CALL(*manager(), UpdateDevice(device));
    device->ResetByteCounters();
    EXPECT_TRUE(ExpectByteCounts(device, 0, 0));
  }
  
  }  // namespace shill
  