2707 lines
106 KiB
C++
2707 lines
106 KiB
C++
//
|
|
// 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
|