//
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "shill/device.h"

#include <ctype.h>
#include <sys/socket.h>
#include <linux/if.h>  // NOLINT - Needs typedefs from sys/socket.h.

#include <map>
#include <memory>
#include <string>
#include <vector>

#include <base/bind.h>
#include <base/callback.h>
#include <base/macros.h>
#include <base/memory/weak_ptr.h>
#if defined(__ANDROID__)
#include <dbus/service_constants.h>
#else
#include <chromeos/dbus/service_constants.h>
#endif  // __ANDROID__
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "shill/connectivity_trial.h"
#include "shill/dhcp/dhcp_provider.h"
#include "shill/dhcp/mock_dhcp_config.h"
#include "shill/dhcp/mock_dhcp_provider.h"
#include "shill/event_dispatcher.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_connection.h"
#include "shill/mock_connection_health_checker.h"
#include "shill/mock_control.h"
#include "shill/mock_dhcp_properties.h"
#include "shill/mock_device.h"
#include "shill/mock_device_info.h"
#include "shill/mock_dns_server_tester.h"
#include "shill/mock_event_dispatcher.h"
#include "shill/mock_ip_address_store.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/property_store_unittest.h"
#include "shill/static_ip_parameters.h"
#include "shill/technology.h"
#include "shill/testing.h"
#include "shill/tethering.h"
#include "shill/traffic_monitor.h"

using base::Bind;
using base::Callback;
using std::map;
using std::string;
using std::vector;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::DefaultValue;
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;
using ::testing::Test;
using ::testing::Values;

namespace shill {

class TestDevice : public Device {
 public:
  TestDevice(ControlInterface* control_interface,
             EventDispatcher* dispatcher,
             Metrics* metrics,
             Manager* manager,
             const std::string& link_name,
             const std::string& address,
             int interface_index,
             Technology::Identifier technology)
      : Device(control_interface, dispatcher, metrics, manager, link_name,
               address, interface_index, technology) {
    ON_CALL(*this, IsIPv6Allowed())
        .WillByDefault(Invoke(this, &TestDevice::DeviceIsIPv6Allowed));
    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));
  }

  ~TestDevice() {}

  virtual void Start(Error* error,
                     const EnabledStateChangedCallback& callback) {
    DCHECK(error);
  }

  virtual void Stop(Error* error,
                    const EnabledStateChangedCallback& callback) {
    DCHECK(error);
  }

  MOCK_CONST_METHOD0(IsIPv6Allowed, bool());
  MOCK_CONST_METHOD0(IsTrafficMonitorEnabled, bool());

  MOCK_METHOD3(SetIPFlag, bool(IPAddress::Family family,
                               const std::string& flag,
                               const std::string& value));

  MOCK_METHOD3(StartDNSTest, bool(
      const std::vector<std::string>& dns_servers,
      const bool retry_until_success,
      const base::Callback<void(const DNSServerTester::Status)>& callback));

  MOCK_METHOD1(StartConnectionDiagnosticsAfterPortalDetection,
               bool(const PortalDetector::Result& result));

  virtual bool DeviceIsIPv6Allowed() const {
    return Device::IsIPv6Allowed();
  }

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

class DeviceTest : public PropertyStoreTest {
 public:
  DeviceTest()
      : device_(new TestDevice(control_interface(),
                               dispatcher(),
                               nullptr,
                               manager(),
                               kDeviceName,
                               kDeviceAddress,
                               kDeviceInterfaceIndex,
                               Technology::kUnknown)),
        device_info_(control_interface(), nullptr, nullptr, nullptr),
        metrics_(dispatcher()) {
    DHCPProvider::GetInstance()->control_interface_ = control_interface();
    DHCPProvider::GetInstance()->dispatcher_ = dispatcher();
    device_->time_ = &time_;
  }
  virtual ~DeviceTest() {}

  virtual void SetUp() {
    device_->metrics_ = &metrics_;
    device_->rtnl_handler_ = &rtnl_handler_;
  }

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

  void SetTrafficMonitor(TrafficMonitor* traffic_monitor) {
    device_->set_traffic_monitor(traffic_monitor);  // Passes ownership.
  }

  void StartTrafficMonitor() {
    device_->StartTrafficMonitor();
  }

  void StopTrafficMonitor() {
    device_->StopTrafficMonitor();
  }

  void NetworkProblemDetected(int reason) {
    device_->OnEncounterNetworkProblem(reason);
  }

  DeviceMockAdaptor* GetDeviceMockAdaptor() {
    return static_cast<DeviceMockAdaptor*>(device_->adaptor_.get());
  }

  void SetManager(Manager* manager) {
    device_->manager_ = manager;
  }

  MOCK_METHOD0(ReliableLinkCallback, void());
  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.push_back(kDnsServer1);
    properties.dns_servers.push_back(kDnsServer2);

    device_->ip6config_ = new MockIPConfig(control_interface(), kDeviceName);
    device_->ip6config_->set_properties(properties);
  }

  bool SetHostname(const string& hostname) {
    return device_->SetHostname(hostname);
  }

  MockControl control_interface_;
  scoped_refptr<TestDevice> device_;
  MockDeviceInfo device_info_;
  MockMetrics metrics_;
  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, PropertyStoreTest::kStringV, &error));
  EXPECT_EQ(Error::kInvalidArguments, error.type());
}

TEST_F(DeviceTest, ClearReadOnlyProperty) {
  Error error;
  EXPECT_FALSE(device_->mutable_store()->SetAnyProperty(
      kAddressProperty, PropertyStoreTest::kStringV, &error));
}

TEST_F(DeviceTest, ClearReadOnlyDerivedProperty) {
  Error error;
  EXPECT_FALSE(device_->mutable_store()->SetAnyProperty(
      kIPConfigsProperty, PropertyStoreTest::kStringsV, &error));
}

TEST_F(DeviceTest, DestroyIPConfig) {
  ASSERT_FALSE(device_->ipconfig_.get());
  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_FALSE(device_->ipconfig_.get());
  ASSERT_FALSE(device_->ip6config_.get());
  ASSERT_FALSE(device_->dhcpv6_config_.get());
}

TEST_F(DeviceTest, DestroyIPConfigNULL) {
  ASSERT_FALSE(device_->ipconfig_.get());
  ASSERT_FALSE(device_->ip6config_.get());
  ASSERT_FALSE(device_->dhcpv6_config_.get());
  device_->DestroyIPConfig();
  ASSERT_FALSE(device_->ipconfig_.get());
  ASSERT_FALSE(device_->ip6config_.get());
  ASSERT_FALSE(device_->dhcpv6_config_.get());
}

MATCHER_P(IsCombinedDhcpProperties, dhcp_props, "") {
  return dhcp_props == arg.properties();
}

TEST_F(DeviceTest, AcquireIPConfigWithSelectedService) {
  device_->ipconfig_ = new IPConfig(control_interface(), "randomname");
  std::unique_ptr<MockDHCPProvider> dhcp_provider(new MockDHCPProvider());
  device_->dhcp_provider_ = dhcp_provider.get();

  scoped_refptr<MockDHCPConfig> dhcp_config(
      new MockDHCPConfig(control_interface(), kDeviceName));
  NiceMock<MockStore> storage;
  const string service_storage_id = "service_storage_id";
  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));

  std::unique_ptr<DhcpProperties> service_dhcp_properties(new DhcpProperties());
  service_dhcp_properties->Load(&storage, service_storage_id);

  scoped_refptr<MockService> service(
      new NiceMock<MockService>(control_interface(),
                                dispatcher(),
                                metrics(),
                                manager()));
  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.VendorClass", _))
      .WillOnce(DoAll(SetArgPointee<2>(string("vendorclass")),
                      Return(true)));
  EXPECT_CALL(default_profile_storage, GetString(default_profile_storage_id,
                                 "DHCPProperty.Hostname", _))
      .WillOnce(Return(false));

  std::unique_ptr<DhcpProperties> manager_dhcp_properties(new DhcpProperties());
  manager_dhcp_properties->Load(&default_profile_storage,
                                default_profile_storage_id);
  std::unique_ptr<DhcpProperties> combined_props =
      DhcpProperties::Combine(*manager_dhcp_properties,
                              *service_dhcp_properties);
  service->dhcp_properties_ = std::move(service_dhcp_properties);
#ifndef DISABLE_DHCPV6
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);
  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
  device_->manager_->dhcp_properties_ = std::move(manager_dhcp_properties);
  EXPECT_CALL(*dhcp_provider,
              CreateIPv4Config(_, _, _,
                               IsCombinedDhcpProperties(
                                   combined_props->properties())))
      .WillOnce(Return(dhcp_config));
  EXPECT_CALL(*dhcp_config, RequestIP())
      .WillOnce(Return(true));
  EXPECT_TRUE(device_->AcquireIPConfig());
  ASSERT_TRUE(device_->ipconfig_.get());
  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");
  std::unique_ptr<MockDHCPProvider> dhcp_provider(new MockDHCPProvider());
  device_->dhcp_provider_ = dhcp_provider.get();
  scoped_refptr<MockDHCPConfig> dhcp_config(
      new MockDHCPConfig(control_interface(), kDeviceName));
  std::unique_ptr<DhcpProperties> manager_dhcp_properties(new DhcpProperties());
  device_->manager_->dhcp_properties_ = std::move(manager_dhcp_properties);
#ifndef DISABLE_DHCPV6
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);
  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(_, _, _,
                               Ref(*(device_->manager_->dhcp_properties_))))
      .WillOnce(Return(dhcp_config));
  EXPECT_CALL(*dhcp_config, RequestIP())
      .WillOnce(Return(true));
  EXPECT_TRUE(device_->AcquireIPConfig());
  ASSERT_TRUE(device_->ipconfig_.get());
  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;

  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  EXPECT_CALL(manager, GetMinimumMTU()).WillOnce(Return(minimum_mtu));

  device_->ipconfig_ = new IPConfig(control_interface(), "anothername");
  std::unique_ptr<MockDHCPProvider> dhcp_provider(new MockDHCPProvider());
  device_->dhcp_provider_ = dhcp_provider.get();

  scoped_refptr<MockDHCPConfig> dhcp_config(
      new 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, EnableIPv6) {
  EXPECT_CALL(*device_, SetIPFlag(IPAddress::kFamilyIPv6,
                                  StrEq(Device::kIPFlagDisableIPv6),
                                  StrEq("0")))
      .WillOnce(Return(true));
  device_->EnableIPv6();
}

TEST_F(DeviceTest, EnableIPv6NotAllowed) {
  EXPECT_CALL(*device_, IsIPv6Allowed()).WillOnce(Return(false));
  EXPECT_CALL(*device_, SetIPFlag(_, _, _)).Times(0);
  device_->EnableIPv6();
}

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, StorageIdGeneration) {
  string to_process("/device/stuff/0");
  ControlInterface::RpcIdToStorageId(&to_process);
  EXPECT_TRUE(isalpha(to_process[0]));
  EXPECT_EQ(string::npos, to_process.find('/'));
}

TEST_F(DeviceTest, SelectedService) {
  EXPECT_FALSE(device_->selected_service_.get());
  device_->SetServiceState(Service::kStateAssociating);
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  manager()));
  SelectService(service);
  EXPECT_TRUE(device_->selected_service_.get() == service.get());

  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, LinkMonitorFailure) {
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  manager()));
  SelectService(service);
  EXPECT_TRUE(device_->selected_service().get() == service.get());

  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>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  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>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  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 MockIPConfig(control_interface(),
                                                          kDeviceName);
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  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, SetState(Service::kStateConnected));
  EXPECT_CALL(*service, IsConnected())
      .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 MockIPConfig(control_interface(),
                                                          kDeviceName);
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  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>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  manager()));
  SelectService(service);
  service->static_ip_parameters_.args_.SetString(kAddressProperty, "1.1.1.1");
  service->static_ip_parameters_.args_.SetInt(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>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  manager()));
  SelectService(service);
  scoped_refptr<MockIPConfig> ipconfig = new MockIPConfig(control_interface(),
                                                          kDeviceName);
  device_->set_ipconfig(ipconfig);
  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())
      .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<string>{ 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 MockIPConfig(control_interface(),
                                                          kDeviceName);
  SelectService(nullptr);
  OnIPConfigUpdated(ipconfig.get());
}

TEST_F(DeviceTest, OnIPConfigExpired) {
  scoped_refptr<MockIPConfig> ipconfig =
      new MockIPConfig(control_interface(), kDeviceName);
  const int kLeaseLength = 1234;
  ipconfig->properties_.lease_duration_seconds = kLeaseLength;

  EXPECT_CALL(metrics_,
              SendToUMA("Network.Shill.Unknown.ExpiredLeaseLengthSeconds",
                        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;
  StrictMock<MockManager> manager(control_interface(),
                                  dispatcher(),
                                  metrics());
  SetManager(&manager);
  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;
  StrictMock<MockManager> manager(control_interface(),
                                  dispatcher(),
                                  metrics());
  EXPECT_CALL(manager, UpdateDevice(_));
  SetManager(&manager);
  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>(&control_interface_,
                                dispatcher(),
                                metrics(),
                                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_FALSE(device_->ipconfig_.get());
  EXPECT_FALSE(device_->selected_service_.get());
}

TEST_F(DeviceTest, StartProhibited) {
  DeviceRefPtr device(new TestDevice(control_interface(),
                                     dispatcher(),
                                     nullptr,
                                     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().get());
  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>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  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>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  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_));
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  &manager));
  SelectService(service);
  SetConnection(connection.get());
  MockLinkMonitor* link_monitor = new StrictMock<MockLinkMonitor>();
  SetLinkMonitor(link_monitor);  // Passes ownership.
  SetManager(&manager);
  EXPECT_CALL(*link_monitor, Start()).Times(0);
  EXPECT_CALL(manager, IsTechnologyLinkMonitorEnabled(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_));
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  &manager));
  SelectService(service);
  SetConnection(connection.get());
  MockLinkMonitor* link_monitor = new StrictMock<MockLinkMonitor>();
  SetLinkMonitor(link_monitor);  // Passes ownership.
  SetManager(&manager);
  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_));
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  &manager));
  SelectService(service);
  SetConnection(connection.get());
  MockTrafficMonitor* traffic_monitor = new StrictMock<MockTrafficMonitor>();
  SetTrafficMonitor(traffic_monitor);  // Passes ownership.
  SetManager(&manager);

  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 = new StrictMock<MockTrafficMonitor>();
  SetTrafficMonitor(traffic_monitor);
  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_));
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  &manager));
  SelectService(service);
  SetConnection(connection.get());
  MockTrafficMonitor* traffic_monitor = new StrictMock<MockTrafficMonitor>();
  SetTrafficMonitor(traffic_monitor);  // Passes ownership.
  EXPECT_CALL(*device_, IsTrafficMonitorEnabled()).WillRepeatedly(Return(true));
  SetManager(&manager);
  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, PerformTDLSOperation) {
  EXPECT_EQ(
      "", device_->PerformTDLSOperation("do something", "to someone", nullptr));
}

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<string>(), device_->AvailableIPConfigs(nullptr));
  device_->ipconfig_ = new IPConfig(control_interface(), kDeviceName);
  EXPECT_EQ(vector<string> { 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<string> { IPConfigMockAdaptor::kRpcId },
            device_->AvailableIPConfigs(nullptr));

  device_->ip6config_ = nullptr;
  EXPECT_EQ(vector<string>(), device_->AvailableIPConfigs(nullptr));
}

TEST_F(DeviceTest, OnIPv6AddressChanged) {
  StrictMock<MockManager> manager(control_interface(),
                                  dispatcher(),
                                  metrics());
  manager.set_mock_device_info(&device_info_);
  EXPECT_CALL(manager, FilterPrependDNSServersByFamily(_))
      .WillRepeatedly(Return(vector<string>()));
  SetManager(&manager);

  // An IPv6 clear while ip6config_ is nullptr will not emit a change.
  EXPECT_CALL(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(Return(false));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(kIPConfigsProperty, _)).Times(0);
  device_->OnIPv6AddressChanged();
  EXPECT_THAT(device_->ip6config_, IsNullRefPtr());
  Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  Mock::VerifyAndClearExpectations(&device_info_);

  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(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address0), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  device_->OnIPv6AddressChanged();
  EXPECT_THAT(device_->ip6config_, NotNullRefPtr());
  EXPECT_EQ(kAddress0, device_->ip6config_->properties().address);
  Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  Mock::VerifyAndClearExpectations(&device_info_);

  // If the IPv6 address does not change, no signal is emitted.
  EXPECT_CALL(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address0), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(kIPConfigsProperty, _)).Times(0);
  device_->OnIPv6AddressChanged();
  EXPECT_EQ(kAddress0, device_->ip6config_->properties().address);
  Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  Mock::VerifyAndClearExpectations(&device_info_);

  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(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address1), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  device_->OnIPv6AddressChanged();
  EXPECT_EQ(kAddress1, device_->ip6config_->properties().address);
  Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  Mock::VerifyAndClearExpectations(&device_info_);

  // If the IPv6 prefix changes, a signal is emitted.
  address1.set_prefix(64);
  EXPECT_CALL(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address1), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  device_->OnIPv6AddressChanged();
  EXPECT_EQ(kAddress1, device_->ip6config_->properties().address);

  // Return the IPv6 address to nullptr.
  EXPECT_CALL(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(Return(false));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(kIPConfigsProperty,
                                            vector<string>()));
  device_->OnIPv6AddressChanged();
  EXPECT_THAT(device_->ip6config_, IsNullRefPtr());
  Mock::VerifyAndClearExpectations(GetDeviceMockAdaptor());
  Mock::VerifyAndClearExpectations(&device_info_);
}

TEST_F(DeviceTest, OnIPv6DnsServerAddressesChanged_LeaseExpirationUpdated) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  EXPECT_CALL(manager, FilterPrependDNSServersByFamily(_))
      .WillRepeatedly(Return(vector<string>()));
  SetManager(&manager);

  scoped_refptr<MockIPConfig> ip6config =
      new MockIPConfig(control_interface(), kDeviceName);
  device_->ip6config_ = ip6config;

  // Non-infinite lifetime should trigger an update of the current lease
  // expiration time.
  const uint32_t kExpiredLifetime = 1;
  EXPECT_CALL(device_info_,
              GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
      .WillOnce(DoAll(SetArgPointee<2>(kExpiredLifetime),
                      Return(true)));
  EXPECT_CALL(*ip6config, UpdateLeaseExpirationTime(_)).Times(1);
  EXPECT_CALL(*ip6config, ResetLeaseExpirationTime()).Times(0);
  device_->OnIPv6DnsServerAddressesChanged();

  // Infinite lifetime should cause a reset of the current lease expiration
  // time to its default value.
  const uint32_t kExpiredLifetimeInfinity = ND_OPT_LIFETIME_INFINITY;
  EXPECT_CALL(device_info_,
              GetIPv6DnsServerAddresses(kDeviceInterfaceIndex, _, _))
      .WillOnce(DoAll(SetArgPointee<2>(kExpiredLifetimeInfinity),
                      Return(true)));
  EXPECT_CALL(*ip6config, UpdateLeaseExpirationTime(_)).Times(0);
  EXPECT_CALL(*ip6config, ResetLeaseExpirationTime()).Times(1);
  device_->OnIPv6DnsServerAddressesChanged();
}

TEST_F(DeviceTest, OnIPv6DnsServerAddressesChanged) {
  StrictMock<MockManager> manager(control_interface(),
                                  dispatcher(),
                                  metrics());
  manager.set_mock_device_info(&device_info_);
  EXPECT_CALL(manager, FilterPrependDNSServersByFamily(_))
      .WillRepeatedly(Return(vector<string>()));
  SetManager(&manager);

  // 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;
  dns_server_addresses.push_back(ipv6_address1);
  dns_server_addresses.push_back(ipv6_address2);
  vector<string> dns_server_addresses_str;
  dns_server_addresses_str.push_back(kAddress1);
  dns_server_addresses_str.push_back(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<string> { 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(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address3), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  device_->OnIPv6AddressChanged();
  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<string> { 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<string> { 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<string> { 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) {
  StrictMock<MockManager> manager(control_interface(),
                                  dispatcher(),
                                  metrics());
  manager.set_mock_device_info(&device_info_);
  EXPECT_CALL(manager, FilterPrependDNSServersByFamily(_))
      .WillRepeatedly(Return(vector<string>()));
  SetManager(&manager);
  scoped_refptr<MockService> service(
      new StrictMock<MockService>(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  &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(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address1), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  EXPECT_CALL(*connection, IsIPv6())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*service, SetConnection(_)).Times(0);
  device_->OnIPv6AddressChanged();
  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(device_info_, GetPrimaryIPv6Address(kDeviceInterfaceIndex, _))
      .WillOnce(DoAll(SetArgPointee<1>(address2), Return(true)));
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { 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, SetState(Service::kStateConnected));
  EXPECT_CALL(*service, IsConnected())
      .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();
  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<string> { 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.address = "2001:db8:0:1::1";
  properties.delegated_prefix = "2001:db8:0:100::";
  properties.lease_duration_seconds = 1;
  device_->dhcpv6_config_->set_properties(properties);
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  device_->OnDHCPv6ConfigFailed(device_->dhcpv6_config_.get());
  EXPECT_TRUE(device_->dhcpv6_config_->properties().address.empty());
  EXPECT_TRUE(device_->dhcpv6_config_->properties().delegated_prefix.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.address = "2001:db8:0:1::1";
  properties.delegated_prefix = "2001:db8:0:100::";
  properties.lease_duration_seconds = 1;
  device_->dhcpv6_config_->set_properties(properties);
  EXPECT_CALL(*GetDeviceMockAdaptor(),
              EmitRpcIdentifierArrayChanged(
                  kIPConfigsProperty,
                  vector<string> { IPConfigMockAdaptor::kRpcId }));
  device_->OnDHCPv6ConfigExpired(device_->dhcpv6_config_.get());
  EXPECT_TRUE(device_->dhcpv6_config_->properties().address.empty());
  EXPECT_TRUE(device_->dhcpv6_config_->properties().delegated_prefix.empty());
  EXPECT_EQ(0, device_->dhcpv6_config_->properties().lease_duration_seconds);
}

TEST_F(DeviceTest, PrependIPv4DNSServers) {
  MockManager manager(control_interface(), dispatcher(), metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  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) {
  MockManager manager(control_interface(), dispatcher(), metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  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) {
  MockManager manager(control_interface(), dispatcher(), metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  scoped_refptr<IPConfig> ipconfig =
      new IPConfig(control_interface(), kDeviceName);

  device_->set_ipconfig(ipconfig);

  scoped_refptr<MockService> service = new MockService(control_interface(),
                                                       dispatcher(),
                                                       metrics(),
                                                       &manager);
  EXPECT_CALL(*service, IsPortalDetectionDisabled())
      .WillRepeatedly(Return(true));
  SelectService(service);

  auto parameters = service->mutable_static_ip_parameters();
  parameters->args_.SetString(kAddressProperty, "1.1.1.1");
  parameters->args_.SetInt(kPrefixlenProperty, 16);

  scoped_refptr<MockConnection> connection = new 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_.SetStrings(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) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  // 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.
  const char kPeerIp[] = "192.168.1.1";
  error.Reset();
  EXPECT_CALL(device_info_,
              HasDirectConnectivityTo(device_->interface_index(), _))
      .WillOnce(Return(false));
  EXPECT_FALSE(device_->ResolvePeerMacAddress(kPeerIp, &result, &error));
  EXPECT_EQ(Error::kInvalidArguments, error.type());
  Mock::VerifyAndClearExpectations(&device_info_);

  // Provided IP address is in the ARP cache, return the resolved MAC address.
  const char kResolvedMac[] = "00:11:22:33:44:55";
  const ByteString kMacBytes(
      Device::MakeHardwareAddressFromString(kResolvedMac));
  error.Reset();
  EXPECT_CALL(device_info_,
              HasDirectConnectivityTo(device_->interface_index(), _))
      .WillOnce(Return(true));
  EXPECT_CALL(device_info_,
              GetMACAddressOfPeer(device_->interface_index(), _, _))
      .WillOnce(DoAll(SetArgPointee<2>(kMacBytes), Return(true)));
  EXPECT_TRUE(device_->ResolvePeerMacAddress(kPeerIp, &result, &error));
  EXPECT_EQ(kResolvedMac, result);
}

TEST_F(DeviceTest, SetHostnameWithEmptyHostname) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  EXPECT_CALL(manager, ShouldAcceptHostnameFrom(_)).Times(0);
  EXPECT_CALL(device_info_, SetHostname(_)).Times(0);
  EXPECT_FALSE(SetHostname(""));
}

TEST_F(DeviceTest, SetHostnameForDisallowedDevice) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  EXPECT_CALL(manager, ShouldAcceptHostnameFrom(kDeviceName))
      .WillOnce(Return(false));
  EXPECT_CALL(device_info_, SetHostname(_)).Times(0);
  EXPECT_FALSE(SetHostname("wilson"));
}

TEST_F(DeviceTest, SetHostnameWithFailingDeviceInfo) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  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) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  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) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

  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) {
  MockManager manager(control_interface(),
                      dispatcher(),
                      metrics());
  manager.set_mock_device_info(&device_info_);
  SetManager(&manager);

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

class DevicePortalDetectionTest : public DeviceTest {
 public:
  DevicePortalDetectionTest()
      : connection_(new StrictMock<MockConnection>(&device_info_)),
        manager_(control_interface(),
                 dispatcher(),
                 metrics()),
        service_(new StrictMock<MockService>(control_interface(),
                                             dispatcher(),
                                             metrics(),
                                             &manager_)),
        portal_detector_(new StrictMock<MockPortalDetector>(connection_)) {}
    virtual ~DevicePortalDetectionTest() {}
  virtual void SetUp() {
    DeviceTest::SetUp();
    SelectService(service_);
    SetConnection(connection_.get());
    device_->portal_detector_.reset(portal_detector_);  // Passes ownership.
    SetManager(&manager_);
  }

 protected:
  static const int kPortalAttempts;

  bool StartPortalDetection() { return device_->StartPortalDetection(); }
  void StopPortalDetection() { device_->StopPortalDetection(); }

  void PortalDetectorCallback(const PortalDetector::Result& result) {
    device_->PortalDetectorCallback(result);
  }
  bool RequestPortalDetection() {
    return device_->RequestPortalDetection();
  }
  void SetServiceConnectedState(Service::ConnectState state) {
    device_->SetServiceConnectedState(state);
  }
  void ExpectPortalDetectorReset() {
    EXPECT_FALSE(device_->portal_detector_.get());
  }
  void ExpectPortalDetectorSet() {
    EXPECT_TRUE(device_->portal_detector_.get());
  }
  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_;
  StrictMock<MockManager> manager_;
  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_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline));
  EXPECT_FALSE(StartPortalDetection());
}

TEST_F(DevicePortalDetectionTest, TechnologyPortalDetectionDisabled) {
  EXPECT_CALL(*service_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline));
  EXPECT_FALSE(StartPortalDetection());
}

TEST_F(DevicePortalDetectionTest, PortalDetectionProxyConfig) {
  EXPECT_CALL(*service_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline));
  EXPECT_FALSE(StartPortalDetection());
}

TEST_F(DevicePortalDetectionTest, PortalDetectionBadUrl) {
  EXPECT_CALL(*service_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillOnce(Return(true));
  const string portal_url;
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillRepeatedly(ReturnRef(portal_url));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline));
  EXPECT_FALSE(StartPortalDetection());
}

TEST_F(DevicePortalDetectionTest, PortalDetectionStart) {
  EXPECT_CALL(*service_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillOnce(Return(true));
  const string portal_url(ConnectivityTrial::kDefaultURL);
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillRepeatedly(ReturnRef(portal_url));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline))
      .Times(0);
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillRepeatedly(Return(false));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection_.get(), 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_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillOnce(Return(true));
  const string portal_url(ConnectivityTrial::kDefaultURL);
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillRepeatedly(ReturnRef(portal_url));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline))
      .Times(0);
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillRepeatedly(Return(true));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection_.get(), 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, PortalDetectionNonFinal) {
  EXPECT_CALL(*service_.get(), IsConnected())
      .Times(0);
  EXPECT_CALL(*service_.get(), SetState(_))
      .Times(0);
  PortalDetectorCallback(
      PortalDetector::Result(
          ConnectivityTrial::Result(
              ConnectivityTrial::kPhaseUnknown,
              ConnectivityTrial::kStatusFailure),
          kPortalAttempts,
          false));
}

MATCHER_P(IsPortalDetectorResult, result, "") {
  return (result.num_attempts == arg.num_attempts &&
          result.final == arg.final &&
          result.trial_result.phase == arg.trial_result.phase &&
          result.trial_result.status == arg.trial_result.status);
}

TEST_F(DevicePortalDetectionTest, PortalDetectionFailure) {
  PortalDetector::Result result(
      ConnectivityTrial::Result(ConnectivityTrial::kPhaseConnection,
                                ConnectivityTrial::kStatusFailure),
      kPortalAttempts, true);
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(),
              SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
                                        kPortalDetectionStatusFailure));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  EXPECT_CALL(metrics_,
              SendEnumToUMA("Network.Shill.Unknown.PortalResult",
                            Metrics::kPortalResultConnectionFailure,
                            Metrics::kPortalResultMax));
  EXPECT_CALL(metrics_,
              SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline",
                        _, _, _, _)).Times(0);
  EXPECT_CALL(metrics_,
              SendToUMA("Network.Shill.Unknown.PortalAttempts",
                        kPortalAttempts,
                        Metrics::kMetricPortalAttemptsMin,
                        Metrics::kMetricPortalAttemptsMax,
                        Metrics::kMetricPortalAttemptsNumBuckets));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(false));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillOnce(Return(false));
  EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                            IsPortalDetectorResult(result)));
  PortalDetectorCallback(result);
}

TEST_F(DevicePortalDetectionTest, PortalDetectionSuccess) {
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), SetPortalDetectionFailure(_, _)).Times(0);
  EXPECT_CALL(*service_.get(), 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(
          ConnectivityTrial::Result(
              ConnectivityTrial::kPhaseContent,
              ConnectivityTrial::kStatusSuccess),
          kPortalAttempts,
          true));
}

TEST_F(DevicePortalDetectionTest, PortalDetectionSuccessAfterFailure) {
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(),
              SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
                                        kPortalDetectionStatusFailure));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  EXPECT_CALL(metrics_,
              SendEnumToUMA("Network.Shill.Unknown.PortalResult",
                            Metrics::kPortalResultConnectionFailure,
                            Metrics::kPortalResultMax));
  EXPECT_CALL(metrics_,
              SendToUMA("Network.Shill.Unknown.PortalAttemptsToOnline",
                        _, _, _, _)).Times(0);
  EXPECT_CALL(metrics_,
              SendToUMA("Network.Shill.Unknown.PortalAttempts",
                        kPortalAttempts,
                        Metrics::kMetricPortalAttemptsMin,
                        Metrics::kMetricPortalAttemptsMax,
                        Metrics::kMetricPortalAttemptsNumBuckets));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(false));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillOnce(Return(false));
  PortalDetectorCallback(
      PortalDetector::Result(
          ConnectivityTrial::Result(
              ConnectivityTrial::kPhaseConnection,
              ConnectivityTrial::kStatusFailure),
          kPortalAttempts,
          true));
  Mock::VerifyAndClearExpectations(&metrics_);
  EXPECT_CALL(*service_.get(), SetPortalDetectionFailure(_, _)).Times(0);
  EXPECT_CALL(*service_.get(), 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));
  EXPECT_CALL(metrics_,
              SendToUMA("Network.Shill.Unknown.PortalAttempts",
                        _, _, _, _)).Times(0);
  PortalDetectorCallback(
      PortalDetector::Result(
          ConnectivityTrial::Result(
              ConnectivityTrial::kPhaseContent,
              ConnectivityTrial::kStatusSuccess),
          kPortalAttempts,
          true));
}

TEST_F(DevicePortalDetectionTest, RequestPortalDetection) {
  EXPECT_CALL(*service_.get(), state())
      .WillOnce(Return(Service::kStateOnline))
      .WillRepeatedly(Return(Service::kStatePortal));
  EXPECT_FALSE(RequestPortalDetection());

  EXPECT_CALL(*connection_.get(), is_default())
      .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_.get(), IsPortalDetectionDisabled())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillRepeatedly(Return(false));
  const string kPortalCheckURL("http://portal");
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillOnce(ReturnRef(kPortalCheckURL));
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection_.get(), dns_servers())
      .WillRepeatedly(ReturnRef(kDNSServers));
  EXPECT_TRUE(RequestPortalDetection());
}

TEST_F(DevicePortalDetectionTest, RequestStartConnectivityTest) {
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillRepeatedly(Return(false));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection_.get(), 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_.get(), IsConnected())
      .WillOnce(Return(false));
  SetServiceConnectedState(Service::kStatePortal);
  // 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_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), SetState(Service::kStateOnline));
  SetServiceConnectedState(Service::kStateOnline);
  ExpectPortalDetectorReset();
}

TEST_F(DevicePortalDetectionTest, NotDefault) {
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  SetServiceConnectedState(Service::kStatePortal);
  ExpectPortalDetectorReset();
}

TEST_F(DevicePortalDetectionTest, PortalIntervalIsZero) {
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, GetPortalCheckInterval())
      .WillOnce(Return(0));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  SetServiceConnectedState(Service::kStatePortal);
  ExpectPortalDetectorReset();
}

TEST_F(DevicePortalDetectionTest, RestartPortalDetection) {
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(true));
  const int kPortalDetectionInterval = 10;
  EXPECT_CALL(manager_, GetPortalCheckInterval())
      .Times(AtLeast(1))
      .WillRepeatedly(Return(kPortalDetectionInterval));
  const string kPortalCheckURL("http://portal");
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillOnce(ReturnRef(kPortalCheckURL));
  EXPECT_CALL(*portal_detector_, StartAfterDelay(kPortalCheckURL,
                                                 kPortalDetectionInterval))
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  SetServiceConnectedState(Service::kStatePortal);
  ExpectPortalDetectorSet();
}

TEST_F(DevicePortalDetectionTest, CancelledOnSelectService) {
  ExpectPortalDetectorSet();
  EXPECT_CALL(*service_.get(), state())
      .WillOnce(Return(Service::kStateIdle));
  EXPECT_CALL(*service_.get(), SetState(_));
  EXPECT_CALL(*service_.get(), SetConnection(_));
  SelectService(nullptr);
  ExpectPortalDetectorReset();
}

TEST_F(DevicePortalDetectionTest, PortalDetectionDNSFailure) {
  const char* kGoogleDNSServers[] = { "8.8.8.8", "8.8.4.4" };
  vector<string> fallback_dns_servers(kGoogleDNSServers, kGoogleDNSServers + 2);
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));

  // DNS Failure, start DNS test for fallback DNS servers.
  PortalDetector::Result result_dns_failure(
      ConnectivityTrial::Result(ConnectivityTrial::kPhaseDNS,
                                ConnectivityTrial::kStatusFailure),
      kPortalAttempts, true);
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(),
              SetPortalDetectionFailure(kPortalDetectionPhaseDns,
                                        kPortalDetectionStatusFailure));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(false));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillOnce(Return(false));
  EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                            IsPortalDetectorResult(result_dns_failure)));
  EXPECT_CALL(*device_, StartDNSTest(fallback_dns_servers, false, _)).Times(1);
  PortalDetectorCallback(result_dns_failure);
  Mock::VerifyAndClearExpectations(device_.get());

  // DNS Timeout, start DNS test for fallback DNS servers.
  PortalDetector::Result result_dns_timeout(
      ConnectivityTrial::Result(ConnectivityTrial::kPhaseDNS,
                                ConnectivityTrial::kStatusTimeout),
      kPortalAttempts, true);
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(),
              SetPortalDetectionFailure(kPortalDetectionPhaseDns,
                                        kPortalDetectionStatusTimeout));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(false));
  EXPECT_CALL(*connection_.get(), IsIPv6()).WillOnce(Return(false));
  EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                            IsPortalDetectorResult(result_dns_timeout)));
  EXPECT_CALL(*device_, StartDNSTest(fallback_dns_servers, false, _)).Times(1);
  PortalDetectorCallback(result_dns_timeout);
  Mock::VerifyAndClearExpectations(device_.get());

  // Other Failure, DNS server tester not started.
  PortalDetector::Result result_connection_failure(
      ConnectivityTrial::Result(ConnectivityTrial::kPhaseConnection,
                                ConnectivityTrial::kStatusFailure),
      kPortalAttempts, true);
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(),
              SetPortalDetectionFailure(kPortalDetectionPhaseConnection,
                                        kPortalDetectionStatusFailure));
  EXPECT_CALL(*service_.get(), SetState(Service::kStatePortal));
  EXPECT_CALL(*connection_.get(), is_default())
      .WillOnce(Return(false));
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillOnce(Return(false));
  EXPECT_CALL(*device_, StartConnectionDiagnosticsAfterPortalDetection(
                            IsPortalDetectorResult(result_connection_failure)));
  EXPECT_CALL(*device_, StartDNSTest(_, _, _)).Times(0);
  PortalDetectorCallback(result_connection_failure);
  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_.get(), 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_.get(), is_dns_auto_fallback_allowed())
      .WillOnce(Return(false));
  EXPECT_CALL(*connection_.get(), UpdateDNSServers(_)).Times(0);
  EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(0);
  EXPECT_CALL(*service_.get(), 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_.get(), is_dns_auto_fallback_allowed())
      .WillOnce(Return(true));
  EXPECT_CALL(*service_.get(), IsPortalDetectionDisabled())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillRepeatedly(Return(false));
  const string kPortalCheckURL("http://portal");
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillOnce(ReturnRef(kPortalCheckURL));
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection_.get(), dns_servers())
      .WillRepeatedly(ReturnRef(kDNSServers));

  EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(1);
  EXPECT_CALL(*connection_.get(), UpdateDNSServers(_)).Times(1);
  EXPECT_CALL(*service_.get(), 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_.get(), 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_.get(), IsPortalDetectionDisabled())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillRepeatedly(Return(false));
  const string kPortalCheckURL("http://portal");
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillOnce(ReturnRef(kPortalCheckURL));
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection_.get(), IsIPv6())
      .WillRepeatedly(Return(false));
  EXPECT_CALL(*connection_.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection_.get(), dns_servers())
      .WillRepeatedly(ReturnRef(kDNSServers));
  EXPECT_CALL(*connection_.get(), UpdateDNSServers(_)).Times(1);
  EXPECT_CALL(*ipconfig, UpdateDNSServers(_)).Times(1);
  EXPECT_CALL(*service_.get(), 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_.get(), IsPortalDetectionDisabled())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsConnected())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*service_.get(), HasProxyConfig())
      .WillOnce(Return(false));
  EXPECT_CALL(*service_.get(), IsPortalDetectionAuto())
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, IsPortalDetectionEnabled(device_->technology()))
      .WillOnce(Return(true));
  const string portal_url(ConnectivityTrial::kDefaultURL);
  EXPECT_CALL(manager_, GetPortalCheckURL())
      .WillRepeatedly(ReturnRef(portal_url));
  const string kInterfaceName("int0");
  EXPECT_CALL(*connection.get(), interface_name())
      .WillRepeatedly(ReturnRef(kInterfaceName));
  EXPECT_CALL(*connection.get(), IsIPv6())
      .WillRepeatedly(Return(false));
  const vector<string> kDNSServers;
  EXPECT_CALL(*connection.get(), 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_.get(), SetConnection(IsNullRefPtr()));
  DestroyConnection();
  EXPECT_TRUE(connection->HasOneRef());
}

class DeviceByteCountTest : public DeviceTest {
 public:
  DeviceByteCountTest()
      : manager_(control_interface(),
                 dispatcher(),
                 metrics()),
        rx_byte_count_(0),
        tx_byte_count_(0),
        rx_stored_byte_count_(0),
        tx_stored_byte_count_(0) {}
  virtual ~DeviceByteCountTest() {}

  virtual void SetUp() {
    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<MockManager> manager_;
  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(control_interface(),
                                     dispatcher(),
                                     nullptr,
                                     &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